jobserver-count 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. #!/usr/bin/env python
  2. # SPDX-License-Identifier: GPL-2.0+
  3. #
  4. # This determines how many parallel tasks "make" is expecting, as it is
  5. # not exposed via an special variables.
  6. # https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html#POSIX-Jobserver
  7. from __future__ import print_function
  8. import os, sys, fcntl, errno
  9. # Default parallelism is "1" unless overridden on the command-line.
  10. default="1"
  11. if len(sys.argv) > 1:
  12. default=sys.argv[1]
  13. # Extract and prepare jobserver file descriptors from envirnoment.
  14. try:
  15. # Fetch the make environment options.
  16. flags = os.environ['MAKEFLAGS']
  17. # Look for "--jobserver=R,W"
  18. # Note that GNU Make has used --jobserver-fds and --jobserver-auth
  19. # so this handles all of them.
  20. opts = [x for x in flags.split(" ") if x.startswith("--jobserver")]
  21. # Parse out R,W file descriptor numbers and set them nonblocking.
  22. fds = opts[0].split("=", 1)[1]
  23. reader, writer = [int(x) for x in fds.split(",", 1)]
  24. # Open a private copy of reader to avoid setting nonblocking
  25. # on an unexpecting process with the same reader fd.
  26. reader = os.open("/proc/self/fd/%d" % (reader),
  27. os.O_RDONLY | os.O_NONBLOCK)
  28. except (KeyError, IndexError, ValueError, IOError, OSError) as e:
  29. print(e, file=sys.stderr)
  30. # Any missing environment strings or bad fds should result in just
  31. # using the default specified parallelism.
  32. print(default)
  33. sys.exit(0)
  34. # Read out as many jobserver slots as possible.
  35. jobs = b""
  36. while True:
  37. try:
  38. slot = os.read(reader, 1)
  39. jobs += slot
  40. except (OSError, IOError) as e:
  41. if e.errno == errno.EWOULDBLOCK:
  42. # Stop when reach the end of the jobserver queue.
  43. break
  44. raise e
  45. # Return all the reserved slots.
  46. os.write(writer, jobs)
  47. # If the jobserver was (impossibly) full or communication failed, use default.
  48. if len(jobs) < 1:
  49. print(default)
  50. sys.exit(0)
  51. # Report available slots (with a bump for our caller's reserveration).
  52. print(len(jobs) + 1)