代码之家  ›  专栏  ›  技术社区  ›  x-x

在makefile中,如何获得从一个绝对路径到另一个绝对路径的相对路径?

  •  8
  • x-x  · 技术社区  · 16 年前

    顶级生成文件

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

    src/libfoo的Makefile

    currentdir = $(realpath .)
    includedir = $(function or magic to make a relative path
                   from $(currentdir) to $(includedir),
                   which in this example would be ../../../include)
    

    current dir = /home/username/projects/app/trunk/src/libs/libfoo/ 
    destination = /home/username/projects/app/build/libfoo/ 
    relative    = ../../../../build/libfoo
    

    如何做到这一点,同时尽量便携?

    6 回复  |  直到 16 年前
        1
  •  8
  •   Didier Trosset    16 年前

    $(if 在makefile中,但不可移植(仅限于gmake)且繁琐。

    我知道,你在试图解决你自己造成的问题。你为什么不把正确的价值 includedir

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

    $(includedir) 在子生成文件中。它已经被定义为相对的。

        2
  •  13
  •   Mark Veltzer    10 年前

    shell使用 实路径(1) 以及 --相对于

    举个例子:

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

    您甚至可以通过调用 实路径(1) 因为它知道如何处理许多文件名。

    举个例子:

    RELATIVES:=$(shell realpath --relative-to $(RELATIVE) $(FILES))
        3
  •  6
  •   djondal    6 年前

    Python是可移植的!所以,我建议您在子基文件中使用这个简单的示例

    current_dir 和 destination_dir 路径 os.path.relpath()

    子密钥文件.mk

    current_dir=$(CURDIR)
    makefile_target:
        (echo "import os"; echo "print(os.path.relpath('$(destination_dir)', '$(current_dir)'))" )| python
    
        4
  •  3
  •   Yale Zhang    10 年前

    这里有一个解决方案 使用GNU make函数。尽管它是递归的,但它应该比调用外部程序更有效。这个想法很直截了当:相对路径为零或更多。。上移到最常见的祖先,然后用后缀下移到第二个目录。困难的部分是在两条路径中找到最长的公共前缀。

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

    我最近将一大组递归makefile翻译成了一个完整的项目make,因为众所周知,递归make是不好的,因为它没有公开整个依赖关系图( http://aegis.sourceforge.net/auug97.pdf ). 所有源代码和库路径都是相对于当前makefile目录定义的。我没有定义固定数量的泛型%build规则,而是为每个(源代码目录、输出目录)对创建一组规则,这避免了使用vpath的模糊性。在创建构建规则时,我需要每个源代码目录的规范路径。虽然可以使用绝对路径,但它通常太长,而且不易移植(我碰巧使用了Cygwin GNU make,其中绝对路径有/cygdrive前缀,Windows程序无法识别)。因此,我大量使用这个函数来生成规范路径。

        5
  •  2
  •   Norman Gray    16 年前

    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"
    

    (没人说一定要漂亮……)

        6
  •  2
  •   Alexander Shukaev    7 年前

    纯品牌的强力滴入式解决方案:

    override define \s :=
    $() $()
    endef
    
    ifndef $(\s)
    override $(\s) :=
    else
    $(error Defined special variable '$(\s)': reserved for internal use)
    endif
    
    override define dirname
    $(patsubst %/,%,$(dir $(patsubst %/,%,$1)))
    endef
    
    override define prefix_1
    $(if $(or $\
    $(patsubst $(abspath $3)%,,$(abspath $1)),$\
    $(patsubst $(abspath $3)%,,$(abspath $2))),$\
    $(strip $(call prefix_1,$1,$2,$(call dirname,$3))),$\
    $(strip $(abspath $3)))
    endef
    
    override define prefix
    $(call prefix_1,$1,$2,$1)
    endef
    
    override define relpath_1
    $(patsubst /%,%,$(subst $(\s),/,$(patsubst %,..,$(subst /,$(\s),$\
    $(patsubst $3%,%,$(abspath $2)))))$\
    $(patsubst $3%,%,$(abspath $1)))
    endef
    
    override define relpath
    $(call relpath_1,$1,$2,$(call prefix,$1,$2))
    endef
    

    $(info $(call prefix,/home/user,/home/user))
    $(info $(call prefix,/home/user,/home/user/))
    $(info $(call prefix,/home/user/,/home/user))
    $(info $(call prefix,/home/user/,/home/user/))
    
    $(info $(call relpath,/home/user,/home/user))
    $(info $(call relpath,/home/user,/home/user/))
    $(info $(call relpath,/home/user/,/home/user))
    $(info $(call relpath,/home/user/,/home/user/))
    
    $(info ----------------------------------------------------------------------)
    
    $(info $(call prefix,/home/user,/home/user/.local/share))
    $(info $(call prefix,/home/user,/home/user/.local/share/))
    $(info $(call prefix,/home/user/,/home/user/.local/share))
    $(info $(call prefix,/home/user/,/home/user/.local/share/))
    
    $(info $(call relpath,/home/user,/home/user/.local/share))
    $(info $(call relpath,/home/user,/home/user/.local/share/))
    $(info $(call relpath,/home/user/,/home/user/.local/share))
    $(info $(call relpath,/home/user/,/home/user/.local/share/))
    
    $(info ----------------------------------------------------------------------)
    
    $(info $(call prefix,/home/user/.config,/home/user/.local/share))
    $(info $(call prefix,/home/user/.config,/home/user/.local/share/))
    $(info $(call prefix,/home/user/.config/,/home/user/.local/share))
    $(info $(call prefix,/home/user/.config/,/home/user/.local/share/))
    
    $(info $(call relpath,/home/user/.config,/home/user/.local/share))
    $(info $(call relpath,/home/user/.config,/home/user/.local/share/))
    $(info $(call relpath,/home/user/.config/,/home/user/.local/share))
    $(info $(call relpath,/home/user/.config/,/home/user/.local/share/))
    
    $(info ----------------------------------------------------------------------)
    
    $(info $(call prefix,/home/user/.local/share,/home/user))
    $(info $(call prefix,/home/user/.local/share,/home/user/))
    $(info $(call prefix,/home/user/.local/share/,/home/user))
    $(info $(call prefix,/home/user/.local/share/,/home/user/))
    
    $(info $(call relpath,/home/user/.local/share,/home/user))
    $(info $(call relpath,/home/user/.local/share,/home/user/))
    $(info $(call relpath,/home/user/.local/share/,/home/user))
    $(info $(call relpath,/home/user/.local/share/,/home/user/))
    
    $(info ----------------------------------------------------------------------)
    
    $(info $(call prefix,/home/user/.local/share,/home/user/.config))
    $(info $(call prefix,/home/user/.local/share,/home/user/.config/))
    $(info $(call prefix,/home/user/.local/share/,/home/user/.config))
    $(info $(call prefix,/home/user/.local/share/,/home/user/.config/))
    
    $(info $(call relpath,/home/user/.local/share,/home/user/.config))
    $(info $(call relpath,/home/user/.local/share,/home/user/.config/))
    $(info $(call relpath,/home/user/.local/share/,/home/user/.config))
    $(info $(call relpath,/home/user/.local/share/,/home/user/.config/))
    
    $(info ----------------------------------------------------------------------)
    
    $(info $(call prefix,/root,/home/user))
    $(info $(call prefix,/root,/home/user/))
    $(info $(call prefix,/root/,/home/user))
    $(info $(call prefix,/root/,/home/user/))
    
    $(info $(call relpath,/root,/home/user))
    $(info $(call relpath,/root,/home/user/))
    $(info $(call relpath,/root/,/home/user))
    $(info $(call relpath,/root/,/home/user/))
    

    预期结果:

    /home/user
    /home/user
    /home/user
    /home/user
    
    
    
    
    ----------------------------------------------------------------------
    /home/user
    /home/user
    /home/user
    /home/user
    ../..
    ../..
    ../..
    ../..
    ----------------------------------------------------------------------
    /home/user
    /home/user
    /home/user
    /home/user
    ../../.config
    ../../.config
    ../../.config
    ../../.config
    ----------------------------------------------------------------------
    /home/user
    /home/user
    /home/user
    /home/user
    .local/share
    .local/share
    .local/share
    .local/share
    ----------------------------------------------------------------------
    /home/user
    /home/user
    /home/user
    /home/user
    ../.local/share
    ../.local/share
    ../.local/share
    ../.local/share
    ----------------------------------------------------------------------
    
    
    
    
    ../../root
    ../../root
    ../../root
    ../../root
    
        7
  •  0
  •   Slaven Rezic    5 年前

    Perl是可移植的!下面是一个使用核心Perl模块文件::Spec(resp。文件::规范::函数):

    current_dir= /home/username/projects/app/trunk/src/libs/libfoo/ 
    destination= /home/username/projects/app/build/libfoo/ 
    relative=    $(shell perl -MFile::Spec::Functions=abs2rel -E 'say abs2rel(shift, shift)' $(destination) $(current_dir))
    
    makefile_target:
        @echo "Use now $(relative)"