我们有一个具有如下模块结构的项目:
- Project
- mod1
- mod1.cpp
- mod1.h
- main.cpp
- mod2
- mod2.cpp
- mod2.h
- main.cpp
- more modules
每个模块中的main.cpp文件实例化并测试该模块。一个模块可以包括并使用另一个模块。因此,例如,模块1可以包括模块2,最终包括其他模块。
我们想要创建一个makefile,它编译并包含正确的模块和main.cpp文件。因此,如果我写“make module2”,makefile将编译mod2.cpp,main.cpp(在模块2中)并包含mod2.h.如果我写“make module1”,makefile将编译mod2.cpp,mod1.cpp main.cpp(在模块1中)并包含mod1.h和mod2.h。
我对makefile的体验很谦虚,而且我已经用了几天没有成功。 使其成为通用的将是更可取的,因此添加新模块不需要对makefile进行重大更改。
我得到的最接近的解决方案是:
.PHONY: clean
FLAGS=-std=c++11 -g -I"$(SYSTEMC_PATH)/include" -I"$(SYSTEMC_PATH)" -L"$(SYSTEMC_PATH)/lib-linux64" -lsystemc $(addprefix -I, $(wildcard src/*))
SRCS_WITHOUT_MAIN= $(filter-out %main.cpp, $(shell find src -name '*.cpp'))
TARGET_OBJS=$(subst src, .build/obj, $(subst .cpp,.o,$(SRCS_WITHOUT_MAIN)))
all: $(filter-out unit_test, $(subst src/, ,$(wildcard src/*)))
.SECONDARY:
.build/obj/%.o: src/%.cpp
@mkdir -p $(shell dirname $@)
g++ $(FLAGS) $^ -c -o $@
clean:
@rm -r .build
%: $(TARGET_OBJS) .build/obj/%/main.o
@mkdir -p $(shell dirname .build/bin/$@)
g++ $(FLAGS) $^ -o .build/bin/$@
SRCS_WITHOUT_MAIN包含除主文件之外的所有模块的所有cpp文件。 TARGET_OBJS是对应文件的对应列表。 %目标匹配,例如编译所有cpp文件的“mod1”和mod1的main.cpp。
问题是,我们偶尔会在编译后运行时遇到段错误,我们需要做一个“make clean&& make”让它再次运行。有一天我用了4个小时来调试我的代码只是为了找出makefile是某种破坏。现在,项目中的每个人都一直使用“make clean&& make”,因为害怕和我一样经历... ...
有谁知道一个聪明的方法吗?顺便说一下:这是一个SystemC项目。
答案 0 :(得分:1)
这是一个粗略但有效的makefile,可以完成这项工作:
CC=g++
vpath %.h mod1 mod2
module1: mod1.o mod2.o main1.o
$(CC) $^ -o $@
module2: mod2.o main2.o
$(CC) $^ -o $@
mod1.o: mod1/mod1.cpp mod1.h mod2.h
mod2.o: mod2/mod2.cpp mod1.h mod2.h
main1.o: mod1/main.cpp mod1.h mod2.h
main2.o: mod2/main.cpp mod1.h mod2.h
%.o:
$(CC) -c $< -o $@
注意:
答案 1 :(得分:1)
使用GNU make可能无法使用模式规则来匹配彼此依赖的模块名称的策略。从GNU make 3.82手册(0.71版)
链中不会出现多个隐式规则。这个 意味着'make'甚至不会考虑这样一个荒谬的事情 通过运行链接器两次从'foo.o.o'制作'foo'。这个 约束具有防止任何无限循环的额外好处 搜索隐式规则链。
(我发现了对此here的一些讨论 - 在以后的版本中可能会有所不同。)
因此,如果module3依赖于module2,而module2依赖于module1,如果我们运行模式规则来构建module3,那么我们就不能使用相同的规则来构建module2。
可以使用静态模式规则来解决这个问题,其中指定了规则匹配的目标列表。我编辑了我使用这些的答案。
您可以根据您的问题调整以下内容。每个子目录都有许多“* .in”文件,当子目录构建时,会创建带有相应“* .out”文件的文件。模块本身相互依赖:在这种情况下,mod3取决于mod2,而mod2又取决于mod1。例如,更新文件“mod1 / file.in”,运行“make mod3”会导致重建mod1,mod2和mod3。
MODULES=mod1 mod2 mod3
mod1_DEPS =
mod2_DEPS = mod1
mod3_DEPS = mod2
TARGET_OBJS=$(subst .in,.out,$(shell find $@ -name '*.in'))
.PRECIOUS: %.out
%.out: %.in
touch $@
%.in:
.SECONDEXPANSION:
$(MODULES) : $$(TARGET_OBJS) $$($$@_DEPS)
@echo Building module $@
.SECONDEXPANSION
目标并使用$$
代替$
,可以访问$@
宏中的TARGET_OBJS
变量。
要以不同方式处理模块,无论它们是作为程序还是库构建,您可以有两个单独的规则,以不同的方式搜索它们的来源:
MODULES=mod1 mod2 mod3
mod1_DEPS =
mod2_DEPS = mod1_AS_MODULE
mod3_DEPS = mod2_AS_MODULE
TARGET_OBJS=$(subst .in,.out,$(shell find $* -name '*.in'))
MODULE_TARGET_OBJS= $(filter-out main.out $(subst .in,.out,$(shell find $* -name '*.in')))
# (elided code as above example)
$(addsuffix _AS_MODULE, $(MODULES)) : %_AS_MODULE: $$(MODULE_TARGET_OBJS) $$(%_DEPS)
@echo Building module $@
$(addsuffix _AS_PROGRAM, $(MODULES)) : %_AS_PROGRAM: $$(TARGET_OBJS) $$(%_DEPS)
@echo Building program $@
这将被调用,例如“make mod3_AS_PROGRAM”。