五、Makefile
知识点补充1.mv命令除了可以移动文件和目录之外还具有给文件重命名的作用mv旧文件名 新文件名2.ls命令的常用参数ls -l全称--longlong listing (长格式详细列表)作用以长格式显示文件的详细信息。ls -a全称--all作用显示所有文件包括以.开头的隐藏文件 / 目录。ls -R全称--recursive递归的作用递归列出当前目录及其所有子目录里的内容。ls-h全称--human-readable作用格式化文件大小的单位但前提是得先让 ls 显示文件大小ls -lls -F全称--classify作用在文件名后加符号直观区分文件类型。/目录 (比如 redis-5.0.10/ )*可执行文件 (比如 app*)软链接文件|管道文件 (FIFO)套接字文件3.-f全称Force强力强制作用忽略不存在的文件 如果 objs 列表里的文件已经不存在了rm 不会报错也不会停止执行。不提示 即使文件有写保护它也会直接删除而不询问。4.不同环境/系统的ll意义并不相同很多 CentOS / 旧版系统中代表ls -s而我的虚拟机中它其实是ls -laF的缩写具体定义可以通过alias ll进行查看。5.$表示目标文件的完整名称但其实Makefile 命令前的还可以关闭命令回显不打印命令本身如果不加test: echo abc终端会输出如果加了test: echo abc终端会输出介绍与使用 Makefile什么是 Makefile一个工程中的源文件不计其数其按类型、功能、模块分别放在若干个目录中Makefile 文件定义了一系列的规则来指定哪些文件需要先编译哪些文件需要后编译哪些文件需要重新编译甚至于进行更复杂的功能操作因为 Makefile 文件就像一个 Shell 脚本一样也可以执行操作系统的命令。Makefile 带来的好处就是“自动化编译”一旦写好只需要一个make命令整个工程完全自动编译极大的提高了软件开发的效率。make 是一个命令工具是一个解释Makefile文件中指令的命令工具一般来说大多数的IDE都有这个命令比如 Delphi 的 make VisualC 的 nmake Linux 下 GNU 的 make 。初识 Makefile这里是提前准备的资源如果需要私我可以看到这个 redis-5.0.10 目录下有 Makefile 文件打开看看内容vimMakefile可以看到这个文件内容中 执行了cd src和make命令因此真正的 Makefile 文件处于 src 中。进入 src 目录查看 Makefile 文件内容cdsrcvimMakefile可以看到很多内容Make 文件命名和规则(1) 文件命名makefile 或者 Makefile(2) Makefile 规则一个 Makefile 文件中可以有一个或多个规则目标 . . . : 依赖 . . .命令 (Shell 命令). . .目标最终要生成的文件 (伪目标除外)依赖生成目标所需要的文件或是目录命令通过执行命令对依赖操作生成目标 (命令前必须 Tab 缩进)Makefile 中的其他规则一般都是为第一条规则服务的第一个 Makefile 文件1.进入目标目录并创建 Makefile 文件cdlesson07vimMakefile2.向 Makefile 中添加以下内容后保存并退出app:sub.c add.c mult.c div.c main.c gcc sub.c add.c mult.c div.c main.c-oapp3.执行make指令如果提示出错就先执行以下命令进行安装sudoaptinstallmake执行make可以看到执行成功4.查看当前目录是否生成 app 程序如果生成则运行ls./app可以看到运行成功Makefile 文件工作原理(1) 工作原理原理1命令在执行之前需要先检查规则中的依赖是否存在如果存在执行命令。如果不存在向下检查其它的规则检查有没有一个规则是用来生成这个依赖的如果找到了则执行该规则中的命令。原理2检测更新在执行规则中的命令时会比较目标和依赖文件的时间如果依赖的时间比目标的时间晚需要重新生成目标。如果依赖的时间比目标的时间早目标不需要更新对应规则中的命令不需要被执行。原理3默认只执行第一个目标当你只输入 make 时它会把在 Makefile 中出现的第一个非变量定义的目标作为它的终极任务。后面所有的语句、所有的规则只有在直接或间接被第一个目标所依赖时才会被执行。如果后面的语句跟第一个目标没关系make 就会完全无视它们。(2) 检验原理检验原理11.将刚才的 Makefile 文件内容改成这样app:sub.o add.o mult.o div.o main.o gcc sub.o add.o mult.o div.o main.o-oapp sub.o:sub.c gcc-csub.c-osub.o add.o:add.c gcc-cadd.c-oadd.o mult.o:mult.c gcc-cmult.c-omult.o div.o:div.c gcc-cdiv.c-odiv.o main.o:main.c gcc-cmain.c-omain.o2.输入ll查看当前目录文件可以看到第一条规则直接执行会缺少依赖文件。3.继续执行make由于第一条规则的依赖不存在所有先向下查看其他规则可以看到先执行了五个 .c 文件的汇编得到五个 .o 文件后才执行第一条规则开始链接。检验原理21.在 main.c 文件的return 0的行位进行回车然后保存退出2.再执行make指令由于目标 app 依赖于 sub.o, add.o, mult.o, div.o, main.o每个 .o 文件又各自依赖于对应的 .c 文件。修改了 main.c 文件之后make发现 main.c 的时间比 main.o 新执行了gcc -c main.c -o main.o因为 main.o 刚被重新生成时间戳变新make回头看第一条规则发现依赖 main.o 比目标 app 更新与之执行链接命令生成新的 app 。检验原理31.在当前目录创建 test.c 文件touchtest.c2.打开 Makefile 文件向其中加入test.s:test.c gcc -S test.c -o test.s3.再次执行make可以看得终端提示 app 已经最新由于没有指定目标比如没写 make test.smake 会自动寻找 Makefile 中的第一个目标又因为新加上的语句与第一条规则的依赖没有关系因此没有被执行。变量(1) 变量分类我们将 Makefile 变量分为三大类其中“自动变量”是提高脚本通用性的核心。1. 自定义变量用户手动定义的变量建议使用大写习惯用法或清晰的小写。获取值使用$(VAR)或${VAR}。2. 预定义变量Make 内置的变量代表了默认的工具链变量名含义默认值AR归档维护程序的名称arCCC 语言编译器cc(通常指向 gcc)CXXC 语言编译器gCFLAGSC 编译参数无CPPFLAGSC 预处理参数无3. 自动变量注意自动变量只能出现在规则的命令行中。$代表规则中的目标文件的完整名称。$^代表规则中所有的依赖文件去重。$代表规则中第一个依赖文件的名称。(2) 常用赋值符这是 Makefile 最容易混淆的地方理解它们的时机决定了你的脚本是否稳健。符号类型核心逻辑:立即赋值定义时立即展开锁定当前值。递归赋值使用时才展开可以引用后面的变量。?条件赋值只有变量未定义时才赋值。追加赋值在原值末尾添加内容自动加空格。举个例子区分下:和给定一下 Makefile 文件内容B : $(A) A : abc test1: echo $(B)由于:是立刻赋值A 还未定义所以终端输出空。将B : $(A)替换成B $(A)B $(A) A : abc test1: echo $(B)可以发现终端输出了 abc 。那么来说下原理Makefile 分两个阶段阶段 1解析阶段读完全部文件读所有目标:读所有变量 值遇到不展开只存表达式遇到:立即展开算成最终值阶段 2执行阶段运行命令执行规则里的echogcc遇到变量才现场展开、现场查找、现场计算遇到谁就查谁当前最新的值建议在 90% 的场景下请优先使用:避免不必要的递归和性能损耗。(3)样例使用变量简化代码可以看到正常运行模式匹配由上图可以看到如果需要为每个 .c 文件逐一汇编真的很麻烦这个时候模式匹配可以很好简化代码量运行依然成功函数wildcard$(wildcard PATTERN ...)wildcard通配符功能获取指定目录下指定类型的文件列表 (通配符匹配)参数PATTERN 指的是某个或多个目录下的对应的某种类型的文件如果有多个目录一般使用空格间隔返回得到的若干个文件的文件列表文件名之间使用空格间隔示例$(wildcard *.c ./sub/*.c)返回值格式a.c b.c c.c d.c e.c f.c样例对于刚才的这个 Makefile 文件不难发现如果依赖文件过多代码依然会显得繁琐wildcard这个函数可以很好的解决这个问题target : app src : $(wildcard ./*.o) # $(target):$(src) # $(CC) $(src) -o $(target) # 或者 $(target):$(src) $(CC) $^ -o $ %.o:%.c $(CC) -c $ -o $运行一下报错了这是由于wildcard是在磁盘上通配符匹配.o 文件又没有真正得到因此 src 的赋值还是要改成src : $(wildcard ./*.c)。patsubst$(patsubst pattern, replacement, text)功能查找 text 中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式 pattern如果匹配的话则以 replacement 替换。pattern 可以包括通配符%表示任意长度的字串。如果 replacement 中也包含%那么replacement 中的这个%将是 pattern 中的那个%所代表的字串。(可以用\来转义以\%来表示真实含义的%字符)返回函数返回被替换过后的字符串示例$(patsubst %.c, %.o, x.c bar.c)返回值格式x.o bar.o样例当 src 的赋值改成src : $(wildcard ./*.c)Makefile 文件的内容逻辑就发生了更改为了与原先逻辑一致我们可以把获得的文件名批量修改成我们要的运行也是成功的伪目标.PHONY(1) 什么是伪目标伪目标是 Makefile 中一种特殊的目标它不是磁盘上的真实文件而是一个命令集合的名字比如clean、install。(2) 为什么要用.PHONYclean: rm $(objs) -f这条规则没有依赖只有目标如果当前目录存在一个叫clean的文件make clean会误以为目标已经是最新的从而不执行命令。.PHONY: clean就是告诉 Makeclean不是文件每次都要执行它的命令。(3) 示例.PHONY: clean clean: rm $(objs) -f