In a makefile, how to get the relative path from one absolute path to another?

Didier's answer is the best one, but the following might give you some ideas:

includedir=/a/b/c/d
currentdir=/a/b/e/f/g
up=; while ! expr $includedir : $currentdir >/dev/null; do up=../$up; currentdir=`dirname $currentdir`; done; relative=$up`expr $includedir : $currentdir'/*\(.*\)'`
echo "up=$up  currentdir=$currentdir, relative=$relative"

Sorted!

(no-one said it had to be pretty...)


Doing what you want does not look easy. It may be possible using a lot of combined $(if in the makefile but not portable (gmake only) and cumbersome.

IMHO, you are trying to solve a problem that you create yourself. Why don't you send the correct value of includedir as a relative path from the Top-level Makefile? It can be done very easily as follows:

rootdir = $(realpath .)
default:
    @$(MAKE) --directory=$(rootdir)/src/libs/libfoo includedir=../../../include

Then you can use $(includedir) in the sub-makefiles. It is already defined as relative.


You can use the shell function, and use realpath(1) (which is part of coreutils) and the --relative-to flag.

Here is an example:

RELATIVE_FILE1_FILE2:=$(shell realpath --relative-to $(FILE1) $(FILE2))

You can even process a whole list of files with one invocation of realpath(1) since it knows how to process many file names.

Here is an example:

RELATIVES:=$(shell realpath --relative-to $(RELATIVE) $(FILES))

Python is portable! So, I would suggest you this simple example in your submakefile

With current_dir and destination_dir paths os.path.relpath() does the job for you so you do not have to re-invent the wheel.

submakefile.mk

current_dir=$(CURDIR)
makefile_target:
    (echo "import os"; echo "print(os.path.relpath('$(destination_dir)', '$(current_dir)'))" )| python