cmake 为什么sub-make只在被python而不是bash调用时显示jobserver警告

toiithl6  于 2023-05-29  发布在  Python
关注(0)|答案(1)|浏览(125)

假设我有一个包含ExternalProject目标的CMake项目。该目标的BUILD_COMMAND执行一个shell脚本。

# demo/CMakeLists.txt
cmake_minimum_required(VERSION 3.14.1)
project(demo)
include(ExternalProject)
ExternalProject_Add(demo
    SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
    CONFIGURE_COMMAND ""
    BUILD_COMMAND bash "${CMAKE_CURRENT_SOURCE_DIR}/test.sh"  
    INSTALL_COMMAND ""
    BUILD_ALWAYS TRUE
)

该脚本调用make:

# demo/test.sh
cd $(dirname "${BASH_SOURCE[0]}")
make

它执行一个简单的Makefile:

# demo/Makefile
demo:
    echo demo

这在并发构建exeternal项目时会产生以下警告消息(mkdir build && cd build && cmake .. && make -j8):

jobserver unavailable: using -j1. Add `+' to parent make rule.

我从manual中读到,如果一个规则包含$(MAKE),那么它会得到特殊处理。因此,我将$(MAKE)添加到BUILD_COMMAND的末尾,以尝试删除警告:
BUILD_COMMAND bash "${CMAKE_CURRENT_SOURCE_DIR}/test.sh" $(MAKE)
即使实际上没有使用新添加的参数,警告也会消失。
但是现在我想在python中重新实现bash脚本:

# demo.py
from pathlib import Path
import subprocess as sp
SCRIPT_DIR = Path(__file__).resolve().parent
sp.run(['make'], cwd=SCRIPT_DIR)

并相应地更改BUILD_COMMAND:
BUILD_COMMAND python3 "${CMAKE_CURRENT_SOURCE_DIR}/test.py" $(MAKE)
返回的警告带有或不带有最后一个参数$(MAKE),这是不期望的。
那么,如何解释这种不一致的行为呢?添加$(MAKE)参数是消除该警告的可接受的解决方法吗?为什么这种变通方法只适用于bash而不适用于python?
所涉及的真实的项目要比上面的演示复杂得多。但关键思想是一样的:一个外部项目执行bash脚本来构建调用make而没有警告的软件,然后所有内容都用python重写,警告出现了,我不知道如何让它们消失。
我还尝试了BUILD_COMMAND -> bash script -> python script -> make,即修改原来的bash脚本,通过python脚本调用make,而不是直接调用make。在添加额外的python层后出现警告。所以我想知道是否有一些python做或不做的事情使事情有所不同。当python调用子make时,是什么原因导致子make无法从父make接收到手册中提到的完整信息?
我还尝试在原始bash/python脚本中向make命令添加-j8参数。这两个脚本都显示了带有或不带有$(MAKE)参数的警告,只是这次的警告消息是-jN forced in submake: disabling jobserver mode,这似乎与手册中所说的相符,因此也符合预期。
我真的很想知道如何使python脚本的警告消失。

wnavrhmk

wnavrhmk1#

你应该使用subprocess.run(..., close_fds=False)。默认值是close_fds=True,根据Python的文档:
如果close_fds为true,则除了012之外的所有文件描述符都将在子进程执行之前关闭。
make打开一些内部FD,这些FD通常会继承到其子品牌,这些FD用于与其子品牌通信。使用subprocess.run()的默认close_fds=True,这些FD将被关闭,因此子make无法看到这些FD,因此父make无法与其子make通信。
Bash(我猜还有所有合理的shell)在启动子进程之前不会关闭FD。这就是为什么Bash和Python的subprocess.run()之间存在差异。
另一种解决方案是使用Python的os.system(),它“通过调用标准C函数system()实现”,它在内部简单地调用sh -c

相关问题