package : added EditableMakefile

Modifications :
- added EditableMakefile to PackageBase subclasses
- astyle modified as an example
- preliminary hook to stop at a certain phase of install
This commit is contained in:
alalazo 2016-07-11 10:08:19 +02:00
parent 8f75d34331
commit a43c63f149
7 changed files with 78 additions and 48 deletions

View File

@ -179,11 +179,12 @@
__all__ = ['Package', __all__ = ['Package',
'CMakePackage', 'CMakePackage',
'AutotoolsPackage', 'AutotoolsPackage',
'EditableMakefile',
'Version', 'Version',
'when', 'when',
'ver'] 'ver']
from spack.package import Package, ExtensionConflictError from spack.package import Package, ExtensionConflictError
from spack.package import CMakePackage, AutotoolsPackage from spack.package import CMakePackage, AutotoolsPackage, EditableMakefile
from spack.version import Version, ver from spack.version import Version, ver
from spack.multimethod import when from spack.multimethod import when

View File

@ -56,6 +56,9 @@ def setup_parser(subparser):
subparser.add_argument( subparser.add_argument(
'--dirty', action='store_true', dest='dirty', '--dirty', action='store_true', dest='dirty',
help="Install a package *without* cleaning the environment.") help="Install a package *without* cleaning the environment.")
subparser.add_argument(
'--stop-at', help="Stop at a particular phase of installation"
)
subparser.add_argument( subparser.add_argument(
'packages', nargs=argparse.REMAINDER, help="specs of packages to install") 'packages', nargs=argparse.REMAINDER, help="specs of packages to install")
subparser.add_argument( subparser.add_argument(
@ -88,4 +91,6 @@ def install(parser, args):
verbose=args.verbose, verbose=args.verbose,
fake=args.fake, fake=args.fake,
dirty=args.dirty, dirty=args.dirty,
explicit=True) explicit=True,
stop_at=args.stop_at
)

View File

@ -104,6 +104,10 @@ def phase_wrapper(spec, prefix):
# and give them the chance to fail # and give them the chance to fail
for check in self.sanity_checks: for check in self.sanity_checks:
check(instance) check(instance)
# Permit instance to drive the execution
if self.name == instance.last_phase:
raise StopIteration('Stopping at \'{0}\' phase'.format(self.name))
return phase_wrapper return phase_wrapper
@ -123,10 +127,10 @@ def __new__(meta, name, bases, attr_dict):
# Check if phases is in attr dict, then set # Check if phases is in attr dict, then set
# install phases wrappers # install phases wrappers
if 'phases' in attr_dict: if 'phases' in attr_dict:
phases = [PackageMeta.phase_fmt.format(x) for x in attr_dict['phases']] _InstallPhase_phases = [PackageMeta.phase_fmt.format(x) for x in attr_dict['phases']]
for phase_name, callback_name in zip(phases, attr_dict['phases']): for phase_name, callback_name in zip(_InstallPhase_phases, attr_dict['phases']):
attr_dict[phase_name] = InstallPhase(callback_name) attr_dict[phase_name] = InstallPhase(callback_name)
attr_dict['phases'] = phases attr_dict['_InstallPhase_phases'] = _InstallPhase_phases
def _append_checks(check_name): def _append_checks(check_name):
# Name of the attribute I am going to check it exists # Name of the attribute I am going to check it exists
@ -956,7 +960,8 @@ def namespace(self):
return namespace return namespace
def do_fake_install(self): def do_fake_install(self):
"""Make a fake install directory contaiing a 'fake' file in bin.""" """Make a fake install directory containing a 'fake' file in bin."""
# FIXME : Make this part of the 'install' behavior ?
mkdirp(self.prefix.bin) mkdirp(self.prefix.bin)
touch(join_path(self.prefix.bin, 'fake')) touch(join_path(self.prefix.bin, 'fake'))
mkdirp(self.prefix.lib) mkdirp(self.prefix.lib)
@ -990,7 +995,7 @@ def do_install(self,
fake=False, fake=False,
explicit=False, explicit=False,
dirty=False, dirty=False,
allowed_phases=None): **kwargs):
"""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
@ -1010,9 +1015,13 @@ 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:
if allowed_phases is None: # allowed_phases = self.phases
allowed_phases = self.phases # FIXME : Refine the error message
last_phase = kwargs.get('stop_at', None)
if last_phase is not None and last_phase not in self.phases:
raise KeyError('phase {0} is not among the allowed phases for package {1}'.format(last_phase, self))
self.last_phase = last_phase
if not self.spec.concrete: if not self.spec.concrete:
raise ValueError("Can only install concrete packages.") raise ValueError("Can only install concrete packages.")
@ -1097,9 +1106,10 @@ def build_process():
True): True):
dump_environment(env_path) dump_environment(env_path)
try: try:
for phase in filter(lambda x: x in allowed_phases, self.phases): for phase in self._InstallPhase_phases:
# TODO : Log to screen the various phases # TODO : Log to screen the various phases
getattr(self, phase)(self.spec, self.prefix) getattr(self, phase)(self.spec, self.prefix)
self.log()
except AttributeError as e: except AttributeError as e:
# FIXME : improve error messages # FIXME : improve error messages
raise ProcessError(e.message, long_message='') raise ProcessError(e.message, long_message='')
@ -1126,9 +1136,10 @@ 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:
# FIXME : refactor this as a prerequisites to configure
if 'install' in self.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 someone else's stuff)
tty.warn("Keeping existing install prefix in place.") tty.warn("Keeping existing install prefix in place.")
raise raise
else: else:
@ -1154,7 +1165,7 @@ 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 log(self, spec, prefix): def log(self):
# Copy provenance into the install directory on success # Copy provenance into the install directory on success
log_install_path = spack.install_layout.build_log_path( log_install_path = spack.install_layout.build_log_path(
self.spec) self.spec)
@ -1533,14 +1544,41 @@ def rpath_args(self):
class Package(PackageBase): class Package(PackageBase):
phases = ['install', 'log'] phases = ['install']
# This will be used as a registration decorator in user # This will be used as a registration decorator in user
# packages, if need be # packages, if need be
PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix) PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix)
class EditableMakefile(PackageBase):
phases = ['edit', 'build', 'install']
def wdir(self):
return self.stage.source_path
def build_args(self):
return list()
def install_args(self):
return list()
def edit(self, spec, prefix):
raise NotImplementedError('\'edit\' function not implemented')
def build(self, spec, prefix):
args = self.build_args()
with working_dir(self.wdir()):
inspect.getmodule(self).make(*args)
def install(self, spec, prefix):
args = self.install_args() + ['install']
with working_dir(self.wdir()):
inspect.getmodule(self).make(*args)
PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix)
class AutotoolsPackage(PackageBase): class AutotoolsPackage(PackageBase):
phases = ['autoreconf', 'configure', 'build', 'install', 'log'] phases = ['autoreconf', 'configure', 'build', 'install']
def autoreconf(self, spec, prefix): def autoreconf(self, spec, prefix):
"""Not needed usually, configure should be already there""" """Not needed usually, configure should be already there"""

View File

@ -25,7 +25,7 @@
from spack import * from spack import *
class Astyle(Package): class Astyle(EditableMakefile):
""" """
A Free, Fast, and Small Automatic Formatter for C, C++, C++/CLI, A Free, Fast, and Small Automatic Formatter for C, C++, C++/CLI,
Objective-C, C#, and Java Source Code. Objective-C, C#, and Java Source Code.
@ -35,18 +35,14 @@ class Astyle(Package):
version('2.04', '30b1193a758b0909d06e7ee8dd9627f6') version('2.04', '30b1193a758b0909d06e7ee8dd9627f6')
def install(self, spec, prefix): parallel = False
with working_dir('src'): def wdir(self):
# we need to edit the makefile in place to set compiler: return join_path(self.stage.source_path, 'build', self.compiler.name)
make_file = join_path(self.stage.source_path,
'build', 'gcc', 'Makefile')
filter_file(r'^CXX\s*=.*', 'CXX=%s' % spack_cxx, make_file)
make('-f', def edit(self, spec, prefix):
make_file, makefile = join_path(self.wdir(), 'Makefile')
parallel=False) filter_file(r'^CXX\s*=.*', 'CXX=%s' % spack_cxx, makefile)
mkdirp(self.prefix.bin) def install_args(self):
install(join_path(self.stage.source_path, 'src', 'bin', 'astyle'), return ['prefix={0}'.format(prefix)]
self.prefix.bin)

View File

@ -24,16 +24,10 @@
############################################################################## ##############################################################################
from spack import * from spack import *
class Blitz(Package):
class Blitz(AutotoolsPackage):
"""N-dimensional arrays for C++""" """N-dimensional arrays for C++"""
homepage = "http://github.com/blitzpp/blitz" homepage = "http://github.com/blitzpp/blitz"
url = "https://github.com/blitzpp/blitz/tarball/1.0.0" url = "https://github.com/blitzpp/blitz/tarball/1.0.0"
version('1.0.0', '9f040b9827fe22228a892603671a77af') version('1.0.0', '9f040b9827fe22228a892603671a77af')
# No dependencies
def install(self, spec, prefix):
configure('--prefix=%s' % prefix)
make()
make("install")

View File

@ -24,7 +24,8 @@
############################################################################## ##############################################################################
from spack import * from spack import *
class Gmp(Package):
class Gmp(AutotoolsPackage):
"""GMP is a free library for arbitrary precision arithmetic, """GMP is a free library for arbitrary precision arithmetic,
operating on signed integers, rational numbers, and operating on signed integers, rational numbers, and
floating-point numbers.""" floating-point numbers."""
@ -36,8 +37,3 @@ class Gmp(Package):
version('6.0.0' , '6ef5869ae735db9995619135bd856b84') version('6.0.0' , '6ef5869ae735db9995619135bd856b84')
depends_on("m4") depends_on("m4")
def install(self, spec, prefix):
configure("--prefix=%s" % prefix)
make()
make("install")

View File

@ -66,8 +66,8 @@ def autoreconf(self, spec, prefix):
autogen = Executable('./autogen.sh') autogen = Executable('./autogen.sh')
autogen() autogen()
def config_args(self): def configure_args(self):
return ['--prefix=%s' % prefix, return ['--prefix=%s' % self.prefix,
'--enable-mpi' if '+mpi' in spec else '--disable-mpi', '--enable-mpi' if '+mpi' in self.spec else '--disable-mpi',
'--with-metis={0}'.format(self.spec['metis'].prefix), '--with-metis={0}'.format(self.spec['metis'].prefix),
'--enable-optimization'] '--enable-optimization']