21.打造专业的编译环境_链接
21.1.第二阶段任务
-
完成整个工程的makefile文件;
-
调用makefile编译生成静态库文件;
-
链接所有模块的静态库文件,得到最终的可执行程序。
21.2.关键的实现要点
-
如何自动创建build文件夹及其子文件夹?
-
如何进入每一个模块文件夹进行编译?
-
编译成功后如何链接所有模块静态库?
21.3.开发中的经验假设
项目中的各个模块在设计阶段就已经基本确定,因此,在之后的开发过程中模块不会频繁的增加或减少。
21.4.解决方案设计
-
1.定义变量保存模块名列表(模块名变量)
-
2.利用shell中的for循环遍历模块名变量
-
3.在for循环中进入模块文件夹进行编译
-
4.循环结束后连接所有的模块静态库文件
21.4.1.Makefile中嵌入shell的for循环
21.4.2. 注意事项
Makefile中嵌入shell代码时,如果需要使用shell变量的值,必须在变量前面加$$,譬如$$dir,用于区分该变量是shell变量而非Makefile中定义的变量。
21.4.3. shell中for循环实验
编程实验:
compile : $(DIR_BUILD) $(DIR_BUILD_SUB) @echo "Begin to compile ..." @set -e; for dir in $(MODULES); do cd $$dir && $(MAKE) all DEBUG:=$(DEBUG) && cd .. ; done @echo "Compile Success!"
运行结果:
工程makefile中的关键构成:编译 + 链接
编译makefile:
.PHONY : all compile MODULES := common module main MKDIR := mkdir RM := rm -fr DIR_PROJECT := $(realpath .) DIR_BUILD := build DIR_BUILD_SUB := $(addprefix $(DIR_BUILD)/, $(MODULES)) MODULE_LIB := $(addsuffix .a, $(MODULES)) compile : $(DIR_BUILD) $(DIR_BUILD_SUB) @echo "Begin to compile ..." @set -e; for dir in $(MODULES); do cd $$dir && $(MAKE) all DEBUG:=$(DEBUG) && cd .. ; done @echo "Compile Success!" $(DIR_BUILD) $(DIR_BUILD_SUB) : $(MKDIR) $@
输出结果:
编译成功,在build文件夹下生成了各个模块的打包文件。
21.5. 链接时的注意事项
(1)gcc 在进行静态库连接时,必须遵守严格的依赖关系
- gcc -o app.out x.a y.a
其中的依赖关必须为:x.a -> y.a , y.a -> z.a。默认情况下遵循自左向右的依赖关系。
(2)如果不清楚库间的依赖,可以使用-Xlinker自动确定依赖关系
gcc -o app.out -Xlinker “-(”z.a y.a x.a -Xlinker”-)”
link $(APP) : $(MODULE_LIB)
@echo "Begin to link ..."
$(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)
@echo "Link Success!"
21.6. 最终方案
.PHONY : all compile link clean rebuild MODULES := common module main MKDIR := mkdir RM := rm -fr CC := gcc LFLAGS := DIR_PROJECT := $(realpath .) DIR_BUILD := build DIR_BUILD_SUB := $(addprefix $(DIR_BUILD)/, $(MODULES)) MODULE_LIB := $(addsuffix .a, $(MODULES)) MODULE_LIB := $(addprefix $(DIR_BUILD)/, $(MODULE_LIB)) APP := app.out APP := $(addprefix $(DIR_BUILD)/, $(APP)) all : compile $(APP) @echo "Success! Target ==> $(APP)" compile : $(DIR_BUILD) $(DIR_BUILD_SUB) @echo "Begin to compile ..." @set -e; for dir in $(MODULES); do cd $$dir && $(MAKE) all DEBUG:=$(DEBUG) && cd .. ; done @echo "Compile Success!" link $(APP) : $(MODULE_LIB) @echo "Begin to link ..." $(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS) @echo "Link Success!" $(DIR_BUILD) $(DIR_BUILD_SUB) : $(MKDIR) $@ clean : @echo "Begin to clean ..." $(RM) $(DIR_BUILD) @echo "Clean Success!" rebuild : clean all
运行结果:
思考:
当前整个项目的makefile是否存在潜在的问题?是否需要重构?