summaryrefslogtreecommitdiff
path: root/zephyr/zmake/zmake/jobserver.py
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/zmake/zmake/jobserver.py')
-rw-r--r--zephyr/zmake/zmake/jobserver.py149
1 files changed, 18 insertions, 131 deletions
diff --git a/zephyr/zmake/zmake/jobserver.py b/zephyr/zmake/zmake/jobserver.py
index c1c9538b35..a3d6287da2 100644
--- a/zephyr/zmake/zmake/jobserver.py
+++ b/zephyr/zmake/zmake/jobserver.py
@@ -3,17 +3,13 @@
# found in the LICENSE file.
"""Module for job counters, limiting the amount of concurrent executions."""
-import fcntl
-import functools
import logging
import multiprocessing
import os
import re
import select
-import selectors
import shlex
import subprocess
-import sys
import zmake
@@ -30,8 +26,7 @@ class JobHandle:
pass
def __exit__(self, exc_type, exc_value, traceback):
- if self.release_func:
- self.release_func(*self.args, **self.kwargs)
+ self.release_func(*self.args, **self.kwargs)
class JobClient:
@@ -46,16 +41,6 @@ class JobClient:
"""Get the environment variables necessary to share the job server."""
return {}
- @staticmethod
- def is_sequential():
- """Returns True if the jobserver is using -j1."""
- return False
-
- @staticmethod
- def pass_fds():
- """Returns the file descriptors that should be passed to subprocesses."""
- return []
-
def popen(self, argv, **kwargs):
"""Start a process using subprocess.Popen
@@ -68,9 +53,7 @@ class JobClient:
# the bare minimum (PATH only). This prevents us from building obscure
# dependencies on the environment variables.
kwargs.setdefault("env", {"PATH": "/bin:/usr/bin"})
- kwargs.setdefault("pass_fds", [])
kwargs["env"].update(self.env())
- kwargs["pass_fds"] += self.pass_fds()
logger = logging.getLogger(self.__class__.__name__)
logger.debug(
@@ -79,53 +62,17 @@ class JobClient:
" " if kwargs["env"] else "",
zmake.util.repr_command(argv),
)
- return subprocess.Popen( # pylint:disable=consider-using-with
- argv, **kwargs
- )
+ return subprocess.Popen(argv, **kwargs)
class GNUMakeJobClient(JobClient):
- """A job client for GNU make.
+ """A job client for GNU make."""
- A client of jobserver is allowed to run 1 job without contacting the
- jobserver, so maintain an optional self._internal_pipe to hold that
- job.
- """
-
- def __init__(self, inheritable_pipe, jobs, internal_jobs=0, makeflags=None):
- self._makeflags = makeflags
- self._inheritable_pipe = inheritable_pipe
- self.jobs = jobs
- self._selector = selectors.DefaultSelector()
- if internal_jobs:
- self._internal_pipe = os.pipe()
- os.write(self._internal_pipe[1], b"+" * internal_jobs)
- os.set_blocking(self._internal_pipe[0], False)
- self._selector.register(
- self._internal_pipe[0],
- selectors.EVENT_READ,
- self._internal_pipe[1],
- )
- else:
- self._internal_pipe = None
- if self._inheritable_pipe is not None:
- os.set_blocking(self._inheritable_pipe[0], False)
- self._selector.register(
- self._inheritable_pipe[0],
- selectors.EVENT_READ,
- self._inheritable_pipe[1],
- )
-
- def __del__(self):
- if self._inheritable_pipe:
- os.close(self._inheritable_pipe[0])
- os.close(self._inheritable_pipe[1])
- if self._internal_pipe:
- os.close(self._internal_pipe[0])
- os.close(self._internal_pipe[1])
+ def __init__(self, read_fd, write_fd):
+ self._pipe = [read_fd, write_fd]
@classmethod
- def from_environ(cls, env=None, jobs=0):
+ def from_environ(cls, env=None):
"""Create a job client from an environment with the MAKEFLAGS variable.
If we are started under a GNU Make Job Server, we can search
@@ -134,57 +81,22 @@ class GNUMakeJobClient(JobClient):
respectively. If we don't find this environment variable (or
the string inside of it), this will raise an OSError.
- The specification for MAKEFLAGS is:
- * If the first char is "n", this is a dry run, just exit.
- * If the flags contains -j1, go to sequential mode.
- * If the flags contains --jobserver-auth=R,W AND those file
- descriptors are valid, use the jobserver. Otherwise output a
- warning.
-
Args:
env: Optionally, the environment to search.
- jobs: The number of jobs set by the user on the command line.
Returns:
- A GNUMakeJobClient configured appropriately or None if there is
- no MAKEFLAGS environment variable.
+ A GNUMakeJobClient configured appropriately.
"""
if env is None:
env = os.environ
makeflags = env.get("MAKEFLAGS")
if not makeflags:
- return None
+ raise OSError("MAKEFLAGS is not set in the environment")
match = re.search(r"--jobserver-auth=(\d+),(\d+)", makeflags)
- if match:
- pipe = [int(x) for x in match.groups()]
- if jobs:
- pipe = None
- logging.warning(
- "-jN forced on command line; ignoring GNU make jobserver"
- )
- else:
- try:
- # Use F_GETFD to see if file descriptors are valid
- fcntl.fcntl(pipe[0], fcntl.F_GETFD)
- fcntl.fcntl(pipe[1], fcntl.F_GETFD)
- logging.info("using GNU make jobserver")
- except OSError:
- pipe = None
- logging.warning(
- "No file descriptors; ignoring GNU make jobserver"
- )
- else:
- pipe = None
- if not jobs:
- match = re.search(r"-j(\d+)", makeflags)
- if match:
- jobs = int(match.group(1))
- if jobs == 1:
- logging.info("Running in sequential mode (-j1)")
- if makeflags[0] == "n":
- logging.info("MAKEFLAGS contained dry-run flag")
- sys.exit(0)
- return cls(pipe, jobs, internal_jobs=1, makeflags=makeflags)
+ if not match:
+ raise OSError("MAKEFLAGS did not contain jobserver flags")
+ read_fd, write_fd = map(int, match.groups())
+ return cls(read_fd, write_fd)
def get_job(self):
"""Claim a job.
@@ -192,38 +104,12 @@ class GNUMakeJobClient(JobClient):
Returns:
A JobHandle object.
"""
- while True:
- ready_items = self._selector.select()
- if len(ready_items) > 0:
- read_fd = ready_items[0][0].fd
- write_fd = ready_items[0][0].data
- try:
- byte = os.read(read_fd, 1)
- return JobHandle(
- functools.partial(os.write, write_fd, byte)
- )
- except BlockingIOError:
- pass
+ byte = os.read(self._pipe[0], 1)
+ return JobHandle(lambda: os.write(self._pipe[1], byte))
def env(self):
"""Get the environment variables necessary to share the job server."""
- if self._makeflags:
- return {"MAKEFLAGS": self._makeflags}
- flag = ""
- if self.jobs:
- flag += f" -j{self.jobs}"
- if self.jobs != 1 and self._inheritable_pipe is not None:
- flag += " --jobserver-auth={},{}".format(*self._inheritable_pipe)
- return {"MAKEFLAGS": flag}
-
- def is_sequential(self):
- return self.jobs == 1
-
- def pass_fds(self):
- """Returns the file descriptors that should be passed to subprocesses."""
- if self.jobs != 1 and self._inheritable_pipe is not None:
- return self._inheritable_pipe
- return []
+ return {"MAKEFLAGS": "--jobserver-auth={},{}".format(*self._pipe)}
class GNUMakeJobServer(GNUMakeJobClient):
@@ -234,10 +120,11 @@ class GNUMakeJobServer(GNUMakeJobClient):
"""
def __init__(self, jobs=0):
+ [read_fd, write_fd] = os.pipe()
+ super().__init__(read_fd, write_fd)
if not jobs:
jobs = multiprocessing.cpu_count()
elif jobs > select.PIPE_BUF:
jobs = select.PIPE_BUF
- super().__init__(os.pipe(), jobs)
- os.write(self._inheritable_pipe[1], b"+" * jobs)
+ os.write(self._pipe[1], b"+" * jobs)