在本章节介绍make工具。
你可能会遇到一些名词:GNU,Linux,make。它们是什么,又有什么样的联系?
在1970年,最早的UNIX系统被开发出来。在UNIX刚开发出来的时候,为了促进UNIX系统的发展,AT&T公司将UNIX源码许可证授权给一些学术机构,许多机构在UNIX源码的基础上开发,开发出了很多类UNIX系统,UNIX系统在这一时期得到非常快速的发展。但是后来,AT&T公司将UNIX商业化并且闭源了,不在将UNIX源码的许可证授权给一些学术机构。
为了避免版权纠纷,一个叫Richard M. Stallman的大叔发起了GNU计划,在类UNIX系统上从新开发出一个开源的操作系统。GNU是一个名字递归的缩写(GNU’s Not Unix!)。GNU是一个计划,它的目标是创建一套完全自由的操作系统。Rechard M.Stallman大叔为GNU计划开发著名的GCC和Emacs编辑器,并起草了GNU通用公共许可协议(GNU General Public License,GNU GPL),创造了copyleft的授权方式。所有的GNU程序遵循 "copyleft"原则,既可以拷贝、修改、出售,但源代码必须对每个用户公开,所有用户都可以获得修改后的源码。Rechard M. Stallman设立了自由软件基金会(Free Sofeware Fundation Inc. ,简称FSF)。
有了GNU计划,大家开始着手开发,开发出了很多项目,比如著名的GCC,GLIBC,还有Rechard M. Sstallman大叔开发的Emacs编辑器,基本上实现了GNU计划,但GNU计划还缺少一个操作系统内核。GNU项目从1990年开始开发GNU HURD,这个GNU HURD项目一直不能完成。这时候,一个叫Linus的同学在Minix的启发下开发了Linux,Linux只是一个系统内核,刚好这正是GNU计划的一部分,于是两者一拍即合,一起发布了GNU Linux。Linux系统启动之后,运行的是gcc,bash,emacs等软件。
make也是GNU计划的一个项目。GNU make是一个项目构建工具,由Rechard M. Stallman大叔和Roland McGrath开发。
在准备使用make工具之前,你需要写一个文件名为Makefile,顾名思义,Makefile文件就是一个项目的配置文件信息,由make程序执行。在一个程序中,可执行文件一般需要从object文件(.o文件)更新。一旦你写好了Makefile文件,你就可以在shell里使用make:
make
make工具使用Makefile里面的数据为基础,并且决定哪些文件需要被更新,每个文件的执行过程都记录在Makefile文件里面。Makefile文件告诉make程序如何编译和连接一个程序。
4.1一个简单的makefile包含了一个规则,如下面描述的那样:
target : prerequisites
recipe 1
recipe 2
recipe 3
......
接下来,我们开始写一个Makefile。这里我准备通过用一个项目介绍Makefile,cJSON是c语言编写的JSON解码器,代码非常简洁,只有750行代码。先将cJSON项目下载下来,点击这里下载cJSON源码,
解压文件:
tar -zxvf cJSONSourceFiles.tar.gz
4.2接下来开始为这个项目创建Makefile文件,使用vim将下面内容粘贴到一个文件中,并命名为Makefile,如下:
all: test
test: test.c cJSON.o
cc -W -Wall -o test test.c cJSON.o -lm
cJSON.o: cJSON.c cJSON.h
cc -W -Wall -c -o cJSON.o cJSON.c
clean:
rm -rf *.o test
来看一下这个Makefile文件是怎么写的,测试文件test.c用于生成一个可执行文件,它会用到cJSON.h里面的函数,而cJSON.h的函数实现写在cJSON.c中,所以,生成可执行文件的时候需要链接cJSON.o文件,cJSON.c文件编译之后就是一个.o文件了。
下面是对编译参数的介绍:
-W:关闭所有警告信息。
-Wall:打开警告信息。
-o:输出目标文件
-lm:链接库math。
-c:编译目标文件,但不链接。
4.3在当前文件打开命令行,输入命令:make就可以构建整个cJSON项目了。下面来看一些Makefile的执行流程:
①:target为all,all需要依赖条件test,test文件存在且最新吗 ? 报make: Nothing to be done for ‘all’ :执行②;
②:target为test,test需要依赖条件cJSON.o,cJSON.o文件存在且最新吗 ?执行⑧⑨ :执行④;
④:target为cJSON.o,cJSON.o需要先决条件cJSON.c和cJSON.h,cJSON.c和cJSON.h文件存在吗 ?执行⑦ : 报错make: *** No rule to make target `cJSON.c’,
⑦:是一条熟悉的gcc命令,写gcc命令之前需要敲一个tab键,执行完⑦后回到②的步骤,此时步骤②的依赖条件都满足了,执行下面的gcc命令⑨,编译test.c文件并且链接cJSON.o文件,生成可执行文件test。
⑩:target为clean,clean没有需要的先决条件,所以默认不会执行,除非指定参数才会执行。
make的执行流程不知道我讲清除没有,有点印象的话,我们赶紧生成一个程序,跑一下cJSON项目。在Makefile的同级目录下打开命令行,输入命令:
make
如果你按照上面的流程写,不出意外的话,将会得到下面的输出:
gcc -W -Wall -c -o cJSON.o cJSON.c
gcc -W -Wall -o test test.c cJSON.o -lm
......
上面的两条gcc命令看到之后,是不是觉得很熟悉!这和我们在shell里写的编译命令是一样的!
如果要删除编译的文件,可以执行命令:(这和上面讲到的make执行流程第⑩是一样的指定一个参数执行,就会执行⑪)
make clean
4.4 编译好了可执行文件,下面就可以运行了:
./test
输出:
{
"name": "Jack (\"Bee\") Nimble",
"format": {
"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,
"frame rate": 24
}
}
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
[[0, -1, 0], [1, 0, 0], [0, 0, 1]]
{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http:/*www.example.com/image/481989943",
"Height": 125,
"Width": "100"
},
"IDs": [116, 943, 234, 38793]
}
}
[{
"precision": "zip",
"Latitude": 37.766800,
"Longitude": -122.395900,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
}, {
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.026020,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}]
{
"name": "Jack (\"Bee\") Nimble",
"format": {
"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,
"frame rate": 24
}
}
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
[[0, -1, 0], [1, 0, 0], [0, 0, 1]]
{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http:/*www.example.com/image/481989943",
"Height": 125,
"Width": "100"
},
"IDs": [116, 943, 234, 38793]
}
}
[{
"precision": "zip",
"Latitude": 37.766800,
"Longitude": -122.395900,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
}, {
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.026000,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}]