2009-12-19 6 views
14

J'ai un répertoire "lib" dans le répertoire principal de mes applications, qui contient un nombre arbitraire de sous-répertoires, chacun ayant son propre Makefile.créer des cibles de sous-répertoire génériques

Je voudrais avoir un seul Makefile dans le répertoire principal, qui appelle Makefile de chaque sous-répertoire. Je sais que c'est possible si je liste manuellement les sous-répertoires, mais je voudrais l'avoir fait automatiquement.

Je pensais à quelque chose comme ce qui suit, mais cela ne marche évidemment pas. Notez que j'ai aussi des objectifs de nettoyage, de test, etc., donc% n'est probablement pas une bonne idée du tout.

LIBS=lib/* 

all: $(LIBS) 

%: 
    (cd [email protected]; $(MAKE)) 

Toute aide est appréciée!

Répondre

23

Ce qui suit travaillera avec GNU make:

LIBS=$(wildcard lib/*) 
all: $(LIBS) 
.PHONY: force 
$(LIBS): force 
    cd [email protected] && pwd 

S'il pourrait y avoir autre chose que des répertoires dans lib, vous pourriez également utiliser:

LIBS=$(shell find lib -type d) 

Pour résoudre le problème multiple des cibles, vous peut construire des cibles spéciales pour chaque répertoire, puis enlever le préfixe de la sous-construction:

LIBS=$(wildcard lib/*) 
clean_LIBS=$(addprefix clean_,$(LIBS)) 
all: $(LIBS) 
clean: $(clean_LIBS) 
.PHONY: force 
$(LIBS): force 
    echo make -C [email protected] 
$(clean_LIBS): force 
    echo make -C $(patsubst clean_%,%,[email protected]) clean 
+0

Wow, Merci. Fonctionne comme un charme! – Zed

+1

Pourquoi n'avez-vous pas défini '$ (clean_LIBS)' comme faux? Par exemple. '.PHONY: $ (LIBS) $ (clean_LIBS)', alors je pense que vous n'avez pas besoin de 'force'. –

+0

@dma_k: Vous faites un excellent point. Je n'ai pas testé votre suggestion, mais votre raisonnement me semble juste. En fait, cela ressemble beaucoup à ce que le manuel GNU Make suggère ici (voir la partie sur les sous-répertoires): http://www.gnu.org/software/make/manual/make.html#Phony-Targets – mrkj

2

Il y a aussi une manière de lister les sous-répertoires avec gmake commandes seulement, sans utiliser de commandes shell:

test: 
    @echo $(filter %/, $(wildcard lib/*/)) 

Ceci listera tous les sous-répertoires avec '/' arrière. Pour le supprimer, vous pouvez utiliser le modèle de remplacement:

subdirs = $(filter %/, $(wildcard lib/*/)) 
test: 
    @echo $(subdirs:%/=%) 

ensuite créer des règles d'exécution en fait makefiles dans chaque sous-répertoire, vous pouvez utiliser une petite astuce - une cible dans un répertoire phony inexistant. Je pense que dans ce cas un exemple racontera plus que toute explication:

FULL_DIRS =$(filter %/, $(wildcard lib/*/)) 
LIB_DIRS =$(FULL_DIRS:%/=%) 
DIRS_CMD =$(foreach subdir, $(LIB_DIRS), make-rule/$(subdir)) 

make-rule/%: 
    cd $* && $(MAKE) 

all: DIRS_CMD 

Fondamentalement, la cible 'all' répertorie tous les sous-répertoires comme conditions préalables. Par exemple, si LIB_DIRS contenait lib/folder1 lib/folder2 alors l'expansion devrait ressembler à ceci:

all: make-rule/lib/folder1 make-rule/lib/folder2 

ensuite « faire », afin d'exécuter la règle 'all', essaie de faire correspondre chaque condition sine qua non à une cible existante. Dans ce cas, la cible est 'make-rule/%:', qui utilise '$*' pour extraire la chaîne après 'make-rule/' et l'utilise comme argument dans la recette. Par exemple, la première condition serait adaptée et développée comme ceci:

make-rule/lib/folder1: 
    cd lib/folder1 && $(MAKE) 
+0

Je crois que le 'all: DIRS_CMD' devrait à la place être' all: $ DIRS_CMD' car l'itération en cours ne l'étend pas correctement. Je recevais quelque chose comme 'make: *** Aucune règle pour rendre la cible 'DIRS_CMD', nécessaire pour 'all'. Stop.' – jnt30

+0

C'est possible. Je pense que pour moi ça a marché sans $ mais je n'ai pas les sources et je ne peux pas vérifier. – Amiramix

0

Que faire si vous voulez appeler différentes cibles que tous dans un nombre inconnu de sous-répertoires?

Le Makefile suivant utilise des macros créent donc un transfert cible factice pour un certain nombre de sous-répertoires d'appliquer la cible donnée de la ligne de commande à chacun d'eux:

# all direct directories of this dir. uses "-printf" to get rid of the "./" 
DIRS=$(shell find . -maxdepth 1 -mindepth 1 -type d -not -name ".*" -printf '%P\n') 
# "all" target is there by default, same logic as via the macro 
all: $(DIRS) 

$(DIRS): 
    $(MAKE) -C [email protected] 
.PHONY: $(DIRS) 

# if explcit targets where given: use them in the macro down below. each target will be delivered to each subdirectory contained in $(DIRS). 
EXTRA_TARGETS=$(MAKECMDGOALS) 

define RECURSIVE_MAKE_WITH_TARGET 
# create new variable, with the name of the target as prefix. it holds all 
# subdirectories with the target as suffix 
$(1)_DIRS=$$(addprefix $(1)_,$$(DIRS)) 

# create new target with the variable holding all the subdirectories+suffix as 
# prerequisite 
$(1): $$($1_DIRS) 

# use list to create target to fullfill prerequisite. the rule is to call 
# recursive make into the subdir with the target 
$$($(1)_DIRS): 
     $$(MAKE) -C $$(patsubst $(1)_%,%,[email protected]) $(1) 

# and make all targets .PHONY 
.PHONY: $$($(1)_DIRS) 
endef 

# evaluate the macro for all given list of targets 
$(foreach t,$(EXTRA_TARGETS),$(eval $(call RECURSIVE_MAKE_WITH_TARGET,$(t)))) 

Hope this helps.Vraiment utile en traitant du parallélisme: make -j12 nettoie tout dans un arbre avec des makefiles ayant ces cibles ... Comme toujours: jouer avec make est dangereux, différents méta-niveaux de programmation sont trop proches, -)

Questions connexes