【问题标题】:Compiling multiple test files efficiently with Makefile使用 Makefile 高效编译多个测试文件
【发布时间】:2017-11-08 15:36:26
【问题描述】:

我有一个具有以下结构的 C++ 项目:

/Project
    Makefile
    /src (.cpp source files)
     ...
    /include (.h header files)
     ...    
    /libs 
     ...
    /build (.o object files)
     ...    
    /tests (target .cpp files I want to compile)
     test1.cpp
     test2.cpp
     test3.cpp
     ...    
    /bin (output directory for compiled files)
     ...

对于我的测试文件中的测试,我希望能够

  • 单独编译它们,例如“制作 test1”、“制作 test2”

  • 一次全部编译

但我希望能够做到这一点不需要为每个新的测试文件定义新变量(例如 TARGET1、TARGET2...),也不需要添加一堆新行每个新测试文件的生成文件。

例如,现在我有类似的东西:

CXX = g++
SRC_DIR = ./src
BUILD_DIR = ./build
LIB = -I libs
INC = -I include 

SRCS = $(shell find $(SRC_DIR) -type f -name *.cpp)
OBJS = $(patsubst $(SRC_DIR)/%, $(BUILD_DIR)/%, $(SRCS:.cpp=.o))


TARGET1 ?= test1.cpp
TARGET2 ?= test2.cpp
TARGET3 ?= test3.cpp

all: $(OBJS)
        $(CXX) ./tests/$(TARGET1).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET1)
        $(CXX) ./tests/$(TARGET2).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET2)
        $(CXX) ./tests/$(TARGET3).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET3)

$(TARGET1): $(OBJS)
        $(CXX) ./tests/$(TARGET1).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET1)

$(TARGET2): $(OBJS)
        $(CXX) ./tests/$(TARGET2).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET2)

$(TARGET3): $(OBJS)
        $(CXX) ./tests/$(TARGET3).cpp $(LIB) $(INC) $^ -o ./bin/$(TARGET3)

$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
        $(CXX) $(INC) -c -o $@ $<

它可以完成这项工作,但不是很可扩展。我怎样才能以可扩展的方式做到这一点?

【问题讨论】:

  • 你可以使用define/endef定义一个多行宏,然后eval()它。结合 patsubst,使用这种方法,您可以自动为每个目标自动生成所有规则。当我做了类似的事情时,我花了一些时间来浏览 GNU make 文档,所以我无法完全回忆起我脑海中的所有细节,但这绝对是可行的。只需花时间阅读和重新阅读 GNU make 的信息,直到您完全了解它。

标签: c++ makefile


【解决方案1】:

Make 还有一些你可以使用的技巧(未经测试):

CXX = g++
SRC_DIR = src
BUILD_DIR = build
TEST_DIR = tests
BIN_DIR = bin
LIB = -I libs
INC = -I include

SRCS = $(wildcard $(SRC_DIR)/*.cpp)
OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))

TESTS = $(wildcard $(TEST_DIR)/*.cpp)
TARGETS = $(patsubst $(TEST_DIR)/%.cpp,$(BIN_DIR)/%,$(TESTS))

all: $(TARGETS)

$(TARGETS): $(BIN_DIR)/%: $(OBJS)
    $(CXX) $(TEST_DIR)/$*.cpp $(LIB) $(INC) $^ -o $@

$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
    $(CXX) $(INC) -c -o $@ $<

这里的主要技巧是static pattern rule 用于$(TARGETS):在配方中$* 扩展为模式的主干。其他技巧是更简单地使用patsubst 和使用wildcard 而不是效率较低的shell find。请注意,仅当您的源文件位于 src 中时,最后一个才有效,而不是当它们以子目录的层次结构组织时。

但这并不能满足您最棘手的要求:一种调用make testX 而不是make bin/testX 的方法。所以,这里是最棘手的部分:

SHORTERTARGETS = $(patsubst $(TEST_DIR)/%.cpp,%,$(TESTS))

.PHONY: $(SHORTERTARGETS)

# $(1): short target
define TARGETS_rule
$(1): $(BIN_DIR)/$(1)
endef
$(foreach t,$(SHORTERTARGETS),$(eval $(call TARGETS_rule,$(t))))

您甚至可以使用 foreach-eval-call 来分解您的 Makefile 的其他部分:

CXX = g++
SRC_DIR = src
BUILD_DIR = build
TEST_DIR = tests
BIN_DIR = bin
LIB = -I libs
INC = -I include

SRCS = $(wildcard $(SRC_DIR)/*.cpp)
OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))

TESTS = $(wildcard $(TEST_DIR)/*.cpp)
TARGETS = $(patsubst $(TEST_DIR)/%.cpp,$(BIN_DIR)/%,$(TESTS))
SHORTERTARGETS = $(patsubst $(TEST_DIR)/%.cpp,%,$(TESTS))

.PHONY: all $(SHORTERTARGETS)

all: $(TARGETS)

$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
    $(CXX) $(INC) -c -o $@ $<

# $(1): short target
define TARGETS_rule
$(1): $(BIN_DIR)/$(1)
$(BIN_DIR)/$(1): $(OBJS)
    $(CXX) $(TEST_DIR)/$(1).cpp $(LIB) $(INC) $$^ -o $$@
endef
$(foreach t,$(SHORTERTARGETS),$(eval $(call TARGETS_rule,$(t))))

在最后一个版本中最难理解的是配方中需要$$(双重扩展)。但这里GNU make manual 是你的朋友。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-16
    • 1970-01-01
    • 1970-01-01
    • 2015-01-25
    • 1970-01-01
    • 2013-03-10
    • 1970-01-01
    相关资源
    最近更新 更多