summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2024-10-03 14:36:53 -0700
committerGitHub <noreply@github.com>2024-10-03 14:36:53 -0700
commit3c2a68287687be63dcdae84c146124cb15cbcb0a (patch)
treee7449f39ccc888e8c3ccdf74a90a16fc0281012f /lib
parenta52ec2a9cc0e0916e6bdeecab1bc2a6385af007b (diff)
downloadspack-3c2a68287687be63dcdae84c146124cb15cbcb0a.tar.gz
spack-3c2a68287687be63dcdae84c146124cb15cbcb0a.tar.bz2
spack-3c2a68287687be63dcdae84c146124cb15cbcb0a.tar.xz
spack-3c2a68287687be63dcdae84c146124cb15cbcb0a.zip
Better docs and typing for `_setup_pkg_and_run` (#46709)
There was a bit of mystery surrounding the arguments for `_setup_pkg_and_run`. It passes two file descriptors for handling the `gmake`'s job server in child processes, but they are unsed in the method. It turns out that there are good reasons to do this -- depending on the multiprocessing backend, these file descriptors may be closed in the child if they're not passed directly to it. - [x] Document all args to `_setup_pkg_and_run`. - [x] Document all arguments to `_setup_pkg_and_run`. - [x] Add type hints for `_setup_pkg_and_run`. - [x] Refactor exception handling in `_setup_pkg_and_run` so it's easier to add type hints. `exc_info()` was problematic because it *can* return `None` (just not in the context where it's used). `mypy` was too dumb to notice this. Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/build_environment.py60
1 files changed, 52 insertions, 8 deletions
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index 73f5d438a2..4d35172651 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -44,7 +44,7 @@ import types
from collections import defaultdict
from enum import Flag, auto
from itertools import chain
-from typing import Dict, List, Optional, Set, Tuple
+from typing import Callable, Dict, List, Optional, Set, Tuple
import archspec.cpu
@@ -1168,8 +1168,51 @@ def get_cmake_prefix_path(pkg):
def _setup_pkg_and_run(
- serialized_pkg, function, kwargs, write_pipe, input_multiprocess_fd, jsfd1, jsfd2
+ serialized_pkg: "spack.subprocess_context.PackageInstallContext",
+ function: Callable,
+ kwargs: Dict,
+ write_pipe: multiprocessing.connection.Connection,
+ input_multiprocess_fd: Optional[MultiProcessFd],
+ jsfd1: Optional[MultiProcessFd],
+ jsfd2: Optional[MultiProcessFd],
):
+ """Main entry point in the child process for Spack builds.
+
+ ``_setup_pkg_and_run`` is called by the child process created in
+ ``start_build_process()``, and its main job is to run ``function()`` on behalf of
+ some Spack installation (see :ref:`spack.installer.PackageInstaller._install_task`).
+
+ The child process is passed a ``write_pipe``, on which it's expected to send one of
+ the following:
+
+ * ``StopPhase``: error raised by a build process indicating it's stopping at a
+ particular build phase.
+
+ * ``BaseException``: any exception raised by a child build process, which will be
+ wrapped in ``ChildError`` (which adds a bunch of debug info and log context) and
+ raised in the parent.
+
+ * The return value of ``function()``, which can be anything (except an exception).
+ This is returned to the caller.
+
+ Note: ``jsfd1`` and ``jsfd2`` are passed solely to ensure that the child process
+ does not close these file descriptors. Some ``multiprocessing`` backends will close
+ them automatically in the child if they are not passed at process creation time.
+
+ Arguments:
+ serialized_pkg: Spack package install context object (serialized form of the
+ package that we'll build in the child process).
+ function: function to call in the child process; serialized_pkg is passed to
+ this as the first argument.
+ kwargs: additional keyword arguments to pass to ``function()``.
+ write_pipe: multiprocessing ``Connection`` to the parent process, to which the
+ child *must* send a result (or an error) back to parent on.
+ input_multiprocess_fd: stdin from the parent (not passed currently on Windows)
+ jsfd1: gmake Jobserver file descriptor 1.
+ jsfd2: gmake Jobserver file descriptor 2.
+
+ """
+
context: str = kwargs.get("context", "build")
try:
@@ -1195,13 +1238,14 @@ def _setup_pkg_and_run(
# Do not create a full ChildError from this, it's not an error
# it's a control statement.
write_pipe.send(e)
- except BaseException:
+ except BaseException as e:
# catch ANYTHING that goes wrong in the child process
- exc_type, exc, tb = sys.exc_info()
# Need to unwind the traceback in the child because traceback
# objects can't be sent to the parent.
- tb_string = traceback.format_exc()
+ exc_type = type(e)
+ tb = e.__traceback__
+ tb_string = traceback.format_exception(exc_type, e, tb)
# build up some context from the offending package so we can
# show that, too.
@@ -1218,8 +1262,8 @@ def _setup_pkg_and_run(
elif context == "test":
logfile = os.path.join(pkg.test_suite.stage, pkg.test_suite.test_log_name(pkg.spec))
- error_msg = str(exc)
- if isinstance(exc, (spack.multimethod.NoSuchMethodError, AttributeError)):
+ error_msg = str(e)
+ if isinstance(e, (spack.multimethod.NoSuchMethodError, AttributeError)):
process = "test the installation" if context == "test" else "build from sources"
error_msg = (
"The '{}' package cannot find an attribute while trying to {}. "
@@ -1229,7 +1273,7 @@ def _setup_pkg_and_run(
"More information at https://spack.readthedocs.io/en/latest/packaging_guide.html#installation-procedure"
).format(pkg.name, process, context)
error_msg = colorize("@*R{{{}}}".format(error_msg))
- error_msg = "{}\n\n{}".format(str(exc), error_msg)
+ error_msg = "{}\n\n{}".format(str(e), error_msg)
# make a pickleable exception to send to parent.
msg = "%s: %s" % (exc_type.__name__, error_msg)