简体   繁体   English

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

[英]Compiling multiple test files efficiently with Makefile

I have a C++ project with the following structure: 我有一个具有以下结构的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)
     ...

For the tests inside my test file, I would like to be able to 对于我的测试文件中的测试,我希望能够

  • Compile them individually, eg "make test1", "make test2" 分别编译它们,例如“ make test1”,“ make test2”

  • Compile them all at once 一次全部编译

But I would like to be able to do this without needing to define new variables (eg TARGET1, TARGET2,...) for each new test file, nor add a bunch of new lines to my makefile for each new test file. 但是我希望能够做到这一点, 而无需为每个新测试文件定义新变量(例如TARGET1,TARGET2等),也不必为每个新测试文件在我的makefile中添加一堆新行。

For example, right now I have something like: 例如,现在我有类似的东西:

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

which does the job, but isn't very scalable. 可以完成工作,但扩展性不是很好。 How could I do this scalably? 我该怎么做呢?

Make has some more tricks that you can use (not tested): 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 $@ $<

The main trick here is the static pattern rule for $(TARGETS) : in the recipe $* expands as the stem of the pattern. 这里的主要技巧是$(TARGETS)静态模式规则 :在配方中, $*随着模式的茎而扩展。 The other tricks are a simpler use of patsubst and the use of wildcard instead of the less efficient shell find . 其他技巧是更简单地使用patsubst和使用wildcard而不是效率较低的shell find Note that this last one works only if your source files are flat in src , not if they are organized in a hierarchy of sub-directories. 请注意,仅当源文件在src是平坦的时,这最后一个才有效,而在它们按子目录的层次结构组织时,则无效。

But this does not answer your most tricky request: a way to invoke make testX instead of make bin/testX . 但这并不能解决您最棘手的请求:一种调用make testX而不是make bin/testX So, here is the most tricky part: 因此,这是最棘手的部分:

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))))

You can even use this foreach-eval-call to factorize other parts of your Makefile: 您甚至可以使用此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))))

The most difficult to understand in this last version is the need of $$ in the recipe (double expansion). 在最后一个版本中最难理解的是配方中需要$$ (双扩展)。 But here the GNU make manual is your friend. 但是在这里, GNU make手册是您的朋友。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM