Reputation: 340
I have a simple project that looks something like this
.
├── build
│ ├── file1.o
│ └── one
│ ├── file1.o
│ └── file2.o
├── .depend
├── Makefile
└── src
├── file1.cpp
└── one
├── file1.cpp
└── file2.cpp
The Makefile is something like this:
# Get all of the source files
SRC = $(shell find src/ -name "*.cpp")
# Get all of the object files
OBJ = $(subst src,build,$(SRC:.cpp=.o))
$(OBJ):
@mkdir -p $(shell dirname $@)
g++ -g -c $(subst build,src,$(subst .o,.cpp,$@)) -o $@
all: depend build
build: $(OBJ)
gcc -o project $^
depend:
g++ -MM $(SRC) > .depend
sed -i 's/.\/src/.\/build\//g' .depend
sinclude .depend
I am attempting to generate makefile dependencies by running g++ -MM src/file1.cpp src/one/file1.cpp src/one/file2.cpp > .depend
, and it generates the following directives:
file1.o: src/file1.cpp <other headers>
file1.o: src/one/file1.cpp <other headers>
file2.o: src/one/file2.cpp <other headers>
The problem with this, is that build/file1.o
does not match file1.o
, and as a result, changing src/file1.cpp
or any of the headers it depends on does not cause the object file to be rebuilt. At first I thought it might have been an issue where sinclude .depend
was run before the .depend file was generated, but the problem persists even if I run make depend
followed by make build
. From everything I've read, there are no g++
arguments or options that would preserve the path of the name.
Is it possible to generate a dependency file this way, or is this a fundamentally incorrect approach to building a project?
I took a look at the answers for the question this question was marked as a possible duplicate of, but it seems that question is asking how to create a complete makefile for a project, whereas my issue is not with the creation of a Makefile, but rather an issue with gcc -MM
dependency generation. The answers to that question do not address my problems.
Upvotes: 5
Views: 3246
Reputation: 29325
What about:
# Get all of the source files
SRC = $(shell find src/ -name "*.cpp")
# Get all of the object files
OBJ = $(patsubst src/%.cpp,build/%.o,$(SRC))
.PHONY: all
all: project
project: $(OBJ)
gcc -o $@ $^
$(OBJ): build/%.o: src/%.cpp
@mkdir -p $(dir $@)
g++ -g -c $< -o $@
.depend: $(SRC)
g++ -MM $^ > $@ && \
sed -Ei 's#^(.*\.o: *)src/(.*/)?(.*\.cpp)#build/\2\1src/\2\3#' $@
include .depend
The sed
command substitutes any:
file.o: src/file.cpp ...
by:
build/file.o: src/file.cpp ...
and any:
file.o: src/X/Y/Z/file.cpp ...
by:
build/X/Y/Z/file.o: src/X/Y/Z/file.cpp ...
The target is directly .depend
and it has all source files as dependencies such that it is automatically rebuilt if missing or older than any source file. No need to use the depend
phony target or to add it as a pre-requisite of all
(make automatically tries to rebuild files included with include
, if needed).
I added some GNU make features (patsubst
, static pattern rule, systematic use of automatic variables...) Rework the non-supported ones if you use another make.
Upvotes: 2
Reputation: 99154
Here are three approaches.
One, modify the output with sed (similar to Renaud Pacalet's answer):
depend:
g++ -MM $(SRC) | sed 's/.*: src\([^ ]*\)cpp/build\1o: src\1cpp/' > .depend
Two, use a shell loop:
STEMS := file1 one/file1 one/file2
depend:
rm -f .depend
for x in $(STEMS); do g++ -MM -MT build/$$x.o src/$$x.cpp >> .depend; done
Three, a Make approach:
DEPENDENCIES := $(addsuffix -depend,$(STEMS))
clear-depend:
rm -f .depend
depend: $(DEPENDENCIES)
%-depend: clear-depend
g++ -MM -MT build/$*.o src/$*.cpp >> .depend
(My favorite approach is to have a separate dependency file for each object file, instead of one big .depend
file. It has several advantages, but it takes some time to explain, and it's also tricky if there are name collisions in your source tree, such as file1.cpp
and file1.cpp
.)
Upvotes: 1
Reputation: 25653
I use the following sequence for my build:
define req
$(subst ..,__,$(dir build-$(TARGET)$(build_dir_ext)/$(1)))%.o: $(dir $1)%.cpp
mkdir -p $$(dir $$(subst ..,__,$$@))
$$(CXX) -MM $$(CXXFLAGS) $$< -MT $$(subst ..,__,$$@) > $$(patsubst %.o,%.d,$$(subst ..,__,$$@))
$$(CXX) $$(CXXFLAGS) $$< -c -o $$(subst ..,__,$$@)
endef
$(eval $(foreach x,$(OBJ),$(call req,$(x))))
As a result make is now able to handle a path which can be "outside" the source tree, simply by using '__' instead of '..' and the build dir is set accordingly to the found patterns, so there is no problem with src
and build
anymore. I need the "outside" files to use a source pool where the local build dir is under the root of source pool and project directory.
In hope that helps...
EDIT 1: Why replacing '..'
Think of the following source tree:
./sourcepool/lib1/src/one.cpp
./sourcepool/project/build
If your Makefile is in the ./sourcepool/project path and one of the OBJ is "../lib1/src/one.o" the Makefile should create a equivalent path in the build directory. That is, if '..' is used, not possible, because the path is then not longer in build but one depth higher. If replacing .. with __ the result is as following:
./sourcepool/project/build/__/lib1/src/one.o
This makes it possible to not copy or link all used dirs to the local project and build file tree.
Upvotes: 0