makefile,目录树创建适合并行(-j)构建

dan*_*tel 8 build-process makefile build

我的项目需要使用mkdir -p在构建期间创建的临时目录,类似于:

all: dirtree $(OBJFILES)

dirtree: 
  @mkdir -p $(BUILD)/temp_directory
Run Code Online (Sandbox Code Playgroud)

但是这种方法不能与-j开关一起使用,因为在制作mkdir目标之前首先编译OBJFILES.

有没有标准的方法来做到这一点?

P S*_*ved 15

makefile的问题在于,创建目标文件不依赖于相关目录的创建(只有虚假的"所有"目标).这种依赖关系对于-j选项是必要的,即使没有它,你的makefile也只是偶然的.有两种(正确的)方法可以强制使用相关性.

目录作为单独的目标

您为目录创建创建了目标; 剩下的就是把它作为对象文件规则的先决条件:

$(BUILD)/temp_directory/%.o: %.c   |   dirtree
        $(CC) $^ -o $@
Run Code Online (Sandbox Code Playgroud)

管道符号|表示dirtree是"仅限订单的先决条件".当"dirtree"是先决条件时使用它,但是dirtree中的更改不会使目标文件无效,也不会影响编译命令的结果.

在这里使用"仅订购"先决条件非常重要.问题是dirtree每次Make调用都会重新生成目标.这将导致依赖于它的所有内容也被重新创建,因此它每次都会重建所有目标文件.

在shell命令中创建目录

另一种方法是确保在调用编译之前立即创建目录

$(BUILD)/temp_directory/%.o: %.c
        @mkdir -p $(@D)
        $(CC) $^ -o $@
Run Code Online (Sandbox Code Playgroud)

注意用法$(@D).这被扩展为"目标文件的目录".因此它可以在许多地方统一使用,甚至可以在变量的帮助下使用.

Mkdir=@mkdir -p $(@D)
$(BUILD)/temp_directory/%.o: %.c
        $(Mkdir)
        $(CC) $^ -o $@
$(INSTALL_DIR)/%: src_dir/%
        $(Mkdir)
        cp -p $^ $@
Run Code Online (Sandbox Code Playgroud)

两种方式都可确保在调用编译命令之前创建目录.两种方式都要求您在需要它的每个规则上写一些文本(| dirtree或者$(Mkdir)).两种方式都是-j兼容的,但第二种解决方案需要mkdir -p是线程安全的(因为两个这样的命令可能会尝试创建相同的目录,其中一个会失败).

虽然大多数系统mkdir -p在某些系统(例如某些Solaris系统)中以一种或多或少线程安全的方式实现它,但它们的线程安全性低于其他系统.但是,即使在GNU工具链中,mkdir -p如果它们同时调用相同的mkdir(2)库调用,也可能会失败.

如果你想要非常安全,你也可以这样做.可能是什么问题呢?这两个mkdir -p脚本尝试创建相同的目录,并在C库中的某个地方发生冲突.然后,其中一个mkdir-s将成功,另一个将失败.但是,如果mkdir您调用失败,那么只有当目录是由并发创建的时,它才可能与线程不安全相关mkdir.因此,只需检查mkdir调用后是否创建目标目录就足够了:

Mkdir=@mkdir -p $(@D) || test -d $(@D)
Run Code Online (Sandbox Code Playgroud)

(这个解决方案也存在模式问题:当目录存在时mkdir可能会失败,但是不符合umask,所以你可能也想检查它.但是我想这太多了.)

  • `mkdir -p`不是`-j`兼容的.执行`mkdir -p/a/b/c`竞赛的两个进程和一个使用EEXIST失败的进程.没有神秘的_internal文件系统locks_. (4认同)