2010-07-27 439 views
6

一个例子来说明我的问题:在makefile中,如何获取从一个绝对路径到另一个绝对路径的相对路径?

顶层makefile

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

的Makefile的src/libfoo的

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

做你想做的事不容易。在makefile中可能会使用很多合并的$(if,但不可移植(仅适用于gmake)并且很麻烦。

恕我直言,你正试图解决你自己创造的问题。为什么不将includedir的正确值作为顶级Makefile的相对路径发送?

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

然后你就可以在子makefile文件使用$(includedir):它可以很容易如下进行。它已被定义为相对。

+0

这个元的回答是最好的一个 - 避免的问题!还要注意'--directory'不可移植:'cd $(rootdir)/ src/libs/libfoo; $(MAKE)...'会更可靠。 – 2010-07-27 13:09:43

+1

@Norman Gray:'$(MAKE)-C ...'有什么问题? – Beta 2010-07-27 16:54:49

+0

我不认为我会尽量_wrong_,但不是所有的'make'命令都支持它(并且它不在[posix]中)(http://www.opengroup.org/onlinepubs/009695399/utilities/make例如.html),所以如果这个makefile是用于分发的话,那么它可能会产生问题。无论如何,我倾向于认为'cd foo; $(MAKE)...'更清楚地表达意图。 – 2010-07-29 17:51:06

4

Python是可移植的!所以,我建议你这个简单的例子,在你的submakefile

随着current_dirdestination_dir路径os.path.relpath()没有工作给你,让你不必重新发明轮子。

submakefile.mk

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

第一:很好的解决方案!我将不得不记得使用python的真棒操作系统库......第二:你切换os.path.relpath()的参数。它应该是:os.path.relpath('$(destination_dir)','$(current_dir)') – jvriesem 2012-12-17 20:34:41

+0

我写了一个Python脚本,它需要1-2个参数并打印结果。然后通过$(shell python relpath.py $(destination_dir))调用它。惊人的提示! – 2015-04-04 10:19:51

2

德罗巴的回答是最好的,但下面可能给你一些想法:

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" 

排序!

(没有人说,它必须是相当...)

1

下面是只有使用GNU使功能的解决方案。即使它是递归的,它应该比调用外部程序更有效率。这个想法非常简单:相对路径将为零或更多......以继续最常见的祖先,然后后缀到第二个目录。困难的部分是在两条路径中找到最长的公共前缀。

# 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是坏的,由于没有暴露整个依赖图(http://aegis.sourceforge.net/auug97.pdf)。所有源代码和库路径都是相对于当前makefile目录定义的。我没有定义固定数量的通用%构建规则,而是为每个(源代码目录,输出目录)对创建一组规则,这避免了使用vpath的不明确性。创建构建规则时,我需要每个源代码目录的规范路径。尽管可以使用绝对路径,但它通常太长而且不便于携带(我正好在使用Cygwin GNU make,其中绝对路径具有/ cygdrive前缀并且不被Windows程序识别)。因此,我大量使用这个函数来生成规范路径。

4

外壳具有使用真实路径(1)--relative到标志,所以你可以只调用shell此功能。

下面是一个例子:

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

你甚至可以处理文件与真实路径(1),因为它知道如何处理大量文件名中的一个调用整个列表。

下面是一个例子:

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

realpath在我的shell中不存在。 (BSD)我不认为它也存在于cygwin之下。 – dbn 2017-02-13 20:01:28

+1

realpath(1)是GNU coreutils的一部分,因此它是Linux上的标准配置。 – 2017-02-13 21:27:40