diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py index cabde7dc86..84d2bd77ef 100644 --- a/lib/spack/spack/build_environment.py +++ b/lib/spack/spack/build_environment.py @@ -28,6 +28,7 @@ calls you can make from within the install() function. """ import os +import sys import shutil import multiprocessing import platform @@ -212,3 +213,58 @@ def setup_package(pkg): for dep_spec in pkg.spec.traverse(root=False): dep_spec.package.setup_dependent_environment( 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) diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index c48816cb5b..5d04fed8ff 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -804,32 +804,21 @@ def do_install(self, **kwargs): if not fake_install: self.do_patch() - # Fork a child process to do the build. This allows each - # package authors to have full control over their environment, - # etc. without offecting other builds that might be executed - # in the same spack call. - try: - pid = os.fork() - except OSError, e: - raise InstallError("Unable to fork build process: %s" % e) + # 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) - if pid == 0: + def real_work(): try: 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 # the directory is created. spack.hooks.pre_install(self) # Set up process's build environment before running install. self.stage.chdir_to_source() - build_env.setup_package(self) - if fake_install: self.do_fake_install() else: @@ -852,10 +841,6 @@ def do_install(self, **kwargs): % (_hms(self._fetch_time), _hms(build_time), _hms(self._total_time))) print_pkg(self.prefix) - # Use os._exit here to avoid raising a SystemExit exception, - # which interferes with unit tests. - os._exit(0) - except: if not keep_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." + "Manually remove this directory to fix:", self.prefix) + raise - # 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) - - # 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) + build_env.fork(self, real_work) # Once everything else is done, run post install hooks spack.hooks.post_install(self) - def _sanity_check_install(self): installed = set(os.listdir(self.prefix)) installed.difference_update(spack.install_layout.hidden_file_paths)