J'essaye de faire ce qui suit. Il existe un programme, appelez-le foo-bin
, qui prend un seul fichier d'entrée et génère deux fichiers de sortie. Une règle Makefile stupide pour cela serait:
file-a.out file-b.out: input.in
foo-bin input.in file-a.out file-b.out
Cependant, cela n'indique make
en aucun cas que les deux cibles seront générées simultanément. C'est bien lors de l'exécution make
en série, mais cela causera probablement des problèmes si l'on essaie make -j16
ou quelque chose d'aussi fou.
La question est de savoir s'il existe un moyen d'écrire une règle Makefile appropriée pour un tel cas? Clairement, cela générerait un DAG, mais d'une manière ou d'une autre, le manuel de fabrication de GNU ne spécifie pas comment ce cas pourrait être traité.
Exécuter le même code deux fois et générer un seul résultat est hors de question, car le calcul prend du temps (pensez: heures). La sortie d'un seul fichier serait également assez difficile, car il est fréquemment utilisé comme entrée dans GNUPLOT qui ne sait pas gérer seulement une fraction d'un fichier de données.
Réponses:
Je le résoudrais comme suit:
Dans ce cas, la création parallèle «sérialisera» la création de a et b mais puisque la création de b ne fait rien, cela ne prend pas de temps.
la source
file-b.out
été créé comme indiqué, puis mystérieusement supprimé,make
il sera impossible de le recréer car ilfile-a.out
sera toujours présent. Des pensées?file-a.out
la solution ci-dessus fonctionne bien. Cela suggère un hack: quand un seul de a ou b existe, alors les dépendances doivent être ordonnées de telle sorte que le fichier manquant apparaisse comme sortie deinput.in
. Quelques$(widcard...)
s,$(filter...)
s,$(filter-out...)
s etc. devraient faire l'affaire. Pouah.foo-bin
créera évidemment l'un des fichiers en premier (en utilisant une résolution de microseconde), vous devez donc vous assurer que vous avez le bon ordre des dépendances lorsque les deux fichiers existent.touch file-a.out
comme deuxième commande après l'foo-bin
invocation.touch file-b.out
comme deuxième commande dans la première règle et dupliquez les commandes dans la deuxième règle. Si l'un des fichiers est manquant ou plus ancien queinput.in
, la commande ne sera exécutée qu'une seule fois et régénérera les deux fichiers avecfile-b.out
plus récent quefile-a.out
.L'astuce consiste à utiliser une règle de modèle avec plusieurs cibles. Dans ce cas, make supposera que les deux cibles sont créées par une seule invocation de la commande.
Cette différence d'interprétation entre les règles de modèle et les règles normales n'a pas vraiment de sens, mais elle est utile pour des cas comme celui-ci, et elle est documentée dans le manuel.
Cette astuce peut être utilisée pour n'importe quel nombre de fichiers de sortie tant que leurs noms ont une sous-chaîne commune à laquelle le
%
doit correspondre. (Dans ce cas, la sous-chaîne commune est ".")la source
make
exécutera la même commande deux fois simultanément.foo%in bar%src: input.in
:-)$(subst .,%,$(varA) $(varB)): input.in
(testé avec GNU make 4.1).Make n'a aucun moyen intuitif de le faire, mais il existe deux solutions de contournement décentes.
Premièrement, si les cibles impliquées ont une racine commune, vous pouvez utiliser une règle de préfixe (avec GNU make). Autrement dit, si vous souhaitez corriger la règle suivante:
Vous pouvez l'écrire de cette façon:
(En utilisant la variable de règle de modèle $ *, qui correspond à la partie correspondante du modèle)
Si vous voulez être portable vers des implémentations non-GNU Make ou si vos fichiers ne peuvent pas être nommés pour correspondre à une règle de modèle, il existe un autre moyen:
Cela indique à make que input.in.intermediate n'existera pas avant que make ne soit exécuté, donc son absence (ou son horodatage) ne provoquera pas une exécution erronée de foo-bin. Et que file-a.out ou file-b.out ou les deux soient obsolètes (par rapport à input.in), foo-bin ne sera exécuté qu'une seule fois. Vous pouvez utiliser .SECONDARY au lieu de .INTERMEDIATE, ce qui demandera à make NOT de supprimer un nom de fichier hypothétique input.in.intermediate. Cette méthode est également sûre pour les builds parallèles.
Le point-virgule sur la première ligne est important. Cela crée une recette vide pour cette règle, de sorte que Make sache que nous allons vraiment mettre à jour file-a.out et file-b.out (merci @siulkiulki et d'autres qui l'ont signalé)
la source
-j1
versions). De plus, si le makefile est interrompu et qu'il laisse leinput.in.intermediate
fichier autour, il devient vraiment confus.file-a.out file-b.out: input.in.intermediate
àfile-a.out file-b.out: input.in.intermediate ;
Voir stackoverflow.com/questions/37873522/… pour plus de détails.Ceci est basé sur la deuxième réponse de @ deemer qui ne repose pas sur des règles de modèle, et corrige un problème que je rencontrais avec les utilisations imbriquées de la solution de contournement.
J'aurais ajouté ceci en commentaire à la réponse de @ deemer, mais je ne peux pas car je viens de créer ce compte et je n'ai aucune réputation.
Explication: La recette vide est nécessaire pour permettre à Make de faire la comptabilité appropriée pour marquer
file-a.out
etfile-b.out
comme ayant été reconstruite. Si vous avez encore une autre cible intermédiaire qui dépend defile-a.out
, Make choisira de ne pas construire l'intermédiaire externe, en affirmant:la source
Après GNU Make 4.3 (19 janvier 2020), vous pouvez utiliser des «cibles explicites groupées». Remplacez
:
par&:
.Le fichier NEWS de GNU Make dit:
la source
Voilà comment je fais. Premièrement, je sépare toujours les pré-demandes des recettes. Puis dans ce cas une nouvelle cible pour faire la recette.
La première fois:
1. Nous essayons de construire file-a.out, mais dummy-a-and-b.out doit d'abord le faire, alors make lance la recette dummy-a-and-b.out.
2. Nous essayons de construire file-b.out, dummy-a-and-b.out est à jour.
La deuxième fois et les suivantes:
1. Nous essayons de construire file-a.out: examinez les prérequis, les prérequis normaux sont à jour, les prérequis secondaires sont manquants donc ignorés.
2. Nous essayons de construire file-b.out: examinez les prérequis, les prérequis normaux sont à jour, les prérequis secondaires sont manquants donc ignorés.
la source
file-b.out
make n'a aucun moyen de le recréer.Comme extension de la réponse de @ deemer, je l'ai généralisée en une fonction.
Panne:
Supprimez tous les espaces de début / fin du premier argument
Créez une cible variable avec une valeur unique à utiliser comme cible intermédiaire. Dans ce cas, la valeur sera .inter.file-a.out_file-b.out.
Créez une recette vide pour les sorties avec la cible unique comme dépendance.
Déclarez la cible unique comme intermédiaire.
Terminez par une référence à la cible unique pour que cette fonction puisse être utilisée directement dans une recette.
Notez également que l'utilisation de eval ici est due au fait que eval se développe en rien et que l'expansion complète de la fonction est juste la cible unique.
Doit donner du crédit aux règles atomiques dans GNU Make dont cette fonction est inspirée.
la source
Pour empêcher l'exécution multiple parallèle d'une règle avec plusieurs sorties avec
make -jN
, utilisez.NOTPARALLEL: outputs
. Dans ton cas :la source