makefile入门

Makefile

shell 中特殊变量:

  • $# 是传给脚本的参数个数
  • $0 是脚本本身的名字
  • $1 是传递给该shell脚本的第一个参数
  • $2 是传递给该shell脚本的第二个参数
  • $@ 是传给脚本的所有参数的列表
  • $* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个
  • $$ 是脚本运行的当前进程ID号
  • $? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误
targets : prerequisites; command
    command             # 命令的开始一定要使用Tab键

示例

main:main.o test1.o test2.o         # 第一个称为最终目标
    gcc main.o test1.o test2.o -o main
main.o:main.c test.h
    gcc -c main.c -o main.o
test1.o:test1.c test.h
    gcc -c test1.c -o test1.o
test2.o:test2.c test.h
    gcc -c test2.c -o test2.o

# 用于清理
.PHONY:clean
clean:
    rm -rf *.o test

通配符

  1. * 匹配0个或者是任意个字符
  2. ? 匹配任意一个字符
  3. [] 我们可以指定匹配的字符放在 "[]" 中
  4. % 也是匹配任意字符

"%.o" 取出所有以 ".o" 结尾的文件,挨个找到对应的 ".c" 文件。依次执行命令。

test:test.o test1.o
    gcc -o $@ $^
%.o:%.c
    gcc -o $@ $^

注意:使用变量方式时无法展开通配符

OBJ=*.c
test:$(OBJ)
    gcc -o $@ $^

会报错“找不到文件 ‘*.c’ ”
使用函数 wildcard 可以展开

OBJ=$(wildcard *.c)
test:$(OBJ)
    gcc -o $@ $^

变量定义

定义:VALUE_LIST = one two three
使用:$(VALUE_LIST)${VALUE_LIST}

  • 简单赋值 ( := ) 常规理解的赋值方式,只对当前语句与之后语句有效。
  • 递归赋值 ( = ) 可能影响多个变量,以前、以后的使用都会受到影响。
  • 条件赋值 ( ?= ) 变量未定义则赋值,已定义则无效。
  • 追加赋值 ( += ) 原变量用空格隔开的方式追加一个新值。

自动化变量

| 变量 | 说明                                                         |
| ---- | ------------------------------------------------------------ |
| $@   | 表示规则的目标文件名。在多目标模式规则中,它代表的是触发规则被执行的文件名。 |
| $%   | 当目标文件是一个静态库文件时,代表静态库的一个成员名。       |
| $<   | 规则的第一个依赖的文件名                                     |
| $?   | 所有比目标文件更新的依赖文件列表,空格分隔。如果目标文件时静态库文件,代表的是库文件(.o 文件)。 |
| $^  \| 代表的是所有依赖文件列表,使用空格分隔。变量“$^”会去掉重复的依赖文件。 |
| $+  \| 类似“$^”,保留了依赖文件中重复出现的文件。主要用在程序链接时库的交叉引用场合。 |
| $*   | 在模式规则和静态模式规则中,代表“茎”。“茎”是目标模式中“%”所代表的部分(当文件名中存在目录时,  “茎”也包含目录部分)。 |

示例:

test:test.o test1.o test2.o     
    gcc -o $@ $^
test.o:test.c test.h         
    gcc -o $@ $<
test1.o:test1.c test1.h        
    gcc -o $@ $<
test2.o:test2.c test2.h         
    gcc -o $@ $<

拓展,不重要:

| 变量名       | 功能                                                         |
| ------------ | ------------------------------------------------------------ |
| $(@D)        \| 表示文件的目录部分(不包括斜杠)。如果 "$@" 表示的是 "dir/foo.o" 那么 "$(@D)" 表示的值就是 "dir"。如果 "$@" 不存在斜杠(文件在当前目录下),其值就是 "."。 |
| $(@F)        \| 表示的是文件除目录外的部分(实际的文件名)。如果 "$@" 表示的是 "dir/foo.o",那么 "$@F" 表示的值为 "dir"。 |
| $(*D)  $(*F) | 分别代表 "茎" 中的目录部分和文件名部分                       |
| $(%D)  $(%F) | 当以 "archive(member)" 形式静态库为目标时,分别表示库文件成员 "member" 名中的目录部分和文件名部分。踏进对这种新型时的目标有效。 |
| $(<D)  $(<F) | 表示第一个依赖文件的目录部分和文件名部分。                   |
| $(^D)  $(^F) | 分别表示所有依赖文件的目录部分和文件部分。                   |
| $(+D)  $(+F) | 分别表示所有的依赖文件的目录部分和文件部分。                 |
| $(?D)  $(?F) | 分别表示更新的依赖文件的目录部分和文件名部分。               |

目标文件搜索

VPATH 是变量,更具体的说是环境变量,Makefile 中的一种特殊变量,使用时需要指定文件的路径;

# VPATH:=src:car
VPATH:=src car	# 在当前目录搜寻不到时,到src与car子目录下搜索
test:test.0
    gcc -o $@ $^

vpath 是关键字,按照模式搜索,也可以说成是选择搜索。搜索的时候不仅需要加上文件的路径,还需要加上相应限制的条件。

  1. vpath PATTERN DIRECTORIES

    vpath test.c src:car 在src与car目录下搜索 test.c 文件

  2. vpath PATTERN

    vpath test.c 清除符合文件 test.c 的搜索目录

  3. vpath

    清除所有已被设置的 vpath 路径

文件搜索示例

dir
 |-Makefile
 |
 |-include/
 |		|-list1.h
 |		|-list2.h
 |
 |-src/
 		|-list1.c
 		|-list2.c
VPATH=src include
main:main.o list1.o list2.o
    gcc -o $@ $<
main.o:main.c
    gcc -o $@ $^
list1.o:list1.c list1.h
    gcc -o $@ $<
list2.o:list2.c list2.h
    gcc -o $@ $<
vpath %.c src
vpath %.h include
main:main.o list1.o list2.o
    gcc -o $@ $<
main.o:main.c
    gcc -o $@ $^
list1.o:list1.c list1.h
    gcc -o $@ $<
list2.o:list2.c list2.h
    gcc -o $@ $<

隐含规则

谨慎使用,可能出现无法预料的情况。

隐含条件只能省略中间目标文件重建的命令和规则,但是最终目标的命令和规则不能省略。

test:test.o    
	gcc -o test 
test.otest.o:test.c

隐含规则的具体的工作流程:make 执行过程中找到的隐含规则,提供了此目标的基本依赖关系。确定目标的依赖文件和重建目标需要使用的命令行。隐含规则所提供的依赖文件只是一个基本的(在C语言中,通常他们之间的对应关系是:test.o 对应的是 test.c 文件)。当需要增加这个文件的依赖文件的时候要在 Makefile 中使用没有命令行的规则给出。

条件判断

关键字 功能
ifeq 判断参数是否不相等,相等为 true,不相等为 false。
ifneq 判断参数是否不相等,不相等为 true,相等为 false。
ifdef 判断是否有值,有值为 true,没有值为 false。
ifndef 判断是否有值,没有值为 true,有值为 false。
ifeq (ARG1, ARG2)
ifeq 'ARG1' 'ARG2'
ifeq "ARG1" "ARG2"
ifeq "ARG1" 'ARG2'
ifeq 'ARG1' "ARG2"
libs_for_gcc= -lgnu
normal_libs=
ifeq($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
foo:$(objects)
$(CC) -o foo $(objects) $(libs)

伪目标

总是会被执行的命令

clean:
	rm -rf *.o

若当前目录下存在名为clean的文件时,因为没有依赖文件,所以目标总是被认为是最新的。导致shell命令不会执行。

使用 .PHONY 将clean声明为伪目标后无论当前目录下是否有clean文件都会执行,且不会查找隐含关系。

.PHONY:clean
clean:
	rm -rf *.o test
.PHONY:all
all:test1 test2 test3
test1:test1.o
	gcc -o $@ $^
test2:test2.o
	gcc -o $@ $^
test3:test3.o
	gcc -o $@ $^

会生成三个可执行文件,当只想生成一个时使用 make test1

函数

函数调用 $(<function> <arguments>) 或者是 ${<function> <arguments>}

字符串函数

  1. $(patsubst <pattern>,<replacement>,<text>)

    OBJ=$(patsubst %.c,%.o,1.c 2.c 3.c)
    all:    
    	@echo $(OBJ)
    # 1.o 2.o 3.o
    
  2. $(subst <from>,<to>,<text>)

    OBJ=$(subst ee,EE,feet on the street)
    all:
        @echo $(OBJ)
    # fEEt on the strEEt
    
  3. $(strip <string>) 去除两端空格,将内部空格合并为一个

    OBJ=$(strip    a       b c)
    all:
    	@echo $(OBJ)
    # a b c
    
  4. $(findstring <find>,<in>)

    OBJ=$(findstring a,a b c)
    all:
    	@echo $(OBJ)
    # a
    # 如果不存在,返回空
    
  5. $(filter <pattern>,<text>)

    OBJ=$(filter %.c %.o,1.c 2.o 3.s)
    all:
    	@echo $(OBJ)
    # 1.c 2.o
    
  6. $(filter-out <pattern>,<text>)

    OBJ=$(filter-out 1.c 2.o ,1.o 2.c 3.s)
    all:
    	@echo $(OBJ)
    # 3.s
    
  7. $(sort <list>)

    OBJ=$(sort foo bar foo lost)
    all:
    	@echo $(OBJ)
    # bar foo lost
    
  8. $(word <n>,<text>)

    OBJ=$(word 2,1.c 2.c 3.c)
    all:
    	@echo $(OBJ)
    # 2.c
    

文件名函数

  1. $(dir <names>)

    OBJ=$(dir src/foo.c hacks)
    all:
    	@echo $(OBJ)
    # src/ ./
    
  2. $(notdir <names>)

    OBJ=$(notdir src/foo.c hacks)
    all:
        @echo $(OBJ)
    # foo.c hacks
    
  3. $(suffix <names>)

    OBJ=$(suffix src/foo.c hacks)
    all:
    	@echo $(OBJ)
    # .c
    
  4. $(basename <names>)

    OBJ=$(notdir src/foo.c hacks)
    all:
        @echo $(OBJ)
    # src/foo hacks
    
  5. $(addsuffix <suffix>,<names>)

  6. $(addperfix <prefix>,<names>)

  7. $(join <list1>,<list2>)

    OBJ=$(join src car,abc zxc qwe)
    all:
        @echo $(OBJ)
    # srcabc carzxc qwe
    
  8. $(wildcard PATTERN) 列出当前目录下所有符合模式的 PATTERN 格式的文件名

其他函数

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

    把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text> 的返所返回的每个字符串会以空格分割,最后当整个循环结束的时候,<text> 所返回的每个字符串所组成的整个字符串(以空格分隔)将会是 foreach 函数的返回值。所以<var>最好是一个变量名,<list> 可以是一个表达式,而<text>中一般会只用 <var>这个参数来一次枚举<list>中的单词。

    name:=a b c d
    files:=$(foreach n,$(names),$(n).o)
    all:
        @echo $(files)
    # a.o b.o c.o d.o
    
  2. $(if <condition>,<then-part>)或(if<condition>,<then-part>,<else-part>)

    OBJ:=foo.c
    OBJ:=$(if $(OBJ),$(OBJ),main.c)
    all:
         @echo $(OBJ)
    
  3. $(call <expression>,<parm1>,<parm2>,<parm3>,...)

    reverse = $(1) $(2)
    foo = $(call reverse,a,b)
    all:
         @echo $(foo)
    # a b
    
  4. $(origin <variable>) 返回变量从哪里来的

shell 命令相关

  1. 在命令前加上 @ 就不会显示这一条命令

  2. 每条命令是一个独立的shell进程,使用 ‘cd’ 切换目录后不会影响后续的语句。应用 ; 分割并放在一行

    cd bar;gobble lose >../foo
    # 或
        cd bar; \
        gobble lose > ../foo
    
  3. make -j 可以指定并发数,不建议使用

文件包含

include filename1 
-include filename1 filename2       # 忽略文件不存在或者是无法创建的错误提示

导入定义的变量或模式规则

嵌套执行

subsystem:
	cd subdir && $(MAKE)
# 切换到当前目录下的subdir子目录,并执行makefile
subsystem
	$(MAKE) -C subdir
# makefile中有一个名为"CURDIR"的变量,代表工作目录。 使用 "-C" 时切换到指定目录。

使用 export 导入变量

示例

├──Makefile         //最外层的Makefile文件,不是目录文件。
├──include          //编译的时候需要链接的库文件
│      ├──codec   //libui.a 库文件所在的目录
│      ├──db        //libdb.a 库文件所在的目录
│      ├──ui         //libui.a库文件所在的目录
├──lib                   //源文件所在的目录,子目录文件中包含Makefile文件
│      ├──codec     //编解码器所在的源文件的目录
│      ├──db           //数据库源文件所在的目录
│      ├──ui            //用户界面源文件所在目录
├──app
│      ├──player    
└──doc              //这个工程编译说明    
lib_codec := lib/codec
lib_db    := lib/db
lib_ui     := lib/ui
libraries   := $(lib_codec) $(lib_db) $(lib_ui)
player    := app/player

.PHONY : all $(player) $(libraries)
all : $(player)

$(player) $(libraries) :
    $(MAKE) -C $@
posted @ 2021-10-12 15:39  某某人8265  阅读(35)  评论(0编辑  收藏  举报