GNU 在看到其他同名文件时重用源文件

GNU Make re-using source file when sees other files with same name

本文关键字:文件 源文件 其他 GNU      更新时间:2023-10-16

我正在尝试创建一个顶级递归makefile,想法是一旦创建了makefile,在项目中添加或删除文件时几乎不需要维护。

我遇到的问题是,当存在同名但位于不同目录中的文件时,GNU make 将使用第一个目录中的.cpp文件贯穿构建的其余部分,当链接器运行时,它会抱怨函数的多个定义,因为生成的 .o 文件都基于同一个源文件。

我在下面引用的代码不是我实际项目的代码,但它表现出与我在尝试构建主项目时遇到的相同问题。这是我的顶级目录的布局,源代码驻留在子目录中。

otter@ubuntu:~/work/temp/maketest$ tree
.
├── exec
│   └── main.cpp
├── hello
│   ├── hello.cpp
│   └── hello.h
├── makefile
└── world
├── hello.cpp
└── hello.h
这个例子非常简单 - hello/hello.cpp打印">

Hello"和world/hello.cpp打印"World!!"。 main.cpp 调用每个函数来打印"HelloWorld!!

这是每个文件。

执行/主要.cpp

#include <iostream>
#include "../hello/hello.h"
#include "../world/hello.h"
int main()
{
print_hello();
print_world();
return 0;
}

你好/你好.cpp

#include "hello.h"
void print_hello()
{
std::cout << "Hello";
}

世界/你好.cpp

#include "hello.h"
void print_world()
{
std::cout << "World!!n";
}

这是我的制作文件

#Specify modules to include in the build - corresponds to source code locations. 
MODULES := exec 
world 
hello
CXX := g++
RM  := rm -rf 
#Create a list of the source directories
SRC_DIR     := $(addprefix ./,$(MODULES))
#Create a variable for the build output directory
BUILD_DIR   := $(addprefix ./build/,$(MODULES))
#C++ Compiler flags
CPPFLAGS    := -std=c++0x -O0 -g3 -Wall -fmessage-length=0 -c
#Flags for generating dependency files. 
DEPFLAGS    := -MMD -MP -MT "$$@" -MF "$$(@:%.o=%.d)"
#Creates a list of all the source files that we wish to include in the build
CPP_SRCS    := $(foreach sdir, $(SRC_DIR), $(wildcard $(sdir)/*.cpp))
#Creates a list of all the object files that we need to build based off source files
OBJ_TARGET  := $(foreach sdir, $(MODULES), $(wildcard $(sdir)/*.cpp))
OBJS        := $(patsubst %.cpp, ./build/%.o, $(OBJ_TARGET))
#Specify directories to search 
vpath %.cpp $(SRC_DIR) $(BUILD_DIR)
#"function" that contains the rule to make the .o files that exist within a source director and sub-directory
define make-goal
$1/%.o: %.cpp
@echo 'Building file: $$<'
@echo 'Invoking: Linux G++ Compiler'
$(CXX) $(CPPFLAGS) "$$<" -o "$$@" $(DEPFLAGS)
@echo 'Finished building: $$<'
@echo ' '
endef
.PHONY: all checkdirs clean build/HelloWorld
all: checkdirs build/HelloWorld
build/HelloWorld: $(OBJS)
@echo 'Building Target: $@'
@echo 'Invoking: G++ Linker'
$(CXX) -L/usr/local/lib $^ -o $@
@echo 'Finished building target: $@'
@echo ' '
clean:
-$(RM) $(BUILD_DIR)
#Makes sure that the output directory exists
checkdirs: $(BUILD_DIR)
#Creates the output directory, build, if it doesn't exist
$(BUILD_DIR):
@mkdir -p $@
#This is the important "recursive" part - this will loop through all source directories and call 'make-goal', which contains the rule to make the associated .o file.
$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir))))

当我尝试构建这个小项目时,我得到以下结果。注意 ./world/hello.cpp 在构建 .o 文件时使用两次 - 第一次输出到 build/world/hello.o,第二次输出到/build/hello/hello.o,然后链接器失败,因为 .o 文件相同。

Building file: ./exec/main.cpp
Invoking: Linux G++ Compiler
g++ -std=c++0x -O0 -g3 -Wall -fmessage-length=0 -c "./exec/main.cpp" -o "build/exec/main.o" -MMD -MP -MT "build/exec/main.o" -MF "build/exec/main.d"
Finished building: ./exec/main.cpp
Building file: ./world/hello.cpp
Invoking: Linux G++ Compiler
g++ -std=c++0x -O0 -g3 -Wall -fmessage-length=0 -c "./world/hello.cpp" -o "build/world/hello.o" -MMD -MP -MT "build/world/hello.o" -MF "build/world/hello.d"
Finished building: ./world/hello.cpp
Building file: ./world/hello.cpp
Invoking: Linux G++ Compiler
g++ -std=c++0x -O0 -g3 -Wall -fmessage-length=0 -c "./world/hello.cpp" -o "build/hello/hello.o" -MMD -MP -MT "build/hello/hello.o" -MF "build/hello/hello.d"
Finished building: ./world/hello.cpp
Building Target: build/HelloWorld
Invoking: G++ Linker
CPP_SRCS:  ./exec/main.cpp  ./world/hello.cpp  ./hello/hello.cpp
g++ -L/usr/local/lib build/exec/main.o build/world/hello.o build/hello/hello.o -o build/HelloWorld
build/hello/hello.o: In function `print_world()':
/home/otter/work/temp/maketest/./world/hello.cpp:4: multiple definition of `print_world()'
build/world/hello.o:/home/otter/work/temp/maketest/./world/hello.cpp:4: first defined here
build/exec/main.o: In function `main':
/home/otter/work/temp/maketest/./exec/main.cpp:7: undefined reference to `print_hello()'
collect2: error: ld returned 1 exit status
makefile:46: recipe for target 'build/HelloWorld' failed
make: *** [build/HelloWorld] Error 1

那么有谁知道为什么在构建.o文件时使用两次./world/hello.cpp?即使.cpp文件具有相同的名称,它们位于不同的目录中,我认为make足够聪明,可以将它们视为不同的文件,而不是每次看到具有相同名称的后续文件时都重复使用第一个文件。

我不确定为什么你的方法不起作用——如果我有时间,我会稍后尝试解决这个问题——但有一种更简单的方法:

build/HelloWorld: $(OBJS)
@echo 'Building Target: $@'
@echo 'Invoking: G++ Linker'
$(CXX) -L/usr/local/lib $^ -o $@
@echo 'Finished building target: $@'
@echo ' '

$(OBJS): build/%.o: %.cpp
@echo building $@ from $<
mkdir -p $(dir $@)
$(CXX) $(CPPFLAGS) $< -o $@

编辑:user657267找到了为什么你的方法不起作用。