如何编写一个生成两个可执行文件的makefile



我正在学习如何使用make。最近,我编写了一个makefile来编译我的一个项目,其结构为:src(包含file.cppmain.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/srcobj/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
  • 依赖关系文件的生成和包含
  • 将文件夹等保存在变量中

相关内容

  • 没有找到相关文章

最新更新