¿Qué significan los símbolos makefile $ @ y $ <?

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 $@

¿Qué hacen los $@y $<hacen exactamente?

Mohit Deshpande
fuente
55
El enlace de arriba está roto, aquí está el otro: gnu.org/software/make/manual/html_node/Automatic-Variables.html
asciz
1
Hola, ¿qué significa ".cpp.o:" como objetivo? (pre última línea?)
pseudonym_127
3
El ".cpp.o:" significa construir ".o" (archivos de objetos) a partir de ".cpp" (archivos de origen)
jaguzu
1
Creo que debe tenerse en cuenta que hay un tutorial de creación en el siguiente enlace del que creo que Mohit obtuvo el archivo MAKE en su publicación. mrbook.org/blog/tutorials/make
DeepDeadpool
Microsoft lo llama Macros de nombre de archivo (para NMAKE) que es más claro que las Variables automáticas (para MAKE). Es útil ver ambos lados con fines educativos.
Ivanzinho

Respuestas:

502

$@es el nombre del archivo que se genera y $<el primer requisito previo (generalmente el archivo fuente). Puede encontrar una lista de todas estas variables especiales en el manual de GNU Make .

Por ejemplo, considere la siguiente declaración:

all: library.cpp main.cpp

En este caso:

  • $@ evalúa a all
  • $< evalúa a library.cpp
  • $^ evalúa a library.cpp main.cpp
bdonlan
fuente
16
Vale la pena señalar que $@no necesariamente tiene que terminar siendo un archivo, también podría ser el nombre de un .PHONYobjetivo.
Ephemera
¿Puedo agregar a las opciones de línea de comandos esto: $@spara generar resultados de ensamblaje como name.os?
huseyin tugrul buyukisik
44
Tenga cuidado cuando la primera dependencia es una variable que representa una lista, $ <se evalúa después de que se expande. Entonces, cuando LIST = lib1.cpp lib2.cpp, y todo: $ {LIST} main.cpp, $ <se evalúa como solo lib1.cpp. Hace unos años, pasé algún tiempo averiguando qué sucedió en el resultado causado por este comportamiento.
Chan Kim
En general, $ @ se refiere al nombre del objetivo que se encuentra en el lado izquierdo del:
Deepak Kiran
78

Los $@y $<se llaman variables automáticas . La variable $@representa el nombre del archivo creado (es decir, el destino) y $<representa el primer requisito previo requerido para crear el archivo de salida.
Por ejemplo:

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

Aquí hello.oestá el archivo de salida. Esto es a lo que se $@expande. La primera dependencia es hello.c. Eso es lo que$< expande.

La -cbandera genera el .oarchivo; ver man gccpara una explicación más detallada. los-o especifica el archivo de salida a crear.

Para más detalles, puede leer este artículo sobre Linux Makefiles .

Además, puede consultar los manuales de GNU make . Será más fácil crear Makefiles y depurarlos.

Si ejecuta este comando, generará la base de datos makefile:

make -p 
diestro
fuente
1
Su respuesta parece que $<se expandirá a hello.c hello.h(ambos). Por favor aclarar.
Dr Beco
Sí, incluirá hello.c y hello.h
diestro
19
$<Es solo el primer artículo. Para incluir todo, use $^.
Dr Beco
1
El Dr. Beco tiene razón. El autor debe modificar su respuesta.
PT Huynh
67

De Gestión de proyectos con GNU Make, 3a edición, p. 16 (está bajo licencia de documentación libre de GNU ):

Variables automáticas son establecidas pormake después de que una regla coincida. Proporcionan acceso a elementos desde el destino y listas de requisitos previos para que no tenga que especificar explícitamente ningún nombre de archivo. Son muy útiles para evitar la duplicación de código, pero son críticos al definir reglas de patrones más generales.

Hay siete variables automáticas "principales":

  • $@: El nombre de archivo que representa el objetivo.

  • $%: El elemento de nombre de archivo de una especificación de miembro de archivo.

  • $<: El nombre de archivo del primer requisito previo.

  • $?: Los nombres de todos los requisitos previos que son más nuevos que el destino, separados por espacios.

  • $^: Los nombres de archivo de todos los requisitos previos, separados por espacios. Esta lista tiene nombres de archivo duplicados eliminados ya que para la mayoría de los usos, como compilar, copiar, etc., no se desean duplicados.

  • $+: Similar a $^, este es el nombre de todos los requisitos previos separados por espacios, excepto que $+incluye duplicados. Esta variable se creó para situaciones específicas, como argumentos para vinculadores donde los valores duplicados tienen significado.

  • $*: El tallo del nombre de archivo de destino. Un tallo es típicamente un nombre de archivo sin su sufijo. Se desaconseja su uso fuera de las reglas del patrón.

Además, cada una de las variables anteriores tiene dos variantes para compatibilidad con otras marcas. Una variante devuelve solo la parte del directorio del valor. Esto se indica añadiendo un “D” para el símbolo, $(@D), $(<D), etc. Los otros retornos variantes únicamente la parte de archivo del valor. Esto se indica añadiendo una “F” para el símbolo, $(@F), $(<F), etc. Tenga en cuenta que estos nombres variantes son más de un carácter de largo y por lo tanto debe ser encerrado entre paréntesis. GNU make proporciona una alternativa más legible con las funciones dir y notdir.

alex
fuente
37

Las $@y $<son macros especiales.

Dónde:

$@ es el nombre del archivo de destino.

$< es el nombre de la primera dependencia.

Eric
fuente
19

El Makefile construye el helloejecutable si uno cualquiera de main.cpp, hello.cpp, factorial.cppcambió. El Makefile más pequeño posible para lograr esa especificación podría haber sido:

hello: main.cpp hello.cpp factorial.cpp
    g++ -o hello main.cpp hello.cpp factorial.cpp
  • pro: muy fácil de leer
  • con: pesadilla de mantenimiento, duplicación de las dependencias de C ++
  • con: problema de eficiencia, recompilamos todo C ++ incluso si solo se cambió uno

Para mejorar lo anterior, solo compilamos los archivos C ++ que fueron editados. Luego, simplemente vinculamos los archivos de objetos resultantes.

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: soluciona el problema de eficiencia
  • con: nueva pesadilla de mantenimiento, posible error tipográfico en reglas de archivos de objetos

Para mejorar esto, podemos reemplazar todas las reglas de archivos de objetos con una sola .cpp.oregla:

OBJECTS=main.o hello.o factorial.o

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

.cpp.o:
    g++ -c $< -o $@
  • pro: volver a tener un archivo MAKE corto, algo fácil de leer

Aquí la .cpp.oregla define cómo construir anyfile.odesde anyfile.cpp.

  • $< coincide con la primera dependencia, en este caso, anyfile.cpp
  • $@coincide con el objetivo, en este caso anyfile.o,.

Los otros cambios presentes en el Makefile son:

  • Facilitando el cambio de compiladores de g ++ a cualquier compilador de C ++.
  • Haciendo más fácil cambiar las opciones del compilador.
  • Haciendo más fácil cambiar las opciones del enlazador.
  • Facilitando el cambio de los archivos de origen y la salida de C ++.
  • Se agregó una regla predeterminada 'todos' que actúa como una verificación rápida para garantizar que todos sus archivos de origen estén presentes antes de que se intente construir su aplicación.
Stephen Quan
fuente
1

a modo de ejemplo si desea compilar fuentes pero tiene objetos en un directorio diferente:

Necesitas hacer :

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

pero con la mayoría de las macros, el resultado será todos los objetos seguidos de todas las fuentes, como:

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

así que esto no compilará nada ^^ y no podrá colocar sus archivos de objetos en un directorio diferente :(

la solución es usar estas macros especiales

$@ $<

esto generará un archivo .o (obj / file.o) para cada archivo .c en SRC (src / file.c)

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

significa :

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

pero líneas por líneas EN LUGAR de todas las líneas de OBJ seguido de todas las líneas de SRC

Aominé
fuente
Me pregunto, ¿qué haces con todo ese tiempo que ahorras escribiendo "u" en lugar de "tú"?
Ivanzinho