Makefile学习

5 篇文章 0 订阅

简述

Makefile是一种被用作自动化编译,Makefile定义了一系列的规则来指定文件编译顺序,以及哪些文件需要重新编译,甚至可以像shell脚本一样执行操作系统的命令。
本篇只是简单概念及使用方式,详细的可参考:C语言中文网-Makefile教程

规则

普通规则

target... : prerequisites ...
     command
     ......
模块描述
target可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)
prerequisites生成该target所依赖的文件,可以有多个依赖
command该target要执行的命令(任意的shell命令)

Make工具从上找寻target,根据target后对应的依赖关系,先去查找依赖项的文件。若依赖项不存在,则在makefile中去寻找对应的target,直至生成需要的文件;否则,直接调用command部分。若找到最底层都无法满足最终目标文件的生成条件,就会报错。

在检查依赖关系时,同时会检查目标与源文件的时间戳,当源文件时间戳更新时,make会更新依赖它的链路上所有文件。

	# test依赖于libtest.a main.o  
	test: libtest.a main.o  
	    gcc main.o -ltest -L. -o test  
	# main.o依赖于main.c  
	main.o:main.c  
	    gcc main.c -c -o main.o  
	# libtest.a依赖于test1.o test2.o  
	libtest.a: test1.o test2.o  
	    ar -r libtest.a test1.o test2.o  
	# test1.o依赖于test1.c  
	test1.o:test1.c  
	    gcc test1.c -c -o test1.o  
	# test2.o依赖于test2.c  
	test2.o:test2.c  
	    gcc test2.c -c -o test2.o  

模式规则

模式规则类似于普通规则,只是在模式规则中,目标名中需要包含有模式字符“%”,包含有模式字符“%”的目标被用来匹配一个文件名,“%”可以匹配任何非空字符串。

	%.o:%.c  
	    gcc -c $< -o $@

后缀规则

后缀规则是一个比较老式的定义隐含规则的方法。后缀规则不允许依赖文件,如果有,前面的后缀会被认为是文件名。

	#下面的写法等同于模式规则 %.o:%.c
	.c.o: 
	    gcc -c -o $@ $<  
	#下面的写法会被解析为普通规则  
	.c.o:foo.h  
    gcc -c -o $@ $<  

模式规则和后缀规则的差异

模式规则定义后会覆盖隐式规则,后缀规则不会,所以后缀规则不添加命令将没有意义。

	#后缀规则编译成功  
	main: main.o  
	    echo "generate target"  
	    gcc -o main $<  
	  
	.c.o:  
	   
	#模式规则编译失败  
	main: main.o  
	    echo "generate target"  
	    gcc -o main $<  
	  
	%.o: %.c   

变量和函数

单纯使用基本语法,若要对项目新增模块或文件,需要手动添加新的依赖关系,不利于扩展。引入变量,自动完成依赖关系添加。Makefile中的变量类型基本上就可以直接理解为字符串类型。

等号

Makefile中的等号有4种,"=",":=","?=","+="。

"?="表示,如果左边的变量没有被赋值,那么将等号右边的值赋给左边的变量。如果赋过值,则保持原来的值不变。

"+="表示将等号右边的值追加到左边变量中,但是中间会有一个空格。

"=“与”:=“是比较不好区分的两个等号,可以将”=“理解为"址传递"或引用,”:=“理解为"值传递”。
在Makefile中是不允许将变量自己的值赋给自己的,Makefile不允许出现循环引用。

	var := ${var} 1 2 3 	# 允许  
	var  = ${var} 1 2 3 	# 不允许 

最开始例子中*.o的生成方式可写成如下。

	SRC = test1.c test2.c main.c  
	OBJ = test1.o test2.o main.o  
	  
	${OBJ}:${SRC}  
	gcc -c ${SRC}  

函数

使用 “$(< function > < arguments >)” 的形式可以调用函数。

函数名作用
wildcard通配符展开函数
patsubst字符串替换

常见用法:

	src := $(shell ls *.c)  
	objs := $(patsubst %.c, %.o, $(src))  
	
	${objs}:${src} 
	gcc -c ${src} 

自动化变量

关于自动化变量可以理解为由 Makefile 自动产生的变量,而且只能在规则中使用。

自动化变量作用
$@规则的目标文件名(依赖关系中冒号:左边的文件,如果a: b c,那么$@指a)
$%当目标文件是一个静态库文件时,代表静态库的一个成员名
$<被依赖文件的第一项(如果a: b c,那么$<指b)
$?所有比目标文件更新的依赖文件列表,空格分隔
$^所有依赖文件列表,使用空格分隔(如果a: b c ,那么$^指b c),不包含重复文件
$+所有依赖文件列表,使用空格分隔(如果a: b c c,那么$+指b c c),包含重复文件
$*在模式规则和静态模式规则中的"%“所匹配的内容。这个变量表示目标模式中”%“及其之前的部分。如果目标是"dir/a.foo.b”,并且目标的模式是"a.%.b",那么,"$*“的值就是"dir/a.foo”

递归编译

递归编译主要通过make的 -C 参数实现,使用该方式命令会进入后面的传入的目录中查找makefile并执行。一般这种情况需要再最外层设置一个总控的makefile,完成整个项目的编译。

	make -C $(dirpath) 

内置变量

$(AR)    		# 生产 archive 文件的默认程序 ar
$(CC)    		# 编译 C 代码的默认编译器 cc
$(CXX)   		# 编译 C++ 代码的默认编译器 g++
$(CFLAGS)    	# 编译 C 代码的参数
$(CXXFLAGS)  	# 编译 C++ 代码的参数
$(LDFLAGS)		# 链接时的参数

基本模板

多文件项目编译

CC = gcc
LD = gcc
SRCS = $(wildcard *.cpp)
OBJS = $(patsubst %cpp, %o, $(SRCS))
# -I指定头文件目录
INCLUDE = -I./include
# -L指定库文件目录,-l指定静态库名字(去掉文件名中的lib前缀和.a后缀)
LDFLAGS = -L./libs -ltomcrypt
# 开启编译warning和设置优化等级
CFLAGS = -Wall -O2
CXXFLAGS = 

TARGET = TestDemo

.PHONY:all clean

all: $(TARGET)
# 链接时候指定库文件目录及库文件名
$(TARGET): $(OBJS)
	$(LD) -o $@ $^ $(LDFLAGS)
 
# 编译时候指定头文件目录
%.o:%.cpp
	$(CC) -c $^ $(INCLUDE) $(CFLAGS) 

clean:
	rm -f $(OBJS) $(TARGET)

多层目录编译

.PHONY:all clean
# 排除目录
exclude_dirs := .git
# 显示深度为1的子目录
dirs := $(shell find . -type d -maxdepth 1)
# 去掉获取到目录名称前面的./
dirs := $(basename $(patsubst ./%, %, $(dirs)))
# 过滤指定目录
dirs := $(filter-out $(exclude_dirs), $(dirs))

all:
    $(foreach N,$(dirs),make -C $(N);)
clean:
    $(foreach N,$(dirs),make -C $(N) clean;)
  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值