尝试将每个 .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.oTestReactor.o 都依赖于 Reactor.cppTestReactor.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.oTestReactor.o 的方式,并解释了 Makefile 的错误行为。

您需要替换这个不正确的依赖声明:

$(CPP_OBJ): $(CPP_SRC)

您需要将 $(CPP_SRC) 替换为一个宏,该宏将单个依赖目标替换为相应的 .cpp 文件。将 $(CPP_SRC) 替换为 $@$(patsubst),替换扩展名,应该可以解决问题。