学习Makefile(一)---基本概念

目录

 

程序的编译和链接

Makefile 介绍

1.1 Makefile的编写规则

1.2 Makefile怎么执行

1.3 Makefile的简化编写


一直在linux操作系统下做开发,但是对makefile一点也不了解,真是惭愧和汗颜。编写 Makefile 也是构建整个软件的一部分,其重要性并不亚于编写 .c 或 .h 文件。当你用 IDE 的时候,是由 IDE 来生成相当于 Makefile 的文件。但是这个生成的过程并不是完全自动的,它是由你的鼠标点击、拖拽(把 .c 文件加入项目)、和填写一些表单、以及勾选编译选项完成的。

如果你在用 C++ ,在用 C++ 中的 STL ,那么是不是应该搞清楚 STL 到底做了什么,怎么做到的,这些问题呢?这是用 C/C++ 语言编程的程序员的一个基本态度。即使不去研究透彻,至少也应该了解一些大致的原理吧。那么对于 Makefile 来说也是这样。我们应弄清楚 Make 到底如何在工作,我们在 Makefile 里写的每一行代码是什么含义。为什么可以帮我们完成那些工作。这个连载选择 Make 来展开,也正是因为 Make 的工作原理非常简单,方便我们学习。

程序的编译和链接

最新的项目编译脚本是jamfile,所以想初步学习和了解一下gun make和boost jam。

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

  • 编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。   
  • 链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。

源文件---------编译------中间目标文件.o文件-------链接-------可执行程序 

Makefile 介绍

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

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。Makefile的根本任务是根据规则生成目标文件。

1.1 Makefile的编写规则

在讲述这个Makefile之前,还是让我们先来粗略地看一看Makefile的规则。   

target... : prerequisites ...
(tab)command

目标文件:依赖文件
(tab)编译命令
  •  target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。

  •  prerequisites就是,要生成那个target所需要的文件或是目标。

  • command也就是make需要执行的命令。(任意的Shell命令)

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

targets : prerequisites ; command
(tab)command
  • targets是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。command是命令行,如果其不与“target:prerequisites”在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。(见上)

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

hello : hello.o   #可执行文件hello依赖于hello.o
hello.o : hello.c  # hello.o 依赖于 hello.c
    gcc -c hello.c -o hello.o #gcc编译hello.c便可以生成hello.o
gcc加-c指定生成为可重链接.o文件。直白点说,最后生成的可执行文件就是链接.o文件得到,而.o文件靠着“依赖关系”生成。

1.2 Makefile怎么执行

如下的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 
sub.o: sub.c
    gcc -c sub.c -o sub.o

.PHONY:clean
clean:
    -rm -rf *.o
  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只管文件的依赖性,即如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

<targets ...>: <target-pattern>: <prereq-patterns ...>

<commands>

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合

target-parrtern是指明了targets的模式,也就是的目标集模式。

prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。

<target-parrtern>定义成“%.o”,意思是我们的<target>集合中都是以“.o”结尾的,而如果我们的<prereq-parrterns>定义成“%.cpp”,意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.cpp]这个结尾,形成的新集合。如main.o通过模式取到的依赖目标就是main.cpp文件。

静态模式帮助我们完成将几百个源文件用一种通用的模式来代表,我们可以不用写出所有中间文件的依赖关系,只要用一个静态模式就OK了。

1.3 Makefile的简化编写

从前面的makefile编写来看,  当中我们每写一个依赖关系就需要写一个形如gcc XX.c  -o  XX.o生成命令,如果简单还好,若是较大的工程,这样难免就太繁琐了,因此还有写着更简洁方式,就是利用下面这几个符号:

$@,$^,$<代表的意义分别是:   $@  是目标文件, $^  是所有的依赖文件, $<  是第一个依赖文件。

注意 $< 代表的是依赖关系表中的第一项(如果我们想引用的是整个关系表,那么就应该使用$^),具体到我们这里就是XX.c。而$@代表的是当前语句的目标,即XX.o。于是便可以将上面的makefile改写成

main: main.o add.o sub.o
main.o: main.c  
    gcc -c $< -o $@
add.o: add.c
    gcc -c $^ -o $@  
sub.o: sub.c
    gcc -c $^ -o $@

clean:
    rm -rf *.o

由于链接依赖的是中间目标文件.o,如果makefile变得复杂,那么我们就有可能会少写一个依赖关系,得到.o文件不完整,从而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用常量(这里看到很多人都把它说成变量,个人认为它在后面并没有被改变,因次叫常量更好)。定义一个常量OBJS来表示所有的.o文件,于是便还可将Makefile写成这样:

OBJS = main.o \   //\转义字符,换行的意思
       add.o \
       sub.o

main: $(OBJS) 
%.o : %.c
    gcc -c $^ -o $@

.PHONY:clean
clean:
    -rm -rf $(OBJS)

这样是不是感觉又简洁了不少!  这里的%.o : %.c 想必都可以大家都可以猜出来,这代表的意思就是所有的.o文件依赖相应的.C文件,是一个模式规则。

这样一来,make命令就会自动将所有的.c源文件编译成同名的.o文件。这样便又省去好几步。

makefile每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略,但是如果该空格或空行是以Tab键开头的,那么make会认为其是一个空命令。

我们在UNIX下可能会使用不同的Shell,但是make的命令默认是被“/bin/sh”——UNIX的标准Shell解释执行的。除非你特别指定一个其它的Shell。Makefile中,“#”是注释符,很像C/C++中的“//”,其后的本行字符都被注释。

记住,要学习shell命令!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值