【问题标题】:GNU Make - Trailing "/"s being dropped?GNU Make - 尾随“/”被删除?
【发布时间】:2019-06-04 20:20:21
【问题描述】:

我正在开发一个在输出目录中创建多个文件的生成文件。为了创建这些文件,输出目录需要已经存在,否则文件创建失败。

这是一个演示我遇到的问题的最小示例:

.PHONY: default
default: dir/file.dat dir/other.dat

# In order to create these files, their parent directory must exist
dir/%.dat: | dir/
    touch "$@"

# To create a directory, use mkdir -p
%/:
    mkdir -p "$@"

当我运行 makefile 时,我收到一个错误,即不存在生成 dir 的规则。调试运行显示 make 正在从 "dir/" 中删除尾随 "/":

root@69654136a2ae:~# make -rdf dirs.mk
GNU Make 3.81
[snipped]

Considering target file `default'.
 File `default' does not exist.
  Considering target file `dir/file.dat'.
   File `dir/file.dat' does not exist.
    Considering target file `dir'.
     File `dir' does not exist.
     Looking for an implicit rule for `dir'.
     No implicit rule found for `dir'.
     Finished prerequisites of target file `dir'.
    Must remake target `dir'.
make: *** No rule to make target `dir', needed by `dir/file.dat'.  Stop.

顺便说一句,在命令行上请求一个目录目标就可以了:

root@69654136a2ae:~# make -rdf dirs.mk /some/dir/
GNU Make 3.81
[snipped]

Updating goal targets....
Considering target file `/some/dir/'.
 File `/some/dir/' does not exist.
 Looking for an implicit rule for `/some/dir/'.
 Trying pattern rule with stem `/some/dir'.
 Found an implicit rule for `/some/dir/'.
 Finished prerequisites of target file `/some/dir/'.
Must remake target `/some/dir/'.
mkdir -p "/some/dir/"

我怎样才能做我想做的事?
理想情况下,我正在寻找一个通用的解决方案。上面的例子只有一个子目录,但实际项目会有很多子目录,所以我想避免到处复制粘贴解决方案。

【问题讨论】:

  • 我无法重现该问题。与 Cygwin 一起为我工作。虽然最后我得到了rm dir/make: unlink: dir/: Operation not permitted,这是一个不相关的问题。
  • 我无法重现该问题。您使用的是哪个版本的 Make?
  • @JosephQuinsey,我使用 GNU Make 3.81 重现了这个问题
  • @leiyc:使用 make 3.81,我得到一个不同的错误:make: *** No rule to make target 'dir/file.dat', needed by 'default'. Stop. mkdir 永远不会被调用。
  • @JosephQuinsey,当您将-rd 选项与make 一起使用时,您将看到Trying rule prerequisite dir'。`/ 不见了。

标签: makefile gnu-make


【解决方案1】:

是的,/ 被删除在 dir/ 目标中(可以在 GNU Make 3.81 下重现问题)。

另一种解决方案是使用automatic variable $(@D) 获取目标文件名的目录部分,并删除尾部斜杠。并将touch之前的目录制作成文件:

.PHONY: default
default: dir/file.dat dir/other.dat

# In order to create these files, make their parent directory first
dir/%.dat:
        mkdir -p $(@D)
        touch "$@"

在 GNU Make 3.81 下测试通过:

$ make --version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
...

$ make
mkdir -p dir
touch "dir/file.dat"
mkdir -p dir
touch "dir/other.dat"

【讨论】:

    【解决方案2】:

    您的%/ 规则仅匹配带有结尾/ 的目标名称。因为,根据您使用的 make 版本,在将名称视为目标名称之前,可以从名称中删除或不删除尾随 /,这并不总是有效。

    对于单个目录,不要使用这个尾随 / 并且不要使用模式规则:

    dir/%.dat: | dir
        touch "$@"
    
    dir:
        mkdir -p "$@"
    

    对于更通用的解决方案,leiyc 的解决方案很好。还有其他:

    1. 如果您可以获得要创建的所有目录的列表,则可以使用以下命令一次性创建所有目录:

      DIRS := dir ...
      $(shell mkdir -p $(DIRS) > /dev/null)
      
      <any-target>: <prerequisites>
          <recipe>
      
    2. 如果您可以获得要创建的所有目录的列表,但更喜欢仅订购的先决条件,则可以使用secondary expansion 和创建目录的规则:

      DIRS := dir ...
      
      .SECONDEXPANSION:
      
      <any-target>: <prerequisites> | $$(@D)
          <recipe>
      
      $(DIRS):
          mkdir -p $@
      

    而且可能还有其他可能性……

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-19
      • 2018-05-06
      • 1970-01-01
      • 1970-01-01
      • 2014-07-08
      • 2011-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多