Title:makefile 学习
0 吐槽
发现自己一年没更新博客了,掏出存货来刷一下存在感 ( ̄▽ ̄)“…
主要原因 是:① 忙于实习;② 懒于撰稿;③ 完美主义越来越强,note回看就修订觉得发不出手、过半个月回看又修订还是发不出手,结果懒得发了… ( ̄▽ ̄)”
先随手发;之后本地修订后,再回来改线上的吧~
1 Basic Concept
1.1 是什么
make是GNU里的一个utility,可实现方便的自动化编译,在unix和linix都可以写;
首先要写一个文本叫 makefile,然后调用命令make,terminal即会按makefile的内容对工程或源文件进行 re-compile or link;
makefile作用是对bash等命令进行方便地一键重用,故可以用makefile进行compile、link、simulation、执行程序等都可以,反正要执行什么 bash 指令自定义写进去就完事儿了;
makefile中写的内容,表示的是 how、when对什么文件进行什么操作,从而让我们后续可以方便的 “一键remake”。
1.2 OS概念复习
compile 和 link 的概念
- 源码compile(检查语法,有函数声明即可通过)后得到中间文件(windows是
.obj、UNIX是.o); - 对中间文件进行打包得到库文件(windows是
.lib、UNIX是.aArchive File); - 把它们link(合成一坨一个可执行文件)——链接函数和全局变量等,找不到函数实现就会出现link error——得到可执行文件(windows是
.exe、UNIX无后缀);
windows下的make
一般默认 make是Unix OS下;
若是windows下,得看IDE——Delphi是用make,VC++用nmake;
windows下要想make就得下载MinGW编译器了.
1.3 怎么用make
-
①terminal中敲
make,OS会自动按顺序寻找文件GNUmakefile、makefile、Makefile; -
②也可手动指定执行makefile文件(很少用,一般直接
make)make -f <...makefile>
1.4 makefile 与 shell
-
makefile和shell的区别
作用:shell是为了重用terminal内的命令服务的,是系统程序员与OS内核的交互接口;makefile是用于工程compile/link用的;makefile内可写shell命令、调用shell脚本,如:
+ ./xxx.sh;写法区别:
- shell内 =号不允许有空格,makefile内 =号空格无碍;
- shell内开头要写
#!/bin/bash,后缀名是.sh; - shell内是自上而下执行,有数组、循环等;makefile内执行shell命令是一行独立一个进程,若想一个进程统一执行,得在末尾用
\表同一行。 - shell内通配符:
*;makefile内通配符:%; - shell内变量用
{},命令串用();makefile中访问变量用$()或${}都行;
-
shell与cshell的区别
linux下提供了很多种shell: (简称sh)、C-Shelll(简称csh)、Korn Shell(简称ksh)和Bourne Again Shell (简称bash)。
- Bourne Shell,文件后缀
.sh,是Unix的默认shell,也是其他shell的基础;性能好,但用户交互低; - C shell,文件后缀
.csh,语法类似C,提供了.sh没有的用户交互功能——命令补全、命令别名等;与.sh不兼容; - Korn Shell:文件后缀
.ksh,结合了.sh和.csh的优点,并向下兼容了.sh; - Bourne Again Shell,即bash,是Linux的默认shell,结合了以上
.sh、.csh、.ksh的优点,并向下兼容了.sh;最强!
- Bourne Shell,文件后缀
2 语法
2.1 基本格式⭐️
目标target: 所需prerequisites
(TAB) command (任意的Shell命令)
label:
(TAB) command...
- 要注意 =号的空格无碍;tab很关键,和python一样;
- 用
make触发执行makefile后,make会检查 各prerequisite的更新日期,若比target新,就重新执行此句的命令。 - makefile会把第一个target作为编译目标,然后逐层迭代的判断是是否需要更新所需的 prerequisites. 其他没被牵扯到的target或者label,需要自己调用
make label来单独执行。 - 若target不是实体存在的文件而是个虚名,那它称呼为**label**,label后面的prerequisites不用加文件名,因为target是个label的话压根就没有实体文件给你比日期,写文件也没啥用;
一般label后面的prerequisites也写label;那此时若make target就会先去make prerequisites里的label,嵌套执行,就当成函数好了. .o文件与自身相关的.h、.c文件的prerequisites可省略,因为会自动补充;- 养成好习惯,自己写一个
cleanlabel,用于删除上一次调用make后产生的所有中间、执行文件,日后用make clean就能起到复位的作用;cleanlabel内,务必用rm -rf而不是rm,不然报错一句,后续clean都不执行了!
2.2 举个例子Case ⭐️
-
makefile的触发条件
① prerequisite比target要新;
② 或者 prerequisite不存在;
所以,若target的A不是实物,永远不会生成,则此语句每次make都 必会被调用。
B = ? C = ? # B和C是具体的文件,A只是抽象的程序tag无对应实体文件 A: B C B -o C #和A没关系,A永远不会生成,故此语句必执行也可以有多个抽象的程序块,如下:
all: do1 do2 do3 # 四个label do1: A B C ...balabala... do2: E F ...balabala... do3: ...balabala... -
makefile中
.PHONY的学习考虑以上makefile的case,会出现这样的问题:
若在写完makefile后,本地恰巧出现了个文件或文件夹的名称也叫做 A ! 那按makefile的规则,由于A被检测到文件存在,且prerequisite B、C并不是比 A 要新的话,此行makefile就不会执行了!总结:makefile中的target字段本该是无实体文件的抽象label,却因本地存在同名文件/文件夹,使此行makefile无法如意执行。
Solution:在A前面加上
.PHONY(可以理解为关键字),声明此A就是label、不是实体文件,别去检索当前路径下是否存在同名文件啦!之后,即使makefile目录下有同名文件A,makefile也会按label进行处理此target.B = ? C = ? .PHNONY: A #声明A是phony抽象的 A: B C B -o C #和A没关系,A永远不会生成,故此语句必执行.PHONY只是一个声明,不会影响后续label的执行的。
2.3 细节的其他makefile语法
-
target和prerequisite的数量
至少一个target:target是一个东西或事件、自定义函数名。最好加个注释说明一下是啥;
可以0~n个prerequisite:prerequisite是前置文件,可用通配符
*; -
行注释 用
#; -
行续写,于行尾 用
/, 表多行代码用一个进程执行;makefile内写的程序默认一行一个进程执行,故需要将程序串行执行,得自己加
/;注:注释也可用
/进行续写;使用/后会读取“空行”,故下一行别接注释! -
宏定义 Macro define
- 定义: 等号
=,pi=3.14, 可嵌套,大小敏感; - 使用:
$(宏名) - 常用系统宏:
- $@,当前target
- $<,第一个条件的名字
- $?, 所有条件中 比 当前target更newer的 民名字
- $^, 所有条件的名字
- $(MAKE),就是预设的 make命令
- 定义: 等号
-
define
即多行版本的宏定义
define 宏名 ... ... endef -
include
导入别的makefile(类似导入库),文件名语法可用正则表达和shell.
若多个导入的makefile库内,出现 target冲突,取最后一行定义为准
-
正则表达式,
如:通配符等——
*, [], ?,@, @好像是让系统回话功能闭嘴,干净。 -
Reference

本文详细介绍了Makefile的基本概念、使用方法和核心语法,包括目标、依赖项、命令的执行规则,以及如何处理phony目标。通过实例解析,展示了Makefile如何实现编译过程的自动化,同时提到了在Windows环境下使用Makefile的注意事项,以及解决Makefile中目标与本地文件冲突的问题。文章还提及了宏定义和include指令,帮助读者深入掌握Makefile的编写技巧。
322

被折叠的 条评论
为什么被折叠?



