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

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 $< exactamente?

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

Por ejemplo, considere la siguiente statement:

 all: library.cpp main.cpp 

En este caso:

  • $@ evalúa a all
  • $< evalúa a library.cpp
  • $^ evalúa a library.cpp main.cpp

Los $@ y $< se llaman las variables automáticas. El $@ es la variable de salida. $< es la primera variable de entrada. Por ejemplo:

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

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

El indicador -c genera el archivo .o ; ver man gcc para una explicación más detallada. El -o especifica el archivo a la salida.

Para más detalles, puedes leer esto .

Además, puedes consultar los manuales de GNU. Hay una manera de depurar su archivo MAKE para entenderlo mejor.

Esto generará la base de datos de archivo MAKE:

 $make -p 

Los $@ y $< son macros especiales.

Dónde:

$@ es el nombre del archivo del objective.

$< es el nombre de la primera dependencia.

Desde Administrar proyectos con GNU Make, 3ª edición (está bajo la Licencia de documentación libre de GNU ):

Las variables automáticas se configuran por make una vez que se cumple una regla. Brindan acceso a los elementos de las listas de requisitos previos y de destino 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 cuando se definen reglas de patrones más generales.

Hay siete variables automáticas “centrales”:

  • $@ : El nombre del archivo que representa el objective.

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

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

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

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

  • $+ : Similar a $^ , estos son los nombres 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 valor del nombre del archivo de destino. Un tallo es típicamente un nombre de archivo sin su sufijo. Se desaconseja su uso fuera de las reglas de patrones.

Además, cada una de las variables anteriores tiene dos variantes para la compatibilidad con otras marcas. Una variante devuelve solo la parte del directorio del valor. Esto se indica al agregar una "D" al símbolo, $(@D) , $( , etc. La otra variante solo devuelve la parte del archivo del valor. Esto se indica al agregar una "F" al símbolo, $(@F) , $( , etc. Tenga en cuenta que estos nombres de variantes tienen más de un carácter y, por lo tanto, deben aparecer entre paréntesis. GNU make proporciona una alternativa más legible con las funciones dir y notdir.

Makefile construye el ejecutable hello si alguno de main.cpp , hello.cpp , factorial.cpp cambiado. 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, volvemos a comstackr todos los C ++, incluso si solo se cambió uno

Para mejorar lo anterior, solo comstackmos aquellos archivos C ++ que fueron editados. Luego, solo 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 la eficiencia
  • con: nueva pesadilla de mantenimiento, posibles errores tipográficos en las reglas de archivos de objeto

Para mejorar esto, podemos reemplazar todas las reglas de archivo de objeto con una sola regla .cpp.o :

 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 regla .cpp.o define cómo comstackr anyfile.o desde anyfile.cpp .

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

Los otros cambios presentes en el Makefile son:

  • Haciendo más fácil el cambio de comstackdores de g ++ a cualquier comstackdor de C ++.
  • Haciendo que sea más fácil cambiar las opciones del comstackdor.
  • Haciendo que sea más fácil cambiar las opciones del vinculador.
  • Haciendo que sea más fácil cambiar los archivos fuente de C ++ y la salida.
  • Se agregó una regla predeterminada 'todo' que actúa como una comprobación rápida para asegurar que todos sus archivos fuente estén presentes antes de que se intente crear su aplicación.