千家信息网

makefile(09)_扩展支持

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,23.独立模块的支持23.1.问题:一般而言,不同工程师负责不同模块的开发,编译环境中如何支持模块的独立编译?23.2.问题背景:大型项目的代码成千上万,完整编译的时间较长,编写模块代码时,可以通过编
千家信息网最后更新 2026年10月31日makefile(09)_扩展支持

23.独立模块的支持

23.1.问题:

一般而言,不同工程师负责不同模块的开发,编译环境中如何支持模块的独立编译?

23.2.问题背景:

  • 大型项目的代码成千上万,完整编译的时间较长,
  • 编写模块代码时,可以通过编译检查语法错误;
  • 为了提高开发效率,需要支持指定模块的独立编译

    23.3.解决方案

  • 将模块名(module)作为目标名(伪目标)建立规则
  • 目标(module)对应的依赖为build build/module
  • 规则中的命令进入对应的模块文件夹进行编译
  • 编译结果存放于build文件夹下

    23.4.关键技术点

    如何获取make命令行中指定编译的模块名?
    预定义变量:$(MAKECMDGOALS),命令行中指定的目标名(make的命令行参数)

    编程实验:

    $(MODULES) : $(DIR_BUILD) $(DIR_BUILD)/$(MAKECMDGOALS)@echo "Begin to compile $@"@set -e; \cd $$dir && \$(MAKE) all \        DEBUG:=$(DEBUG) \        DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) \        DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC)) \        CMD_CFG:=$(addprefix $(DIR_PROJECT)/, $(CMD_CFG)) \        MOD_CFG:=$(addprefix $(DIR_PROJECT)/, $(MOD_CFG)) \        MOD_RULE:=$(addprefix $(DIR_PROJECT)/, $(MOD_RULE)) && \cd .. ;

    注意:自动变量只能在规则的命令中使用,不能在依赖中使用

    23.5.Makefile中的代码复用

    当不同规则中的命令大量重复时,可以考虑自定义函数
    Makefile中的自定义函数是一种代码复用的方式

23.6.思路

将编译模块的命令集作为自定义函数的具体实现
函数参数作为模块名,函数调用后编译参数指定的模块
在不同的规则中调用该函数

编程实验:

define makemodule    cd $(1) && \    $(MAKE) all \            DEBUG:=$(DEBUG) \            DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) \            DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC)) \            CMD_CFG:=$(addprefix $(DIR_PROJECT)/, $(CMD_CFG)) \            MOD_CFG:=$(addprefix $(DIR_PROJECT)/, $(MOD_CFG)) \            MOD_RULE:=$(addprefix $(DIR_PROJECT)/, $(MOD_RULE)) && \    cd .. ; endef

23.7.总结

  • 编写模块代码时可以通过模块独立编译快速检查语法错误
  • 自动变量只能在规则的命令中使用,不能在依赖中使用
  • Makefile中的自定义函数是代码复用的一种方式
  • 当不同规则中的命令大量重复时,可以考虑自定义函数

    24.第三方库的支持

    24.1.问题

    当需要使用第三方库文件时,编译环境中的makefile如何修改?

    24.2.经验假设

  • 第三方库通过函数调用的方式提供库中的功能
  • 库文件发布时都附带了声明库函数原型的头文件
  • 编译阶段使用头文件,链接阶段使用库文件

    24.3.第三方库在项目中的位置

    24.4.第三方库的编译阶段

    定义变量DIR_LIBS_INC用于指示头文件的存储位置
    DIR_LIBS_INC := $(DIR_PROJECT)/libs/inc
    使用DIR_LIBS_INC提示make头文件的存储位置
    Vpath %$(TYPE_INC) $(DIR_LIBS_INC)
    使用DIR_LIBS_INC提示编译器头文件的存储位置
    CFLAGS += -i$(DIR_LIBS_INC)
    编程实验(改动如下):

    24.5.注意事项

    定于DIR_LIBS_LIB := libs/lib (第三方库所在位置)
    链接时不会直接链接DIR_LIBS_LIB中的库文件,需要先将库文件拷贝到DIR_BUILD文件夹
    必须考虑拷贝后的库文件和原始库文件的新旧关系

    $(DIR_BUILD)/% : $(DIR_LIBS_LIB)/%$(CP) $^ $@

    24.6.第三方库链接的支持

    定义变量EXTERNAL_LIB用于保存第三方库列表
    目标link需要依赖第三方库列表

    原因是,可能存在子模块库名和第三方库名相同的情况,这时我们要优先链接子模块。
    编程实验(改动如下):

最终方案:模块makefile:

include $(MOD_CFG)# Custmization Begin# # DIR_SRC := src# DIR_INC := inc## TYPE_INC := .h# TYPE_SRC := .c# TYPE_OBJ := .o# TYPE_DEP := .dep## Custmization Endinclude $(CMD_CFG)include $(MOD_RULE)

模块makefile中include包含的子模块mod-cfg.mk:

DIR_SRC := srcDIR_INC := incTYPE_INC := .hTYPE_SRC := .cTYPE_OBJ := .oTYPE_DEP := .dep

模块makefile中include包含的子模块mod-rule.mk:

.PHONY : allMODULE := $(realpath .)MODULE := $(notdir $(MODULE))DIR_OUTPUT := $(addprefix $(DIR_BUILD)/, $(MODULE))OUTPUT := $(MODULE).aOUTPUT := $(addprefix $(DIR_BUILD)/, $(OUTPUT))SRCS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))OBJS := $(SRCS:$(TYPE_SRC)=$(TYPE_OBJ))OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(OBJS))DEPS := $(SRCS:$(TYPE_SRC)=$(TYPE_DEP))DEPS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(DEPS))vpath %$(TYPE_INC) $(DIR_INC)vpath %$(TYPE_INC) $(DIR_COMMON_INC)vpath %$(TYPE_INC) $(DIR_LIBS_INC)vpath %$(TYPE_SRC) $(DIR_SRC)-include $(DEPS)all : $(OUTPUT)    @echo "Success! Target ==> $(OUTPUT)"$(OUTPUT) : $(OBJS)    $(AR) $(ARFLAGS) $@ $^$(DIR_OUTPUT)/%$(TYPE_OBJ) : %$(TYPE_SRC)    $(CC) $(CFLAGS) -o $@ -c $(filter %$(TYPE_SRC), $^)$(DIR_OUTPUT)/%$(TYPE_DEP) : %$(TYPE_SRC)    @echo "Creating $@ ..."    @set -e; \    $(CC) $(CFLAGS) -MM -E $(filter %$(TYPE_SRC), $^) | sed 's,\(.*\)\.o[ :]*,$(DIR_OUTPUT)/\1$(TYPE_OBJ) $@ : ,g' > $@

模块makefile中include包含的子模块cmd-cfg.mk:

AR := arARFLAGS := crsCC := gccLFLAGS := CFLAGS := -I$(DIR_INC) -I$(DIR_COMMON_INC) -I$(DIR_LIBS_INC)ifeq ($(DEBUG),true)CFLAGS += -gendifMKDIR := mkdirRM := rm -frCP := cp

项目主makefile:

include pro-cfg.mkinclude cmd-cfg.mkinclude pro-rule.mk

项目makefile中include包含的子模块pro-cfg.mk:

MODULES := common \           module \           mainMOD_CFG := mod-cfg.mkMOD_RULE := mod-rule.mkCMD_CFG := cmd-cfg.mkDIR_BUILD := buildDIR_COMMON_INC := common/incDIR_LIBS_INC := libs/incDIR_LIBS_LIB := libs/libAPP := app.out

项目makefile中include包含的子模块pro-rele.mk:

.PHONY : all compile link clean rebuild $(MODULES)DIR_PROJECT := $(realpath .)DIR_BUILD_SUB := $(addprefix $(DIR_BUILD)/, $(MODULES))MODULE_LIB := $(addsuffix .a, $(MODULES))MODULE_LIB := $(addprefix $(DIR_BUILD)/, $(MODULE_LIB))EXTERNAL_LIB := $(wildcard $(DIR_LIBS_LIB)/*)EXTERNAL_LIB := $(patsubst $(DIR_LIBS_LIB)/%, $(DIR_BUILD)/%, $(EXTERNAL_LIB))APP := $(addprefix $(DIR_BUILD)/, $(APP))define makemodule    cd $(1) && \    $(MAKE) all \            DEBUG:=$(DEBUG) \            DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) \            DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC)) \            DIR_LIBS_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_LIBS_INC)) \            CMD_CFG:=$(addprefix $(DIR_PROJECT)/, $(CMD_CFG)) \            MOD_CFG:=$(addprefix $(DIR_PROJECT)/, $(MOD_CFG)) \            MOD_RULE:=$(addprefix $(DIR_PROJECT)/, $(MOD_RULE)) && \    cd .. ; endefall : compile $(APP)    @echo "Success! Target ==> $(APP)"compile : $(DIR_BUILD) $(DIR_BUILD_SUB)    @echo "Begin to compile ..."    @set -e; \    for dir in $(MODULES); \    do \        $(call makemodule, $$dir) \    done    @echo "Compile Success!"link $(APP) : $(MODULE_LIB) $(EXTERNAL_LIB)    @echo "Begin to link ..."    $(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)    @echo "Link Success!"$(DIR_BUILD)/% : $(DIR_LIBS_LIB)/%    $(CP) $^ $@$(DIR_BUILD) $(DIR_BUILD_SUB) :     $(MKDIR) $@clean :     @echo "Begin to clean ..."    $(RM) $(DIR_BUILD)    @echo "Clean Success!"rebuild : clean all$(MODULES) : $(DIR_BUILD) $(DIR_BUILD)/$(MAKECMDGOALS)    @echo "Begin to compile $@"    @set -e; \    $(call makemodule, $@)

24.7.总结

  • 编译环境必须支持第三方库的使用(静态库或动态库)
  • 工程开发中一般会使用特殊的文件夹存放第三方库
  • 第三方库所附带的头文件用于声明库函数(编译阶段需要)
  • 在链接阶段先将库文件拷贝到build文件夹,再进行连接
模块 编译 第三方 函数 文件 命令 库文件 规则 支持 代码 链接 不同 位置 变量 文件夹 目标 阶段 项目 实验 独立 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 科技互联网 互联网科技有限公司经营范围 互联网金融 金融科技 科技服务与互联网 有马互联网科技 长亮科技互联网金融 海南科技产业互联网平台 互联网科技产品 杭州壁岐互联网科技有限公司 天津腾达互联网科技有限公司 为客找房互联网科技公司怎么样 黑科技 体验场景 互联网大会 互联网第三次科技革命特征 郑州欣桑互联网科技有限公司 宁波跨境商互联网科技有限公司 同城互联网络科技 米泉互联网科技专业怎么样 广西泛欧互联网科技有限公司 平安好医生打造互联网科技医疗 高科技和互联网的现代国家 北京互联网科技 互联网金融 科技创新 深圳艾佳互联网科技 互联网科技股有哪些 深圳互联网科技怎么样 承德科技产业互联网发展 上海宁丞科技互联网 洛阳昕诺飞互联网科技有限公司 华夏恒生科技与互联网科技 恒生互联网科技趋势
0