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 # 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. # should live. This file is overloaded for spack core vs. for packages.
# #
__all__ = ['Package', 'StagedPackage', 'CMakePackage', \ __all__ = ['Package', 'CMakePackage', \
'Version', 'when', 'ver'] 'Version', 'when', 'ver']
from spack.package import Package, ExtensionConflictError 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.version import Version, ver
from spack.multimethod import when from spack.multimethod import when

View file

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