Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Makefile récursif et -j

16 réponses
Avatar
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=E8le), mais j'ai un probl=E8me de d=E9pendances que je n'arrive
pas =E0 r=E9soudre.

Mon projet se comporte de plusieurs r=E9pertoires avec chacun des sous-
r=E9pertoires. En gros, quand je fais 'make' =E0 la racine, il descend
dans tous les r=E9pertoires et y refait 'make', chacun refaisant la m=EAme
chose dans ses sous-r=E9pertoires. En pratique, dans chaque r=E9pertoire,
j'ai un Makefile.inc qui contient une ligne du style 'SUBDIRS =3D dir1
dir2 dir3'. Ce fichier est inclus par le Makefile du r=E9pertoire et il
inclut aussi un fichier de d=E9finitions g=E9n=E9riques qui d=E9finit une
cible pour make comme :

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

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

Mon probl=E8me vient du fait que, dans certains cas, certains
r=E9pertoires doivent =EAtre compil=E9s 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=E9cut=E9e =E0 la fois, il me suffit donc de mettre les
r=E9pertoires dans le bon ordre dans SUBDIRS.

Par contre, d=E8s que j'utilise -j, make lance plusieurs commandes =E0 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=E9cifier un ordre de d=E9pendance ? Si
dir1 et dir2 =E9taient des cibles de make, alors je pourrais dire que
dir2 d=E9pend de dir1, mais en l'occurrence, ce ne sont qu'une variable
qui contient les deux... Je ne vois pas de mani=E8re facile de faire =E7a.

Bon, =E7a n'est pas un syst=E8me que j'ai cr=E9=E9, et il y a peut-=EAtre u=
ne
meilleure mani=E8re de faire =E7a, mais je n'ai qu'un contr=F4le tr=E8s lim=
it=E9
sur ces Makefile, donc je suis oblig=E9 de rester dans ce cadre-l=E0.
J'utilise GNU Make 3.80, d=E8s fois que =E7a ait une importance.

Est-ce que quelqu'un a une id=E9e ?

Merci d'avance !
--
R=E9mi Moyen

10 réponses

1 2
Avatar
Nicolas George
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.
Avatar
Rémi Moyen
On Dec 18, 5:08 pm, Nicolas George <nicolas$ wrote:

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
Avatar
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.

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.
Avatar
Paul Gaborit
À (at) 18 Dec 2009 19:33:59 GMT,
Nicolas George <nicolas$ écrivait (wrote):

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 - <http://perso.mines-albi.fr/~gaborit/>
Avatar
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.
Avatar
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'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.
Avatar
Paul Gaborit
À (at) 18 Dec 2009 23:08:31 GMT,
Nicolas George <nicolas$ écrivait (wrote):

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 - <http://perso.mines-albi.fr/~gaborit/>
Avatar
Nicolas George
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.
Avatar
Jean-Marc Bourguet
Rémi Moyen writes:

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
Avatar
Jean-Marc Bourguet
Cyrille Lefevre <cyrille.lefevre-news% writes:

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
1 2