我正在参加「掘金·启航计划」
前言
在Shell脚本中使用make命令来进行编译,尤其在C/C++开发中,make命令通过makefile文件中描述源程序之间的依赖关系进行自动编译;makefile文件是按照规定格式编写,需说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系; 很多大型项目的编译都是通过 Makefile 来组织的, 如果没有 Makefile, 那很多项目中各种库和代码之间的依赖关系不知会多复杂。
make命令
make常见指令
| 指令 | 含义 |
|---|---|
| make all | 编译所有目标 |
| make -j | 使用所有的核心编译目标 |
| make -j8 | 使用8个核心编译目标 |
| make install | 安装已编译的程序 |
| make uninstall | 卸载已安装的程序 |
| make clean | 删除由make命令产生的文件,通常删除目标文件.o |
| make distclean | 删除由./configure产生的文件 |
| make check | 测试刚编译的软件 |
| make installcheck | 检查安装的库和程序 |
| make dist | 重新打包成packname-version.tar.gz |
make工作流程
- make在当前目录下寻找“Makefile”或“makefile”文件
- 若找到,查找文件中的第一个目标文件.o
- 若目标文件不存在,根据依赖关系查找.s文件
- 若.s文件不存在,根据依赖关系查找.i文件
- 若.i文件不存在,根据依赖关系查找.c文件,此时.c文件一定存在,于是生成一个.o文件,再去执行
Makefile文件
执行make命令时,需要一个Makefile文件,以告诉make命令如何编译和链接程序。 Makefile文件由一系列规则rules构成,每条规则形式如下:
<target>: <prerequisites>
[Tab]<commands>
第一行冒号前为目标,冒号后为前置条件;第二行必须由一个Tab键起首,后接命令;目标是必须的,不可省略;前置条件和命令是可选的,但两者必须至少存在一个。下面对这几个概念详细解析一下:
目标target
目标可以是文件名,指明make命令所要构建的对象;也可以是某个操作名称,称“伪目标”; 举个例子:
clean:
rm *.o
以上代码目标是clean,命令是rm *.o;执行make clean命令,实现对象文件的删除;
前置条件prerequisites
前置条件通常是一组文件名,用空格隔开; 指定目标是否重新构建的判断标准——只要有一个前置条件不存在或有更新,则该目标需重新构建; 举例:
result.txt:source.txt
cp source.txt result.txt
若当前路径下source.txt存在,make result.txt可正常执行,否则需再写一条规则,用于生成source.txt;
source.txt:
echo "This is a source file." > source.txt
source.txt没有前置条件,与其他文件文官,只要该文件不存在,每次执行make source.txt命令都会生成该文件;
命令commands
命令表示如何更新目标文件,由一行或多行shell命令组成;
注意: shell命令一定是写在命令中,否则会被make忽略; 每行命令前必须有一个Tab键; 每行命令在一个独立的shell中执行,shell之间没有继承关系,因此上一行为的变量赋值,在下一行无效;
若前后两条命令有共享数据,可写在一行,用分号隔开; 举例:
var-kept:
export foo=bar;echo "foo=[$$foo]"
语法
| 指令 | 含义 |
|---|---|
| # | 注释 |
| echoing | 正常情况下,make打印每条命令,再执行该命令,称回声;在命令前加@,关闭回声,即只输出命令的执行结果,出错则停止执行 |
| % | make命令允许对文件名进行类似正则运算的匹配,主要用到% |
| $() | 调用变量,将变量名放在$()中 |
赋值运算符
| 指令 | 含义 |
|---|---|
| = | 递归展开赋值,默认赋值方式 |
| := | 直接赋值,不会递归展开,若引用的变量不存在,则为空串 |
| ?= | 若未初始化,则赋值 |
| += | 将值追加到现有内容末尾 |
自动变量
| 指令 | 含义 |
|---|---|
| $@ | 当前目标 |
| $< | 第一个前置条件 |
| $? | 所有比目标更新的前置条件 |
| $^ | 所有前置条件 |
判断语句
<条件语句>
<条件为真,执行程序段>
else
<条件为假,执行程序段>
endif
循环
LIST变量是Makefile变量,引用Makefile变量需使用$()括起来;
而all目标后的命令是shell命令,其中定义的变量也是shell变量,引用shell变量需使用$$作为开头,但shell变量不需括号;
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
all:
for i in one two three; do \
echo $$i; \
done