如何编写必须生成两个可执行文件的 makefile
How to write a makefile which has to generate two executables
我正在学习如何使用 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),我将在其中托管一个 tests.cpp 代码测试代码。在这种情况下,我想获得两个可执行文件:main 和 tests(不仅是 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 文件中实现)。我试着查看 答案,但我没有解决这个问题。
你知道如何正确编译doctest test.cpp文件吗?谢谢。
编辑 3
感谢 Jarod42 的回答,我也解决了与 doctest 相关的最后一个问题。现在一切似乎都很完美。
非常感谢你们!这是我在 Stack Overflow 上的第一个 post,我真的很喜欢这个网站。
尝试过卑微的开始吗?这是我开始和构建的地方,关键是将 main.o 与 OBJ 分开,以便测试可以从 tests.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。
- 依赖文件生成和包含。
- 将文件夹等保存在变量中。
我正在学习如何使用 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),我将在其中托管一个 tests.cpp 代码测试代码。在这种情况下,我想获得两个可执行文件:main 和 tests(不仅是 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 文件中实现)。我试着查看
你知道如何正确编译doctest test.cpp文件吗?谢谢。
编辑 3
感谢 Jarod42 的回答,我也解决了与 doctest 相关的最后一个问题。现在一切似乎都很完美。
非常感谢你们!这是我在 Stack Overflow 上的第一个 post,我真的很喜欢这个网站。
尝试过卑微的开始吗?这是我开始和构建的地方,关键是将 main.o 与 OBJ 分开,以便测试可以从 tests.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。
- 依赖文件生成和包含。
- 将文件夹等保存在变量中。