首页 文章

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

提问于
浏览
1

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

- 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是对应文件的对应列表 . %目标匹配,例如“mod1”编译所有cpp文件和mod1的main.cpp .

问题是,我们偶尔会在编译后运行时遇到段错误,我们需要做一个“make clean && make”让它再次运行 . 有一天我用了4个小时来调试我的代码只是为了找出makefile是某种破坏 . 现在项目中的每个人都在使用“make clean && make”,因为害怕和我一样经历...

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

2 回答

  • 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 $@
    

    注意:

    • 它将所有目标文件(例如mod1.o)放在父目录中,这是我认为makefile所在的位置 .

    • 它命名两个"main"目标文件"main1.o"和"main2.o" . 在同一个代码库中拥有两个具有相同名称的文件只会引发麻烦 .

    • 可以进一步改进;这是为了简单而设计的 .

  • 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” .

相关问题