1

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

- Project
  - mod1
    - mod1.cpp
    - mod1.h
    - main.cpp
  - mod2
    - mod2.cpp
    - mod2.h
    - main.cpp
  - more modules

每个模块中的 main.cpp 文件实例化并测试该模块。一个模块可以包含和使用另一个模块。因此,例如,模块 1 可以包括模块 2 并最终包括其他模块。

我们想要创建一个编译文件并包含正确的模块和 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 是相应的目标文件列表。% 目标匹配,例如“mod1”,它编译所有 cpp 文件和 mod1 的 main.cpp。

问题是,有时我们在编译后运行时会出现段错误,我们需要执行“make clean && make”才能让它再次工作。有一天,我花了 4 个小时调试我的代码,只是为了发现 makefile 出现了某种损坏。现在项目中的每个人都一直在使用“make clean && make”,因为害怕遇到和我一样的情况……

有谁知道一个聪明的方法来做到这一点?顺便说一句:这是一个 SystemC 项目。

4

2 回答 2

1

这是一个粗略但有效的生成文件,可以完成这项工作:

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. 进一步的改进是可能的;这是为了简单而设计的。
于 2013-10-06T20:02:19.490 回答
1

使用模式规则来匹配可以相互依赖的模块名称的策略可能无法使用 GNU make。来自 GNU make 3.82 手册(0.71 版)

一条隐含规则在一条链中不能出现多次。这意味着'make' 甚至不会考虑通过运行链接器两次从'foo.oo' 生成'foo' 这样荒谬的事情。此约束具有额外的好处,即防止在搜索隐式规则链时出现任何无限循环。

(我在这里找到了一些讨论- 在以后的版本中可能会有所不同。)

因此,如果 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”。

于 2013-10-07T00:17:18.327 回答