Что означают символы make-файла $ @ и $ <?

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

Что делать $@и $<делать именно?

Мохит Дешпанде
источник
5
Ссылка выше сломана, вот еще один: gnu.org/software/make/manual/html_node/Automatic-Variables.html
ASCIZ
1
Привет, что означает ".cpp.o:" как цель? (до последней строки?).
pseudonym_127
3
«.Cpp.o:» означает сборку «.o» (объектные файлы) из «.cpp» (исходные файлы)
jaguzu
1
Я чувствую, что следует отметить, что по следующей ссылке есть учебник по make, по которому, как мне кажется, Мохит получил make-файл в своем посте. mrbook.org/blog/tutorials/make
DeepDeadpool
Microsoft называет это « Макрос имени файла» (для NMAKE), который более понятен, чем « Автоматические переменные» (для MAKE). Полезно видеть обе стороны в образовательных целях.
Иванзиньо

Ответы:

502

$@это имя генерируемого файла и $<первая предпосылка (обычно исходный файл). Вы можете найти список всех этих специальных переменных в руководстве по GNU Make .

Например, рассмотрим следующую декларацию:

all: library.cpp main.cpp

В этом случае:

  • $@ оценивает all
  • $< оценивает library.cpp
  • $^ оценивает library.cpp main.cpp
bdonlan
источник
16
Стоит отметить, что $@это не обязательно должен быть файл, это также может быть имя .PHONYцели.
Ephemera
Могу ли я добавить в параметры командной строки это: $@sгенерировать вывод сборки, такой как name.os?
хусейн тугрул буюкисик
4
Остерегайтесь, когда первой зависимостью является переменная, представляющая список, $ <оценивается после его расширения. Поэтому, когда LIST = lib1.cpp lib2.cpp и all: $ {LIST} main.cpp, $ <оценивается как просто lib1.cpp. Несколько лет назад я потратил некоторое время на выяснение того, что произошло в результате этого поведения.
Чан Ким
В общем, $ @ относится к названию цели, которое находится слева от:
Дипак Киран
78

$@И $<называются автоматическими переменными . Переменная $@представляет имя файла, который был создан (то есть цель) и $<представляет первую предпосылку, необходимую для создания выходного файла.
Например:

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

Здесь hello.oнаходится выходной файл. Это то, что $@расширяется до. Первая зависимость есть hello.c. Вот что $<расширяется до.

-cФлаг генерирует .oфайл; см. man gccдля более подробного объяснения. -oОпределяет выходной файл , чтобы создать.

Для получения более подробной информации вы можете прочитать эту статью о Linux Makefiles .

Кроме того , вы можете проверить GNU makeруководства . Это облегчит создание Make-файлов и их отладку.

Если вы запустите эту команду, она выведет базу данных makefile:

make -p 
ловкий
источник
1
Ваш ответ звучит как $<расширится до hello.c hello.h(оба). Просьба уточнить.
Доктор Беко
Да, это будет включать и hello.c и hello.h
ловкий
19
$<это только первый пункт. Чтобы включить все, используйте $^.
Доктор Беко
1
Доктор Беко прав. Автор должен изменить свой ответ.
PT Huynh
67

Из управления проектами с помощью GNU Make, 3-е издание, с. 16 ( лицензия GNU Free Documentation License ):

Автоматические переменные устанавливаются makeпосле сопоставления правила. Они предоставляют доступ к элементам из целевого и обязательного списков, поэтому вам не нужно явно указывать имена файлов. Они очень полезны для избежания дублирования кода, но очень важны при определении более общих шаблонных правил.

Существует семь «основных» автоматических переменных:

  • $@: Имя файла, представляющее цель.

  • $%: Элемент имени файла спецификации члена архива.

  • $<: Имя файла первой предпосылки.

  • $?: Имена всех предпосылок, которые являются новыми, чем цель, разделенные пробелами.

  • $^: Имена файлов всех предварительных условий, разделенных пробелами. В этом списке удалены дубликаты имен файлов, поскольку для большинства целей, таких как компиляция, копирование и т. Д., Дубликаты не нужны.

  • $+Аналогично $^, это имена всех предпосылок, разделенных пробелами, за исключением того, что $+включает дубликаты. Эта переменная была создана для конкретных ситуаций, таких как аргументы для компоновщиков, где значения дубликатов имеют значение.

  • $*: Ствол целевого имени файла. Основа - это обычно имя файла без суффикса. Его использование вне шаблонных правил не рекомендуется.

Кроме того, каждая из перечисленных переменных имеет два варианта совместимости с другими производителями. Один вариант возвращает только часть каталога значения. Это указано с помощью добавления «D» на символ, $(@D), $(<D)и т.д. Другой вариант возвращает только файл часть стоимости. Это обозначено присоединени «F» на символ, $(@F), $(<F)и т.д. Обратите внимание , что эти имена вариантов более чем один персонаж долго и должны быть заключены в скобках. GNU make предоставляет более удобочитаемую альтернативу функциям dir и notdir.

Алекс
источник
37

$@И $<специальные макросы.

Куда:

$@ это имя файла цели.

$< это имя первой зависимости.

Эрик
источник
19

Makefile создает helloисполняемый файл , если какой - либо один из main.cpp, hello.cpp, factorial.cppизменилось. Наименьший возможный Makefile для достижения этой спецификации мог бы быть:

hello: main.cpp hello.cpp factorial.cpp
    g++ -o hello main.cpp hello.cpp factorial.cpp
  • Pro: очень легко читать
  • con: обслуживание кошмар, дублирование зависимостей C ++
  • con: проблема эффективности, мы перекомпилируем весь C ++, даже если был изменен только один

Чтобы улучшить вышесказанное, мы компилируем только те файлы C ++, которые были отредактированы. Затем мы просто связываем результирующие объектные файлы.

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: исправляет проблему эффективности
  • con: новый кошмар обслуживания, потенциальная опечатка в правилах объектных файлов

Чтобы улучшить это, мы можем заменить все правила объектных файлов одним .cpp.oправилом:

OBJECTS=main.o hello.o factorial.o

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

.cpp.o:
    g++ -c $< -o $@
  • pro: вернемся к короткому make-файлу, довольно легко читаемому

Здесь .cpp.oправило определяет, как строить anyfile.oиз anyfile.cpp.

  • $< соответствует первой зависимости, в этом случае, anyfile.cpp
  • $@соответствует цели, в этом случае anyfile.o.

Другие изменения, присутствующие в Makefile:

  • Облегчение изменения компиляторов с g ++ на любой компилятор C ++.
  • Облегчить изменение параметров компилятора.
  • Облегчить изменение параметров компоновщика.
  • Облегчение изменения исходных файлов C ++ и вывода.
  • Добавлено правило по умолчанию «все», которое действует как быстрая проверка, чтобы убедиться, что все ваши исходные файлы присутствуют до того, как будет предпринята попытка построить ваше приложение.
Стивен Куан
источник
1

Например, если вы хотите скомпилировать исходники, но объекты находятся в другом каталоге:

Вам нужно сделать:

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

но с большинством макросов результатом будут все объекты, за которыми следуют все источники, например:

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

так что это ничего не скомпилирует ^^ и вы не сможете поместить ваши файлы объектов в другой каталог :(

решение состоит в том, чтобы использовать эти специальные макросы

$@ $<

это сгенерирует файл .o (obj / file.o) для каждого файла .c в SRC (src / file.c)

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

это значит :

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

но строки за строками INSTEAD всех строк OBJ, за которыми следуют все строки SRC

Aominé
источник
Интересно, что вы делаете со всем тем количеством времени, которое вы экономите, набирая «и» вместо «вы»?
Иванзиньо