简体   繁体   中英

Generate gcc dependencies with full path

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.

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

Dependencies computation

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).

Note

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.

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 .)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM