Makefile 符号 $@ 和 $< 是什么意思?

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

$@$<到底是做什么的?

444094 次浏览

$@是正在生成的目标的名称,$<是第一个前提条件(通常是一个源文件)。您可以在GNU制作手册中找到所有这些特殊变量的列表。

例如,考虑以下声明:

all: library.cpp main.cpp

在这种情况下:

  • $@的结果是all
  • $<的结果是library.cpp
  • $^的结果是library.cpp main.cpp

$@$<是特殊的宏。

地点:

$@是目标的文件名。

$<是第一个依赖项的名称。

$@$<被称为自动变量。变量$@表示目标的名称,$<表示创建输出文件所需的第一个前提条件。
例如:< / p >
hello.o: hello.c hello.h
gcc -c $< -o $@

这里,hello.o是输出文件。这就是$@展开的内容。第一个依赖项是hello.c。这就是$<展开的内容。

-c标志生成.o文件;更详细的解释见man gcc-o指定要创建的输出文件。

要了解更多详细信息,请阅读这篇关于Linux Makefiles的linoxide文章

另外,你可以检查GNU make手册。这将使生成makefile和调试它们变得更容易。

如果你运行这个命令,它将输出makefile数据库:

make -p

使用GNU Make管理项目,第3版,第16页(在GNU自由文档许可下面):

自动变量在规则匹配后由make设置。他们 提供对目标和先决条件列表中的元素的访问 您不必显式地指定任何文件名。他们非常 有助于避免代码重复,但在定义时非常重要

这里有7个“核心”自动变量:

  • $@:表示目标的文件名。

  • $%:存档成员规范的filename元素。

  • $<:第一个先决条件的文件名。

  • $?:比目标更新的所有先决条件的名称, 以空格分隔

  • $^:所有先决条件的文件名,用空格分隔。这 列表删除了重复的文件名,因为对于大多数用途,例如

  • .
  • $+:类似于$^,这是所有分离的先决条件的名称 除了$+包含重复项。这个变量是 为特定情况创建,例如链接器的参数 重复值有意义

  • $*:目标文件名的词干。词干通常是一个文件名 不带后缀。它在模式规则之外的用途是 李气馁。< / p > < / >

此外,上面的每个变量都有两个变量 与其他产品的兼容性。一种变体只返回目录 值的一部分。的后面加上“D”表示这一点 符号,$(@D)$(<D)等。另一个变体只返回文件 值的一部分。的后面加一个“F”表示 符号,$(@F)$(<F)等。请注意,这些变量名大于 一个字符长,所以必须用圆括号括起来。GNU使 提供一个更具可读性的dir和notdir选项 功能。< / p >

如果main.cpphello.cppfactorial.cpp中的任何一个被更改,Makefile将构建hello可执行文件。实现该规范的最小Makefile可以是:

hello: main.cpp hello.cpp factorial.cpp
g++ -o hello main.cpp hello.cpp factorial.cpp
  • 教授:非常容易阅读
  • 缺点:维护的噩梦,c++依赖的复制
  • 缺点:效率问题,我们重新编译所有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
  • 利:修复效率问题
  • 缺点:新的维护噩梦,目标文件规则上的潜在错字

为了改进这一点,我们可以用一个.cpp.o规则替换所有的对象文件规则:

OBJECTS=main.o hello.o factorial.o


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


.cpp.o:
g++ -c $< -o $@
  • 优点:回到一个简短的makefile,有点容易阅读

这里,.cpp.o规则定义了如何从anyfile.cpp构建anyfile.o

  • $<匹配第一个依赖项,在本例中为anyfile.cpp
  • $@匹配目标,在本例中为anyfile.o

Makefile中的其他更改包括:

  • 更容易将编译器从g++更改为任何c++编译器。
  • 使更改编译器选项更容易。
  • 使它更容易改变链接器选项。
  • 使更改c++源文件和输出变得更容易。
  • 添加了一个默认规则'all',它可以作为一个快速检查,以确保在尝试构建应用程序之前,所有的源文件都存在。

例如,如果你想编译源代码,但在不同的目录中有对象:

你需要做的是:

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

但对于大多数宏,结果将是所有对象后面跟着所有源,例如:

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

所以这不会编译任何东西^^,你将不能把你的对象文件放在不同的目录:(

解决方案是使用这些特殊的宏

$@ $<

这将为SRC (SRC /file.c)中的每个.c文件生成一个.o文件(obj/file.o)

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

意思是:

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

而是一行一行,而不是OBJ的所有行后面跟着SRC的所有行