diff options
-rw-r--r-- | lib/spack/llnl/util/tty/log.py | 204 | ||||
-rw-r--r-- | lib/spack/spack/__init__.py | 13 | ||||
-rw-r--r-- | lib/spack/spack/build_environment.py | 71 | ||||
-rw-r--r-- | lib/spack/spack/cmd/install.py | 7 | ||||
-rw-r--r-- | lib/spack/spack/cmd/setup.py | 85 | ||||
-rw-r--r-- | lib/spack/spack/package.py | 692 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/astyle/package.py | 24 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/blitz/package.py | 11 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/gmp/package.py | 15 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/hdf5/package.py | 53 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/ibmisc/package.py | 2 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/lzo/package.py | 19 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/openjpeg/package.py | 11 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/qhull/package.py | 11 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/swiftsim/package.py | 19 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/szip/package.py | 25 |
16 files changed, 707 insertions, 555 deletions
diff --git a/lib/spack/llnl/util/tty/log.py b/lib/spack/llnl/util/tty/log.py index b67edcf9cc..a4ba2a9bdf 100644 --- a/lib/spack/llnl/util/tty/log.py +++ b/lib/spack/llnl/util/tty/log.py @@ -24,11 +24,11 @@ ############################################################################## """Utility classes for logging the output of blocks of code. """ -import sys +import multiprocessing import os import re import select -import inspect +import sys import llnl.util.tty as tty import llnl.util.tty.color as color @@ -100,25 +100,29 @@ class keyboard_input(object): class log_output(object): - """Redirects output and error of enclosed block to a file. + """Spawns a daemon that reads from a pipe and writes to a file Usage: - with log_output(open('logfile.txt', 'w')): - # do things ... output will be logged. + # Spawns the daemon + with log_output('logfile.txt', 'w') as log_redirection: + # do things ... output is not redirected + with log_redirection: + # do things ... output will be logged or: - with log_output(open('logfile.txt', 'w'), echo=True): - # do things ... output will be logged - # and also printed to stdout. - - Closes the provided stream when done with the block. - If echo is True, also prints the output to stdout. + with log_output('logfile.txt', echo=True) as log_redirection: + # do things ... output is not redirected + with log_redirection: + # do things ... output will be logged + # and also printed to stdout. + + Opens a stream in 'w' mode at daemon spawning and closes it at + daemon joining. If echo is True, also prints the output to stdout. """ - def __init__(self, stream, echo=False, force_color=False, debug=False): - self.stream = stream - - # various output options + def __init__(self, filename, echo=False, force_color=False, debug=False): + self.filename = filename + # Various output options self.echo = echo self.force_color = force_color self.debug = debug @@ -126,70 +130,81 @@ class log_output(object): # Default is to try file-descriptor reassignment unless the system # out/err streams do not have an associated file descriptor self.directAssignment = False - - def trace(self, frame, event, arg): - """Jumps to __exit__ on the child process.""" - raise _SkipWithBlock() + self.read, self.write = os.pipe() + + # Sets a daemon that writes to file what it reads from a pipe + self.p = multiprocessing.Process( + target=self._spawn_writing_daemon, + args=(self.read,), + name='logger_daemon' + ) + self.p.daemon = True + # Needed to un-summon the daemon + self.parent_pipe, self.child_pipe = multiprocessing.Pipe() def __enter__(self): - """Redirect output from the with block to a file. - - This forks the with block as a separate process, with stdout - and stderr redirected back to the parent via a pipe. If - echo is set, also writes to standard out. - - """ - # remember these values for later. - self._force_color = color._force_color - self._debug = tty._debug - - read, write = os.pipe() - - self.pid = os.fork() - if self.pid: - # Parent: read from child, skip the with block. - os.close(write) - - read_file = os.fdopen(read, 'r', 0) - with self.stream as log_file: - with keyboard_input(sys.stdin): - while True: - rlist, w, x = select.select( - [read_file, sys.stdin], [], []) - if not rlist: + self.p.start() + return log_output.OutputRedirection(self) + + def __exit__(self, exc_type, exc_val, exc_tb): + self.parent_pipe.send(True) + self.p.join(60.0) # 1 minute to join the child + + def _spawn_writing_daemon(self, read): + # Parent: read from child, skip the with block. + read_file = os.fdopen(read, 'r', 0) + with open(self.filename, 'w') as log_file: + with keyboard_input(sys.stdin): + while True: + rlist, _, _ = select.select([read_file, sys.stdin], [], []) + if not rlist: + break + + # Allow user to toggle echo with 'v' key. + # Currently ignores other chars. + if sys.stdin in rlist: + if sys.stdin.read(1) == 'v': + self.echo = not self.echo + + # Handle output from the with block process. + if read_file in rlist: + line = read_file.readline() + if not line: + # For some reason we never reach this point... break - # Allow user to toggle echo with 'v' key. - # Currently ignores other chars. - if sys.stdin in rlist: - if sys.stdin.read(1) == 'v': - self.echo = not self.echo + # Echo to stdout if requested. + if self.echo: + sys.stdout.write(line) - # handle output from the with block process. - if read_file in rlist: - line = read_file.readline() - if not line: - break + # Stripped output to log file. + log_file.write(_strip(line)) + log_file.flush() - # Echo to stdout if requested. - if self.echo: - sys.stdout.write(line) + if self.child_pipe.poll(): + break - # Stripped output to log file. - log_file.write(_strip(line)) + def __del__(self): + """Closes the pipes""" + os.close(self.write) + os.close(self.read) - read_file.flush() - read_file.close() + class OutputRedirection(object): - # Set a trace function to skip the with block. - sys.settrace(lambda *args, **keys: None) - frame = inspect.currentframe(1) - frame.f_trace = self.trace + def __init__(self, other): + self.__dict__.update(other.__dict__) - else: - # Child: redirect output, execute the with block. - os.close(read) + def __enter__(self): + """Redirect output from the with block to a file. + Hijacks stdout / stderr and writes to the pipe + connected to the logger daemon + """ + # remember these values for later. + self._force_color = color._force_color + self._debug = tty._debug + # Redirect this output to a pipe + write = self.write try: # Save old stdout and stderr self._stdout = os.dup(sys.stdout.fileno()) @@ -205,53 +220,26 @@ class log_output(object): output_redirect = os.fdopen(write, 'w') sys.stdout = output_redirect sys.stderr = output_redirect - if self.force_color: color._force_color = True - if self.debug: tty._debug = True - def __exit__(self, exc_type, exception, traceback): - """Exits on child, handles skipping the with block on parent.""" - # Child should just exit here. - if self.pid == 0: + def __exit__(self, exc_type, exception, traceback): + """Plugs back the original file descriptors + for stdout and stderr + """ # Flush the log to disk. sys.stdout.flush() sys.stderr.flush() - - if exception: - # Restore stdout on the child if there's an exception, - # and let it be raised normally. - # - # This assumes that even if the exception is caught, - # the child will exit with a nonzero return code. If - # it doesn't, the child process will continue running. - # - # TODO: think about how this works outside install. - # TODO: ideally would propagate exception to parent... - if self.directAssignment: - sys.stdout = self._stdout - sys.stderr = self._stderr - else: - os.dup2(self._stdout, sys.stdout.fileno()) - os.dup2(self._stderr, sys.stderr.fileno()) - - return False - + if self.directAssignment: + # We seem to need this only to pass test/install.py + sys.stdout = self._stdout + sys.stderr = self._stderr else: - # Die quietly if there was no exception. - os._exit(0) - - else: - # If the child exited badly, parent also should exit. - pid, returncode = os.waitpid(self.pid, 0) - if returncode != 0: - os._exit(1) - - # restore output options. - color._force_color = self._force_color - tty._debug = self._debug + os.dup2(self._stdout, sys.stdout.fileno()) + os.dup2(self._stderr, sys.stderr.fileno()) - # Suppresses exception if it's our own. - return exc_type is _SkipWithBlock + # restore output options. + color._force_color = self._force_color + tty._debug = self._debug diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py index 3d508d0fde..c9c29a32cc 100644 --- a/lib/spack/spack/__init__.py +++ b/lib/spack/spack/__init__.py @@ -185,10 +185,17 @@ sys_type = None # packages should live. This file is overloaded for spack core vs. # for packages. # -__all__ = ['Package', 'StagedPackage', 'CMakePackage', - 'Version', 'when', 'ver', 'alldeps', 'nolink'] +__all__ = ['Package', + 'CMakePackage', + 'AutotoolsPackage', + 'EditableMakefile', + 'Version', + 'when', + 'ver', + 'alldeps', + 'nolink'] from spack.package import Package, ExtensionConflictError -from spack.package import StagedPackage, CMakePackage +from spack.package import CMakePackage, AutotoolsPackage, EditableMakefile from spack.version import Version, ver from spack.spec import DependencySpec, alldeps, nolink from spack.multimethod import when diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py index 9b5ed367d1..9f7cf0bb1b 100644 --- a/lib/spack/spack/build_environment.py +++ b/lib/spack/spack/build_environment.py @@ -51,16 +51,14 @@ There are two parts to the build environment: Skimming this module is a nice way to get acquainted with the types of calls you can make from within the install() function. """ +import multiprocessing import os -import sys import shutil -import multiprocessing -import platform +import sys import llnl.util.tty as tty -from llnl.util.filesystem import * - import spack +from llnl.util.filesystem import * from spack.environment import EnvironmentModifications, validate from spack.util.environment import * from spack.util.executable import Executable, which @@ -341,16 +339,8 @@ def set_module_variables_for_package(pkg, module): m.cmake = Executable('cmake') m.ctest = Executable('ctest') - # standard CMake arguments - m.std_cmake_args = ['-DCMAKE_INSTALL_PREFIX=%s' % pkg.prefix, - '-DCMAKE_BUILD_TYPE=RelWithDebInfo'] - if platform.mac_ver()[0]: - m.std_cmake_args.append('-DCMAKE_FIND_FRAMEWORK=LAST') - - # Set up CMake rpath - m.std_cmake_args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE') - m.std_cmake_args.append('-DCMAKE_INSTALL_RPATH=%s' % - ":".join(get_rpaths(pkg))) + # Standard CMake arguments + m.std_cmake_args = spack.CMakePackage._std_args(pkg) # Put spack compiler paths in module scope. link_dir = spack.build_env_path @@ -497,41 +487,26 @@ def fork(pkg, function, dirty=False): carries on. """ - try: - pid = os.fork() - except OSError as e: - raise InstallError("Unable to fork build process: %s" % e) - - if pid == 0: - # Give the child process the package's build environment. - setup_package(pkg, dirty=dirty) - + def child_execution(child_connection): try: - # call the forked function. + setup_package(pkg, dirty=dirty) function() - - # Use os._exit here to avoid raising a SystemExit exception, - # which interferes with unit tests. - os._exit(0) - - except spack.error.SpackError as e: - e.die() - - except: - # Child doesn't raise or return to main spack code. - # Just runs default exception handler and exits. - sys.excepthook(*sys.exc_info()) - os._exit(1) - - else: - # Parent process just waits for the child to complete. If the - # child exited badly, assume it already printed an appropriate - # message. Just make the parent exit with an error code. - pid, returncode = os.waitpid(pid, 0) - if returncode != 0: - message = "Installation process had nonzero exit code : {code}" - strcode = str(returncode) - raise InstallError(message.format(code=strcode)) + child_connection.send([None, None, None]) + except Exception as e: + child_connection.send([type(e), e, None]) + finally: + child_connection.close() + + parent_connection, child_connection = multiprocessing.Pipe() + p = multiprocessing.Process( + target=child_execution, + args=(child_connection,) + ) + p.start() + exc_type, exception, traceback = parent_connection.recv() + p.join() + if exception is not None: + raise exception class InstallError(spack.error.SpackError): diff --git a/lib/spack/spack/cmd/install.py b/lib/spack/spack/cmd/install.py index 7663a97a28..634a603ddc 100644 --- a/lib/spack/spack/cmd/install.py +++ b/lib/spack/spack/cmd/install.py @@ -58,6 +58,9 @@ def setup_parser(subparser): '--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( @@ -89,4 +92,6 @@ def install(parser, args): verbose=args.verbose, fake=args.fake, dirty=args.dirty, - explicit=True) + explicit=True, + stop_at=args.stop_at + ) diff --git a/lib/spack/spack/cmd/setup.py b/lib/spack/spack/cmd/setup.py index b55e102c0e..652c08354f 100644 --- a/lib/spack/spack/cmd/setup.py +++ b/lib/spack/spack/cmd/setup.py @@ -22,16 +22,18 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## -import sys -import os import argparse +import os +import string +import sys import llnl.util.tty as tty - import spack import spack.cmd +from spack import which from spack.cmd.edit import edit_package from spack.stage import DIYStage +from llnl.util.filesystem import set_executable description = "Create a configuration script and module, but don't build." @@ -48,6 +50,72 @@ def setup_parser(subparser): help="specs to use for install. Must contain package AND version.") +def spack_transitive_include_path(): + return ';'.join( + os.path.join(dep, 'include') + for dep in os.environ['SPACK_DEPENDENCIES'].split(os.pathsep) + ) + + +def write_spconfig(package): + # Set-up the environment + spack.build_environment.setup_package(package) + + cmd = [str(which('cmake'))] + package.std_cmake_args + package.cmake_args() + + env = dict() + + paths = os.environ['PATH'].split(':') + paths = [item for item in paths if 'spack/env' not in item] + env['PATH'] = ':'.join(paths) + env['SPACK_TRANSITIVE_INCLUDE_PATH'] = spack_transitive_include_path() + env['CMAKE_PREFIX_PATH'] = os.environ['CMAKE_PREFIX_PATH'] + env['CC'] = os.environ['SPACK_CC'] + env['CXX'] = os.environ['SPACK_CXX'] + env['FC'] = os.environ['SPACK_FC'] + + setup_fname = 'spconfig.py' + with open(setup_fname, 'w') as fout: + fout.write( + r"""#!%s +# + +import sys +import os +import subprocess + +def cmdlist(str): + return list(x.strip().replace("'",'') for x in str.split('\n') if x) +env = dict(os.environ) +""" % sys.executable) + + env_vars = sorted(list(env.keys())) + for name in env_vars: + val = env[name] + if string.find(name, 'PATH') < 0: + fout.write('env[%s] = %s\n' % (repr(name), repr(val))) + else: + if name == 'SPACK_TRANSITIVE_INCLUDE_PATH': + sep = ';' + else: + sep = ':' + + fout.write( + 'env[%s] = "%s".join(cmdlist("""\n' % (repr(name), sep)) + for part in string.split(val, sep): + fout.write(' %s\n' % part) + fout.write('"""))\n') + + fout.write("env['CMAKE_TRANSITIVE_INCLUDE_PATH'] = env['SPACK_TRANSITIVE_INCLUDE_PATH'] # Deprecated\n") # NOQA: ignore=E501 + fout.write('\ncmd = cmdlist("""\n') + fout.write('%s\n' % cmd[0]) + for arg in cmd[1:]: + fout.write(' %s\n' % arg) + fout.write('""") + sys.argv[1:]\n') + fout.write('\nproc = subprocess.Popen(cmd, env=env)\nproc.wait()\n') + set_executable(setup_fname) + + def setup(self, args): if not args.spec: tty.die("spack setup requires a package spec argument.") @@ -86,9 +154,8 @@ def setup(self, args): # TODO: make this an argument, not a global. spack.do_checksum = False - package.do_install( - keep_prefix=True, # Don't remove install directory - ignore_deps=args.ignore_deps, - verbose=args.verbose, - keep_stage=True, # don't remove source dir for SETUP. - install_phases=set(['setup', 'provenance'])) + if not isinstance(package, spack.CMakePackage): + raise RuntimeError( + 'Support for {0} not yet implemented'.format(type(package))) + + write_spconfig(package) diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index 882901d887..dcdcee293d 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -33,12 +33,17 @@ Homebrew makes it very easy to create packages. For a complete rundown on spack and how it differs from homebrew, look at the README. """ +import copy +import functools +import inspect import os -import sys +import platform import re +import sys import textwrap import time -import string +from StringIO import StringIO +from urlparse import urlparse import llnl.util.tty as tty import spack @@ -52,26 +57,188 @@ import spack.mirror import spack.repository import spack.url import spack.util.web - -from urlparse import urlparse -from StringIO import StringIO from llnl.util.filesystem import * from llnl.util.lang import * from llnl.util.link_tree import LinkTree from llnl.util.tty.log import log_output +from spack import directory_layout from spack.stage import Stage, ResourceStage, StageComposite from spack.util.compression import allowed_archive from spack.util.environment import dump_environment -from spack.util.executable import ProcessError, which +from spack.util.executable import ProcessError from spack.version import * -from spack import directory_layout - """Allowed URL schemes for spack packages.""" _ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"] -class Package(object): +class InstallPhase(object): + """Manages a single phase of the installation + + This descriptor stores at creation time the name of the method it should + search for execution. The method is retrieved at __get__ time, so that + it can be overridden by subclasses of whatever class declared the phases. + + It also provides hooks to execute prerequisite and sanity checks. + """ + + def __init__(self, name): + self.name = name + self.preconditions = [] + self.sanity_checks = [] + + def __get__(self, instance, owner): + # The caller is a class that is trying to customize + # my behavior adding something + if instance is None: + return self + # If instance is there the caller wants to execute the + # install phase, thus return a properly set wrapper + phase = getattr(instance, self.name) + + @functools.wraps(phase) + def phase_wrapper(spec, prefix): + # Check instance attributes at the beginning of a phase + self._on_phase_start(instance) + # Execute phase pre-conditions, + # and give them the chance to fail + for check in self.preconditions: + # Do something sensible at some point + check(instance) + phase(spec, prefix) + # Execute phase sanity_checks, + # and give them the chance to fail + for check in self.sanity_checks: + check(instance) + # Check instance attributes at the end of a phase + self._on_phase_exit(instance) + return phase_wrapper + + def _on_phase_start(self, instance): + pass + + def _on_phase_exit(self, instance): + # If a phase has a matching last_phase attribute, + # stop the installation process raising a StopIteration + if getattr(instance, 'last_phase', None) == self.name: + raise StopIteration('Stopping at \'{0}\' phase'.format(self.name)) + + def copy(self): + try: + return copy.deepcopy(self) + except TypeError: + # This bug-fix was not back-ported in Python 2.6 + # http://bugs.python.org/issue1515 + other = InstallPhase(self.name) + other.preconditions.extend(self.preconditions) + other.sanity_checks.extend(self.sanity_checks) + return other + + +class PackageMeta(type): + """Conveniently transforms attributes to permit extensible phases + + Iterates over the attribute 'phases' and creates / updates private + InstallPhase attributes in the class that is being initialized + """ + phase_fmt = '_InstallPhase_{0}' + + _InstallPhase_sanity_checks = {} + _InstallPhase_preconditions = {} + + def __new__(meta, name, bases, attr_dict): + # Check if phases is in attr dict, then set + # install phases wrappers + if 'phases' in attr_dict: + _InstallPhase_phases = [PackageMeta.phase_fmt.format(x) for x in attr_dict['phases']] # NOQA: ignore=E501 + for phase_name, callback_name in zip(_InstallPhase_phases, attr_dict['phases']): # NOQA: ignore=E501 + attr_dict[phase_name] = InstallPhase(callback_name) + attr_dict['_InstallPhase_phases'] = _InstallPhase_phases + + def _append_checks(check_name): + # Name of the attribute I am going to check it exists + attr_name = PackageMeta.phase_fmt.format(check_name) + checks = getattr(meta, attr_name) + if checks: + for phase_name, funcs in checks.items(): + try: + # Search for the phase in the attribute dictionary + phase = attr_dict[ + PackageMeta.phase_fmt.format(phase_name)] + except KeyError: + # If it is not there it's in the bases + # and we added a check. We need to copy + # and extend + for base in bases: + phase = getattr( + base, + PackageMeta.phase_fmt.format(phase_name), + None + ) + attr_dict[PackageMeta.phase_fmt.format( + phase_name)] = phase.copy() + phase = attr_dict[ + PackageMeta.phase_fmt.format(phase_name)] + getattr(phase, check_name).extend(funcs) + # Clear the attribute for the next class + setattr(meta, attr_name, {}) + + @classmethod + def _register_checks(cls, check_type, *args): + def _register_sanity_checks(func): + attr_name = PackageMeta.phase_fmt.format(check_type) + check_list = getattr(meta, attr_name) + for item in args: + checks = check_list.setdefault(item, []) + checks.append(func) + setattr(meta, attr_name, check_list) + return func + return _register_sanity_checks + + @staticmethod + def on_package_attributes(**attrs): + def _execute_under_condition(func): + @functools.wraps(func) + def _wrapper(instance): + # If all the attributes have the value we require, then + # execute + if all([getattr(instance, key, None) == value for key, value in attrs.items()]): # NOQA: ignore=E501 + func(instance) + return _wrapper + return _execute_under_condition + + @classmethod + def precondition(cls, *args): + return cls._register_checks('preconditions', *args) + + @classmethod + def sanity_check(cls, *args): + return cls._register_checks('sanity_checks', *args) + + if all([not hasattr(x, '_register_checks') for x in bases]): + attr_dict['_register_checks'] = _register_checks + + if all([not hasattr(x, 'sanity_check') for x in bases]): + attr_dict['sanity_check'] = sanity_check + + if all([not hasattr(x, 'precondition') for x in bases]): + attr_dict['precondition'] = precondition + + if all([not hasattr(x, 'on_package_attributes') for x in bases]): + attr_dict['on_package_attributes'] = on_package_attributes + + # Preconditions + _append_checks('preconditions') + # Sanity checks + _append_checks('sanity_checks') + return super(PackageMeta, meta).__new__(meta, name, bases, attr_dict) + + def __init__(cls, name, bases, dict): + type.__init__(cls, name, bases, dict) + spack.directives.ensure_dicts(cls) + + +class PackageBase(object): """This is the superclass for all spack packages. ***The Package class*** @@ -307,6 +474,7 @@ class Package(object): Package creators override functions like install() (all of them do this), clean() (some of them do this), and others to provide custom behavior. """ + __metaclass__ = PackageMeta # # These are default values for instance variables. # @@ -329,12 +497,6 @@ class Package(object): """ sanity_check_is_dir = [] - class __metaclass__(type): - """Ensure attributes required by Spack directives are present.""" - def __init__(cls, name, bases, dict): - type.__init__(cls, name, bases, dict) - spack.directives.ensure_dicts(cls) - def __init__(self, spec): # this determines how the package should be built. self.spec = spec @@ -411,6 +573,8 @@ class Package(object): if self.is_extension: spack.repo.get(self.extendee_spec)._check_extendable() + self.extra_args = {} + @property def package_dir(self): """Return the directory where the package.py file lives.""" @@ -826,7 +990,8 @@ class Package(object): 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) @@ -849,8 +1014,6 @@ class Package(object): resource_stage_folder = '-'.join(pieces) return resource_stage_folder - install_phases = set(['configure', 'build', 'install', 'provenance']) - def do_install(self, keep_prefix=False, keep_stage=False, @@ -862,7 +1025,7 @@ class Package(object): fake=False, explicit=False, dirty=False, - install_phases=install_phases): + **kwargs): """Called by commands to install a package and its dependencies. Package implementations should override install() to describe @@ -895,8 +1058,7 @@ class Package(object): return # Ensure package is not already installed - layout = spack.install_layout - if 'install' in install_phases and layout.check_installed(self.spec): + if 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: @@ -905,6 +1067,8 @@ class Package(object): rec.explicit = True return + self._do_install_pop_kwargs(kwargs) + tty.msg("Installing %s" % self.name) # First, install dependencies recursively. @@ -936,109 +1100,132 @@ class Package(object): else: self.do_stage() - tty.msg("Building %s" % self.name) + tty.msg("Building {0} [{1}]".format( + self.name, type(self).__base__)) 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 - with self.stage: - # Run the pre-install hook in the child process after - # the directory is created. - spack.hooks.pre_install(self) - - if fake: - self.do_fake_install() - else: - # Do the real install in the source directory. - self.stage.chdir_to_source() - - # Save the build environment in a file before building. - env_path = join_path(os.getcwd(), 'spack-build.env') - - try: + try: + with self.stage: + # Run the pre-install hook in the child process after + # the directory is created. + spack.hooks.pre_install(self) + if fake: + self.do_fake_install() + else: + # Do the real install in the source directory. + self.stage.chdir_to_source() + # Save the build environment in a file before building. + env_path = join_path(os.getcwd(), 'spack-build.env') # Redirect I/O to a build log (and optionally to # the terminal) log_path = join_path(os.getcwd(), 'spack-build.out') - log_file = open(log_path, 'w') - with log_output(log_file, verbose, sys.stdout.isatty(), - True): - dump_environment(env_path) - self.install(self.spec, self.prefix) - - except ProcessError as e: - # Annotate ProcessErrors with the location of - # the build log - e.build_log = log_path - 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 = layout.build_log_path(self.spec) - env_install_path = layout.build_env_path(self.spec) - packages_dir = layout.build_packages_path(self.spec) - - # Remove first if we're overwriting another build - # (can happen with spack setup) - try: - # log_install_path and env_install_path are here - shutil.rmtree(packages_dir) - 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. - spack.hooks.post_install(self) - - # Stop timer. - self._total_time = time.time() - start_time - build_time = self._total_time - self._fetch_time - - tty.msg("Successfully installed %s" % self.name, - "Fetch: %s. Build: %s. Total: %s." % - (_hms(self._fetch_time), _hms(build_time), - _hms(self._total_time))) - print_pkg(self.prefix) + # FIXME : refactor this assignment + self.log_path = log_path + self.env_path = env_path + dump_environment(env_path) + # Spawn a daemon that reads from a pipe and redirects + # everything to log_path + redirection_context = log_output( + log_path, verbose, + sys.stdout.isatty(), + True + ) + with redirection_context as log_redirection: + for phase_name, phase in zip(self.phases, self._InstallPhase_phases): # NOQA: ignore=E501 + tty.msg( + 'Executing phase : \'{0}\''.format(phase_name) # NOQA: ignore=E501 + ) + # Redirect stdout and stderr to daemon pipe + with log_redirection: + getattr(self, phase)( + self.spec, self.prefix) + self.log() + # Run post install hooks before build stage is removed. + spack.hooks.post_install(self) + + # Stop timer. + self._total_time = time.time() - start_time + build_time = self._total_time - self._fetch_time + + tty.msg("Successfully installed %s" % self.name, + "Fetch: %s. Build: %s. Total: %s." % + (_hms(self._fetch_time), _hms(build_time), + _hms(self._total_time))) + print_pkg(self.prefix) + + except ProcessError as e: + # Annotate ProcessErrors with the location of + # the build log + e.build_log = log_path + raise e try: # 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: - # Abort install if install directory exists. - # But do NOT remove it (you'd be overwriting someone's data) - tty.warn("Keeping existing install prefix in place.") - raise - else: - # We're not installing anyway, so don't worry if someone - # else has already written in the install directory - pass - - try: + # Fork a child to do the actual installation spack.build_environment.fork(self, build_process, dirty=dirty) - except: - # remove the install prefix if anything went wrong during install. + # If we installed then we should keep the prefix + keep_prefix = True if self.last_phase is None else keep_prefix + # note: PARENT of the build process adds the new package to + # the database, so that we don't need to re-read from file. + spack.installed_db.add(self.spec, self.prefix, explicit=explicit) + except directory_layout.InstallDirectoryAlreadyExistsError: + # Abort install if install directory exists. + # But do NOT remove it (you'd be overwriting someone else's stuff) + tty.warn("Keeping existing install prefix in place.") + raise + except StopIteration as e: + # A StopIteration exception means that do_install + # was asked to stop early from clients + tty.msg(e.message) + except Exception: + tty.warn("Keeping install prefix in place despite error.", + "Spack will think this package is installed. " + + "Manually remove this directory to fix:", + self.prefix, + wrap=False) + raise + finally: + # Remove the install prefix if anything went wrong during install. if not keep_prefix: self.remove_prefix() - else: - tty.warn("Keeping install prefix in place despite error.", - "Spack will think this package is installed. " + - "Manually remove this directory to fix:", - self.prefix, - wrap=False) - raise - # note: PARENT of the build process adds the new package to - # the database, so that we don't need to re-read from file. - spack.installed_db.add(self.spec, self.prefix, explicit=explicit) + def _do_install_pop_kwargs(self, kwargs): + """Pops kwargs from do_install before starting the installation + + Args: + kwargs: + 'stop_at': last installation phase to be executed (or None) + + """ + self.last_phase = kwargs.pop('stop_at', None) + if self.last_phase is not None and self.last_phase not in self.phases: + tty.die('\'{0.last_phase}\' is not among the allowed phases for package {0.name}'.format(self)) # NOQA: ignore=E501 + + def log(self): + # 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: + # log_install_path and env_install_path are inside this + shutil.rmtree(packages_dir) + 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.""" @@ -1197,13 +1384,6 @@ class Package(object): """ pass - def install(self, spec, prefix): - """ - Package implementations override this with their own configuration - """ - raise InstallError("Package %s provides no install method!" % - self.name) - def do_uninstall(self, force=False): if not self.installed: # prefix may not exist, but DB may be inconsistent. Try to fix by @@ -1413,6 +1593,128 @@ class Package(object): return " ".join("-Wl,-rpath,%s" % p for p in self.rpath) +class Package(PackageBase): + 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'] + + def autoreconf(self, spec, prefix): + """Not needed usually, configure should be already there""" + pass + + @PackageBase.sanity_check('autoreconf') + def is_configure_or_die(self): + if not os.path.exists('configure'): + raise RuntimeError( + 'configure script not found in {0}'.format(os.getcwd())) + + def configure_args(self): + return list() + + def configure(self, spec, prefix): + options = ['--prefix={0}'.format(prefix)] + self.configure_args() + inspect.getmodule(self).configure(*options) + + def build(self, spec, prefix): + inspect.getmodule(self).make() + + def install(self, spec, prefix): + inspect.getmodule(self).make('install') + + # This will be used as a registration decorator in user + # packages, if need be + PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix) + + +class CMakePackage(PackageBase): + phases = ['cmake', 'build', 'install'] + + def build_type(self): + return 'RelWithDebInfo' + + def root_cmakelists_dir(self): + return self.source_directory + + @property + def std_cmake_args(self): + # standard CMake arguments + return CMakePackage._std_args(self) + + @staticmethod + def _std_args(pkg): + try: + build_type = pkg.build_type() + except AttributeError: + build_type = 'RelWithDebInfo' + + args = ['-DCMAKE_INSTALL_PREFIX:PATH={0}'.format(pkg.prefix), + '-DCMAKE_BUILD_TYPE:STRING={0}'.format(build_type)] + if platform.mac_ver()[0]: + args.append('-DCMAKE_FIND_FRAMEWORK:STRING=LAST') + + # Set up CMake rpath + args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=FALSE') + rpaths = ':'.join(spack.build_environment.get_rpaths(pkg)) + args.append('-DCMAKE_INSTALL_RPATH:STRING={0}'.format(rpaths)) + return args + + def wdir(self): + return join_path(self.stage.source_path, 'spack-build') + + def cmake_args(self): + return list() + + def cmake(self, spec, prefix): + options = [self.root_cmakelists_dir()] + self.std_cmake_args + \ + self.cmake_args() + create = not os.path.exists(self.wdir()) + with working_dir(self.wdir(), create=create): + inspect.getmodule(self).cmake(*options) + + def build(self, spec, prefix): + with working_dir(self.wdir()): + inspect.getmodule(self).make() + + def install(self, spec, prefix): + with working_dir(self.wdir()): + inspect.getmodule(self).make('install') + + PackageBase.sanity_check('install')(PackageBase.sanity_check_prefix) + + def install_dependency_symlinks(pkg, spec, prefix): """Execute a dummy install and flatten dependencies""" flatten_dependencies(spec, prefix) @@ -1523,166 +1825,6 @@ def _hms(seconds): return ' '.join(parts) -class StagedPackage(Package): - """A Package subclass where the install() is split up into stages.""" - - def install_setup(self): - """Creates a spack_setup.py script to configure the package later.""" - 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'): - pass - - -# stackoverflow.com/questions/12791997/how-do-you-do-a-simple-chmod-x-from-within-python -def make_executable(path): - mode = os.stat(path).st_mode - mode |= (mode & 0o444) >> 2 # copy R bits to X - os.chmod(path, mode) - - -class CMakePackage(StagedPackage): - - def make_make(self): - import multiprocessing - # number of jobs spack will to build with. - jobs = multiprocessing.cpu_count() - if not self.parallel: - jobs = 1 - elif self.make_jobs: - jobs = self.make_jobs - - make = spack.build_environment.MakeExecutable('make', jobs) - return make - - def configure_args(self): - """Returns package-specific arguments to be provided to - the configure command. - """ - return list() - - def configure_env(self): - """Returns package-specific environment under which the - configure command should be run. - """ - return dict() - - def transitive_inc_path(self): - return ';'.join( - os.path.join(dep, 'include') - for dep in os.environ['SPACK_DEPENDENCIES'].split(os.pathsep) - ) - - def install_setup(self): - cmd = [str(which('cmake'))] - cmd += spack.build_environment.get_std_cmake_args(self) - cmd += ['-DCMAKE_INSTALL_PREFIX=%s' % os.environ['SPACK_PREFIX'], - '-DCMAKE_C_COMPILER=%s' % os.environ['SPACK_CC'], - '-DCMAKE_CXX_COMPILER=%s' % os.environ['SPACK_CXX'], - '-DCMAKE_Fortran_COMPILER=%s' % os.environ['SPACK_FC']] - cmd += self.configure_args() - - env = { - 'PATH': os.environ['PATH'], - 'SPACK_TRANSITIVE_INCLUDE_PATH': self.transitive_inc_path(), - 'CMAKE_PREFIX_PATH': os.environ['CMAKE_PREFIX_PATH'] - } - - setup_fname = 'spconfig.py' - with open(setup_fname, 'w') as fout: - fout.write(r"""#!%s -# - -import sys -import os -import subprocess - -def cmdlist(str): - return list(x.strip().replace("'",'') for x in str.split('\n') if x) -env = dict(os.environ) -""" % sys.executable) - - env_vars = sorted(list(env.keys())) - for name in env_vars: - val = env[name] - if string.find(name, 'PATH') < 0: - fout.write('env[%s] = %s\n' % (repr(name), repr(val))) - else: - if name == 'SPACK_TRANSITIVE_INCLUDE_PATH': - sep = ';' - else: - sep = ':' - - fout.write('env[%s] = "%s".join(cmdlist("""\n' - % (repr(name), sep)) - for part in string.split(val, sep): - fout.write(' %s\n' % part) - fout.write('"""))\n') - - fout.write("env['CMAKE_TRANSITIVE_INCLUDE_PATH'] = " - "env['SPACK_TRANSITIVE_INCLUDE_PATH'] # Deprecated\n") - fout.write('\ncmd = cmdlist("""\n') - fout.write('%s\n' % cmd[0]) - for arg in cmd[1:]: - fout.write(' %s\n' % arg) - fout.write('""") + sys.argv[1:]\n') - fout.write('\nproc = subprocess.Popen(cmd, env=env)\n') - fout.write('proc.wait()\n') - make_executable(setup_fname) - - def install_configure(self): - cmake = which('cmake') - with working_dir(self.build_directory, create=True): - env = os.environ - env.update(self.configure_env()) - env['SPACK_TRANSITIVE_INCLUDE_PATH'] = self.transitive_inc_path() - - options = self.configure_args() - options += spack.build_environment.get_std_cmake_args(self) - cmake(self.source_directory, *options) - - def install_build(self): - make = self.make_make() - with working_dir(self.build_directory, create=False): - make() - - def install_install(self): - make = self.make_make() - with working_dir(self.build_directory, create=False): - make('install') - - class FetchError(spack.error.SpackError): """Raised when something goes wrong during fetch.""" diff --git a/var/spack/repos/builtin/packages/astyle/package.py b/var/spack/repos/builtin/packages/astyle/package.py index eecdcfdad7..7acb77b304 100644 --- a/var/spack/repos/builtin/packages/astyle/package.py +++ b/var/spack/repos/builtin/packages/astyle/package.py @@ -25,28 +25,24 @@ 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. """ homepage = "http://astyle.sourceforge.net/" - url = "http://downloads.sourceforge.net/project/astyle/astyle/astyle%202.04/astyle_2.04_linux.tar.gz" + url = "http://downloads.sourceforge.net/project/astyle/astyle/astyle%202.04/astyle_2.04_linux.tar.gz" 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)] diff --git a/var/spack/repos/builtin/packages/blitz/package.py b/var/spack/repos/builtin/packages/blitz/package.py index c92e49a732..16ad3bc2ab 100644 --- a/var/spack/repos/builtin/packages/blitz/package.py +++ b/var/spack/repos/builtin/packages/blitz/package.py @@ -25,16 +25,9 @@ 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" + 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") diff --git a/var/spack/repos/builtin/packages/gmp/package.py b/var/spack/repos/builtin/packages/gmp/package.py index d85330dd6e..bd1f653bd7 100644 --- a/var/spack/repos/builtin/packages/gmp/package.py +++ b/var/spack/repos/builtin/packages/gmp/package.py @@ -25,12 +25,12 @@ from spack import * -class Gmp(Package): - """GMP is a free library for arbitrary precision arithmetic, operating - on signed integers, rational numbers, and floating-point numbers.""" - +class Gmp(AutotoolsPackage): + """GMP is a free library for arbitrary precision arithmetic, + operating on signed integers, rational numbers, and + floating-point numbers.""" homepage = "https://gmplib.org" - url = "https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2" + url = "https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2" version('6.1.1', '4c175f86e11eb32d8bf9872ca3a8e11d') version('6.1.0', '86ee6e54ebfc4a90b643a65e402c4048') @@ -38,8 +38,3 @@ class Gmp(Package): version('6.0.0', '6ef5869ae735db9995619135bd856b84') depends_on("m4", type='build') - - def install(self, spec, prefix): - configure("--prefix=%s" % prefix) - make() - make("install") diff --git a/var/spack/repos/builtin/packages/hdf5/package.py b/var/spack/repos/builtin/packages/hdf5/package.py index aedaf18218..601e3278e5 100644 --- a/var/spack/repos/builtin/packages/hdf5/package.py +++ b/var/spack/repos/builtin/packages/hdf5/package.py @@ -27,10 +27,10 @@ from spack import * import shutil -class Hdf5(Package): +class Hdf5(AutotoolsPackage): """HDF5 is a data model, library, and file format for storing and managing - data. It supports an unlimited variety of datatypes, and is designed for - flexible and efficient I/O and for high volume and complex data. + data. It supports an unlimited variety of datatypes, and is designed for + flexible and efficient I/O and for high volume and complex data. """ homepage = "http://www.hdfgroup.org/HDF5/" @@ -57,17 +57,19 @@ class Hdf5(Package): variant('threadsafe', default=False, description='Enable thread-safe capabilities') - depends_on("mpi", when='+mpi') - depends_on("szip", when='+szip') - depends_on("zlib") + depends_on('mpi', when='+mpi') + depends_on('szip', when='+szip') + depends_on('zlib') - def validate(self, spec): + @AutotoolsPackage.precondition('configure') + def validate(self): """ Checks if incompatible variants have been activated at the same time :param spec: spec of the package :raises RuntimeError: in case of inconsistencies """ + spec = self.spec if '+fortran' in spec and not self.compiler.fc: msg = 'cannot build a fortran variant without a fortran compiler' raise RuntimeError(msg) @@ -76,8 +78,8 @@ class Hdf5(Package): msg = 'cannot use variant +threadsafe with either +cxx or +fortran' raise RuntimeError(msg) - def install(self, spec, prefix): - self.validate(spec) + def configure_args(self): + spec = self.spec # Handle compilation after spec validation extra_args = [] @@ -140,17 +142,14 @@ class Hdf5(Package): '--disable-hl', ]) - configure( - "--prefix=%s" % prefix, - "--with-zlib=%s" % spec['zlib'].prefix, - *extra_args) - make() - make("install") - self.check_install(spec) + return ["--with-zlib=%s" % spec['zlib'].prefix] + extra_args - def check_install(self, spec): + @AutotoolsPackage.sanity_check('install') + @AutotoolsPackage.on_package_attributes(run_tests=True) + def check_install(self): "Build and run a small program to test the installed HDF5 library" - print "Checking HDF5 installation..." + spec = self.spec + print("Checking HDF5 installation...") checkdir = "spack-check" with working_dir(checkdir, create=True): source = r""" @@ -187,15 +186,15 @@ HDF5 version {version} {version} output = "" success = output == expected if not success: - print "Produced output does not match expected output." - print "Expected output:" - print '-' * 80 - print expected - print '-' * 80 - print "Produced output:" - print '-' * 80 - print output - print '-' * 80 + print("Produced output does not match expected output.") + print("Expected output:") + print('-' * 80) + print(expected) + print('-' * 80) + print("Produced output:") + print('-' * 80) + print(output) + print('-' * 80) raise RuntimeError("HDF5 install check failed") shutil.rmtree(checkdir) diff --git a/var/spack/repos/builtin/packages/ibmisc/package.py b/var/spack/repos/builtin/packages/ibmisc/package.py index 736886df96..172204a75b 100644 --- a/var/spack/repos/builtin/packages/ibmisc/package.py +++ b/var/spack/repos/builtin/packages/ibmisc/package.py @@ -43,7 +43,7 @@ class Ibmisc(CMakePackage): depends_on('cmake', type='build') depends_on('doxygen', type='build') - def configure_args(self): + def cmake_args(self): spec = self.spec return [ '-DUSE_EVERYTRACE=%s' % ('YES' if '+everytrace' in spec else 'NO'), diff --git a/var/spack/repos/builtin/packages/lzo/package.py b/var/spack/repos/builtin/packages/lzo/package.py index 1c44ced4d4..dc8f316a72 100644 --- a/var/spack/repos/builtin/packages/lzo/package.py +++ b/var/spack/repos/builtin/packages/lzo/package.py @@ -25,7 +25,7 @@ from spack import * -class Lzo(Package): +class Lzo(AutotoolsPackage): """Real-time data compression library""" homepage = 'https://www.oberhumer.com/opensource/lzo/' @@ -37,15 +37,14 @@ class Lzo(Package): version('2.06', '95380bd4081f85ef08c5209f4107e9f8') version('2.05', 'c67cda5fa191bab761c7cb06fe091e36') - def install(self, spec, prefix): - configure_args = [ - '--prefix={0}'.format(prefix), + def configure_args(self): + return [ '--disable-dependency-tracking', '--enable-shared' ] - configure(*configure_args) - make() - if self.run_tests: - make('check') - make('test') # more exhaustive test - make('install') + + @AutotoolsPackage.sanity_check('build') + @AutotoolsPackage.on_package_attributes(run_tests=True) + def check(self): + make('check') + make('test') diff --git a/var/spack/repos/builtin/packages/openjpeg/package.py b/var/spack/repos/builtin/packages/openjpeg/package.py index fc505e19a9..988ebc155d 100644 --- a/var/spack/repos/builtin/packages/openjpeg/package.py +++ b/var/spack/repos/builtin/packages/openjpeg/package.py @@ -25,8 +25,9 @@ from spack import * -class Openjpeg(Package): +class Openjpeg(CMakePackage): """OpenJPEG is an open-source JPEG 2000 codec written in C language. + It has been developed in order to promote the use of JPEG 2000, a still-image compression standard from the Joint Photographic Experts Group (JPEG). @@ -35,7 +36,7 @@ class Openjpeg(Package): """ homepage = "https://github.com/uclouvain/openjpeg" - url = "https://github.com/uclouvain/openjpeg/archive/version.2.1.tar.gz" + url = "https://github.com/uclouvain/openjpeg/archive/version.2.1.tar.gz" version('2.1', '3e1c451c087f8462955426da38aa3b3d') version('2.0.1', '105876ed43ff7dbb2f90b41b5a43cfa5') @@ -44,9 +45,3 @@ class Openjpeg(Package): version('1.5.1', 'd774e4b5a0db5f0f171c4fc0aabfa14e') depends_on('cmake', type='build') - - def install(self, spec, prefix): - cmake('.', *std_cmake_args) - - make() - make("install") diff --git a/var/spack/repos/builtin/packages/qhull/package.py b/var/spack/repos/builtin/packages/qhull/package.py index 462a681ad9..e221bf1552 100644 --- a/var/spack/repos/builtin/packages/qhull/package.py +++ b/var/spack/repos/builtin/packages/qhull/package.py @@ -25,7 +25,7 @@ from spack import * -class Qhull(Package): +class Qhull(CMakePackage): """Qhull computes the convex hull, Delaunay triangulation, Voronoi diagram, halfspace intersection about a point, furt hest-site Delaunay triangulation, and furthest-site Voronoi diagram. The @@ -45,8 +45,7 @@ class Qhull(Package): depends_on('cmake@2.6:', type='build') - def install(self, spec, prefix): - with working_dir('spack-build', create=True): - cmake('..', *std_cmake_args) - make() - make("install") + @CMakePackage.sanity_check('build') + @CMakePackage.on_package_attributes(run_tests=True) + def check(self): + make('test') diff --git a/var/spack/repos/builtin/packages/swiftsim/package.py b/var/spack/repos/builtin/packages/swiftsim/package.py index c591c48d19..1c424b5ca0 100644 --- a/var/spack/repos/builtin/packages/swiftsim/package.py +++ b/var/spack/repos/builtin/packages/swiftsim/package.py @@ -26,7 +26,7 @@ from spack import * import llnl.util.tty as tty -class Swiftsim(Package): +class Swiftsim(AutotoolsPackage): """SPH With Inter-dependent Fine-grained Tasking (SWIFT) provides astrophysicists with a state of the art framework to perform particle based simulations. @@ -58,20 +58,15 @@ class Swiftsim(Package): tty.warn('This is needed to clone SWIFT repository') spack_env.set('GIT_SSL_NO_VERIFY', 1) - def install(self, spec, prefix): - # Generate configure from configure.ac - # and Makefile.am + def autoreconf(self, spec, prefix): libtoolize() aclocal() autoconf() autogen = Executable('./autogen.sh') autogen() - # Configure and install - options = ['--prefix=%s' % prefix, - '--enable-mpi' if '+mpi' in spec else '--disable-mpi', - '--with-metis={0}'.format(spec['metis'].prefix), - '--enable-optimization'] - configure(*options) - make() - make("install") + 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'] diff --git a/var/spack/repos/builtin/packages/szip/package.py b/var/spack/repos/builtin/packages/szip/package.py index b2ca6f3995..91934f7d03 100644 --- a/var/spack/repos/builtin/packages/szip/package.py +++ b/var/spack/repos/builtin/packages/szip/package.py @@ -25,24 +25,21 @@ from spack import * -class Szip(Package): - """An implementation of the extended-Rice lossless compression algorithm. - It provides lossless compression of scientific data, and is provided - with HDF software products. +class Szip(AutotoolsPackage): + """Szip is an implementation of the extended-Rice lossless + compression algorithm. + It provides lossless compression of scientific data, and is + provided with HDF software products. """ homepage = "https://www.hdfgroup.org/doc_resource/SZIP/" - url = "http://www.hdfgroup.org/ftp/lib-external/szip/2.1/src/szip-2.1.tar.gz" + url = "http://www.hdfgroup.org/ftp/lib-external/szip/2.1/src/szip-2.1.tar.gz" version('2.1', '902f831bcefb69c6b635374424acbead') - def install(self, spec, prefix): - configure('--prefix=%s' % prefix, - '--enable-production', - '--enable-shared', - '--enable-static', - '--enable-encoding') - - make() - make("install") + def configure_args(self): + return ['--enable-production', + '--enable-shared', + '--enable-static', + '--enable-encoding'] |