参考教程
原文:https://makefiletutorial.com/
中文翻译:https://makefiletutorial.vercel.app
Makefile常用内容
本文主要基于以上教程记录一些平时阅读Makefile
可能会用到的信息。
Makefile基础使用
首先,Makefile用于帮助决定一个大型程序的哪些部分需要重新编译,如果有任何文件的依赖项发生改变,那么该文件将会重新编译。Makefile通常由一组规则组成,规则通常如下所示:
target: prerequisites
command
command
command
target
是文件名,以空格分隔,通常每个规则只有一个。command
通常是用于制作目标的一系列步骤,需要以制表符Tab
而不是空格开头。prerequisites
也是文件名,称为依赖项,以空格分隔,在运行针对目标的命令之前,这些文件需要存在。
下面是一个Makefile
的实例:
blah: blah.o
gcc blah.o -o blah # Runs third
blah.o: blah.c
gcc -c blah.c -o blah.o # Runs second
# Typically blah.c would already exist, but I want to limit any additional required files
blah.c:
echo "int main() { return 0; }" > blah.c # Runs first
使用make
时默认会执行第一个目标blah
,或者可以指定目标进行生成make blah
。上面的Makefile在执行make
指令后按照一系列步骤进行调用:
- make
选择目标blah
,因为第一个目标是默认目标
- blah
需要blah.o
,所以搜索blah.o
目标
- blah.o
需要blah.c
,所以搜索blah.c
目标
- blah.c
没有依赖关系,所以echo
命令运行
- 然后运行该gcc -c
命令,因为所有blah.o
依赖项都已完成
- gcc
运行top命令,因为所有的blah
依赖都跑完了
- 就是这样:blah
是一个编译好的c程序
Makefile使用文件系统时间戳来判断上次编译以来的先决条件是否发生了变化。例如,如果运行上面的Makefile后删除blah.c
,那么三个目标都会被重新执行;如果只是编辑blah.c
,那么只有前两个目标会被重新执行;如果只是通过touch blah.o
更新blah.o
的时间戳,那么只有第一个目标会被重新执行;如果不做任何更改,那么所有目标都不会被执行。需要注意的是,时间戳并不总是有效,例如,您可以修改一个文件,然后将该文件的修改时间戳更改为旧的时间戳。如果你这样做了,make
会错误地猜测文件没有改变,因此可以被忽略。
全部目标和多个目标
如果有多个目标并希望所有目标都被执行,那么可以使用一个all
目标,如下所示
all: one two three
one:
touch one
two:
touch two
three:
touch three
clean:
rm -f one two three
当规则有多个目标时,将为每个目标都运行命令,如下所示,其中$@
是包含目标名称的自动变量
all: f1.o f2.o
f1.o f2.o:
echo $@
# Equivalent to:
# f1.o:
# echo f1.o
# f2.o:
# echo f2.o
自动变量和通配符
*
通配符
在Makefile
中*
和%
是通配符,但它们的含义完全不同。*
在文件系统中搜索匹配的文件名。*
使用时最好包装在wildcard
函数中,如下所示:
# Print out file information about every .c file
print: $(wildcard *.c)
ls -la $?
*
不可以直接用在变量定义中,当*
没有匹配到文件时如果没有使用wildcard
函数包装,会直接保持原样。
thing_wrong := *.o # Don't do this! '*' will not get expanded
thing_right := $(wildcard *.o)
all: one two three four
# Fails, because $(thing_wrong) is the string "*.o"
one: $(thing_wrong)
# Stays as *.o if there are no files that match this pattern :(
two: *.o
# Works as you would expect! In this case, it does nothing.
three: $(thing_right)
# Same as rule three
four: $(wildcard *.o)
%
通配符
%
通配符通常在多种情况下使用:
- 在"匹配"模式下,它匹配一个或多个字符,这种匹配称为stem
。
- 在"替换"模式下,它使用匹配到的stem
并在字符串中进行替换。
- %
最常用于规则定义和某些特定函数中
规则
隐式规则
- 编译
C
程序:基于n.c
生成n.o
,使用命令$(CC) -c $(CPPFLAGS) $(CFLAGS) $^ -o $@
。 - 编译
C++
程序,基于n.cc
或n.cpp
生成n.o
,使用命令$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $^ -o $@
。 - 链接单个目标文件:基于
n.o
生成n
,使用命令$(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
。
使用到的变量为 CC
:编译C程序的程序;默认ccCXX
:编译C++程序的程序;默认g++CFLAGS
: 给 C 编译器的额外标志CXXFLAGS
: 提供给 C++ 编译器的额外标志CPPFLAGS
: 提供给 C 预处理器的额外标志LDFLAGS
: 在编译器应该调用链接器时提供给编译器的额外标志
基于隐式规则可以编译C/C++程序而不用显式地告诉make怎么做。
TO DO
后续有时间会添加关于更多规则以及变量与函数,还有命令的相关内容。
评论 (0)