其他分享
首页 > 其他分享> > 学习Makefile(内容可真多呀。。。哭)(其一)

学习Makefile(内容可真多呀。。。哭)(其一)

作者:互联网

一.Makefile介绍

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

二.Makefile 综述

1.//执行过程

编译------>产生.o文件(object file)------>链接---------->产生可执行文件

 

2.//makefil的一般格式

目标生成文件:生成目标文件所需的依赖文件

[TAB]编译指令

 

3.可以定义     变量=多个.o文件,在重复使用这多个文件时,调用变量就很方便

 

4.GNU的make可以自动推导,有一个.o文件,在依赖关系中可以不写依赖的.c,因为(隐式规则)默认包含。并且也可以不写编译指令,因为同样被默认包含了。

 

5.Makefile的文件名,有GNUmakefile,makefile,Makefile,这里我自己只想用Makefile,

在当前目录输入make,make命令会在当前目录按顺序查找上面三种文件。

  当然也可以使用别文件名来书写Makefile,比如Make.linux,Make.Sollris等等,但这样就需要使用make -f "filename"这种格式的指令。

 

6.可以使用include包含其他Makefile文件(看到这里我还不太明白,包含起来的作用,先做个标记)

 

7.如果当前环境定义了环境变量MAKEFILES,make会把这个变量中的值做一个类似include的动作,在使用make的时候每次都会收到此环境变量的影响,不建议使用。写在这里只是告诉大家,有时候Makefile除了怪事,那么可以看你看当前环境有没有这个环境变量。

 

8.make的工作方式

  1.读入全部的Makefile      //阶段1

  2.读入被include的其他Makefile

  3.初始化文件中的变量

  4.推到隐晦规则,分析全部规则

 

  5.为所有目标创建依赖关系链        //阶段2

  6.根据依赖关系,决定哪些目标要重新生成

  7.执行生成命令

 

三.Makefile书写规则

1.书写规则

targets:prerequisites

[TAB]command

或者是这样

targets:prerequisites;command

[TAB]command

2.通配符

如果我们想定义一系列比较类似的文件,就会使用通配符  "*"       "?"         "[...]" 

"~" 

如果是“~/test”,这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录。

"*"

*.C代表所有后缀为C的文件,

 

3.文件搜寻

3.1变量VPATH

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,再让make自动去找。

Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。

VPATH = src:../headers

上面定义的指定两个目录,用":"分隔,make会按照这个顺序搜索(不过,当前目录永远是最高优先搜索的地方)

3.2.关键字vpath

(1).        vpath < pattern> < directories>    为符合模式< pattern>的文件指定搜索目录<directories>。

(2).        vpath < pattern>                              清除符合模式< pattern>的文件的搜索目录。

(3).        vpath                                                 清除所有已被设置好了的文件搜索目录。

vapth使用方法中的< pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。< pattern>指定了要搜索的文件集,而< directories>则指定了的文件集的搜索的目录。例如:

vpath %.h ../headers

如果是连续的使用vpath,make会按照不同的vpath的顺序来执行

3.3伪目标

比如"clean",这是一个伪目标,
clean:

  rm*.o temp 

我们并不生成clean这个文件,这是是一个标签,通过使用这个标签才能使其生效。

为了避免重名,使用".PHONY"来显式的指明这是一个伪目标,

只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。于是整个过程可以这样写:

    .PHONY: clean

   clean:

           rm *.o temp

3.4多目标

3.5静态模式(非常灵活,有效利用的话会十分高效)

静态模式可以更加容易地定义多目标的规则,可以让我们的规则更灵活

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

   <commands>

...

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

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

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

   objects = foo.o bar.o

 

   all: $(objects)

 

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

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

在这里,就统一把带有.o的依赖目标统一替换成.c。编译的时候一句话就搞定了。

"$<"和"$@"是自动化变量,"$<"表示全部依赖的目标集,"$@"就表示目标集。

3.8自动生成依赖性

比如  cc -M main.c

其输出是:

   main.o : main.c defs.h

(如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。)

如何将编译器的这个功能如何与我们的Makefile联系在一起呢,GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。并让make自动更新或自成[.d]文件,并把其包含在我们的主Makefile中。

这里,我们给出了一个模式规则来产生[.d]文件:

   %.d: %.c             //.d依赖于.c文件

           @set -e; rm -f $@; \       //删除全部目标文件(.d文件)

            $(CC) -M $(CPPFLAGS) $< > $@.//把全部依赖(.c文件)生成目标(.d文件)

 

; \

            sed 's,                             //sed为随机编号做了一个替换

\.o[ :]*,\1.o $@ : ,g' < $@.      

 

> $@; \                                      

            rm -f $@.              //删除临时文件

这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依赖关系:

   main.o : main.c defs.h

转成:

   main.o main.d : main.c defs.h

于是,我们的[.d]文件也会自动更新了,并会自动生成了,

我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过),例如:

   sources = foo.c bar.c

   include $(sources:.c=.d)

这样就包含了.d文件

四.Makefile书写命令

4.1显示命令

使用@字符放置于命令行之前,这个命令在make执行时,将不会被显示出来

make执行时加入参数make -n或者 --just-print 只是显示命令,但是不会执行命令。(这样可以看到我们书写的命令执行起来是什么样子的)

make参数 -s或者--slient,则全面禁止命令的显示。

4.2命令执行

你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔.

4.3命令出错

如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则。

忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。如:

  clean:

           -rm -f *.o

又例如

  -mkdir file

还有一个全局的办法是,给make加上“-i”或是“--ignore-errors”参数,那么,Makefile中所有命令都会忽略错误。而如果一个规则是以“.IGNORE”作为目标的,那么这个规则中的所有命令将会忽略错误。这些是不同级别的防止命令出错的方法,你可以根据你的不同喜欢设置。

还有一个要提一下的make的参数的是“-k”或是“--keep-going”,这个参数的意思是,如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。

4.4嵌套执行make

在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁。如果把全部的东西都写在一个Makefile中,那么这样会非常难维护我们的Makefile。

例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:

   subsystem:

           cd subdir && $(MAKE)

其等价于:

    subsystem:

           $(MAKE) -C subdir

如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:

export<variable ...>

如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明:

unexport<variable ...>

但是make命令中的有几个参数并不往下传递,它们是“-C”,“-f”,“-h”“-o”和“-W”(有关Makefile参数的细节将在后面说明),

4.5定义命令包

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

   define run-yacc

   yacc $(firstword $^)

   mv y.tab.c $@

   endef

 

标签:其一,文件,make,Makefile,可真,命令,规则,目录
来源: https://www.cnblogs.com/easterncabbage/p/13684365.html