使用Makefile高效编译多个测试文件

时间:2017-11-08 02:24:49

标签: c++ makefile

我有一个具有以下结构的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)
     ...

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

  • 单独编译,例如“make test1”,“make test2”

  • 一次编译所有内容

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

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

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

完成这项工作,但不是很可扩展。我怎么能这样做呢?

1 个答案:

答案 0 :(得分: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 $@ $<

这里的主要技巧是$(TARGETS)的{​​{3}}:在食谱中$*扩展为模式的主干。其他技巧更简单地使用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))))

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