Make工具的基础使用

make 是什么

GNU Make(下称 make)是一个用来控制从源文件生成可执行文件或者其他非源文件的项目构建工具。比如可以执行命令

make foo 

构建可执行文件 foo。但实际上执行这条命令并不会起作用,因为 make 不知道如何构建 foo。

make 通过一个叫作 Makefile 的文件获得构建程序的规则。所以为了使用 make 构建项目,我们需要学习书写 Makefile。

Makefile 文件的格式
Makefile文件由一系列规则构成。每条规则的形式如下:

target ... : prerequisites ...
    command
    ...


上面第一行冒号前面的部分,叫做目标,冒号后面的部分叫做依赖;第二行必须由一个 tab 键起首,后面跟着命令。这描述了文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。prerequisites 中如果有一个或以上的文件比 target 文件要新的话,command 所定义的命令就会被执行。

目标通常是文件名,指明 make 命令所要构建的对象,比如上文的 foo 。目标可以是一个文件名,也可以是多个文件名,之间用空格分隔。目标也可以不是一个文件名,而是表示某种操作,这类目标被称作伪目标。

.PHONY : clean
clean :
    -rm $(objects)


在上面的示例中,.PHONY 表示 clean 是一个“伪目标”,所以 make 就不会去检查是否存在一个叫做 clean 的文件。

依赖通常是一组文件名,之间用空格分隔。它指定了目标是否需要被重新构建:只要有一个依赖不存在,或者有过更新,目标就需要重新构建。

命令表示了如何更新目标文件,由一行或多行的 Shell 命令组成。它是构建目标的具体指令,它的运行结果通常就是生成目标文件。

Makefile 文件的基本语法
注释
# 在 Makefile 中表示注释。

foo.o: foo.c def.h
    # This is a comment.
    cc -c foo.c


使用变量

Makefile 允许使用变量。变量会在被使用的地方展开,就像 C 语言中的宏一样。

objects := main.o foo.o bar.o
CXX := clang++
CXXFLAGS := -Wall -g -O2 -std=c++17
main: $(object)
    $(CXX) -o main $(objects) $(CXXFLAGS)


等价于:

main: main.o foo.o bar.o
    clang++ -o main main.o foo.o bar.o -Wall -g -O2 -std=c++17

Makefile 提供了 4 种赋值运算符:

  • VARIABLE = value    // make 会将整个 Makefile 展开后,将 value 最终的值赋给 VARIABLE
  • VARIABLE ?= value    // 如果 VARIABLE 没有被赋值过,则赋值为 value
  • VARIABLE := value    // make 会将 Makefile 展开到当前位置,将 value 当前的值赋给 VARIABLE
  • VARIABLE += value    // 为 VARIABLE 追加 value

隐含规则

如果将一个 Makefile 中的所有命令都写出来,是比较繁琐的:

objects := main.o foo.o bar.o
CXX := clang++
CXXFLAGS := -Wall -g -O2 -std=c++17
main: $(object)
    $(CXX) -o main $(objects) $(CXXFLAGS)
main.o: main.cc
    $(CXX) -c main.cc
foo.o: foo.cc
    $(CXX) -c foo.cc
bar.o: bar.cc
    $(CXX) -c bar.cc


可以发现,像 $(CXX) -c main.cc 这样的命令是比较 trivial 的,make 可以使用隐含规则对类似于这种的常用规则进行自动推导。这里介绍一些隐含规则。

对于 C 程序,<n>.o 目标的依赖会自动推导为 <n>.c ,并且其生成命令是 $(CC) –c $(CPPFLAGS) $(CFLAGS)。
对于 C++ 程序,<n>.o 的目标的依赖会自动推导为 <n>.cc 或是 <n>.C ,并且其生成命令是 $(CXX) –c $(CPPFLAGS) $(CXXFLAGS) 。
对于汇编和汇编预处理,<n>.o 的目标的依赖会自动推导为 <n>.s ,默认使用编译器 as ,并且其生成命令是: $ (AS) $(ASFLAGS) 。 <n>.s 的目标的依赖会自动推导为 <n>.S ,默认使用 C 预编译器 cpp ,并且其生成命令是: $(AS) $(ASFLAGS) 。
链接Object文件时,<n> 目标依赖于 <n>.o ,通过运行 C 的编译器来运行链接程序生成(一般是 ld ),其生成命令是: $(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)。
在使用隐含规则时,要注意对应变量的定义。

根据隐含规则,上述 Makefile 可以被简化:

objects := main.o foo.o bar.o
CXX := clang++
CXXFLAGS := -Wall -g -O2 -std=c++17
main: $(object)
    $(CXX) $(objects) -o $@ 
%.o: %.cc
    $(CXX) $(CXXFLAGS) -c %<


上述代码中,$<代表依赖项(.cc文件),$@代表目标(.o文件)。

这里仅介绍了一些基本的 Makefile 语法,还需要在对 Makefile 的高级语法进行更深入研究。

 

posted @ 2025-04-18 19:24  Travic  阅读(160)  评论(0)    收藏  举报