【问题标题】:How to get current relative directory of your Makefile?如何获取 Makefile 的当前相对目录?
【发布时间】:2013-08-10 19:24:50
【问题描述】:

我在应用程序特定目录中有几个 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 以便将二进制文件放入各自的类型文件夹中?

【问题讨论】:

    标签: makefile gnu-make working-directory


    【解决方案1】:

    据我所知,这是这里唯一可以正确使用空格的答案:

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

    它本质上是通过将' ' 替换为'\ ' 来转义空格字符来工作的,这允许Make 正确解析它,然后它通过进行另一个替换来删除MAKEFILE_LIST 中makefile 的文件名,这样你就剩下了makefile 所在的目录。不是世界上最紧凑的东西,但它确实有效。

    你最终会得到这样的东西,所有的空格都被转义了:

    $(info CURRENT_PATH = $(CURRENT_PATH))
    
    CURRENT_PATH = /mnt/c/Users/foobar/gDrive/P\ roje\ cts/we\ b/sitecompiler/
    

    【讨论】:

      【解决方案2】:

      Makefile 中的一行就足够了:

      DIR := $(notdir $(CURDIR))

      【讨论】:

        【解决方案3】:
        THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
        

        【讨论】:

        • ...除非您在任何路径中有空格。
        • ... 除非您在前一行中包含了其他一些 makefile
        • @robal 是的。这就是我刚刚遇到的。如果您只有两个 Makefile(主要的一个加上一个包含的),这非常好
        • 这对我来说效果很好(没有测试过目录中的空间,但我不希望遇到这种情况)。我对这段代码的主要问题是$(dir) 保留了最后一个路径分隔符。在解决该问题之前添加$(realpath)
        【解决方案4】:

        取自here

        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
        

        希望对你有帮助

        【讨论】:

        • 恕我直言,最好使用内部 make-function dir 而不是 shell dirname 尽管它的路径名带有尾随 / 字符。
        • 因为减少了对外部工具的依赖。即如果在某些系统上会有 dirname 命令的别名怎么办?..
        • 这几乎对我有用,但是 DIR 有一个前导空格,所以一个小的改动就可以完美地工作:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
        • 要引用 current makefile,$MAKEFILE_LIST 的使用必须在任何 include 操作之前 (docs)。
        • 应该使用引号——至少在dirname的参数周围——以防路径中有空格。
        【解决方案5】:

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

        解决方案是:$(CURDIR)

        你可以这样使用它:

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

        【讨论】:

        • 欢迎来到 Stack Overflow。您可以在答案中添加更多细节吗?该问题指出$(CURDIR) 已尝试失败。
        • 警告,快速谷歌用户:这不是 makefile 所在的目录,而是当前working 目录(make 是推出)。这确实是 OP 真正想要的,但问题标题是虚假的,所以问题本身是不一致的。因此,这是对不正确问题的正确答案。 ;)
        • 这完全是错误的,而且是多余的错误答案
        【解决方案6】:

        这是使用 shell 语法获取Makefile 文件的绝对路径的单行代码:

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

        这是基于@0xff answer的无外壳版本:

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

        通过打印来测试它,例如:

        cwd:
                @echo $(CWD)
        

        【讨论】:

        • 至关重要!上面的第二个示例需要 := 赋值运算符,否则复杂的 makefile 可能会发生一些非常奇怪和愤怒的事情(相信我)
        • 第一个是重复错误,不适用于 out-of-tree-builds。
        【解决方案7】:

        shell 函数。

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

        更新。

        仅当您从 Makefile 的当前目录运行 make 时,给定的解决方案才有效。
        正如@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))))
        

        【讨论】:

        • 那行不通。这是同样的问题。 pwd 打印完整路径名,我只想要我所在的实际当前文件夹的名称。
        • 它需要立即扩展值,因此应该使用 := 运算符。与 mkfile_path := 和 current_dir = 一样(否则,当包含其他 Makefile 后 MAKEFILE_LIST 发生更改时,可以稍后评估右侧值
        • 我知道这是一个老问题,但我刚刚遇到并认为这可能很有用。当 makefile 的相对路径中有空格时,这也不起作用。具体来说,路径"Sub Directory/Makefile" 的makefile 的$(lastword "$(MAKEFILE_LIST)") 将为您提供"Directory/Makefile"。我认为没有办法解决这个问题,因为带有两个 makefile 的 $(MAKEFILE_LIST) 给了你一个字符串 "Makefile One Makefile Two,它不能处理空格
        • current_dir 对我不起作用。 $(PWD) 可以,而且要简单得多。
        • "solution only works when you are running make from the Makefile's current directory" 是一种温和的方式来表达“没有真正工作”。我会将最新的文章放在答案的顶部。
        【解决方案8】:

        我喜欢所选的答案,但我认为实际展示它的工作原理比解释它更有帮助。

        /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/])用于去除尾部斜杠。

        【讨论】:

          【解决方案9】:

          更新 2018/03/05 最后我用这个:


          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和其他目录中执行,它可以显示正确。

          【讨论】:

          • “如果执行其他shell和其他目录,它可以显示正确”在英语中没有意义。你能再解释一下吗?
          • 对不起我的英语不好
          • 感谢您的编辑,我明白您的意思是现在。在我尝试之前你能解释一下这是做什么的吗?我不确定如何使用它。就像你所说的“其他外壳”是什么意思
          • 这个答案有很多错误的东西,不可能修复。我建议一个简单的删除。
          【解决方案10】:

          如果您使用 GNU make,$(CURDIR) 实际上是一个内置变量。它是 Makefile 所在的位置 当前工作目录,可能是 Makefile 所在的位置,但并不总是

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

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

          【讨论】:

          • 来自 GNU Make 手册(第 51 页):“当 GNU make 启动时(在它处理了任何 -C 选项之后),它会将变量 CURDIR 设置为当前工作目录的路径名。”不是 Makefile 所在的位置——尽管它们可能是相同的。
          • 投反对票,因为答案在技术上不正确,正如 Simon Peverett 在之前的评论中指出的那样。
          • @SimonPeverett:括号中的表达式表示“应用了 -C 选择后的当前工作目录”。即 $(CURDIR) 为“make -C xxx”和“cd xxx; make”给出完全相同的结果。它还删除了尾随的/。我尝试与gmake v3.81 绑定。但是如果make 被称为make -f xxx/Makefile,你是对的。
          • CURDIR 对我有用,而 PWD 没有。我做了mkdir -p a/s/d,然后在每个文件夹中都有一个makefile,在+$(MAKE) &lt;subdir&gt;之前打印PWDCURDIR
          • 这应该是正确的答案。无需重新发明轮子。 Make 为你做 ootb。
          【解决方案11】:

          示例供您参考,如下:

          文件夹结构可能如下:

          这里有两个Makefile,每个如下;

          sample/Makefile
          test/Makefile
          

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

          示例/制作文件

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

          测试/生成文件

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

          现在,执行示例/Makefile,as;

          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'
          

          解释,就是 parent/home 目录可以存储在 environment-flag 中,并且可以导出,这样它就可以在所有子目录的 makefile 中使用。

          【讨论】:

          • 获取当前工作目录,而不是 makefile 的主目录。
          • 这些是 Makefile 中的行。现在,随着 Makefile 命令的执行,这将获得 Makefile 所在的路径。我理解“makefile 主目录”指的是相同的,即 Makefile 正在执行的目录路径。
          • @Jesse Chisholm 请重新访问。我已经解释了用法。
          • 重复错误:它不适用于树外构建。此外,您的 gnome 终端提供了一种复制文本的简单方法:只需选择它。传闻 Imgur 破产了。
          【解决方案12】:

          我尝试了很多这样的答案,但在我的带有 gnu make 3.80 的 AIX 系统上,我需要做一些老派的事情。

          原来lastwordabspathrealpath 直到 3.81 才添加。 :(

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

          正如其他人所说,不是最优雅的,因为它调用了两次 shell,并且仍然存在空格问题。

          但由于我的路径中没有任何空格,因此无论我如何开始make,它都适用于我:

          • make -f ../wherever/makefile
          • make -C ../任何地方
          • make -C ~/wherever
          • cd ../任何地方;制作

          全部给我wherevercurrent_dirwherever 的绝对路径为mkfile_dir

          【讨论】:

          • 我在 aix (5-6-7) 上编译 gnu-make-3.82 没有问题。
          • 是的,在 3.81 之前,实现了早期解决方案所需的功能。我的答案是针对 3.80 或更早版本的旧系统。遗憾的是,在工作中,我不允许向 AIX 系统添加或升级工具。 :(
          猜你喜欢
          • 2015-04-07
          • 2010-10-26
          • 2017-02-16
          • 2013-11-27
          • 1970-01-01
          • 2010-11-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多