GNU make: Génération automatique des dépendances avec les fichiers d'en-tête généré
J'ai donc suivi la Avancé D'Auto-Dépendance Génération papier --
Makefile:
SRCS := main.c foo.c
main: main.o foo.o
%.o: %.c
$(CC) -MMD -MG -MT '$@ $*.d' -c $< -o $@
cp $*.d $*.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\$$;;' \
-e '/^$$/d' -e 's;$$; :;' < $*.tmp >> $*.d
rm $*.tmp
clean::
-rm *.o *.d main
-include $(SRCS:.c=.d)
principal.c:
#include "foo.h"
int main(int argc, char** argv) {
foo() ;
return 0 ;
}
foo.h:
#ifndef __FOO_H__
#define __FOO_H__
void foo() ;
#endif
-- et il fonctionne comme un charme.
Mais quand foo.h
devient un fichier généré --
Makefile:
...
HDRS := foo.h
$(HDRS):
mk_header.sh $*
clean::
-rm $(HDRS)
...
mk_header.sh:
#!/bin/bash
UP=$(tr "[:lower:]" "[:upper:]" <<< $1)
cat <<EOF > $1.h
#ifndef __${UP}_H__
#define __${UP}_H__
void $1() ;
#endif
EOF
La 1ère fois que je lance make
, main.d
est pas encore produit, et donc foo.h
n'est pas considérée comme une condition préalable, et donc n'est pas généré:
$ ls
foo.c main.c Makefile mk_header.sh*
$ make
cc -MMD -MG -MT 'main.o main.d' -c main.c -o main.o
cp main.d main.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\$;;' \
-e '/^$/d' -e 's;$; :;' < main.tmp >> main.d
rm main.tmp
cc -MMD -MG -MT 'foo.o foo.d' -c foo.c -o foo.o
cp foo.d foo.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\$;;' \
-e '/^$/d' -e 's;$; :;' < foo.tmp >> foo.d
rm foo.tmp
cc main.o foo.o -o main
$ ls
foo.c foo.d foo.o
main* main.c main.d main.o
Makefile mk_header.sh*
Seulement dans le 2ème invocation de make
, le foo.h
est généré, et comme un résultat d'une autre de construire des cascades.
$ make
./mk_header.sh foo
cc -MMD -MG -MT 'main.o main.d' -c main.c -o main.o
cp main.d main.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\$;;' \
-e '/^$/d' -e 's;$; :;' < main.tmp >> main.d
rm main.tmp
cc main.o foo.o -o main
$ ls
foo.c foo.d foo.h foo.o
main* main.c main.d main.o
Makefile mk_header.sh*
Et seulement après que make
se rend compte que:
$ make
make: `main' is up to date.
Donc ma question est: Est-il un moyen d'étendre la recette suggérée par le document ci-dessus, pour permettre d'en-tête généré des fichiers, sans l'élimination de la performance gain réalisé par ne pas avoir à ré-évaluer l'ensemble de la font de l'arbre lors de l'inclusion de la *.d
fragments?
OriginalL'auteur Chen Levy | 2011-03-08
Vous devez vous connecter pour publier un commentaire.
Le problème est que le
*.d
Makefile-fragments de génération doit être effectuée après toutes les en-têtes de génération est terminée. Mettre les choses de cette façon, on peut utiliser les dépendances de force de l'ordre:Notes:
- Je utiliser un commande-la seule dépendance.
Cette solution est assez évolutif: Chaque générer l'en-tête de la règle, ne doit être une condition préalable de la
generated_headers
.PHONY
cible. En supposant que l'en-tête de la génération de la règle est écrite correctement, une fois qu'il a été correctement généré, à satisfaire lesgenerated_headers
cible doit être un no-op.On ne peut pas compiler un seul objet, même si l'objet ne nécessite pas généré les en-têtes, sans générer de tous générés en-têtes du premier projet. Bien que ce soit techniquement solides, vos développeurs se plaignent.
De sorte que vous devriez penser à avoir un
FAST_AND_LOOSE
drapeau, qui permettra de désactiver cette fonction:Ainsi, un développeur peut émettre:
OriginalL'auteur Chen Levy
Réponse courte: non. La recette décrite dans le document est très intelligent, un de mes favoris, mais c'est une utilisation sophistiquée de brut de l'outil. Il faut profiter de l'habituel système dans lequel tous les en-têtes nécessaires existent pas; ce qu'il tente de résoudre est le problème de la détermination dont les en-têtes, si récemment modifié, exiger le fichier de l'objet à reconstruire. En particulier, si l'objet fichier n'existe pas, alors il doit être reconstruit-- et dans ce cas il n'y a pas de raison de s'inquiéter sur les fichiers d'en-tête, car le compilateur va sûrement trouver.
Maintenant fichiers d'en-tête sont générés. Donc
foo.h
peut ne pas exister, de sorte que quelqu'un aura à exécuter le script pour générer et Faire seulement peut le faire. Mais ne peuvent pas savoir quefoo.h
est nécessaire, sans procéder à une analyse demain.c
. Mais cela ne peut pas arriver jusqu'à ce que commence à Faire exécutermain
-règles liées au e.gmain.o
oumain.o.d
), il ne peut exécuter jusqu'à ce que après il a décidé des objectifs qu'il va construire.Donc, nous aurons à utiliser... récursive faire! [Dun-dun-dunnnn!]
Nous ne pouvons pas atteindre le papier de l'objectif d'éviter reinvocation de Faire, mais on peut au moins éviter de (certains) ne sont plus nécessaires à la reconstruction. Vous pourriez faire quelque chose comme la "Base de l'Auto-Dépendances" décrites dans l'article; l'article décrit les problèmes de cette approche. Ou vous pouvez utiliser une commande comme celle de la "Avancé" de la recette pour générer une liste d'en-têtes, passer ensuite que
$(MAKE)
; cette approche est en ordre, mais peut Faire appel à plusieurs reprises sur le même en-tête, selon ce que votre arbre de code ressemble.OriginalL'auteur Beta
Le makefile dans la question d'origine ne fonctionne pas pour moi avec gcc 4.8.2:
Je suppose que gcc changé le comportement de
-MG
à un certain point dans les 4 dernières années.Il semble que si vous voulez soutenir généré des fichiers d'en-tête, il n'est plus
de toute façon à générer le ".d" et le ".o" fichier dans le même temps, sans
invoquant le préprocesseur C deux fois.
J'ai donc mis à jour la recette:
(Note également que la gcc a maintenant
-MP
de générer de fausses cibles pour chaque en-tête,donc, vous n'avez plus besoin sed sur gcc est sortie.)
Nous avons toujours le même problème que la question d'origine: la gestion des
make
l'première fois ne parvient pas à générer
foo.h
:De l'exécuter à nouveau œuvres:
Puisque nous avons à exécuter le préprocesseur C deux fois de toute façon, nous allons générer le
.d
fichier dans une autre règle:
Maintenant, il génère le fichier d'en-tête correctement:
Est-ce à souffrir du problème de performance que la question initiale a été
en essayant d'éviter? C'est essentiellement la Base de l'Auto-Dépendances" solution
décrit dans la Avancé D'Auto-Dépendance
Génération papier.
Que le papier revendications 3 problèmes avec cette solution:
make
si rien ne change.si la mention de celui-ci est retiré de la .c fichier.
Problème 2 est résolu en utilisant la
-include
au lieu deinclude
. Aussi loin que je peuxdites, c'est orthogonale à la livre la technique pour éviter la re-exec de
make
. Au moins, je n'ai pas été capable de causer des problèmes à l'aide de-include
au lieu de
include
.3 problème est résolu par la GCC est
-MP
(ou l'équivalent script sed) -- c'estaussi orthogonale à la technique pour éviter la re-exec de
make
.Problème 1 peut être peut-être quelque peu atténuées par quelque chose comme ceci:
Avant que le changement:
Après ce changement:
Un peu mieux. Bien sûr, si une nouvelle dépendance est introduit,
make
va encorebesoin de ré-exécuter. Peut-être il n'y a rien qui peut être fait pour améliorer cette situation; il semble être un compromis entre l'exactitude et la vitesse.
Tous les ci-dessus a été testé avec faire est de 3,81.
OriginalL'auteur David Röthlisberger
Vous pouvez créer une dépendance explicite de la règle de pour votre en-tête généré:
Si l'en-tête généré est directement incluse dans un petit nombre de fichiers, cela peut être une approche pratique.
OriginalL'auteur bdonlan