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

前端 未结 7 2092
别那么骄傲
别那么骄傲 2021-02-19 20:57

An example to illustrate my question:

Top level makefile

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


        
7条回答
  •  日久生厌
    2021-02-19 21:22

    Here's a solution that only uses GNU make functions. Even though it's recursive, it ought to be more efficient than calling an external program. The idea is pretty straight forward: the relative path will be zero or more .. to go up to the most common ancestor, then a suffix to go down to the 2nd directory. The hard part is finding the longest common prefix in both paths.

    # DOES not work if path has spaces
    OneDirectoryUp=$(patsubst %/$(lastword $(subst /, ,$(1))),%,$(1))
    
    # FindParentDir2(dir0, dir1, prefix)  returns prefix if dir0 and dir1
    # start with prefix, otherwise returns
    # FindParentDir2(dir0, dir1, OneDirectoryUp(prefix))
    FindParentDir2=
    $(if
      $(or
        $(patsubst $(3)/%,,$(1)),
        $(patsubst $(3)/%,,$(2))
       ),
       $(call FindParentDir2,$(1),$(2),$(call OneDirectoryUp,$(3))),
       $(3)
     )
    
    FindParentDir=$(call FindParentDir2,$(1),$(2),$(1))
    
    # how to make a variable with a space, courtesy of John Graham-Cumming 
    # http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
    space:= 
    space+=
    
    # dir1 relative to dir2 (dir1 and dir2 must be absolute paths)
    RelativePath=$(subst
                   $(space),
                   ,
                   $(patsubst
                     %,
                     ../,
                     $(subst
                       /,
                       ,
                       $(patsubst
                         $(call FindParentDir,$(1),$(2))/%,
                         %,
                         $(2)
                        )
                      )
                    )
                  )
                 $(patsubst
                   $(call FindParentDir,$(1),$(2))/%,
                   %,
                   $(1)
                  )
    
    # example of how to use (will give ..)
    $(call RelativePath,/home/yale,/home/yale/workspace)
    

    I recently translated a large set of recursive makefiles into a whole project make as it's well known that recursive make is bad due to not exposing the entire dependence graph (http://aegis.sourceforge.net/auug97.pdf). All source code and library paths are defined relative to the current makefile directory. Instead of defining a fixed number of generic % build rules, I create a set of rules for every (source code directory, output directory) pair, which avoids the ambiguity of using vpath. When creating the build rules, I need a canonical path for each source code directory. Although the absolute path can be used, it's usually too long and less portable (I happened to be using Cygwin GNU make where absolute paths have a /cygdrive prefix and aren't recognized by Windows programs). Therefore, I use this function heavily for generating canonical paths.

提交回复
热议问题