如何获取Makefile的当前相对目录?


202

我在应用程序特定的目录中有几个Makefile,例如:

/project1/apps/app_typeA/Makefile
/project1/apps/app_typeB/Makefile
/project1/apps/app_typeC/Makefile

每个Makefile在此路径上一级都包含一个.inc文件:

/project1/apps/app_rules.inc

在app_rules.inc内部,我设置了要在生成二进制文件的位置放置目标文件。我希望所有二进制文件都在各自的app_type路径中:

/project1/bin/app_typeA/

我尝试使用$(CURDIR),如下所示:

OUTPUT_PATH = /project1/bin/$(CURDIR)

但是相反,我将二进制文件埋入了整个路径名,如下所示:(请注意冗余)

/project1/bin/projects/users/bob/project1/apps/app_typeA

我该怎么做才能获得执行的“当前目录”,以便我只知道app_typeX将二进制文件放入其各自的types文件夹中?

Answers:


271

外壳功能。

您可以使用shell函数:current_dir = $(shell pwd)。或shell结合使用notdir,如果不需要绝对路径: current_dir = $(notdir $(shell pwd))

更新。

给定的解决方案仅在make从Makefile的当前目录运行时有效。
正如@Flimm指出的那样:

请注意,这将返回当前的工作目录,而不是Makefile的父目录。
例如,如果您运行cd /; make -f /home/username/project/Makefile,则current_dir变量将为/,而不是/home/username/project/

以下代码适用于从任何目录调用的Makefile:

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))

5
那行不通。这是同样的问题。pwd打印完整路径名,我只想知道我所在的当前当前文件夹的名称
。– boltup_im_coding

6
它需要立即扩展该值,因此应使用:=运算符。就像在mkfile_path:=和current_dir =中一样(否则,稍后在包含其他Makefile之后更改MAKEFILE_LIST时,可以稍后评估右侧值
Tom Gruner 2014年

10
我知道这是一个古老的问题,但是我遇到了,并认为这可能有用。当makefile的相对路径中有空格时,这也将不起作用。具体来说,$(lastword "$(MAKEFILE_LIST)")在path的makefile文件"Sub Directory/Makefile"将为您提供"Directory/Makefile"。我不认为有任何办法可以解决,因为$(MAKEFILE_LIST)有了两个makefile文件,您"Makefile One Makefile Two可以得到一个不能处理空格的单个字符串
mrosales 2015年

2
current_dir对我不起作用。 $(PWD)做到,而且要简单得多。
CaTx

3
"solution only works when you are running make from the Makefile's current directory"是说“不是很有效”的一种温和方法。我会将最新的文章放在答案的顶部。
维克多·谢尔坚科

137

这里 ;

ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))

显示为;

$ cd /home/user/

$ make -f test/Makefile 
/home/user/test

$ cd test; make Makefile 
/home/user/test

希望这可以帮助


3
恕我直言,最好使用内部make-function,dir而不要使用shell dirname带有结尾/字符的路径名。
Serge Roussak 2015年

3
因为减少了对外部工具的依赖。即如果在某个系统上会出现dirname命令的别名该怎么办?..
Serge Roussak

6
这几乎对我有用,但是DIR具有领先的空格,因此,进行较小的更改就可以完美地工作:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz

9
要引用当前的 makefile,$MAKEFILE_LIST必须在任何include操作(docs)之前使用。
nobar 2015年

2
如果dirname路径中有空格,应至少在参数周围使用引号。
nobar 2015年

46

如果使用的是GNU make,则$(CURDIR)实际上是一个内置变量。它是Makefile驻留 在当前工作目录中位置,位置可能是Makefile所在位置,但并非始终如此

OUTPUT_PATH = /project1/bin/$(notdir $(CURDIR))

请参阅http://www.gnu.org/software/make/manual/make.html中的附录A快速参考


18
在GNU Make手册(第51页)中:“当GNU make启动时(在处理任何-C选项之后),它将变量CURDIR设置为当前工作目录的路径名。” 不是Makefile所在的位置-尽管它们可能相同。
Simon Peverett 2014年

4
如Simon Peverett在先前评论中指出的那样,由于答案在技术上不正确,因此被否决。
Subfuzion 2014年

5
@SimonPeverett:方括号中的表达式表示“应用后的当前工作目录-C opts”。即$(CURDIR)给出与“ make -C xxx”和“ cd xxx; make”完全相同的结果。同时它也删除了结尾/。我尝试与gmakev3.81 捆绑在一起。但是如果make被称为,您是正确的make -f xxx/Makefile
2015年

CURDIR为我工作哪里PWD没有。我没有mkdir -p a/s/d那么每个文件夹中有哪些打印生成文件PWDCURDIR+$(MAKE) <subdir>
Mark K Cowan's

27
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))

12
...除非您在任何路径中都有空格。
Craig Ringer

9
...除非您在上一行中包含了其他一些生成文件
-robal 2015年

1
@robal Yep。那就是我刚遇到的。如果您只有两个Makefile(主要的一个加上附带的一个),这将非常有用。
sherrellbc

6

我尝试了许多这样的答案,但是在使用gnu make 3.80的AIX系统上,我需要做一些旧的事情。

原来是lastwordabspath并且realpath直到3.81才被添加。:(

mkfile_path := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
mkfile_dir:=$(shell cd $(shell dirname $(mkfile_path)); pwd)
current_dir:=$(notdir $(mkfile_dir))

就像其他人所说的那样,它不是最优雅的,因为它两次调用了外壳程序,并且仍然存在空格问题。

但是由于我的路径中没有空格,所以无论我如何开始,它都对我有用make

  • make -f ../wherever/makefile
  • make -C ../在任何地方
  • 使-C〜/ where
  • cd ../wherewhere; 使

所有给我wherevercurrent_dir和绝对路径wherevermkfile_dir


我举一个例子,在aix(5-6-7)上编译了gnu-make-3.82没有问题。
ZsigmondLőrinczy'16

是的,到了3.81,实现了较早解决方案所需的功能。我的答案是针对3.80或更早版本的旧系统。不幸的是,在工作中,我不允许向AIX系统添加或升级工具。:(
杰西·奇斯霍尔姆

5

我喜欢选择的答案,但是我认为实际展示它比解释它会更有帮助。

/tmp/makefile_path_test.sh

#!/bin/bash -eu

# Create a testing dir
temp_dir=/tmp/makefile_path_test
proj_dir=$temp_dir/dir1/dir2/dir3
mkdir -p $proj_dir

# Create the Makefile in $proj_dir
# (Because of this, $proj_dir is what $(path) should evaluate to.)
cat > $proj_dir/Makefile <<'EOF'
path := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
cwd  := $(shell pwd)

all:
    @echo "MAKEFILE_LIST: $(MAKEFILE_LIST)"
    @echo "         path: $(path)"
    @echo "          cwd: $(cwd)"
    @echo ""
EOF

# See/debug each command
set -x

# Test using the Makefile in the current directory
cd $proj_dir
make

# Test passing a Makefile
cd $temp_dir
make -f $proj_dir/Makefile

# Cleanup
rm -rf $temp_dir

输出:

+ cd /tmp/makefile_path_test/dir1/dir2/dir3
+ make
MAKEFILE_LIST:  Makefile
         path: /private/tmp/makefile_path_test/dir1/dir2/dir3
          cwd: /tmp/makefile_path_test/dir1/dir2/dir3

+ cd /tmp/makefile_path_test
+ make -f /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
MAKEFILE_LIST:  /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
         path: /tmp/makefile_path_test/dir1/dir2/dir3
          cwd: /tmp/makefile_path_test

+ rm -rf /tmp/makefile_path_test

注意:该功能$(patsubst %/,%,[path/goes/here/])用于去除斜杠。


2

这是Makefile使用shell语法获取文件绝对路径的一种方法:

SHELL := /bin/bash
CWD := $(shell cd -P -- '$(shell dirname -- "$0")' && pwd -P)

这是基于@ 0xff答案的不带外壳的版本:

CWD := $(abspath $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))))

通过打印进行测试,例如:

cwd:
        @echo $(CWD)

1
至关重要!上面的第二个示例需要:=赋值运算符,否则复杂的makefile可能会发生一些非常奇怪和愤怒的事情(请相信我)
EMon

1
第一个是重复的错误,不适用于树外版本。
JohanBoulé19年

2

在这里找到解决方案:https : //sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

解决方案是:$(CURDIR)

您可以这样使用它:

CUR_DIR = $(CURDIR)

## Start :
start:
    cd $(CUR_DIR)/path_to_folder

3
欢迎使用堆栈溢出。您可以在答案中添加更多细节吗?问题指出$(CURDIR)已经尝试失败。
肖恩

4
快速谷歌警告:这不是 makefile所在的目录,而是当前的工作目录(在其中make启动)。确实,这确实是OP真正想要的,但是问题标题是虚假的,因此问题本身是不一致的。因此,这是对错误问题的正确答案。;)
Sz。

这完全是错误的,也是多余的错误答案
UpAndAdam

0

据我所知,这是唯一可以正确处理空格的答案:

space:= 
space+=

CURRENT_PATH := $(subst $(lastword $(notdir $(MAKEFILE_LIST))),,$(subst $(space),\$(space),$(shell realpath '$(strip $(MAKEFILE_LIST))')))

它本质上的工作方式逃避空格字符被替换' ''\ '允许做出正确分析,然后将其删除生成文件的文件名中MAKEFILE_LIST的那么留给你与生成文件所在的目录做另一个替换。不完全是最紧凑世界上的东西,但它确实起作用。

您将最终得到如下所示的所有空格都被转义的内容:

$(info CURRENT_PATH = $(CURRENT_PATH))

CURRENT_PATH = /mnt/c/Users/foobar/gDrive/P\ roje\ cts/we\ b/sitecompiler/

-2

供参考的示例,如下:

文件夹结构可能如下:

在此处输入图片说明

如果有两个Makefile,每个文件如下:

sample/Makefile
test/Makefile

现在,让我们看看Makefile的内容。

样本/制作文件

export ROOT_DIR=${PWD}

all:
    echo ${ROOT_DIR}
    $(MAKE) -C test

测试/ Makefile

all:
    echo ${ROOT_DIR}
    echo "make test ends here !"

现在,按以下方式执行示例/ Makefile:

cd sample
make

输出:

echo /home/symphony/sample
/home/symphony/sample
make -C test
make[1]: Entering directory `/home/symphony/sample/test'
echo /home/symphony/sample
/home/symphony/sample
echo "make test ends here !"
make test ends here !
make[1]: Leaving directory `/home/symphony/sample/test'

解释是,父目录/主目录可以存储在环境标志中,并且可以导出,以便可以在所有子目录的makefile中使用。


2
这将获取当前的工作目录,而不是makefile的主目录。
Jesse Chisholm

这些是Makefile中的行。现在,随着Makefile命令的执行,这将获取Makefile所在的路径。我了解“ makefile主目录”是指同一目录,即在其中执行Makefile的目录路径。
parasrish

@Jesse Chisholm,请重新访问。我已经解释了用法。
parasrish

重复的错误:不适用于树外版本。另外,您的gnome终端提供了一种简单的方法来复制文本:只需选择它即可。有传闻称伊穆尔是不道德的。
约翰·布勒(JohanBoulé)

-2

更新2018/03/05 finnaly我使用这个:


shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

可以在相对路径或绝对路径中正确执行。执行正确的crontab调用。在其他shell中正确执行。

显示示例,a.sh打印自身路径。

[root@izbp1a7wyzv7b5hitowq2yz /]# more /root/test/a.sh
shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

echo $shellPath
[root@izbp1a7wyzv7b5hitowq2yz /]# more /root/b.sh
shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

$shellPath/test/a.sh
[root@izbp1a7wyzv7b5hitowq2yz /]# ~/b.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# /root/b.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# cd ~
[root@izbp1a7wyzv7b5hitowq2yz ~]# ./b.sh
/root/./test
[root@izbp1a7wyzv7b5hitowq2yz ~]# test/a.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz ~]# cd test
[root@izbp1a7wyzv7b5hitowq2yz test]# ./a.sh
/root/test/.
[root@izbp1a7wyzv7b5hitowq2yz test]# cd /
[root@izbp1a7wyzv7b5hitowq2yz /]# /root/test/a.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# 

老:我用这个:

MAKEFILE_PATH := $(PWD)/$({0%/*})

如果在其他shell和其他目录中执行,则可以显示正确。


1
“如果执行其他外壳程序和其他目录,如果执行则显示正确”在英语中没有意义。您可以再试一次吗?
基思M

对不起,我的英文不好
zhukunqian

感谢您的修改,我现在看到您的意思是。在我尝试之前,您能解释一下它的作用吗?我不确定如何使用它。就像你是什么意思“其他壳”
基思中号

1
这个答案有很多错误,无法解决。我建议一个简单的删除。
约翰·布勒(JohanBoulé)

-2

Makefile中的一行应该足够了:

DIR := $(notdir $(CURDIR))

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.