diff options
author | Todd Gamblin <tgamblin@llnl.gov> | 2024-10-03 14:36:53 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-03 14:36:53 -0700 |
commit | 3c2a68287687be63dcdae84c146124cb15cbcb0a (patch) | |
tree | e7449f39ccc888e8c3ccdf74a90a16fc0281012f /lib | |
parent | a52ec2a9cc0e0916e6bdeecab1bc2a6385af007b (diff) | |
download | spack-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.py | 60 |
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) |