如何在使用mkdir时,在makefile中避免“目录已存在错误”?

123

我需要在我的makefile中生成一个目录,但我不想一遍又一遍地收到“目录已经存在”的错误提示,即使我可以轻松地忽略它。

我主要使用mingw/msys,但希望能够适用于其他shell/系统。

我尝试了这个方法,但没有成功,有什么其他建议吗?

ifeq (,$(findstring $(OBJDIR),$(wildcard $(OBJDIR) )))
-mkdir $(OBJDIR)
endif
12个回答

128

查看官方make文档,以下是一种好的做法:

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

$(OBJDIR)/%.o : %.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<

all: $(OBJS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
    mkdir -p $(OBJDIR)
你应该看到了这里使用了 | 管道操作符,它定义了一个仅限于顺序的先决条件。这意味着 $(OBJDIR) 目标应该存在(而不是更近)才能构建当前目标。
请注意,我使用了 mkdir -p。相比文档示例,加入了 -p 标志。查看其他答案以获取另一种选择。

1
@CraigMcQueen:我真正的意思是“$(OBJDIR)目标应该是存在的”。检查文件是否存在(和时间戳)以决定是否需要构建目标。因此,在这里,如果$(OBJDIR)不存在,它将构建它,以便在内部创建当前目标$(OBJS)之前,它是存在的 - ofavre
我想在找到这个方法之前,我已经尝试了解决这个问题的每种可能的组合(我正在尝试生成尽可能通用的makefile,适用于Windows / Linux) - 这几乎是唯一一个我能够让它正常工作的方法,但它确实非常有效!:) - code_fodder

127

在UNIX中,只需使用以下命令:

mkdir -p $(OBJDIR)

如果目录已存在,mkdir命令的-p选项可以避免错误提示。


39
一般而言,应当坚持使用这些程序广泛支持的选项和功能(通常是posix指定的)。例如,不要使用'mkdir -p',尽管它很方便,因为有些系统根本不支持它,在其他系统上也不安全进行并行执行。 - Grae Kindel
8
“-p” 在 Windows 上无效,这可能是该问题所询问的内容。不确定为什么会被接受。 - Antimony
2
对于Windows,请点赞此链接:https://connect.microsoft.com/PowerShell/feedback/details/1074131/mkdir-and-rm-needs-unix-like-most-pertinent-switches - vulcan raven
1
@Antimony 如果你在MinGW shell之外使用GNU Make,那么你可能做错了什么。不过这是你的选择。 - yyny

71

你可以使用测试命令:

test -d $(OBJDIR) || mkdir $(OBJDIR)

8
如果你需要让你的 makefile 在 Windows(带有 gnuwin32 和 PowerShell)和符合 POSIX 标准的操作系统中都能工作,那么这是首选方法。 - Kris Hardy
8
如果您安装了gnuwin32 CoreUtils软件包并提供了“test”实用工具,则可执行此操作。 - davenpcj
2
虽然已经晚了,但我想指出这个解决方案在并行构建(make -j)时可能会出现问题,因为存在一个竞争条件,即当目录被测试是否存在和创建之间的时间差。一个作业可以测试它不存在,但在它创建之前,另一个作业创建了该目录。然后当第一个作业尝试创建它时,它将失败,因为该目录已经存在。在这种情况下,最好的解决方案是使用仅顺序先决条件,如@TeKa的答案中所提到的(应该是被接受的答案)。 - C0deH4cker
@MarcusJ 在我的OS X El Capitan上可以工作。哪个版本破坏了它? - Franklin Yu
@C0deH4cker - 请原谅我的无知... TeKa是哪个答案?我想TeKa自从你发表评论以来已经改名了。 - jww

19

这是我在使用GNU make创建编译输出目录时使用的一个技巧。首先定义以下规则:

  %/.d:
          mkdir -p $(@D)
          touch $@

然后使所有进入该目录的文件都依赖于该目录中的 .d 文件:

 obj/%.o: %.c obj/.d
    $(CC) $(CFLAGS) -c -o $@ $<
注意使用 $< 而不是 $^。
最后防止 .d 文件被自动删除:
 .PRECIOUS: %/.d

跳过.d文件,直接依赖于目录将无法工作,因为每次在该目录中写入文件时,目录修改时间都会更新,这将导致每次调用make时强制重建。


1
啊,谢谢,我一直在尝试让这个东西工作。我不想在几个目标上使用“test -d foo || mkdir foo”,因为使用“make -j2”将同时测试它们,两个测试都会失败,然后两个进程将尝试创建目录,最后一个进程会失败。 - unhammer

13
如果目录已经存在对你来说不是问题的话,你可以为该命令重定向 stderr,从而消除错误信息:
-mkdir $(OBJDIR) 2>/dev/null

这样做的好处是它也可以在Windows上运行。不要忘记在命令前加“-”,这样make就不会退出。 - Michael Burr

11

在您的 makefile 内:

target:
    if test -d dir; then echo "hello world!"; else mkdir dir; fi

10

在Windows平台上

if not exist "$(OBJDIR)" mkdir $(OBJDIR)

在Unix | Linux上

if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi

这个是为Windows设计的。 - checksum
/bin/sh: 1: Syntax error: end of file unexpected (expecting "then") - wotanii
第一个版本适用于Windows,第二个选项可以在Linux上运行,就像@checksum所说的那样。 - Edgard Leal
首个 Windows 版本不是原子的,因此当另一个进程(如并行模式下的规则)在“不存在”和“mkdir”之间创建目录时,它无法工作。 - Petr Vepřek

7
ifeq "$(wildcard $(MY_DIRNAME) )" ""
  -mkdir $(MY_DIRNAME)
endif

这对 mingw 的 make 功能非常适用,而不需要额外的工具。 - davenpcj

6
$(OBJDIR):
    mkdir $@

这也适用于多个目录,例如...
OBJDIRS := $(sort $(dir $(OBJECTS)))

$(OBJDIRS):
    mkdir $@

$(OBJDIR)作为第一个目标添加可以很好地工作。


3

它可以在mingw32/msys/cygwin/linux环境下运行。

ifeq "$(wildcard .dep)" ""
-include $(shell mkdir .dep) $(wildcard .dep/*)
endif

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