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. 26f8f4dc76SMasahiro Yamada # If the MAKEFLAGS variable contains multiple instances of the 27f8f4dc76SMasahiro Yamada # --jobserver-auth= option, the last one is relevant. 28f8f4dc76SMasahiro Yamada fds = opts[-1].split("=", 1)[1] 29*8c008988SMartin Liska 30*8c008988SMartin Liska # Starting with GNU Make 4.4, named pipes are used for reader and writer. 31*8c008988SMartin Liska # Example argument: --jobserver-auth=fifo:/tmp/GMfifo8134 32*8c008988SMartin Liska _, _, path = fds.partition('fifo:') 33*8c008988SMartin Liska 34*8c008988SMartin Liska if path: 35*8c008988SMartin Liska reader = os.open(path, os.O_RDONLY | os.O_NONBLOCK) 36*8c008988SMartin Liska writer = os.open(path, os.O_WRONLY) 37*8c008988SMartin Liska else: 3851e46c7aSKees Cook reader, writer = [int(x) for x in fds.split(",", 1)] 3951e46c7aSKees Cook # Open a private copy of reader to avoid setting nonblocking 4051e46c7aSKees Cook # on an unexpecting process with the same reader fd. 4151e46c7aSKees Cook reader = os.open("/proc/self/fd/%d" % (reader), 4251e46c7aSKees Cook os.O_RDONLY | os.O_NONBLOCK) 4351e46c7aSKees Cook 4451e46c7aSKees Cook # Read out as many jobserver slots as possible. 4551e46c7aSKees Cook while True: 4651e46c7aSKees Cook try: 4751e46c7aSKees Cook slot = os.read(reader, 8) 4851e46c7aSKees Cook jobs += slot 4951e46c7aSKees Cook except (OSError, IOError) as e: 5051e46c7aSKees Cook if e.errno == errno.EWOULDBLOCK: 5151e46c7aSKees Cook # Stop at the end of the jobserver queue. 5251e46c7aSKees Cook break 5351e46c7aSKees Cook # If something went wrong, give back the jobs. 5451e46c7aSKees Cook if len(jobs): 5551e46c7aSKees Cook os.write(writer, jobs) 5651e46c7aSKees Cook raise e 5751e46c7aSKees Cook # Add a bump for our caller's reserveration, since we're just going 5851e46c7aSKees Cook # to sit here blocked on our child. 5951e46c7aSKees Cook claim = len(jobs) + 1 6051e46c7aSKees Cookexcept (KeyError, IndexError, ValueError, OSError, IOError) as e: 6151e46c7aSKees Cook # Any missing environment strings or bad fds should result in just 6251e46c7aSKees Cook # not being parallel. 6351e46c7aSKees Cook pass 6451e46c7aSKees Cook 6551e46c7aSKees Cook# We can only claim parallelism if there was a jobserver (i.e. a top-level 6651e46c7aSKees Cook# "-jN" argument) and there were no other failures. Otherwise leave out the 6751e46c7aSKees Cook# environment variable and let the child figure out what is best. 6851e46c7aSKees Cookif claim > 0: 6951e46c7aSKees Cook os.environ['PARALLELISM'] = '%d' % (claim) 7051e46c7aSKees Cook 7151e46c7aSKees Cookrc = subprocess.call(sys.argv[1:]) 7251e46c7aSKees Cook 7351e46c7aSKees Cook# Return all the reserved slots. 7451e46c7aSKees Cookif len(jobs): 7551e46c7aSKees Cook os.write(writer, jobs) 7651e46c7aSKees Cook 7751e46c7aSKees Cooksys.exit(rc) 78