Makefile

1 三要素:

  • 依赖:判断依赖是否存在,若存在则判断跟目标的时间戳关系
  • 目标:判断目标是否存在,若存在则退出;否则执行动作
  • 动作:shell语句,行首为Tab
 image:a.o b.o c.o
         gcc a.o b.o c.o -o image
 
 a.o:a.c
         gcc a.c -o a.o -c
 b.o:b.c
         gcc b.c -o b.o -c
 c.o:c.c
         gcc c.c -o c.o -c

运行结果:

make
gcc a.c -o a.o -c
gcc b.c -o b.o -c
gcc c.c -o c.o -c
gcc a.o b.o c.o -o image

@:不打印命令本身

  1 image:a.o b.o c.o
  2         gcc a.o b.o c.o -o image
  3 
  4 a.o:a.c
  5         @gcc a.c -o a.o -c
  6 b.o:b.c
  7         @gcc b.c -o b.o -c
  8 c.o:c.c
  9         @gcc c.c -o c.o -c
//输出:
gcc a.o b.o c.o -o image

2 变量

  • 格式:$(变量名)
  • 变量和函数的展开实在make读取Makefile时进行。
  • 变量名大小写敏感,建议一般变量使用小写方式,参数列表采用大写方式

2.1 自定义变量&系统预定义变量

OBJ = a.o b.o c.o

image:a.o b.o c.o
    $(CC) $(OBJ) -o image

a.o:a.c
    @$(CC) a.c -o a.o -c
b.o:b.c
    @$(CC) b.c -o b.o -c
c.o:c.c
    @$(CC) c.c -o c.o -c

$(CC)默认为:CC

//输出
cc a.o b.o c.o -o image

在不同平台重新给预定义变量赋值一次即可,如在ARM平台重新赋值,此时代表交叉编译工具链

OBJ = a.o b.o c.o
CC = arm-Linux-gun-gcc

image:a.o b.o c.o
    $(CC) $(OBJ) -o image

2.2 Makefile预定义变量

  • RM:删除命令,默认为:rm -f
  • CC:c编译程序,默认为:cc
  • CPP:c程序的预处理器,默认为:-E
  • CXX:c++编译程序,默认为:g++
  • AS:汇编程序,默认为:as
  • AR:函数库打包程序,可创建静态库 .a文档,默认为:ar
  • ARFLAGS:执行AR命令的命令行参数,默认为:rv
  • CFLAGS:CC编译器的命令行参数
  • CXXFLAGS:g++编译器的命令行参数
  • ASFLAGS:汇编器AS的命令行参数

2.3 自动化变量

  • @:所在规则的目标的完整名称
    @D:目标文件的目录部分
    @F:目标文件的文件名
OBJ = a.o b.o c.o

output/image:a.o b.o c.o
    echo $(@) $(@D) $(@F)

//输出
output/image output image
  • <:所在规则的依赖列表的第一个文件的完整名称
    <D:第一个依赖文件的目录部分
    <F:第一个依赖文件的文件名
output/image:a.o b.o c.o
    echo $(<) $(<D) $(<F)

//输出
a.o . a.o
  • ^:所在规则的依赖列表,去除重复的依赖文件
    ^D:所有依赖列表的目录部分
    ^F:所有依赖列表的文件名部分

  • +:所在规则的依赖列表,同一文件可重复
    +D:所有依赖列表的目录部分
    +F:所有依赖列表的文件名部分

  • ?:被更新的依赖文件列表,用空格隔开
    ?F:被更新的依赖文件的目录部分
    ?D:被更新的依赖文件的文件名部分

  • *:在模式规则和静态规则中,代表茎

  • %:代表所在规则的静态库文件的一个成员名

用自动化变量代表依赖库和目标

OBJ = a.o b.o c.o

output/image:$(OBJ)
    $(CC) $(^) -o $(@) 

a.o:a.c
    @$(CC) $(^) -o $(@) 
b.o:b.c
    @$(CC) $(^) -o $(@) 
c.o:c.c
    @$(CC) $(^) -o $(@) 

2.4 变量的定义

  • 递归定义

A = Here is $(B)
B = China
在整个文件搜索变量B

  • 直接定义

B = China
A := Here is $(B)
在语句之前搜索变量B,若搜不到则返回空

  • 条件定义

A = apple
A ?= banana
如果A已定义则不做操作;
如果A没有定义,则定义为“banana”。

  • 多行命令定义

define commands
    echo "ok!"
    echo "done!"
enddef
  • 追加变量

A = apple
A += tree
变量A的值为:apple tree

  • 替换变量的值

A = a.c b.c c.c
B = $(A:%.c=%.o)
变量B的值为,将变量A中所有以".c"结尾的单词替换为 “.o”结尾
内嵌函数patsubst也可实现该功能:
B = $(patsubst %.c, %.o, $(A))

  • override变量

在执行make命令时,可以携带一个变量的定义。
如果这个变量跟Makefile中的变量重名,则覆盖Makefile中变量的定义。
如果Makefile中无此变量,则追加该变量的定义。

override CFLAGS += -Wall
OBJ = a.o b.o c.o
image:$(OBJ)
    $(CC) $(^) -o $(@) $(CFLAGS)

a.o:a.c
    @$(CC) $(^) -o $(@) -c 
b.o:b.c
    @$(CC) $(^) -o $(@) -c 
c.o:c.c
    @$(CC) $(^) -o $(@) -c 
//输出
//make
cc a.o b.o c.o -o image -Wall
//make CFLAGS="-g"
cc a.o b.o c.o -o image -g -Wall
  • 传递变量

将变量传递给子Makefile

//cat Makefile 
export A = apple
B = banana

all:
    echo "$A"
    echo "$B"
    $(MAKE) -C src/  #调用 src/目录下的Makefile

子Makefile:

//cat src/Makefile 
image:
    echo "src/: $A"
    echo "src/: $B"

输出:

echo "apple"
apple
echo "banana"
banana
make -C src/ 
make[1]: Entering directory '...projectA/src'
echo "src/: apple"
src/: apple
echo "src/: "
src/: 
make[1]: Leaving directory '...projectA/src'
  • 默认传递的变量:

SHELL
MAKEFLAGS
MAKEFILES
执行make前就已存在的环境变量

对于默认传递的变量可以用unexport阻止传递给子Makefile

  • VPATH

VPATH = 路径1:路径2
VPATH = 路径1 路径2
指定文件搜索时,除当前路径之外的备用路径,以空格或者冒号隔开

VPATH = src1:src2

小写的指示符:vpath 可以为不同类型的文件指定不同的路径

vpath %.c = src1:src2
vpath %.h = include/

VPATH是一个变量,而vpath是一个指示符。

  • MAKE

调用子Makefile:

$(MAKE) -C subdir/
  • MAKEFLAGS

执行make时的命令行参数,这个变量默认传递给子Makefile。

3 规则

3.1 隐式规则

make可以自动找到依赖文件所需要的源程序文件,并自动编译。
但是只能自动找到与目标同名的依赖文件。

OBJ = a.o b.o c.o

image:$(OBJ)
    $(CC) $(^) -o $(@) 

//输出
cc    -c -o a.o a.c
cc    -c -o b.o b.c
cc    -c -o c.o c.c
cc a.o b.o c.o -o image

若存在文件clean.c

OBJ = a.o b.o c.o 

image:$(OBJ)
    $(CC) $(^) -o $(@) 

clean:

//make clean
cc     clean.c   -o clean

为区分动作的代号和要生成的文件,用指示符 .PHONY 来定义伪目标,即不运用隐式规则。

OBJ = a.o b.o c.o 

image:$(OBJ)
    $(CC) $(^) -o $(@) 

clean:

.PHONY: clean


//make clean
make: Nothing to be done for 'clean'.

3.2 静态规则

编译.o文件时,需要加特定的编译选项。

OBJ = a.o b.o c.o 

image:$(OBJ)
    $(CC) $(^) -o $(@) 

$(OBJ):%.o:%.c
    $(CC) $(^) -o $(@) -Wall -c 
     
clean:
    $(RM) $(OBJ)

.PHONY: clean

//make
cc a.c -o a.o -Wall -c 
cc b.c -o b.o -Wall -c 
cc c.c -o c.o -Wall -c 
cc a.o b.o c.o -o image 

$(OBJ):%o:%.c
在(a.o、b.o、c.o)中匹配“.o”为后缀的文件,冒号后面的内容是目标对应的依赖。

3.3 多目标规则

  • 可描述文件的依赖关系
    如:OBJS中的.o文件都依赖于head.h文件,而又各自依赖其他的头文件
OBJS = a.o b.o c.o
a.o:a.h
b.o:b.h B.h
c.o:c.h
$(OBJS):head.h
  • 有多个具有类似构建命令的目标(也可用静态规则)
SRC = a.c b.c x.c y.c
OBJS = $(SRC:.c=.o)

image:$(OBJS)
  $(CC) $(OBJS) -o image

$(OBJS):$(SRC)
  $(CC) $(subst .o,.c,$@) -o $@ -c

以a.o为例,多目标规则将构建如下对a.o的规则:

a.o:a.c b.c x.c y.c
  $(CC) a.c -o a.o -c

意味着所有的目标依赖相同的文件

3.4 双冒号规则

用于同一个文件作为多个规则的目标。
在Makefile中,一个目标可以出现在多个规则中,但是这些规则必须是同一种规则,要么都是双冒号规则,要么都是普通规则。
普通规则会将所有依赖合并到一个目标文件。
双冒号规则的作用:

  • 当依赖列表为空,即使目标文件已存在,规则中的shell命令无条件执行
  • 根据依赖文件修改的情况,执行对应的命令
image::a.c
  $(CC) a.c -o $@ -L. -lx

image::b.c
  $(CC) b.c -o $@ -L. -ly

同一个目标出现在多个双冒号规则中,执行顺序按照书写顺序执行;
当a.c更新时,按照a.c文件重建目标image;
当b.c更新时,按照b.c文件重建目标image。

4 条件判断

OBJ = a.o b.o x.o y.o

#判断变量TOOLCHAIN是否有定义
ifdef TOOLCHAIN
  CC= $(TOOLCHAIN)
else
  CC = gcc
endif

image:$(OBJ)
#判断CC的值是否等于gcc,ifeq与后面的括号之间有空格
ifeq ($(CC), gcc)
  $(CC) $(OBJ) -o image -lgcc
else
  $(CC) $(OBJ) -o image
endif
...

执行:
make TOOLCHAIN=arm-none-linux-gnueabi-gcc

5 函数

函数格式:
$(function arg1,arg2,arg3,...)

5.1 wildcard

找到参数匹配的文件名

SRC=$(wildcard *.c)  #找到所有.c文件

5.2 文本处理函数

5.2.1 subst FROM,TO,TEXT

将字符串TEXT中的字符FROM替换为TO

5.2.2 patsubst PATTERN,REPLACEMENT,TEXT

按照PATTERN搜索TEXT中所有以空格隔开的单词,并将它们替换为REPLACEMENT
A = $(patsubst %.c,%.o,a.c b.c)
A的值为:a.o b.o

5.2.3 strip STRING

去掉字符串中开头和结尾的多余的空白符(空格、制表符),并将其中连续的多个空白符合并为一个。

5.2.4 findstring FIND,STRING

在字符串STRING中查找FIND子串
找到返回FIND,否则返回空

5.2.5 filter PATTERN,TEXT

过滤TEXT中不符合给定模式PATTERN的单词

A = a.c b.o c.s d.txt
B = $(filter %.c %.o,$(A))

B的值为:a.c b.o

5.2.6 filter-out PATTERN,TEXT

过滤TEXT中符合PATTERN模式的单词,与filter相反

5.2.7 sort LIST

将字符串LIST中的单词按字母升序的顺序排序,去掉重复的单词

5.2.8 word N,TEXT

取TEXT中的第N个单词,N必须为正整数

5.2.9 wordlist START,END,TEXT

取TEXT中介于START和END之间的子串;
START,END均为正整数
START大于TEXT中的单词总数或者START大于END时返回空

5.2.10 words TEXT

计算TEXT中的单词数

5.2.11 firstword TEXT

取字符串TEXT中的第一个单词;
相当于$(word 1 TEXT)

5.3 文件名的处理函数

5.3.1 dir NAMES

取文件列表NAMES中的每一个路径的目录部分

5.3.2 notdir NAMES

取文件列表NAMES中的每一个路径的文件名部分

5.3.3 suffix NAMES

取文件列表NAMES中的每一个路径文件的后缀部分;
指最后一个"."及后面的子串

5.3.4 basename NAMES

取文件列表NAMES中的每一个路径文件的前缀部分;
指最后一个"."前面的子串

5.3.5 addsuffix SUFFIX,NAMES

为文件列表NAMES中每一个路径的文件名添加后缀SUFFIX

5.3.6 addprefix PREFIX,NAMES

为文件列表NAMES中每一个路径的文件名添加前缀PREFFIX

5.3.7 wildcard PATTERN

获取满足匹配模式PATTERN的文件名

5.3.8 foreach VAR,LIST,TEXT

将LIST中以空格分割的单词依次取出赋值给VAR,然后执行TEXT表达式,重复直到LIST的最后一个单词

DIR = dir1 dir2
FILES = $(foreach dir,$(DIR),$(wildcard $(dir)/*))
all:
  echo $(FILES)

依次执行:
$(wildcard dir1/*)
$(wildcard dir2/*)

5.3.9 if CONDITION,THEN-PART[,ELSE-PART]

5.3.10 call VAR,ARGS,...

将ARGS一一替换VAR中的(1)、(2)...

A = hello, $(1) $(2)
B = $(call A )

5.3.11 origin VAR

查看参数VAR的出处

5.3.12 Shell COMMANDS

执行COMMANDS命令

6 实例

参考:
https://blog.csdn.net/xukai871105/article/details/37083675

6.1 单文件编译

#可执行文件
TARGET = test
#c文件
SRCS = main.c
#依赖目标
OBJS = $(SRCS:.c=.o) 

#指令编译器和选项
CC=gcc
CFLAGS = -Wall

$(TARGET):$(OBJS)
# @echo TARGET:$@
# @echo OBJECTS:$^
    $(CC) -o $@ $^

clean:
    $(RM) $(TARGET) $(OBJS) 

%.o:%.c
    $(CC) $(CFLAGS) -o $@ -c $

//执行:
make
gcc -Wall   -c -o main.o main.c
gcc -o test main.o

使用@echo指令:

make
gcc -Wall   -c -o main.o main.c

6.2 多文件编译

#可执行文件
TARGET = test
#c文件
SRCS = a.c b.c c.c d.c
#依赖目标
OBJS = $(SRCS:.c=.o) 

#指令编译器和选项
CC=gcc
CFLAGS = -Wall

$(TARGET):$(OBJS)
# @echo TARGET:$@
# @echo OBJECTS:$^
    $(CC) -o $@ $^

%.o:%.c
    $(CC) $(CFLAGS) -o $@ -c $<

clean:
    $(RM) $(TARGET) $(OBJS) 

6.3 生成自定义库

# 指令编译器和选项
CC = gcc
CFLAGS = -Wall 

# 目标文件
#需以lib开头, .so结尾
#在使用该共享库时系统会自动的去除lib和.so
#使用时写成-ltest
TARGET = libtest.so
# C文件
SRCS = test_func.c
# 目标文件
OBJS = $(SRCS:.c=.o)
 
# 链接为可执行文件
#shared参数意为共享形式的目标文件
$(TARGET):$(OBJS)
    $(CC) -shared -o $@ $^
 
clean:
    $(RM) $(TARGET) $(OBJS)
 
# 编译规则 $@代表目标文件 $< 代表第一个依赖文件
#-fPIC参数,意为生成和地址无关的目标文件
%.o:%.c
    $(CC) $(CFLAGS) -o $@ -fPIC -c $
//执行
gcc -Wall  -o test_func.o -fPIC -c test_func.c
gcc -shared -o libtest.so test_func.o

6.4 使用自定义库文件

# 指令编译器和选项
CC = gcc
CFLAGS = -Wall 
 
# 目标文件
TARGET = test
# C文件
SRCS = main.c
# 头文件查找路径
INC = -I.
# 库文件和库查找路径
DLIBS = -ltest
LDFLAGS = -L./lib
# 指定运行时的库文件路径
RPATH = -Wl,-rpath=./lib
 
# 目标文件
OBJS = $(SRCS:.c=.o)
 
# 链接为可执行文件
$(TARGET):$(OBJS)
    $(CC) -o $@ $^ $(LDFLAGS) $(DLIBS) $(RPATH)
 
clean:
    $(RM) $(TARGET) $(OBJS)
 
# 连续动作,请清除再编译链接,最后执行
exec:clean $(TARGET)
    @echo 开始执行
    ./$(TARGET)
    @echo 执行结束
 
# 编译规则 $@代表目标文件 $< 代表第一个依赖文件
%.o:%.c
    $(CC) $(CFLAGS) $(INC) -o $@ -c $<

//输出
gcc -Wall  -I. -o main.o -c main.c
gcc -o test main.o -L./lib -ltest -Wl,-rpath=./lib
```k
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容