Makefile用于编译可以包含彼此的不同“模块”

时间:2013-10-06 16:48:44

标签: c++ makefile

我们有一个具有如下模块结构的项目:

- 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项目。

2 个答案:

答案 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. 它将所有目标文件(例如mod1.o)放在父目录中,这是我认为makefile所在的位置。
  2. 它命名两个“主”对象文件“main1.o”和“main2.o”。在同一个代码库中拥有两个具有相同名称的文件只会引发麻烦。
  3. 可以进一步改进;这是为了简单而设计的。

答案 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”。