package : added hooks for generic phases

This commit is contained in:
alalazo 2016-07-07 12:03:43 +02:00
parent 069de3f008
commit a36f3764af
2 changed files with 92 additions and 72 deletions

View file

@ -176,10 +176,10 @@
# TODO: it's not clear where all the stuff that needs to be included in packages
# should live. This file is overloaded for spack core vs. for packages.
#
__all__ = ['Package', 'StagedPackage', 'CMakePackage', \
__all__ = ['Package', 'CMakePackage', \
'Version', 'when', 'ver']
from spack.package import Package, ExtensionConflictError
from spack.package import StagedPackage, CMakePackage
from spack.package import CMakePackage
from spack.version import Version, ver
from spack.multimethod import when

View file

@ -875,7 +875,7 @@ def _resource_stage(self, resource):
resource_stage_folder = '-'.join(pieces)
return resource_stage_folder
install_phases = set(['configure', 'build', 'install', 'provenance'])
_phases = ['install', 'create_spack_logs']
def do_install(self,
keep_prefix=False,
keep_stage=False,
@ -887,7 +887,7 @@ def do_install(self,
fake=False,
explicit=False,
dirty=False,
install_phases = install_phases):
allowed_phases=None):
"""Called by commands to install a package and its dependencies.
Package implementations should override install() to describe
@ -907,6 +907,10 @@ def do_install(self,
make_jobs -- Number of make jobs to use for install. Default is ncpus
run_tests -- Runn tests within the package's install()
"""
# FIXME : we need a better semantic
if allowed_phases is None:
allowed_phases = self._phases
if not self.spec.concrete:
raise ValueError("Can only install concrete packages.")
@ -917,7 +921,8 @@ def do_install(self,
return
# Ensure package is not already installed
if 'install' in install_phases and spack.install_layout.check_installed(self.spec):
# FIXME : This should be a pre-requisite to a phase
if 'install' in self._phases and spack.install_layout.check_installed(self.spec):
tty.msg("%s is already installed in %s" % (self.name, self.prefix))
rec = spack.installed_db.get_record(self.spec)
if (not rec.explicit) and explicit:
@ -960,7 +965,6 @@ def build_process():
tty.msg("Building %s" % self.name)
self.stage.keep = keep_stage
self.install_phases = install_phases
self.build_directory = join_path(self.stage.path, 'spack-build')
self.source_directory = self.stage.source_path
@ -983,11 +987,25 @@ def build_process():
# the terminal)
log_path = join_path(os.getcwd(), 'spack-build.out')
log_file = open(log_path, 'w')
# FIXME : refactor this assignment
self.log_path = log_path
self.env_path = env_path
with log_output(log_file, verbose, sys.stdout.isatty(),
True):
dump_environment(env_path)
self.install(self.spec, self.prefix)
try:
for phase in filter(lambda x: x in allowed_phases, self._phases):
# TODO : Log to screen the various phases
action = getattr(self, phase)
if getattr(action, 'preconditions', None):
action.preconditions()
action(self.spec, self.prefix)
if getattr(action, 'postconditions', None):
action.postconditions()
except AttributeError as e:
# FIXME : improve error messages
raise ProcessError(e.message, long_message='')
except ProcessError as e:
# Annotate ProcessErrors with the location of
# the build log
@ -995,29 +1013,9 @@ def build_process():
raise e
# Ensure that something was actually installed.
if 'install' in self.install_phases:
self.sanity_check_prefix()
# Copy provenance into the install directory on success
if 'provenance' in self.install_phases:
log_install_path = spack.install_layout.build_log_path(
self.spec)
env_install_path = spack.install_layout.build_env_path(
self.spec)
packages_dir = spack.install_layout.build_packages_path(
self.spec)
# Remove first if we're overwriting another build
# (can happen with spack setup)
try:
shutil.rmtree(packages_dir) # log_install_path and env_install_path are inside this
except:
pass
install(log_path, log_install_path)
install(env_path, env_install_path)
dump_packages(self.spec, packages_dir)
# FIXME : This should be executed after 'install' as a postcondition
# if 'install' in self._phases:
# self.sanity_check_prefix()
# Run post install hooks before build stage is removed.
spack.hooks.post_install(self)
@ -1036,7 +1034,7 @@ def build_process():
# Create the install prefix and fork the build process.
spack.install_layout.create_install_directory(self.spec)
except directory_layout.InstallDirectoryAlreadyExistsError:
if 'install' in install_phases:
if 'install' in self._phases:
# Abort install if install directory exists.
# But do NOT remove it (you'd be overwriting someon else's stuff)
tty.warn("Keeping existing install prefix in place.")
@ -1064,6 +1062,27 @@ def build_process():
# the database, so that we don't need to re-read from file.
spack.installed_db.add(self.spec, self.prefix, explicit=explicit)
def create_spack_logs(self, spec, prefix):
# Copy provenance into the install directory on success
log_install_path = spack.install_layout.build_log_path(
self.spec)
env_install_path = spack.install_layout.build_env_path(
self.spec)
packages_dir = spack.install_layout.build_packages_path(
self.spec)
# Remove first if we're overwriting another build
# (can happen with spack setup)
try:
shutil.rmtree(packages_dir) # log_install_path and env_install_path are inside this
except Exception:
# FIXME : this potentially catches too many things...
pass
install(self.log_path, log_install_path)
install(self.env_path, env_install_path)
dump_packages(self.spec, packages_dir)
def sanity_check_prefix(self):
"""This function checks whether install succeeded."""
@ -1529,42 +1548,42 @@ def _hms(seconds):
parts.append("%.2fs" % s)
return ' '.join(parts)
class StagedPackage(Package):
"""A Package subclass where the install() is split up into stages."""
def install_setup(self):
"""Creates an spack_setup.py script to configure the package later if we like."""
raise InstallError("Package %s provides no install_setup() method!" % self.name)
def install_configure(self):
"""Runs the configure process."""
raise InstallError("Package %s provides no install_configure() method!" % self.name)
def install_build(self):
"""Runs the build process."""
raise InstallError("Package %s provides no install_build() method!" % self.name)
def install_install(self):
"""Runs the install process."""
raise InstallError("Package %s provides no install_install() method!" % self.name)
def install(self, spec, prefix):
if 'setup' in self.install_phases:
self.install_setup()
if 'configure' in self.install_phases:
self.install_configure()
if 'build' in self.install_phases:
self.install_build()
if 'install' in self.install_phases:
self.install_install()
else:
# Create a dummy file so the build doesn't fail.
# That way, the module file will also be created.
with open(os.path.join(prefix, 'dummy'), 'w') as fout:
pass
#class StagedPackage(Package):
# """A Package subclass where the install() is split up into stages."""
# _phases = ['configure']
# def install_setup(self):
# """Creates an spack_setup.py script to configure the package later if we like."""
# raise InstallError("Package %s provides no install_setup() method!" % self.name)
#
# def install_configure(self):
# """Runs the configure process."""
# raise InstallError("Package %s provides no install_configure() method!" % self.name)
#
# def install_build(self):
# """Runs the build process."""
# raise InstallError("Package %s provides no install_build() method!" % self.name)
#
# def install_install(self):
# """Runs the install process."""
# raise InstallError("Package %s provides no install_install() method!" % self.name)
#
# def install(self, spec, prefix):
# if 'setup' in self._phases:
# self.install_setup()
#
# if 'configure' in self._phases:
# self.install_configure()
#
# if 'build' in self._phases:
# self.install_build()
#
# if 'install' in self._phases:
# self.install_install()
# else:
# # Create a dummy file so the build doesn't fail.
# # That way, the module file will also be created.
# with open(os.path.join(prefix, 'dummy'), 'w') as fout:
# pass
# stackoverflow.com/questions/12791997/how-do-you-do-a-simple-chmod-x-from-within-python
def make_executable(path):
@ -1574,7 +1593,8 @@ def make_executable(path):
class CMakePackage(StagedPackage):
class CMakePackage(Package):
_phases = ['configure', 'build', 'install', 'provenance']
def make_make(self):
import multiprocessing
@ -1602,7 +1622,7 @@ def spack_transitive_include_path(self):
for dep in os.environ['SPACK_DEPENDENCIES'].split(os.pathsep)
)
def install_setup(self):
def setup(self):
cmd = [str(which('cmake'))] + \
spack.build_environment.get_std_cmake_args(self) + \
['-DCMAKE_INSTALL_PREFIX=%s' % os.environ['SPACK_PREFIX'],
@ -1657,7 +1677,7 @@ def cmdlist(str):
make_executable(setup_fname)
def install_configure(self):
def configure(self):
cmake = which('cmake')
with working_dir(self.build_directory, create=True):
os.environ.update(self.configure_env())
@ -1665,12 +1685,12 @@ def install_configure(self):
options = self.configure_args() + spack.build_environment.get_std_cmake_args(self)
cmake(self.source_directory, *options)
def install_build(self):
def build(self):
make = self.make_make()
with working_dir(self.build_directory, create=False):
make()
def install_install(self):
def install(self):
make = self.make_make()
with working_dir(self.build_directory, create=False):
make('install')