【发布时间】:2021-11-09 23:45:19
【问题描述】:
我正在学习如何使用 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 和 tests(不仅像以前那样 main),一个用于 main,一个用于测试。我尝试了一切,但我不明白如何修改以前的 makefile 以实现新的 test 文件夹信息。
编辑 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和tests,但是它们做的事情是一样的(和之前的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 可执行文件已正确编译并且可以正常工作。还编译了 tests 可执行文件(我认为是正确的),但我得到另一个错误,与 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 文件中实现的)。我尝试查看this的答案,但我没有解决这个问题。
你知道如何正确编译doctest test.cpp文件吗?谢谢。
编辑 3
感谢 Jarod42 的回答,我也解决了与 doctest 相关的最后一个问题。现在一切似乎都运行良好。
非常感谢你们!这是我在 Stack Overflow 上的第一篇文章,我真的很喜欢这个网站。
【问题讨论】:
-
您打算构建
src/obj/file.o还是obj/file.o?也就是说,您希望obj/在src/中还是在src/的父目录中? -
Edit 1 的问题是在新配方中使用 $(OBJ) 而不是自动变量 $^。此外,OBJ 仍然包含 main.o。这就像我的回答中最重要的一件事;-)
-
嗨@Andreas 我添加了另一个编辑,我可能在其中解决了这个问题。但是出现了另一个问题,并且与正确的 test.cpp (with doctest) 编译有关,以防 test.cpp 没有 main 功能。你知道怎么解决吗?谢谢。
-
我要补充一点,你在这个问题上的挣扎是正常的。随着目标数量的增加,使用
make管理项目可能非常困难(但仍然很常见)。这就是为什么存在诸如cmake和autoconf之类的工具来帮助为更复杂的项目生成makefile 的原因。如果您的项目变得更复杂,请深思。 -
你可能想要那个
#defineDOCTEST_CONFIG_IMPLEMENT_WITH_MAIN。