makefile 浅谈

跟我一起写MakefilePDF:点击打开链接

Makefile简介:

       简单的说,一个大的工程中的源文件不计其数,其按照不同的类型,功能,模块放在了不同的目录之中,makefile则是定义了一些规则来指定,哪些文件需要先编译,哪些需要后编译,哪些需要重新编译,而makefile带来的好处就是,一旦写好,之后一个make命令 就可以搞定,极大的提高了软件的开发。

简述编译链接:

       无论选择什么语言c,c++都需要将源文件编译成为中间代码文件,在windows下面是.obj文件,而unix则是.o文件,这就是编译(compile),然后将.o文件链接(link操作)形成可执行文件。但由于源文件太多,编译生成的中间文件太多,不太方便,此时需要给中间目标文件打包,在windows下成为库文件.lib(library file),而在unix称为.a(archivefile)文件。

一个简单的makefile样例:

       Edit(最终目标可执行文件):main.otest.o start.o xd.o(依赖文件)

       Cc –o edit main.o test.o start.o xd.o

      

       Main.o: main.c defs.h

       Cc –c main.c

       Test.o : test.c defs.h

       Cc –c test.c

       Start.o: start.c defs.h

       Cc –c start.c

       Xd.o: xd.c defs.h

       Cc –c xd.c

       Clean:(伪目标) 依赖文件不执行,退出时可以显示执行 make clean

       Rm –r edit main.o test.o xd.o start.o

Gund make很强大,有自动推导的功能,一个.o文件 会自动推导.c文件,于此同时还可以使用变量 来代替目标文件(过多很复杂),新的makefile:

       Objects = main.o test.o start.o xd.o

       Cc –c edit $(objects)

       Main.o:defs.h

       Start.o:defs.h

       Xd.o:defs.h

       Test.o:defs.h

      所有.c文件可以自动推导。

下面是一个makefile的小知识点:

Tips

Makefile:

1)      如果make执行,遇到-I或者是includedir的时候,操作按照参数指定的路径去寻找

2)      如果/usr/local/bin 或者是/usr/include存在是话 make也会去寻找,到最后如果还是找不到的话,make会报告一个致命的信息,如果想让make忽略这些信息的话,寻找在include的前面加上一个-号: -include <filename>或者使用sinclude命令和-的作用是一致的。

3)      不建议使用环境变量:MAKEFILES(主要就是包含其他的makeifile文件)

 

Make的工作方式:

1)读入所有的makefiles

2)读入include的其他makefiles

3)初始化文件中的变量

4)推到隐晦规则,分析规则

5)创建依赖关系

6)根据依赖关系,决定哪些目标文见需要重新生成。

7)执行命令

在初始化变量的时候,只有在生成目标文件的时候,变量才会被展开使用,否则并不会立即进行展开。

 

第一条规则中第一个目标将会是最终的目标。

Make的命令需要以Tab开始

 

通配符的使用:

但是 当objects = *.o时 并不是代表着所有的.o文件,而是真正是*.o,要是需要所有的.o文件,可以使用 object := $(wildcard *.o)关键字wildcard(自动全部扩展开)

 

$?:自动化变量(上一次程序的退出码)

 

VPATH这个特殊的变量是指明了需找的路径,是在make在当前目录找不到的情况下会按照指定的目录去寻找文件

:VPATH= src:../headers(:是分隔)

与此同时还有一个vpath 关键字,不是一个变量是一个make关键字。

 

vpath %.h ../headers 表示在../headers目录下搜索以.h结尾是文件

 

伪目标不是一个文件,只是一个标签:(一般放在命令的最后,并且需要显示的调用 make clean)

.PHONY: clean

clean:

-rm *.o temp

 

静态模式

Objects = foo.obar.o

all:$(objects)

 

$(objects):%.o:%.c

$(cc) –c $(CFLAGS)$< -o $@      /*$<:所有依赖目标集 $@:目标集*/

 

自动寻找头文件和源文件:main.o: main.c defs.h

但是由于头文件过多的原因,需要自动寻找源文件和头文件,使用cc –M main.c

但是如果使用的是gnu的编译器,需要的是gcc –MM main.c 否则会将标准库的头文件一起包含进来。

 

Makefile的显示命令

@echo xxxx 当make时会输出 echo xxxx 并不会执行命令

Make –n 或—just-print 只是显示命令 并不会执行

Make –s 或者是–slient 全面禁止命令的显示

 

嵌套执行make

在一些比较大的工程之中,我们会把不同模块或是不同功能的源文件放在不同的目录里面,可以在每个目录下面都写一个makefile文件,这样还会很简洁,但是也存在着缺陷,就是难以维护,嵌套执行make语句就体现出了很强大的好处。例如:

subsystem:

cd subdir&& $(make) – 意思是先进入subdir文件,之后执行make命令,这个是总控的makefile文件,总控文件里面的变量可以传递到下一级的makefile文件中,但是不会覆盖下一级makefile的变量,除非使用指定参数-e

 

传递变量:export variable = value

 

如果要是想传入所有的变量。只使用一个export即可。例外shell ,makeflags 变量都是需要传入下一级的变量。

 

变量的使用,使用$来表示变量,但是要想使用单独的$时候,需要使用$$来表示。

 

变量的使用,可以使用变量的变量,变量的值可以是后续变量的值:foo = $(bar)

bar = xd;

但是这样同时也会存在着弊端,就是递归定义,形成类似于死循环的样式。

解决

使用:= 来限制,是前面的变量不可以使用后面的变量的值,只可以使用前面已经定义好的变量的值

y := $(x) bar

x := foo

其中y的值就是 bar ,而不是 foo bar

 

空格的定义:(先定义一个空的变量,在将这个空的变量在等号的右边使用)

nullstring :=

space :=$(nullstring)

 

?= 是否存在的意思

 

替换

Foo := a.o b.o

Bar := $(foo: .o =.c)

Bar = a.c b.c

 

追加变量值 使用 += (好比java的那个字符串的拼接)

 

Makefile的函数使用:

subst替换函数

       $(subst ee,EE,geet ont the feet)

patsubst:模式字符串替换函数

       $(patsubst %.c,%.o,x.c.c bar.c)=>x.c.obar.o

strip: 去空格函数(去掉开头和结尾的空格)

findstring:$(findstring <find> ,<in>)在<in>查找<find>

       $(findstring a, a b c)=>a

       $(findstring a, b c)=>“”

filter: 按照条件过滤(过滤掉不符合条件的)

filter-out :反过滤函数

sort:给单词排序(升序)并且去掉相同的单词

word:取出单词

       $(word <n> ,<text>)取出text中的第n个单词

wordlist:取单词串的函数

       $(wordlist 2,3,<text>)取出text中的第2-3个单词

words:统计单词个数

firstword:取首个单词

 

文件名的操作函数:

 

dir:取目录函数 目录部分是/之前的 没有/ 返回“./”

       $(dir src/foo.c hacks)=>src/./

notdir:取文件函数

       $(notdir src/foo.c hacks)=>foo.c hacks

suffix:取后缀函数

basename:取前缀函数

addsuffix:添加后缀函数

addprefix:添加前缀函数

join:连接函数(对应链接)

       $(join aaa bbb, 111 222 333)=>aaa111bbb222 333

foreach:用于循环的使用

       $(foreach<var>,<list>,<text>)

name:= a b c d

files:= $(foreachn,$(name),$(n).o)就是将临时局部变量每次赋予一个值,之后转化为.o文件 临时变量出了函数 将会无效,类似于c++for函数中的i。

 

if函数:

$(if<condition>,<then-part>,<else-part>)

call:创建一个新参数化的函数

origin主要是返回这个变量是从什么地方来的

       $(origin <variable>) 返回值:

              undefined:

              default:

              file

              command line

              override

              automatic

 

控制make的函数

Error:

       $(error <text>) text是出错信息

Warning

       $(warning <text>) text是警告信息

 

Make的退出码

0---表示成功执行

1---出现任何错误,返回1

2--- 一些目标不需要更新

 

Makefile伪目标:

all:

所有的目标

clean:

       删除所被make创建的文件

install:

       安装已经编译好的程序,就是将目标执行文件拷贝到制定的目标中去

print:

       列出改变过的源文件

tar:

       将源文件打包备份

dist:

       创建一个压缩文件

TAGS:

       更新所有目标

check和test:

       测试makefile的流程


V

七、静态模式 静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。我们还是先来看一下语法: ;: ;: ; ; ... targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。 target-parrtern是指明了targets的模式,也就是的目标集模式。 prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。 这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的;定义成“%.o”,意 思是我们的;集合中都是以“.o”结尾的,而如果我们的;定义成“%. c”,意思是对;所形成的目标集进行二次定义,其计算方法是,取;模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。 所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符,如果你的文件名中有“%”那么你可以使用反斜杠“\”进行转义,来标明真实的“%”字符。 看一个例子: objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ 上面的例子中,指明了我们的目标从$object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。于是,上面的规则展开后等价于下面的规则: foo.o : foo.c $(CC) -c $(CFLAGS) foo.c -o foo.o bar.o : bar.c $(CC) -c $(CFLAGS) bar.c -o bar.o 试想,如果我们的“%.o”有几百个,那种我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子: files = foo.elc bar.o lose.o $(filter %.o,$(files)): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ $(filter %.elc,$(files)): %.elc: %.el emacs -f batch-byte-compile $< $(filter %.o,$(files))表示调用Makefile的filter函数,过滤“$filter”集,只要其中模式为“%.o”的内容。其的它内容,我就不用多说了吧。这个例字展示了Makefile中更大的弹性。 八、自动生成依赖性 在Makefile中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的main.c中有一句“#include "defs.h"”,那么我们的依赖关系应该是: main.o : main.c defs.h
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值