【问题标题】:Recursive Make passes incorrect -j argument递归 Make 传递不正确的 -j 参数
【发布时间】:2021-08-07 15:16:17
【问题描述】:

我正在使用递归 Makefile 运行 make (GNU Make 3.82)。
我正在运行make -j2,以便仅并行生成 2 个进程。
使用 $(MAKE) 调用内部 Makefile。

但是,内部 Makefile(由主 Makefile 启动)似乎无限生成进程,就好像它被赋予 -j 而不是 -j2

为了验证这一点,我转储了子“make”的环境变量:

# pgrep -a make
17218 make -j2
17227 make -C obj_dir/ -f Vf1_package.mk ...

# strings /proc/17227/environ
...
MAKEFLAGS= --jobserver-fds=3,4 -j
...

MAKEFLAGS 没有在任何地方显式设置,-j 仅在命令行中提供,不会出现在 makefile 的任何地方。因此,在为子“make”编写 MAKEFLAGS 时,似乎“make”本身决定从 -j 参数中去除“2”。

知道什么可能导致“make”将MAKEFLAGS 设置为-j 而不是-j2


更新 1

我已经确定了问题,但我仍然不明白为什么会发生以及如何解决。

问题是当子make在SCL上下文下运行时,作业服务器不能正常工作。
这是必需的,因为我需要子制作才能使用特定的 gcc 工具链。

SCL      = scl enable devtoolset-8
...
sub_make:
    $(SCL) "$(MAKE) -C $(SUB_MAKE_DIR) ... "

当这样运行时,子制作会产生无限数量的作业。删除 SCL 后,它会按预期工作。

  • 为什么 SCL 会干扰 make 的作业服务器?
  • 我该如何解决这个问题?我知道我可以在运行外部 Makefile 之前启用 SCL,但我想从 Makefile 中控制工具集。

更新 2

似乎与SCL更改PATH环境变量有关。在新的PATH 上,“make”是较新的(“GNU Make 4.2.1”)。

因此,如果顶级 make 正在运行旧的 GNU Make 3.82 而子 make 正在运行较新的 4.2.1 make,那么 make 作业服务器似乎会失败,也许这些版本之间在 make 与子 make 通信的方式上发生了一些变化.

【问题讨论】:

  • 不知道你为什么认为ls的版本和make的版本有关系;它们是完全不同的工具。如果你想知道 make 的版本,你应该运行make --version。但是,是的,GNU make 3.82 已经很老了(十多年前发布),并且在 GNU make 4.2(五年前发布)中制作的作业服务器发生了变化。所以如果你想让它工作,你需要使用相同版本的 make。
  • @MadScientist 抱歉打错了,这是“GNU Make 4.2.1”,而不是 ls,在 SCL 更改了 PATH 之后。我已经解决了这个问题。对作业服务器的更改是问题的根源。这意味着不能在 make 中使用 SCL 以使用更新的工具链运行子 make。您是否有更多关于工作服务器变更性质的信息?
  • 它在 GNU make 的 NEWS 文件中。你可以在 Git 中看到最新的副本:git.savannah.gnu.org/cgit/make.git/tree/NEWS 在 4.2 版本信息中提到了它。基本上,jobserver 接口是作为其他工具可以使用的外部可见工具发布的(其他多线程 GNU 构建工具,如 GNU 链接器等也可以使用 jobserver 令牌,这样它们就不会压倒系统)。作为从仅内部可见设施迁移到外部可见设施的一部分,必须清理一些命名。

标签: makefile gnu-make devtoolset


【解决方案1】:

这里没有错。顶级制造商知道总共有多少工作,并安排所有子制造通过工作服务器共享这些工作(这就是MAKEFLAGS 中的--jobserver-fds 条目的用途)。子制作不需要知道总共有多少工作,他们只需要知道如何询问他们是否可以开始新工作。

在您使用的非常旧版本的 GNU make 中,没有办法,从子 make 中知道此版本的特定 -j 编号是什么。

从 GNU make 4.2 开始,make 会将特定的 -j 值添加到 MAKEFLAGS 以供参考,即使它仍未使用。

编辑

我对@9​​87654326@ 或其工作原理一无所知。但是,GNU make jobserver 通过在所有子 make 之间共享文件描述符来工作。如果这个scl 工具干扰了它,比如强制关闭所有文件描述符,或者在显然无法访问这些共享文件描述符的 docker 映像中运行 sub-make,或者类似的事情,那么它显然无法使用作业服务器功能,您必须在 scl 中运行整个 make。

一种选择是不要将-j 放在外部make 上,而是在scl 内部使用-j 运行单个内部make。

【讨论】:

  • 那么为什么 sub-make 会产生无限数量的工作,而不仅仅是 2 个并行呢?这里还是有问题。当我直接启动子制作时,它只会产生 2 个作业(使用 make -j2),这只发生在子制作从主制作中运行时。
  • 不可能发生这种情况。它发生的事实与出现在您的MAKEFLAGS 中的朴素的-j 无关,因此我们需要寻找其他地方。也许您的 GNU make 版本有错误;您使用什么操作系统?你的版本是官方的 GNU make 版本还是补丁版本?您可以在这里发布一个较小的示例来重现这个“无限数量的工作”吗?我们需要查看调用 sub-make 的规则,以及同时构建两个以上目标的规则。
  • 好的,我想我找到了根本原因,但没有找到解决方案。查看问题的更新。
  • SCL 更改PATH 使得子make 是“make”的更新版本。 Make jobserver 似乎在 make 版本之间失败。请参阅“更新 2”。
【解决方案2】:

您能否运行make --print-data-base 并检查您是否获得了正确的-j 值。

可以执行一个简单的测试示例,如下所示,您可以在其中测试 gnu make 是否能够并行编译多个文件以生成目标文件并给出正确的 -j 值:

# .SILENT:
.PHONY:compile objs
TARGET = program.exe
CC=gcc

SOURCES = file_1.c file_2.c file_3.c
OBJ_FILES:= $(SOURCES:.c=.o)


objs: $(OBJ_FILES)

%.o: %.c
    $(CC) $(FLAGS) -c $< -o $@

all: test 

# Enable parallel compilation
compile:
    make -j ${NUMBER_OF_PROCESSORS} objs

link : compile $(TARGET)

$(TARGET): $(OBJ_FILES)
    $(CC) $(FLAGS) $(OBJ_FILES) -o $@

test: link 
    # Execute test script
    echo "Executing test script"

要执行的命令:ma​​ke test
这将帮助您调试并检查是否存在 gnu-make 问题或某些内部错误或 make 无法并行运行,因为它没有找到任何东西。我已经使用${NUMBER_OF_PROCESSORS} 来使用所有可用的处理器,您可以根据需要更改它的值并测试不同的运行。

编辑
不幸的是,我不知道 sc1。如果 scl 是确定的根本原因,那么选项将在 sc1 内运行整个 make。或者通过在 sc1 中显式传递 -j2 来测试一次可能会很好,因为全局标志可能没有传递给 SC1。

【讨论】:

  • 查看 SCL 相关问题的更新
猜你喜欢
  • 1970-01-01
  • 2014-03-16
  • 2014-12-21
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 2013-03-14
相关资源
最近更新 更多