【手册】makefile

跟我一起写 Makefile - 陈皓

书写规则

一、规则举例

foo.o : foo.c defs.h       # foo模块
    cc -c -g foo.c

二、规则语法

targets : prerequisites
    command
#一行过长 /
#多行合并 ;

三、通配符

  • ~$HOME目录
  • *:任意长度字符
  • ?
  • [...]
  • %

四、文件搜寻

  • VPATH = src:../headers
  • vpath

五、伪目标

.PHONY: clean
clean:
    rm *.o temp

六、多目标

  • $@:目标的集合
  • $(subst output, $@):subst(outout, $@)返回”目标”去掉”output”的部分

七、静态模式

<targets>: <target-pattern>: <prereq-patterns>
    <commands>
  • $<:依赖的集合
  • $(filter %.o, $(files)):filter(%.o, $(files))返回”$(files)”中符合”%.o”的部分

八、自动生成依赖性

  • -M:自动寻找源文件包含的头文件

  • 生成依赖文件.d

    %.d: %.c
    @set -e; rm -f $@; /
    $(CC) -M $(CPPFLAGS) $< > $@. ; /
    sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@. > $@ ; /
    rm -f $@.
  • 包含依赖文件

    sources = foo.c bar.c
    include $(sources:.c=.d) #将变量sources以.c结尾替换为.d结尾

书写命令

一、显示命令

  • @<command>:命令不会在终端上显示
  • make -nmake --just-print:只显示命令,但不执行命令
  • make -smake -silent:全面禁止命令的显示

二、命令执行

三、命令出错

  • -<command>:若命令执行出错,继续执行
  • make -imake --ignore-errors:忽略所有错误

四、嵌套执行

  • 总控Makefile

    subsystem:
    cd subdir && $(MAKE)
    
    # 等价于
    
    subsystem:
    $(MAKE) -C subdir
    
    # $(MAKE):make及其参数
    
    
    # subdir:次级目录
    
  • 传递变量到下级Makefile

    export <variable ...>
    unexport <variable ...>
  • 指定传递参数

    subsystem:
    cd subdir && $(MAKE) MAKEFLAGS
    
    # -w、--print-directory:目录变化时输出信息
    
    
    # -C:默认打开-w
    
    
    # -s:关闭-w
    

五、定义命令包

  • 定义

    define run-yacc
    yacc $(firstword $^)
    mv y.tab.c $@
    endef
  • 调用

    foo.c : foo.y
    $(run-yacc)

使用变量

一、变量基础

  • 使用变量:$<var>$(<var>)${<var>};使用$:$$

二、变量中的变量

  • 访问后定义的变量=

    foo = $(bar)
    bar = $(ugh)
    ugh = Huh
    
    all:
    echo $(foo) #输出为""Huh",makefile允许访问后定义的变量
  • 仅访问已定义的变量:=

    x := foo
    y := $(x) bar
    x := later
  • 条件定义?=

    FOO ?= bar #若FOO未定义则初始化未bar,若已定义则跳过

三、变量的替换

sources = foo.c bar.c
include $(sources:.c=.d) #将变量sources以.c结尾替换为.d结尾

四、 把变量的值再当成变量

x = y
y = z
a := $($(x))

五、追加变量值

  • +=
objects = main.o foo.o bar.o utils.o
objects += another.o
  • override 指示符
override <variable> = <value>
override <variable> := <value>
override <variable> += <more text>

override define foo
bar
endef

六、多行变量

define two-lines-var
echo foo
echo $(bar)
endef

七、环境变量

  • -e参数:系统环境变量覆盖定义变量。
  • export:向下层Makefile传递变量。

八、目标变量

<target ...> : <variable-assignment>              //目标使用的var为局部变量
<target ...> : overide <variable-assignment>

九、模式变量

<pattern ...> : <variable-assignment>       // %.o 即所有目标使用此变量
<pattern ...> : override <variable-assignment>

条件判断

一、语法

ifeq (<arg1>, <arg2>) 
ifeq '<arg1>' '<arg2>' 
ifeq "<arg1>" "<arg2>" 
ifeq "<arg1>" '<arg2>' 
ifeq '<arg1>' "<arg2>"

ifneq (<arg1>, <arg2>) 
ifneq '<arg1>' '<arg2>' 
ifneq "<arg1>" "<arg2>" 
ifneq "<arg1>" '<arg2>' 
ifneq '<arg1>' "<arg2>"
ifdef <variable-name>   //测试变量是否有值
ifndef <variable-name>

使用函数

一、函数的调用

$(<function> <arguments>)
${<function> <arguments>}

二、字符串处理

  • 替换$(subst <from>,<to>,<text>):把字串中的字符串替换成。
  • 模式替换$(patsubst <pattern>,<replacement>,<text>) :查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式,如果匹配的话,则以替换。
  • 清除$(strip <string>):去掉字串中开头和结尾的空字符。
  • 查找$(findstring <find>,<in>):在字串中查找字串。
  • 过滤$(filter <pattern...>,<text>):以模式过滤字符串中的单词,保留符合模式的单词。可以有多个模式。
  • 反过滤$(filter-out <pattern...>,<text>):以模式过滤字符串中的单词,去除符合模式的单词。可以有多个模式。
  • 排序$(sort <list>):给字符串中的单词排序(升序)。
  • 取单词$(word <n>,<text>):取字符串中第个单词。(从一开始)
  • 取单词串$(wordlist <s>,<e>,<text>):从字符串中取从开始到的单词串。和是一个数字。
  • 单词统计$(words <text>):统计中字符串中的单词个数。
  • 首单词$(firstword <text>):取字符串中的第一个单词。

三、文件名操作

  • 取目录$(dir <names...>):从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
  • 取文件$(notdir <names...>):从文件名序列中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。
  • 取后缀$(suffix <names...>):从文件名序列中取出各个文件名的后缀。
  • 取前缀$(basename <names...>):从文件名序列中取出各个文件名的前缀部分。
  • 加后缀$(addsuffix <suffix>,<names...>):把后缀加到中的每个单词后面。
  • 加前缀$(addprefix <prefix>,<names...>):把前缀加到中的每个单词前面。
  • 连接$(join <list1>,<list2>):把中的单词对应地加到的单词后面。如果的单词个数要比的多,那么,中的多出来的单词将保持原样。如果的单词个数要比多,那么,多出来的单词将被复制到中。

四、foreach 函数

  • $(foreach <var>,<list>,<text>):把参数中的单词逐一取出放到参数所指定的变量中,然后再执行所包含的表达式。

    names := a b c d
    files := $(foreach n,$(names),$(n).o)
    //执行结果a.o b.o c.o d.o

五、if 函数

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

六、call函数

  • $(call <expression>,<parm1>,<parm2>,<parm3>...):为表达式的各参数赋值

七、origin函数

  • $(origin <variable>)返回值:

    • “undefined” 如果从来没有定义过,origin函数返回这个值“undefined”。
    • “default” 如果是一个默认的定义,比如“CC”这个变量。
    • “environment” 如果是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。
    • “file” 如果这个变量被定义在Makefile中。
    • “command line” 如果这个变量是被命令行定义的。
    • “override” 如果是被override指示符重新定义的。
    • “automatic” 如果是一个命令运行中的自动化变量。

八、shell函数

  • $(shell cat foo)

九、控制make

  • 产生错误信息:$(error <text ...>),make停止
  • 产生警告信息:$(warning <text ...>),make继续

make运行

一、make的退出码

  • 0 —— 表示成功执行。
  • 1 —— 如果make运行时出现任何错误,其返回1。
  • 2 —— 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。

二、指定Makefile

  • 查找顺序:“GNUmakefile”、“makefile”和“Makefile”
  • 指定文件:make –f

三、指定目标

  • 默认目标:makefile中的第一个目标
  • 指定目标:make

四、检查规则

  • “-n” “–just-print” “–dry-run” “–recon”

    • 不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。
    • “-t” “–touch”
    • 这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
  • “-q” “–question”

    • 这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。
  • “-W ” “–what-if=” “–assume-new=” “–new-file=”

    • 这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。

五、make的参数

  • “-b” “-m” 这两个参数的作用是忽略和其它版本make的兼容性。
  • “-B” “–always-make” 认为所有的目标都需要更新(重编译)。
  • “-C ” “–directory= ” 指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make –C ~hchen/test –C prog”等价于“make –C ~hchen/test/prog”。
  • “—debug[=]” 输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是的取值:

    a —— 也就是all,输出所有的调试信息。(会非常的多)
    b —— 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。
    v —— 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。
    i —— 也就是implicit,输出所以的隐含规则。
    j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
    m —— 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。

  • “-d” 相当于“–debug=a”。

  • “-e” “–environment-overrides” 指明环境变量的值覆盖makefile中定义的变量的值。
  • “-f=” “–file=” “–makefile=” 指定需要执行的makefile。
  • “-h” “–help” 显示帮助信息。
  • “-i” “–ignore-errors” 在执行时忽略所有的错误。
  • “-I ” “–include-dir= ” 指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。
  • “-j []” “–jobs[=]” 指同时运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。(注意这个参数在MS-DOS中是无用的)
  • “-k” “–keep-going” 出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。
  • “-l ” “–load-average[=

隐含规则

一、使用隐含规则

foo : foo.o bar.o
    cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
//自动推倒出
foo.o : foo.c
    cc –c foo.c $(CFLAGS)
bar.o : bar.c
    cc –c bar.c $(CFLAGS)

二、隐含规则一览

1、编译C程序的隐含规则。
“.o”的目标的依赖目标会自动推导为“.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)”

2、编译C++程序的隐含规则。
“.o”的目标的依赖目标会自动推导为“.cc”或是“.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”)

3、编译Pascal程序的隐含规则。“.o”的目标的依赖目标会自动推导为“.p”,并且其生成命令是“$(PC) –c $(PFLAGS)”。

4、编译Fortran/Ratfor程序的隐含规则。“.o”的目标的依赖目标会自动推导为“.r”或“.F”或“.f”,并且其生成命令是:
​ “.f” “$(FC) –c (FFLAGS).F$(FC)c$(FFLAGS) ( F F L A G S ) ” ​ “ . F ” “ $ ( F C ) – c $ ( F F L A G S ) (CPPFLAGS)”
​ “.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”

5、预处理Fortran/Ratfor程序的隐含规则。
“.f”的目标的依赖目标会自动推导为“.r”或“.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:
​ “.F” “$(FC) –F $(CPPFLAGS) (FFLAGS).r$(FC)F$(FFLAGS) ( F F L A G S ) ” ​ “ . r ” “ $ ( F C ) – F $ ( F F L A G S ) (RFLAGS)”

6、编译Modula-2程序的隐含规则。
“.sym”的目标的依赖目标会自动推导为“.def”,并且其生成命令是:“ (M2C) ( M 2 C ) (M2FLAGS) $(DEFFLAGS)”。“

三、隐含规则使用的变量

1、关于命令的变量。

AR
​ 函数库打包程序。默认命令是“ar”。
AS
​ 汇编语言编译程序。默认命令是“as”。
CC
​ C语言编译程序。默认命令是“cc”。
CXX
​ C++语言编译程序。默认命令是“g++”。
CO
​ 从 RCS文件中扩展文件程序。默认命令是“co”。
CPP
​ C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
FC
​ Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。
GET
​ 从SCCS文件中扩展文件的程序。默认命令是“get”。
LEX
​ Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”。
PC
​ Pascal语言编译程序。默认命令是“pc”。
YACC
​ Yacc文法分析器(针对于C程序)。默认命令是“yacc”。
YACCR
​ Yacc文法分析器(针对于Ratfor程序)。默认命令是“yacc –r”。
MAKEINFO
​ 转换Texinfo源文件(.texi)到Info文件程序。默认命令是“makeinfo”。
TEX
​ 从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。
TEXI2DVI
​ 从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。
WEAVE
​ 转换Web到TeX的程序。默认命令是“weave”。
CWEAVE
​ 转换C Web 到 TeX的程序。默认命令是“cweave”。
TANGLE
​ 转换Web到Pascal语言的程序。默认命令是“tangle”。
CTANGLE
​ 转换C Web 到 C。默认命令是“ctangle”。
RM
​ 删除文件命令。默认命令是“rm –f”。

2、关于命令参数的变量
下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空。
ARFLAGS
​ 函数库打包程序AR命令的参数。默认值是“rv”。
ASFLAGS
​ 汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。
CFLAGS
​ c语言编译器参数。
CXXFLAGS
​ C++语言编译器参数。
COFLAGS
​ RCS命令参数。
CPPFLAGS
​ C预处理器参数。( C 和 Fortran 编译器也会用到)。
FFLAGS
​ Fortran语言编译器参数。
GFLAGS
​ SCCS “get”程序参数。
LDFLAGS
​ 链接器参数。(如:“ld”)
LFLAGS
​ Lex文法分析器参数。
PFLAGS
​ Pascal语言编译器参数。
RFLAGS
​ Ratfor 程序的Fortran 编译器参数。
YFLAGS
​ Yacc文法分析器参数。

四、隐含规则链

  • 在“隐含规则链”中,禁止同一个目标出现两次或两次以上,这样一来,就可防止在make自动推导时出现无限递归的情况。

五、定义模式规则

  • 1、模式规则介绍
    • %.o : %.c ;

六、后缀规则

七、隐含规则搜索算法

使用make更新函数库文件

一、函数库文件的成员

  • archive(member)

二、函数库成员的隐含规则

三、函数库文件的后缀规则

​ .c.a:
​ $(CC) $(CFLAGS) $(CPPFLAGS) -c <o$.o$(AR)r$@ < − o $ ∗ . o ​ $ ( A R ) r $ @ <script type="math/tex" id="MathJax-Element-4">< -o \$*.o ​ \$(AR) r \$@ </script>*.o
​ $(RM) $*.o

其等效于:

​ (%.o) : %.c
​ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o .o$(AR)r$@ ∗ . o ​ $ ( A R ) r $ @ *.o
​ $(RM) $*.o

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值