1d8d2d382SMasahiro Yamada#!/usr/bin/env python3 251e46c7aSKees Cook# SPDX-License-Identifier: GPL-2.0+ 351e46c7aSKees Cook# 451e46c7aSKees Cook# This determines how many parallel tasks "make" is expecting, as it is 551e46c7aSKees Cook# not exposed via an special variables, reserves them all, runs a subprocess 651e46c7aSKees Cook# with PARALLELISM environment variable set, and releases the jobs back again. 751e46c7aSKees Cook# 851e46c7aSKees Cook# https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html#POSIX-Jobserver 951e46c7aSKees Cookfrom __future__ import print_function 1051e46c7aSKees Cookimport os, sys, errno 1151e46c7aSKees Cookimport subprocess 1251e46c7aSKees Cook 1398a499a1SJonathan Neuschäfer# Extract and prepare jobserver file descriptors from environment. 1451e46c7aSKees Cookclaim = 0 1551e46c7aSKees Cookjobs = b"" 1651e46c7aSKees Cooktry: 1751e46c7aSKees Cook # Fetch the make environment options. 1851e46c7aSKees Cook flags = os.environ['MAKEFLAGS'] 1951e46c7aSKees Cook 2051e46c7aSKees Cook # Look for "--jobserver=R,W" 2151e46c7aSKees Cook # Note that GNU Make has used --jobserver-fds and --jobserver-auth 2251e46c7aSKees Cook # so this handles all of them. 2351e46c7aSKees Cook opts = [x for x in flags.split(" ") if x.startswith("--jobserver")] 2451e46c7aSKees Cook 2551e46c7aSKees Cook # Parse out R,W file descriptor numbers and set them nonblocking. 26*f8f4dc76SMasahiro Yamada # If the MAKEFLAGS variable contains multiple instances of the 27*f8f4dc76SMasahiro Yamada # --jobserver-auth= option, the last one is relevant. 28*f8f4dc76SMasahiro Yamada fds = opts[-1].split("=", 1)[1] 2951e46c7aSKees Cook reader, writer = [int(x) for x in fds.split(",", 1)] 3051e46c7aSKees Cook # Open a private copy of reader to avoid setting nonblocking 3151e46c7aSKees Cook # on an unexpecting process with the same reader fd. 3251e46c7aSKees Cook reader = os.open("/proc/self/fd/%d" % (reader), 3351e46c7aSKees Cook os.O_RDONLY | os.O_NONBLOCK) 3451e46c7aSKees Cook 3551e46c7aSKees Cook # Read out as many jobserver slots as possible. 3651e46c7aSKees Cook while True: 3751e46c7aSKees Cook try: 3851e46c7aSKees Cook slot = os.read(reader, 8) 3951e46c7aSKees Cook jobs += slot 4051e46c7aSKees Cook except (OSError, IOError) as e: 4151e46c7aSKees Cook if e.errno == errno.EWOULDBLOCK: 4251e46c7aSKees Cook # Stop at the end of the jobserver queue. 4351e46c7aSKees Cook break 4451e46c7aSKees Cook # If something went wrong, give back the jobs. 4551e46c7aSKees Cook if len(jobs): 4651e46c7aSKees Cook os.write(writer, jobs) 4751e46c7aSKees Cook raise e 4851e46c7aSKees Cook # Add a bump for our caller's reserveration, since we're just going 4951e46c7aSKees Cook # to sit here blocked on our child. 5051e46c7aSKees Cook claim = len(jobs) + 1 5151e46c7aSKees Cookexcept (KeyError, IndexError, ValueError, OSError, IOError) as e: 5251e46c7aSKees Cook # Any missing environment strings or bad fds should result in just 5351e46c7aSKees Cook # not being parallel. 5451e46c7aSKees Cook pass 5551e46c7aSKees Cook 5651e46c7aSKees Cook# We can only claim parallelism if there was a jobserver (i.e. a top-level 5751e46c7aSKees Cook# "-jN" argument) and there were no other failures. Otherwise leave out the 5851e46c7aSKees Cook# environment variable and let the child figure out what is best. 5951e46c7aSKees Cookif claim > 0: 6051e46c7aSKees Cook os.environ['PARALLELISM'] = '%d' % (claim) 6151e46c7aSKees Cook 6251e46c7aSKees Cookrc = subprocess.call(sys.argv[1:]) 6351e46c7aSKees Cook 6451e46c7aSKees Cook# Return all the reserved slots. 6551e46c7aSKees Cookif len(jobs): 6651e46c7aSKees Cook os.write(writer, jobs) 6751e46c7aSKees Cook 6851e46c7aSKees Cooksys.exit(rc) 69