常见的GNU makefile目录路径

53

我想通过使用一个通用的 makefile 来整合一些构建信息。我的问题是,我希望能够从不同的子目录层次结构中使用该 makefile,这使得工作目录值 (pwd) 不可预测。例如:

# Makefile.common
TOP := $(shell pwd)
COMPONENT_DIR := $(TOP)/component
COMPONENT_INC := $(COMPONENT_DIR)/include
COMPONENT_LIB := $(COMPONENT_DIR)/libcomponent.a

如果我从子目录包含Makefile.common,像这样,那么$(TOP)目录是不正确的,其它所有内容也会跟着错:

# other_component/Makefile
include ../Makefile.common
# $(COMPONENT_LIB) is incorrectly other_component/component

如何让Makefile.common使用它自己的目录路径而非更为不稳定的pwd?

5个回答

78

您应该能够使用MAKEFILE_LIST变量,如下所示:

# This must be the first line in Makefile.common
TOP := $(dir $(firstword $(MAKEFILE_LIST)))

根据文档:

当make阅读各种makefile(包括从MAKEFILES变量获取的任何文件、命令行、默认文件或include指令)时,它们的名称将自动附加到MAKEFILE_LIST变量中。它们会在make开始解析它们之前添加。这意味着,如果一个makefile的第一件事是检查这个变量的最后一个单词,那么它将是当前makefile的名称。然而,一旦当前makefile使用了include,最后一个单词就会变成刚刚被include的makefile的名称。


1
这在我的开发环境中非常好用,但我意识到目标机器正在运行make 3.79,并且MAKEFILE_LIST功能是在make 3.80中添加的:http://www.mail-archive.com/help-make@gnu.org/msg05902.html - cdleary
1
似乎MAKEFILE_LIST中不包含完整路径,只有相对路径。 - kbyrd
16
如果有人需要的话,虽然有些迟了,但以下是一段代码片段,可以给你提供一个绝对路径,而不依赖 realpath(基于这个答案和这篇博客文章): TOP := $(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) - Xavier Holt
6
Xavier,你应该将那个发表为答案,而不是评论。过后发表也可以(甚至是鼓励的),因为这是一个不断更新的网站。 - Frank Szczerba
6
这是我用来获取绝对路径的可行方法:TOP := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))。根据我的快速网络搜索,$(abspath ...) 是在 GNU make 3.81(2006年4月1日)同时引入了 $(lastword ...)。Xavier Holt 的解决方案适用于较旧版本的 GNU make。 - FooF
我有点困惑。答案使用了 firstword,但这不会返回原始的 makefile 吗?按照原帖的要求,难道不应该使用 lastword 来获取 当前(包含的)makefile 吗? - Matthijs Kooijman

17

试试这个:

ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))

编辑:一定要使用:=而不是=,因为后者会导致make使用延迟绑定,MAKEFILE_LIST可能会由于后来的包含而发生更改。


我应该指出,这是在JesperE上面的示例表单基础上构建的。但是,它提供了一个绝对路径来构建顶部。 - Bill
+1 这个解决方案完美且便携。我已经在 UNIX 和 Windows/Cygwin 上成功测试过。还要注意 VPATH 变量/指令。 - Andreas Spindler
3
在我看来,realpath 看起来是 make 的内置函数,而不是依赖于已安装的二进制文件(文档)。 - Brent Bradburn
1
此处使用的realpath是gnu make内置函数。另外,与其使用shell dirname,更好的选择可能是使用dir内置函数。 - akhan
1
编辑了答案以消除关于realpath的混淆:这个命令是有效的,不需要担心主机系统上是否安装了realpath -- 请参考@BrentBradburn和@akhan的评论。 ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) - Stabledog

2

我的解决方案:

cwd  :=  $(shell readlink -en $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))) 

当你在/opt/other/path/subdir目录下时,像make -f /opt/some/dir/Makefile这样的调用也可以正常工作。


2
你试过这样做吗:
# Makefile.common
TOP ?= $(shell pwd)
COMPONENT_DIR := $(TOP)/component
COMPONENT_INC := $(COMPONENT_DIR)/include
COMPONENT_LIB := $(COMPONENT_DIR)/libcomponent.a

# other_component/Makefile
TOP ?= ..
include ../Makefile.common

使用?=结构将使TOP在已设置的情况下不被重新定义。您可以根据调用make时所在树的位置将其设置为适当的值。我承认我已经有一段时间没有使用GNU make了,因此这可能无法正常工作或需要进行一些微调。

同意,那样可以。我希望有一种方法可以完成这个任务,而不需要在makefile的准备方面进行操作(除了包含),但是那肯定是一个好的备选方案。 - cdleary

2
请将通用内容写在common.mk中,然后将common.mk放置在Make遇到include语句时搜索的默认目录中。请参见手册以获取Make查找常用目录的信息。
您还可以将common.mk放置在自定义目录中,然后键入make -I customdir
在每个子文件夹的Makefile中,请执行以下操作:
include common.mk

就是这样,不用担心路径和移动东西的问题。


1
有趣,但对于复杂的项目和部署来说太幼稚了。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接