makefile学习

基本介绍

  1. makefile编写的关键在于解决源文件的“文件依赖性”
  2. 编译链接过程:源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数,变量是否被声明。如果函数未声明,编译器会给出一个警告,但可以生成object file。而在链接程序时,链接器会在所有的object file中寻找函数的实现,如果找不到,那到就会报链接错误码linker error.
  3. makefile最核心的内容:prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。
  4. clean不是一个文件,只是一个动作名字,类似地,我么可以在一个makefile中定义不用地编译或是和编译无关地命令,比如程序地打包,程序的备份。
  5. 使用变量:
    objs = a.o b.o
    c.o d.o
    program: $(objs)
    main: $(objs)
    cc -o program $(objs)
  6. make自动推导
    make看到a.o就会自动寻找a.c
  7. “隐晦规则”和“伪目标文件”
    .PHONY
    $(objects) : defs.h
    可以用共同的依赖头文件,但是会使得关系变得混乱
  8. 清理目标文件的规则
    .PHONY : clean
    clean :
    -rm edit $(objects)
    而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。不成文的规矩是——“clean从来都是放在文件的最后”。
  9. makefile主要包含了五个东西:显示规则,隐晦规则,变量定义,文件指示,和注释
  10. 引用其他的makefile
  • 通过include
    如include foo.make *,mk $(bar)
  • 如果有-I或–include-dir参数,那么make就会在参数指定的目录下去寻找
  • 如果目录是/include(一般是/usr/local/bin或/usr/include存在的话,make也会去找
  • -include <filename> 会忽略找不到的文件而不会因找不到文件而出现致命信息
  • 环境变量:如果环境变量定义了MAKEFILES,那么,make会把这个变量的值当作一个类似于include的动作,但建议不要使用

make的工作方式

GNU的make工作执行步骤如下:

  1. 读入所有的Makefile
  2. 读入被include的其它Makefile
  3. 初始化文件中的变量
  4. 推导隐晦规则,并分析所有规则
  5. 为所有的目标文件创建依赖关系链
  6. 根据依赖关系,决定哪些目标要重新生成
  7. 执行生成命令

书写规则

依赖关系,生成目标的方法

  • 在规则中使用通配符
    支持三种*,?,[…], ~的特殊用途

    • /test",表示当前用户$HOME目录下的test目录。而"hchen/test"则表示用户hchen的宿主目录下的test目录.
  • makefile中的变量其实就是c++/c中的宏
    objects = *.o并不会展开
    使用关键字才会展开
    objects := $(wildcard *.o)

文件搜寻

  1. 指定搜寻变量
    VPATH = src:…/headers
    上面的的定义指定两个目录,“src”和“…/headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。当前目录永远是最高优先搜寻的地方
  2. 另一个设置文件搜索路径的方法是使用make的“vpath”关键字
    1、vpath
    为符合模式的文件指定搜索目录。
    2、vpath
    清除符合模式的文件的搜索目录。
    3、vpath
    清除所有已被设置好了的文件搜索目录。
    vapth使用方法中的需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件
    vpath %.h …/headers
    vpath %.c foo:bar
    vpath % blish
    而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录。

伪目标

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用“伪目标”这个特性
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o

多目标

bigoutput littleoutput : text.g
generate text.g - ( s u b s t o u t p u t , , (subst output,, (substoutput,,@) > @ 上 述 规 则 等 价 于 : b i g o u t p u t : t e x t . g g e n e r a t e t e x t . g − b i g > b i g o u t p u t l i t t l e o u t p u t : t e x t . g g e n e r a t e t e x t . g − l i t t l e > l i t t l e o u t p u t 其 中 , − @ 上述规则等价于: bigoutput : text.g generate text.g -big > bigoutput littleoutput : text.g generate text.g -little > littleoutput 其中,- @bigoutput:text.ggeneratetext.gbig>bigoutputlittleoutput:text.ggeneratetext.glittle>littleoutput(subst output, @ ) 中 的 “ @)中的“ @)”表示执行一个Makefile的函数,函数名为subst,后面的为参数

静态模式

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -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
另一个例子:
files = foo.elc bar.o lose.o
( f i l t e r (filter %.o, (filter(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
( f i l t e r (filter %.elc, (filter(files)): %.elc: %.el
emacs -f batch-byte-compile $<

自动生成依赖性

我们可以使用C/C++编译的一个功能。大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。
例如,如果我们执行下面的命令:
cc -M main.c
其输出是:
main.o : main.c defs.h
需要提醒一句的是,如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。
%.d: %.c
@set -e; rm -f $@;
$(CC) -M $(CPPFLAGS) $< > @ . @. @.$KaTeX parse error: Undefined control sequence: \ at position 3: ; \̲ ̲sed 's,\($*\)\.…KaTeX parse error: Can't use function '$' in math mode at position 4: > $̲@; \ rm -f $@.$$

第六章 书写命令

显示目录

@echo 正在编译XXX模块…
如果make执行时,带入make参数“-n”或“–just-print”,那么其只是显示命令,但不会执行命令,这个功能很有利于我们调试我们的Makefile,看看我们书写的命令是执行起来是什么样子的或是什么顺序的。
而make参数“-s”或“–slient”则是全面禁止命令的显示。

命令执行

需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。
exec:
cd /home/hchen; pwd
为了做到忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。
clean:
-rm -f *.o
还有一个全局的办法是,给make加上“-i”或是“–ignore-errors”参数,那么,Makefile中所有命令都会忽略错误。而如果一个规则是以“.IGNORE”作为目标的,那么这个规则中的所有命令将会忽略错误。这些是不同级别的防止命令出错的方法,你可以根据你的不同喜欢设置。
还有一个要提一下的make的参数的是“-k”或是“–keep-going”,这个参数的意思是,如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。
例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:
subsystem:
cd subdir && $(MAKE)
等价于
subsystem:
$(MAKE) -C subdir
我们把这个Makefile叫做“总控Makefile”,总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了“-e”参数。
如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:
export <variable …>
如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明:
unexport <variable …>
需要注意的是,有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量不管你是否export,其总是要传递到下层Makefile中,特别是MAKEFILES变量,其中包含了make的参数信息,如果我们执行“总控Makefile”时有make参数或是在上层Makefile中定义了这个变量,那么MAKEFILES变量将会是这些参数,并会传递到下层Makefile中,这是一个系统级的环境变量。
但是make命令中的有几个参数并不往下传递,它们是“-C”,“-f”,“-h”“-o”和“-W”(有关Makefile参数的细节将在后面说明),如果你不想往下层传递参数,那么,你可以这样来:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
如果你定义了环境变量MAKEFLAGS,那么你得确信其中的选项是大家都会用到的,如果其中有“-t”,“-n”,和“-q”参数,那么将会有让你意想不到的结果,或许会让你异常地恐慌。
“/home/hchen/gnu/make”,如果我们使用“make -w”来执行,那么当进入该目录时,我们会看到:
make: Entering directory /home/hchen/gnu/make'. 而在完成下层make后离开目录时,我们会看到: make: Leaving directory/home/hchen/gnu/make’
当你使用“-C”参数来指定make下层Makefile时,“-w”会被自动打开的。如果参数中有“-s”(“–slient”)或是“–no-print-directory”,那么,“-w”总是失效的。

定义命令包

如果Makefile中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束

第七章 使用变量

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值