Que signifient les symboles makefile $ @ et $ <?

416
CC=g++
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=main.cpp hello.cpp factorial.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=hello

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(LDFLAGS) $(OBJECTS) -o $@

.cpp.o:
    $(CC) $(CFLAGS) $< -o $@

Que font $@et $<font exactement?

Mohit Deshpande
la source
5
Le lien ci-dessus est cassé, en voici un autre: gnu.org/software/make/manual/html_node/Automatic-Variables.html
asciz
1
Bonjour, que signifie ce ".cpp.o:" comme cible? (avant dernière ligne?).
pseudonym_127
3
Le ".cpp.o:" signifie construire ".o" (fichiers objets) à partir de ".cpp" (fichiers source)
jaguzu
1
Je pense qu'il convient de noter qu'il existe un tutoriel make sur le lien suivant à partir duquel je pense que Mohit a obtenu le makefile dans son message. mrbook.org/blog/tutorials/make
DeepDeadpool
Microsoft l'appelle Macros de nom de fichier (pour NMAKE) qui est plus claire que les variables automatiques (pour MAKE). Il est utile de voir les deux côtés à des fins éducatives.
Ivanzinho

Réponses:

502

$@est le nom du fichier généré et $<le premier prérequis (généralement le fichier source). Vous pouvez trouver une liste de toutes ces variables spéciales dans le manuel GNU Make .

Par exemple, considérez la déclaration suivante:

all: library.cpp main.cpp

Dans ce cas:

  • $@ évalue à all
  • $< évalue à library.cpp
  • $^ évalue à library.cpp main.cpp
bdonlan
la source
16
Il convient de noter que $@cela ne doit pas nécessairement être un fichier, il peut également s'agir du nom d'une .PHONYcible.
Ephemera
Puis-je ajouter aux options de ligne de commande ceci: $@spour générer une sortie d'assembly telle que name.os?
huseyin tugrul buyukisik
4
Attention lorsque la première dépendance est une variable représentant une liste, $ <est évalué après avoir été développé. Ainsi, lorsque LIST = lib1.cpp lib2.cpp, et tout: $ {LIST} main.cpp, $ <est évalué à juste lib1.cpp. Il y a quelques années, j'avais passé du temps à comprendre ce qui se passait dans le résultat causé par ce comportement.
Chan Kim
En général, $ @ fait référence au nom cible qui se trouve à gauche du:
Deepak Kiran
78

Les $@et $<sont appelés variables automatiques . La variable $@représente le nom du fichier créé (c'est-à-dire la cible) et $<représente la première condition préalable requise pour créer le fichier de sortie.
Par exemple:

hello.o: hello.c hello.h
         gcc -c $< -o $@

Voici hello.ole fichier de sortie. C'est ce qui se $@développe. La première dépendance est hello.c. C'est ce qui se $<développe.

Le -cdrapeau génère le .ofichier; voir man gccpour une explication plus détaillée. Le -ospécifie le fichier de sortie à créer.

Pour plus de détails, vous pouvez lire cet article sur les Makefiles Linux .

Vous pouvez également consulter les manuels GNU make . Cela facilitera la création de Makefiles et leur débogage.

Si vous exécutez cette commande, elle affichera la base de données makefile:

make -p 
agile
la source
1
Votre réponse semble $<s'étendre à hello.c hello.h(les deux). Précisez s'il vous plaît.
Dr Beco
Oui, il comprendra à la fois hello.c et hello.h
adroite
19
$<n'est que le premier élément. Pour tout inclure, utilisez $^.
Dr Beco
1
Le Dr Beco a raison. L'auteur devrait modifier sa réponse.
PT Huynh
67

Extrait de Gestion de projets avec GNU Make, 3e édition, p. 16 (il est sous licence de documentation gratuite GNU ):

Les variables automatiques sont définies par makeaprès qu'une règle a été mise en correspondance. Ils permettent d'accéder aux éléments de la liste cible et des listes de prérequis afin que vous n'ayez à spécifier explicitement aucun nom de fichier. Ils sont très utiles pour éviter la duplication de code, mais sont essentiels lors de la définition de règles de modèle plus générales.

Il existe sept variables automatiques «essentielles»:

  • $@: Le nom de fichier représentant la cible.

  • $%: L'élément de nom de fichier d'une spécification de membre d'archive.

  • $<: Nom de fichier du premier prérequis.

  • $?: Les noms de tous les prérequis plus récents que la cible, séparés par des espaces.

  • $^: Les noms de fichiers de toutes les conditions préalables, séparés par des espaces. Cette liste a supprimé les noms de fichiers en double car pour la plupart des utilisations, telles que la compilation, la copie, etc., les doublons ne sont pas nécessaires.

  • $+: Similaire à $^, il s'agit des noms de tous les prérequis séparés par des espaces, sauf ceux qui $+incluent les doublons. Cette variable a été créée pour des situations spécifiques telles que les arguments aux éditeurs de liens où les valeurs en double ont une signification.

  • $*: La racine du nom de fichier cible. Une racine est généralement un nom de fichier sans son suffixe. Son utilisation en dehors des règles de modèle est déconseillée.

De plus, chacune des variables ci-dessus a deux variantes pour la compatibilité avec d'autres marques. Une variante renvoie uniquement la partie répertoire de la valeur. Ceci est indiqué par l' ajout d' un « D » au symbole, $(@D), $(<D), etc. Les autres déclarations variant seulement la partie du fichier de la valeur. Ceci est indiqué en ajoutant un « F » au symbole, $(@F), $(<F), etc. Notez que ces noms de variantes sont plus d'un caractère à long et doit donc être mis entre parenthèses. GNU make fournit une alternative plus lisible avec les fonctions dir et notdir.

alex
la source
37

Les $@et $<sont des macros spéciales.

Où:

$@ est le nom de fichier de la cible.

$< est le nom de la première dépendance.

Eric
la source
19

Le Makefile construit l' helloexécutable si l'une main.cpp, hello.cpp, a factorial.cppchangé. Le Makefile le plus petit possible pour atteindre cette spécification aurait pu être:

hello: main.cpp hello.cpp factorial.cpp
    g++ -o hello main.cpp hello.cpp factorial.cpp
  • pro: très facile à lire
  • con: maintenance cauchemar, duplication des dépendances C ++
  • con: problème d'efficacité, nous recompilons tout C ++ même si un seul a été changé

Pour améliorer ce qui précède, nous compilons uniquement les fichiers C ++ qui ont été modifiés. Ensuite, nous lions simplement les fichiers d'objets résultants ensemble.

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

main.o: main.cpp
    g++ -c main.cpp

hello.o: hello.cpp
    g++ -c hello.cpp

factorial.o: factorial.cpp
    g++ -c factorial.cpp
  • pro: corrige un problème d'efficacité
  • con: nouveau cauchemar de maintenance, faute de frappe potentielle sur les règles des fichiers objets

Pour améliorer cela, nous pouvons remplacer toutes les règles de fichier objet par une seule .cpp.orègle:

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

.cpp.o:
    g++ -c $< -o $@
  • pro: retour à avoir un makefile court, un peu facile à lire

Ici, la .cpp.orègle définit comment construire à anyfile.opartir de anyfile.cpp.

  • $< correspond à la première dépendance, dans ce cas, anyfile.cpp
  • $@correspond à la cible, dans ce cas anyfile.o,.

Les autres modifications présentes dans le Makefile sont:

  • Faciliter les changements de compilateurs de g ++ vers n'importe quel compilateur C ++.
  • Pour faciliter la modification des options du compilateur.
  • Pour faciliter la modification des options de l'éditeur de liens.
  • Pour faciliter la modification des fichiers source et de la sortie C ++.
  • Ajout d'une règle par défaut «tous» qui agit comme une vérification rapide pour s'assurer que tous vos fichiers source sont présents avant de tenter de créer votre application.
Stephen Quan
la source
1

par exemple si vous souhaitez compiler des sources mais avoir des objets dans un répertoire différent:

Tu as besoin de faire :

gcc -c -o <obj/1.o> <srcs/1.c> <obj/2.o> <srcs/2.c> ...

mais avec la plupart des macros, le résultat sera tous les objets suivis de toutes les sources, comme:

gcc -c -o <all OBJ path> <all SRC path>

donc cela ne compilera rien ^^ et vous ne pourrez pas mettre vos fichiers objets dans un répertoire différent :(

la solution est d'utiliser ces macros spéciales

$@ $<

cela générera un fichier .o (obj / file.o) pour chaque fichier .c dans SRC (src / file.c)

$(OBJ):$(SRC)
   gcc -c -o $@ $< $(HEADERS) $(FLAGS)

ça veut dire :

    $@ = $(OBJ)
    $< = $(SRC)

mais lignes par lignes AU LIEU de toutes les lignes de OBJ suivies de toutes les lignes de SRC

Aominé
la source
Je me demande, que faites-vous de tout ce temps que vous économisez en tapant "u" au lieu de "vous"?
Ivanzinho