makefile从入门到实战 第一章 认识makefile(一)

发布时间:2026/6/6 23:29:44

makefile从入门到实战 第一章 认识makefile(一) 一、makefile入门1、相关概念介绍1一个工程中的源文件不计其数其按类型、功能、模块分别放在若干个目录中makefile定义了一系列的规则来指定哪些文件需要先编译、哪些文件需要后编译、哪些文件需要重新编译甚至需要进行更复杂的功能操作。2“make”是一个命令工具它解释makefile中的指令在Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。2、入门的第一个Makefilemakefile基础语法1makefile的基本语法目标:依赖1 依赖2……Tab 命令1Tab 命令2……①目标一般是指要编译的目标也可以是一个动作它由程序员自行命名。②依赖指执行当前目标所要依赖的先项或者说前置条件包括执行其它目标、某个具体文件或库等一个目标可以有多个依赖也可以没有依赖如果没有则不写如果有多个依赖则用空格分割。③命令该目标下需要执行的具体命令可以没有也可以有若干条如果有多条命令则每条命令写一行命令前加“”会抑制命令本身的打印就是把命令的原文内容打印出来一般不需要加。2第一个Makefile示例a:b c echo hello A_world ls ./ #列出当前目录下的文件 b: echo hello B_world c: echo hello C_world目标a依赖目标b和目标c那么在执行目标a时需要先执行目标b和目标c目标b和目标c的执行顺序不固定再执行目标a3、make命令工具常用选项1make命令工具的格式make [-f filename] [other options] [target]2make默认在当前目录中按顺序依次寻找名为GUNmakefile、makefile、Makefile的文件作为输入文件仅有一个输入文件找到即进行编译不是三个都读取一遍如果不写参数指定执行的目标则make执行输入文件中的第一个目标如果第一个目标有依赖则会先执行其所依赖的目标再执行它。make # 不写参数自动读上面三个文件之一然后执行第一个目标 make b # 手动指定目标b只执行输入文件中的目标b3常用选项可叠加①“-f filename”make默认只寻找名为GNUmakefile、makefile、Makefile的文件如果是自定义的脚本名字如build.mk则需要加上该参数。make -f build.mk②“-n”空跑、预览指令不会真实运行多用于调试检查Makefile中的指令是否写错。make -n③“-s”只执行命令但不显示具体命令作用和“”一样只不过“”是屏蔽单行指令打印。make -s④“-C”指定Makefile所在的目录如./src。make -C ./src⑤“-v”查看make工具的版本号。make -v⑥“-w”多级工程跨目录编译时打印执行前和执行后的所在路径。make -w4、程序编译流程1以gcc为例gcc命令能够直接从源代码生成目标可执行文件其格式如下所示。需要注意的是gcc是C编译器如果要编译C源码则需要加“-lstdc”。gcc main.c gcc -lstdc main.cpp2一条gcc命令其实是分4步进行编译的①预处理展开#include替换#define宏删除注释如果是编译.c文件则输出.i文件如果是编译.cpp文件则输出.ii文件。gcc -E main.c main.i gcc -E main.cpp main.ii②编译经过语法分析、词法分析、语义分析和符号会中把C/C代码转换成汇编代码结果保存在.s文件中。gcc -S main.i gcc -S main.ii③汇编把汇编代码转换为二进制/机器指令结果保存在.o文件中。gcc -c main.s④链接把多个.o文件合并C的链接还需要合并标准库libstdc.so以及符号表的重定位生成可执行程序。gcc main.o gcc -lstdc main.o5、makefile中的变量1自动系统变量①自动系统变量只在“目标:依赖”下的命令中生效不用手动赋值使用方法比如app:main.c func.c test.c $(CC) $^ -o $②自动系统变量的符号符号释义上例目标app中的自动系统变量含义$当前目标的目标名$ app$当前目标的第一个依赖$ main.c$^当前目标的所有依赖空格分隔重复的将去除$^ main.c func.c test.c$当前目标的所有依赖空格分隔重复的也不去除$ main.c func.c test.c$?比目标新时间戳比目标文件晚的依赖文件$?只列出最近修改过的源文件更新时间比目标文件晚的依赖文件$*当前目标去掉后缀的名字如果当前目标名为“main.o”则$* main2系统内置常量①make工具出厂自带全局变量“make -p”可以查看全部系统内置常量及默认值。②常用的系统内置常量常量名释义默认值AS汇编程序的名称asCCC编译器的名称ccCPPC预编译器的名称cc -ECXXC编译器的名称gRM文件删除程序名rm -f3自定义变量①自定义变量由程序员自行定义可用于简化代码、方便统一修改编译参数作用有点类似于C语言中的#define。②自定义变量定义格式特别说明尖括号不是语法的一部分变量名 变量值③自定义变量调用格式特别说明尖括号不是语法的一部分$(变量名)${变量名}④自定义变量定义和使用举例# 自定义变量 CC gcc # 指定编译器 CFLAGS -g -Wall # 编译参数g调试、Wall告警 SRC main.c func.c OBJ main.o func.o TARGET app $(TARGET):$(OBJ) $(CC) $(OBJ) -o $(TARGET) $(CFLAGS) %.o:%.c $(CC) -c $ -o $ $(CFLAGS) clean: rm -rf $(OBJ) $(TARGET)6、makefile的运行流程和伪目标1make的时间戳原理编译时间最小化比如在Linux中每个文件都有一个“修改时间”即文件内容最后一次被改动的时间make会将目标文件和其任意一个依赖文件的时间比对如果依赖文件更新时间更晚则需要重新编译目标的依赖仍然是一个目标它也要和自己所依赖的文件做更新时间比对如果依赖文件更新时间更晚则也需要重新编译以此递归下去直到目标没有依赖则判断目标文件是否存在若存在则不需要重新编译否则需要生成目标文件2伪目标①如果目录中有与makefile中目标同名的文件比如“clean”而它正好没有任何依赖那么“make clean”会判定clean文件已经是最新的文件不会执行clean这个目标为了避免这种情况凡是只做动作、不生成与目标名同名文件的目标都声明为伪目标这样makefile就不会判断目标是否存在对应的文件以及是否需要更新。②伪目标的声明方式.PHONY: 目标名③伪目标声明举例.PHONY: clean clean: rm -rf *.o app7、模式匹配1模式匹配①“%”是通配符可以代表任意一串文件名作用类似于模糊搜索。②举例.PHONY:clean show OBJ sub.o multi.o calc.o add.o TARGETcalc $(TARGET):$(OBJ) $(CXX) $^ -o $ %.o:%.cpp $(CXX) -c $^ -o $ clean: $(RM) *.o $(TARGET)遇到sub.o自动匹配依赖sub.cpp遇到add.o自动匹配依赖add.cpp再配合自动系统变量——$ %.cpp、$ %.o可不用将目标和依赖关系逐个列出2两个Make内置函数①wildcard函数可以获取当前目录中所有指定后缀的文件。SRC : $(wildcard ./*.cpp) # SRC a.cpp b.cpp main.cpp 当前目录中有a.cpp、b.cpp、main.cpp②patsubst函数对被指定变量中的文件名做批量替换被指定的变量不会改变该函数是返回替换后的文件名常用于替换后缀。SRC$(wildcard ./*.cpp) OBJ$(patsubst %.cpp,%.o,$(SRC)) # SRC:a.cpp b.cpp → OBJ:a.o b.o 当前目录中有a.cpp、b.cpp4使用以上两个内置函数可以对模式匹配中的举例进行优化定义变量OBJ时可以不用再一一列出当前目录中的文件名。.PHONY:clean show OBJ$(patsubst %.cpp,%.o,$(wildcard ./*.cpp)) TARGETcalc $(TARGET):$(OBJ) $(CXX) $^ -o $ %.o:%.cpp $(CXX) -c $^ -o $ clean: $(RM) *.o $(TARGET)

相关新闻