summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/spack89
-rw-r--r--lib/spack/llnl/util/lang.py31
-rw-r--r--lib/spack/llnl/util/link_tree.py4
-rw-r--r--lib/spack/spack/build_environment.py56
-rw-r--r--lib/spack/spack/cmd/activate.py6
-rw-r--r--lib/spack/spack/cmd/deactivate.py57
-rw-r--r--lib/spack/spack/cmd/extensions.py4
-rw-r--r--lib/spack/spack/cmd/find.py5
-rw-r--r--lib/spack/spack/directory_layout.py165
-rw-r--r--lib/spack/spack/hooks/extensions.py2
-rw-r--r--lib/spack/spack/package.py112
-rw-r--r--lib/spack/spack/packages.py12
-rw-r--r--var/spack/packages/R/package.py33
-rw-r--r--var/spack/packages/dyninst/package.py7
-rw-r--r--var/spack/packages/libgcrypt/package.py19
-rw-r--r--var/spack/packages/libgpg-error/package.py17
-rw-r--r--var/spack/packages/libpng/package.py5
-rw-r--r--var/spack/packages/libxml2/package.py3
-rw-r--r--var/spack/packages/libxslt/package.py24
-rw-r--r--var/spack/packages/py-ipython/package.py1
-rw-r--r--var/spack/packages/py-libxml2/package.py1
-rw-r--r--var/spack/packages/py-matplotlib/package.py9
-rw-r--r--var/spack/packages/py-mpi4py/package.py1
-rw-r--r--var/spack/packages/py-nose/package.py3
-rw-r--r--var/spack/packages/py-pylint/package.py4
-rw-r--r--var/spack/packages/py-pyqt/package.py (renamed from var/spack/packages/py-pyqt4/package.py)16
-rw-r--r--var/spack/packages/py-pyside/package.py14
-rw-r--r--var/spack/packages/py-rpy2/package.py2
-rw-r--r--var/spack/packages/py-scientificpython/package.py6
-rw-r--r--var/spack/packages/py-shiboken/package.py24
-rw-r--r--var/spack/packages/py-sip/package.py7
-rw-r--r--var/spack/packages/py-virtualenv/package.py1
-rw-r--r--var/spack/packages/python/package.py52
-rw-r--r--var/spack/packages/qhull/package.py27
-rw-r--r--var/spack/packages/qt/package.py6
-rw-r--r--var/spack/packages/tcl/package.py22
-rw-r--r--var/spack/packages/tk/package.py22
37 files changed, 680 insertions, 189 deletions
diff --git a/bin/spack b/bin/spack
index 626d9d9d11..c49caf37f9 100755
--- a/bin/spack
+++ b/bin/spack
@@ -58,14 +58,16 @@ parser = argparse.ArgumentParser(
description='Spack: the Supercomputing PACKage Manager.')
parser.add_argument('-V', '--version', action='version',
version="%s" % spack.spack_version)
-parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
+parser.add_argument('-v', '--verbose', action='store_true',
help="Print additional output during builds")
-parser.add_argument('-d', '--debug', action='store_true', dest='debug',
+parser.add_argument('-d', '--debug', action='store_true',
help="Write out debug logs during compile")
-parser.add_argument('-k', '--insecure', action='store_true', dest='insecure',
+parser.add_argument('-k', '--insecure', action='store_true',
help="Do not check ssl certificates when downloading archives.")
-parser.add_argument('-m', '--mock', action='store_true', dest='mock',
+parser.add_argument('-m', '--mock', action='store_true',
help="Use mock packages instead of real ones.")
+parser.add_argument('-p', '--profile', action='store_true',
+ help="Profile execution using cProfile.")
# each command module implements a parser() function, to which we pass its
# subparser for setup.
@@ -85,42 +87,49 @@ if len(sys.argv) == 1:
# actually parse the args.
args = parser.parse_args()
-# Set up environment based on args.
-tty.set_verbose(args.verbose)
-tty.set_debug(args.debug)
-spack.debug = args.debug
-
-spack.spack_working_dir = working_dir
-if args.mock:
- from spack.packages import PackageDB
- spack.db = PackageDB(spack.mock_packages_path)
-
-# If the user asked for it, don't check ssl certs.
-if args.insecure:
- tty.warn("You asked for --insecure, which does not check SSL certificates or checksums.")
- spack.curl.add_default_arg('-k')
-
-# Try to load the particular command asked for and run it
-command = spack.cmd.get_command(args.command)
-try:
- return_val = command(parser, args)
-except SpackError, e:
- if spack.debug:
- # In debug mode, raise with a full stack trace.
- raise
- elif e.long_message:
- tty.die(e.message, e.long_message)
+def main():
+ # Set up environment based on args.
+ tty.set_verbose(args.verbose)
+ tty.set_debug(args.debug)
+ spack.debug = args.debug
+
+ spack.spack_working_dir = working_dir
+ if args.mock:
+ from spack.packages import PackageDB
+ spack.db = PackageDB(spack.mock_packages_path)
+
+ # If the user asked for it, don't check ssl certs.
+ if args.insecure:
+ tty.warn("You asked for --insecure, which does not check SSL certificates or checksums.")
+ spack.curl.add_default_arg('-k')
+
+ # Try to load the particular command asked for and run it
+ command = spack.cmd.get_command(args.command)
+ try:
+ return_val = command(parser, args)
+ except SpackError, e:
+ if spack.debug:
+ # In debug mode, raise with a full stack trace.
+ raise
+ elif e.long_message:
+ tty.die(e.message, e.long_message)
+ else:
+ tty.die(e.message)
+
+ except KeyboardInterrupt:
+ sys.stderr.write('\n')
+ tty.die("Keyboard interrupt.")
+
+ # Allow commands to return values if they want to exit with some ohter code.
+ if return_val is None:
+ sys.exit(0)
+ elif isinstance(return_val, int):
+ sys.exit(return_val)
else:
- tty.die(e.message)
-
-except KeyboardInterrupt:
- sys.stderr.write('\n')
- tty.die("Keyboard interrupt.")
+ tty.die("Bad return value from command %s: %s" % (args.command, return_val))
-# Allow commands to return values if they want to exit with some ohter code.
-if return_val is None:
- sys.exit(0)
-elif isinstance(return_val, int):
- sys.exit(return_val)
+if args.profile:
+ import cProfile
+ cProfile.run('main()', sort='tottime')
else:
- tty.die("Bad return value from command %s: %s" % (args.command, return_val))
+ main()
diff --git a/lib/spack/llnl/util/lang.py b/lib/spack/llnl/util/lang.py
index db15da0506..332367f537 100644
--- a/lib/spack/llnl/util/lang.py
+++ b/lib/spack/llnl/util/lang.py
@@ -291,6 +291,37 @@ def check_kwargs(kwargs, fun):
% (next(kwargs.iterkeys()), fun.__name__))
+def match_predicate(*args):
+ """Utility function for making string matching predicates.
+
+ Each arg can be a:
+ - regex
+ - list or tuple of regexes
+ - predicate that takes a string.
+
+ This returns a predicate that is true if:
+ - any arg regex matches
+ - any regex in a list or tuple of regexes matches.
+ - any predicate in args matches.
+ """
+ def match(string):
+ for arg in args:
+ if isinstance(arg, basestring):
+ if re.search(arg, string):
+ return True
+ elif isinstance(arg, list) or isinstance(arg, tuple):
+ if any(re.search(i, string) for i in arg):
+ return True
+ elif callable(arg):
+ if arg(string):
+ return True
+ else:
+ raise ValueError("args to match_predicate must be regex, "
+ "list of regexes, or callable.")
+ return False
+ return match
+
+
class RequiredAttributeError(ValueError):
def __init__(self, message):
super(RequiredAttributeError, self).__init__(message)
diff --git a/lib/spack/llnl/util/link_tree.py b/lib/spack/llnl/util/link_tree.py
index 4e4e48316e..4d778eca1e 100644
--- a/lib/spack/llnl/util/link_tree.py
+++ b/lib/spack/llnl/util/link_tree.py
@@ -175,6 +175,10 @@ class LinkTree(object):
kwargs['order'] = 'post'
for src, dest in traverse_tree(self._root, dest_root, **kwargs):
if os.path.isdir(src):
+ # Skip non-existing links.
+ if not os.path.exists(dest):
+ continue
+
if not os.path.isdir(dest):
raise ValueError("File blocks directory: %s" % dest)
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index 5e8c52cb3c..59b25d96e7 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -28,6 +28,7 @@ Skimming this module is a nice way to get acquainted with the types of
calls you can make from within the install() function.
"""
import os
+import sys
import shutil
import multiprocessing
import platform
@@ -226,3 +227,58 @@ def setup_package(pkg):
for dep_spec in pkg.spec.traverse(root=False):
dep_spec.package.setup_dependent_environment(
pkg.module, dep_spec, pkg.spec)
+
+
+def fork(pkg, function):
+ """Fork a child process to do part of a spack build.
+
+ Arguments:
+
+ pkg -- pkg whose environemnt we should set up the
+ forked process for.
+ function -- arg-less function to run in the child process.
+
+ Usage:
+ def child_fun():
+ # do stuff
+ build_env.fork(pkg, child_fun)
+
+ Forked processes are run with the build environemnt set up by
+ spack.build_environment. This allows package authors to have
+ full control over the environment, etc. without offecting
+ other builds that might be executed in the same spack call.
+
+ If something goes wrong, the child process is expected toprint
+ the error and the parent process will exit with error as
+ well. If things go well, the child exits and the parent
+ carries on.
+ """
+ try:
+ pid = os.fork()
+ except OSError, e:
+ raise InstallError("Unable to fork build process: %s" % e)
+
+ if pid == 0:
+ # Give the child process the package's build environemnt.
+ setup_package(pkg)
+
+ try:
+ # call the forked function.
+ function()
+
+ # Use os._exit here to avoid raising a SystemExit exception,
+ # which interferes with unit tests.
+ os._exit(0)
+ 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:
+ sys.exit(1)
diff --git a/lib/spack/spack/cmd/activate.py b/lib/spack/spack/cmd/activate.py
index c1e23852d6..71eca4f453 100644
--- a/lib/spack/spack/cmd/activate.py
+++ b/lib/spack/spack/cmd/activate.py
@@ -31,6 +31,9 @@ description = "Activate a package extension."
def setup_parser(subparser):
subparser.add_argument(
+ '-f', '--force', action='store_true',
+ help="Activate without first activating dependencies.")
+ subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help="spec of package extension to activate.")
@@ -44,6 +47,9 @@ def activate(parser, args):
spack.db.get(specs[0])
spec = spack.cmd.disambiguate_spec(specs[0])
+ if not spec.package.is_extension:
+ tty.die("%s is not an extension." % spec.name)
+
if spec.package.activated:
tty.die("Package %s is already activated." % specs[0].short_spec)
diff --git a/lib/spack/spack/cmd/deactivate.py b/lib/spack/spack/cmd/deactivate.py
index fd13f051df..bfec618c8e 100644
--- a/lib/spack/spack/cmd/deactivate.py
+++ b/lib/spack/spack/cmd/deactivate.py
@@ -24,13 +24,22 @@
##############################################################################
from external import argparse
import llnl.util.tty as tty
+
import spack
import spack.cmd
+from spack.graph import topological_sort
description = "Deactivate a package extension."
def setup_parser(subparser):
subparser.add_argument(
+ '-f', '--force', action='store_true',
+ help="Run deactivation even if spec is NOT currently activated.")
+ subparser.add_argument(
+ '-a', '--all', action='store_true',
+ help="Deactivate all extensions of an extendable pacakge, or "
+ "deactivate an extension AND its dependencies.")
+ subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help="spec of package extension to deactivate.")
@@ -39,12 +48,52 @@ def deactivate(parser, args):
if len(specs) != 1:
tty.die("deactivate requires one spec. %d given." % len(specs))
- # TODO: remove this hack when DAG info is stored in dir layout.
+ # TODO: remove this hack when DAG info is stored properly.
# This ensures the ext spec is always normalized properly.
spack.db.get(specs[0])
spec = spack.cmd.disambiguate_spec(specs[0])
- if not spec.package.activated:
- tty.die("Package %s is not activated." % specs[0].short_spec)
+ pkg = spec.package
+
+ if args.all:
+ if pkg.extendable:
+ tty.msg("Deactivating all extensions of %s" % pkg.spec.short_spec)
+ ext_pkgs = spack.db.installed_extensions_for(spec)
+ for ext_pkg in ext_pkgs:
+ ext_pkg.spec.normalize()
+ if ext_pkg.activated:
+ ext_pkg.do_deactivate(force=True)
+
+ elif pkg.is_extension:
+ # TODO: store DAG info properly (see above)
+ spec.normalize()
+
+ tty.msg("Deactivating %s and all dependencies." % pkg.spec.short_spec)
+
+ topo_order = topological_sort(spec)
+ index = spec.index()
+
+ for name in topo_order:
+ espec = index[name]
+ epkg = espec.package
+
+ # TODO: store DAG info properly (see above)
+ epkg.spec.normalize()
+
+ if epkg.extends(pkg.extendee_spec):
+ if epkg.activated or args.force:
+
+ epkg.do_deactivate(force=args.force)
+
+ else:
+ tty.die("spack deactivate --all requires an extendable package or an extension.")
+
+ else:
+ if not pkg.is_extension:
+ tty.die("spack deactivate requires an extension.",
+ "Did you mean 'spack deactivate --all'?")
+
+ if not args.force and not spec.package.activated:
+ tty.die("Package %s is not activated." % specs[0].short_spec)
- spec.package.do_deactivate()
+ spec.package.do_deactivate(force=args.force)
diff --git a/lib/spack/spack/cmd/extensions.py b/lib/spack/spack/cmd/extensions.py
index ae73d8ac55..fc8e6842c3 100644
--- a/lib/spack/spack/cmd/extensions.py
+++ b/lib/spack/spack/cmd/extensions.py
@@ -89,10 +89,10 @@ def extensions(parser, args):
spack.cmd.find.display_specs(installed, mode=args.mode)
# List specs of activated extensions.
- activated = spack.install_layout.get_extensions(spec)
+ activated = spack.install_layout.extension_map(spec)
print
if not activated:
tty.msg("None activated.")
return
tty.msg("%d currently activated:" % len(activated))
- spack.cmd.find.display_specs(activated, mode=args.mode)
+ spack.cmd.find.display_specs(activated.values(), mode=args.mode)
diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py
index f6f503afe5..70b10edb4e 100644
--- a/lib/spack/spack/cmd/find.py
+++ b/lib/spack/spack/cmd/find.py
@@ -85,7 +85,7 @@ def display_specs(specs, **kwargs):
elif mode == 'deps':
for spec in specs:
- print spec.tree(indent=4, format='$_$@$+', color=True),
+ print spec.tree(indent=4, format='$_$@$+$#', color=True),
elif mode in ('short', 'long'):
fmt = '$-_$@$+'
@@ -122,5 +122,8 @@ def find(parser, args):
if not args.mode:
args.mode = 'short'
+
+ if sys.stdout.isatty():
+ tty.msg("%d installed packages." % len(specs))
display_specs(specs, mode=args.mode)
diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
index 37740720a2..b2cf5dc801 100644
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -27,9 +27,11 @@ import os
import exceptions
import hashlib
import shutil
+import tempfile
from contextlib import closing
import llnl.util.tty as tty
+from llnl.util.lang import memoized
from llnl.util.filesystem import join_path, mkdirp
import spack
@@ -84,17 +86,38 @@ class DirectoryLayout(object):
raise NotImplementedError()
- def get_extensions(self, spec):
- """Get a set of currently installed extension packages for a spec."""
+ def extension_map(self, spec):
+ """Get a dict of currently installed extension packages for a spec.
+
+ Dict maps { name : extension_spec }
+ Modifying dict does not affect internals of this layout.
+ """
+ raise NotImplementedError()
+
+
+ def check_extension_conflict(self, spec, ext_spec):
+ """Ensure that ext_spec can be activated in spec.
+
+ If not, raise ExtensionAlreadyInstalledError or
+ ExtensionConflictError.
+ """
+ raise NotImplementedError()
+
+
+ def check_activated(self, spec, ext_spec):
+ """Ensure that ext_spec can be removed from spec.
+
+ If not, raise NoSuchExtensionError.
+ """
raise NotImplementedError()
- def add_extension(self, spec, extension_spec):
+ def add_extension(self, spec, ext_spec):
"""Add to the list of currently installed extensions."""
raise NotImplementedError()
- def remove_extension(self, spec, extension_spec):
+ def remove_extension(self, spec, ext_spec):
"""Remove from the list of currently installed extensions."""
raise NotImplementedError()
@@ -173,6 +196,8 @@ class SpecHashDirectoryLayout(DirectoryLayout):
self.spec_file_name = spec_file_name
self.extension_file_name = extension_file_name
+ # Cache of already written/read extension maps.
+ self._extension_maps = {}
@property
def hidden_file_paths(self):
@@ -199,6 +224,9 @@ class SpecHashDirectoryLayout(DirectoryLayout):
if all(spack.db.exists(s.name) for s in spec.traverse()):
copy = spec.copy()
+
+ # TODO: It takes a lot of time to normalize every spec on read.
+ # TODO: Storing graph info with spec files would fix this.
copy.normalize()
if copy.concrete:
return copy # These are specs spack still understands.
@@ -252,17 +280,20 @@ class SpecHashDirectoryLayout(DirectoryLayout):
self.write_spec(spec, spec_file_path)
+ @memoized
def all_specs(self):
if not os.path.isdir(self.root):
- return
+ return []
+ specs = []
for path in traverse_dirs_at_depth(self.root, 3):
arch, compiler, last_dir = path
spec_file_path = join_path(
self.root, arch, compiler, last_dir, self.spec_file_name)
if os.path.exists(spec_file_path):
spec = self.read_spec(spec_file_path)
- yield spec
+ specs.append(spec)
+ return specs
def extension_file_path(self, spec):
@@ -271,54 +302,94 @@ class SpecHashDirectoryLayout(DirectoryLayout):
return join_path(self.path_for_spec(spec), self.extension_file_name)
- def get_extensions(self, spec):
+ def _extension_map(self, spec):
+ """Get a dict<name -> spec> for all extensions currnetly
+ installed for this package."""
_check_concrete(spec)
- extensions = set()
- path = self.extension_file_path(spec)
- if os.path.exists(path):
- with closing(open(path)) as ext_file:
- for line in ext_file:
- try:
- extensions.add(Spec(line.strip()))
- except spack.error.SpackError, e:
- raise InvalidExtensionSpecError(str(e))
- return extensions
+ if not spec in self._extension_maps:
+ path = self.extension_file_path(spec)
+ if not os.path.exists(path):
+ self._extension_maps[spec] = {}
+
+ else:
+ exts = {}
+ with closing(open(path)) as ext_file:
+ for line in ext_file:
+ try:
+ spec = Spec(line.strip())
+ exts[spec.name] = spec
+ except spack.error.SpackError, e:
+ # TODO: do something better here -- should be
+ # resilient to corrupt files.
+ raise InvalidExtensionSpecError(str(e))
+ self._extension_maps[spec] = exts
+
+ return self._extension_maps[spec]
+
+
+ def extension_map(self, spec):
+ """Defensive copying version of _extension_map() for external API."""
+ return self._extension_map(spec).copy()
+
+
+ def check_extension_conflict(self, spec, ext_spec):
+ exts = self._extension_map(spec)
+ if ext_spec.name in exts:
+ installed_spec = exts[ext_spec.name]
+ if ext_spec == installed_spec:
+ raise ExtensionAlreadyInstalledError(spec, ext_spec)
+ else:
+ raise ExtensionConflictError(spec, ext_spec, installed_spec)
- def write_extensions(self, spec, extensions):
+ def check_activated(self, spec, ext_spec):
+ exts = self._extension_map(spec)
+ if (not ext_spec.name in exts) or (ext_spec != exts[ext_spec.name]):
+ raise NoSuchExtensionError(spec, ext_spec)
+
+
+ def _write_extensions(self, spec, extensions):
path = self.extension_file_path(spec)
- with closing(open(path, 'w')) as spec_file:
- for extension in sorted(extensions):
- spec_file.write("%s\n" % extension)
+
+ # Create a temp file in the same directory as the actual file.
+ dirname, basename = os.path.split(path)
+ tmp = tempfile.NamedTemporaryFile(
+ prefix=basename, dir=dirname, delete=False)
+
+ # Write temp file.
+ with closing(tmp):
+ for extension in sorted(extensions.values()):
+ tmp.write("%s\n" % extension)
+
+ # Atomic update by moving tmpfile on top of old one.
+ os.rename(tmp.name, path)
- def add_extension(self, spec, extension_spec):
+ def add_extension(self, spec, ext_spec):
_check_concrete(spec)
- _check_concrete(extension_spec)
+ _check_concrete(ext_spec)
- exts = self.get_extensions(spec)
- if extension_spec in exts:
- raise ExtensionAlreadyInstalledError(spec, extension_spec)
- else:
- for already_installed in exts:
- if spec.name == extension_spec.name:
- raise ExtensionConflictError(spec, extension_spec, already_installed)
+ # Check whether it's already installed or if it's a conflict.
+ exts = self._extension_map(spec)
+ self.check_extension_conflict(spec, ext_spec)
- exts.add(extension_spec)
- self.write_extensions(spec, exts)
+ # do the actual adding.
+ exts[ext_spec.name] = ext_spec
+ self._write_extensions(spec, exts)
- def remove_extension(self, spec, extension_spec):
+ def remove_extension(self, spec, ext_spec):
_check_concrete(spec)
- _check_concrete(extension_spec)
+ _check_concrete(ext_spec)
- exts = self.get_extensions(spec)
- if not extension_spec in exts:
- raise NoSuchExtensionError(spec, extension_spec)
+ # Make sure it's installed before removing.
+ exts = self._extension_map(spec)
+ self.check_activated(spec, ext_spec)
- exts.remove(extension_spec)
- self.write_extensions(spec, exts)
+ # do the actual removing.
+ del exts[ext_spec.name]
+ self._write_extensions(spec, exts)
class DirectoryLayoutError(SpackError):
@@ -365,24 +436,24 @@ class InvalidExtensionSpecError(DirectoryLayoutError):
class ExtensionAlreadyInstalledError(DirectoryLayoutError):
"""Raised when an extension is added to a package that already has it."""
- def __init__(self, spec, extension_spec):
+ def __init__(self, spec, ext_spec):
super(ExtensionAlreadyInstalledError, self).__init__(
- "%s is already installed in %s" % (extension_spec.short_spec, spec.short_spec))
+ "%s is already installed in %s" % (ext_spec.short_spec, spec.short_spec))
class ExtensionConflictError(DirectoryLayoutError):
"""Raised when an extension is added to a package that already has it."""
- def __init__(self, spec, extension_spec, conflict):
+ def __init__(self, spec, ext_spec, conflict):
super(ExtensionConflictError, self).__init__(
"%s cannot be installed in %s because it conflicts with %s."% (
- extension_spec.short_spec, spec.short_spec, conflict.short_spec))
+ ext_spec.short_spec, spec.short_spec, conflict.short_spec))
class NoSuchExtensionError(DirectoryLayoutError):
- """Raised when an extension isn't there on remove."""
- def __init__(self, spec, extension_spec):
+ """Raised when an extension isn't there on deactivate."""
+ def __init__(self, spec, ext_spec):
super(NoSuchExtensionError, self).__init__(
- "%s cannot be removed from %s because it's not installed."% (
- extension_spec.short_spec, spec.short_spec))
+ "%s cannot be removed from %s because it's not activated."% (
+ ext_spec.short_spec, spec.short_spec))
diff --git a/lib/spack/spack/hooks/extensions.py b/lib/spack/spack/hooks/extensions.py
index 9d6fa23d03..cf87a78c8c 100644
--- a/lib/spack/spack/hooks/extensions.py
+++ b/lib/spack/spack/hooks/extensions.py
@@ -33,4 +33,4 @@ def pre_uninstall(pkg):
if pkg.is_extension:
if pkg.activated:
- pkg.do_deactivate()
+ pkg.do_deactivate(force=True)
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index b18d054990..bc8541a184 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -529,12 +529,10 @@ class Package(object):
@property
def activated(self):
- if not self.spec.concrete:
- raise ValueError("Only concrete package extensions can be activated.")
if not self.is_extension:
raise ValueError("is_extension called on package that is not an extension.")
-
- return self.spec in spack.install_layout.get_extensions(self.extendee_spec)
+ exts = spack.install_layout.extension_map(self.extendee_spec)
+ return (self.name in exts) and (exts[self.name] == self.spec)
def preorder_traversal(self, visited=None, **kwargs):
@@ -803,32 +801,21 @@ class Package(object):
if not fake_install:
self.do_patch()
- # Fork a child process to do the build. This allows each
- # package authors to have full control over their environment,
- # etc. without offecting other builds that might be executed
- # in the same spack call.
- try:
- pid = os.fork()
- except OSError, e:
- raise InstallError("Unable to fork build process: %s" % e)
+ # create the install directory. The install layout
+ # handles this in case so that it can use whatever
+ # package naming scheme it likes.
+ spack.install_layout.make_path_for_spec(self.spec)
- if pid == 0:
+ def real_work():
try:
tty.msg("Building %s." % self.name)
- # create the install directory. The install layout
- # handles this in case so that it can use whatever
- # package naming scheme it likes.
- spack.install_layout.make_path_for_spec(self.spec)
-
# Run the pre-install hook in the child process after
# the directory is created.
spack.hooks.pre_install(self)
# Set up process's build environment before running install.
self.stage.chdir_to_source()
- build_env.setup_package(self)
-
if fake_install:
self.do_fake_install()
else:
@@ -847,14 +834,10 @@ class Package(object):
build_time = self._total_time - self._fetch_time
tty.msg("Successfully installed %s." % self.name,
- "Fetch: %.2f sec. Build: %.2f sec. Total: %.2f sec."
- % (self._fetch_time, build_time, self._total_time))
+ "Fetch: %s. Build: %s. Total: %s."
+ % (_hms(self._fetch_time), _hms(build_time), _hms(self._total_time)))
print_pkg(self.prefix)
- # Use os._exit here to avoid raising a SystemExit exception,
- # which interferes with unit tests.
- os._exit(0)
-
except:
if not keep_prefix:
# If anything goes wrong, remove the install prefix
@@ -864,24 +847,14 @@ class Package(object):
"Spack will think this package is installed." +
"Manually remove this directory to fix:",
self.prefix)
+ raise
- # 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)
-
- # 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:
- sys.exit(1)
+ build_env.fork(self, real_work)
# Once everything else is done, run post install hooks
spack.hooks.post_install(self)
-
def _sanity_check_install(self):
installed = set(os.listdir(self.prefix))
installed.difference_update(spack.install_layout.hidden_file_paths)
@@ -980,18 +953,33 @@ class Package(object):
raise ValueError("%s does not extend %s!" % (self.name, self.extendee.name))
- def do_activate(self):
+ def do_activate(self, **kwargs):
"""Called on an etension to invoke the extendee's activate method.
Commands should call this routine, and should not call
activate() directly.
"""
self._sanity_check_extension()
+ force = kwargs.get('force', False)
+
+ # TODO: get rid of this normalize - DAG handling.
+ self.spec.normalize()
+
+ spack.install_layout.check_extension_conflict(self.extendee_spec, self.spec)
+
+ if not force:
+ for spec in self.spec.traverse(root=False):
+ if spec.package.extends(self.extendee_spec):
+ # TODO: fix this normalize() requirement -- revisit DAG handling.
+ spec.package.spec.normalize()
+ if not spec.package.activated:
+ spec.package.do_activate(**kwargs)
+
self.extendee_spec.package.activate(self, **self.extendee_args)
spack.install_layout.add_extension(self.extendee_spec, self.spec)
tty.msg("Activated extension %s for %s."
- % (self.spec.short_spec, self.extendee_spec.short_spec))
+ % (self.spec.short_spec, self.extendee_spec.format("$_$@$+$%@")))
def activate(self, extension, **kwargs):
@@ -1014,16 +1002,32 @@ class Package(object):
tree.merge(self.prefix, ignore=ignore)
- def do_deactivate(self):
+ def do_deactivate(self, **kwargs):
"""Called on the extension to invoke extendee's deactivate() method."""
self._sanity_check_extension()
+ force = kwargs.get('force', False)
+
+ # Allow a force deactivate to happen. This can unlink
+ # spurious files if something was corrupted.
+ if not force:
+ spack.install_layout.check_activated(self.extendee_spec, self.spec)
+
+ activated = spack.install_layout.extension_map(self.extendee_spec)
+ for name, aspec in activated.items():
+ if aspec != self.spec and self.spec in aspec:
+ raise ActivationError(
+ "Cannot deactivate %s beacuse %s is activated and depends on it."
+ % (self.spec.short_spec, aspec.short_spec))
+
self.extendee_spec.package.deactivate(self, **self.extendee_args)
- if self.spec in spack.install_layout.get_extensions(self.extendee_spec):
+ # redundant activation check -- makes SURE the spec is not
+ # still activated even if something was wrong above.
+ if self.activated:
spack.install_layout.remove_extension(self.extendee_spec, self.spec)
tty.msg("Deactivated extension %s for %s."
- % (self.spec.short_spec, self.extendee_spec.short_spec))
+ % (self.spec.short_spec, self.extendee_spec.format("$_$@$+$%@")))
def deactivate(self, extension, **kwargs):
@@ -1188,6 +1192,18 @@ def print_pkg(message):
print message
+def _hms(seconds):
+ """Convert time in seconds to hours, minutes, seconds."""
+ m, s = divmod(seconds, 60)
+ h, m = divmod(m, 60)
+
+ parts = []
+ if h: parts.append("%dh" % h)
+ if m: parts.append("%dm" % m)
+ if s: parts.append("%.2fs" % s)
+ return ' '.join(parts)
+
+
class FetchError(spack.error.SpackError):
"""Raised when something goes wrong during fetch."""
def __init__(self, message, long_msg=None):
@@ -1236,7 +1252,15 @@ class NoURLError(PackageError):
"Package %s has no version with a URL." % cls.__name__)
-class ExtensionConflictError(PackageError):
+class ExtensionError(PackageError): pass
+
+
+class ExtensionConflictError(ExtensionError):
def __init__(self, path):
super(ExtensionConflictError, self).__init__(
"Extension blocked by file: %s" % path)
+
+
+class ActivationError(ExtensionError):
+ def __init__(self, msg, long_msg=None):
+ super(ActivationError, self).__init__(msg, long_msg)
diff --git a/lib/spack/spack/packages.py b/lib/spack/spack/packages.py
index 7ef8135c1a..43c4c191c1 100644
--- a/lib/spack/spack/packages.py
+++ b/lib/spack/spack/packages.py
@@ -119,8 +119,15 @@ class PackageDB(object):
@_autospec
def installed_extensions_for(self, extendee_spec):
- return [s.package for s in self.installed_package_specs()
- if s.package.extends(extendee_spec)]
+ for s in self.installed_package_specs():
+ try:
+ if s.package.extends(extendee_spec):
+ yield s.package
+ except UnknownPackageError, e:
+ # Skip packages we know nothing about
+ continue
+ # TODO: add some conditional way to do this instead of
+ # catching exceptions.
def dirname_for_package_name(self, pkg_name):
@@ -185,6 +192,7 @@ class PackageDB(object):
yield self.get(name)
+ @memoized
def exists(self, pkg_name):
"""Whether a package with the supplied name exists ."""
return os.path.exists(self.filename_for_package_name(pkg_name))
diff --git a/var/spack/packages/R/package.py b/var/spack/packages/R/package.py
new file mode 100644
index 0000000000..2e6f65a742
--- /dev/null
+++ b/var/spack/packages/R/package.py
@@ -0,0 +1,33 @@
+from spack import *
+
+class R(Package):
+ """R is 'GNU S', a freely available language and environment for
+ statistical computing and graphics which provides a wide va
+ riety of statistical and graphical techniques: linear and
+ nonlinear modelling, statistical tests, time series analysis,
+ classification, clustering, etc. Please consult the R project
+ homepage for further information."""
+ homepage = "http://www.example.com"
+ url = "http://cran.cnr.berkeley.edu/src/base/R-3/R-3.1.2.tar.gz"
+
+ version('3.1.2', '3af29ec06704cbd08d4ba8d69250ae74')
+
+ depends_on("readline")
+ depends_on("ncurses")
+ depends_on("icu")
+ depends_on("glib")
+ depends_on("zlib")
+ depends_on("libtiff")
+ depends_on("jpeg")
+ depends_on("cairo")
+ depends_on("pango")
+ depends_on("freetype")
+ depends_on("tcl")
+ depends_on("tk")
+
+ def install(self, spec, prefix):
+ configure("--prefix=%s" % prefix,
+ "--enable-R-shlib",
+ "--enable-BLAS-shlib")
+ make()
+ make("install")
diff --git a/var/spack/packages/dyninst/package.py b/var/spack/packages/dyninst/package.py
index 2e6f3e010a..f3d661f9a9 100644
--- a/var/spack/packages/dyninst/package.py
+++ b/var/spack/packages/dyninst/package.py
@@ -31,8 +31,9 @@ class Dyninst(Package):
url = "http://www.dyninst.org/sites/default/files/downloads/dyninst/8.1.2/DyninstAPI-8.1.2.tgz"
list_url = "http://www.dyninst.org/downloads/dyninst-8.x"
- version('8.2.1', 'abf60b7faabe7a2e4b54395757be39c7',
- url="http://www.paradyn.org/release8.2/DyninstAPI-8.2.1.tgz")
+# Doesn't build right with boost@1.55.0
+# version('8.2.1', 'abf60b7faabe7a2e4b54395757be39c7',
+# url="http://www.paradyn.org/release8.2/DyninstAPI-8.2.1.tgz")
version('8.1.2', 'bf03b33375afa66fe0efa46ce3f4b17a',
url="http://www.paradyn.org/release8.1.2/DyninstAPI-8.1.2.tgz")
version('8.1.1', 'd1a04e995b7aa70960cd1d1fac8bd6ac',
@@ -40,7 +41,7 @@ class Dyninst(Package):
depends_on("libelf")
depends_on("libdwarf")
- depends_on("boost@1.42:1.43")
+ depends_on("boost@1.42:")
# new version uses cmake
def install(self, spec, prefix):
diff --git a/var/spack/packages/libgcrypt/package.py b/var/spack/packages/libgcrypt/package.py
new file mode 100644
index 0000000000..1d0a57f317
--- /dev/null
+++ b/var/spack/packages/libgcrypt/package.py
@@ -0,0 +1,19 @@
+from spack import *
+
+class Libgcrypt(Package):
+ """Libgcrypt is a general purpose cryptographic library based on
+ the code from GnuPG. It provides functions for all cryptographic
+ building blocks: symmetric ciphers, hash algorithms, MACs, public
+ key algorithms, large integer functions, random numbers and a lot
+ of supporting functions. """
+ homepage = "http://www.gnu.org/software/libgcrypt/"
+ url = "ftp://ftp.gnupg.org/gcrypt/libgcrypt/libgcrypt-1.6.2.tar.bz2"
+
+ version('1.6.2', 'b54395a93cb1e57619943c082da09d5f')
+
+ depends_on("libgpg-error")
+
+ def install(self, spec, prefix):
+ configure("--prefix=%s" % prefix)
+ make()
+ make("install")
diff --git a/var/spack/packages/libgpg-error/package.py b/var/spack/packages/libgpg-error/package.py
new file mode 100644
index 0000000000..6c1d1a10a7
--- /dev/null
+++ b/var/spack/packages/libgpg-error/package.py
@@ -0,0 +1,17 @@
+from spack import *
+
+class LibgpgError(Package):
+ """Libgpg-error is a small library that defines common error
+ values for all GnuPG components. Among these are GPG, GPGSM,
+ GPGME, GPG-Agent, libgcrypt, Libksba, DirMngr, Pinentry,
+ SmartCard Daemon and possibly more in the future. """
+
+ homepage = "https://www.gnupg.org/related_software/libgpg-error"
+ url = "ftp://ftp.gnupg.org/gcrypt/libgpg-error/libgpg-error-1.18.tar.bz2"
+
+ version('1.18', '12312802d2065774b787cbfc22cc04e9')
+
+ def install(self, spec, prefix):
+ configure("--prefix=%s" % prefix)
+ make()
+ make("install")
diff --git a/var/spack/packages/libpng/package.py b/var/spack/packages/libpng/package.py
index affc14ea92..e02b08663e 100644
--- a/var/spack/packages/libpng/package.py
+++ b/var/spack/packages/libpng/package.py
@@ -3,12 +3,13 @@ from spack import *
class Libpng(Package):
"""libpng graphics file format"""
homepage = "http://www.libpng.org/pub/png/libpng.html"
- url = "http://prdownloads.sourceforge.net/libpng/libpng-1.6.16.tar.gz?download"
+ url = "http://download.sourceforge.net/libpng/libpng-1.6.16.tar.gz"
version('1.6.16', '1a4ad377919ab15b54f6cb6a3ae2622d')
+ version('1.6.15', '829a256f3de9307731d4f52dc071916d')
+ version('1.6.14', '2101b3de1d5f348925990f9aa8405660')
def install(self, spec, prefix):
configure("--prefix=%s" % prefix)
-
make()
make("install")
diff --git a/var/spack/packages/libxml2/package.py b/var/spack/packages/libxml2/package.py
index 5eaed36d94..72199d8def 100644
--- a/var/spack/packages/libxml2/package.py
+++ b/var/spack/packages/libxml2/package.py
@@ -9,6 +9,9 @@ class Libxml2(Package):
version('2.9.2', '9e6a9aca9d155737868b3dc5fd82f788')
+ depends_on('zlib')
+ depends_on('xz')
+
def install(self, spec, prefix):
configure("--prefix=%s" % prefix,
"--without-python")
diff --git a/var/spack/packages/libxslt/package.py b/var/spack/packages/libxslt/package.py
new file mode 100644
index 0000000000..f97332d020
--- /dev/null
+++ b/var/spack/packages/libxslt/package.py
@@ -0,0 +1,24 @@
+from spack import *
+
+class Libxslt(Package):
+ """Libxslt is the XSLT C library developed for the GNOME
+ project. XSLT itself is a an XML language to define
+ transformation for XML. Libxslt is based on libxml2 the XML C
+ library developed for the GNOME project. It also implements
+ most of the EXSLT set of processor-portable extensions
+ functions and some of Saxon's evaluate and expressions
+ extensions."""
+ homepage = "http://www.xmlsoft.org/XSLT/index.html"
+ url = "http://xmlsoft.org/sources/libxslt-1.1.28.tar.gz"
+
+ version('1.1.28', '9667bf6f9310b957254fdcf6596600b7')
+
+ depends_on("libxml2")
+ depends_on("xz")
+ depends_on("zlib")
+ depends_on("libgcrypt")
+
+ def install(self, spec, prefix):
+ configure("--prefix=%s" % prefix)
+ make()
+ make("install")
diff --git a/var/spack/packages/py-ipython/package.py b/var/spack/packages/py-ipython/package.py
index 731e661dfd..907ea9edcd 100644
--- a/var/spack/packages/py-ipython/package.py
+++ b/var/spack/packages/py-ipython/package.py
@@ -9,6 +9,7 @@ class PyIpython(Package):
extends('python')
depends_on('py-pygments')
+ depends_on('py-setuptools')
def install(self, spec, prefix):
python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/packages/py-libxml2/package.py b/var/spack/packages/py-libxml2/package.py
index e645acb5dd..59005428e4 100644
--- a/var/spack/packages/py-libxml2/package.py
+++ b/var/spack/packages/py-libxml2/package.py
@@ -9,6 +9,7 @@ class PyLibxml2(Package):
extends('python')
depends_on('libxml2')
+ depends_on('libxslt')
def install(self, spec, prefix):
python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/packages/py-matplotlib/package.py b/var/spack/packages/py-matplotlib/package.py
index 8b8684c563..04037f004e 100644
--- a/var/spack/packages/py-matplotlib/package.py
+++ b/var/spack/packages/py-matplotlib/package.py
@@ -8,7 +8,8 @@ class PyMatplotlib(Package):
version('1.4.2', '7d22efb6cce475025733c50487bd8898')
- extends('python')
+ extends('python', ignore=r'bin/nosetests.*$')
+
depends_on('py-pyside')
depends_on('py-ipython')
depends_on('py-pyparsing')
@@ -17,8 +18,12 @@ class PyMatplotlib(Package):
depends_on('py-pytz')
depends_on('py-nose')
depends_on('py-numpy')
- depends_on('qt')
+ depends_on('qt')
+ depends_on('bzip2')
+ depends_on('tcl')
+ depends_on('tk')
+ depends_on('qhull')
def install(self, spec, prefix):
python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/packages/py-mpi4py/package.py b/var/spack/packages/py-mpi4py/package.py
index fdea340dc2..8001689a18 100644
--- a/var/spack/packages/py-mpi4py/package.py
+++ b/var/spack/packages/py-mpi4py/package.py
@@ -7,6 +7,7 @@ class PyMpi4py(Package):
version('1.3.1', 'dbe9d22bdc8ed965c23a7ceb6f32fc3c')
extends('python')
+ depends_on('py-setuptools')
depends_on('mpi')
def install(self, spec, prefix):
diff --git a/var/spack/packages/py-nose/package.py b/var/spack/packages/py-nose/package.py
index 6df84e831d..155019289d 100644
--- a/var/spack/packages/py-nose/package.py
+++ b/var/spack/packages/py-nose/package.py
@@ -9,7 +9,8 @@ class PyNose(Package):
version('1.3.4', '6ed7169887580ddc9a8e16048d38274d')
- extends('python')
+ extends('python', ignore=r'bin/nosetests.*$')
+ depends_on('py-setuptools')
def install(self, spec, prefix):
python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/packages/py-pylint/package.py b/var/spack/packages/py-pylint/package.py
index ebde861f94..7a6ee7dbbc 100644
--- a/var/spack/packages/py-pylint/package.py
+++ b/var/spack/packages/py-pylint/package.py
@@ -8,9 +8,9 @@ class PyPylint(Package):
version('1.4.1', 'df7c679bdcce5019389038847e4de622')
-# extends('python')
- extends('python', ignore=lambda f:re.match(r"site.py*", f))
+ extends('python')
depends_on('py-nose')
+ depends_on('py-setuptools')
def install(self, spec, prefix):
python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/packages/py-pyqt4/package.py b/var/spack/packages/py-pyqt/package.py
index eeb1382560..8edca105bb 100644
--- a/var/spack/packages/py-pyqt4/package.py
+++ b/var/spack/packages/py-pyqt/package.py
@@ -1,18 +1,24 @@
from spack import *
-class PyPyqt4(Package):
- """PyQt is a set of Python v2 and v3 bindings for Digia's Qt application framework and runs on all platforms supported by Qt including Windows, MacOS/X and Linux."""
+class PyPyqt(Package):
+ """PyQt is a set of Python v2 and v3 bindings for Digia's Qt
+ application framework and runs on all platforms supported by Qt
+ including Windows, MacOS/X and Linux."""
homepage = "http://www.riverbankcomputing.com/software/pyqt/intro"
url = "http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.3/PyQt-x11-gpl-4.11.3.tar.gz"
version('4.11.3', '997c3e443165a89a559e0d96b061bf70')
extends('python')
- depends_on('qt')
depends_on('py-sip')
+ # TODO: allow qt5 when conditional deps are supported.
+ # TODO: Fix version matching so that @4 works like @:4
+ depends_on('qt@:4')
+
def install(self, spec, prefix):
- version_array = str(spec['python'].version).split('.')
- python('configure.py', '--confirm-license', '--destdir=%s/python%s.%s/site-packages' %(self.prefix.lib, version_array[0], version_array[1]))
+ python('configure.py',
+ '--confirm-license',
+ '--destdir=%s' % site_packages_dir)
make()
make('install')
diff --git a/var/spack/packages/py-pyside/package.py b/var/spack/packages/py-pyside/package.py
index 1fd037d75f..6583431124 100644
--- a/var/spack/packages/py-pyside/package.py
+++ b/var/spack/packages/py-pyside/package.py
@@ -1,5 +1,4 @@
from spack import *
-import spack.package
import os
class PyPyside(Package):
@@ -9,26 +8,33 @@ class PyPyside(Package):
version('1.2.2', 'c45bc400c8a86d6b35f34c29e379e44d')
+ # TODO: make build dependency
+ # depends_on("cmake")
+
extends('python')
depends_on('py-setuptools')
depends_on('qt@:4')
-
def patch(self):
"""Undo PySide RPATH handling and add Spack RPATH."""
+ # Figure out the special RPATH
+ pypkg = self.spec['python'].package
+ rpath = self.rpath
+ rpath.append(os.path.join(self.prefix, pypkg.site_packages_dir, 'PySide'))
+
# Add Spack's standard CMake args to the sub-builds.
# They're called BY setup.py so we have to patch it.
filter_file(
r'OPTION_CMAKE,',
r'OPTION_CMAKE, ' + (
'"-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE", '
- '"-DCMAKE_INSTALL_RPATH=%s",' % ':'.join(self.rpath)),
+ '"-DCMAKE_INSTALL_RPATH=%s",' % ':'.join(rpath)),
'setup.py')
# PySide tries to patch ELF files to remove RPATHs
# Disable this and go with the one we set.
filter_file(
- r'rpath_cmd\(pyside_path, srcpath\)',
+ r'^\s*rpath_cmd\(pyside_path, srcpath\)',
r'#rpath_cmd(pyside_path, srcpath)',
'pyside_postinstall.py')
diff --git a/var/spack/packages/py-rpy2/package.py b/var/spack/packages/py-rpy2/package.py
index 3817059911..dd0c0672af 100644
--- a/var/spack/packages/py-rpy2/package.py
+++ b/var/spack/packages/py-rpy2/package.py
@@ -10,5 +10,7 @@ class PyRpy2(Package):
extends('python')
depends_on('py-setuptools')
+ depends_on('R')
+
def install(self, spec, prefix):
python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/packages/py-scientificpython/package.py b/var/spack/packages/py-scientificpython/package.py
index 73600e6cb9..020d830703 100644
--- a/var/spack/packages/py-scientificpython/package.py
+++ b/var/spack/packages/py-scientificpython/package.py
@@ -1,7 +1,11 @@
from spack import *
class PyScientificpython(Package):
- """ScientificPython is a collection of Python modules for scientific computing. It contains support for geometry, mathematical functions, statistics, physical units, IO, visualization, and parallelization."""
+ """ScientificPython is a collection of Python modules for
+ scientific computing. It contains support for geometry,
+ mathematical functions, statistics, physical units, IO,
+ visualization, and parallelization."""
+
homepage = "https://sourcesup.renater.fr/projects/scientific-py/"
url = "https://sourcesup.renater.fr/frs/download.php/4411/ScientificPython-2.8.1.tar.gz"
diff --git a/var/spack/packages/py-shiboken/package.py b/var/spack/packages/py-shiboken/package.py
index 47abe64e65..e4bf4ce07e 100644
--- a/var/spack/packages/py-shiboken/package.py
+++ b/var/spack/packages/py-shiboken/package.py
@@ -1,4 +1,5 @@
from spack import *
+import os
class PyShiboken(Package):
"""Shiboken generates bindings for C++ libraries using CPython source code."""
@@ -15,6 +16,29 @@ class PyShiboken(Package):
depends_on("libxml2")
depends_on("qt@:4.8")
+ def patch(self):
+ """Undo Shiboken RPATH handling and add Spack RPATH."""
+ # Add Spack's standard CMake args to the sub-builds.
+ # They're called BY setup.py so we have to patch it.
+ pypkg = self.spec['python'].package
+ rpath = self.rpath
+ rpath.append(os.path.join(self.prefix, pypkg.site_packages_dir, 'Shiboken'))
+
+ filter_file(
+ r'OPTION_CMAKE,',
+ r'OPTION_CMAKE, ' + (
+ '"-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE", '
+ '"-DCMAKE_INSTALL_RPATH=%s",' % ':'.join(rpath)),
+ 'setup.py')
+
+ # Shiboken tries to patch ELF files to remove RPATHs
+ # Disable this and go with the one we set.
+ filter_file(
+ r'^\s*rpath_cmd\(shiboken_path, srcpath\)',
+ r'#rpath_cmd(shiboken_path, srcpath)',
+ 'shiboken_postinstall.py')
+
+
def install(self, spec, prefix):
python('setup.py', 'install',
'--prefix=%s' % prefix,
diff --git a/var/spack/packages/py-sip/package.py b/var/spack/packages/py-sip/package.py
index 06aea35a74..6753bdd2a5 100644
--- a/var/spack/packages/py-sip/package.py
+++ b/var/spack/packages/py-sip/package.py
@@ -1,4 +1,5 @@
from spack import *
+import os
class PySip(Package):
"""SIP is a tool that makes it very easy to create Python bindings for C and C++ libraries."""
@@ -10,6 +11,10 @@ class PySip(Package):
extends('python')
def install(self, spec, prefix):
- python('configure.py')
+ python('configure.py',
+ '--destdir=%s' % site_packages_dir,
+ '--bindir=%s' % spec.prefix.bin,
+ '--incdir=%s' % python_include_dir,
+ '--sipdir=%s' % os.path.join(spec.prefix.share, 'sip'))
make()
make('install')
diff --git a/var/spack/packages/py-virtualenv/package.py b/var/spack/packages/py-virtualenv/package.py
index c1b359e164..9d94c2dcda 100644
--- a/var/spack/packages/py-virtualenv/package.py
+++ b/var/spack/packages/py-virtualenv/package.py
@@ -9,6 +9,7 @@ class PyVirtualenv(Package):
version('1.11.6', 'f61cdd983d2c4e6aeabb70b1060d6f49')
extends('python')
+ depends_on('py-setuptools')
def clean(self):
if os.path.exists('build'):
diff --git a/var/spack/packages/python/package.py b/var/spack/packages/python/package.py
index 4b3b31eb6b..31a12ea653 100644
--- a/var/spack/packages/python/package.py
+++ b/var/spack/packages/python/package.py
@@ -1,6 +1,7 @@
import os
import re
from contextlib import closing
+from llnl.util.lang import match_predicate
from spack import *
import spack
@@ -43,6 +44,11 @@ class Python(Package):
@property
+ def python_include_dir(self):
+ return os.path.join('include', 'python%d.%d' % self.version[:2])
+
+
+ @property
def site_packages_dir(self):
return os.path.join(self.python_lib_dir, 'site-packages')
@@ -58,8 +64,9 @@ class Python(Package):
module.python = Executable(join_path(spec.prefix.bin, 'python'))
# Add variables for lib/pythonX.Y and lib/pythonX.Y/site-packages dirs.
- module.python_lib_dir = os.path.join(ext_spec.prefix, self.python_lib_dir)
- module.site_packages_dir = os.path.join(ext_spec.prefix, self.site_packages_dir)
+ module.python_lib_dir = os.path.join(ext_spec.prefix, self.python_lib_dir)
+ module.python_include_dir = os.path.join(ext_spec.prefix, self.python_include_dir)
+ module.site_packages_dir = os.path.join(ext_spec.prefix, self.site_packages_dir)
# Make the site packages directory if it does not exist already.
mkdirp(module.site_packages_dir)
@@ -79,28 +86,24 @@ class Python(Package):
def python_ignore(self, ext_pkg, args):
"""Add some ignore files to activate/deactivate args."""
- orig_ignore = args.get('ignore', lambda f: False)
-
- def ignore(filename):
- # Always ignore easy-install.pth, as it needs to be merged.
- patterns = [r'easy-install\.pth$']
+ ignore_arg = args.get('ignore', lambda f: False)
- # Ignore pieces of setuptools installed by other packages.
- if ext_pkg.name != 'py-setuptools':
- patterns.append(r'/site\.pyc?$')
- patterns.append(r'setuptools\.pth')
- patterns.append(r'bin/easy_install[^/]*$')
- patterns.append(r'setuptools.*egg$')
+ # Always ignore easy-install.pth, as it needs to be merged.
+ patterns = [r'easy-install\.pth$']
- return (any(re.search(p, filename) for p in patterns) or
- orig_ignore(filename))
+ # Ignore pieces of setuptools installed by other packages.
+ if ext_pkg.name != 'py-setuptools':
+ patterns.append(r'/site\.pyc?$')
+ patterns.append(r'setuptools\.pth')
+ patterns.append(r'bin/easy_install[^/]*$')
+ patterns.append(r'setuptools.*egg$')
- return ignore
+ return match_predicate(ignore_arg, patterns)
- def write_easy_install_pth(self, extensions):
+ def write_easy_install_pth(self, exts):
paths = []
- for ext in extensions:
+ for ext in sorted(exts.values()):
ext_site_packages = os.path.join(ext.prefix, self.site_packages_dir)
easy_pth = "%s/easy-install.pth" % ext_site_packages
@@ -139,15 +142,16 @@ class Python(Package):
args.update(ignore=self.python_ignore(ext_pkg, args))
super(Python, self).activate(ext_pkg, **args)
- extensions = set(spack.install_layout.get_extensions(self.spec))
- extensions.add(ext_pkg.spec)
- self.write_easy_install_pth(extensions)
+ exts = spack.install_layout.extension_map(self.spec)
+ exts[ext_pkg.name] = ext_pkg.spec
+ self.write_easy_install_pth(exts)
def deactivate(self, ext_pkg, **args):
args.update(ignore=self.python_ignore(ext_pkg, args))
super(Python, self).deactivate(ext_pkg, **args)
- extensions = set(spack.install_layout.get_extensions(self.spec))
- extensions.remove(ext_pkg.spec)
- self.write_easy_install_pth(extensions)
+ exts = spack.install_layout.extension_map(self.spec)
+ if ext_pkg.name in exts: # Make deactivate idempotent.
+ del exts[ext_pkg.name]
+ self.write_easy_install_pth(exts)
diff --git a/var/spack/packages/qhull/package.py b/var/spack/packages/qhull/package.py
new file mode 100644
index 0000000000..9da4078a70
--- /dev/null
+++ b/var/spack/packages/qhull/package.py
@@ -0,0 +1,27 @@
+from spack import *
+
+class Qhull(Package):
+ """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
+ source code runs in 2-d, 3-d, 4-d, and higher dimensions. Qhull
+ implements the Quickhull algorithm for computing the convex
+ hull. It handles roundoff errors from floating point
+ arithmetic. It computes volumes, surface areas, and
+ approximations to the convex hull.
+
+ Qhull does not support triangulation of non-convex surfaces,
+ mesh generation of non-convex objects, medium-sized inputs in
+ 9-D and higher, alpha shapes, weighted Voronoi diagrams,
+ Voronoi volumes, or constrained Delaunay triangulations."""
+
+ homepage = "http://www.qhull.org"
+
+ version('1.0', 'd0f978c0d8dfb2e919caefa56ea2953c',
+ url="http://www.qhull.org/download/qhull-2012.1-src.tgz")
+
+ def install(self, spec, prefix):
+ with working_dir('spack-build', create=True):
+ cmake('..', *std_cmake_args)
+ make()
+ make("install")
diff --git a/var/spack/packages/qt/package.py b/var/spack/packages/qt/package.py
index 446f7b67a4..30f46c08dc 100644
--- a/var/spack/packages/qt/package.py
+++ b/var/spack/packages/qt/package.py
@@ -78,9 +78,9 @@ class Qt(Package):
'-qt-xcb',
'-optimized-qmake',
'-no-pch',
-# phonon required for py-pyqt4
-# '-no-phonon',
-# '-no-phonon-backend',
+ # phonon required for py-pyqt
+ # '-no-phonon',
+ # '-no-phonon-backend',
'-no-openvg')
make()
make("install")
diff --git a/var/spack/packages/tcl/package.py b/var/spack/packages/tcl/package.py
new file mode 100644
index 0000000000..529adf7788
--- /dev/null
+++ b/var/spack/packages/tcl/package.py
@@ -0,0 +1,22 @@
+from spack import *
+
+class Tcl(Package):
+ """Tcl (Tool Command Language) is a very powerful but easy to
+ learn dynamic programming language, suitable for a very wide
+ range of uses, including web and desktop applications,
+ networking, administration, testing and many more. Open source
+ and business-friendly, Tcl is a mature yet evolving language
+ that is truly cross platform, easily deployed and highly
+ extensible."""
+ homepage = "http://www.tcl.tk"
+
+ version('8.6.3', 'db382feca91754b7f93da16dc4cdad1f',
+ url="http://prdownloads.sourceforge.net/tcl/tcl8.6.3-src.tar.gz")
+
+ depends_on('zlib')
+
+ def install(self, spec, prefix):
+ with working_dir('unix'):
+ configure("--prefix=%s" % prefix)
+ make()
+ make("install")
diff --git a/var/spack/packages/tk/package.py b/var/spack/packages/tk/package.py
new file mode 100644
index 0000000000..96736f6f95
--- /dev/null
+++ b/var/spack/packages/tk/package.py
@@ -0,0 +1,22 @@
+from spack import *
+
+class Tk(Package):
+ """Tk is a graphical user interface toolkit that takes developing
+ desktop applications to a higher level than conventional
+ approaches. Tk is the standard GUI not only for Tcl, but for
+ many other dynamic languages, and can produce rich, native
+ applications that run unchanged across Windows, Mac OS X, Linux
+ and more."""
+ homepage = "http://www.tcl.tk"
+ url = "http://prdownloads.sourceforge.net/tcl/tk8.6.3-src.tar.gz"
+
+ version('src', '85ca4dbf4dcc19777fd456f6ee5d0221')
+
+ depends_on("tcl")
+
+ def install(self, spec, prefix):
+ with working_dir('unix'):
+ configure("--prefix=%s" % prefix,
+ "--with-tcl=%s" % spec['tcl'].prefix.lib)
+ make()
+ make("install")