Factor out forking logic to build_environment.py.

This commit is contained in:
Todd Gamblin 2015-02-16 20:38:22 -08:00
parent 614c22fc1b
commit e6b2c27011
2 changed files with 63 additions and 32 deletions

View File

@ -28,6 +28,7 @@
calls you can make from within the install() function. calls you can make from within the install() function.
""" """
import os import os
import sys
import shutil import shutil
import multiprocessing import multiprocessing
import platform import platform
@ -212,3 +213,58 @@ def setup_package(pkg):
for dep_spec in pkg.spec.traverse(root=False): for dep_spec in pkg.spec.traverse(root=False):
dep_spec.package.setup_dependent_environment( dep_spec.package.setup_dependent_environment(
pkg.module, dep_spec, pkg.spec) pkg.module, dep_spec, pkg.spec)
def fork(pkg, function):
"""Fork a child process to do part of a spack build.
Arguments:
pkg -- pkg whose environemnt we should set up the
forked process for.
function -- arg-less function to run in the child process.
Usage:
def child_fun():
# do stuff
build_env.fork(pkg, child_fun)
Forked processes are run with the build environemnt set up by
spack.build_environment. This allows package authors to have
full control over the environment, etc. without offecting
other builds that might be executed in the same spack call.
If something goes wrong, the child process is expected toprint
the error and the parent process will exit with error as
well. If things go well, the child exits and the parent
carries on.
"""
try:
pid = os.fork()
except OSError, e:
raise InstallError("Unable to fork build process: %s" % e)
if pid == 0:
# Give the child process the package's build environemnt.
setup_package(pkg)
try:
# call the forked function.
function()
# Use os._exit here to avoid raising a SystemExit exception,
# which interferes with unit tests.
os._exit(0)
except:
# Child doesn't raise or return to main spack code.
# Just runs default exception handler and exits.
sys.excepthook(*sys.exc_info())
os._exit(1)
else:
# Parent process just waits for the child to complete. If the
# child exited badly, assume it already printed an appropriate
# message. Just make the parent exit with an error code.
pid, returncode = os.waitpid(pid, 0)
if returncode != 0:
sys.exit(1)

View File

@ -804,32 +804,21 @@ def do_install(self, **kwargs):
if not fake_install: if not fake_install:
self.do_patch() self.do_patch()
# Fork a child process to do the build. This allows each # create the install directory. The install layout
# package authors to have full control over their environment, # handles this in case so that it can use whatever
# etc. without offecting other builds that might be executed # package naming scheme it likes.
# in the same spack call. spack.install_layout.make_path_for_spec(self.spec)
try:
pid = os.fork()
except OSError, e:
raise InstallError("Unable to fork build process: %s" % e)
if pid == 0: def real_work():
try: try:
tty.msg("Building %s." % self.name) tty.msg("Building %s." % self.name)
# create the install directory. The install layout
# handles this in case so that it can use whatever
# package naming scheme it likes.
spack.install_layout.make_path_for_spec(self.spec)
# Run the pre-install hook in the child process after # Run the pre-install hook in the child process after
# the directory is created. # the directory is created.
spack.hooks.pre_install(self) spack.hooks.pre_install(self)
# Set up process's build environment before running install. # Set up process's build environment before running install.
self.stage.chdir_to_source() self.stage.chdir_to_source()
build_env.setup_package(self)
if fake_install: if fake_install:
self.do_fake_install() self.do_fake_install()
else: else:
@ -852,10 +841,6 @@ def do_install(self, **kwargs):
% (_hms(self._fetch_time), _hms(build_time), _hms(self._total_time))) % (_hms(self._fetch_time), _hms(build_time), _hms(self._total_time)))
print_pkg(self.prefix) print_pkg(self.prefix)
# Use os._exit here to avoid raising a SystemExit exception,
# which interferes with unit tests.
os._exit(0)
except: except:
if not keep_prefix: if not keep_prefix:
# If anything goes wrong, remove the install prefix # If anything goes wrong, remove the install prefix
@ -865,24 +850,14 @@ def do_install(self, **kwargs):
"Spack will think this package is installed." + "Spack will think this package is installed." +
"Manually remove this directory to fix:", "Manually remove this directory to fix:",
self.prefix) self.prefix)
raise
# Child doesn't raise or return to main spack code. build_env.fork(self, real_work)
# Just runs default exception handler and exits.
sys.excepthook(*sys.exc_info())
os._exit(1)
# Parent process just waits for the child to complete. If the
# child exited badly, assume it already printed an appropriate
# message. Just make the parent exit with an error code.
pid, returncode = os.waitpid(pid, 0)
if returncode != 0:
sys.exit(1)
# Once everything else is done, run post install hooks # Once everything else is done, run post install hooks
spack.hooks.post_install(self) spack.hooks.post_install(self)
def _sanity_check_install(self): def _sanity_check_install(self):
installed = set(os.listdir(self.prefix)) installed = set(os.listdir(self.prefix))
installed.difference_update(spack.install_layout.hidden_file_paths) installed.difference_update(spack.install_layout.hidden_file_paths)