Make 编译脚本上手
考察下面的示例代码: main.c #include <stdio.h>
正常情况下,通过 $ gcc -o main.out main.c 上面命令编译后运行 $ ./main.out
hello world! Make 工具通过
其中定义任务的基本语法为: target1 [target2 ...]: [pre-req-1 pre-req-2 ...]
[command1
command2
......] 上面形式也可称作是一条编译规则(rule)。 其中,
Makefile 示例比如文章最开始的编译,可通过编写下面的 Makefile 来完成: Makefile all:main.out
上面的 Makefile 中定义了三个任务,调用时可通过 比如: $ make main.out
gcc -o main.out main.c 产出 再比如: $ make clean
rm main.out 该 三个任务中, $ make
gcc -o main.out main.c 命令的换行如果一条编译规则中所要执行的 shell 命令有单条很长的情况,可通过 main.out: main.c
gcc \
-o main.out \
main.c 注意 main.out: main.c
gcc\ # 🚨
-o main.out\ # 🚨
main.c 任务间的依赖前面调用 增量编译使用 Makefile 进行编译有个好处是,在执行任务时,它会先检查依赖项是否比需要产出的文件新,如果说依赖项更新新,则说明我们需要产出的目标文件属于过时的产物,需要重新生成。 什么意思。比如上面的示例,当执行 $ make main.out 试图生成 比如前面我们已经执行过该任务产生过 $ make main.out
make: `main.out' is up to date. NOTE: 上面是 Mac 上最新版本的 Make 工具(GNU Make 3.81)的提示语,老版或其他变种工具得到的可能是 现在对输入文件 #include <stdio.h>
再次执行 $ make main.out
gcc -o main.out main.c
这里 过时的任务才会被重新执行,而未过时的会跳过,并输出相应信息。 以上,Makefile 天然实现了增量编译的效果,在大型项目下会节省不少编译时间,因为它只编译过期的任务。 Phony 类型任务的执行需要注意的是,phony 类型的任务永远都属于过时类型,即,每次 拿这里的 $ make
make: Nothing to be done for `all'. 这里看不出来 为了验证 phony 类型任务是否每次都执行,向 all:main.out
+ echo "[all] done"
再次执行: $ make
echo "[all] done"
[all] done
可以看到,属于 phony 类型的任务 变量/宏Makefile 中可使用变量(宏)来让脚本更加灵活和减少冗余。 其中变量使用 CC=gcc 这里定义 + CC=gcc
这样做的好处是什么?因为编译工具可能随着平台或环境或需要编译的目标不同,而不同。比如 无论怎样变,我们只需要修改定义在文件开头的 自动变量/Automatic Variables自动变量/Automatic Variables 是在编译规则匹配后工具进行设置的,具体包括:
这些变量都只有一个符号,区别于正常用字母命名的变量需要使用 利用自动变量,前面示例可改造成: CC=gcc
减少了重复代码,更加易于维护,需要修改时,改动比较小。 VPATH & vpath可通过 VPATH = src include 通过小写的 vpath %.c src
vpath %.h include 此处 依赖规则/Dependency RulesMain.o : Main.h Test1.h Test2.h
Test1.o : Test1.h Test2.h
Test2.o : Test2.h 像这种,只定义了产出与依赖没包含任务命令的无则,叫作依赖无则。因为它只定义了某个产出依赖哪些输入,故名。 这种规则可达到这种效果,即,右边任何文件有变更,左边的产出便成为过时的了。 匹配规则/Pattern Rules区别于明确指定了产出与依赖,如果一条规则包含通配符,则称作匹配规则(Pattern Rules)。 比如, %.o: %.c
gcc -o $@ $^ 上面定义了这么一条编译规则,将所有匹配到的 c 文件编译成 Object 产出。 有什么用? 这种规则一般不是直接调用的,是被其他它规则触间接使用。比如上面的依赖规则。 %.o : %.cpp
g++ -g -o $@ -c $<
当右侧这些头文件有变动时,左边的产出会在 Main.o : Main.h Test1.h Test2.h
g++ -g -o $@ -c $<
Test1.o : Test1.h Test2.h
g++ -g -o $@ -c $<
Test2.o : Test2.h
g++ -g -o $@ -c $< 一个实际一点的示例添加 lame 依赖到项目并将其编译打包。 首先下载并解压 lame 到项目目录: $ wget https://sourceforge.net/projects/lame/files/lame/3.100/lame-3.100.tar.gz
$ tar -zxvf lame-3.100.tar.gz 主程序中调用 lame,这只仅简单地打印其版本信息: main.c #include <stdio.h>
#include "./lame-3.100/include/lame.h"
编译项目的 Makefile: Makefile CC=gcc
编译并运行: $ make
$ ./main.out
lame ver: 3⏎ 相关资源 |
![](https://licensebuttons.net/l/by-nc-sa/3.0/88x31.png)