目录
1.Linux下编译大型项目的一般方法
一般情况下,大型项目有很多源文件,其按类型、功能、模块分别放在若干个目录中,如何有序编译源文件?
Linux下编译大型项目可以用makefile命令,以开源的高性能键值存储数据库redis为例,目录的结构比较复杂
先将redis项目克隆到服务器上:
git clone https://github.com/redis/redis.git
tree命令查看redis目录的结构:
tree redis
而编译这些源文件只需要一个命令:make,而项目的构建方法写在Makefile文件中:
使用切换到项目的目录后,用make命令编译项目:
cd redis
make
全自动化构建
一段时间后完成编译
2.make和makefile
区别
make是命令,而makefile(或Makefile)是当前项目目录下的文件
makefile的简单编写
例如有一个test.c文件(功能是打印Hello World)需要编译,可以这样写makefile:
test:test.c
gcc test.c -o test #必须按tab键来分隔,否则会提示missing separator
make命令执行后,成功生成test可执行文件,并且./test命令让test打印了Hello World!字符串:
依赖关系和依赖方法
上述的makefile文件的"test:test.c"为依赖关系,表示test可执行文件依赖于test.c文件
依赖关系必须有依赖方法,"gcc test.c -o test"为依赖方法,必须按tab键来开头
清理方法
生成test可执行文件后,再次使用make会发现无法再次编译test.c,make命令会提示"up to date",即最新的
一般情况下需要清理掉test可执行文件后才能重新make,这里不建议手动执行rm命令,出错风险较大,建议将rm写到makefile中
test:test.c
gcc test.c -o test
clean:#clean没有依赖关系,:的右侧不用写
rm -rf test
使用make clean对可执行文件进行清除:
make clean
之后就能继续编译test.c
会发现执行make clean时,make会找makefile中的"clean"字符串,之后执行,现将clean改成step,看看执行make step能否清除可执行文件
test:test.c
gcc test.c -o test
step:
rm -rf test
会发现仍然可以
演示makefile中的多个依赖关系
这里可将生成可执行文件的步骤进行拆解:预处理→编译→汇编→连接
注意:依赖关系写完后紧接着写依赖方法
依赖关系图:
test:test.o
gcc test.o -o test
test.o:test.s
gcc -c test.s -o test.o
test.s:test.i
gcc -S test.i -o test.s
test.i:test.c
gcc -E test.c -o test.i
clean:
#其实可以写成rm -rf test test.o test.s test.i
rm -rf test
rm -rf test.o
rm -rf test.s
rm -rf test.i
make命令执行后,会产生test.i、test.o、test.s这些中间文件
可以使用make clean清除:
简单理解make的自动化推导
从上面的例子可以看出:test依赖于test.o,但没有后者,发现test.o依赖于test.s, 但没有后者, test.s依
赖于test.i, 但没有后者, 发现test.i依赖于test.c, test.c是有的,所以从gcc -E test.c -o test.i命令开始执
行
类似”递归”,像栈式结构
如果打乱顺序,make也能执行,例如
test.o:test.s
gcc -c test.s -o test.o
test:test.o
gcc test.o -o test
test.i:test.c
gcc -E test.c -o test.i
test.s:test.i
gcc -S test.i -o test.s
clean:
rm -rf test
rm -rf test.o
rm -rf test.s
rm -rf test.i
照样能按正确的顺序执行:
make的默认选项
单独的一个make命令就是执行make的默认选项
生成可执行文件后,调换clean到第一行
clean:
rm -rf test
rm -rf test.o
rm -rf test.s
rm -rf test.i
test.o:test.s
gcc -c test.s -o test.o
test:test.o
gcc test.o -o test
test.i:test.c
gcc -E test.c -o test.i
test.s:test.i
gcc -S test.i -o test.s
执行make命令后:
结论:make会自顶向下扫描makefile,遇到的第一个选项就是make的默认选项
判断可执行文件是否最新的方法
上文提到了生成test可执行文件后,再次使用make会发现无法再次编译test.c,make命令会提示"up to date",即最新的,那么make是如何判断可执行文件是否最新的呢?
答:比较修改时间,可执行文件相对于源文件是老的,不需要重新编译;可执行文件相对于源文件是新的,可以重新编译
当修改test.c后就能再次执行make
结论:如果源文件文件没有更新过,make不允许反复编译这样是为了提高运行效率
Linux下的三种时间类型
之前在OS6.【Linux】基本指令入门(5)简单提到过
stat命令
其中一个是作用查看文件的时间(stat全称display file or file system status)
例如查看之前生成的可执行文件test
可以看到时间一共分为三种
三种时间类型
修改时间(Modification Time,mtime)
文件=文件内容+文件属性,更改文件内容会更新修改时间
改变时间(Change Time,ctime)
文件=文件内容+文件属性,更改文件属性会更新改变时间
删除或增加文件的内容modify和change会同时修改,因为文件大小属于文件属性
访问时间(Access Time,又称atime)
1.从定义上来讲,更改文件内容或者文件属性都会改变访问时间,但某些Linux系统会因为访问时间修改
太过于频繁而修改访问时间的更新策略: 例如访问多次才会更改更新访问时间
2.touch在新建文件时会更新所有时间
在不更改源文件的情况下让make反复编译
方法1:改变修改时间
见如下图:
方法2: .PHONY修饰
phony adj.假的
例如以下写法:
.PHONY:test
test:test.c
gcc test.c -o test
clean:
rm -rf test
认为test是伪目标,每次make都会执行gcc test.c -o test,而不管test是否更新过;如果test没有用.PHONY修饰,那就要看test的修改时间
但工程上不建议使用.PHONY修饰来可执行文件,可以用.PHONY修饰clean,例如:
.PHONY:test
test:test.c
gcc test.c -o test
.PHONY:clean
clean:
rm -rf test
这样就能多次make了:
简写符号
$@和%^
可以使用$@ %^来简写
test:test.c
gcc $^ -o $@
.PHONY:clean
clean:
rm -rf test
其中$@代表:前面的test,而$^代表:后面的test.c
仍然能正常make
@
使用@可以隐藏命令的执行细节
test:test.o
@gcc test.o -o test
test.o:test.s
@gcc -c test.s -o test.o
test.s:test.i
@gcc -S test.i -o test.s
test.i:test.c
@gcc -E test.c -o test.i
.PHONY:clean
clean:
rm -rf test test.o test.s test.i
这样使用make就不会显示任何信息