Make和Makefile学习笔记

make和makefile

为什么需要make和makefile?

对于大型程序来说,多文件之间具有耦合和关联性,如果其中部分文件更改后,关联文件没有重新编译,就会导致程序出错的潜在问题,通过makefile可以执行make规则,make就可以自动执行gcc指令。

程序的编译过程主要分为编译和链接

编译时,主要是要告诉编辑器头文件的所在位置,当文件中语法全部正确时,编译器就可以自动编译出中间目标文件(.o或者.obj)编译器只检查程序语法,以及函数、变量的声明。

链接时,主要是链接函数和全局变量,链接器器不管函数所在的源文件,只管函数的中间目标文件,当中间目标文件过多时,就需要给中间目标文件打包提高编译效率,打包好的文件就叫库文件(.lib或者.a)链接器会在所有的目标文件中找寻函数的实现。\

GNU make的基本规则:

target ... : prerequisites ... (1command2)
    ...
    ...

在makefile中,注释用#开始。

1.依赖关系(前置条件)

依赖关系定义最终应用程序中的每个文件于源文件之间的关系,就是上文GNU make规则中(1)处。写法如下,前面为目标名称,后面紧跟一个冒号,接着是用空格或者制表符隔开的多个文件依赖列表:

myapp: main.o 2.o 3.0
main.0: main.c a.h

在makefile中,定义目标all可同时制作多个文件,若未定义,则make只创建在makefile中找到的第一个目标!

除了文件名当目标外,还可以是一些伪命令,比如clean:,这样在执行make clean指令后,就会自动执行后面的shell指令。

all: myapp myapp.l

依赖关系有什么用?它指定了文件之间的依赖关系,当开始编译时,如果有依赖文件发生改变(时间戳比其他的新),编译器就会自动寻找其关联的文件,并进行编译,保证全局代码的最新。

如果只有文件名,没有依赖关系的情况下,则表示每次都执行该目标名称后面的指令。

2.创建规则

在上述GNU make规则中(2)就是在生成目标文件后可以执行的shell命令,在设定依赖编译器只知道依赖关系,makefile还需要知道如何创建文件。因此我们可以自动创建规则,大多数规则都包含一个简单命令,在每一个命令前有且只能有一个制表符

myapp:main.o 2.o 3.o
	gcc  -o myapp main.o 2.o 3.o

在这些编译命令中,主要是使用对应的编译链工具(如gcc),并且通过指定参数完成编译。

PS:【1】每一行命令都在单独的shell中执行,指令之间没有继承关系。如果需要继承关系,但是又太长,可以使用反斜杠转义 \ 。

​ 【2】 在正常执行的情况下,make会打印每一条指令,然后再执行,叫做回升(echoing)在每条命令前加上@,就可以关闭回声(输出)其实就是编译的时候会出现的一大堆的字的来源。

test:
    @echo $$HOME

在makefile中还有一些自动变量:

$@ ——目标文件(shell中:输出用[“ ”]括起来的情况,单独输出)

$^ ——所有的依赖文件

$< ——第一个依赖文件

$? ——指代所有依赖文件中的最新的那个(shell中:最后运行的命令结束代码(返回值))

$ ——指代通配符%的匹配部分(shell中:所有的参数列表)*

以上这些是makefile中用来简化创建规则的,第一个是直接指代上面依赖关系中的目标文件,第二个是指代所有的依赖文件,第三个是指定第一个依赖文件,可以减少重复书写文件名的麻烦。

其他的(shell指令中的):

$$ ——shell本身的PID(ProcessID)

$! ——shell最后运行的后台PID

$- ——使用Set命令设置的Flag一览

$1~$n ——添加到Shell的各参数值

dest/%.txt: src/%.txt
    @[ -d dest ] || mkdir dest
    cp $< $@

在makefile中还有一些判断和循环的语句,遵循Bash语法:

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif=

甚至还可以使用函数,目前还没用过:

$(function arguments)

3.宏

makefile中的宏,类似于C语言中的#define,定义和引用方式如下所示,一般写在makefile的开头,用于方便替换一些常用的变量。

MACRONAME=value

$(MACRONAME)
${MACRONAME}

在宏定义内容过长时,可以用\进行换行。

在makefile中,“=” 还有一些特殊的赋值语句:

“=” ——赋值的值为makefile中最后被指定的值,会将整个makefile展开,拉通决定变量的值

“:=” ——直接赋值,直接将当前等式右边的值赋给左边。

“?=” ——如果该变量没有被赋值,则赋予等号后面的值。

“+=” ——将等号后面的值加到前面的变量上。

在make中,还提供了一系列内置变量,$(CC)指向当前使用的编译器等。

4.内置规则

make本身带有大量的内置规则,就是说执行make+可执行文件名 后,make会自动匹配该文件并编译。并且我们可以只在makefile中指定依赖关系即可。通过make -p可以打印出make指令的内置规则,通过make -MM指令可以导出项目中的依赖关系清单。

5.多目标

通过all:指定,同时生成多个目标。

通过install:指定,让可执行文件直接安装。

通过clean:指定清理临时文件。

6.Linux内核中的makefile

linux内核也采用makefile文件对内核进行编译,并且可以通过.config文件直接生成makefile。.config文件可以通过menuconfig等方式进行配置。

posted @   伦敦烟云  阅读(63)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示