欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

初始Mkaefile

程序员文章站 2022-05-27 07:52:53
初识MakefIle 在学习Linux过程中,我越发的觉得Linux系统给了使用者更大的*,同时也就增加了学习的成本。在gcc下去调试代码,没有了熟悉的VS,没有的人性话的错误提示(当然Makefile是有错误提示和警告的),也没有一键编译。全得自己来,但是在这个过程中,你将会熟悉系统的整个编译过 ......

初识makefile

在学习linux过程中,我越发的觉得linux系统给了使用者更大的*,同时也就增加了学习的成本。在gcc下去调试代码,没有了熟悉的vs,没有的人性话的错误提示(当然makefile是有错误提示和警告的),也没有一键编译。全得自己来,但是在这个过程中,你将会熟悉系统的整个编译过程,以及自己去写编译文件的那种快感。今天我将不去重点介绍编译的过程,重点在gnu的makefile怎样写,算是自己的一个总结。

在此我先说一下,我的makefile是在陈皓《跟我一起写makefile》学到的,里面讲的很是全面有兴趣的话可以认真的去读一遍(内容比较多)。

从认为,makefile是为生成,编译好的、可执行的文件(make只是一个根据指定的shell命令进行构建的工具),可以用来调试,也可以直接运行。有人可能要说用gcc自己写命令编译不是更好吗?但是如果一个项目有成百上千个.c .h文件怎么整。

从最简单的说起,一般情况下我们执行makefile文件直接make就可以了(有些人会进行一些骚操作这就不说了),会生成许多 .o文件最终会有一个或多个可执行文件。

咱们还是直接从例子中去体会吧!

src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))
target = sendmsg
mode = -g
cc   := gcc
$(target):$(obj)
$(cc) $(cflags) $^ -o $(target)

.phony:
clean:
rm -rf *.o $(target)

上面的代码块就是一个简单的makefile,我们一句一句的分析他的语法,等到末尾基本上简单的makefile也就会写了,再去系统的学习makefile也比较容易(请保持耐心):

整体的看最多的就是赋值符号,细心点会发现有两种复制方式 “=”和“:=”,这两个都是赋值符号,符号左边是变量,右面是所要赋值的内容;

"="是最基本的赋值,如同我们c/c++语言中的赋值一样;

":="表示覆盖赋值,也就是同一个变量在如果用":="赋值,则会覆盖掉之前的内容。

开始看第一句:

src = $(wildcard *.c)

之前说过“=”左边是变量,则"src"就是变量,想来我不用解释变量是什么意思吧。“=”右边是所要赋值的内容。

$(wildcard *.c)

在这内容里面"$"是函数的调用符,也是变量的调用符。就是说你调用一个变量或一个函数时前面要加上"$"来作为标志,表示这是个函数,或者这是个变量,并且里面的内容需要用括号括起来,括号可以用"{}"也可以用"()"但是最好统一一下不要混用,显得杂乱。(如果对指针比较熟悉的话很快就应该想到“$”跟指针中的“*”用法很类似,为了提取出对方的内容所要加的标志符,如果对指针不熟悉可以忽略这句话)。

因此上面那句话也可以这样写 ${wildcard *.c};

我们分析一下 $(wildcard *.c) 括号里面的内容“*”是通配符的一种,这里表示所有的以".c"结尾的文件,如果是"*.o"那就是所有的以“.o”结尾的文。“wildcard”是makefile中的函数,makefile中是有自己内置函数就像我们c/c++中的库函数一样。在这里“wildcard”函数的作用是来获取工作目录下的所有的 “*.c” 文件列表。makefile中不止这一个函数,还有很多可以自己去看去实践。

现在整体看“src = $(wildcard *.c)”这句话的意思就是,取出当前目录下所有的".c"文件并且将此赋给 src,那么 src 将会代表此目录下的所有 “.c”文件。

第二句

obj = $(patsubst %.c,%.o,$(src))

同样定义“obj”变量,调用“patsubst”函数,函数参数为“%.c”,“%.o”,“$(src)”;(函数中有多个参数时,以逗号隔开)

“%”也是一中通配符:非空长度任意的非空字符串。(“%”与“*”是有区别的)

“patsubst”函数的作用是将变量“src”中所用“.c”文件用“.o”替代。因此obj就是很多将后缀为".c"变成“.o”的文件集合;

第三、四、五句(同类型)

target = sendmsg
mode = -g
cc   := gcc

定义target变量名字角sendmsg;

定义“mode”变量且赋值为“-g”;

定义“cc”变量且赋值为"gcc"

第六、七句

 

$(target):$(obj)
$(cc) $(cflags) $^ -o $(target)

这段代码是核心,它符合makefile 的编译规则如下:

target ... : prerequisites ...
          command
          ...
          ...

target是目标文件(可以不止一个),prerequisites是目标的依赖文件(可以不止一个),(command)生成目标文件所需要执行的命令。

“目标文件”:即最终生成的可执行的文件;

“目标依赖文件”:即生成目标文件所需要的文件;

“命令”:即处依赖文件到目标文件的过程。注意命令需要以开头必须是table,这是命令的标识。

现在回过头去看我们的程序,五六句:

$(target),表示所要生成的目标文件;

$(obj),表示生成目标文件所需的依赖文件;

$(cc) $(mode) $^ -o $(target),处理生成目标的命令;

{命令中"$^"是自动化变量终端的一种,表示所有依赖文件的集合,如果有重复的依赖文件,则去掉重复的依赖文件(自动化变量:会将模式变量一次取出的一种机制变量,自动化变量有好几个,可以去详细解读)};

重点:在makefile中是以时间戳为参考标准去更新文件的,假若依赖文件中任意一个文件比目标文件的时间戳新,则再次编译目标文件,直到目标文件的时间戳为最新为止。

则五六句代码的理解就是:

以obj为依赖文件生成target文件,命令规则是“gcc -g $^ -o target”(假设懂 gcc 的基本语法)。

八、九、十句

.phony:
clean:
rm -rf *.o $(target)

这个小模块的目的是为了,清除所有的已经编译后的目标文件;在调试时改变更改程序后需要重新编译,那么将会生成新的编译文件;为了比避免出错,一般先会清除先前的编译文件,在重新生成编译文件。

在这个模块中“clean”时一条伪命令,为什么这样说,是因为它不会再编译的过程中执行,只有使用语句 “make clean”时才会执行此命令。其中“.phony”是伪命令的标识,可以选择不写。

最后“rm -rf *.o $(target)”,想来应该也都清楚了,清除所有以“.o”和“target”文件。

至此整个makefile结束。此段程序虽然简短但是五脏俱全,其中涉及很多makefile的知识点,可以由此去逐点击破,对makefile做个详细的了解。

如有问题请指出,我修改……一起进步!