【Linux开发】另一个小规模工程的Makefile模板

和上一个Makefile模板相似,但是解决了一个问题,就是源码目录中可能有不需要被编译的源码文件,为了解决这个问题,需要设置好哪些源码文件需要被编译,我们可以在源码根目录下的Makefile中设置好,但是显得很不灵活方便,想到了Linux内核的Makefile配置方式,使用obj-y和obj-m这样的变量来代表需要编译的文件,我这里设计了一个专门的文件叫做SourceList,在每个源码目录中都会存放一个SourceList文件,这个文件里面存放的是需要被编译的文件名,使用ls命令打印出所有文件名 ,并修剪即可,文件之间隔空格或者换行都行,例如:

audio_test.cpp
TCloudHTTPSender.cpp
TCloudRealtimeASR.cpp
TCloudRequestBuilder.cpp
TCloudUtil.cpp
TConfigParse.cpp
TSpeex.cpp

Makefile文件的内容只有微小变化,将SOURCES变量的内容设置成读取各个源码目录的SourceList文件内容:

# GNU的make工作时的执行步骤如下:
# 1.读入所有的Makefile。
# 2.读入被include的其它Makefile。
# 3.初始化文件中的变量。
# 4.推导隐晦规则,并分析所有规则。
# 5.为所有的目标文件创建依赖关系链。
# 6.根据依赖关系,决定哪些目标要重新生成。
# 7.执行生成命令。
 
# Makefile最基础语法格式(依赖关系):
# targets : prerequisites
# <tab>commands
# 上面的示例中目标名为targets,其依赖于prerequisites表示的文件,
# 其可能是源码文件、头文件、库文件、其它依赖目标等,如果依赖文件中有
# 任何一个文件的更新时间新于targets表示的文件,那么依赖生效,则会
# 执行依赖下面的commands指令。
 
# Makefile中的变量其实相当于C语言里面的宏,会在使用变量的地方做替换
 
# Makefile通配符:* , ? 和 ~
# ~ 表示当前用户的HOME目录
# * 表示全能匹配
# ? 表示
 
# Makefile有三个非常有用的变量。分别是$@,$^,$<代表的意义分别是:
# $@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。
 
 
CC := g++
LD := g++
 
# 这是编译阶段使用的参数选项,一般是一些选项设置和头文件路径
CFLAGS  += -std=c++11 
 
# 这是链接阶段使用的参数选项,一般是链接库和链接库路径
LDFLAGS += -L./lib \
			-lpthread \
			-lm \
			-ldl

SOURCE_PATH ?= src src/a src/b src/c
BINARY_PATH ?= 
OBJECT_PATH ?= objs
INCLUDE_PATH := -I./include $(foreach tmp,$(SOURCE_PATH),-I$(tmp))
TARGET ?= target
 
# wildcard 通配符,将所有的源文件的文件名都赋值给SOURCES变量
# SOURCES := $(foreach tmp,$(SOURCE_PATH),$(wildcard $(tmp)/*.c $(tmp)/*.cpp))
SOURCES := $(foreach tmp,$(SOURCE_PATH),$(addprefix $(tmp)/,$(shell cat $(tmp)/SourceList)))

# notdir 过滤掉所有文件的路径信息,例如src/main.c变为main.c
# basename 去除文件的文件类型后缀,例如main.c变为main
# addprefix 添加一个前缀,为objects文件的存放目录,例如main变为objs/main
# addsuffix 添加一个后缀,为存放的中间文件的文件名,例如objs/main变为objs/main.o
OBJECTS := $(addsuffix .o,$(addprefix $(OBJECT_PATH)/,$(basename $(notdir $(SOURCES)))))
# OBJECTS := main.o display.o gpio.o video.o spi.o
 
# addsuffix 添加一个后缀,为存放的依赖文件的文件名
DEPENDS := $(addsuffix .d,$(OBJECTS))
 
# 第一个依赖,也是默认依赖,Makefile会根据这个依赖关系展开所有依赖
# 如果依赖的内容是其它的依赖目标,就会依次展开所有依赖目标的依赖关系,
# 例如这里就会展开$(TARGET)、test1、test2这三个依赖目标的依赖关系。
all : $(TARGET) test1 test2
# all : test1 test2
 
$(TARGET) : $(OBJECTS)
	$(info LD $@)
	$(LD) -o $@ $(OBJECTS) $(LDFLAGS)
 
# 可以将依赖关系和执行的命令分开描述,例如下面的两个依赖关系中没有直接
# 说明test1和test2两个依赖的命令部分,而是在后面定义了命令部分。
test1:
test2:
 
test1:
	$(info $@)
# $(info SOURCES:$(SOURCES))
# $(info OBJECTS:$(OBJECTS))
# $(info DEPENDS:$(DEPENDS))
# $(foreach tmp,$(SOURCES),\
# 	$(info $(addprefix $(OBJECT_PATH)/,$(addsuffix .o,$(basename $(notdir $(tmp)))))))
test2:
	$(info $@)
 
# 包含所有源文件的依赖关系,举其中一个依赖文件为例:
# objs/audio_test.o: src/audio_test.cpp include/TCloudRealtimeASR.h \
#  include/TCloudRealtimeASRDefines.h include/TCloudRequestBuilder.h \
#  include/TCloudHTTPSender.h include/TSpeex.h include/speex/speex.h \
#  include/speex/speex_types.h include/speex/speex_config_types.h \
#  include/speex/speex_bits.h
# 这是一个典型的依赖关系,‘.o’文件作为一个目标,其依赖于后面的所有源码文件和头文件,
# 如果后面的任何源文件发生修改都会导致依赖关系生效。但是没有描述依赖关系的命令,
# 这和上面的 test 依赖结构是一样的,在后面定义了依赖关系的命令。
-include $(DEPENDS)
 
# # eval函数用于动态添加makefile脚本,类似js中的eval。
# $(foreach tmp,$(OBJECTS),\
# 	$(eval $(tmp):;${CC} ${CFLAGS} -MMD -MF $(tmp).d -c -o  $(tmp) $(addprefix $(SOURCE_PATH)/,$(addsuffix .cpp,$(basename $(notdir $(tmp)))))))
$(foreach tmp,$(SOURCES),\
	$(eval $(addprefix $(OBJECT_PATH)/,$(addsuffix .o,$(basename $(notdir $(tmp))))):; \
				${CC} ${CFLAGS} $(INCLUDE_PATH) \
				-MMD -MF $(OBJECT_PATH)/$(basename $(notdir $(tmp))).o.d \
				-c -o $(OBJECT_PATH)/$(basename $(notdir $(tmp))).o $(tmp)))
	
.PHONY : clean setenv
clean:
	rm objs/*.o objs/*.d ${TARGET}

示例:

猜你喜欢

转载自blog.csdn.net/tq384998430/article/details/100676782
今日推荐