Linux——makefile编写

以前对makefile的编写,限于刚开始接触,我都比较局限一些死板的格式,有时候就会显得有些繁琐。在进一步了解一些系统编译和链接的知识后,对makefile编写流程有了一些新的认识,所以来此梳理梳理,方便更灵活地编写makefile。

限于makefile认识不足,这里参考了一篇比较好博文:makefile

关于makefile

  makefile带来直接好处就是——“自动化编译”。一旦写好,只需要一个make命令,整个工程完全自动编译,所以十分方便。而Makefile文件就是告诉make命令怎么样地去编译和链接程序。但是想要比较灵活的运用它,还是先要熟悉一些关于系统对程序编译和链接的知识。

  一般来说,对C、C++程序、先把源文件编译成中间代码文件。Linux下是 .o 文件即 Object File,在Windows下也就是 .obj 文件,这个动作叫做编译(compile)。然后再把大量的.O文件合成执行文件,这个动作叫作链接(link)

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

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

  

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

  直白点说,最后生成的可执行文件就是靠着这种“各各依赖关系”逐步得到的。

来个例子感受一下,

hello: hello.o
hello.o: hello.c
    gcc -c hello.c -o hello.o

这里make,便会自动编译了。这当中生成可执行文件hello依赖于hello.o,hello.o 依赖于 hello.c; 最后找到了hello.c便可以gcc生成hello.o这样往后‘带’,目标文件的hello便链接上.o文件去执行了。这里值得注意的是写gcc命令时需要添上 -c选项,用来保证得到的.o文件可重链接,不然基本会make报错(某些情况如直接gcc hello.c -o hello例外)。

还有注意一点就是在Makefile中的命令(如gcc ..),必须要以[Tab]键开始,不然你很可能就会make出错哦~。

上面例子直接链接一个中间目标文件,显得比较简单,当遇到源文件需要多个链接多个中间目标文件时是怎么个样子呢?

比如 分别创建一个加法的add.c 和 add.h ,一个减法 sub.c和 sub.h 最后main.c 来调用add 和 sub实现加减法。此时Makefile 会像这样

main: main.o add.o sub.o
main.o: main.c
    gcc -c main.c -o main.o
add.o: add.c
    gcc -c add.c -o add.o  #加-c 指定生成为可重链接.o文件
sub.o: sub.c
    gcc -c sub.c -o sub.o

.PHONY:clean
clean:
    -rm -rf *.o

使用看看

   

从上面注意几个地方

  当最终目标文件依赖多个.o时,将依赖的多个.o  一起写到最前面。然后依次以  目标:依赖文件  gcc...   的格式,罗列所有依赖关系

  由于在上面的过程中生成了多个中间.o文件(实际工程中肯定是比较多的),所以每次编译完成,需要进行一定的清理工作,这时候就用上一个 "clean" (后面细说一下)来清理。

  .PHONY意思表示clean是一个“伪目标”。也即是无论clean是否最新,一定执行它。rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但并不理睬。当然,clean的规则不要放在文件的开头,否则这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”

关于clean:  

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

到这,大致可以了解了makefile,以及大致怎么实现makefile.好, 那么make又是怎么用makefile进行执行的呢?

make怎么执行

  1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。

  2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“main”这个文件,并把这个文件作为最终的目标文件。

  3、如果main文件不存在,或是main所依赖的后面的 .o 文件的文件修改时间要比main这个文件新,那么,它就会执行后面所定义的命令来生成main这个文件。

  4、如果main所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)

  5、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件main了。

  

  这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误或是编译不成功,make根本不理。make只管文件的依赖性,即如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

灵活编写makefile

从前面的makefile编写来看,  当中我们每写一个依赖关系就需要写一个形如gcc X.c  -o  X.o生成命令,这里还好,若是较大的工程,这样难免就太繁琐了,所以据了解,一般在公司专门编写makefile的人是不会那样写的。还有写着更简洁方式,就是利用下面这几个符号:

  $^    代表所有的依赖文件
  [email protected]  代表所有的目标文件 
  $<   代表第一个依赖文件 

于是便可以将上面的makefile改写成

.PHONY:clean

main: main.o add.o sub.o
main.o: main.c
    gcc -c $< -o [email protected]
add.o: add.c
    gcc -c $^ -o [email protected]
sub.o: sub.c
    gcc -c $^ -o [email protected]

clean:
    rm -rf *.o

由于依赖的 都是中间目标文件.o ,如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用常量(这里看了好多人都把它说成变量,个人认为 它在后面并没有被改变,因次叫常量更好)那么还可以定义一个常量来表示所有的.o文件,于是便还可将它们写成这样

.PHONY:clean

OBJS = main.o\   //\转义字符
       add.o       sub.o

main: $(OBJS)
%.o : %.c
    gcc -c $^ -o [email protected]

clean:
    -rm -rf $(OBJS)

这里的%.o : %.c 想必都可以猜出来,这代表的意思就是所有的.o文件依赖相应的.C文件,这样便又省去好几步。

到这,相信聪明的你,可以更灵活编写makefile了。最后,再补充补充关于Makefile的东西

Makefile还有什么

  1. 显式规则。显式规则说明了,如何生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
  2. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写Makefile,这是由make所支持的。
  3. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  4. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
  5. 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜杠进行转义,如:“\#”。

在工程应用时,我们的规则一般这样:

  ①如果这个工程没有编译过,那么我们的所有c文件都要编译并被链接。

  ②如果这个工程的某几个c文件被修改,那么我们只编译被修改的c文件,并链接目标程序。

  ③如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的c文件,并链接目标程序

  所以只要我们的makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。

原文地址:https://www.cnblogs.com/tp-16b/p/8955462.html

时间: 2024-10-10 10:24:46

Linux——makefile编写的相关文章

linux 下C语言编程库文件处理与Makefile编写

做开发快3年了,在linux下编译安装软件算是家常便饭了.就拿gcc来说,都有不下10次了,可基本每次都会碰到些奇奇怪怪的问题.看来还是像vs.codeblocks这样的ide把人弄蠢了.便下定决心一定要好好学习下如何在linux下纯手工gcc编译c项目.今天学了2点,一个是库文件处理,另一个是makefile编写. 学习的系统是centos6.6,编译升级的gcc4.8.2,明天写个博客总结下这回gcc安装的过程,每次都能学到些东西. gcc的编译过程 首先需要清楚gcc编译做了些什么 源文件

linux下makefile编写及automake整理

makefile编写规则 在一个makefile中通常包含如下内容: 1 需要由make工具创建的目标体(target),通常是目标文件或可执行文件 2 要创建的目标体所依赖的文件(dependency_file) 3 创建每个目标体时需要运行的命令(command),这一行必须以制表符(tab键)开头 格式: target: dependency_files command /* 该行必须以tab键开头*/ 例如,有两个文件分别为hello.c和hello.h,创建的目标体为hello.o,执

Linux内核模块编写详解

内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统,本文给大家介绍linux内核模块编写,需要的朋友可以参考下 内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统.浮点运算做起来可不容易,堆栈固定而狭小,而你写的代码总是异步的,

Linux makefile 教程 很具体,且易懂

近期在学习Linux下的C编程,买了一本叫<Linux环境下的C编程指南>读到makefile就越看越迷糊,可能是我的理解能不行. 于是google到了下面这篇文章.通俗易懂.然后把它贴出来,方便学习. 后记,看完发现这篇文章和<Linux环境下的C编程指南>的makefile一章所讲述的惊人的类似,仅仅是这篇文章从一个实例切入,在有些地方比較好理解.能让人看懂就是好文章. 跟我一起写 Makefile陈皓 (CSDN)概述--什么是makefile?也许非常多Winodws的程序

很详细、很移动的Linux makefile教程:介绍,总述,书写规则,书写命令,使用变量,使用条件推断,使用函数,Make 的运行,隐含规则 使用make更新函数库文件 后序

很详细.很移动的Linux makefile 教程 内容如下: Makefile 介绍 Makefile 总述 书写规则 书写命令 使用变量 使用条件推断 使用函数 make 的运行 隐含规则 使用make更新函数库文件 后序 近期在学习Linux下的C编程,买了一本叫<Linux环境下的C编程指南>读到makefile就越看越迷糊,可能是我的理解能不行. 于是google到了以下这篇文章.通俗易懂.然后把它贴出来,方便学习. 后记,看完发现这篇文章和<Linux环境下的C编程指南>

小型C/C++项目的makefile编写

[前言]在我所接触到的Linux嵌入式开发中,大多使用的是C语言,采用makefile文件对源文件进行编译后生成可执行文件.本文即从个人经历上介绍小型的C项目如何编写makefile文档. 一.gcc命令 从目的上看,gcc命令和makefile的功能是一样,即是把源文件编译后生成可执行文件或.o二进制文件.gcc命令中有许多的额外的参数,本文仅介绍以下几种最简单和常用的方法: 有helloworld.c文件如下: #include <stdio.h> int main() { printf(

Linux makefile教程之函数七[转]

使用函数 ———— 在Makefile中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具有智能.make所支持的函数也不算很多,不过已经足够我们的操作了.函数调用后,函数的返回值可以当做变量来使用. 一.函数的调用语法 函数调用,很像变量的使用,也是以“$”来标识的,其语法如下: $(<function> <arguments> ) 或是 ${<function> <arguments>} 这 里,<function>就是函数名,m

Linux makefile教程之后序十一[转]

后序 —— 终 于到写结束语的时候了,以上基本上就是GNU make的Makefile的所有细节了.其它的产商的make基本上也就是这样的,无论什么样的make,都是以文件的依赖性为基础的,其基本是都是遵 循一个标准的.这篇文档中80%的技术细节都适用于任何的make,我猜测"函数"那一章的内容可能不是其它make所支持的,而隐含规则方面,我想不同 的make会有不同的实现,我没有精力来查看GNU的make和VC的nmake.BCB的make,或是别的UNIX下的make有些什么样的差

Linux makefile教程之总述二[转]

Makefile 总述——————— 一.Makefile里有什么? Makefile里主要包含了五个东西:显式规则.隐晦规则.变量定义.文件指示和注释. 1.显式规则.显式规则说明了,如何生成一个或多的的目标文件.这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令. 2.隐晦规则.由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的. 3.变量的定义.在Makefile中我们要定义一系列的变量,变