【问题标题】:Parallel make generates code multiple times并行 make 多次生成代码
【发布时间】:2014-11-07 13:05:47
【问题描述】:

让我们假设一个简单的项目由两个静态源和三个动态源组成,它们是生成的。 生成文件如下所示:

SRC_DIR = src
OUTPUT = output

TARGET = $(OUTPUT)/lib.so

GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
SRCS = static_1.c static_2.c $(GEN_SRCS)

OBJS = $(SRCS:%.c=$(OUTPUT)/%.o)

all: $(TARGET)

$(TARGET): $(OBJS)
    gcc -shared -o $@ $^

$(OUTPUT)/%.o: $(SRC_DIR)/$*%.c | $(OUTPUT)
    @echo "Creating object $@ from $^"
    gcc -c -o $@ $<

$(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
    @echo "---------------------------------------"
    @echo "Generating dynamic Cs"
    @echo "---------------------------------------"
    ./generate.sh $<

$(OUTPUT):
    mkdir -p $(OUTPUT)

.PHONY: clean
clean:
    rm -rf $(OUTPUT) $(SRC_DIR)/dynamic_1.c $(SRC_DIR)/dynamic_2.c $(SRC_DIR)/dynamic_3.c 

虽然只使用一个核心构建,但它构建得很好:

mkdir -p output
Creating object output/static_1.o from src/static_1.c
gcc -c -o output/static_1.o src/static_1.c
Creating object output/static_2.o from src/static_2.c
gcc -c -o output/static_2.o src/static_2.c
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
Creating object output/dynamic_1.o from src/dynamic_1.c
gcc -c -o output/dynamic_1.o src/dynamic_1.c
Creating object output/dynamic_2.o from src/dynamic_2.c
gcc -c -o output/dynamic_2.o src/dynamic_2.c
Creating object output/dynamic_3.o from src/dynamic_3.c
gcc -c -o output/dynamic_3.o src/dynamic_3.c
gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o
rm src/dynamic_1.c src/dynamic_2.c src/dynamic_3.c

但是,使用并行构建 (-j 2),dynamic_* 文件会生成两次:

mkdir -p output
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
Creating object output/static_1.o from src/static_1.c
gcc -c -o output/static_1.o src/static_1.c
Creating object output/static_2.o from src/static_2.c
gcc -c -o output/static_2.o src/static_2.c
Creating object output/dynamic_1.o from src/dynamic_1.c
gcc -c -o output/dynamic_1.o src/dynamic_1.c
Creating object output/dynamic_2.o from src/dynamic_2.c
gcc -c -o output/dynamic_2.o src/dynamic_2.c
Creating object output/dynamic_3.o from src/dynamic_3.c
gcc -c -o output/dynamic_3.o src/dynamic_3.c
gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o
rm src/dynamic_1.c src/dynamic_2.c src/dynamic_3.c

我应该如何更改makefile以仅生成一个文件? 线索可以在这里,但我还没有找到使用它的方法: multiple targets from one recipe and parallel execution

要下载的项目在这里:http://stuff.pitris.info/make.tar.gz

谢谢!

【问题讨论】:

    标签: makefile


    【解决方案1】:

    链接的答案就是答案。你的规则:

    $(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
        @echo "---------------------------------------"
        @echo "Generating dynamic Cs"
        @echo "---------------------------------------"
        ./generate.sh $<
    

    告诉 make 每次调用 generate.sh 都会创建一个 $(SRC_DIR)/dynamic_%.c 文件。然而,运行该脚本实际上会生成 多个 文件(但 make 不知道)。

    因此,当您运行 make 时,通常 make 会按顺序运行,当它看到第一个 dynamic_*.c 文件时,它会运行脚本来生成单个文件(但会生成所有文件)。当 make 然后去确定是否需要生成下一个dynamic_*.c 文件时,它会看到该文件已经存在并跳过生成它的规则。

    当您运行 make -j 时,make 会生成并行进程,而这些进程恰好同时命中一个 dynamic_*.c 文件,因此它们全部运行脚本(即使只有一个其中需要)。

    这就是您看到它运行N 次的原因。更改您的目标订单/等。并且您可能会看到更少的 generate.sh 运行,因为并行性的工作方式不同(您甚至可以设法让它只运行一次),但这都是巧合。

    正确的解决方案,如该链接的答案,是教通过列出该规则的多个模式来运行脚本来创建所有文件。或者,或者,修改generate.sh 以一次只生成一个文件(并始终运行它N 次)。


    执行多种模式的最简单(虽然有些丑陋)的方法可能是替换

    GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
    

    GEN_SRCS = %ynamic_1.c %ynamic_2.c %ynamic_3.c
    

    或者类似的,然后替换

    $(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
    

    $(GEN_SRCS): definition_for_generator.xml
    

    【讨论】:

      【解决方案2】:

      您的代码还有另一个问题:依赖于 $(OUTPUT) 目录。它永远不会按预期工作。

      以下makefile按预期工作,请尝试:

      SRC_DIR = src
      OUTPUT = output
      
      TARGET = $(OUTPUT)/lib.so
      
      GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
      SRCS = static_1.c static_2.c $(GEN_SRCS)
      
      ifeq ($(filter clean,$(MAKECMDGOALS)),)
      -include $(OUTPUT)/.timestamp
      endif
      
      OBJS = $(SRCS:%.c=$(OUTPUT)/%.o)
      
      mkdir_@ = $(if $(wildcard $(@D)),,mkdir -p $(@D))
      
      all: $(TARGET)
      
      $(TARGET): $(OBJS)
          gcc -shared -o $@ $^
      
      $(OUTPUT)/%.o: $(SRC_DIR)/$*%.c
          $(mkdir_@)
          @echo "Creating object $@ from $^"
          gcc -c -o $@ $<
      
      $(OUTPUT)/.timestamp: definition_for_generator.xml
          $(mkdir_@)
          @echo "---------------------------------------"
          @echo "Generating dynamic Cs"
          @echo "---------------------------------------"
          ./generate.sh $<
          touch $@
      
      .PHONY: clean
      clean:
          rm -rf $(OUTPUT) $(SRC_DIR)/dynamic_1.c $(SRC_DIR)/dynamic_2.c $(SRC_DIR)/dynamic_3.c 
      

      这个想法是注入对生成的空 makefile - $(OUTPUT)/.timestamp 的依赖。这是为了强制 GNU Make 重新读取你的 Makefile。生成 $(OUTPUT)/.timestamp 的规则运行您的源生成器。因此,当 GNU Make 第二次开始处理您的 Makefile 时,生成的源代码已经就位。因此,您不需要为它们定义任何依赖项,即它们就像您的常规源文件一样。

      提示:将生成的源代码写入源代码树并不是防止多个构建同时使用此源代码树的好主意。这是这种方法最明显的问题。

      另外看看 mkdir_@ 函数:如果它不存在,它将有效地为目标创建目录,首先尝试使用 GNU Make builtin 在生成之前检查目录是否存在 mkdir 进程。与 Cygwin 一起使用时会有所不同,因为与一流的 UNIX 系统相比,在这种环境中创建进程是非常耗时的操作。

      输出:

      $ make -j
      mkdir -p output
      ---------------------------------------
      Generating dynamic Cs
      ---------------------------------------
      ./generate.sh definition_for_generator.xml
      touch output/.timestamp
      Creating object output/static_1.o from src/static_1.c
      gcc -c -o output/static_1.o src/static_1.c
      Creating object output/static_2.o from src/static_2.c
      gcc -c -o output/static_2.o src/static_2.c
      Creating object output/dynamic_1.o from src/dynamic_1.c
      Creating object output/dynamic_2.o from src/dynamic_2.c
      gcc -c -o output/dynamic_1.o src/dynamic_1.c
      gcc -c -o output/dynamic_2.o src/dynamic_2.c
      Creating object output/dynamic_3.o from src/dynamic_3.c
      gcc -c -o output/dynamic_3.o src/dynamic_3.c
      gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-08-18
        • 1970-01-01
        • 2013-07-13
        • 1970-01-01
        • 2017-06-29
        • 2012-02-23
        • 2016-08-17
        相关资源
        最近更新 更多