下面是只有使用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程序识别)。因此,我大量使用这个函数来生成规范路径。
这个元的回答是最好的一个 - 避免的问题!还要注意'--directory'不可移植:'cd $(rootdir)/ src/libs/libfoo; $(MAKE)...'会更可靠。 – 2010-07-27 13:09:43
@Norman Gray:'$(MAKE)-C ...'有什么问题? – Beta 2010-07-27 16:54:49
我不认为我会尽量_wrong_,但不是所有的'make'命令都支持它(并且它不在[posix]中)(http://www.opengroup.org/onlinepubs/009695399/utilities/make例如.html),所以如果这个makefile是用于分发的话,那么它可能会产生问题。无论如何,我倾向于认为'cd foo; $(MAKE)...'更清楚地表达意图。 – 2010-07-29 17:51:06