Make 教程

代码的编译过程:  

  1. 无论是c , c++ , php , 等 首先需要将源文件编译成中间代码文件 (Object File) 此过程 ,美其名曰:编译
    • windows 下称为 .obj 文件
    • unix  下是 .o 文件 
    • 编译主要检查语法的正确性
  2. 大量的中间代码文件 合并成可执行文件  此过程,美其名曰:链接 【也有人叫编排】
    • 主要链接函数和全局变量
    • 链接需要明显指出中间目标文件名称

  ps: 当中间文件太多的时候编译起来就很不方便,通常我们会将其打包,windows 下叫库文件(Library File , .lib 文件),Unix 下 是 Archive File , 也就是  .a 文件

Make 概念


  Make 这个词,英语意思为“制作”。 Make 命令直接使用了这个意思,就是要做出某个文件出来。 比如我要做出文件  a.txt ,就可以直接使用   

make a.txt

  但是,如果你真的输入这条命令,它不会起到任何作用,因为 Make 本身并不知道,如何做出  a.txt 需要有人告诉它怎么去做出a.txt 文件。

  用例一:

   a.txt 文件依赖于  b.txt 和  c.txt 这两个文件,是后面两个文件内容的集合。那么make 需要知道下面的规则。

  

a.txt: b.txt c.txt  #第一步
    cat b.txt c.txt > a.txt #第二步

  解析:

    第一步:确认 b.txt 和  c.txt 这两个文件必须已经存在。

    第二步:  使用cat 命令将这两个文件合并起来了,输出为新文件。

  总结:

  1. 像上面描述的这种规则默认都写在了一个叫 Makefile 的文件中,而我们大名鼎鼎的 make 命令就依赖这个文件进行构建。
  2. Makefile 也可以写成  makefile 
  3. 如果你实在不想使用makefile 或者 Makefile 来命名你的规则文件,例如使用  aaaa.txt  ,你可以使用 make  的 -f  参数来指定 :  make  -f  aaaa.txt
  4. make 只是一个根据指定Shell 命令进行构建的工具而已,它的规则:
    • 你规定要构建哪些文件 【 target】
    • 它依赖于哪些源文件 【 prerequisits】
    • 当哪些文件由变动是,如何重新构建它 【 commands】

Makefile 文件格式


  构建规则都写在了Makefile 文件中,要学会如何Make 命令,就必须先学会如何编写Makefile 文件

概述:


  Makefile 文件由一系列规则构成,格式:

<target>  :<prerequisites>

    <commands>

  解析:

    target  :  目标,必须的

    prerequisites: 前置条件,可选

    commands: 命令,它前面需要跟着一个空格 , 可选

目标<target>


  一个目标(target) 就构成了一个规则,目标通常是文件名,指明make 命令所要构建的对象,比如上文的  a.txt 。  目标可以是一个文件名,也可以是多个文件名,之间用空格隔开。

  除了文件名,目标还可以是操作名或任意字母组成的字符串,这类目标称为 “伪目标” (phony target)

  

  上面代码的目标是  bb , 它不是文件名 , 而是一个操作名,属于“伪目标” , 作用是列举根目下的文件或目录

  ps :  伪目标,系统是不会去检查bb 文件是否存在的。而是每次执行都执行对应的命令。

  但是,如果目录中刚好有个文件就叫bb , 那么bb 也就变成了“目标”了,避免这种情况,可以使用如下命令

.PHONY: bb
clean:
  ls /

  通过 ".PHONY" 可以用来指定伪目标,这种内置目标名还有很多,可以查看手册  

前置条件   prerequisites


  前置条件通常是一组文件名,之间用空格分隔。它指定了“目标” , 是否重现构建的判断标准:只要有一个前置文件不存在或者被更新了,目标就需要重新构建

  用例一:

result.txt:  source.txt
  cp source.txt  result.txt

  

  解析:

    上面的代码中,构建了目标  result.txt   ,  它的前置条件是  source.txt   已经存在了,那么 make  result.txt  可以正常运行,否则必须再写一条规则,来生成  source.txt 

  用例二:

 source.txt:
  echo " hello world" >  source.txt

  解析:

    上面的代码中,source.txt 后面没有前置条件 , 这意味着这个“source.txt”目标不依赖其它文件,直接运行它的  commands  就好了。只要source.txt 不存在 , 每次调用  make  source.txt 它都会生成 source.txt

    如果你连续执行两次  make  source.txt , 它只会执行第一次就不会再执行了

  

  用例三:  

source: a.txt  b.txt  c.txt

  解析:

    source  是一个伪目标 , 它只有三个前置条件 , 没有任何的 commands 。作用:一次性创建a.txt , b.txt , c.txt 这三个文件。 

命令(commands)


  命令就是一堆shell  命令组成的,它是构建“目标” 的具体指令,它的运行结果通常就是生成目标文件。每行命令之前必须有一个tab键,如果要使用其它键,可以使用内置变量 .RECIPEPREFIX 声明

  用例一:

.RECIPEPREFIX = >
all:
> echo Hello , world

  解析:

    上面的代码就是用 .RECIPEPREFIX 来指定了每行命令开头 是  而不再是 tab键

   

  用例二:

var-lost:
  export foo=bar
  echo "foo=[$$foo]"

  解析:

    commands 中的每条独自换行的命令都是独立分开的,不在同一个进程了,所以数据也是不共享的

    第一行的 定义的变量  foo  是不能在  第二行中使用的

    解决办法就是: 

    1. 将这两个shell命令合并为一条,中间用  分号 ";"  隔开 
    2. 如果感觉太才了,那么就就可以在  分号 后面加个  反斜杆进行转义  "  "
    3. 感觉加   太扯,那么你可以使用 内置命令  .ONESHELL 来指定
var-kept:
  export foo=bar; echo "foo=[$$foo]"

或者

var-kept:
  export foo=bar;
  echo "foo=[$$foo]"

或者

.ONESHELL
var-kept:
  export foot=bar;
  echo "foo=[$$foo]"

Makefile 文件的语法  


注释  : 


  在Makefile 文件中 注释符是:   # 

回声:


  正常情况下 , make 会 打印每条命令 , 然后再执行, 这叫做回声(echoing).

  如果你想关闭回声,直接在shell  命令行前面加一个 @  符号就好了

test:
  # this is test
  @echo TODO

  

通配符:


  通配符 , 用来指定符合条件的文件名的。 Makefile 的通配符与Bash  一致 , 主要有  *  ,  ?   比如以o 结尾的文件  可以这样  *.o

模式匹配


  Make 命令 允许 对文件名 , 进行类似正则运算的匹配 , 主要用到的匹配符是  %  , 比如需要找到 当前目录下 有  f1.c  和 f2.c 两个源码文件,需要将它们编译伪对应的对象文件

%.o : %.c

等同于

f1.o :f1.c
f2.o :f2.c

  

变量和赋值符


  Makefile 中允许使用   等号 “ = ” 来 自定义变量

bb = Hello  World
test:
  @echo $(bb)
  @echo $$HOME

  解析:

    变量bb  等于 Hello  World  ,  变量调用是通过   $()  来调用的。

    如果你要调用Shell  自带的一些变量,那么你需要在  美元符号前面再加一个美元符号, 因为  Makefile 中会对美元符号进行转义

  

  Makefile 中提供了  4中赋值运算符(  =  ,  :=  , ?=  , += )  :

#执行时扩展, 允许递归扩展

key = value 

 

#定义是扩展

key := value

 

#只有在该变量为空时才设置值

key ?= value

 

#将值追加到变量的尾端

key += value

内置变量


Make  命令提供了一系列的内置变量,详情见手册 

自动变量


  Make 命令提供了一些自动变量,他们的值与当前规则有关。

  (1)$@ :  

      代指当前目标,就是Make  命令当前构建的那个目标, 比如  make foo  的  $@ 就值  foo

a.txt  b.txt:
  touch $@

等同于

a.txt: 
  touch a.txt

b.txt: 
  touch b.txt

  (2) $<

    代指第一个前置条件

a.txt : b.txt c.txt
  cp $< $@

等同于

a.txt: b.txt c.txt
  cp b.txt a.txt  

  (3) $?

    代指比目标更新的所有前置条件 【即:更新的依赖文件】,比如,  t1: p1 p2 ,  p2 的时间戳比t1 新,那么 $? 就代指  p2

  (4) $^

    代指所有前置条件

  

     (5) $*

    代指匹配符 % 匹配成功的部分 , 比如   %匹配  f1.txt 中的  f1 , 那么  $* 就表示   f1

     (6) $(@D)  和 $(@F)

     $(@D)  和 $(@F) 分别代指:  $@ 的目录名和文件名

    (7)$(<D)  和  $(<F)

     $(<D)  和  $(<F) 分别代指: $<  的目录名和文件名

   其它的自动变量请查看手册 

  

  用例一:

dest/%.txt:  src/%.txt
  @[ -d dest ] || mkdir dest
  cp $< $@

  解析:

    1. 上面的代码就是将  src  目录下的  txt 文件 拷贝到 dest  目录下 。 

    2.  @[-d dest] ||  mkdir dest  :   是说  判断 dest 目录是否存在,不存在则创建之 , 并且关闭回声

    3.  cp $<  $@ :  将 src 中的 txt  拷贝到  dest 目录下

判断和循环


  Makefile  使用Bash 语法,完成了 判断和循环

  用例一:

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)

else
  libs=$(normal_libs)
endif

  

  用例二:

   

LIST = one  two three
test:
  for i in $(LIST); do 
    echo $$i; 
  done

等同于

test:
  for i in one two three; do 
    echo $i; 
  done

  

函数


  Makefile 还可以使用函数 , 它许多内置函数  见手册

  格式:

$(function arguments)

或者

${function arguments}

  

Makefile 实例


  编译 c语言项目

edit: main.o  kbd.o  command.o  display.o
  cc -o edit main.o kbd.o command.o display.o
main: main.c defs.h
  cc -c main.c
kdb.o: kbd.c defs.h  command.h
  cc -c kbd.c
command.o: command.c defs.h  command.h
  cc -c command.c
display.o: display.c defs.h
  cc -c display.c
clean:
  rm edit main.o kbd.o command.o display.o
.PHONY: edit clean

  

原文地址:https://www.cnblogs.com/yinguohai/p/10951144.html