文档章节

Makefile 学习 1 - 基于若干 Blog 的汇总

公孙衍
 公孙衍
发布于 09/21 20:36
字数 2511
阅读 1
收藏 0

基于若干 Blog 汇总的 makefile 教程

陈皓 https://blog.csdn.net/haoel/article/details/2886

Makefile 基础知识

1. 什么是 Makefile?

规定软件工程的编译规则。一个工程中的源文件,其按类型功能模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile 就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile 带来的好处就是——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。

2. 程序的编译和链接

程序编译过程: 源文件 --- 编译 compile ---> 中间代码文件 .obj (windows) .o (unix) --- 链接 link ---> 可执行文件 .exe

编译:语法的正确;函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。

链接:主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。

总结:源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)

3. Makefile 规则

Makefile 文件模板 -

target (目标文件,object file,exe file or Label) :prerequisites (要生成 target 文件所需要的文件或者目标)…..
command (make 要执行的 shell 命令)

………

这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。说白一点就是说,prerequisites 中如果有一个以上的文件比target文件要新的话,command 所定义的命令就会被执行。这就是 Makefile 的规则。也就是Makefile 中最核心的内容。

4. Makefile example

edit: main.o kbd.o command.o display.o insert.o search.o files.o utils.o   
    cc -o edit: main.o kbd.o command.o display.o insert.o search.o files.o utils.o

explain:

main.o (target file, example is object file): main.c defs.h (prerequisites: to compile this object file, main.c and defs.h is needed)  
(command line should start with a Tab)cc -c main.c (command: shell 命令用于使用 prerequisites 用于生成 target file)
main.o : main.c defs.h  
    cc -c main.c  
kbd.o : kbd.c defs.h command.h  
    cc -c kbd.c  
……  
utils.o : utils.c defs.h  
    cc -c utils.c

clean : rm edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o

我们可以把这个内容保存在文件为 “Makefile” 或 “makefile” 的文件中,然后在该目录下直接输入命令 “make” 就可以生成执行文件 edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下 “make clean” 就可以了。

在这个 makefile 中,目标文件(target)包含:执行文件edit和中间目标文件(.o),依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个 Tab 键作为开头。记住,make 并不管命令是怎么工作的,他只管执行所定义的命令。make 会比较 targets 文件和 prerequisites 文件的修改日期,如果 prerequisites 文件的日期要比 targets 文件的日期要新,或者 target 不存在的话,那么,make 就会执行后续定义的命令。

这里要说明一点的是,clean 不是一个文件,它只不过是一个动作名字,有点像 C 语言中的 lable 一样,其冒号后什么也没有,那么,make 就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在 make 命令后明显得指出这个 lable 的名字。这样的方法非常有用,我们可以在一个 makefile 中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

5. make 如何工作

在默认的方式下,也就是我们只输入make命令。那么,

  • make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  • 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
  • 如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。
  • 如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
  • 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件声明make的终极任务,也就是执行文件edit了。

这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。 通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。

于是在我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,比如file.c,那么根据我们的依赖性,我们的目标file.o会被重编译(也就是在这个依性关系后面所定义的命令),于是file.o的文件也是最新的啦,于是file.o的文件修改时间要比edit要新,所以edit也会被重新链接了(详见edit目标文件后定义的命令)。

6. makefile 中使用变量

在上面的例子中,先让我们看看edit的规则:

 edit : main.o kbd.o command.o display.o  insert.o search.o files.o utils.o
       cc -o edit main.o kbd.o command.o display.o  insert.o search.o files.o utils.o

我们可以看到[.o]文件的字符串被重复了两次,如果我们的工程需要加入一个新的[.o]文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。

比如,我们声明一个变量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正不管什么啦,只要能够表示obj文件就行了。我们在makefile一开始就这样定义:

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

于是,我们就可以很方便地在我们的makefile中以“$(objects)”的方式来使用这个变量了,于是我们的改良版makefile就变成下面这个样子:

objects = main.o kbd.o command.o display.o insert.osearch.o files.o utils.o 
edit : $(objects) 
    cc -o edit $(objects)
......
clean :
    rm edit $(objects)

7. GNU make 自动推导 command 命令

   objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
   edit : $(objects)
           cc -o edit $(objects)

   main.o : defs.h
   kbd.o : defs.h command.h
......
   utils.o : defs.h

   .PHONY : clean
   clean :
           rm edit $(objects)

简而言之:GNU 的推导规则即使,在已知 target 文件和 prerequisites 文件的情况下,自动推导出 command 语句
例如: 在已知目标文件 main.o 和依赖文件 defs.h 的情况下, cc -c defs.h 这个command 预计被推导出来

本文转载自:https://blog.csdn.net/haoel/article/details/2886

共有 人打赏支持
公孙衍
粉丝 1
博文 24
码字总数 10384
作品 0
合肥
程序员
Makefile 学习 2 - 基于若干 Blog 的汇总

基于若干 Blog 汇总的 makefile 教程 陈皓 https://blog.csdn.net/haoel/article/details/2886 Makefile 进阶 1. Makefile 中的内容 显式规则。显式规则说明了,如何生成一个或多的的目标文件...

公孙衍
09/21
0
0
Linux平台代码覆盖率测试工具GCOV的前端工具LCOV简介

本博客http://blog.csdn.net/livelylittlefish 贴出作者(三二一@小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正! Content 1. Lcov是什么? 2. 如何在Linux平台安装Lcov? 3. 如何使...

晨曦之光
2012/03/09
540
0
linux下c++编程环境搭建,运行过程以及调试

安装g++环境 安装两个RPM包即可搞定 [root@localhost Desktop]# rpm -ivh /home/weiwei/Desktop/libstdc++-devel-4.4.5-6.el6.i686.rpm [root@localhost Desktop]# rpm -ivh /home/weiwei/D......

memristor
2014/04/27
0
4
挂接在/proc上的对LED灯控制的驱动

通过一天的学习总结一下挂接在/proc上的对LED灯控制的驱动开发,代码和过程 1.驱动代码 #include include include include include include include MODULE_LICENSE("Dual...

长平狐
2012/08/28
105
0
Eclipse GCC 入门

下面是本人入门的一些做法及结论: 1. 安装:下载Eclipse3.5 CPP版本 和Cygwin. 如果Cygwin安装成功, 则配置开发环境基本无问题。网上有关于Cygwin和MinGW的Eclipse配置,这里也无需我多费口水...

晨曦之光
2012/03/09
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

【大福利】极客时间专栏返现二维码大汇总

我已经购买了如下专栏,大家通过我的二维码你可以获得一定额度的返现! 然后,再给大家来个福利,只要你通过我的二维码购买,并且关注了【飞鱼说编程】公众号,可以加我微信或者私聊我,我再...

飞鱼说编程
今天
1
0
Spring5对比Spring3.2源码之容器的基本实现

最近看了《Spring源码深度解析》,该书是基于Spring3.2版本的,其中关于第二章容器的基本实现部分,目前spring5的实现方式已有较大改变。 Spring3.2的实现: public void testSimpleLoad(){...

Ilike_Java
今天
1
0
【王阳明心学语录】-001

1.“破山中贼易,破心中贼难。” 2.“夫万事万物之理不外于吾心。” 3.“心即理也。”“心外无理,心外无物,心外无事。” 4.“人心之得其正者即道心;道心之失其正者即人心。” 5.“无...

卯金刀GG
今天
2
0
OSChina 周三乱弹 —— 我们无法成为野兽

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ _刚刚好: 霸王洗发水这波很骚 手机党少年们想听歌,请使劲儿戳(这里) hahahahahahh @嘻酱:居然忘了喝水。 让你喝可乐的话, 你准忘不了...

小小编辑
今天
9
0
vm GC 日志 配置及查看

-XX:+PrintGCDetails 打印 gc 日志 -XX:+PrintTenuringDistribution 监控晋升分布 -XX:+PrintGCTimeStamps 包含时间戳 -XX:+printGCDateStamps 包含时间 -Xloggc:<filename> 可以将数据保存为......

Canaan_
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部