[linux专题] 关于makefile 编写的详解

目录

1.为什么需要makefile

2.makefile基本的格式

3.makefile 工作流程

4.基本写法案例

5.升级写法案例

6. 常用工程写法案例

7.基础语法备查


1.为什么需要makefile

主要是项目比较大时,方便多人一起开发,管理和编译工程。

2.makefile基本的格式

target: prerequisites

     cmd

3.makefile 工作流程

-step1:读入所有的makefile。
-step2:读入被include的其它makefile。
-step3:初始化文件中的变量。
-step4:推导隐晦规则,并分析所有规则。
-step5:为所有的目标文件创建依赖关系链。
-step6:根据依赖关系,决定哪些目标要重新生成。
-step7:执行生成命令

4.基本写法案例

    有一个工程,三个 .c文件,分别为 main.c   add1.h  add1.c   add2.h  add2.c ,需要编译生成一个  add.out 的可执行文件

按照格式 编写如下:

#格式
#target:prerequisites
#    cmd
#

add.out : main.o  add1.o  add2.o
    gcc main.o add1.o add2.o -o add.out

main.o : main.c
    gcc main.c -c -Wall -g -o main.o

add1.o : add1.c
    gcc add1.c -c -Wall -g -o add1.o

add2.o : add2.c
    gcc add2.c -c -Wall -g -o add2.o

    编写makefile后,只有生成的文件被修改后,才会需要重新编译。

但是我们经常想要重新编译,这个时候,我们会在makefile中,增加一条命令clean命令,见如下代码。

#格式
#target:prerequisites
#    cmd
#

add.out : main.o  add1.o  add2.o
    gcc main.o add1.o add2.o -o add.out

main.o : main.c
    gcc main.c -c -Wall -g -o main.o

add1.o : add1.c
    gcc add1.c -c -Wall -g -o add1.o

add2.o : add2.c
    gcc add2.c -c -Wall -g -o add2.o

clean:
    rm *.o add.out -rf

5.升级写法案例

基本写法只是为了更好让我们理解编译过程,但是文件之间的关联关系需要自己去区分,当工程足够大的时候,对我们来说管理太复杂了,因此需要进一步说明升级写法。

同样基于上面案例说明

高级写法的特点之一,就是用变量以及通配符匹配的一些方式,来省略我们自己去手动区分管理文件关联关系。

如上面 我们定义变量 OBJS = main.o add1.o add2.o   ,取变量的值使用符号 $   ,其中注意下 变量RM 相关于 rm -f

变量替换方式案例

#格式
#target:prerequisites
#    cmd
#

OBJS = main.o  add1.o  add2.o
CC = gcc
CFLAGS += -c -Wall -g

add.out : $(OBJS)
    $(CC) $(OBJS) -o add.out

main.o : main.c
    $(CC) main.c $(CFLAGS) -o main.o

add1.o : add1.c
    $(CC) add1.c $(CFLAGS) -o add1.o

add2.o : add2.c
    $(CC) add2.c $(CFLAGS) -o add2.o

clean:
    $(RM) *.o add.out -r

上面的案例我们看着已经有些简洁了,但是依赖关系还是没给我们省掉

再来说明几个 符号,其中的 $^ 表示继承上一句的文件  $@表示继承上一句的目标

add.out : $(OBJS)
    $(CC) $(OBJS) -o add.out
--------
add.out : $(OBJS)
    $(CC) $^ -o $@
--------
两者是一个效果

下面进一步说明,在我们编译 生成.o 时,我们看到语句 其实 都是xx.o: xx.c ,这个时候,我们就可以使用通配符 % 来替代,结合可以得到如下的makefile。

#格式
#target:prerequisites
#    cmd
#

OBJS = main.o  add1.o  add2.o
CC = gcc
CFLAGS += -c -Wall -g

add.out : $(OBJS)
    $(CC) $(OBJS) -o $@

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

clean:
    $(RM) *.o add.out -r

6. 常用工程写法案例

我们经常使用到的工程都是有多个 文件夹 很多文件,并且很多文件夹的地方包含 makefile,那么针对这类大型工程,makefile如何写呢?

一个makefile,包含其他makefile,基本语法 主要用于包含 其他.mk文件

include <filename>

 路径包含

VPATH = src:../headers
----
路径用:隔开

案例说明

文件目录如下

基本代码

其他test 代码类似,仅供测试

主makefile

# this is the main makefile

TGT = jy.out

SUB_DIR = main test1 test2 test3

export SUB_TGT = built-in.o

export TOP_DIR = $(shell pwd)

export HEAD_DIR += $(TOP_DIR)/test1 $(TOP_DIR)/test2 $(TOP_DIR)/test3 $(TOP_DIR)/test3/test4

export CC = gcc

export CFGLAGS += -I $(HEAD_DIR) -Wall

export LD =ld

export LDFLAGS = 

$(TGT): $(SUB_DIR)
    $(CC) $(CFLAGS) $(^:=/$(SUB_TGT))

$(SUB_DIR):
    make -C $@

clean:
	-rm -vf $(TGT) 
	for dir in $(SUB_DIR);do \
		make -C $$dir clean;\
	done

.PHONY:clean &(SUB_DIR)

子makefile

# this is the sub  makefile
SRCS = main.c

SUB_DIR = 

$(SUB_TGT): $(SRCS:.c=.o) $(SUB_DIR)
	$(LD) $(LDFLAGS) $(SRCS:.c=.o) $(SUB_DIR:=/$(SUB_TGT)) -r -o $@

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

%.d: %.c 
	$(CC) $(CFLAGS) $< -MM > $@
sinclude $(SRCS:.c=.d)

$(SUB_DIR):
	make -C $@

clean:
	-rm -vf $(SUB_TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)
	for dir in $(SUB_DIR);do \
		make -C $$dir clean;\
	done

7.基础语法备查

变量定义 

OBJ = a.o b.o    

OBJ+= c.o  # 表示追加  相当于 OBJ = a.o b.o  c.o

OBJ:= d.o  # 表示替换  相当于 OBJ = d.o

OBJ?= e.o  # 表示未定义才重新定义 相当于 OBJ = d.o

变量取值  $(OBJ)

通配符  * ? [....]

如 用 a.o  b.o c.o 通配符 替换 为 *.o

如 用 [abcd]  --> [a-d]

伪目标

伪目标是一个标签,而不是一个文件,常用语 clean处,如

.PHONY clean:

clean :

    rm -rf *.o &(TARGET)

自动取目标

   使用 $@

自动取依赖项

   使用$^  或$>

字符串处理函数

subst  字符替换

基本语法 $(subst  CC,cc,get cc to CC)  出来的字符 是 get cc to cc 表示把CC 替换成 cc

patsubst 模式替换

基本语法 $(patsubst  %.c,%.o,bar.c)  出来的字符 是bar.o 表示把所有.c替换成 o

findstring 查找字符串

基本语法 $(findstring a,a b c) 出来的字符 是a

基本语法 $(findstring a,b c) 出来的字符 是空字符

filter 模式过滤字符串

基本语法 $(filter %c %o,a.c b.o c.s) 出来的字符 是a.c b.o

filter-out 模式反过滤字符串

基本语法 $(filter-out %c %o,a.c b.o c.s) 出来的字符 是c.s

sort  按字符排序

基本语法 $(sort  b a c d) 出来的字符 是a b c d

word 取单词数

基本语法 $(word 2, i am a boy) 出来的字符 是am

words 统计单词个数

基本语法 $(words  i am a boy) 出来的值 是 4

文件名操作

dir 取目录

基本语法 $(dir usr/src/test/makefile  hello.c) 出来的字符 是 usr/src/test   和 ./

notdir 取文件名

基本语法 $(notdir usr/src/test/makefile  hello.c) 出来的字符 是 makefile  和 hello.c

suffix 取后缀

基本语法 $(suffix usr/src/test/makefile  hello.c) 出来的字符 是 .c

basename取前缀

基本语法 $(basename usr/src/test/makefile  hello.c) 出来的字符 是 makefile hello

addsuffix 加后缀

基本语法 $(addsuffix  .c ,foo1 foo) 出来的字符 是foo1.c foo.c

addprefix 加前缀

基本语法 $(addprefix /usr/src/ ,foo1.c foo.c) 出来的字符 是 /usr/src/foo1.c  /usr/src/foo.c

循环函数

foreach

基本语法举例

names := a b c d

files := $(foreach n,names,$(n).c)

得到$(files) = a.c b.c c.c d.c

条件判断

ifeq  ifneq  ifdef ifndef 

makefile中 一些默认变量

变量       含义
AR函数库打包 同ar
AS汇编语言编译程序, 同 as
CCC语言编译,同 cc
CXXC++语言编译,同 g++
CO从RCS文件中扩展文件程序,同co
CPP C程序的预处理器 同 $(CC) -E
GET从SCCS文件中扩展文件的程序, 同 get
YACC      yacc文法分析器,同 yacc
MAKEINFO  转换texinfo文件.texi到infor文件程序,同 makeinfo        
RM删除文件命令,同 rm -f

makefile中 一些自动变量

$@匹配目标中模式定义的集合
$%目标是函数库文件是,表示规则中的目标成员名,如果不是函数库文件,则值为空
$<依赖目标中的第一个目标名字,如果以来目标是以模式定义,则表示符合模式的一系列的文件集
$^所有的依赖目标的集合
$?所有比目标新的依赖目标的集合
$+同$^
$*目标模式中 % 及其之前的部分
$(@D)针对目录部分
$(@F)针对文件部分
其他 跟D 和F的类似

make 参数选项

-Cdir读入指定目录下的makefile
-f  file读入当前目录下的file文件作为makefile
-i 忽略所有命令执行错误
-I dir制定被包含的makefile所在目录
-n  只打印要执行的命令,但是不执行这些命令
-p  显示make变量数据库的隐含规则
-s  在执行命令时不显示命令
-w  如果执行make在执行过程中改变目录,打印当前目录名

版权声明:本文为AgingMoon原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。