我正在学习如何使用make。最近,我编写了一个makefile来编译我的一个项目,其结构为:src(包含file.cpp和main.cpp(和include文件夹(包含file.h(以及makefile,编写如下:
TARGET_EXEC := main
CC := g++
BUILD_DIR := .
SRC_DIR := src
OBJ_DIR := obj
SRC := $(shell find $(SRC_DIR) -name '*.cpp')
OBJ := $(SRC:%=$(OBJ_DIR)/%.o)
DEPS := $(OBJ:.o=.d)
INC_DIR := $(shell find $(SRC_DIR) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIR))
CPPFLAGS := $(INC_FLAGS) -MMD -MP
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJ)
$(CC) $(OBJ) -o $@ $(LDFLAGS)
$(OBJ_DIR)/%.cpp.o: %.cpp
@ mkdir -p $(dir $@)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -r $(OBJ_DIR) main
-include $(DEPS)
现在我想添加一个新文件夹(test(,在其中我将托管一个带有doctest代码的tests.cpp代码。在这种情况下,我想获得两个可执行文件:main和测试(不仅仅是以前的main(,一个用于main,一个用来测试。我尝试了一切,但我不明白如何修改以前的makefile,以实现新的测试文件夹信息。
编辑1
我尝试添加Andreas建议和all命令来创建两个可执行文件:
TARGET_EXEC := main
TEST_EXEC := tests
CC := g++
BUILD_DIR := .
SRC_DIR := src
OBJ_DIR := obj
TEST_DIR := test
SRC := $(shell find $(SRC_DIR) -name '*.cpp')
OBJ := $(SRC:%=$(OBJ_DIR)/%.o)
DEPS := $(OBJ:.o=.d)
INC_DIR := $(shell find $(SRC_DIR) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIR))
CPPFLAGS := $(INC_FLAGS) -MMD -MP
all: $(BUILD_DIR)/$(TARGET_EXEC) $(BUILD_DIR)/$(TEST_EXEC)
$(BUILD_DIR)/$(TARGET_EXEC): $(SRC_DIR)/$(TARGET_EXEC).o $(OBJ)
$(CC) $(OBJ) -o $@ $(LDFLAGS)
$(BUILD_DIR)/$(TEST_EXEC): $(TEST_DIR)/$(TEST_EXEC).o $(OBJ)
$(CC) $(OBJ) -o $@ $(LDFLAGS)
$(OBJ_DIR)/%.cpp.o: %.cpp
@ mkdir -p $(dir $@)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
.PHONY: clean all
clean:
rm -r $(OBJ_DIR) main
-include $(DEPS)
现在创建了两个可执行文件main和测试,但它们做的事情相同(与上一个main相同(,所以这是错误的。编译时,我的输出是:
g++ -Isrc -MMD -MP -c src/osmanip.cpp -o obj/src/osmanip.cpp.o
g++ -Isrc -MMD -MP -c src/main.cpp -o obj/src/main.cpp.o
g++ obj/src/osmanip.cpp.o obj/src/main.cpp.o -o main
g++ obj/src/osmanip.cpp.o obj/src/main.cpp.o -o tests
我认为错误可能在:
$(OBJ_DIR)/%.cpp.o: %.cpp
@ mkdir -p $(dir $@)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
你觉得怎么样?
编辑2
好吧,我想我找到了一个解决方案:
TARGET_EXEC := main
TEST_EXEC := tests
CC := g++
BUILD_DIR := .
SRC_DIR := src
OBJ_DIR := obj
TEST_DIR := test
SRC := $(shell find $(SRC_DIR) -name '*.cpp')
TEST := $(shell find $(TEST_DIR) -name '*.cpp')
OBJ := $(SRC:%=$(OBJ_DIR)/%.o)
TEST_OBJ := $(TEST:%=$(OBJ_DIR)/%.o)
DEPS := $(OBJ:.o=.d)
INC_DIR := $(shell find $(SRC_DIR) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIR))
CPPFLAGS := $(INC_FLAGS) -MMD -MP
.PHONY: clean all
all: $(BUILD_DIR)/$(TARGET_EXEC) $(BUILD_DIR)/$(TEST_EXEC)
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJ)
$(CC) $(OBJ) -o $@ $(LDFLAGS)
$(BUILD_DIR)/$(TEST_EXEC): $(TEST_OBJ)
$(CC) $(TEST_OBJ) -o $@ $(LDFLAGS)
$(OBJ_DIR)/%.cpp.o: %.cpp
@ mkdir -p $(dir $@)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
clean:
rm -r $(OBJ_DIR) main tests
-include $(DEPS)
有了这个makefile,编译效果很好(我认为(,因为创建了obj/src和obj/test文件夹main可执行文件已正确编译并正常工作。此外,编译了测试可执行文件(我认为是正确的(,但我得到了另一个与makefile无关的错误:
g++ -Isrc -MMD -MP -c src/osmanip.cpp -o obj/src/osmanip.cpp.o
g++ -Isrc -MMD -MP -c src/main.cpp -o obj/src/main.cpp.o
g++ obj/src/osmanip.cpp.o obj/src/main.cpp.o -o main
g++ -Isrc -MMD -MP -c test/tests.cpp -o obj/test/tests.cpp.o
g++ obj/test/tests.cpp.o -o tests
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x24): undefined reference to `main'
collect2: error: ld returned 1 exit status
make: *** [makefile:29: tests] Error 1
我认为这是因为我的test/tests.cpp文件是
#define DOCTEST_CONFIG_IMPLEMENT
#include "doctest/doctest.h"
#include <string>
我暂时没有给它任何命令。我在没有main的情况下实现了doctest,事实上它告诉我main函数丢失了(因为它是在src/main.cpp文件中实现的(。我试着看这个答案,但我没有解决这个问题。
你知道如何正确编译doctesttest.cpp文件吗?谢谢
编辑3
感谢Jarod42的回答,我还解决了最后一个与doctest有关的问题。现在一切似乎都很顺利。
非常感谢你们!这是我在Stack Overflow上的第一篇帖子,我已经非常喜欢这个网站了。
是否尝试过卑微的出身?这是我开始和构建的地方,关键是将main.o与OBJ分离,这样测试就可以从测试中获得它的main。o:
# Adding include directories (weird way of doing it but ok)
CPPFLAGS := $(addprefix -I,$(shell find src -type d))
# object files
OBJ := src/file1.o src/file2.o
# main
main: src/main.o $(OBJ)
# the test program
tests: test/tests.o $(OBJ)
(请注意,这依赖于内置的食谱,应该开箱即用。(
从那里你可以一步一步地重新介绍其他概念:
- 树外构建。
- 被高估了(至少对于GNU Make来说,它是手动的(
- Glob源文件。
- 如果你知道你有什么文件,最好不要glob
- 依赖关系文件的生成和包含
- 将文件夹等保存在变量中