I am attempting to create a single top-level recursive makefile, idea being once the makefile is created, it will require little maintenance when files are added or removed from the project.
The issue that I am having is when there are files with the same name but reside in different directories, GNU make will use the .cpp file from the first directory is sees throughout the rest of the build and when the linker runs, it complains about multiple definitions of a function because the .o files that are generated are all based off of the same source file.
The code I am reference below is not the code of my actual project but it exhibits the same issues I am having when trying to build my main project. Here is the layout of my top-level directory with source code residing in subdirectories.
otter@ubuntu:~/work/temp/maketest$ tree
.
├── exec
│ └── main.cpp
├── hello
│ ├── hello.cpp
│ └── hello.h
├── makefile
└── world
├── hello.cpp
└── hello.h
This example is pretty simple - hello/hello.cpp prints "Hello" and world/hello.cpp prints "World!!". main.cpp calls each function to print "HelloWorld!!"
Here is each file.
exec/main.cpp
#include <iostream>
#include "../hello/hello.h"
#include "../world/hello.h"
int main()
{
print_hello();
print_world();
return 0;
}
hello/hello.cpp
#include "hello.h"
void print_hello()
{
std::cout << "Hello";
}
world/hello.cpp
#include "hello.h"
void print_world()
{
std::cout << "World!!\n";
}
Here is my makefile
#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))))
And when I attempt to build this small project, I get the following. Notice ./world/hello.cpp is used twice when building .o files - the first time is output to build/world/hello.o and the second time to /build/hello/hello.o and then the linker fails because the .o files are the same.
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
So does anyone know why ./world/hello.cpp is used twice when building the .o files? Even though the .cpp files have the same name, they are in different directories and I would think make would be smart enough to treat them as different files and not re-use the first file every time is sees subsequent files with the same name.
$1/%.o: %.cpp
- the "$1" part looks wrong. – Sam Varshavchikvpath
directory tells make to search inSRC_DIR
andBUILD_DIR
. Although putting the build directory as a sourcevpath
dir doesn't make any sense, the root cause of your issue is thatvpath
will search the directories you provide in order, andworld
comes beforehello
inMODULES
. – user657267