多目录下Makefile文件的编写

菜鸟第一次接触Makefile,直接上,基本把所有的坑都踩了一遍,写下来纪念一下。

        首先说一下我们的项目,自己写的主程序在main.c文件中,此外主程序需要调用某个客户端程序的功能,因此还需要把该客户端的源码包含进去(本来是将客户端编译成库直接调用的,无奈客户端的功能满足不了我们的需求,还得改客户端程序),客户端程序代码文件太多,很杂乱,为了和主函数区分,所以将客户端的所有.c文件放在./src文件夹中,把对应的.h文件放在./include文件夹中,将所有输出的.o文件(包含main.o)放在./obj文件夹中,主函数main.c仍放在项目主目录下,大概文件结构如下图所示:

1.接下来的所有操作都在main目录下进行,为了以后更改方便,所以把所有目录都定义成变量

SRC=./src
OBJ=./obj
DIR=.
INC=./include

2.然后进行源文件的展开和对应目标文件的替换(表达可能不准确,但这是我自己理解的意思),这里真的好多小细节(大坑)

SOURCE  := $(wildcard $(SRC)/*.c)  
OBJS    := $(patsubst %.c,$(OBJ)/%.o,$(notdir $(SOURCE))) 
MAIN_SOURCE  := $(wildcard $(DIR)/*.c)
MAIN_OBJS    := $(patsubst %.c,$(OBJ)/%.o,$(notdir $(MAIN_SOURCE)))

首先看第一句,SOURCE  := $(wildcard $(SRC)/*.c)  ,此句的执行结果是将src文件夹中的所有c文件列举出来,比如./src/a.c、./src/b.c等等,中间自动用空格分开,echo $(SOURCE),应该输出./src/a.c ./src/b.c等等

变量名为SOURCE,$var代表引用名为var的变量,wildcard意为扩展通配符(普通的通配符为%,但是在包含文件路径时%不好用,因为%只能匹配文件名),用法为$(wildcard PATTERN...),将符合PATTERN格式的所有文件列举出来,并以空格分开,此句中的PATTERN为$(SRC)/.*c,意为./src文件夹中所有.c文件,*代表任意文件名。

第二句为可以生成的目标文件,OBJS    := $(patsubst %.c,$(OBJ)/%.o,$(notdir $(SOURCE))) ,这句话的执行结果是将所有.c文件替换成.o文件,并且放在obj目录下,即echo $(OBJS),应该输出./obj/a.o ./obj/b.o 等等,但是这句话并不真的生成.o文件,只是替换而已。

patsubst函数以为替换通配符,用法为$(patsubst <pattern>,<replacement>,<text> ),将符合text文本中的.c文件,都替换成replacement。在此句中即是将SOURCE中的所有.c文件都替换到obj目录下的.o文件。在这里,注意pattern位置的.c不要带任何文件路径,就只用通配符%即可,之前看别的教程带有目录,我测试了一下并不对。。。去掉就好了;text中的notdir一定要加,因为前面用的是通配符%,不去掉文件目录会报错的,echo $(notdir $(SOURCE)),输出的就是 a.c b.c等等,将前面的./src/去掉了。

第三四句和第一二句类似。

3.接下来定义一下编译时的参数

CC      := gcc
LIBS    := -lpthread -lconfig 
LDFLAGS :=
DEFINES :=
INCLUDE := -I $(INC) 
CFLAGS  := -g -Wall -O3 -fPIC $(DEFINES) $(INCLUDE) -DHAVE_CONFIG_H
TARGET  := main

这里的参数根据自己需要定义,CC就代表gcc命令,LIBS代表编译时需要的库,中间两个可以忽略,没用到,INCLUDE代表头文件的路径。

4.生成目标文件

$(OBJS):./obj/%.o:./src/%.c 
	$(CC) -c $< -o $@ $(INCLUDE)

第一步,生成所有客户端文件的目标文件。这是Makefile的静态规则,这句命令是gcc -c ./src/xxx.c -o ./obj/xxx.o -I ./include ,遍历所有的.c文件,生成所有相对应的.o文件,有多少c文件,就执行多少次。这种写法很适合有多个c文件的情况,不用一行一行敲gcc,一行命令就解决了。其中$<代表一个.c文件,$@代表目标文件。注意这里的坑,第一句,./obj/%.o:./src/%.c 我看别的教程中用$(OBJ)/%.o : $(SRC)/%.c 来代替的,但是一直报错——“没有对应的规则生成xxx.o”,后来又改回来了,就通过了,很奇怪,没想通咋回事,但是能用就好了。。。

$(MAIN_OBJS):./obj/%.o:./main.c 
	$(CC) -c $< -o $@ $(INCLUDE)

第二步,生成主函数的目标文件,和上面类似。

5.生成最终可执行文件

$(TARGET) : $(OBJS) $(MAIN_OBJS)
	$(CC) $(CFLAGS) -o $@ $(OBJS) $(MAIN_OBJS) $(LDFLAGS) $(LIBS)

这一步应该是链接,将所有的目标文件和所需的库链接在一起,生成最终可执行文件。目标为main,依赖文件为所有的.o文件,生成规则为下面一行。

6.定义其他操作

有时候需要清楚或者重新编译之类的,所以可以定义一些其他操作:

.PHONY : everything objs clean rebuild 

everything : $(TARGET)

objs : $(OBJS)

rebuild: clean everything

clean :
	rm -fr $(TARGET)
	rm -fr $(OBJS) $(MAIN_OBJS)

PHONY是一个伪目标,可以防止在Makefile中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突,另一种是提交执行makefile时的效率,比如make clean 就清除生成的可执行文件main和所有的目标文件。

PS:其实网上看了很多教程,和我的情况都不咋匹配,所以踩了很多坑,不是打广告,只是想和大家分享,让大家少走弯路,可以去b站搜索“快乐学习的代码狗”,他的《零基础学会LINUX系统编程》里面的“09-makefile3个自动变量和模式规则”,“10-习题和作业”讲的很清楚,一共只要40分钟,比在网上找教程快的多。

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值