Makefile récursif et -j

Le
Rémi Moyen
Bonjour,

J'essaye de compiler un (gros) projet en profitant d'une machine multi-
core et donc de l'option -j de make (pour lancer plusieurs commandes
en parallèle), mais j'ai un problème de dépendances que je n'arrive
pas à résoudre.

Mon projet se comporte de plusieurs répertoires avec chacun des sous-
répertoires. En gros, quand je fais 'make' à la racine, il descend
dans tous les répertoires et y refait 'make', chacun refaisant la même
chose dans ses sous-répertoires. En pratique, dans chaque répertoire,
j'ai un Makefile.inc qui contient une ligne du style 'SUBDIRS = dir1
dir2 dir3'. Ce fichier est inclus par le Makefile du répertoire et il
inclut aussi un fichier de définitions génériques qui définit une
cible pour make comme :

all:
for dir in ${SUBDIRS}; do
(cd $$dir; ${MAKE});
done;

Du coup, pour la plupart des répertoires, le Makefile est un truc
quasi-vide qui inclut le Makefile.inc et le fichier générique (qui
contient le bloc ci-dessus), et quand je modifie la structure je n'ai
qu'à changer SUBDIRS dans le Makefile.inc. Je ne sais pas si c'est la
meilleure manière de faire, mais au moins c'est facile :-)

Mon problème vient du fait que, dans certains cas, certains
répertoires doivent être compilés avant d'autres (par exemple, dir1
avant dir2). Avec un make "normal" (i.e. sans -j), il n'y a qu'une
seule commande exécutée à la fois, il me suffit donc de mettre les
répertoires dans le bon ordre dans SUBDIRS.

Par contre, dès que j'utilise -j, make lance plusieurs commandes à la
fois et en particulier, il va commencer dir2 avant que dir1 soit fini.
Ce qui ne marche pas.

Donc, comment je peux faire pour spécifier un ordre de dépendance ? Si
dir1 et dir2 étaient des cibles de make, alors je pourrais dire que
dir2 dépend de dir1, mais en l'occurrence, ce ne sont qu'une variable
qui contient les deux Je ne vois pas de manière facile de faire ça.

Bon, ça n'est pas un système que j'ai créé, et il y a peut-être u=
ne
meilleure manière de faire ça, mais je n'ai qu'un contrôle très lim=
ité
sur ces Makefile, donc je suis obligé de rester dans ce cadre-là.
J'utilise GNU Make 3.80, dès fois que ça ait une importance.

Est-ce que quelqu'un a une idée ?

Merci d'avance !
--
Rémi Moyen
Questions / Réponses high-tech
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Nicolas George
Le #20795431
Rémi Moyen wrote in message
Est-ce que quelqu'un a une idée ?



Arrange-toi pour éliminer la récursion de make : tu as un seul makefile à la
racine qui _inclue_ les makefiles des sous-répertoires, pas qui les appelle
récursivement. Comme ça, tu as l'ensemble des dépendances du projet visible
par make, il peut tout compiler en parallèle dans le bon ordre.
Rémi Moyen
Le #20795621
On Dec 18, 5:08 pm, Nicolas George
Arrange-toi pour éliminer la récursion de make : tu as un seul make file à la
racine qui _inclue_ les makefiles des sous-répertoires, pas qui les app elle
récursivement. Comme ça, tu as l'ensemble des dépendances du projet visible
par make, il peut tout compiler en parallèle dans le bon ordre.



Euh... Je ne comprends pas...

Bon, déjà, vu que je n'ai pas le contrôle complet sur le projet, je
doute que je puisse modifier tous les Makefile (et en particulier,
modifier la structure même du système). Donc, même sans comprendre
vraiment ce que tu veux dire, j'ai peur de ne pas pouvoir l'appliquer.

Mais sinon, je ne suis pas sûr de comprendre ce que tu proposes.
Imaginons que la racine contienne dir1 et dir2, dir1 ayant subdir1 et
subdir2, subdir1 ayant file11.c, subdir2 ayant file12.cpp et dir2
ayant file2.c. Le Makefile de subdir1 a une cible lib11.so qui dépend
de file11.c (via un .o et je ne sais quoi, mais je simplifie). Idem
pour subdir2 (avec lib12.so). dir2, lui, a une cible file.exe qui
dépend de file2.c, lib11.so et lib12.so.

La racine a un Makefile qui inclut celui de dir1 et dir2, et dir1
inclut subdir1 et subdir2. Du coup, la racine connait directement
trois cibles:
lib11.so: file11.c
lib12.so: file12.c
file.exe: file2.c lib11.so lib12.so
et make -j se débrouille tout seul pour ne pas faire file.exe tant que
lib11.so et lib12.so ne sont pas fait.

C'est bien ça ?

Mais du coup, je suis obligé d'écrire tous les Makefile finaux sous
une forme qui inclut les chemins complets, non ? Par exemple, je ne
peux pas dire "lib11.so: file11.c", mais je suis obligé de dire "dir1/
subdir1/lib11.so: dir1/subdir1/file11.c" (bon, en pratique ça ne
serait pas au même endroit, mais peu importe, c'est pour l'exemple) ?

Je peux sans doute me débrouiller avec un CURDIR=dir1 (dans dir1)
avant d'inclure le Makefile de subdir1 et écrire dans subdir1 "$
{CURDIR}/${CURSUBDIR}/lib11.so: ...", mais je ne suis pas sûr de
pouvoir facilement faire une structure récursive à autant de niveaux
que je veux sans avoir à écrire tous les niveaux à la main (i.e. je
veux que le Makefile final de subdir1 et 2, tout comme les Makefile
intermédiaires de dir1, puisse être simplement un include d'un ou deux
Makefile génériques (genre $PROJECTROOT/config/Makefile), donc si je
ne vois pas très bien comment distinguer CURDIR, CURSUBDIR,
CURSUBSUBDIR etc.)...

Bon, je vais essayer de voir si 1) j'arrive à faire ce que je veux 2)
ce sont des changements que je peux faire dans la structure du projet.

Merci !
--
Rémi Moyen
Nicolas George
Le #20795981
Rémi Moyen wrote in message
Bon, déjà, vu que je n'ai pas le contrôle complet sur le projet, je
doute que je puisse modifier tous les Makefile



Bon, ben t'es mort.

Mais sinon, je ne suis pas sûr de comprendre ce que tu proposes.
Imaginons que la racine contienne dir1 et dir2, dir1 ayant subdir1 et
subdir2, subdir1 ayant file11.c, subdir2 ayant file12.cpp et dir2
ayant file2.c. Le Makefile de subdir1 a une cible lib11.so qui dépend
de file11.c (via un .o et je ne sais quoi, mais je simplifie). Idem
pour subdir2 (avec lib12.so). dir2, lui, a une cible file.exe qui
dépend de file2.c, lib11.so et lib12.so.

La racine a un Makefile qui inclut celui de dir1 et dir2, et dir1
inclut subdir1 et subdir2. Du coup, la racine connait directement
trois cibles:
lib11.so: file11.c
lib12.so: file12.c
file.exe: file2.c lib11.so lib12.so
et make -j se débrouille tout seul pour ne pas faire file.exe tant que
lib11.so et lib12.so ne sont pas fait.

C'est bien ça ?



Oui, c'est ça.

Le principe, c'est que make travaille sur des règles qui fabriquent un (ou
éventuellement plusieurs, mais c'est parfois bancal) fichier objet à partir
de fichiers source. Souvent récursivement : un .o est le fichier objet de la
règle de compilation, dont la source est le .c correspondant, mais c'est la
source de la règle d'édition de liens. Make se débrouille pour appliquer
uniquement les règles qui ont besoin d'être appliquées, dans le bon ordre,
pour produire l'objet qu'on lui demande.

Make supporte, presque par hasard, les règles « phony », qui ne produisent
pas le fichier qu'elles sont censées produire. Par exemple quand on écrit
« make install », make applique la règle qui est censée produire un fichier
install. Il se trouve qu'elle fait plein de trucs sans rapport et ne produit
pas de fichier install. Ça marche, mais dans certaines limites.

Si tu écris « make -C foo », tu demandes à make d'aller faire dans le
répertoire foo, mais sans lui préciser quoi. On peut lui préciser « make -C
foo libfoo.so.1 », c'est déjà nettement mieux.

Mais ça reste complètement bancal, parce que, au choix :

- On précise au make parent que cette règle concerne la création du fichier
foo/libfoo.so.1 : dans ce cas, si ce fichier existe déjà, le make parent
ne va pas réinvoquer le make récursif, puisque le fichier existe déjà et
n'a aucune dépendance déclarée. Donc même si tu as modifié des sources
dans le répertoire foo, il n'est pas reconstruit.

- On ne précise pas au make parent que cette règle concerne la création du
fichier, mais dans ce cas, d'une il redescend dans le répertoire à chaque
fois, même pour un changement qui n'a rien à voir, juste pour se rendre
compte qu'il n'y a rien à faire.

Et je ne parle même pas du fait que le make parent ne peut pas savoir
combien le make enfant compte lancer de fils en parallèle, et
réciproquement.

La situation où un seul make voit toute la structure du projet est
infiniment préférable.

Mais du coup, je suis obligé d'écrire tous les Makefile finaux sous
une forme qui inclut les chemins complets, non ? Par exemple, je ne
peux pas dire "lib11.so: file11.c", mais je suis obligé de dire "dir1/
subdir1/lib11.so: dir1/subdir1/file11.c" (bon, en pratique ça ne
serait pas au même endroit, mais peu importe, c'est pour l'exemple) ?



Oui, mais tu peux facilement utiliser des macros pour simplifier largement
ça.

Je peux sans doute me débrouiller avec un CURDIR=dir1 (dans dir1)
avant d'inclure le Makefile de subdir1 et écrire dans subdir1 "$
{CURDIR}/${CURSUBDIR}/lib11.so: ...", mais je ne suis pas sûr de
pouvoir facilement faire une structure récursive à autant de niveaux
que je veux sans avoir à écrire tous les niveaux à la main (i.e. je
veux que le Makefile final de subdir1 et 2, tout comme les Makefile
intermédiaires de dir1, puisse être simplement un include d'un ou deux
Makefile génériques (genre $PROJECTROOT/config/Makefile), donc si je
ne vois pas très bien comment distinguer CURDIR, CURSUBDIR,
CURSUBSUBDIR etc.)...



Regarde comment sont faits les makefiles de ffmpeg.
Paul Gaborit
Le #20796141
À (at) 18 Dec 2009 19:33:59 GMT,
Nicolas George
Rémi Moyen wrote in message
Bon, déjà, vu que je n'ai pas le contrôle complet sur le projet, je
doute que je puisse modifier tous les Makefile



Bon, ben t'es mort.



Pas complètement... On peut utiliser des fichiers "timestamp" dans le
makefile principal pour contraindre l'ordre d'appel des makefiles
secondaires.

--
Paul Gaborit -
Nicolas George
Le #20796681
Paul Gaborit wrote in message
Pas complètement... On peut utiliser des fichiers "timestamp" dans le
makefile principal pour contraindre l'ordre d'appel des makefiles
secondaires.



Oui, mais non : dans ce cas, les fichiers timestamps ne dépendent pas des
fichiers dans les sous-répertoires, donc une modification à ces dépendances
n'est pas propagée.
Cyrille Lefevre
Le #20796901
Rémi Moyen a écrit :
Bonjour,

J'essaye de compiler un (gros) projet en profitant d'une machine multi-
core et donc de l'option -j de make (pour lancer plusieurs commandes
en parallèle), mais j'ai un problème de dépendances que je n'arri ve
pas à résoudre.



Bonjour,

ma boule de cristal ne fonctionne pas actuellement...
quel os ? quel make (sysv, bsd, gnu) ? quelle version ?

gnu make supporte la cible .PHONY...

http://www.gnu.org/software/make/manual/make.html#Phony-Targets

SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
foo: baz

alternative pour les make (sysv) qui ne la supporte pas

http://www.gnu.org/software/make/manual/make.html#Force-Targets

SUBDIRS = foo bar baz
subdirs: $(SUBDIRS)
$(SUBDIRS): FORCE
$(MAKE) -C $@
foo: baz
FORCE:

bsd make supporte les cibles .PHONY et .ORDER

ça doit donner qqc comme ça :

SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
.ORDER: baz foo
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@

http://www.freebsd.org/doc/en/books/pmake/index.html
http://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/index.h tml
http://mail-index.netbsd.org/netbsd-users/2008/10/11/msg002183.html
http://wiki.netbsd.se/BSD_Make
http://wiki.netbsd.se/Basic_Unix_programming#Using_BSD_Make

le bootstrap pour linux (bmake) :

http://www.netbsd.org/docs/software/packages.html#bootstrap
http://pkgsrc.se/

Cordialement,

Cyrille Lefevre.
--
mailto:Cyrille.Lefevre-news%
supprimer "%nospam% et ".invalid" pour me repondre.
Paul Gaborit
Le #20797401
À (at) 18 Dec 2009 23:08:31 GMT,
Nicolas George
Paul Gaborit wrote in message
Pas complètement... On peut utiliser des fichiers "timestamp" dans le
makefile principal pour contraindre l'ordre d'appel des makefiles
secondaires.



Oui, mais non : dans ce cas, les fichiers timestamps ne dépendent pas des
fichiers dans les sous-répertoires, donc une modification à ces dépendances
n'est pas propagée.



Et alors ? Le principe est que l'appel à un makefile secondaire est
une cible forcée. J'ai utilisé cette méthode sur plusieurs projets et
cela fonctionne très bien. En particulier lorsqu'on récupère des
modules de provenances diverses dans lesquels on ne souhaite pas
modifier les makefiles fournis.


--
Paul Gaborit -
Nicolas George
Le #20797451
Paul Gaborit wrote in message
Et alors ? Le principe est que l'appel à un makefile secondaire est
une cible forcée.



C'est quand même très contradictoire avec le principe de make. Autant
utiliser un script, dans ce cas.
Jean-Marc Bourguet
Le #20797621
Rémi Moyen
Bonjour,

J'essaye de compiler un (gros) projet en profitant d'une machine multi-
core et donc de l'option -j de make (pour lancer plusieurs commandes
en parallèle), mais j'ai un problème de dépendances que je n'arrive
pas à résoudre.

Mon projet se comporte de plusieurs répertoires avec chacun des sous-
répertoires. En gros, quand je fais 'make' à la racine, il descend
dans tous les répertoires et y refait 'make', chacun refaisant la même
chose dans ses sous-répertoires. En pratique, dans chaque répertoire,
j'ai un Makefile.inc qui contient une ligne du style 'SUBDIRS = dir1
dir2 dir3'. Ce fichier est inclus par le Makefile du répertoire et il
inclut aussi un fichier de définitions génériques qui définit une
cible pour make comme :

all:
for dir in ${SUBDIRS}; do
(cd $$dir; ${MAKE});
done;

Du coup, pour la plupart des répertoires, le Makefile est un truc
quasi-vide qui inclut le Makefile.inc et le fichier générique (qui
contient le bloc ci-dessus), et quand je modifie la structure je n'ai
qu'à changer SUBDIRS dans le Makefile.inc. Je ne sais pas si c'est la
meilleure manière de faire, mais au moins c'est facile :-)



Il y a des adversaires farouches des Makefiles récursifs, et non sans
arguments (il doit y avoir un "récursive Makefiles considered harmfull"
quelque part), mais l'usage est commun et en pratique viable si pas sans
inconvénients.

Mon problème vient du fait que, dans certains cas, certains
répertoires doivent être compilés avant d'autres (par exemple, dir1
avant dir2). Avec un make "normal" (i.e. sans -j), il n'y a qu'une
seule commande exécutée à la fois, il me suffit donc de mettre les
répertoires dans le bon ordre dans SUBDIRS.
Par contre, dès que j'utilise -j, make lance plusieurs commandes à la
fois et en particulier, il va commencer dir2 avant que dir1 soit fini.
Ce qui ne marche pas.




Je crains que tu ne décrives mal ton problème. Avec

# Makefile
SUBDIRS=suba subb

all:
for dir in ${SUBDIRS} ; do
(cd $$dir; ${MAKE});
done

# suba/Makefile
all:
sleep 20
echo suba done

# subb/Makefile
all:
echo subb done

on ne commence l'exécution du subb/Makefile que quand celle de
suba/Makefile est terminée, même avec -j100. Je ne vois pas comment ça
pourrait être autrement et qu'un seul appel au shell se mette à
paralléliser des commandes.

A+

--
Jean-Marc
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Jean-Marc Bourguet
Le #20797721
Cyrille Lefevre
Rémi Moyen a écrit :
Bonjour,

J'essaye de compiler un (gros) projet en profitant d'une machine multi-
core et donc de l'option -j de make (pour lancer plusieurs commandes
en parallèle), mais j'ai un problème de dépendances que je n'arrive
pas à résoudre.



Bonjour,

ma boule de cristal ne fonctionne pas actuellement... quel os ? quel
make (sysv, bsd, gnu) ? quelle version ?



Quelque soit la version, je n'en connais pas qui ont le problème décrit
avec la structure de Makefile indiquée. (Avec celle que tu donnes, oui).

gnu make supporte la cible .PHONY...

http://www.gnu.org/software/make/manual/make.html#Phony-Targets

SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
foo: baz



Cette méthode a l'inconvénient d'introduire une dépendance de foo sur baz,
donc l'impossibilité de faire foo sans faire baz. Gnu make a les
ORDER-ONLY-PREREQUISITES qui règle ce problème (bsd make le règle avec
.ORDER d'après ce que tu écris plus bas)

foo : | baz

A+

--
Jean-Marc
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Publicité
Poster une réponse
Anonyme