Merge pull request #7 in SCALE/spack from bugfix/SPACK-10-fork-on-install to develop
# By Todd Gamblin # Via Todd Gamblin * commit 'b4fddad7eff448adf701fc9e88cf02cd6e582f15': Fix for SPACK-10: Spack now forks before install()
This commit is contained in:
commit
15589754ec
3 changed files with 90 additions and 35 deletions
|
@ -86,7 +86,10 @@ def remove_path_for_spec(self, spec):
|
||||||
shutil.rmtree(path, True)
|
shutil.rmtree(path, True)
|
||||||
|
|
||||||
path = os.path.dirname(path)
|
path = os.path.dirname(path)
|
||||||
while not os.listdir(path) and path != self.root:
|
while path != self.root:
|
||||||
|
if os.path.isdir(path):
|
||||||
|
if os.listdir(path):
|
||||||
|
return
|
||||||
os.rmdir(path)
|
os.rmdir(path)
|
||||||
path = os.path.dirname(path)
|
path = os.path.dirname(path)
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,9 @@
|
||||||
rundown on spack and how it differs from homebrew, look at the
|
rundown on spack and how it differs from homebrew, look at the
|
||||||
README.
|
README.
|
||||||
"""
|
"""
|
||||||
import inspect
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import inspect
|
||||||
import subprocess
|
import subprocess
|
||||||
import platform as py_platform
|
import platform as py_platform
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
@ -387,8 +387,9 @@ def default_version(self):
|
||||||
try:
|
try:
|
||||||
return url.parse_version(self.__class__.url)
|
return url.parse_version(self.__class__.url)
|
||||||
except UndetectableVersionError:
|
except UndetectableVersionError:
|
||||||
tty.die("Couldn't extract a default version from %s. You " +
|
raise PackageError(
|
||||||
"must specify it explicitly in the package." % self.url)
|
"Couldn't extract a default version from %s." % self.url,
|
||||||
|
" You must specify it explicitly in the package file.")
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -490,7 +491,7 @@ def virtual_dependencies(self, visited=None):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def installed(self):
|
def installed(self):
|
||||||
return os.path.exists(self.prefix)
|
return os.path.isdir(self.prefix)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -544,8 +545,9 @@ def do_fetch(self):
|
||||||
raise ValueError("Can only fetch concrete packages.")
|
raise ValueError("Can only fetch concrete packages.")
|
||||||
|
|
||||||
if spack.do_checksum and not self.version in self.versions:
|
if spack.do_checksum and not self.version in self.versions:
|
||||||
tty.die("Cannot fetch %s@%s safely; there is no checksum on file for this "
|
raise ChecksumError(
|
||||||
"version." % (self.name, self.version),
|
"Cannot fetch %s safely; there is no checksum on file for version %s."
|
||||||
|
% self.version,
|
||||||
"Add a checksum to the package file, or use --no-checksum to "
|
"Add a checksum to the package file, or use --no-checksum to "
|
||||||
"skip this check.")
|
"skip this check.")
|
||||||
|
|
||||||
|
@ -557,8 +559,9 @@ def do_fetch(self):
|
||||||
if checker.check(self.stage.archive_file):
|
if checker.check(self.stage.archive_file):
|
||||||
tty.msg("Checksum passed for %s" % self.name)
|
tty.msg("Checksum passed for %s" % self.name)
|
||||||
else:
|
else:
|
||||||
tty.die("%s checksum failed for %s. Expected %s but got %s."
|
raise ChecksumError(
|
||||||
% (checker.hash_name, self.name, digest, checker.sum))
|
"%s checksum failed for %s." % checker.hash_name,
|
||||||
|
"Expected %s but got %s." % (self.name, digest, checker.sum))
|
||||||
|
|
||||||
|
|
||||||
def do_stage(self):
|
def do_stage(self):
|
||||||
|
@ -640,31 +643,55 @@ def do_install(self):
|
||||||
|
|
||||||
self.do_patch()
|
self.do_patch()
|
||||||
|
|
||||||
# create the install directory (allow the layout to handle this in
|
# Fork a child process to do the build. This allows each
|
||||||
# case it needs to add extra files)
|
# 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)
|
||||||
|
|
||||||
|
if pid == 0:
|
||||||
|
tty.msg("Building %s." % self.name)
|
||||||
|
|
||||||
|
# create the install directory (allow the layout to handle
|
||||||
|
# this in case it needs to add extra files)
|
||||||
spack.install_layout.make_path_for_spec(self.spec)
|
spack.install_layout.make_path_for_spec(self.spec)
|
||||||
|
|
||||||
tty.msg("Building %s." % self.name)
|
# Set up process's build environment before running install.
|
||||||
try:
|
|
||||||
build_env.set_build_environment_variables(self)
|
build_env.set_build_environment_variables(self)
|
||||||
build_env.set_module_variables_for_package(self)
|
build_env.set_module_variables_for_package(self)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Subclasses implement install() to do the build &
|
||||||
|
# install work.
|
||||||
self.install(self.spec, self.prefix)
|
self.install(self.spec, self.prefix)
|
||||||
if not os.path.isdir(self.prefix):
|
|
||||||
tty.die("Install failed for %s. No install dir created." % self.name)
|
if not os.listdir(self.prefix):
|
||||||
|
raise InstallError(
|
||||||
|
"Install failed for %s. Nothing was installed!"
|
||||||
|
% self.name)
|
||||||
|
|
||||||
|
# On successful install, remove the stage.
|
||||||
|
# Leave if if there is an error
|
||||||
|
self.stage.destroy()
|
||||||
|
|
||||||
tty.msg("Successfully installed %s" % self.name)
|
tty.msg("Successfully installed %s" % self.name)
|
||||||
print_pkg(self.prefix)
|
print_pkg(self.prefix)
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.remove_prefix()
|
self.remove_prefix()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
finally:
|
# Parent process just waits for the child to complete. If the
|
||||||
# Once the install is done, destroy the stage where we built it,
|
# child exited badly, assume it already printed an appropriate
|
||||||
# unless the user wants it kept around.
|
# message. Just make the parent exit with an error code.
|
||||||
if not self.dirty:
|
pid, returncode = os.waitpid(pid, 0)
|
||||||
self.stage.destroy()
|
if returncode != 0:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def do_install_dependencies(self):
|
def do_install_dependencies(self):
|
||||||
|
@ -684,16 +711,16 @@ def module(self):
|
||||||
|
|
||||||
def install(self, spec, prefix):
|
def install(self, spec, prefix):
|
||||||
"""Package implementations override this with their own build configuration."""
|
"""Package implementations override this with their own build configuration."""
|
||||||
tty.die("Packages must provide an install method!")
|
raise InstallError("Package %s provides no install method!" % self.name)
|
||||||
|
|
||||||
|
|
||||||
def do_uninstall(self):
|
def do_uninstall(self):
|
||||||
if not self.installed:
|
if not self.installed:
|
||||||
tty.die(self.name + " is not installed.")
|
raise InstallError(self.name + " is not installed.")
|
||||||
|
|
||||||
if not self.ignore_dependencies:
|
if not self.ignore_dependencies:
|
||||||
deps = self.installed_dependents
|
deps = self.installed_dependents
|
||||||
if deps: tty.die(
|
if deps: raise InstallError(
|
||||||
"Cannot uninstall %s. The following installed packages depend on it: %s"
|
"Cannot uninstall %s. The following installed packages depend on it: %s"
|
||||||
% (self.name, deps))
|
% (self.name, deps))
|
||||||
|
|
||||||
|
@ -817,7 +844,32 @@ def print_pkg(message):
|
||||||
print message
|
print message
|
||||||
|
|
||||||
|
|
||||||
class InvalidPackageDependencyError(spack.error.SpackError):
|
|
||||||
|
class FetchError(spack.error.SpackError):
|
||||||
|
"""Raised when something goes wrong during fetch."""
|
||||||
|
def __init__(self, message, long_msg=None):
|
||||||
|
super(FetchError, self).__init__(message, long_msg)
|
||||||
|
|
||||||
|
|
||||||
|
class ChecksumError(FetchError):
|
||||||
|
"""Raised when archive fails to checksum."""
|
||||||
|
def __init__(self, message, long_msg):
|
||||||
|
super(ChecksumError, self).__init__(message, long_msg)
|
||||||
|
|
||||||
|
|
||||||
|
class InstallError(spack.error.SpackError):
|
||||||
|
"""Raised when something goes wrong during install or uninstall."""
|
||||||
|
def __init__(self, message, long_msg=None):
|
||||||
|
super(InstallError, self).__init__(message, long_msg)
|
||||||
|
|
||||||
|
|
||||||
|
class PackageError(spack.error.SpackError):
|
||||||
|
"""Raised when something is wrong with a package definition."""
|
||||||
|
def __init__(self, message, long_msg=None):
|
||||||
|
super(PackageError, self).__init__(message, long_msg)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidPackageDependencyError(PackageError):
|
||||||
"""Raised when package specification is inconsistent with requirements of
|
"""Raised when package specification is inconsistent with requirements of
|
||||||
its dependencies."""
|
its dependencies."""
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
|
|
|
@ -93,6 +93,6 @@ def test_install_and_uninstall(self):
|
||||||
try:
|
try:
|
||||||
pkg.do_install()
|
pkg.do_install()
|
||||||
pkg.do_uninstall()
|
pkg.do_uninstall()
|
||||||
except:
|
except Exception, e:
|
||||||
if pkg: pkg.remove_prefix()
|
if pkg: pkg.remove_prefix()
|
||||||
raise
|
raise
|
||||||
|
|
Loading…
Reference in a new issue