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)。

makefile文件用于管理和组织代码工程的编译和链接,下面介绍一些语法说明:

1.文件包含:

语法:include文件名

作用:将其它makefile文件包含进来,组成一个更大的makefile文件,这样有利于makefile模块化编程。通常我们将一些配置选项分开成一个独立的makefile文件,这样有利于makefile文件的管理,或将模块化代码的依赖关系和需要编译的文件信息独自写到一个makefile文件中,最终通过include命令形成一个顶层makefile文件来完成整个工程代码的编译和链接。

2.变量定义:

语法:变量名 := 变量值

在makefile中,经常先定义一个变量,然后往该变量中追加新的值(通过+=符号)

C_SRCS := 

C_SRCS += test1.c test.c

在makefile中有一类特殊的变量,其名称为自动变量,自动变量的值会根据规则中的target和prerequisites自动计算其值,下面列出常见的自动变量:

$@为规则中target名称

$<为规则中的第一个prerequisites名称

$^代表所有的prerequisites名称

3.内置命令:

Makefile中内置了一些常用的命令,有字符串处理函数subst、patsubst、strip、findstring、filter、filter-out、sort、word、wordlist、words、firstword、lastword;文件名处理函数dir、notdir、suffix、basename、addsuffix、addprefix、join、wildcard、realpath、abspath;条件处理函数if;循环处理函数foreach等。

下面介绍一些常用的函数:

wildcard 函数:其语法为$(wildcard pattern),pattern为匹配的模式,比如$(wildcard %.c) 为查找当前路径下面文件名以.c结尾的文件。

foreach 函数:其语法为$(foreach var,list,text),每循环一次var从list中按顺序取值一个,然后执行一次text代码并记录结果,最终返回所用text代码运行的结果。比如

dirs := C_DIR S_DIR

file := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

将C_DIR和S_DIR文件夹下面的所有文件添加到file变量中。

dir 函数:其语法为$(dir names…),用于获取names中文件夹路径,比如

$(dir src/foo.c hacks)

将获得文件夹路径 src/ ./

notdir 函数:其语法为$(notdir names…),用于获取names中除去路径的信息,比如

$(notdir src/foo.c hacks)

将获得文件信息 foo.c hacks

basename 函数:其语法为$(basename names…),用于获取names中除去后缀信息,比如

$(basename src/foo.c src-1.0/bar hacks)

将获得信息 src/foo src-1.0/bar hacks

addsuffix 函数:其语法为$(addsuffix suffix,names…),用于往names中添加后缀信息suffix,比如

$(addsuffix .c,foo bar)

将获得文件信息 foo.c bar.c

addprefix 函数:其语法为$(addprefix prefix,names…),用于往names中添加前缀信息prefix,比如

$(addprefix src/,foo bar)

将获得信息src/foo src/bar

patsubst 函数:其语法为$(patsubst pattern,replacement,text),根据 pattern信息将text替换成replacement,比如

objects = foo.o bar.o baz.o

files = $(patsubst %.o,%.c,$( objects))

将获得信息 foo.c bar.c baz.c

其可以简单写成

objects = foo.o bar.o baz.o

files = $(objects:.o=.c)

4.规则定义:

规则是makefile中最重要的概念,其告诉make目标文件的依赖关系,以及如何生成及更新这些目标文件。在makefile文件规则中有两种,一种是显示规则,另一种是隐式规则。

显示规则用于说明 何时及如何重新生成目标,其列出了目标依赖的文件信息,并通过调用命令来创建或更新目标,语法一般为:

targets:prerequisites

        recipe

        ...

targets 为要生成或更新的目标,prerequisites为目标依赖的关系,recipe为生成目标的命令,一个规则可以有多条recipe,比如:

foo.o:foo.c defs.h

        cc -c -g foo.c

隐式规则用于说明 何时及如何重新生成一类目标文件根据其名称,其描述了目标是如何依赖名称相似的文件(一般来说除去后缀信息,其目标与依赖文件的名称是一样的),并调用命令来创建或更新目标,比如:

%.o:%.c

        $(CC) -c $(CFLAGS) $< -o $@

这个隐式规则说明了.o的目标文件依赖于同名的.c文件,其中$<及$@为自动变量,$<为第一个prerequisites条件,也就是目标名称.c, $@为目标,也就是目标名称.o。

在makefile中,我们通常要编写3种隐式规则,第1种为代码链接规则,第2种为源代码编译规则,第3中为汇编代码编译规则。

5.文件搜索路径设置

make命令默认会在当前路径中搜索prerequisites中的文件,比如头文件,但我们在写程序时,经常将头文件和源文件隔开放在不同的文件夹下,解决这个问题有两种方法:1.我们可以通过VPATH变量来解决,2.我们可以通过vpath指令来解决。

VPATH变量

VPATH变量为所有的prerequisites指定文件路径,路径之间可以通过:或空格隔开,比如:

        VAPTH = src:../headers

vpath指令

vpath指令的作用与变量VPATH的作用差不多,但vpath有更多的灵活性,其语法为:

        vpath pattern directories

        pattern为需要查找的文件匹配模式信息,directories为要查找的文件路径,比如:

        vpath %.h ../headers

        其代表在上一层文件夹headers中查找.h头文件信息。

6.依赖关系生成

在编写c文件代码时,我们经常通过#include语句包含头文件信息,比如头文件,该c文件被编译时需要依赖于其#include包含进来的文件,在规则编写中,就需要指出这个依赖关系,这样头文件信息改变后,make程序就知道如何更新目标文件了,而不是整个进行重编译,但这个操作可以看出是非常消耗时间的,作为解决方案我们可以通过使用 编译器命令 -M选项来自动完成该工作,比如在main.c中 #include "defs.h",通过cc -M main.c 将产生输出main.o:main.c defs.h,我们可以将输出结果写到.d文件中,然后通过include指令包含到makefile文件中。

7.编译器常用命令选项

编译器通常进行预处理,编译,汇编和链接处理,预处理包含了宏定义,文件包含,条件编译,编译则直接将代码翻译成机器码,汇编则将汇编代码翻译成机器码,链接则按照内存地址分配文件将各个文件的机器码统一形成一个可执行文件,对编译命令(比如gcc)而言该如何区别这些操作呢?即通过命令选项。

-o file 输出生成的file文件

-c 编译或汇编程序文件,但不会执行链接操作

-T script 使用scrip脚本来分配内存

-W1,option 给链接器发送一个选项,比如生成地址映射表,-W1,-Map,output.map

-mcpu=name 规定目标处理器的型号

-Wall 使能所有警告调试信息输出

-glevel 要求带调试信息的等级,-g0代表不产生调试信息,-g1 代表产生最小的调试信息用来跟踪程序的运行,但不包括本地变量,-g3包含了一些额外的调试信息比如程序的宏定义等。

-I dir 增加头文件的搜索路径,比如 -I ../header

-D name 预定义一个宏定义,比如 -DMPC564xB表示定义一个宏MPC564XB

8.示例展示

main:main.o add.o sub.o
    g++ -o main main.o add.o sub.o
main.o:main.cpp
    g++ -c main.cpp -o main.o
add.o:add.cpp
    g++ -c add.cpp -o add.o
sub.o:sub.cpp
    g++ -c sub.cpp -o sub.o
.PHONE:clear
clear:
    -rm -rf *.o

注意事项:

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

(2)由于在上面的过程中生成了多个中间.o文件,所以每次编译完成,后续基本还需要进行一定的清理工作,这时候就用上了“clean"清理。

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

关于clean:

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

9.make的执行

(1)make会在当前目录下找名字Makefile或makefile的文件。

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

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

(4)如果main所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖项,如果找到则再根据那一个规则生成.o文件。

(5)当然,你的c文件和h文件是存在,于是mak会生成.o文件。然后再用.o文件生成执行文件main。

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

10.应用的时候定义的一些规则

(1)如果这个工程没有编译过,那么所有c文件都要编译并链接。

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

(3)如果这个工程的头文件被修改了,那么需要编译这几个头文件的c文件,并链接目标程序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值