代码的编译过程: php
ps: 当中间文件太多的时候编译起来就很不方便,一般咱们会将其打包,windows 下叫库文件(Library File , .lib 文件),Unix 下 是 Archive File , 也就是 .a 文件html
Make 概念node
Make 这个词,英语意思为“制做”。 Make 命令直接使用了这个意思,就是要作出某个文件出来。 好比我要作出文件 a.txt ,就能够直接使用 c++
make a.txt
可是,若是你真的输入这条命令,它不会起到任何做用,由于 Make 自己并不知道,如何作出 a.txt 须要有人告诉它怎么去作出a.txt 文件。shell
用例一:windows
a.txt 文件依赖于 b.txt 和 c.txt 这两个文件,是后面两个文件内容的集合。那么make 须要知道下面的规则。bash
a.txt: b.txt c.txt #第一步 cat b.txt c.txt > a.txt #第二步
解析:函数
第一步:确认 b.txt 和 c.txt 这两个文件必须已经存在。工具
第二步: 使用cat 命令将这两个文件合并起来了,输出为新文件。ui
总结:
Makefile 文件格式
构建规则都写在了Makefile 文件中,要学会如何Make 命令,就必须先学会如何编写Makefile 文件
概述:
Makefile 文件由一系列规则构成,格式:
<target> :<prerequisites>
<commands>
解析:
target : 目标,必须的
prerequisites: 前置条件,可选
commands: 命令,它前面须要跟着一个空格 , 可选
目标<target>
一个目标(target) 就构成了一个规则,目标一般是文件名,指明make 命令所要构建的对象,好比上文的 a.txt 。 目标能够是一个文件名,也能够是多个文件名,之间用空格隔开。
除了文件名,目标还能够是操做名或任意字母组成的字符串,这类目标称为 “伪目标” (phony target)
上面代码的目标是 bb , 它不是文件名 , 而是一个操做名,属于“伪目标” , 做用是列举根目下的文件或目录
ps : 伪目标,系统是不会去检查bb 文件是否存在的。而是每次执行都执行对应的命令。
可是,若是目录中恰好有个文件就叫bb , 那么bb 也就变成了“目标”了,避免这种状况,能够使用以下命令
.PHONY: bb clean: ls /
经过 ".PHONY" 能够用来指定伪目标,这种内置目标名还有不少,能够查看手册
前置条件 prerequisites
前置条件一般是一组文件名,之间用空格分隔。它指定了“目标” , 是否重现构建的判断标准:只要有一个前置文件不存在或者被更新了,目标就须要从新构建
用例一:
result.txt: source.txt cp source.txt result.txt
解析:
上面的代码中,构建了目标 result.txt , 它的前置条件是 source.txt 已经存在了,那么 make result.txt 能够正常运行,不然必须再写一条规则,来生成 source.txt
用例二:
source.txt: echo " hello world" > source.txt
解析:
上面的代码中,source.txt 后面没有前置条件 , 这意味着这个“source.txt”目标不依赖其它文件,直接运行它的 commands 就行了。只要source.txt 不存在 , 每次调用 make source.txt 它都会生成 source.txt
若是你连续执行两次 make source.txt , 它只会执行第一次就不会再执行了
用例三:
source: a.txt b.txt c.txt
解析:
source 是一个伪目标 , 它只有三个前置条件 , 没有任何的 commands 。做用:一次性建立a.txt , b.txt , c.txt 这三个文件。
命令(commands)
命令就是一堆shell 命令组成的,它是构建“目标” 的具体指令,它的运行结果一般就是生成目标文件。每行命令以前必须有一个tab键,若是要使用其它键,能够使用内置变量 .RECIPEPREFIX 声明
用例一:
.RECIPEPREFIX = > all: > echo Hello , world
解析:
上面的代码就是用 .RECIPEPREFIX 来指定了每行命令开头 是 > 而再也不是 tab键了
用例二:
var-lost: export foo=bar echo "foo=[$$foo]"
解析:
commands 中的每条独自换行的命令都是独立分开的,不在同一个进程了,因此数据也是不共享的
第一行的 定义的变量 foo 是不能在 第二行中使用的
解决办法就是:
var-kept: export foo=bar; echo "foo=[$$foo]" 或者 var-kept: export foo=bar;\ echo "foo=[$$foo]" 或者 .ONESHELL var-kept: export foot=bar; echo "foo=[$$foo]"
Makefile 文件的语法
注释 :
在Makefile 文件中 注释符是: #
回声:
正常状况下 , make 会 打印每条命令 , 而后再执行, 这叫作回声(echoing).
若是你想关闭回声,直接在shell 命令行前面加一个 @ 符号就行了
test: # this is test @echo TODO
通配符:
通配符 , 用来指定符合条件的文件名的。 Makefile 的通配符与Bash 一致 , 主要有 * , ? 好比以o 结尾的文件 能够这样 *.o
模式匹配
Make 命令 容许 对文件名 , 进行相似正则运算的匹配 , 主要用到的匹配符是 % , 好比须要找到 当前目录下 有 f1.c 和 f2.c 两个源码文件,须要将它们编译伪对应的对象文件
%.o : %.c 等同于 f1.o :f1.c f2.o :f2.c
变量和赋值符
Makefile 中容许使用 等号 “ = ” 来 自定义变量
bb = Hello World test: @echo $(bb) @echo $$HOME
解析:
变量bb 等于 Hello World , 变量调用是经过 $() 来调用的。
若是你要调用Shell 自带的一些变量,那么你须要在 美圆符号前面再加一个美圆符号, 由于 Makefile 中会对美圆符号进行转义
Makefile 中提供了 4中赋值运算符( = , := , ?= , += ) :
#执行时扩展, 容许递归扩展 key = value #定义是扩展 key := value #只有在该变量为空时才设置值 key ?= value #将值追加到变量的尾端 key += value
内置变量
Make 命令提供了一系列的内置变量,详情见手册
自动变量
Make 命令提供了一些自动变量,他们的值与当前规则有关。
(1)$@ :
代指当前目标,就是Make 命令当前构建的那个目标, 好比 make foo 的 $@ 就值 foo
a.txt b.txt: touch $@ 等同于 a.txt: touch a.txt b.txt: touch b.txt
(2) $<
代指第一个前置条件
a.txt : b.txt c.txt cp $< $@ 等同于 a.txt: b.txt c.txt cp b.txt a.txt
(3) $?
代指比目标更新的全部前置条件 【即:更新的依赖文件】,好比, t1: p1 p2 , p2 的时间戳比t1 新,那么 $? 就代指 p2
(4) $^
代指全部前置条件
(5) $*
代指匹配符 % 匹配成功的部分 , 好比 %匹配 f1.txt 中的 f1 , 那么 $* 就表示 f1
(6) $(@D) 和 $(@F)
$(@D) 和 $(@F) 分别代指: $@ 的目录名和文件名
(7)$(<D) 和 $(<F)
$(<D) 和 $(<F) 分别代指: $< 的目录名和文件名
其它的自动变量请查看手册
用例一:
dest/%.txt: src/%.txt @[ -d dest ] || mkdir dest cp $< $@
解析:
1. 上面的代码就是将 src 目录下的 txt 文件 拷贝到 dest 目录下 。
2. @[-d dest] || mkdir dest : 是说 判断 dest 目录是否存在,不存在则建立之 , 而且关闭回声
3. cp $< $@ : 将 src 中的 txt 拷贝到 dest 目录下
判断和循环
Makefile 使用Bash 语法,完成了 判断和循环
用例一:
ifeq ($(CC),gcc) libs=$(libs_for_gcc) else libs=$(normal_libs) endif
用例二:
LIST = one two three test: for i in $(LIST); do \ echo $$i; \ done 等同于 test: for i in one two three; do \ echo $i; \ done
函数
Makefile 还能够使用函数 , 它许多内置函数 见手册
格式:
$(function arguments) 或者 ${function arguments}
Makefile 实例
编译 c语言项目
edit: main.o kbd.o command.o display.o cc -o edit main.o kbd.o command.o display.o main: main.c defs.h cc -c main.c kdb.o: kbd.c defs.h command.h cc -c kbd.c command.o: command.c defs.h command.h cc -c command.c display.o: display.c defs.h cc -c display.c clean: rm edit main.o kbd.o command.o display.o .PHONY: edit clean