通用Makefile讲解【转】

Makefle实质是一系列命令的集合,使用Makefile是为了提高效率!
Linux C肯定离不开Makefile,小工程也好大工程也罢,哪里都离不开Makefile
即使拥有强大IDE也不能像在Windows下编程一样完全不理会run按钮背后的动作!

1 一个简单的Makefile

从源代码到可执行程序,一般要经过预处理,编译,汇编和链接。

如有如下工程,msg.c中定义了一个打印函数,msg.h 中申明了这个打印函数,main.c中使用了这个打印函数。

$ ls main.c msg.c msg.h  $ gcc -Wall -c -o main.o main.c $ gcc -Wall -c -o msg.o msg.c$ gcc -Wall -o main.out main.o msg.o $ ls main.out main.c main.o msg.c msg.h msg.o$ ./main.outhello world!

gcc选项
-Wall 显示所有警告信息
-E 预处理
-S 编译,生成汇编文件
-c 汇编,生成二进制文件
-o 生成指定的文件

Makefile自动编译工程
Makefile

main.out: main.o msg.o gcc -Wall -o main.out main.o msg.o main.o: main.c msg.h gcc -Wall -c -o main.o main.c msg.o: msg.c msg.h gcc -Wall -c -o msg.o msg.c
$ make gcc -Wall -c -o main.o main.c gcc -Wall -c -o msg.o msg.c gcc -Wall -o main.out main.o msg.o $ ls main.c main.o main.out Makefile msg.c msg.h msg.o $ ./main.out hello world!

编译工程由多条编译命令现在只要一个make就能替代了,方便了一些,工程越大,Makefile的优势越加明显!

下面研究下这个Makefile文件

main.out: main.o msg.o gcc -Wall -o main.out main.o msg.o  main.o: main.c msg.h gcc -Wall -c -o main.o main.c  msg.o: msg.c msg.h gcc -Wall -c -o msg.o msg.c

此文件可分成三个部分,每个部分由两行组成,可称之为一组规则
第一行又由两个部分组成,用冒号隔开,冒号前面的是目标,冒号后面的是依赖
第二行是一条命令
1.Makefile最基本的三大要素目标依赖命令
2.make命令在当前目录找Makefile,找到后,其第一个目标被定为默认目标
3.要生成目标文件,每个依赖文件必须存在
4.依赖条件文件不存在,则查找能生成此依赖文件的规则
5.目标文件不存在,依赖文件存在,则执行命令
6.依赖文件比目标文件的修改日期更新,则执行命令
7.只有目标,没有依赖且目标文件不存在,则执行命令(伪目标除外)
8.命令必须以Tab开头,不能用空格替代

2 Makefile自动处理头文件依赖

main.out: main.o msg.o gcc -Wall -o main.out main.o msg.o main.o: main.c msg.hmsg.o: msg.c msg.h
$ make cc -c -o main.o main.c cc -c -o msg.o msg.c gcc -Wall -o main.out main.o msg.o $ ./main.out hello world!

可以看到后面两条规则没有命令,但是make也能运行正常,这是隐含规则自动推导的,
对于上面后两条规则没有命令,make会定义一条模式规则,并执行

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

上面规则中添加msg.h是有必要的,因为如果msg.h做了更新,如果此时依赖条件又没包含此头文件,则命令不会被执行,因而其他规则也不会被更新。

但这引发了一个问题,一个.c文件可能包含多个头文件,那么写Makefile时得把这些头文件依次加上,问题是谁会去记一个.c文件包含了哪些头文件呢!

可用gcc-MM选项完成这个工作

$ gcc msg.c -MM > msg.depend $ cat msg.depend msg.o: msg.c msg.h

其格式,刚好能代替Makefile中的规则

Makefile改成

all: make depend make main.out  main.out: main.o msg.o gcc -Wall -o main.out main.o msg.o  depend: gcc msg.c -MM > msg.depend gcc main.c -MM > main.depend  -include msg.depend -include main.depend clean: -rm *.out *.o  .PHONY: all depend clean

1.all设成第一个规则,即默认规则
2.make depend,跳至该规则
3.执行depend规则后,生成main.dependmsg.depend文件,把这两个文件includeMakefile中,相当于加了两条规则
min.o: main.c msg.h
msg.o: msg.c msg.h
4.make main.out ,跳至该规则
5.alldepend并非要生成文件的目标,其作用只是为了能执行对应的命令,称之为伪目标
6.如果刚好有与伪目标同名的文件存在,则make认为该规则最新,无需再创建,命令无法执行,所以伪目标用“.PHONY”申明(注意前面有个点),即不管是否有这个文件存在,命令始终执行
7.clean也是伪目标,负责清理工程,make clean可调用
8.includerm前面加了破折号“-”,表明忽略命令产生的错误,不加,命令产生错误make会中止运行

3 Makefile自动处理文件

到目前为止,该Makefile能较好运行了,可是如果工程较大,文件较多,可能会添加或删除一些文件,那么此时就要相应修改Makefile了,还是有些麻烦。

Makefile做些改进

FILES = $(wildcard *.c) # 所有.c文件 OBJS = $(FILES:.c=.o) # 所有.c对应的.o文件  all: make depend make main.out  main.out: $(OBJS) gcc -Wall -o $@ $^  depend: gcc $(FILES) -MM > MAKEFILE.DEPEND  -include MAKEFILE.DEPEND  clean: -rm *.out *.o  .PHONY: all depend clean

1.添加两个变量FILESOBJS,有了这两个变量,工程中有文件要添加和删除就不必修改Makefile
2.$(wildcard *.c)函数查找当前目录下所有.c文件
3.$(FILES:.c=.o)FILES变量中所有.c替换成.o,即所有中间文件
4.main.out规则中使用了变量,则命令中也应该使用变量,$@$^ 是所谓自动化变量,分别代表目标和所有依赖
5.depend规则中对头文件依赖做了整合

函数

1)字符串处理

$(subst from,to,text)字符串替换,在字符串text中出现from的子串都替换为to子串$(subst ee,EE,feet on the street),返回
fEEt on the strEEt
$(patsubst pattern,replacement,text) 字符串模式替换,与subst一样是字符串替换,pattern 可使用通配符“%”,表示匹配零个或若干个字符,text是用空格相隔的多个字符串组合$(patsubst %.c,%.o,x.c.c bar.c),返回
x.c.o bar.o
$(

 

var:

pattern
=

replacement
)

 

相当于

$(patsubst pattern
,

replacement
,$(

var
))$(objects:%.c=%.o),等同于$(patsubst %.c,%.o,$(objects))

$(var:suffix=replacement)相当于

$(patsubst%suffix,%replacement,$(var
))如$(objects:.c=.o),等同于$(patsubst %.c,%.o,$(objects))

$(strip string)去掉字符串首尾空格
$(findstring find,in)在字符串in中查找find字串,找到返回find,否则返回空
$(filter pattern...,text)返回text中匹配patter的子串, pattern可一个或多个,可使用通配符“%”text是用空格相隔的多个字符串组合
$(filter-out pattern...,text)filter相反,返回不匹配的子串
$(sort list)排序,list 是用空格相隔的多个字符串组合
$(word n,text)返回text中的第n个字符串,n1开始,text是用空格相隔的多个字符串组合
$(wordlist s,e,text)返回text中第s个到第e个之间的字符串,s1开始,e0开始,text是用空格相隔的多个字符串组合
$(words text)返回text中字符串个数,text是用空格相隔的多个字符串组合
$(firstword names...)返回names中第一个字符串,names用空格相隔的多个字符串组合
$(lastword names...)返回names中最后一个字符串,names用空格相隔的多个字符串组合

2)文件名处理

函数说明
$(dir names...)提取路径如$(dir src/foo.c hacks),返回‘

src/ ./

$(notdir names...)提取文件名如$(notdir src/foo.c hacks),返回‘
foo.c hacks
$(suffix names...)提取文件后缀如$(suffix src/foo.c src-1.0/bar.c hacks),返回

.c .c

$(basename names...)提取文件路径和文件名,去掉后缀如$(basename src/foo.c src-1.0/bar hacks),返回
src/foo src-1.0/bar hacks
$(addsuffix suffix,names...)添加后缀如$(addsuffix .c,foo bar),返回

foo.c bar.c

$(addprefix prefix,names...)添加前缀如$(addprefix src/,foo bar),返回

src/foo src/bar

$(join list1,list2)连接,如果list1list2是由空格相隔的多个字符串组合,list1中第一个字符串与list2中第一个字符串连接,以此类推如$(join a b,.c .o),返回

a.c b.o

$(wildcard pattern)返回匹配pattern的文件,pattern可使用通配符*
?
 
[...]$(wildcard *.o),返回所有.o文件
$(realpath names...)返回文件真实路径,文件不存在返回空
$(abspath names...)返回文件路径,不处理符号链接,文件不一定要存在

3)条件

函数说明
$(if condition,then-part[,else-part]) 
$(or condition1[,condition2[,condition3...]]) 
$(and condition1[,condition2[,condition3...]]) 

4foreach

函数说明
$(foreach var,list,text)先依次展开list,每次赋值给val,然后val代入text并返回如dirs = b c d$(foreach dir,$(dirs),$(wildcard $(dir)/*)),返回
$(wildcard a/*) $(wildcard b/*) $(wildcard d/*)

5shell

函数说明
$(shell cmd)返回shell命令的执行结果

变量

1.递归变量
用“=”赋值的变量不会马上展开,而是到最后再依次展开,即如果右值是一个变量,这个变量可在任何地方定义,而不一定在此变量之前定义;这样容易导致无穷递归,但是make会检测到这种错误

2.立即变量
用“:=”赋值的变量会马上展开

3.条件变量
用“?=”赋值的变量会判断此变量是否已定义过,如果没定义过则赋值,否则不做任何 事

4.追加变量
用”+=”赋值的变量会在其后追加值

5.自动化变量
$@ 目标
$< 第一个依赖
$^ 所有依赖

6.引用变量
如$(val), 括号是有必要的

4.Makefile自动处理文件夹

一个工程不在可能所有代码都在一个文件夹下,而是以模块为单位,一个模块单独一个文件夹,如


make之前                                    make之后

# 所有头文件路径HEAD_DIRS = $(foreach dir,$(shell ls),$(dir $(wildcard $(dir)/inc/*.h)))INCLUDE_DIRS = $(foreach dir,$(HEAD_DIRS),-I $(dir))# 所有.c文件路径SOURCE_DIRS = $(foreach dir,$(shell ls),$(dir $(wildcard $(dir)/src/*.c)))# 所有.c文件(包括路径)SOURCES = $(wildcard *.c)SOURCES += $(foreach dir,$(shell ls),$(wildcard $(dir)/src/*.c))# 生成.o文件的存放位置OBJ_DIR = obj# 所有.c对应的.o文件(不包括路径)OBJS = $(notdir $(SOURCES:.c=.o)) # 编译选项CC = gccCFLAGS = -Wall $(INCLUDE_DIRS)# 链接选项LD=$(CC)LDFLAGS = -pthread # 最终要生成的文件TARGET = main.out all: -mkdir $(OBJ_DIR) make depend make $(OBJ_DIR)/$(TARGET) # 3.链接$(OBJ_DIR)/$(TARGET): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJ_DIR)/*.o strip --strip-all $@ # 2.编译# 重建模式规则%.o: %.c $(CC) $(CFLAGS) -c -o $(OBJ_DIR)/$@ $<  # 1.头文件依赖 depend: $(CC) $(SOURCES) $(CFLAGS) -MM > .DEPEND clean: -rm .DEPEND $(OBJ_DIR)/* # .c和.o文件不在当前目录,加上文件搜索路径vpath %.c $(SOURCE_DIRS)vpath %.o $(OBJ_DIR) -include .DEPEND .PHONY: all exe depend clean

需要把生成的.o文件存放到一个目录下,所以重建模式规则%.o: %.c ’

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值