【问题标题】:How to create a directory in a makefile when mkdir -p is not available?当 mkdir -p 不可用时如何在 makefile 中创建目录?
【发布时间】:2011-07-08 18:47:35
【问题描述】:

我有一个 makefile,它可以创建通常的目录:

$(Release_target_OBJDIR)/%.o: %.cpp
     mkdir -p $(dir $@)
     $(COMPILE.cpp) $< $(CFLAGS) $(INCLUDES) -o $@

不幸的是,当我在 scratchbox2 下运行它时,mkdir -p 命令总是默默地失败。

我尝试了以下无效的方法:

$(Release_target_OBJDIR)/%.o: %.cpp
    mkdir $(dir $(dir $(dir $@)))
    mkdir $(dir $(dir $@))
    mkdir $(dir $@)
    $(COMPILE.cpp) $< $(CFLAGS) $(INCLUDES) -o $@

这个输出:

mkdir -p /home/foo/projects/htc/arm/obj/cbar/release/                  
mkdir -p /home/foo/projects/htc/arm/obj/cbar/release/                  
mkdir -p /home/foo/projects/htc/arm/obj/cbar/release/  

...结尾的斜杠阻止 dir 函数以我想要的方式剥离最后一个目录。

没有编写脚本或小型 C 应用程序来复制“-p”功能,有人对在 makefile 中创建子目录有任何想法吗?

如果没有 -p 选项,当 makefile 尝试创建一个已经存在的目录时,mkdir 将给出错误。我可以做 mkdir blah 2> /dev/null 但我可能会丢失其他错误消息。

有没有人知道为什么 mkdir -p 在 scratchbox2 下不起作用?

编辑

根据bobbogo 的建议,我把它放在一起。它看起来相当复杂,但似乎可以工作,即使在 scratchbox2 下也是如此。

# Generic variables for use in functions
comma:= ,
empty:=
space:= $(empty) $(empty)

# Make directory function
forlooprange = $(wordlist 1,$(words $1),1 2 3 4 5 6 7 8 9 10)
forloop = $(foreach n,$(call forlooprange,$1),$(call $2,$n,$3))
mkdirfunc0 = test -d $1 || mkdir $1;
mkdirfunc1 = $(call mkdirfunc0,/$(subst $(space),/,$(foreach n,$(wordlist 1,$1,$2),$n)))
mkdirfunc2 = $(call forloop,$1,mkdirfunc1,$1)
mkdirmain = $(call mkdirfunc2,$(subst /, ,$1))

.PRECIOUS: %/.sentinel  
%/.sentinel:
    $(call mkdirmain,$*)
    touch $@

【问题讨论】:

    标签: gnu-make mkdir scratchbox


    【解决方案1】:

    您可以使用- 告诉make 忽略来自命令的任何失败返回代码:

    $(Release_target_OBJDIR)/%.o: %.cpp
        -mkdir $(dir $(dir $(dir $@)))
        -mkdir $(dir $(dir $@))
        -mkdir $(dir $@)
        $(COMPILE.cpp) $< $(CFLAGS) $(INCLUDES) -o $@
    

    (请注意,这并不能解决尾部斜杠问题。)

    【讨论】:

      【解决方案2】:

      你可以用这个替换你的mkdirs 森林:

      $(Release_target_OBJDIR)/%.o: %.cpp
          $(foreach d,$(subst /, ,${@D}),mkdir $d && cd $d && ):
          ∶
      

      这将创建一个类似这样的 shell 命令:

      mkdir projects && cd projects && mkdir htc && cd htc && mkdir arm && cd arm && :
      

      这会在每次编译时运行。不是很优雅。您可以通过使用某种哨兵文件来优化它。例如:

      $(Release_target_OBJDIR)/%.o: %.cpp ${Release_target_OBJDIR}/.sentinel
          ∶
      
      %/.sentinel:
          $(foreach d,$(subst /, ,$*),mkdir $d && cd $d && ):
          touch $@
      

      .sentinel 在所有对象之前创建一次,并且对make -j 友好。事实上,即使mkdir -p 对您有用,您也应该这样做(在这种情况下,您将使用mkdir -p 而不是$(foreach) hack解决方案)。

      【讨论】:

      • 谢谢。一件奇怪的事情:我在链接阶段和编译阶段都添加了一个哨兵,以创建输出目录。这(以某种方式)触发了 make 文件为那些哨兵生成 rm 命令,但不是编译时的命令。我在哨兵规则中添加了一个“触摸”来创建哨兵。 shell 命令不太正确,但您已经给了我足够的信息来让事情顺利进行。
      • @Simon:啊,是的,我添加了touch,这样我就不会显得太笨了。 TVM。
      • @Simon:您可能需要.PRECIOUS.SECONDARY 的某种组合。您还可以将哨兵模式规则变成静态模式规则(耶!)。
      • @bobbogo:谢谢,.PRECIOUS 完成了这项业务,作为奖励,我找到了 gnu make docs 的正确部分,以了解有关中间目标的所有信息。
      • @bobbogo 你能扩展'make -j'-友好性吗?我目前在每次编译中都使用 mkdir -p,但我得到了这个工作:gist.github.com/zeux/8906196。从简单的角度来看,我更喜欢旧方法。
      猜你喜欢
      • 2020-02-03
      • 2020-05-08
      • 2014-11-19
      • 1970-01-01
      • 1970-01-01
      • 2013-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多