c++ Commentaires sur l'utilisation de make sur un projet avec plusieurs sous-répertoires



recursion tree (1)

Pour mon cours de programmation orienté objet, je dois faire un projet final (objectifs académiques). Je veux faire un projet "dans le bon sens" (ie: makefile, modulaire, DRY, facilement évolutif, etc) afin de mieux comprendre les classes, makefile et C ++.

L'idée que j'ai est d'avoir un "tree-source-file-structure-directory" donc dans chaque sous-dossier j'ai eu les fichiers sources avec ses en-têtes, les fichiers de test et le makefile unique. Donc, si je veux travailler sur l'interface, je vais dans l'interface du sous-dossier, j'édite les fichiers, j'exécute les tests, si tout est OK, je lie simplement les objets ensemble sur mon répertoire racine. Même chose si je veux travailler sur ma structure de données, et ainsi de suite. La fonctionnalité intéressante est que dans chaque sous-dossier réside le long du code source et des fichiers objets, de sorte que l'éditeur de liens de mon répertoire racine recherche les fichiers objets déjà compilés dans les sous-dossiers et les lient ensemble

J'ai fait des recherches sur internet, et j'ai pu voir plusieurs solutions différentes: -Faire une recherche récursive, par exemple:

SUBDIRS=eda
.PHONY: subdirs $(SUBDIRS)
$(SUBDIRS):
    $(MAKE) -C [email protected]

Le problème que j'ai trouvé est que mes prérequis sur le dossier "eda" seraient "excentriques"

-Utilisation de la variable automatique $ (@ D), mais je n'ai pas très bien compris comment cela fonctionne -Peut-être utiliser la fonction générique, mais je suis un peu confus à propos de cette option.

Quoi qu'il en soit , la solution la plus tentante pour moi était la première (en utilisant make récursivement), mais j'ai trouvé beaucoup de commentaires disant qu'il n'est pas recommandé d'utiliser make récursive

Donc, je veux vous demander quelques conseils: Comment puis-je atteindre mes objectifs et avoir tous les modules importants dans un dossier séparé? est récursif faire la meilleure solution? Peut-être que je devrais plonger dans "automake"? Ou peut-être qu'il serait préférable de prendre tous les fichiers objet dans un nouveau sous-dossier "objet" sur le répertoire racine, puis les lier ensemble?

En passant, j'ai pris l'inspiration de faire mon projet avec cette structure arborescente en sniffant le code source d'Amarok: il a un sous-dossier appelé "src", et quand vous y entrez, vous pouvez voir beaucoup de sous-dossiers: égaliseur, playlist, dynamique , barre d'état, noyau, playlistgenerator, playlistmanager, etc. Et beaucoup de sous-dossiers ont leurs propres sous-répertoires ... et le résultat est un lecteur de musique incroyable. Si cette méthode fonctionne bien pour l'équipe Amarok ... je pourrais être capable de faire quelque chose de similaire!

Tous les commentaires, commentaires, suggestions et autres sont les bienvenus, merci d'avance!

EDIT # 1

Beta , j'ai quelques règles implicites (suffixes) et une cible pour l'éditeur de liens qui a besoin d'un objet sur mon dossier eda . Tous les autres prérequis de cette cible sont créés dans le dossier en cours. Le problème que j'ai, c'est que quand je lance make pour construire cette cible, il prend le nom de mes prérequis sur le dossier "eda" en tant que cible pour construire avec la règle implicite. C'est la partie difficile / impure du makefile récursif sur mon projet: Je suppose que je dois créer une règle implicite spéciale pour chaque fichier d'objet que make doit rechercher dans un sous-dossier.

C'est pourquoi je veux un retour d'information: Y a-t-il de meilleures alternatives? Ou les avantages d'utiliser faire récursive dans mes projets submergent les autres alternatives?

De toute façon, si cela vous donne une meilleure compréhension, voici mon brouillon Makefile (c'est en spnish-anglais: P)

#Makefile hecho para las pruebas de los archivos dentro de esta carpeta
FLAGS=-g -DDEBUG

OUT_TI=TIndividuo

OUT_TP=TProfesor
OUT_TA=TAula

.SUFFIXES: .cpp .c .h .o
.c.o: ; cc $(FLAGS) -c $*.c
.cc.o: ; gcc $(FLAGS) -c $*.cc
.cpp.o: ; g++ $(FLAGS) -c $*.cpp

SUBDIRS=eda
.PHONY: subdirs $(SUBDIRS) 

$(OUT_TI): eda/TAula.o CandidatoHorario.o TIndividuo.o TIndividuoTest.o TGen.o
    g++ CandidatoHorario.o TIndividuo.o TIndividuoTest.o TGen.o eda/TAula.o -o [email protected]
CandidatoHorario.o: CandidatoHorario.cpp CandidatoHorario.h
TIndividuoTest.o: TIndividuoTest.cpp TIndividuo.h
TIndividuo.o: TIndividuo.cpp TIndividuo.h
TGen.o: TGen.cpp
#eda/TAula.o: eda/TAula.cpp eda/TAula.h
#   g++ -c eda/TAula.cpp -o [email protected]

$(SUBDIRS):
    $(MAKE) -C [email protected]

clean:
    rm -f *.o $(OUT_TI) $(OUT_TA) eda/TAula.o

https://ffff65535.com


La "Recursive Make Considered Harmful" est certainement un document à lire et à comprendre. Ensuite, votre sélection d'outils devrait vraiment être adaptée à vos projets spécifiques.

Pour les petits projets que vous initiez (ou où vous avez l'influence pour guider les décisions de haut niveau), je vous recommande de passer un peu de temps à identifier vos préférences (présentation du projet, structure du répertoire, cadre de test unitaire, etc.) ensemble de fichiers makefiles que vous utiliserez pour tous vos projets. Vous pourriez facilement vous retrouver avec un makefile master générique, éventuellement quelques makefiles inclus génériques pour la modularité (par exemple pour construire des bibliothèques, ou des tests unitaires ou la détection automatique de dépendances). Vous pouvez également fournir une flexibilité supplémentaire avec les makefiles de configuration inclus facultatifs (par exemple en spécifiant l'ordre de vos bibliothèques). La plupart des constructions DAG dépendraient fortement du contenu de vos répertoires de projet. Un exemple pourrait ressembler à:

include config.mk

sources := $(wildcard *.cpp)
programs := $(sources:%.cpp=%)
lib_sources := $(wildcard lib/*/*.cpp)
lib_dirs := $(sort $(patsubst %/, %, $(dir $(lib_sources:lib/%=%))))
lib_objects := $(lib_sources:lib/%.cpp=$(BUILD)/%.o)

all: $(programs:%=$(BUILD)/%)

.PRECIOUS: %/.sentinel %.d

# for dependencies on directories in build tree
%/.sentinel:
        @mkdir -p $* && touch [email protected]

clean:
        $(RM) -r $(BUILD)

programs_aux:=$(programs)
include $(foreach program, $(programs), program.mk)

lib_dirs_aux:=$(lib_dirs)
include $(foreach lib_dir, $(lib_dirs), lib.mk)

# this should probably be in lib.mk
-include $(lib_objects:%.o=%.d)

Le program.mk (et lib.mk ) lib.mk contiendrait du code standard pour itérer sur les listes de programmes (et les listes de bibliothèques) et lib.mk les parties spécifiques du fichier makefile pour construire des programmes (et des bibliothèques).

Pour aider à l'implémentation de tels fichiers makefiles, vous pouvez utiliser une bibliothèque standard comme http://gmsl.sourceforge.net .

Cette approche a plusieurs problèmes: * elle conduit à des makefiles qui requièrent de fortes compétences * elle ne s'adapte pas toujours très bien aux très grands projets * elle repose fortement sur "convention au lieu de configuration" et nécessite une définition claire des conventions que vous utilisera (OMI ceci est bon d'autres pourraient penser qu'il manque de flexibilité) * La vie est trop courte pour déranger avec makefiles

Sinon, je suggérerais d'utiliser des outils de configuration de plus haut niveau tels que SCons ou CMake, car ils ont tendance à être conceptuellement plus simples et permettent également d'autres types de générateurs.





makefile