我有一个具有以下结构的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 $@ $<
完成这项工作,但不是很可扩展。我怎么能这样做呢?
答案 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是你的朋友。