Makefile 递归Make传递错误-j参数

Makefile 递归Make传递错误-j参数,makefile,gnu-make,devtoolset,Makefile,Gnu Make,Devtoolset,我正在使用递归Makefile运行make(GNU make 3.82)。 我正在运行make-j2,以便只并行生成两个进程。 使用$(MAKE)调用内部Makefile 但是,看起来内部Makefile(由主Makefile启动)无限地生成进程,就好像它被赋予了-j,而不是-j2 为了验证这一点,我转储了子“make”的环境变量: MAKEFLAGS未在任何地方显式设置,-j仅在命令行中提供,并且不会出现在makefile中的任何位置。因此,在为子“make”编写MAKEFLAGS时,“mak

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

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

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

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

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


更新1 我已经发现了这个问题,但我仍然不明白为什么会发生,以及如何解决

问题是,当sub make在上下文中运行时,作业服务器无法正常工作。
这是必需的,因为我需要子make来使用特定的gcc工具链

SCL      = scl enable devtoolset-8
...
sub_make:
    $(SCL) "$(MAKE) -C $(SUB_MAKE_DIR) ... "
当这样运行时,子make生成无限多的作业。移除SCL后,它将按预期工作

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

更新2
这似乎与SCL改变环境变量有关。在新的
路径上,“make”较新(“GNU make 4.2.1”)


因此,如果顶级make运行的是旧的GNU make 3.82,而子make运行的是较新的4.2.1 make,则make作业服务器似乎会失败,这可能是因为这些版本之间make与子make通信的方式发生了变化。

这里没有什么问题。顶级make知道总共有多少个作业,并安排所有子make通过jobserver共享这些作业(这就是
MAKEFLAGS
中的
--jobserver fds
条目的作用)。下属不需要知道总共有多少工作,他们只需要知道如何询问是否可以开始一份新工作

在您正在使用的非常旧版本的GNU make中,从子make中无法知道此版本的具体
-j
编号

从GNU make 4.2开始,make将把特定的
-j
值添加到
MAKEFLAGS
中以供参考,即使它仍然没有被使用

编辑

我对scl及其工作原理一无所知。但是,GNU make jobserver通过在所有子制造商之间共享文件描述符来工作。如果这个
scl
工具干扰了这一点,比如强制关闭所有文件描述符,或者在docker映像中运行sub make,显然它无法访问这些共享文件描述符,或者类似的事情,然后,它显然无法与jobserver功能一起工作,您必须在scl中运行整个make

一个选项是不将
-j
放在外部make上,而是使用
-j
运行单个内部make,在
内部使用scl
您可以运行
make--打印数据库
并检查
-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"
#。无提示:
.PHONY:编译objs
TARGET=program.exe
CC=gcc
SOURCES=文件\u 1.c文件\u 2.c文件\u 3.c
OBJ_文件:=$(来源:.c=.o)
objs:$(OBJ_文件)
%.o:%.c
$(CC)$(标志)-c$<-o$@
全部:测试
#启用并行编译
汇编:
make-j${u处理器数}objs
链接:编译$(目标)
$(目标):$(OBJ_文件)
$(CC)$(标志)$(OBJ_文件)-o$@
测试:链接
#执行测试脚本
echo“执行测试脚本”
要执行的命令:进行测试
这将帮助您调试并检查是否存在gnu make问题或某些内部错误,或者make无法并行运行,因为它没有找到任何东西。我使用了
${NUMBER\u OF_PROCESSORS}
来使用所有可用的处理器,您可以根据需要更改它的值并测试不同的运行

编辑

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

那么为什么子make会产生无限多的作业,而不仅仅是2个并行作业?这里还是有点不对劲。当我直接启动子make时,它只生成2个作业(使用make-j2),只有在主make中运行子make时才会发生这种情况。这种情况的发生与在您的
MAKEFLAGS
中显示的未经修饰的
-j
无关,因此我们需要寻找其他地方。也许你的GNUMake版本有一个bug;您使用的是什么操作系统?您的版本是官方GNU make版本还是修补版本?你能用一个你可以贴在这里的小例子来重现这个“无限多的工作”吗?我们需要查看调用子make的规则,以及似乎同时构建两个以上目标的规则。好的,我想我找到了根本原因,但没有找到解决方案。请参阅问题的更新。SCL更改
路径
,使子品牌成为“品牌”的更新版本。Make jobserver似乎在不同的Make版本中出现故障。参见“更新2”。参见与SCL相关问题的更新。我不确定您为什么认为
ls# .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"