[英]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.