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',
'CMakePackage',
'AutotoolsPackage',
'EditableMakefile',
'Version',
'when',
'ver']
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.multimethod import when

View File

@ -56,6 +56,9 @@ def setup_parser(subparser):
subparser.add_argument(
'--dirty', action='store_true', dest='dirty',
help="Install a package *without* cleaning the environment.")
subparser.add_argument(
'--stop-at', help="Stop at a particular phase of installation"
)
subparser.add_argument(
'packages', nargs=argparse.REMAINDER, help="specs of packages to install")
subparser.add_argument(
@ -88,4 +91,6 @@ def install(parser, args):
verbose=args.verbose,
fake=args.fake,
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
for check in self.sanity_checks:
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
@ -123,10 +127,10 @@ def __new__(meta, name, bases, attr_dict):
# Check if phases is in attr dict, then set
# install phases wrappers
if 'phases' in attr_dict:
phases = [PackageMeta.phase_fmt.format(x) for x in attr_dict['phases']]
for phase_name, callback_name in zip(phases, attr_dict['phases']):
_InstallPhase_phases = [PackageMeta.phase_fmt.format(x) for x in attr_dict['phases']]
for phase_name, callback_name in zip(_InstallPhase_phases, attr_dict['phases']):
attr_dict[phase_name] = InstallPhase(callback_name)
attr_dict['phases'] = phases
attr_dict['_InstallPhase_phases'] = _InstallPhase_phases
def _append_checks(check_name):
# Name of the attribute I am going to check it exists
@ -956,7 +960,8 @@ def namespace(self):
return namespace
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)
touch(join_path(self.prefix.bin, 'fake'))
mkdirp(self.prefix.lib)
@ -990,7 +995,7 @@ def do_install(self,
fake=False,
explicit=False,
dirty=False,
allowed_phases=None):
**kwargs):
"""Called by commands to install a package and its dependencies.
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
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 allowed_phases is None:
# 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:
raise ValueError("Can only install concrete packages.")
@ -1097,9 +1106,10 @@ def build_process():
True):
dump_environment(env_path)
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
getattr(self, phase)(self.spec, self.prefix)
self.log()
except AttributeError as e:
# FIXME : improve error messages
raise ProcessError(e.message, long_message='')
@ -1126,9 +1136,10 @@ def build_process():
# Create the install prefix and fork the build process.
spack.install_layout.create_install_directory(self.spec)
except directory_layout.InstallDirectoryAlreadyExistsError:
# FIXME : refactor this as a prerequisites to configure
if 'install' in self.phases:
# 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.")
raise
else:
@ -1154,7 +1165,7 @@ 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 log(self, spec, prefix):
def log(self):
# Copy provenance into the install directory on success
log_install_path = spack.install_layout.build_log_path(
self.spec)
@ -1533,14 +1544,41 @@ def rpath_args(self):
class Package(PackageBase):
phases = ['install', 'log']
phases = ['install']
# This will be used as a registration decorator in user
# packages, if need be
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):
phases = ['autoreconf', 'configure', 'build', 'install', 'log']
phases = ['autoreconf', 'configure', 'build', 'install']
def autoreconf(self, spec, prefix):
"""Not needed usually, configure should be already there"""

View File

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

View File

@ -24,16 +24,10 @@
##############################################################################
from spack import *
class Blitz(Package):
class Blitz(AutotoolsPackage):
"""N-dimensional arrays for C++"""
homepage = "http://github.com/blitzpp/blitz"
url = "https://github.com/blitzpp/blitz/tarball/1.0.0"
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 *
class Gmp(Package):
class Gmp(AutotoolsPackage):
"""GMP is a free library for arbitrary precision arithmetic,
operating on signed integers, rational numbers, and
floating-point numbers."""
@ -36,8 +37,3 @@ class Gmp(Package):
version('6.0.0' , '6ef5869ae735db9995619135bd856b84')
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()
def config_args(self):
return ['--prefix=%s' % prefix,
'--enable-mpi' if '+mpi' in spec else '--disable-mpi',
def configure_args(self):
return ['--prefix=%s' % self.prefix,
'--enable-mpi' if '+mpi' in self.spec else '--disable-mpi',
'--with-metis={0}'.format(self.spec['metis'].prefix),
'--enable-optimization']