Comment forcer un makefile à reconstruire une cible


192

J'ai un makefile qui construit puis appelle un autre makefile. Puisque ce makefile appelle plus de makefiles qui font le travail, il ne change pas vraiment. Ainsi, il continue de penser que le projet est construit et à jour.

dnetdev11 ~ # make
make: `release' is up to date.

Comment forcer le makefile à reconstruire la cible?

clean = $(MAKE) -f ~/xxx/xxx_compile.workspace.mak clean


build = svn up ~/xxx                                                       \
        $(clean)                                                                \
        ~/cbp2mak/cbp2mak -C ~/xxx ~/xxx/xxx_compile.workspace        \
        $(MAKE) -f ~/xxx/xxx_compile.workspace.mak $(1)                    \


release:
        $(build )

debug:
        $(build DEBUG=1)

clean:
        $(clean)

install:
        cp ~/xxx/source/xxx_utility/release/xxx_util /usr/local/bin
        cp ~/xxx/source/xxx_utility/release/xxxcore.so /usr/local/lib

Remarque: les noms ont été supprimés pour protéger les innocents

Edit: Version finale fixe:

clean = $(MAKE) -f xxx_compile.workspace.mak clean;


build = svn up;                                         \
        $(clean)                                        \
        ./cbp2mak/cbp2mak -C . xxx_compile.workspace;   \
        $(MAKE) -f xxx_compile.workspace.mak    $(1);   \


.PHONY: release debug clean install

release:
        $(call build,)

debug:
        $(call build,DEBUG=1)

clean:
        $(clean)

install:
        cp ./source/xxx_utillity/release/xxx_util /usr/bin
        cp ./dlls/Release/xxxcore.so /usr/lib

Lodle, étant donné qu'il s'agit d'une question fréquemment visitée, souhaitez-vous la modifier pour qu'elle soit plus moderne? (Il semble que ce .PHONYn'était pas votre seul problème, et vous n'êtes pas vraiment censé modifier la solution dans la question, ou du moins plus maintenant.)
Keith M

Réponses:


23

Vous pouvez déclarer qu'une ou plusieurs de vos cibles sont fausses .

Une cible bidon est une cible qui n'est pas vraiment le nom d'un fichier; il s'agit plutôt d'un nom pour une recette à exécuter lorsque vous faites une demande explicite. Il y a deux raisons d'utiliser une cible bidon: pour éviter un conflit avec un fichier du même nom, et pour améliorer les performances.

...

Une cible bidon ne doit pas être une condition préalable à un vrai fichier cible; si c'est le cas, sa recette sera exécutée chaque fois que make va mettre à jour ce fichier. Tant qu'une cible bidon n'est jamais une condition préalable d'une cible réelle, la recette cible bidon ne sera exécutée que lorsque la cible bidon est un objectif spécifié


75
Cette réponse, bien qu'elle soit "acceptée" et fortement "votée", est vraiment hors de propos. Premièrement, il dit «déclarer les cibles comme étant fausses», mais ensuite «la cible bidon n'est pas vraiment le nom d'un fichier». Eh bien, si votre cible est un fichier, c'est alors une contradiction dans la réponse. Deuxièmement, il dit que "cible bidon ne devrait pas être une condition préalable à un réel" - eh bien, et si c'est le cas? La question initiale, ne précisait pas si elle est ou non. La bonne réponse est de ne pas déclarer vos cibles fausses, mais plutôt de déclarer une cible bidon supplémentaire, puis de dépendre des cibles que vous voulez reconstruire.
Mark Galeck

2
@MarkGaleck. Quand la réponse indique que "Une cible bidon est une cible qui n'est pas vraiment le nom d'un fichier", elle cite directement du manuel de gcc make. C'est parfaitement correct.
drlolly

"Cible" est un terme Make qui fait référence au texte à gauche d'un signe deux-points :, pas seulement au résultat final que vous voulez créer (par exemple votre fichier binaire). Dans la question, release, debug, cleanet installsont les cibles, et non Make xxx_utilou xxxcore.soou quoi que ce soit d' autre.
Keith M

745

Le -Bcommutateur à faire, dont la forme longue est --always-make, indique makede ne pas tenir compte des horodatages et de créer les cibles spécifiées. Cela peut aller à l'encontre de l'objectif de l'utilisation de make, mais c'est peut-être ce dont vous avez besoin.


5
@MarkKCowan Je suis entièrement d'accord! Cette option est exactement ce que je recherchais, pas une solution de contournement comme le suggère Dave.
Maarten Bamelis

8
La mise en garde avec cette approche est qu'elle construit trop de choses. Plus précisément avec les autotools, je l'ai vu réexécuter configure .. Je souhaite qu'une solution basée sur LD_PRELOAD puisse être construite !!
vrdhn

oui, et il peut même réécrire des fichiers que vous ne vouliez pas! telles que les bibliothèques système globales qui apparaissent dans les dépendances et sont reconstruites et écrasées ...
Julio Guerra

CAVEAT: Cela fera ce qu'il dit (LOL): reconstruisez votre cible à partir de zéro en ignorant tous les horodatages. Donc, si vous voulez simplement reconstruire la toute dernière étape d'une longue chaîne (par exemple, pour tester une nouvelle partie d'un flux de travail), alors un temporaire .PHONYpourrait être plus pratique.
Leo

18

Une astuce qui était autrefois documentée dans un manuel Sun pour makeest d'utiliser une cible (inexistante) '.FORCE'. Vous pouvez le faire en créant un fichier, force.mk, qui contient:

.FORCE:
$(FORCE_DEPS): .FORCE

Ensuite, en supposant que votre makefile existant est appelé makefile, vous pouvez exécuter:

make FORCE_DEPS=release -f force.mk -f makefile release

Puisqu'il .FORCEn'existe pas, tout ce qui en dépend sera obsolète et reconstruit.

Tout cela fonctionnera avec n'importe quelle version de make; sous Linux, vous avez GNU Make et pouvez donc utiliser la cible .PHONY comme indiqué.

Il convient également makede se demander pourquoi la publication est à jour. Cela peut être dû au fait que vous avez une touch releasecommande parmi les commandes exécutées; cela peut être dû au fait qu'il existe un fichier ou un répertoire appelé «release» qui existe et qui n'a pas de dépendances et qui est donc à jour. Ensuite, il y a la vraie raison ...


15

Quelqu'un d'autre a suggéré .PHONY, ce qui est tout à fait correct. .PHONY doit être utilisé pour toute règle pour laquelle une comparaison de date entre l'entrée et la sortie n'est pas valide. Puisque vous n'avez aucune cible du formulaire, output: inputvous devez utiliser .PHONY pour TOUS!

Cela dit, vous devriez probablement définir quelques variables en haut de votre makefile pour les différents noms de fichiers, et définir de vraies règles make qui ont à la fois des sections d'entrée et de sortie afin que vous puissiez utiliser les avantages de make, à savoir que vous ne compilerez réellement les choses qui sont nécessaires pour faire face!

Edit: exemple ajouté. Non testé, mais voici comment vous faites .PHONY

.PHONY: clean    
clean:
    $(clean)

1
Eh bien, si vous pouviez me montrer un exemple, ce serait bien.
Je suis

1
L'emplacement de la .PHONYcible n'a pas d'importance. Cela peut être n'importe où dans le Makefile.
Adrian W

6

Cette technique simple permettra au makefile de fonctionner normalement lorsque le forçage n'est pas souhaité. Créez une nouvelle cible appelée force à la fin de votre makefile . La cible de force touchera un fichier dont dépend votre cible par défaut. Dans l'exemple ci-dessous, j'ai ajouté touch myprogram.cpp . J'ai également ajouté un appel récursif à faire . Cela entraînera la création de la cible par défaut à chaque fois que vous tapez make force .

yourProgram: yourProgram.cpp
       g++ -o yourProgram yourProgram.cpp 

force:
       touch yourProgram.cpp
       make

Vous ne devez jamais utiliser makedans un Makefile. Utilisez $(MAKE)plutôt.
Benjamin Crawford Ctrl-Alt-Tut

5

Si je me souviens bien, «make» utilise des horodatages (heure de modification du fichier) pour déterminer si une cible est à jour ou non. Une manière courante de forcer une reconstruction est de mettre à jour cet horodatage à l'aide de la commande «touch». Vous pouvez essayer d'appeler 'touch' dans votre makefile pour mettre à jour l'horodatage de l'une des cibles (peut-être l'un de ces sous-makefiles), ce qui pourrait forcer Make à exécuter cette commande.


3

J'ai essayé ça et ça a marché pour moi

ajoutez ces lignes à Makefile

clean:
    rm *.o output

new: clean
    $(MAKE)     #use variable $(MAKE) instead of make to get recursive make calls

enregistrer et appeler maintenant

make new 

et il recompilera tout à nouveau

Qu'est-il arrivé?

1) «nouveaux» appels propres. «clean» fait «rm» qui supprime tous les fichiers objets qui ont l'extension «.o».

2) «nouveaux» appels «faire». 'faire' voir qu'il n'y a pas de fichiers '.o', donc il crée à nouveau tous les '.o'. puis l'éditeur de liens relie tout le fichier .o à une sortie exécutable

Bonne chance


1
En recette pour un newmeilleur usage $(MAKE)quemake
Basile Starynkevitch

1

Selon Miller's Recursive Make Considered Harmful, vous devriez éviter d'appeler $(MAKE)! Dans le cas que vous montrez, c'est inoffensif, car ce n'est pas vraiment un makefile, juste un script wrapper, qui aurait tout aussi bien pu être écrit dans Shell. Mais vous dites que vous continuez comme ça à des niveaux de récursion plus profonds, donc vous avez probablement rencontré les problèmes indiqués dans cet essai révélateur.

Bien sûr, avec GNU, c'est difficile à éviter. Et même s'ils sont conscients de ce problème, c'est leur façon documentée de faire les choses.

OTOH , makepp été créé pour résoudre ce problème. Vous pouvez écrire vos makefiles au niveau de chaque répertoire, mais ils sont tous rassemblés dans une vue complète de votre projet.

Mais les makefiles hérités sont écrits de manière récursive. Il existe donc une solution de contournement où $(MAKE)ne fait rien d'autre que canaliser les sous-requêtes vers le processus makepp principal. Seulement si vous faites des choses redondantes ou, pire, contradictoires entre vos soumissions, vous devez demander --traditional-recursive-make(ce qui brise bien sûr cet avantage de makepp). Je ne connais pas vos autres makefiles, mais s'ils sont écrits proprement, avec makepp, les reconstructions nécessaires devraient se produire automatiquement, sans avoir besoin de hacks suggérés ici par d'autres.


N'a pas répondu à la question: tangente au point principal et devrait être un commentaire pas une réponse.
flungo

Peut-être n'ai-je pas été assez clair. Avec makepp, tout ce fichier makefile n'est pas nécessaire. En connaissant les dépendances exactes (toutes, pas seulement ce qui est répertorié après le :), il sera toujours reconstruit si nécessaire.
Daniel

1

Cela a déjà été mentionné, mais je pensais pouvoir ajouter à l'utilisation touch

Si vous avez touchtous les fichiers source à compiler, la touchcommande modifie les horodatages d'un fichier à l'heure système à laquelle la touchcommande a été exécutée.

L'horodatage du fichier source est ce qui permet makede "savoir" qu'un fichier a changé et doit être recompilé

Par exemple: si le projet était un projet C ++, alors faites touch *.cpp, puis réexécutez makeet make devrait recompiler l'ensemble du projet.


1

Si vous n'avez pas besoin de conserver l'une des sorties que vous avez déjà compilées avec succès

nmake /A 

reconstruit tout


0

Cela dépend en fait de la cible. S'il s'agit d'une cible bidon (c'est-à-dire que la cible n'est PAS liée à un fichier), vous devez la déclarer comme .PHONY.

Si toutefois la cible n'est pas une cible bidon mais que vous voulez simplement la reconstruire pour une raison quelconque (un exemple est lorsque vous utilisez la macro de prétraitement __TIME__), vous devez utiliser le schéma FORCE décrit dans les réponses ici.



0

Comme l'a souligné Abernier, il existe une solution recommandée dans le manuel GNU make, qui utilise une `` fausse '' cible pour forcer la reconstruction d'une cible:

clean: FORCE
        rm $(objects)
FORCE: ; 

Cela fonctionnera propre, quelles que soient les autres dépendances.

J'ai ajouté le point-virgule à la solution du manuel, sinon une ligne vide est requise.


-1

Sur mon système Linux (Centos 6.2), il y a une différence significative entre déclarer la cible .PHONY et créer une fausse dépendance sur FORCE, lorsque la règle crée en fait un fichier correspondant à la cible. Lorsque le fichier doit être régénéré à chaque fois, il a fallu à la fois la fausse dépendance FORCE sur le fichier et .PHONY pour la fausse dépendance.

faux:

date > $@

droite:

FORCE
    date > $@
FORCE:
    .PHONY: FORCE

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.