尝试将每个 .cpp 文件编译为相应的 .o 文件
Trying to compile each .cpp file to a corresponding .o file
我来求助 Whosebug 专家是为了更好地理解并可能更好地解决正在发生的事情。所以我通常非常擅长使用 Makefile,但是我的 Makefile 缺少一个关键部分。所以我通常有一个 Makefile 适合我的项目,而且效果很好。或者直到我发现我没有正确地 link 访问库。自从我发现我的 Makefile 不正确,我一直在想办法解决这个问题/有更好的解决方案。
我想做什么
假设我有一些 .cpp 文件(忽略糟糕的命名)
src
core
rector
Reactor.cpp
Reactor.h
reactorprj
TestReactor.cpp
我想将每个 .cpp 编译为相应的 .o 文件,以便我的构建目录如下所示
build_dir
src
core
rector
Reactor.o
reactorprj
TestReactor.o
在每个 .cpp 文件被编译成一个 .o 文件后,我想 link 所有的 .o 文件连同 link 将库一起编译成一个可执行文件.所以我可以做这个但不能。
这是我的
生成文件
include make_rules/config.mk
# Output of the program
OUTPUT := App.exe
# All CPP files to compile
CPP_SRC += $(SRC)/core/reactor/Reactor.cpp
CPP_SRC += $(SRC)/reactorprj/TestReactor.cpp
# Compile OBJ files (.o) to a build directory
CPP_OBJ := $(addprefix $(BLD_DIR), $(subst $(PRJ_DIR), ,$(CPP_SRC:.cpp=.o)))
# Directories on where to include header files
INC_DIR += $(SRC)
INC_DIR += /usr/local/lib/cppzmq
INC_DIR += /usr/local/lib/spdlog/include
# Dependency files
DEP := $(CPP_OBJ:.o=.d)
# The directory to look for the libraries
LIBS_DIR += /usr/lib
LIBS_DIR += /usr/local/lib/spdlog/build
# The library to link
LIBS += zmq
LIBS += pthread
LIBS += spdlog
.PHONY: all
all: $(OUTPUT)
$(OUTPUT): $(CPP_OBJ)
@$(ECHO)
@$(ECHO) Compiling all object files...
@$(ECHO) Linking....
$(VERBOSE)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INC) -o $@ $(CPP_OBJ) $(LIBSPATHS) $(LDLIBS)
$(CPP_OBJ): $(CPP_SRC)
$(VERBOSE)$(MKDIR) -p '$(@D)'
@$(ECHO) 'Compiling $<'
$(VERBOSE)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INC) -c $< -o $@
.PHONY: clean
clean:
$(RM) -rf $(BLD_DIR)
$(RM) -rf $(OUTPUT)
# Include the dependencies. Since the dependency files won't exist at first the -include supresses the warnings
-include $(DEP)
make_rules/config.mk
#############################
### General Config Rules
#############################
# Commands
ECHO := echo
MAKE := make
CAT := cat
RM := rm
MKDIR := mkdir
LS := ls
# Top Level Variables
PRJ_DIR := /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code
BLD_DIR := $(PRJ_DIR)/build_dir
SRC := $(PRJ_DIR)/src
# This is empty because we can add actual defines in the Makefile
DEFINES +=
# Compiler
CXX := g++
# Compiler flags
CXXFLAGS += -Wall -Wextra -Wsign-conversion -g -MMD -MP
# Preprocessor Flags
CPPFLAGS +=
CPPFLAGS += $(addprefix -D, $(DEFINES))
# Directories on where to include header files
INC_DIR +=
# Append -I to each directory
INC += $(INC_DIR:%=-I%)
# LIBS_DIR is used to specify the directories of the libraries
LIBS_DIR +=
# Take LIBS_DIR and add -L to each
LIBSPATHS += $(LIBS_DIR:%=-L%)
# The library to link
LIBS +=
# Add -l to each library
LDLIBS += $(LIBS:%=-l%)
这是我的结果
所以这是 make all 的结果
20:39:41 **** Build of configuration Default for project ExistingMakefile ****
make VERBOSE= all
mkdir -p '/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor'
Compiling /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp
g++ -Wall -Wextra -Wsign-conversion -g -MMD -MP -I/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src -I/usr/local/lib/cppzmq -I/usr/local/lib/spdlog/include -c /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp -o /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o
mkdir -p '/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj'
Compiling /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp
g++ -Wall -Wextra -Wsign-conversion -g -MMD -MP -I/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src -I/usr/local/lib/cppzmq -I/usr/local/lib/spdlog/include -c /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp -o /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o
Compiling all object files...
Linking....
g++ -Wall -Wextra -Wsign-conversion -g -MMD -MP -I/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src -I/usr/local/lib/cppzmq -I/usr/local/lib/spdlog/include -o App.exe /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o -L/usr/lib -L/usr/local/lib/spdlog/build -lzmq -lpthread -lspdlog
/usr/lib/gcc/x86_64-pc-cygwin/11/../../../../x86_64-pc-cygwin/bin/ld: /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o: in function `Reactor::Reactor(int)':
/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:12: multiple definition of `Reactor::Reactor(int)'; /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o:/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:12: first defined here
/usr/lib/gcc/x86_64-pc-cygwin/11/../../../../x86_64-pc-cygwin/bin/ld: /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o: in function `Reactor::Reactor(int)':
/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:12: multiple definition of `Reactor::Reactor(int)'; /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o:/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:12: first defined here
/usr/lib/gcc/x86_64-pc-cygwin/11/../../../../x86_64-pc-cygwin/bin/ld: /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o:/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:18: multiple definition of `main'; /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o:/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:18: first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:36: App.exe] Error 1
"make VERBOSE= all" terminated with exit code 2. Build might be incomplete.
所以我可以看出 Reactor.cpp 正在编译为 Reactor.o 和 TestReactor.o,这导致了重新定义的错误。那么为什么会这样。应该发生的是 Reactor.cpp 编译为 Reactor.o 而 TestReactor.cpp 编译为 TestReactor.o
更新
根据@Sam Varshavchik 的回答,我的新目标现在看起来像这样:
$(CPP_OBJ):
$(VERBOSE)$(MKDIR) -p '$(@D)'
$(eval CPP_FILE := $(addprefix $(PRJ_DIR), $(subst $(BLD_DIR), ,$(subst .o,.cpp,$@))))
@$(ECHO) 'Compiling $(CPP_FILE)'
$(VERBOSE)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INC) -c $(CPP_FILE) -o $@
因为我构建的目录与我的项目文件不同,所以我最终将目标的前缀从 .o 更改为 .cpp 然后我删除了构建目录的模式 ($(BLD_DIR) ) 然后添加我的源代码所在的前缀。然后我将它放在 $(CPP_FILE) 中以供稍后用于目标。最终工作完美,所以我接受了山姆的回答!我确信可能有更好的方法来解决这个问题,但现在我很满意。
您的 Makefile 定义:
CPP_SRC += $(SRC)/core/reactor/Reactor.cpp
CPP_SRC += $(SRC)/reactorprj/TestReactor.cpp
为了避免混淆,从现在开始,我将去掉所有目录名,只引用文件名。所以,这基本上是:
CPP_SRC = Reactor.cpp TestReactor.cpp
定义如下:
CPP_OBJ := $(addprefix $(BLD_DIR), $(subst $(PRJ_DIR), ,$(CPP_SRC:.cpp=.o)))
具有定义的作用:
CPP_OBJ = Reactor.o TestReactor.o
同样,忽略路径。所以现在,我们到达这里:
$(CPP_OBJ): $(CPP_SRC)
如果你拿出一张sheet纸,写出所有这些宏扩展到什么,这就定义了以下依赖关系:
Reactor.o TestReactor.o: Reactor.cpp TestReactor.cpp
这意味着 Reactor.o
和 TestReactor.o
都依赖于 Reactor.cpp
和 TestReactor.cpp
。这就是 Makefile
语法的意思。这并不意味着第一个依赖文件依赖于第一个依赖项,第二个依赖文件依赖于第二个依赖项。这意味着两个依赖文件都依赖于两个依赖项。这实际上等同于:
Reactor.o: Reactor.cpp TestReactor.cpp
TestReactor.o: Reactor.cpp TestReactor.cpp
最后,我们到了这里:
$(VERBOSE)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INC) -c $< -o $@
如果您查看 GNU make 的文档,您会看到 $<
扩展到 first 依赖项。
这就是从 Reactor.cpp
编译 Reactor.o
和 TestReactor.o
的方式,并解释了 Makefile
的错误行为。
您需要替换这个不正确的依赖声明:
$(CPP_OBJ): $(CPP_SRC)
您需要将 $(CPP_SRC)
替换为一个宏,该宏将单个依赖目标替换为相应的 .cpp
文件。将 $(CPP_SRC)
替换为 $@
的 $(patsubst)
,替换扩展名,应该可以解决问题。
我来求助 Whosebug 专家是为了更好地理解并可能更好地解决正在发生的事情。所以我通常非常擅长使用 Makefile,但是我的 Makefile 缺少一个关键部分。所以我通常有一个 Makefile 适合我的项目,而且效果很好。或者直到我发现我没有正确地 link 访问库。自从我发现我的 Makefile 不正确,我一直在想办法解决这个问题/有更好的解决方案。
我想做什么
假设我有一些 .cpp 文件(忽略糟糕的命名)
src
core
rector
Reactor.cpp
Reactor.h
reactorprj
TestReactor.cpp
我想将每个 .cpp 编译为相应的 .o 文件,以便我的构建目录如下所示
build_dir
src
core
rector
Reactor.o
reactorprj
TestReactor.o
在每个 .cpp 文件被编译成一个 .o 文件后,我想 link 所有的 .o 文件连同 link 将库一起编译成一个可执行文件.所以我可以做这个但不能。
这是我的
生成文件
include make_rules/config.mk
# Output of the program
OUTPUT := App.exe
# All CPP files to compile
CPP_SRC += $(SRC)/core/reactor/Reactor.cpp
CPP_SRC += $(SRC)/reactorprj/TestReactor.cpp
# Compile OBJ files (.o) to a build directory
CPP_OBJ := $(addprefix $(BLD_DIR), $(subst $(PRJ_DIR), ,$(CPP_SRC:.cpp=.o)))
# Directories on where to include header files
INC_DIR += $(SRC)
INC_DIR += /usr/local/lib/cppzmq
INC_DIR += /usr/local/lib/spdlog/include
# Dependency files
DEP := $(CPP_OBJ:.o=.d)
# The directory to look for the libraries
LIBS_DIR += /usr/lib
LIBS_DIR += /usr/local/lib/spdlog/build
# The library to link
LIBS += zmq
LIBS += pthread
LIBS += spdlog
.PHONY: all
all: $(OUTPUT)
$(OUTPUT): $(CPP_OBJ)
@$(ECHO)
@$(ECHO) Compiling all object files...
@$(ECHO) Linking....
$(VERBOSE)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INC) -o $@ $(CPP_OBJ) $(LIBSPATHS) $(LDLIBS)
$(CPP_OBJ): $(CPP_SRC)
$(VERBOSE)$(MKDIR) -p '$(@D)'
@$(ECHO) 'Compiling $<'
$(VERBOSE)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INC) -c $< -o $@
.PHONY: clean
clean:
$(RM) -rf $(BLD_DIR)
$(RM) -rf $(OUTPUT)
# Include the dependencies. Since the dependency files won't exist at first the -include supresses the warnings
-include $(DEP)
make_rules/config.mk
#############################
### General Config Rules
#############################
# Commands
ECHO := echo
MAKE := make
CAT := cat
RM := rm
MKDIR := mkdir
LS := ls
# Top Level Variables
PRJ_DIR := /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code
BLD_DIR := $(PRJ_DIR)/build_dir
SRC := $(PRJ_DIR)/src
# This is empty because we can add actual defines in the Makefile
DEFINES +=
# Compiler
CXX := g++
# Compiler flags
CXXFLAGS += -Wall -Wextra -Wsign-conversion -g -MMD -MP
# Preprocessor Flags
CPPFLAGS +=
CPPFLAGS += $(addprefix -D, $(DEFINES))
# Directories on where to include header files
INC_DIR +=
# Append -I to each directory
INC += $(INC_DIR:%=-I%)
# LIBS_DIR is used to specify the directories of the libraries
LIBS_DIR +=
# Take LIBS_DIR and add -L to each
LIBSPATHS += $(LIBS_DIR:%=-L%)
# The library to link
LIBS +=
# Add -l to each library
LDLIBS += $(LIBS:%=-l%)
这是我的结果
所以这是 make all 的结果
20:39:41 **** Build of configuration Default for project ExistingMakefile ****
make VERBOSE= all
mkdir -p '/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor'
Compiling /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp
g++ -Wall -Wextra -Wsign-conversion -g -MMD -MP -I/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src -I/usr/local/lib/cppzmq -I/usr/local/lib/spdlog/include -c /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp -o /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o
mkdir -p '/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj'
Compiling /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp
g++ -Wall -Wextra -Wsign-conversion -g -MMD -MP -I/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src -I/usr/local/lib/cppzmq -I/usr/local/lib/spdlog/include -c /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp -o /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o
Compiling all object files...
Linking....
g++ -Wall -Wextra -Wsign-conversion -g -MMD -MP -I/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src -I/usr/local/lib/cppzmq -I/usr/local/lib/spdlog/include -o App.exe /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o -L/usr/lib -L/usr/local/lib/spdlog/build -lzmq -lpthread -lspdlog
/usr/lib/gcc/x86_64-pc-cygwin/11/../../../../x86_64-pc-cygwin/bin/ld: /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o: in function `Reactor::Reactor(int)':
/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:12: multiple definition of `Reactor::Reactor(int)'; /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o:/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:12: first defined here
/usr/lib/gcc/x86_64-pc-cygwin/11/../../../../x86_64-pc-cygwin/bin/ld: /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o: in function `Reactor::Reactor(int)':
/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:12: multiple definition of `Reactor::Reactor(int)'; /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o:/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:12: first defined here
/usr/lib/gcc/x86_64-pc-cygwin/11/../../../../x86_64-pc-cygwin/bin/ld: /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/reactorprj/TestReactor.o:/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:18: multiple definition of `main'; /cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/build_dir/src/core/reactor/Reactor.o:/cygdrive/c/Users/kmgag/Documents/eclipse-projects/existing-cpp-code/src/core/reactor/Reactor.cpp:18: first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:36: App.exe] Error 1
"make VERBOSE= all" terminated with exit code 2. Build might be incomplete.
所以我可以看出 Reactor.cpp 正在编译为 Reactor.o 和 TestReactor.o,这导致了重新定义的错误。那么为什么会这样。应该发生的是 Reactor.cpp 编译为 Reactor.o 而 TestReactor.cpp 编译为 TestReactor.o
更新
根据@Sam Varshavchik 的回答,我的新目标现在看起来像这样:
$(CPP_OBJ):
$(VERBOSE)$(MKDIR) -p '$(@D)'
$(eval CPP_FILE := $(addprefix $(PRJ_DIR), $(subst $(BLD_DIR), ,$(subst .o,.cpp,$@))))
@$(ECHO) 'Compiling $(CPP_FILE)'
$(VERBOSE)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INC) -c $(CPP_FILE) -o $@
因为我构建的目录与我的项目文件不同,所以我最终将目标的前缀从 .o 更改为 .cpp 然后我删除了构建目录的模式 ($(BLD_DIR) ) 然后添加我的源代码所在的前缀。然后我将它放在 $(CPP_FILE) 中以供稍后用于目标。最终工作完美,所以我接受了山姆的回答!我确信可能有更好的方法来解决这个问题,但现在我很满意。
您的 Makefile 定义:
CPP_SRC += $(SRC)/core/reactor/Reactor.cpp
CPP_SRC += $(SRC)/reactorprj/TestReactor.cpp
为了避免混淆,从现在开始,我将去掉所有目录名,只引用文件名。所以,这基本上是:
CPP_SRC = Reactor.cpp TestReactor.cpp
定义如下:
CPP_OBJ := $(addprefix $(BLD_DIR), $(subst $(PRJ_DIR), ,$(CPP_SRC:.cpp=.o)))
具有定义的作用:
CPP_OBJ = Reactor.o TestReactor.o
同样,忽略路径。所以现在,我们到达这里:
$(CPP_OBJ): $(CPP_SRC)
如果你拿出一张sheet纸,写出所有这些宏扩展到什么,这就定义了以下依赖关系:
Reactor.o TestReactor.o: Reactor.cpp TestReactor.cpp
这意味着 Reactor.o
和 TestReactor.o
都依赖于 Reactor.cpp
和 TestReactor.cpp
。这就是 Makefile
语法的意思。这并不意味着第一个依赖文件依赖于第一个依赖项,第二个依赖文件依赖于第二个依赖项。这意味着两个依赖文件都依赖于两个依赖项。这实际上等同于:
Reactor.o: Reactor.cpp TestReactor.cpp
TestReactor.o: Reactor.cpp TestReactor.cpp
最后,我们到了这里:
$(VERBOSE)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INC) -c $< -o $@
如果您查看 GNU make 的文档,您会看到 $<
扩展到 first 依赖项。
这就是从 Reactor.cpp
编译 Reactor.o
和 TestReactor.o
的方式,并解释了 Makefile
的错误行为。
您需要替换这个不正确的依赖声明:
$(CPP_OBJ): $(CPP_SRC)
您需要将 $(CPP_SRC)
替换为一个宏,该宏将单个依赖目标替换为相应的 .cpp
文件。将 $(CPP_SRC)
替换为 $@
的 $(patsubst)
,替换扩展名,应该可以解决问题。