【问题标题】:Test whether a directory exists inside a makefile测试makefile中是否存在目录
【发布时间】:2014-01-12 20:35:16
【问题描述】:

在他的answer@Grundlefleck 中解释了如何检查一个目录是否存在。我尝试了一些在makefile 中使用它,如下所示:

foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then
        echo "Dir exists"
    fi

运行make foo.bak(假设foo.bar 存在)会产生以下错误:

echo "foo"
foo
if [ -d "~/Dropbox" ]; then
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [foo.bak] Error 2

我所做的解决方法是使用一个独立的 bash 脚本来实现测试,然后我从 makefile 调用该脚本。然而,这听起来很麻烦。有没有更好的方法来检查 makefile 中是否存在目录?

【问题讨论】:

标签: bash makefile


【解决方案1】:

如果是 shell 命令,make 命令必须在一行中,或者在多行中使用反斜杠作为行扩展。所以,这种方法会奏效:

foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then echo "Dir exists"; fi

或者

foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then \
        echo "Dir exists"; \
    fi

【讨论】:

  • 看起来问题是所有东西都需要放在一条线上。这是真的吗?
  • @PatrickCollins 单个命令可以在不同的行上。例如echo "foo" followed by mv bar bah` 可以是单独的行上的命令。但是,shell 将 if 表达式到 fi 视为单个命令,因此它必须在一行上,或者“转义”(`) line breaks. As you can see in the answer, echo "foo"` 可以在单独的行上在 if 表达式之前,因为它是一个单独的命令。
  • @PatrickCollins 为了使长答案简短,是的。每一行都运行它自己的外壳,因此您不能将测试放在一行和它下面的块上,因为该块将位于不同的外壳中。这可以使用行继续字符“\”跨多行格式化,这又一次有效地将其全部放在一行上。
  • ;是必须的,这没关系: if [ -d ${BOOTSTRAPDIR} ];然后 \ echo "找到 ${BOOTSTRAPDIR}"; \ else \ echo "找不到${BOOTSTRAPDIR}"; \fi
【解决方案2】:

这种方法具有最小的回声:

.PHONY: all
all:
ifneq ($(wildcard ~/Dropbox/.*),)
        @echo "Found ~/Dropbox."
else
        @echo "Did not find ~/Dropbox."
endif

【讨论】:

  • $(wildcard ~/Dropbox/.) 就足够了。此用例中不需要通配符
  • 对我来说,由于某种原因,直到我删除了 * 通配符,它​​实际上才起作用。
【解决方案3】:

在没有目录时采取行动

如果您只需要知道一个目录是否不存在并想通过例如创建它来采取行动,您可以使用普通的 Makefile 目标:

directory = ~/Dropbox

all: | $(directory)
    @echo "Continuation regardless of existence of ~/Dropbox"

$(directory):
    @echo "Folder $(directory) does not exist"
    mkdir -p $@

.PHONY: all

备注:

  • | 表示 make 不应该关心时间戳(它是一个 order-only-prerequisite)。
  • 您可以写false 退出,而不是写mkdir -p $@,或者以不同的方式解决您的问题。

如果您还需要在存在目录时运行特定系列的指令,则不能使用上述指令。换句话说,它相当于:

if [ ! -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does not exist"
fi

没有else 声明。

在目录存在时采取行动

如果你想要相反的 if 语句,这也是可能的:

directory = $(wildcard ~/Dropbox)

all: | $(directory)
    @echo "Continuation regardless of existence of ~/Dropbox"

$(directory):
    @echo "Folder $(directory) exists"

.PHONY: all $(directory)

这相当于:

if [ -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does exist"
fi

同样,没有else 声明。

根据目录的存在和不存在采取行动

这变得有点麻烦,但最终为您提供了两种情况的不错目标:

directory = ~/Dropbox
dir_target = $(directory)-$(wildcard $(directory))
dir_present = $(directory)-$(directory)
dir_absent = $(directory)-

all: | $(dir_target)
    @echo "Continuation regardless of existence of ~/Dropbox"

$(dir_present):
    @echo "Folder $(directory) exists"

$(dir_absent):
    @echo "Folder $(directory) does not exist"

.PHONY: all

这相当于:

if [ -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does exist"
else
    echo "The ~/Dropbox folder does not exist"
fi

自然,通配符扩展可能比 if-else-statement 慢。但是,第三种情况可能很少见,只是为了完整性而添加。

【讨论】:

  • 感谢精彩的代码!但是,如果我可以使用多个 $(directories) 怎么办?
  • @Seung-WooLee 那会更进一步。 :-) 在这种情况下,我会主张将 shell 本身与 .ONESHELL 特殊目标一起使用以提高可读性。
【解决方案4】:

试试这个:

.PHONY: all
something:
    echo "hi"
all:
    test -d "Documents" && something

这将仅在Documents 存在时执行something 下的命令。

为了解决 cmets 中提到的problem,您可以像这样创建一个变量:

PATH_TEST = ~/SomeDirectory

test -d $(PATH_TEST) && something

【讨论】:

    【解决方案5】:

    我有一个案例,我想根据测试在Makefile最顶层中是否存在目录来定义一个变量,而上述方法不存在工作。我发现here 是一个不错的解决方案,可以像这样使用:

    MY_DIRNAME=../External
    ifneq "$(wildcard $(MY_DIRNAME) )" ""
      # if directory MY_DIRNAME exists:
      INCLUDES += -I../External
    else
      # if it doesn't:
      INCLUDES += -I$(HOME)/Code/External
    endif
    

    这将根据MY_DIRNAME中存储的目录是否存在来修改变量INCLUDES

    (动机:在我的情况下,这个变量将用于另一个Makefile,稍后包含在第一个中:

    include $(SFRAME_DIR)/Makefile.common
    

    我想让相同的Makefile 以一种简单的方式在两个不同的环境中工作。)

    【讨论】:

      【解决方案6】:

      有一个非常不同的答案,它允许您在一个 shell 中使用您的 if 语句:

      .ONESHELL:
      foo.bak: foo.bar
          echo "foo"
          if [ -d "~/Dropbox" ]; then
              echo "Dir exists"
          fi
      

      请注意,唯一的区别是 ONESHELL special target

      【讨论】:

        【解决方案7】:

        我使用以下方法来检测文件或目录是否存在并对其采取行动:

        $(if $(filter expected,$(wildcard *)), the expected file exists)
        

        根据您的要求:

        .PHONY: ~/Dropbox
        
        ~/Dropbox:
            echo "Dir exists"
        
        foo.bak: foo.bar | $(if $(filter ~/Dropbox,$(wildcard ~/*)), the expected file exists)
        

        这可以进一步简化:

        foo.bak: foo.bar | $(wildcard ~/Dropbox)
        

        【讨论】:

          猜你喜欢
          • 2013-03-08
          • 2014-09-23
          • 1970-01-01
          • 2019-06-04
          • 1970-01-01
          • 2012-12-30
          • 1970-01-01
          • 2014-04-01
          • 1970-01-01
          相关资源
          最近更新 更多