diff options
76 files changed, 2074 insertions, 137 deletions
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py index 9f08832598..576aeb16bd 100644 --- a/lib/spack/llnl/util/filesystem.py +++ b/lib/spack/llnl/util/filesystem.py @@ -23,8 +23,8 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## __all__ = ['set_install_permissions', 'install', 'expand_user', 'working_dir', - 'touch', 'mkdirp', 'force_remove', 'join_path', 'ancestor', - 'can_access', 'filter_file', 'change_sed_delimiter'] + 'touch', 'touchp', 'mkdirp', 'force_remove', 'join_path', 'ancestor', + 'can_access', 'filter_file', 'change_sed_delimiter', 'is_exe'] import os import sys @@ -154,6 +154,11 @@ def install(src, dest): os.chmod(dest, dest_mode) +def is_exe(path): + """True if path is an executable file.""" + return os.path.isfile(path) and os.access(path, os.X_OK) + + def expand_user(path): """Find instances of '%u' in a path and replace with the current user's username.""" @@ -199,6 +204,12 @@ def touch(path): os.utime(path, None) +def touchp(path): + """Like touch, but creates any parent directories needed for the file.""" + mkdirp(os.path.dirname(path)) + touch(path) + + def join_path(prefix, *args): path = str(prefix) for elt in args: diff --git a/lib/spack/llnl/util/link_tree.py b/lib/spack/llnl/util/link_tree.py new file mode 100644 index 0000000000..4e4e48316e --- /dev/null +++ b/lib/spack/llnl/util/link_tree.py @@ -0,0 +1,193 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +"""LinkTree class for setting up trees of symbolic links.""" +__all__ = ['LinkTree'] + +import os +import shutil +from llnl.util.filesystem import * + +empty_file_name = '.spack-empty' + + +def traverse_tree(source_root, dest_root, rel_path='', **kwargs): + """Traverse two filesystem trees simultaneously. + + Walks the LinkTree directory in pre or post order. Yields each + file in the source directory with a matching path from the dest + directory, along with whether the file is a directory. + e.g., for this tree:: + + root/ + a/ + file1 + file2 + b/ + file3 + + When called on dest, this yields:: + + ('root', 'dest') + ('root/a', 'dest/a') + ('root/a/file1', 'dest/a/file1') + ('root/a/file2', 'dest/a/file2') + ('root/b', 'dest/b') + ('root/b/file3', 'dest/b/file3') + + Optional args: + + order=[pre|post] -- Whether to do pre- or post-order traveral. + + ignore=<predicate> -- Predicate indicating which files to ignore. + + follow_nonexisting -- Whether to descend into directories in + src that do not exit in dest. Default True. + + follow_links -- Whether to descend into symlinks in src. + + """ + follow_nonexisting = kwargs.get('follow_nonexisting', True) + follow_links = kwargs.get('follow_link', False) + + # Yield in pre or post order? + order = kwargs.get('order', 'pre') + if order not in ('pre', 'post'): + raise ValueError("Order must be 'pre' or 'post'.") + + # List of relative paths to ignore under the src root. + ignore = kwargs.get('ignore', lambda filename: False) + + # Don't descend into ignored directories + if ignore(rel_path): + return + + source_path = os.path.join(source_root, rel_path) + dest_path = os.path.join(dest_root, rel_path) + + # preorder yields directories before children + if order == 'pre': + yield (source_path, dest_path) + + for f in os.listdir(source_path): + source_child = os.path.join(source_path, f) + dest_child = os.path.join(dest_path, f) + rel_child = os.path.join(rel_path, f) + + # Treat as a directory + if os.path.isdir(source_child) and ( + follow_links or not os.path.islink(source_child)): + + # When follow_nonexisting isn't set, don't descend into dirs + # in source that do not exist in dest + if follow_nonexisting or os.path.exists(dest_child): + tuples = traverse_tree(source_root, dest_root, rel_child, **kwargs) + for t in tuples: yield t + + # Treat as a file. + elif not ignore(os.path.join(rel_path, f)): + yield (source_child, dest_child) + + if order == 'post': + yield (source_path, dest_path) + + + +class LinkTree(object): + """Class to create trees of symbolic links from a source directory. + + LinkTree objects are constructed with a source root. Their + methods allow you to create and delete trees of symbolic links + back to the source tree in specific destination directories. + Trees comprise symlinks only to files; directries are never + symlinked to, to prevent the source directory from ever being + modified. + + """ + def __init__(self, source_root): + if not os.path.exists(source_root): + raise IOError("No such file or directory: '%s'", source_root) + + self._root = source_root + + + def find_conflict(self, dest_root, **kwargs): + """Returns the first file in dest that conflicts with src""" + kwargs['follow_nonexisting'] = False + for src, dest in traverse_tree(self._root, dest_root, **kwargs): + if os.path.isdir(src): + if os.path.exists(dest) and not os.path.isdir(dest): + return dest + elif os.path.exists(dest): + return dest + return None + + + def merge(self, dest_root, **kwargs): + """Link all files in src into dest, creating directories if necessary.""" + kwargs['order'] = 'pre' + for src, dest in traverse_tree(self._root, dest_root, **kwargs): + if os.path.isdir(src): + if not os.path.exists(dest): + mkdirp(dest) + continue + + if not os.path.isdir(dest): + raise ValueError("File blocks directory: %s" % dest) + + # mark empty directories so they aren't removed on unmerge. + if not os.listdir(dest): + marker = os.path.join(dest, empty_file_name) + touch(marker) + + else: + assert(not os.path.exists(dest)) + os.symlink(src, dest) + + + def unmerge(self, dest_root, **kwargs): + """Unlink all files in dest that exist in src. + + Unlinks directories in dest if they are empty. + + """ + kwargs['order'] = 'post' + for src, dest in traverse_tree(self._root, dest_root, **kwargs): + if os.path.isdir(src): + if not os.path.isdir(dest): + raise ValueError("File blocks directory: %s" % dest) + + # remove directory if it is empty. + if not os.listdir(dest): + shutil.rmtree(dest, ignore_errors=True) + + # remove empty dir marker if present. + marker = os.path.join(dest, empty_file_name) + if os.path.exists(marker): + os.remove(marker) + + elif os.path.exists(dest): + if not os.path.islink(dest): + raise ValueError("%s is not a link tree!" % dest) + os.remove(dest) diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py index 6697e00e40..6763411f7d 100644 --- a/lib/spack/spack/__init__.py +++ b/lib/spack/spack/__init__.py @@ -138,7 +138,7 @@ sys_type = None # should live. This file is overloaded for spack core vs. for packages. # __all__ = ['Package', 'Version', 'when', 'ver'] -from spack.package import Package +from spack.package import Package, ExtensionConflictError from spack.version import Version, ver from spack.multimethod import when diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py index 537db536dd..b96ac5af51 100644 --- a/lib/spack/spack/cmd/__init__.py +++ b/lib/spack/spack/cmd/__init__.py @@ -121,3 +121,18 @@ def elide_list(line_list, max_num=10): return line_list[:max_num-1] + ['...'] + line_list[-1:] else: return line_list + + +def disambiguate_spec(spec): + matching_specs = spack.db.get_installed(spec) + if not matching_specs: + tty.die("Spec '%s' matches no installed packages." % spec) + + elif len(matching_specs) > 1: + args = ["%s matches multiple packages." % spec, + "Matching packages:"] + args += [" " + str(s) for s in matching_specs] + args += ["Use a more specific spec."] + tty.die(*args) + + return matching_specs[0] diff --git a/lib/spack/spack/cmd/activate.py b/lib/spack/spack/cmd/activate.py new file mode 100644 index 0000000000..c1e23852d6 --- /dev/null +++ b/lib/spack/spack/cmd/activate.py @@ -0,0 +1,50 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +from external import argparse +import llnl.util.tty as tty +import spack +import spack.cmd + +description = "Activate a package extension." + +def setup_parser(subparser): + subparser.add_argument( + 'spec', nargs=argparse.REMAINDER, help="spec of package extension to activate.") + + +def activate(parser, args): + specs = spack.cmd.parse_specs(args.spec, concretize=True) + if len(specs) != 1: + tty.die("activate requires one spec. %d given." % len(specs)) + + # TODO: remove this hack when DAG info is stored in dir layout. + # This ensures the ext spec is always normalized properly. + spack.db.get(specs[0]) + + spec = spack.cmd.disambiguate_spec(specs[0]) + if spec.package.activated: + tty.die("Package %s is already activated." % specs[0].short_spec) + + spec.package.do_activate() diff --git a/lib/spack/spack/cmd/deactivate.py b/lib/spack/spack/cmd/deactivate.py new file mode 100644 index 0000000000..fd13f051df --- /dev/null +++ b/lib/spack/spack/cmd/deactivate.py @@ -0,0 +1,50 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +from external import argparse +import llnl.util.tty as tty +import spack +import spack.cmd + +description = "Deactivate a package extension." + +def setup_parser(subparser): + subparser.add_argument( + 'spec', nargs=argparse.REMAINDER, help="spec of package extension to deactivate.") + + +def deactivate(parser, args): + specs = spack.cmd.parse_specs(args.spec, concretize=True) + 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. + # 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) + + spec.package.do_deactivate() diff --git a/lib/spack/spack/cmd/extensions.py b/lib/spack/spack/cmd/extensions.py new file mode 100644 index 0000000000..f6ccd7b515 --- /dev/null +++ b/lib/spack/spack/cmd/extensions.py @@ -0,0 +1,97 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# 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 +from external import argparse + +import llnl.util.tty as tty +from llnl.util.tty.colify import colify + +import spack +import spack.cmd +import spack.cmd.find + +description = "List extensions for package." + +def setup_parser(subparser): + format_group = subparser.add_mutually_exclusive_group() + format_group.add_argument( + '-l', '--long', action='store_const', dest='mode', const='long', + help='Show dependency hashes as well as versions.') + format_group.add_argument( + '-p', '--paths', action='store_const', dest='mode', const='paths', + help='Show paths to extension install directories') + format_group.add_argument( + '-d', '--deps', action='store_const', dest='mode', const='deps', + help='Show full dependency DAG of extensions') + + subparser.add_argument( + 'spec', nargs=argparse.REMAINDER, help='Spec of package to list extensions for') + + +def extensions(parser, args): + if not args.spec: + tty.die("extensions requires a package spec.") + + # Checks + spec = spack.cmd.parse_specs(args.spec) + if len(spec) > 1: + tty.die("Can only list extensions for one package.") + + if not spec[0].package.extendable: + tty.die("%s is not an extendable package." % spec[0].name) + + spec = spack.cmd.disambiguate_spec(spec[0]) + + if not spec.package.extendable: + tty.die("%s does not have extensions." % spec.short_spec) + + if not args.mode: + args.mode = 'short' + + # List package names of extensions + extensions = spack.db.extensions_for(spec) + if not extensions: + tty.msg("%s has no extensions." % spec.cshort_spec) + return + tty.msg("%s extensions:" % spec.cshort_spec) + colify(ext.name for ext in extensions) + + # List specs of installed extensions. + installed = [s.spec for s in spack.db.installed_extensions_for(spec)] + print + if not installed: + tty.msg("None activated.") + return + tty.msg("%d installed:" % len(installed)) + spack.cmd.find.display_specs(installed, mode=args.mode) + + # List specs of activated extensions. + activated = spack.install_layout.get_extensions(spec) + print + if not activated: + tty.msg("None activated.") + return + tty.msg("%d currently activated:" % len(exts)) + spack.cmd.find.display_specs(installed, mode=args.mode) diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py index 1de3413d42..f6f503afe5 100644 --- a/lib/spack/spack/cmd/find.py +++ b/lib/spack/spack/cmd/find.py @@ -41,13 +41,13 @@ description ="Find installed spack packages" def setup_parser(subparser): format_group = subparser.add_mutually_exclusive_group() format_group.add_argument( - '-l', '--long', action='store_true', dest='long', + '-l', '--long', action='store_const', dest='mode', const='long', help='Show dependency hashes as well as versions.') format_group.add_argument( - '-p', '--paths', action='store_true', dest='paths', + '-p', '--paths', action='store_const', dest='mode', const='paths', help='Show paths to package install directories') format_group.add_argument( - '-d', '--deps', action='store_true', dest='full_deps', + '-d', '--deps', action='store_const', dest='mode', const='deps', help='Show full dependency DAG of installed packages') subparser.add_argument( @@ -55,26 +55,8 @@ def setup_parser(subparser): help='optional specs to filter results') -def find(parser, args): - # Filter out specs that don't exist. - query_specs = spack.cmd.parse_specs(args.query_specs) - query_specs, nonexisting = partition_list( - query_specs, lambda s: spack.db.exists(s.name)) - - if nonexisting: - msg = "No such package%s: " % ('s' if len(nonexisting) > 1 else '') - msg += ", ".join(s.name for s in nonexisting) - tty.msg(msg) - - if not query_specs: - return - - # Get all the specs the user asked for - if not query_specs: - specs = set(spack.db.installed_package_specs()) - else: - results = [set(spack.db.get_installed(qs)) for qs in query_specs] - specs = set.union(*results) +def display_specs(specs, **kwargs): + mode = kwargs.get('mode', 'short') # Make a dict with specs keyed by architecture and compiler. index = index_by(specs, ('architecture', 'compiler')) @@ -92,7 +74,7 @@ def find(parser, args): specs.sort() abbreviated = [s.format('$_$@$+', color=True) for s in specs] - if args.paths: + if mode == 'paths': # Print one spec per line along with prefix path width = max(len(s) for s in abbreviated) width += 2 @@ -101,11 +83,44 @@ def find(parser, args): for abbrv, spec in zip(abbreviated, specs): print format % (abbrv, spec.prefix) - elif args.full_deps: + elif mode == 'deps': for spec in specs: print spec.tree(indent=4, format='$_$@$+', color=True), - else: + + elif mode in ('short', 'long'): fmt = '$-_$@$+' - if args.long: + if mode == 'long': fmt += '$#' colify(s.format(fmt, color=True) for s in specs) + + else: + raise ValueError( + "Invalid mode for display_specs: %s. Must be one of (paths, deps, short)." % mode) + + + +def find(parser, args): + # Filter out specs that don't exist. + query_specs = spack.cmd.parse_specs(args.query_specs) + query_specs, nonexisting = partition_list( + query_specs, lambda s: spack.db.exists(s.name)) + + if nonexisting: + msg = "No such package%s: " % ('s' if len(nonexisting) > 1 else '') + msg += ", ".join(s.name for s in nonexisting) + tty.msg(msg) + + if not query_specs: + return + + # Get all the specs the user asked for + if not query_specs: + specs = set(spack.db.installed_package_specs()) + else: + results = [set(spack.db.get_installed(qs)) for qs in query_specs] + specs = set.union(*results) + + if not args.mode: + args.mode = 'short' + display_specs(specs, mode=args.mode) + diff --git a/lib/spack/spack/cmd/location.py b/lib/spack/spack/cmd/location.py index e422eaf966..709e894b7f 100644 --- a/lib/spack/spack/cmd/location.py +++ b/lib/spack/spack/cmd/location.py @@ -78,38 +78,30 @@ def location(parser, args): tty.die("You must supply a spec.") if len(specs) != 1: tty.die("Too many specs. Supply only one.") - spec = specs[0] if args.install_dir: # install_dir command matches against installed specs. - matching_specs = spack.db.get_installed(spec) - if not matching_specs: - tty.die("Spec '%s' matches no installed packages." % spec) + spec = spack.cmd.disambiguate_spec(specs[0]) + print spec.prefix - elif len(matching_specs) > 1: - tty.error("%s matches multiple packages:" % spec) - for s in matching_specs: - sys.stderr.write(s.tree(color=True)) - sys.stderr.write("\n") - sys.stderr.write("Use a more specific spec.\n") - sys.exit(1) + else: + spec = specs[0] - print matching_specs[0].prefix + if args.package_dir: + # This one just needs the spec name. + print join_path(spack.db.root, spec.name) - elif args.package_dir: - # This one just needs the spec name. - print join_path(spack.db.root, spec.name) + else: + # These versions need concretized specs. + spec.concretize() + pkg = spack.db.get(spec) + + if args.stage_dir: + print pkg.stage.path + + else: # args.build_dir is the default. + if not pkg.stage.source_path: + tty.die("Build directory does not exist yet. Run this to create it:", + "spack stage " + " ".join(args.spec)) + print pkg.stage.source_path - else: - # These versions need concretized specs. - spec.concretize() - pkg = spack.db.get(spec) - - if args.stage_dir: - print pkg.stage.path - - else: # args.build_dir is the default. - if not pkg.stage.source_path: - tty.die("Build directory does not exist yet. Run this to create it:", - "spack stage " + " ".join(args.spec)) - print pkg.stage.source_path diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py index e787c460ad..0962942f43 100644 --- a/lib/spack/spack/cmd/uninstall.py +++ b/lib/spack/spack/cmd/uninstall.py @@ -65,7 +65,6 @@ def uninstall(parser, args): " b) use a more specific spec."] tty.die(*args) - if len(matching_specs) == 0: tty.die("%s does not match any installed packages." % spec) diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py index 42cac0c9d2..efc40a17a4 100644 --- a/lib/spack/spack/directory_layout.py +++ b/lib/spack/spack/directory_layout.py @@ -53,6 +53,19 @@ class DirectoryLayout(object): self.root = root + @property + def hidden_file_paths(self): + """Return a list of hidden files used by the directory layout. + + Paths are relative to the root of an install directory. + + If the directory layout uses no hidden files to maintain + state, this should return an empty container, e.g. [] or (,). + + """ + raise NotImplementedError() + + def all_specs(self): """To be implemented by subclasses to traverse all specs for which there is a directory within the root. @@ -71,6 +84,21 @@ class DirectoryLayout(object): raise NotImplementedError() + def get_extensions(self, spec): + """Get a set of currently installed extension packages for a spec.""" + raise NotImplementedError() + + + def add_extension(self, spec, extension_spec): + """Add to the list of currently installed extensions.""" + raise NotImplementedError() + + + def remove_extension(self, spec, extension_spec): + """Remove from the list of currently installed extensions.""" + raise NotImplementedError() + + def path_for_spec(self, spec): """Return an absolute path from the root to a directory for the spec.""" _check_concrete(spec) @@ -134,9 +162,16 @@ class SpecHashDirectoryLayout(DirectoryLayout): """Prefix size is number of characters in the SHA-1 prefix to use to make each hash unique. """ - spec_file_name = kwargs.get('spec_file_name', '.spec') + spec_file_name = kwargs.get('spec_file_name', '.spec') + extension_file_name = kwargs.get('extension_file_name', '.extensions') super(SpecHashDirectoryLayout, self).__init__(root) self.spec_file_name = spec_file_name + self.extension_file_name = extension_file_name + + + @property + def hidden_file_paths(self): + return ('.spec', '.extensions') def relative_path_for_spec(self, spec): @@ -225,6 +260,62 @@ class SpecHashDirectoryLayout(DirectoryLayout): yield spec + def extension_file_path(self, spec): + """Gets full path to an installed package's extension file""" + _check_concrete(spec) + return join_path(self.path_for_spec(spec), self.extension_file_name) + + + def get_extensions(self, spec): + _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 + + + 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) + + + def add_extension(self, spec, extension_spec): + _check_concrete(spec) + _check_concrete(extension_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) + + exts.add(extension_spec) + self.write_extensions(spec, exts) + + + def remove_extension(self, spec, extension_spec): + _check_concrete(spec) + _check_concrete(extension_spec) + + exts = self.get_extensions(spec) + if not extension_spec in exts: + raise NoSuchExtensionError(spec, extension_spec) + + exts.remove(extension_spec) + self.write_extensions(spec, exts) + + class DirectoryLayoutError(SpackError): """Superclass for directory layout errors.""" def __init__(self, message): @@ -250,3 +341,32 @@ class InstallDirectoryAlreadyExistsError(DirectoryLayoutError): def __init__(self, path): super(InstallDirectoryAlreadyExistsError, self).__init__( "Install path %s already exists!") + + +class InvalidExtensionSpecError(DirectoryLayoutError): + """Raised when an extension file has a bad spec in it.""" + def __init__(self, message): + super(InvalidExtensionSpecError, self).__init__(message) + + +class ExtensionAlreadyInstalledError(DirectoryLayoutError): + """Raised when an extension is added to a package that already has it.""" + def __init__(self, spec, extension_spec): + super(ExtensionAlreadyInstalledError, self).__init__( + "%s is already installed in %s" % (extension_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): + 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)) + + +class NoSuchExtensionError(DirectoryLayoutError): + """Raised when an extension isn't there on remove.""" + def __init__(self, spec, extension_spec): + super(NoSuchExtensionError, self).__init__( + "%s cannot be removed from %s because it's not installed."% ( + extension_spec.short_spec, spec.short_spec)) diff --git a/lib/spack/spack/hooks/__init__.py b/lib/spack/spack/hooks/__init__.py index 98b7f2323f..1c44e8abaa 100644 --- a/lib/spack/spack/hooks/__init__.py +++ b/lib/spack/spack/hooks/__init__.py @@ -31,7 +31,9 @@ Currently the following hooks are supported: + * pre_install() * post_install() + * pre_uninstall() * post_uninstall() This can be used to implement support for things like module @@ -70,5 +72,8 @@ class HookRunner(object): # # Define some functions that can be called to fire off hooks. # -post_install = HookRunner('post_install') +pre_install = HookRunner('pre_install') +post_install = HookRunner('post_install') + +pre_uninstall = HookRunner('pre_uninstall') post_uninstall = HookRunner('post_uninstall') diff --git a/lib/spack/spack/hooks/extensions.py b/lib/spack/spack/hooks/extensions.py new file mode 100644 index 0000000000..718b24b965 --- /dev/null +++ b/lib/spack/spack/hooks/extensions.py @@ -0,0 +1,40 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## + +import spack + + +def post_install(pkg): + if pkg.is_extension: + pkg.do_activate() + + +def pre_uninstall(pkg): + # Need to do this b/c uninstall does not automatically do it. + # TODO: store full graph info in stored .spec file. + pkg.spec.normalize() + + if pkg.is_extension: + pkg.do_deactivate() diff --git a/lib/spack/spack/mirror.py b/lib/spack/spack/mirror.py index 929c514b61..114c7b6a35 100644 --- a/lib/spack/spack/mirror.py +++ b/lib/spack/spack/mirror.py @@ -46,7 +46,7 @@ from spack.util.compression import extension def mirror_archive_filename(spec): - """Get the path that this spec will live at within a mirror.""" + """Get the name of the spec's archive in the mirror.""" if not spec.version.concrete: raise ValueError("mirror.path requires spec with concrete version.") @@ -61,6 +61,11 @@ def mirror_archive_filename(spec): return "%s-%s.%s" % (spec.package.name, spec.version, ext) +def mirror_archive_path(spec): + """Get the relative path to the spec's archive within a mirror.""" + return join_path(spec.name, mirror_archive_filename(spec)) + + def get_matching_versions(specs, **kwargs): """Get a spec for EACH known version matching any spec in the list.""" matching = [] @@ -141,12 +146,10 @@ def create(path, specs, **kwargs): stage = None try: # create a subdirectory for the current package@version - subdir = join_path(mirror_root, pkg.name) + archive_path = join_path(path, mirror_archive_path(spec)) + subdir = os.path.dirname(archive_path) mkdirp(subdir) - archive_file = mirror_archive_filename(spec) - archive_path = join_path(subdir, archive_file) - if os.path.exists(archive_path): tty.msg("Already added %s" % spec.format("$_$@")) present.append(spec) diff --git a/lib/spack/spack/modules.py b/lib/spack/spack/modules.py index 755e9ea900..7d2ca97a62 100644 --- a/lib/spack/spack/modules.py +++ b/lib/spack/spack/modules.py @@ -49,6 +49,7 @@ import os import re import textwrap import shutil +from glob import glob from contextlib import closing import llnl.util.tty as tty @@ -123,6 +124,13 @@ class EnvModule(object): if os.path.isdir(directory): add_path(var, directory) + # Add python path unless it's an actual python installation + # TODO: is there a better way to do this? + if self.spec.name != 'python': + site_packages = glob(join_path(self.spec.prefix.lib, "python*/site-packages")) + if site_packages: + add_path('PYTHONPATH', site_packages[0]) + # short description is just the package + version # TODO: maybe packages can optionally provide it. self.short_description = self.spec.format("$_ $@") diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index 1a797e88b1..b905968540 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -45,6 +45,7 @@ import textwrap from StringIO import StringIO import llnl.util.tty as tty +from llnl.util.link_tree import LinkTree from llnl.util.filesystem import * from llnl.util.lang import * @@ -320,12 +321,21 @@ class Package(object): """Patches to apply to newly expanded source, if any.""" patches = {} + """Specs of package this one extends, or None. + + Currently, ppackages can extend at most one other package. + """ + extendees = {} + # # These are default values for instance variables. # """By default we build in parallel. Subclasses can override this.""" parallel = True + """Most packages are NOT extendable. Set to True if you want extensions.""" + extendable = False + def __init__(self, spec): # this determines how the package should be built. @@ -395,6 +405,9 @@ class Package(object): self._fetch_time = 0.0 self._total_time = 0.0 + if self.is_extension: + spack.db.get(self.extendee_spec)._check_extendable() + @property def version(self): @@ -459,7 +472,7 @@ class Package(object): raise ValueError("Can only get a stage for a concrete package.") if self._stage is None: - mp = spack.mirror.mirror_archive_filename(self.spec) + mp = spack.mirror.mirror_archive_path(self.spec) self._stage = Stage( self.fetcher, mirror_path=mp, name=self.spec.short_spec) return self._stage @@ -481,6 +494,49 @@ class Package(object): self._fetcher = f + @property + def extendee_spec(self): + """Spec of the extendee of this package, or None if it is not an extension.""" + if not self.extendees: + return None + name = next(iter(self.extendees)) + if not name in self.spec: + spec, kwargs = self.extendees[name] + return spec + + # Need to do this to get the concrete version of the spec + return self.spec[name] + + + @property + def extendee_args(self): + """Spec of the extendee of this package, or None if it is not an extension.""" + if not self.extendees: + return None + name = next(iter(self.extendees)) + return self.extendees[name][1] + + + @property + def is_extension(self): + return len(self.extendees) > 0 + + + def extends(self, spec): + return (spec.name in self.extendees and + spec.satisfies(self.extendees[spec.name][0])) + + + @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) + + def preorder_traversal(self, visited=None, **kwargs): """This does a preorder traversal of the package's dependence DAG.""" virtual = kwargs.get("virtual", False) @@ -713,6 +769,14 @@ class Package(object): tty.msg("Patched %s" % self.name) + def do_fake_install(self): + """Make a fake install directory contaiing a 'fake' file in bin.""" + mkdirp(self.prefix.bin) + touch(join_path(self.prefix.bin, 'fake')) + mkdirp(self.prefix.lib) + mkdirp(self.prefix.man1) + + def do_install(self, **kwargs): """This class should call this version of the install method. Package implementations should override install(). @@ -733,7 +797,7 @@ class Package(object): tty.msg("Installing %s" % self.name) if not ignore_deps: - self.do_install_dependencies() + self.do_install_dependencies(**kwargs) start_time = time.time() if not fake_install: @@ -757,23 +821,27 @@ class Package(object): # 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) + # Allow extendees to further set up the environment. + if self.is_extension: + self.extendee_spec.package.setup_extension_environment( + self.module, self.extendee_spec, self.spec) + if fake_install: - mkdirp(self.prefix.bin) - touch(join_path(self.prefix.bin, 'fake')) - mkdirp(self.prefix.lib) - mkdirp(self.prefix.man1) + self.do_fake_install() else: # Subclasses implement install() to do the real work. self.install(self.spec, self.prefix) # Ensure that something was actually installed. - if not os.listdir(self.prefix): - raise InstallError( - "Install failed for %s. Nothing was installed!" - % self.name) + self._sanity_check_install() # On successful install, remove the stage. if not keep_stage: @@ -814,15 +882,23 @@ class Package(object): if returncode != 0: sys.exit(1) - # Once everything else is done, run post install hooks spack.hooks.post_install(self) - def do_install_dependencies(self): + + def _sanity_check_install(self): + installed = set(os.listdir(self.prefix)) + installed.difference_update(spack.install_layout.hidden_file_paths) + if not installed: + raise InstallError( + "Install failed for %s. Nothing was installed!" % self.name) + + + def do_install_dependencies(self, **kwargs): # Pass along paths of dependencies here for dep in self.spec.dependencies.values(): - dep.package.do_install() + dep.package.do_install(**kwargs) @property @@ -834,6 +910,30 @@ class Package(object): fromlist=[self.__class__.__name__]) + def setup_extension_environment(self, module, spec, ext_spec): + """Called before the install() method of extensions. + + Default implementation does nothing, but this can be + overridden by an extendable package to set up the install + environment for its extensions. This is useful if there are + some common steps to installing all extensions for a + certain package. + + Some examples: + + 1. Installing python modules generally requires PYTHONPATH to + point to the lib/pythonX.Y/site-packages directory in the + module's install prefix. This could set that variable. + + 2. Extensions often need to invoke the 'python' interpreter + from the Python installation being extended. This routine can + put a 'python' Execuable object in the module scope for the + extension package to simplify extension installs. + + """ + pass + + def install(self, spec, prefix): """Package implementations override this with their own build configuration.""" raise InstallError("Package %s provides no install method!" % self.name) @@ -853,6 +953,10 @@ class Package(object): "The following installed packages depend on it: %s" % ' '.join(formatted_deps)) + # Pre-uninstall hook runs first. + spack.hooks.pre_uninstall(self) + + # Uninstalling in Spack only requires removing the prefix. self.remove_prefix() tty.msg("Successfully uninstalled %s." % self.spec.short_spec) @@ -860,6 +964,88 @@ class Package(object): spack.hooks.post_uninstall(self) + def _check_extendable(self): + if not self.extendable: + raise ValueError("Package %s is not extendable!" % self.name) + + + def _sanity_check_extension(self): + if not self.is_extension: + raise ValueError("This package is not an extension.") + extendee_package = self.extendee_spec.package + extendee_package._check_extendable() + + if not extendee_package.installed: + raise ValueError("Can only (de)activate extensions for installed packages.") + if not self.installed: + raise ValueError("Extensions must first be installed.") + if not self.extendee_spec.name in self.extendees: + raise ValueError("%s does not extend %s!" % (self.name, self.extendee.name)) + + + def do_activate(self): + """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() + 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)) + + + def activate(self, extension, **kwargs): + """Symlinks all files from the extension into extendee's install dir. + + Package authors can override this method to support other + extension mechanisms. Spack internals (commands, hooks, etc.) + should call do_activate() method so that proper checks are + always executed. + + """ + def ignore(filename): + return (filename in spack.install_layout.hidden_file_paths or + kwargs.get('ignore', lambda f: False)(filename)) + + tree = LinkTree(extension.prefix) + conflict = tree.find_conflict(self.prefix, ignore=ignore) + if conflict: + raise ExtensionConflictError(conflict) + tree.merge(self.prefix, ignore=ignore) + + + def do_deactivate(self): + """Called on the extension to invoke extendee's deactivate() method.""" + self._sanity_check_extension() + self.extendee_spec.package.deactivate(self, **self.extendee_args) + + if self.spec in spack.install_layout.get_extensions(self.extendee_spec): + 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)) + + + def deactivate(self, extension, **kwargs): + """Unlinks all files from extension out of this package's install dir. + + Package authors can override this method to support other + extension mechanisms. Spack internals (commands, hooks, etc.) + should call do_deactivate() method so that proper checks are + always executed. + + """ + def ignore(filename): + return (filename in spack.install_layout.hidden_file_paths or + kwargs.get('ignore', lambda f: False)(filename)) + + tree = LinkTree(extension.prefix) + tree.unmerge(self.prefix, ignore=ignore) + + def do_clean(self): if self.stage.expanded_archive_path: self.stage.chdir_to_source() @@ -925,6 +1111,23 @@ class Package(object): e.url, e.message) + @property + def rpath(self): + """Get the rpath this package links with, as a list of paths.""" + rpaths = [self.prefix.lib, self.prefix.lib64] + rpaths.extend(d.prefix.lib for d in self.spec.traverse(root=False) + if os.path.isdir(d.prefix.lib)) + rpaths.extend(d.prefix.lib64 for d in self.spec.traverse(root=False) + if os.path.isdir(d.prefix.lib64)) + return rpaths + + + @property + def rpath_args(self): + """Get the rpath args as a string, with -Wl,-rpath= for each element.""" + return " ".join("-Wl,-rpath=%s" % p for p in self.rpath) + + def find_versions_of_archive(*archive_urls, **kwargs): list_url = kwargs.get('list_url', None) list_depth = kwargs.get('list_depth', 1) @@ -1034,3 +1237,9 @@ class NoURLError(PackageError): def __init__(self, cls): super(NoURLError, self).__init__( "Package %s has no version with a URL." % cls.__name__) + + +class ExtensionConflictError(PackageError): + def __init__(self, path): + super(ExtensionConflictError, self).__init__( + "Extension blocked by file: %s" % path) diff --git a/lib/spack/spack/packages.py b/lib/spack/spack/packages.py index db43d3909a..7ef8135c1a 100644 --- a/lib/spack/spack/packages.py +++ b/lib/spack/spack/packages.py @@ -77,6 +77,8 @@ class PackageDB(object): copy = spec.copy() self.instances[copy] = package_class(copy) except Exception, e: + if spack.debug: + sys.excepthook(*sys.exc_info()) raise FailedConstructorError(spec.name, e) return self.instances[spec] @@ -110,6 +112,17 @@ class PackageDB(object): return providers + @_autospec + def extensions_for(self, extendee_spec): + return [p for p in self.all_packages() if p.extends(extendee_spec)] + + + @_autospec + def installed_extensions_for(self, extendee_spec): + return [s.package for s in self.installed_package_specs() + if s.package.extends(extendee_spec)] + + def dirname_for_package_name(self, pkg_name): """Get the directory name for a particular package. This is the directory that contains its package.py file.""" diff --git a/lib/spack/spack/relations.py b/lib/spack/spack/relations.py index b1f4348945..a0c7723473 100644 --- a/lib/spack/spack/relations.py +++ b/lib/spack/spack/relations.py @@ -68,7 +68,7 @@ provides spack install mpileaks ^mvapich spack install mpileaks ^mpich """ -__all__ = [ 'depends_on', 'provides', 'patch', 'version' ] +__all__ = [ 'depends_on', 'extends', 'provides', 'patch', 'version' ] import re import inspect @@ -107,8 +107,9 @@ def depends_on(*specs): """Adds a dependencies local variable in the locals of the calling class, based on args. """ pkg = get_calling_package_name() + clocals = caller_locals() + dependencies = clocals.setdefault('dependencies', {}) - dependencies = caller_locals().setdefault('dependencies', {}) for string in specs: for spec in spack.spec.parse(string): if pkg == spec.name: @@ -116,6 +117,34 @@ def depends_on(*specs): dependencies[spec.name] = spec +def extends(spec, **kwargs): + """Same as depends_on, but dependency is symlinked into parent prefix. + + This is for Python and other language modules where the module + needs to be installed into the prefix of the Python installation. + Spack handles this by installing modules into their own prefix, + but allowing ONE module version to be symlinked into a parent + Python install at a time. + + keyword arguments can be passed to extends() so that extension + packages can pass parameters to the extendee's extension + mechanism. + + """ + pkg = get_calling_package_name() + clocals = caller_locals() + dependencies = clocals.setdefault('dependencies', {}) + extendees = clocals.setdefault('extendees', {}) + if extendees: + raise RelationError("Packages can extend at most one other package.") + + spec = Spec(spec) + if pkg == spec.name: + raise CircularReferenceError('extends', pkg) + dependencies[spec.name] = spec + extendees[spec.name] = (spec, kwargs) + + def provides(*specs, **kwargs): """Allows packages to provide a virtual dependency. If a package provides 'mpi', other packages can declare that they depend on "mpi", and spack diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index 2f4fe9ca24..dffdccaddb 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -553,6 +553,13 @@ class Spec(object): @property + def cshort_spec(self): + """Returns a version of the spec with the dependencies hashed + instead of completely enumerated.""" + return self.format('$_$@$%@$+$=$#', color=True) + + + @property def prefix(self): return Prefix(spack.install_layout.path_for_spec(self)) diff --git a/lib/spack/spack/test/__init__.py b/lib/spack/spack/test/__init__.py index 0eda667abc..c53e6774fc 100644 --- a/lib/spack/spack/test/__init__.py +++ b/lib/spack/spack/test/__init__.py @@ -51,7 +51,8 @@ test_names = ['versions', 'hg_fetch', 'mirror', 'url_extrapolate', - 'cc'] + 'cc', + 'link_tree'] def list_tests(): diff --git a/lib/spack/spack/test/link_tree.py b/lib/spack/spack/test/link_tree.py new file mode 100644 index 0000000000..bc7c2c6b5e --- /dev/null +++ b/lib/spack/spack/test/link_tree.py @@ -0,0 +1,153 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +import os +import unittest +import shutil +import tempfile +from contextlib import closing + +from llnl.util.filesystem import * +from llnl.util.link_tree import LinkTree + +from spack.stage import Stage + + +class LinkTreeTest(unittest.TestCase): + """Tests Spack's LinkTree class.""" + + def setUp(self): + self.stage = Stage('link-tree-test') + + with working_dir(self.stage.path): + touchp('source/1') + touchp('source/a/b/2') + touchp('source/a/b/3') + touchp('source/c/4') + touchp('source/c/d/5') + touchp('source/c/d/6') + touchp('source/c/d/e/7') + + source_path = os.path.join(self.stage.path, 'source') + self.link_tree = LinkTree(source_path) + + + def tearDown(self): + if self.stage: + self.stage.destroy() + + + def check_file_link(self, filename): + self.assertTrue(os.path.isfile(filename)) + self.assertTrue(os.path.islink(filename)) + + + def check_dir(self, filename): + self.assertTrue(os.path.isdir(filename)) + + + def test_merge_to_new_directory(self): + with working_dir(self.stage.path): + self.link_tree.merge('dest') + + self.check_file_link('dest/1') + self.check_file_link('dest/a/b/2') + self.check_file_link('dest/a/b/3') + self.check_file_link('dest/c/4') + self.check_file_link('dest/c/d/5') + self.check_file_link('dest/c/d/6') + self.check_file_link('dest/c/d/e/7') + + self.link_tree.unmerge('dest') + + self.assertFalse(os.path.exists('dest')) + + + def test_merge_to_existing_directory(self): + with working_dir(self.stage.path): + + touchp('dest/x') + touchp('dest/a/b/y') + + self.link_tree.merge('dest') + + self.check_file_link('dest/1') + self.check_file_link('dest/a/b/2') + self.check_file_link('dest/a/b/3') + self.check_file_link('dest/c/4') + self.check_file_link('dest/c/d/5') + self.check_file_link('dest/c/d/6') + self.check_file_link('dest/c/d/e/7') + + self.assertTrue(os.path.isfile('dest/x')) + self.assertTrue(os.path.isfile('dest/a/b/y')) + + self.link_tree.unmerge('dest') + + self.assertTrue(os.path.isfile('dest/x')) + self.assertTrue(os.path.isfile('dest/a/b/y')) + + self.assertFalse(os.path.isfile('dest/1')) + self.assertFalse(os.path.isfile('dest/a/b/2')) + self.assertFalse(os.path.isfile('dest/a/b/3')) + self.assertFalse(os.path.isfile('dest/c/4')) + self.assertFalse(os.path.isfile('dest/c/d/5')) + self.assertFalse(os.path.isfile('dest/c/d/6')) + self.assertFalse(os.path.isfile('dest/c/d/e/7')) + + + def test_merge_with_empty_directories(self): + with working_dir(self.stage.path): + mkdirp('dest/f/g') + mkdirp('dest/a/b/h') + + self.link_tree.merge('dest') + self.link_tree.unmerge('dest') + + self.assertFalse(os.path.exists('dest/1')) + self.assertFalse(os.path.exists('dest/a/b/2')) + self.assertFalse(os.path.exists('dest/a/b/3')) + self.assertFalse(os.path.exists('dest/c/4')) + self.assertFalse(os.path.exists('dest/c/d/5')) + self.assertFalse(os.path.exists('dest/c/d/6')) + self.assertFalse(os.path.exists('dest/c/d/e/7')) + + self.assertTrue(os.path.isdir('dest/a/b/h')) + self.assertTrue(os.path.isdir('dest/f/g')) + + + def test_ignore(self): + with working_dir(self.stage.path): + touchp('source/.spec') + touchp('dest/.spec') + + self.link_tree.merge('dest', ignore=lambda x: x == '.spec') + self.link_tree.unmerge('dest', ignore=lambda x: x == '.spec') + + self.assertFalse(os.path.exists('dest/1')) + self.assertFalse(os.path.exists('dest/a')) + self.assertFalse(os.path.exists('dest/c')) + + self.assertTrue(os.path.isfile('source/.spec')) + self.assertTrue(os.path.isfile('dest/.spec')) diff --git a/lib/spack/spack/test/mirror.py b/lib/spack/spack/test/mirror.py index 51334198ec..89ab14359e 100644 --- a/lib/spack/spack/test/mirror.py +++ b/lib/spack/spack/test/mirror.py @@ -44,7 +44,7 @@ class MirrorTest(MockPackagesTest): self.repos = {} - def set_up_package(self, name, mock_repo_class, url_attr): + def set_up_package(self, name, MockRepoClass, url_attr): """Use this to set up a mock package to be mirrored. Each package needs us to: 1. Set up a mock repo/archive to fetch from. @@ -56,7 +56,7 @@ class MirrorTest(MockPackagesTest): # Get the package and fix its fetch args to point to a mock repo pkg = spack.db.get(spec) - repo = mock_repo_class() + repo = MockRepoClass() self.repos[name] = repo # change the fetch args of the first (only) version. @@ -71,7 +71,7 @@ class MirrorTest(MockPackagesTest): for name, repo in self.repos.items(): if repo.stage: - repo.stage.destroy() + pass #repo.stage.destroy() self.repos.clear() @@ -129,7 +129,7 @@ class MirrorTest(MockPackagesTest): self.assertTrue(all(l in exclude for l in dcmp.left_only)) finally: - stage.destroy() + pass #stage.destroy() def test_git_mirror(self): diff --git a/share/spack/setup-env.sh b/share/spack/setup-env.sh index 91b1dc4630..b2bcbaf6c7 100755 --- a/share/spack/setup-env.sh +++ b/share/spack/setup-env.sh @@ -165,8 +165,8 @@ fi # # Set up modules and dotkit search paths in the user environment # -_sp_share_dir="$(dirname $_sp_source_file)" -_sp_prefix="$(dirname $(dirname $_sp_share_dir))" +_sp_share_dir=$(cd "$(dirname $_sp_source_file)" && pwd) +_sp_prefix=$(cd "$(dirname $(dirname $_sp_share_dir))" && pwd) # TODO: fix SYS_TYPE to something non-LLNL-specific _spack_pathadd DK_NODE "$_sp_share_dir/dotkit/$SYS_TYPE" diff --git a/var/spack/packages/bison/package.py b/var/spack/packages/bison/package.py new file mode 100644 index 0000000000..7c526fb958 --- /dev/null +++ b/var/spack/packages/bison/package.py @@ -0,0 +1,17 @@ +from spack import * + +class Bison(Package): + """Bison is a general-purpose parser generator that converts + an annotated context-free grammar into a deterministic LR or + generalized LR (GLR) parser employing LALR(1) parser tables.""" + + homepage = "http://www.gnu.org/software/bison/" + url = "http://ftp.gnu.org/gnu/bison/bison-3.0.tar.gz" + + version('3.0.4', 'a586e11cd4aff49c3ff6d3b6a4c9ccf8') + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/cloog/package.py b/var/spack/packages/cloog/package.py new file mode 100644 index 0000000000..814a33c76c --- /dev/null +++ b/var/spack/packages/cloog/package.py @@ -0,0 +1,26 @@ +from spack import * + +class Cloog(Package): + """CLooG is a free software and library to generate code for + scanning Z-polyhedra. That is, it finds a code (e.g. in C, + FORTRAN...) that reaches each integral point of one or more + parameterized polyhedra.""" + + homepage = "http://www.cloog.org" + url = "http://www.bastoul.net/cloog/pages/download/count.php3?url=./cloog-0.18.1.tar.gz" + list_url = "http://www.bastoul.net/cloog/pages/download" + + version('0.18.1', 'e34fca0540d840e5d0f6427e98c92252') + version('0.18.0', 'be78a47bd82523250eb3e91646db5b3d') + version('0.17.0', '0aa3302c81f65ca62c114e5264f8a802') + + depends_on("gmp") + depends_on("isl") + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix, + "--with-osl=no", + "--with-isl=%s" % spec['isl'].prefix, + "--with-gmp=%s" % spec['gmp'].prefix) + make() + make("install") diff --git a/var/spack/packages/dri2proto/package.py b/var/spack/packages/dri2proto/package.py new file mode 100644 index 0000000000..11dfa568e2 --- /dev/null +++ b/var/spack/packages/dri2proto/package.py @@ -0,0 +1,14 @@ +from spack import * + +class Dri2proto(Package): + """DRI2 Protocol Headers.""" + homepage = "http://http://cgit.freedesktop.org/xorg/proto/dri2proto/" + url = "http://xorg.freedesktop.org/releases/individual/proto/dri2proto-2.8.tar.gz" + + version('2.8', '19ea18f63d8ae8053c9fa84b60365b77') + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/flex/package.py b/var/spack/packages/flex/package.py new file mode 100644 index 0000000000..b065904912 --- /dev/null +++ b/var/spack/packages/flex/package.py @@ -0,0 +1,15 @@ +from spack import * + +class Flex(Package): + """Flex is a tool for generating scanners.""" + + homepage = "http://flex.sourceforge.net/" + url = "http://download.sourceforge.net/flex/flex-2.5.39.tar.gz" + + version('2.5.39', 'e133e9ead8ec0a58d81166b461244fde') + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/gcc/package.py b/var/spack/packages/gcc/package.py index da0debd5dc..3da6c25d47 100644 --- a/var/spack/packages/gcc/package.py +++ b/var/spack/packages/gcc/package.py @@ -32,29 +32,46 @@ class Gcc(Package): Objective-C, Fortran, and Java.""" homepage = "https://gcc.gnu.org" + url = "http://open-source-box.org/gcc/gcc-4.9.2/gcc-4.9.2.tar.bz2" list_url = 'http://open-source-box.org/gcc/' list_depth = 2 - version('4.9.2', '4df8ee253b7f3863ad0b86359cd39c43', - url="http://open-source-box.org/gcc/gcc-4.9.2/gcc-4.9.2.tar.bz2") - version('4.9.1', 'fddf71348546af523353bd43d34919c1', - url="http://open-source-box.org/gcc/gcc-4.9.1/gcc-4.9.1.tar.bz2") + version('4.9.2', '4df8ee253b7f3863ad0b86359cd39c43') + version('4.9.1', 'fddf71348546af523353bd43d34919c1') + version('4.8.4', '5a84a30839b2aca22a2d723de2a626ec') + version('4.7.4', '4c696da46297de6ae77a82797d2abe28') + version('4.6.4', 'b407a3d1480c11667f293bfb1f17d1a4') + version('4.5.4', '27e459c2566b8209ab064570e1b378f7') - depends_on("mpc") depends_on("mpfr") depends_on("gmp") + depends_on("mpc") # when @4.5: depends_on("libelf") + # Save these until we can do optional deps. + #depends_on("isl") + #depends_on("ppl") + #depends_on("cloog") def install(self, spec, prefix): # libjava/configure needs a minor fix to install into spack paths. filter_file(r"'@.*@'", "'@[[:alnum:]]*@'", 'libjava/configure', string=True) + enabled_languages = set(('c', 'c++', 'fortran', 'java', 'objc')) + if spec.satisfies("@4.7.1:"): + enabled_languages.add('go') + # Rest of install is straightforward. configure("--prefix=%s" % prefix, "--libdir=%s/lib64" % prefix, "--disable-multilib", - "--enable-languages=c,c++,fortran,java,objc,go", + "--enable-languages=" + ','.join(enabled_languages), + "--with-mpc=%s" % spec['mpc'].prefix, + "--with-mpfr=%s" % spec['mpfr'].prefix, + "--with-gmp=%s" % spec['gmp'].prefix, + "--with-libelf=%s" % spec['libelf'].prefix, + "--with-stage1-ldflags=%s" % self.rpath_args, + "--with-boot-ldflags=%s" % self.rpath_args, "--enable-lto", "--with-quad") make() diff --git a/var/spack/packages/hdf5/package.py b/var/spack/packages/hdf5/package.py index 615c2a7fe4..992dd8ec70 100644 --- a/var/spack/packages/hdf5/package.py +++ b/var/spack/packages/hdf5/package.py @@ -18,12 +18,14 @@ class Hdf5(Package): # TODO: currently hard-coded to use OpenMPI def install(self, spec, prefix): + configure( "--prefix=%s" % prefix, "--with-zlib=%s" % spec['zlib'].prefix, "--enable-parallel", - "CC=%s" % spec['openmpi'].prefix.bin + "/mpicc", - "CXX=%s" % spec['openmpi'].prefix.bin + "/mpic++") + "--enable-shared", + "CC=%s" % spec['mpich'].prefix.bin + "/mpicc", + "CXX=%s" % spec['mpich'].prefix.bin + "/mpic++") make() make("install") diff --git a/var/spack/packages/icu4c/package.py b/var/spack/packages/icu4c/package.py new file mode 100644 index 0000000000..55b44463b2 --- /dev/null +++ b/var/spack/packages/icu4c/package.py @@ -0,0 +1,17 @@ +from spack import * + +class Icu4c(Package): + """ICU is a mature, widely used set of C/C++ and Java libraries + providing Unicode and Globalization support for software applications.""" + + homepage = "http://site.icu-project.org/" + url = "http://downloads.sourceforge.net/project/icu/ICU4C/54.1/icu4c-54_1-src.tgz" + + version('54_1', 'e844caed8f2ca24c088505b0d6271bc0') + + def install(self, spec, prefix): + cd("source") + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/isl/package.py b/var/spack/packages/isl/package.py new file mode 100644 index 0000000000..836ef3ea40 --- /dev/null +++ b/var/spack/packages/isl/package.py @@ -0,0 +1,17 @@ +from spack import * + +class Isl(Package): + """isl is a thread-safe C library for manipulating sets and + relations of integer points bounded by affine constraints.""" + homepage = "http://isl.gforge.inria.fr" + url = "http://isl.gforge.inria.fr/isl-0.14.tar.bz2" + + version('0.14', 'acd347243fca5609e3df37dba47fd0bb') + + depends_on("gmp") + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix, + "--with-gmp-prefix=%s" % spec['gmp'].prefix) + make() + make("install") diff --git a/var/spack/packages/libdrm/package.py b/var/spack/packages/libdrm/package.py new file mode 100644 index 0000000000..00736b7811 --- /dev/null +++ b/var/spack/packages/libdrm/package.py @@ -0,0 +1,18 @@ +from spack import * + +class Libdrm(Package): + """A userspace library for accessing the DRM, direct + rendering manager, on Linux, BSD and other operating + systems that support the ioctl interface.""" + + homepage = "http://dri.freedesktop.org/libdrm/" # no real website... + url = "http://dri.freedesktop.org/libdrm/libdrm-2.4.59.tar.gz" + + version('2.4.59', '105ac7af1afcd742d402ca7b4eb168b6') + version('2.4.33', '86e4e3debe7087d5404461e0032231c8') + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/libxcb/package.py b/var/spack/packages/libxcb/package.py new file mode 100644 index 0000000000..521cd0d475 --- /dev/null +++ b/var/spack/packages/libxcb/package.py @@ -0,0 +1,21 @@ +from spack import * + +class Libxcb(Package): + """The X protocol C-language Binding (XCB) is a replacement + for Xlib featuring a small footprint, latency hiding, direct + access to the protocol, improved threading support, and + extensibility.""" + + homepage = "http://xcb.freedesktop.org/" + url = "http://xcb.freedesktop.org/dist/libxcb-1.11.tar.gz" + + version('1.11', '1698dd837d7e6e94d029dbe8b3a82deb') + + depends_on("python") + depends_on("xcb-proto") + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/libxshmfence/package.py b/var/spack/packages/libxshmfence/package.py new file mode 100644 index 0000000000..3aa2448b46 --- /dev/null +++ b/var/spack/packages/libxshmfence/package.py @@ -0,0 +1,16 @@ +from spack import * + +class Libxshmfence(Package): + """This is a tiny library that exposes a event API on top of Linux + futexes.""" + + homepage = "http://keithp.com/blogs/dri3_extension/" # not really... + url = "http://xorg.freedesktop.org/archive/individual/lib/libxshmfence-1.2.tar.gz" + + version('1.2', 'f0b30c0fc568b22ec524859ee28556f1') + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/llvm/package.py b/var/spack/packages/llvm/package.py index c7a10df55a..69354a5c90 100644 --- a/var/spack/packages/llvm/package.py +++ b/var/spack/packages/llvm/package.py @@ -35,6 +35,12 @@ class Llvm(Package): url = "http://llvm.org/releases/3.4.2/llvm-3.4.2.src.tar.gz" version('3.4.2', 'a20669f75967440de949ac3b1bad439c') + version('3.0', 'a8e5f5f1c1adebae7b4a654c376a6005', + url='http://llvm.org/releases/3.0/llvm-3.0.tar.gz') + version('2.9', '793138412d2af2c7c7f54615f8943771', + url='http://llvm.org/releases/2.9/llvm-2.9.tgz') + version('2.8', '220d361b4d17051ff4bb21c64abe05ba', + url='http://llvm.org/releases/2.8/llvm-2.8.tgz') def install(self, spec, prefix): env['CXXFLAGS'] = self.compiler.cxx11_flag diff --git a/var/spack/packages/mesa/package.py b/var/spack/packages/mesa/package.py new file mode 100644 index 0000000000..2dba878a77 --- /dev/null +++ b/var/spack/packages/mesa/package.py @@ -0,0 +1,33 @@ +from spack import * + +class Mesa(Package): + """Mesa is an open-source implementation of the OpenGL + specification - a system for rendering interactive 3D graphics.""" + + homepage = "http://www.mesa3d.org" + url = "ftp://ftp.freedesktop.org/pub/mesa/older-versions/8.x/8.0.5/MesaLib-8.0.5.tar.gz" + # url = "ftp://ftp.freedesktop.org/pub/mesa/10.4.4/MesaLib-10.4.4.tar.gz" + + # version('10.4.4', '8d863a3c209bf5116b2babfccccc68ce') + version('8.0.5', 'cda5d101f43b8784fa60bdeaca4056f2') + + # mesa 7.x, 8.x, 9.x + depends_on("libdrm@2.4.33") + depends_on("llvm@3.0") + + # patch("llvm-fixes.patch") # using newer llvm + + # mesa 10.x + # depends_on("py-mako") + # depends_on("flex") + # depends_on("bison") + # depends_on("dri2proto") + # depends_on("libxcb") + # depends_on("libxshmfence") + + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/ppl/package.py b/var/spack/packages/ppl/package.py new file mode 100644 index 0000000000..018d5c523d --- /dev/null +++ b/var/spack/packages/ppl/package.py @@ -0,0 +1,28 @@ +from spack import * + +class Ppl(Package): + """The Parma Polyhedra Library (PPL) provides numerical + abstractions especially targeted at applications in the field of + analysis and verification of complex systems. These abstractions + include convex polyhedra, some special classes of polyhedra shapes + that offer interesting complexity/precision tradeoffs, and grids + which represent regularly spaced points that satisfy a set of + linear congruence relations. The library also supports finite + powersets and products of polyhedra and grids, a mixed integer + linear programming problem solver using an exact-arithmetic + version of the simplex algorithm, a parametric integer programming + solver, and primitives for termination analysis via the automatic + synthesis of linear ranking functions.""" + + homepage = "http://bugseng.com/products/ppl/" + url = "http://bugseng.com/products/ppl/download/ftp/releases/1.1/ppl-1.1.tar.gz" + + version('1.1', '4f2422c0ef3f409707af32108deb30a7') + + depends_on("gmp") + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix, + "--with-gmp=%s" % spec['gmp'].prefix) + make() + make("install") diff --git a/var/spack/packages/py-basemap/package.py b/var/spack/packages/py-basemap/package.py new file mode 100644 index 0000000000..7b6d8e7e65 --- /dev/null +++ b/var/spack/packages/py-basemap/package.py @@ -0,0 +1,25 @@ +from spack import * +import os + +class PyBasemap(Package): + """The matplotlib basemap toolkit is a library for plotting 2D data on maps in Python.""" + homepage = "http://matplotlib.org/basemap/" + url = "https://downloads.sourceforge.net/project/matplotlib/matplotlib-toolkits/basemap-1.0.7/basemap-1.0.7.tar.gz" + + version('1.0.7', '48c0557ced9e2c6e440b28b3caff2de8') + + geos_version = {'1.0.7' : '3.3.3'} + + extends('python') + depends_on('py-setuptools') + depends_on('py-numpy') + depends_on('py-matplotlib') + depends_on('py-pil') + + def install(self, spec, prefix): + with working_dir('geos-%s' % self.geos_version[str(self.version)]): + configure("--prefix=" + prefix) + make() + make("install") + os.environ['GEOS_DIR'] = prefix + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-biopython/package.py b/var/spack/packages/py-biopython/package.py new file mode 100644 index 0000000000..2ed04c389e --- /dev/null +++ b/var/spack/packages/py-biopython/package.py @@ -0,0 +1,14 @@ +from spack import * + +class PyBiopython(Package): + """It is a distributed collaborative effort to develop Python libraries and applications which address the needs of current and future work in bioinformatics.""" + homepage = "http://biopython.org/wiki/Main_Page" + url = "http://biopython.org/DIST/biopython-1.65.tar.gz" + + version('1.65', '143e7861ade85c0a8b5e2bbdd1da1f67') + + extends('python') + depends_on('py-mx') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-cython/package.py b/var/spack/packages/py-cython/package.py new file mode 100644 index 0000000000..af67a15526 --- /dev/null +++ b/var/spack/packages/py-cython/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyCython(Package): + """The Cython compiler for writing C extensions for the Python language.""" + homepage = "https://pypi.python.org/pypi/cython" + url = "https://pypi.python.org/packages/source/C/Cython/Cython-0.21.2.tar.gz" + + version('0.21.2', 'd21adb870c75680dc857cd05d41046a4') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-dateutil/package.py b/var/spack/packages/py-dateutil/package.py new file mode 100644 index 0000000000..11699e07ee --- /dev/null +++ b/var/spack/packages/py-dateutil/package.py @@ -0,0 +1,14 @@ +from spack import * + +class PyDateutil(Package): + """Extensions to the standard Python datetime module.""" + homepage = "https://pypi.python.org/pypi/dateutil" + url = "https://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-2.4.0.tar.gz" + + version('2.4.0', '75714163bb96bedd07685cdb2071b8bc') + + extends('python') + depends_on('py-setuptools') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-epydoc/package.py b/var/spack/packages/py-epydoc/package.py new file mode 100644 index 0000000000..af05510504 --- /dev/null +++ b/var/spack/packages/py-epydoc/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyEpydoc(Package): + """Epydoc is a tool for generating API documentation documentation for Python modules, based on their docstrings.""" + homepage = "https://pypi.python.org/pypi/epydoc" + url = "https://pypi.python.org/packages/source/e/epydoc/epydoc-3.0.1.tar.gz" + + version('3.0.1', '36407974bd5da2af00bf90ca27feeb44') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-gnuplot/package.py b/var/spack/packages/py-gnuplot/package.py new file mode 100644 index 0000000000..0a2c073a49 --- /dev/null +++ b/var/spack/packages/py-gnuplot/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyGnuplot(Package): + """Gnuplot.py is a Python package that allows you to create graphs from within Python using the gnuplot plotting program.""" + homepage = "http://gnuplot-py.sourceforge.net/" + url = "http://downloads.sourceforge.net/project/gnuplot-py/Gnuplot-py/1.8/gnuplot-py-1.8.tar.gz" + + version('1.8', 'abd6f571e7aec68ae7db90a5217cd5b1') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-h5py/package.py b/var/spack/packages/py-h5py/package.py new file mode 100644 index 0000000000..f72b3ac06e --- /dev/null +++ b/var/spack/packages/py-h5py/package.py @@ -0,0 +1,18 @@ +from spack import * +import re + +class PyH5py(Package): + """The h5py package provides both a high- and low-level interface to the HDF5 library from Python.""" + homepage = "https://pypi.python.org/pypi/h5py" + url = "https://pypi.python.org/packages/source/h/h5py/h5py-2.4.0.tar.gz" + + version('2.4.0', '80c9a94ae31f84885cc2ebe1323d6758') + + extends('python', ignore=lambda f: re.match(r'cy*', f)) + depends_on('hdf5') + depends_on('py-numpy') + depends_on('py-cython') + + def install(self, spec, prefix): + python('setup.py', 'configure', '--hdf5=%s' % spec['hdf5'].prefix) + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-ipython/package.py b/var/spack/packages/py-ipython/package.py new file mode 100644 index 0000000000..731e661dfd --- /dev/null +++ b/var/spack/packages/py-ipython/package.py @@ -0,0 +1,14 @@ +from spack import * + +class PyIpython(Package): + """IPython provides a rich toolkit to help you make the most out of using Python interactively.""" + homepage = "https://pypi.python.org/pypi/ipython" + url = "https://pypi.python.org/packages/source/i/ipython/ipython-2.3.1.tar.gz" + + version('2.3.1', '2b7085525dac11190bfb45bb8ec8dcbf') + + extends('python') + depends_on('py-pygments') + + 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 new file mode 100644 index 0000000000..0dcefbd9cf --- /dev/null +++ b/var/spack/packages/py-libxml2/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyLibxml2(Package): + """A Python wrapper around libxml2.""" + homepage = "https://xmlsoft.org/python.html" + url = "ftp://xmlsoft.org/libxml2/python/libxml2-python-2.6.21.tar.gz" + + version('2.6.21', '229dd2b3d110a77defeeaa73af83f7f3') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-mako/package.py b/var/spack/packages/py-mako/package.py new file mode 100644 index 0000000000..3e91ffd8e5 --- /dev/null +++ b/var/spack/packages/py-mako/package.py @@ -0,0 +1,16 @@ +from spack import * + +class PyMako(Package): + """A super-fast templating language that borrows the best + ideas from the existing templating languages.""" + + homepage = "https://pypi.python.org/pypi/mako" + url = "https://pypi.python.org/packages/source/M/Mako/Mako-1.0.1.tar.gz" + + version('1.0.1', '9f0aafd177b039ef67b90ea350497a54') + + depends_on('py-setuptools') + extends('python') + + 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 new file mode 100644 index 0000000000..5979ceeab0 --- /dev/null +++ b/var/spack/packages/py-matplotlib/package.py @@ -0,0 +1,38 @@ +from spack import * +import os + +class PyMatplotlib(Package): + """Python plotting package.""" + homepage = "https://pypi.python.org/pypi/matplotlib" + url = "https://pypi.python.org/packages/source/m/matplotlib/matplotlib-1.4.2.tar.gz" + + version('1.4.2', '7d22efb6cce475025733c50487bd8898') + + extends('python') + depends_on('py-pyside') + depends_on('py-ipython') + depends_on('py-pyparsing') + depends_on('py-six') + depends_on('py-dateutil') + depends_on('py-pytz') + depends_on('py-nose') + depends_on('py-numpy') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) + if str(self.version) == '1.4.2': + # hack to fix configuration file + config_file = None + for p,d,f in os.walk(prefix.lib): + for file in f: + if file.find('matplotlibrc') != -1: + config_file = join_path(p, 'matplotlibrc') + print config_file + if config_file == None: + raise InstallError('could not find config file') + filter_file(r'backend : pyside', + 'backend : Qt4Agg', + config_file) + filter_file(r'#backend.qt4 : PyQt4', + 'backend.qt4 : PySide', + config_file) diff --git a/var/spack/packages/py-mpi4py/package.py b/var/spack/packages/py-mpi4py/package.py new file mode 100644 index 0000000000..fdea340dc2 --- /dev/null +++ b/var/spack/packages/py-mpi4py/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyMpi4py(Package): + """This package provides Python bindings for the Message Passing Interface (MPI) standard. It is implemented on top of the MPI-1/MPI-2 specification and exposes an API which grounds on the standard MPI-2 C++ bindings.""" + homepage = "https://pypi.python.org/pypi/mpi4py" + url = "https://pypi.python.org/packages/source/m/mpi4py/mpi4py-1.3.1.tar.gz" + + version('1.3.1', 'dbe9d22bdc8ed965c23a7ceb6f32fc3c') + extends('python') + depends_on('mpi') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-mx/package.py b/var/spack/packages/py-mx/package.py new file mode 100644 index 0000000000..717ee0562b --- /dev/null +++ b/var/spack/packages/py-mx/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyMx(Package): + """The eGenix.com mx Base Distribution for Python is a collection of professional quality software tools which enhance Python's usability in many important areas such as fast text searching, date/time processing and high speed data types.""" + homepage = "http://www.egenix.com/products/python/mxBase/" + url = "https://downloads.egenix.com/python/egenix-mx-base-3.2.8.tar.gz" + + version('3.2.8', '9d9d3a25f9dc051a15e97f452413423b') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-nose/package.py b/var/spack/packages/py-nose/package.py new file mode 100644 index 0000000000..6df84e831d --- /dev/null +++ b/var/spack/packages/py-nose/package.py @@ -0,0 +1,15 @@ +from spack import * + +class PyNose(Package): + """nose extends the test loading and running features of unittest, + making it easier to write, find and run tests.""" + + homepage = "https://pypi.python.org/pypi/nose" + url = "https://pypi.python.org/packages/source/n/nose/nose-1.3.4.tar.gz" + + version('1.3.4', '6ed7169887580ddc9a8e16048d38274d') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-numpy/package.py b/var/spack/packages/py-numpy/package.py new file mode 100644 index 0000000000..e6cb6a464f --- /dev/null +++ b/var/spack/packages/py-numpy/package.py @@ -0,0 +1,14 @@ +from spack import * + +class PyNumpy(Package): + """array processing for numbers, strings, records, and objects.""" + homepage = "https://pypi.python.org/pypi/numpy" + url = "https://pypi.python.org/packages/source/n/numpy/numpy-1.9.1.tar.gz" + + version('1.9.1', '78842b73560ec378142665e712ae4ad9') + + extends('python') + depends_on('py-nose') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-pexpect/package.py b/var/spack/packages/py-pexpect/package.py new file mode 100644 index 0000000000..ff5fac84e0 --- /dev/null +++ b/var/spack/packages/py-pexpect/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyPexpect(Package): + """Pexpect allows easy control of interactive console applications.""" + homepage = "https://pypi.python.org/pypi/pexpect" + url = "https://pypi.python.org/packages/source/p/pexpect/pexpect-3.3.tar.gz" + + version('3.3', '0de72541d3f1374b795472fed841dce8') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-pil/package.py b/var/spack/packages/py-pil/package.py new file mode 100644 index 0000000000..743b761981 --- /dev/null +++ b/var/spack/packages/py-pil/package.py @@ -0,0 +1,14 @@ +from spack import * + +class PyPil(Package): + """The Python Imaging Library (PIL) adds image processing capabilities to your Python interpreter. This library supports many file formats, and provides powerful image processing and graphics capabilities.""" + + homepage = "http://www.pythonware.com/products/pil/" + url = "http://effbot.org/media/downloads/Imaging-1.1.7.tar.gz" + + version('1.1.7', 'fc14a54e1ce02a0225be8854bfba478e') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-pmw/package.py b/var/spack/packages/py-pmw/package.py new file mode 100644 index 0000000000..56131811e9 --- /dev/null +++ b/var/spack/packages/py-pmw/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyPmw(Package): + """Pmw is a toolkit for building high-level compound widgets, or megawidgets, constructed using other widgets as component parts.""" + homepage = "https://pypi.python.org/pypi/Pmw" + url = "https://pypi.python.org/packages/source/P/Pmw/Pmw-2.0.0.tar.gz" + + version('2.0.0', 'c7c3f26c4f5abaa99807edefee578fc0') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-pychecker/package.py b/var/spack/packages/py-pychecker/package.py new file mode 100644 index 0000000000..bda5a746aa --- /dev/null +++ b/var/spack/packages/py-pychecker/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyPychecker(Package): + """""" + homepage = "http://pychecker.sourceforge.net/" + url = "http://sourceforge.net/projects/pychecker/files/pychecker/0.8.19/pychecker-0.8.19.tar.gz" + + version('0.8.19', 'c37182863dfb09209d6ba4f38fce9d2b') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-pygments/package.py b/var/spack/packages/py-pygments/package.py new file mode 100644 index 0000000000..990eebde65 --- /dev/null +++ b/var/spack/packages/py-pygments/package.py @@ -0,0 +1,14 @@ +from spack import * + +class PyPygments(Package): + """Pygments is a syntax highlighting package written in Python.""" + homepage = "https://pypi.python.org/pypi/pygments" + url = "https://pypi.python.org/packages/source/P/Pygments/Pygments-2.0.1.tar.gz" + + version('2.0.1', 'e0daf4c14a4fe5b630da765904de4d6c') + + extends('python') + 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 new file mode 100644 index 0000000000..ebde861f94 --- /dev/null +++ b/var/spack/packages/py-pylint/package.py @@ -0,0 +1,16 @@ +from spack import * +import re + +class PyPylint(Package): + """array processing for numbers, strings, records, and objects.""" + homepage = "https://pypi.python.org/pypi/pylint" + url = "https://pypi.python.org/packages/source/p/pylint/pylint-1.4.1.tar.gz" + + version('1.4.1', 'df7c679bdcce5019389038847e4de622') + +# extends('python') + extends('python', ignore=lambda f:re.match(r"site.py*", f)) + depends_on('py-nose') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-pyparsing/package.py b/var/spack/packages/py-pyparsing/package.py new file mode 100644 index 0000000000..a6e50ad139 --- /dev/null +++ b/var/spack/packages/py-pyparsing/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyPyparsing(Package): + """A Python Parsing Module.""" + homepage = "https://pypi.python.org/pypi/pyparsing" + url = "https://pypi.python.org/packages/source/p/pyparsing/pyparsing-2.0.3.tar.gz" + + version('2.0.3', '0fe479be09fc2cf005f753d3acc35939') + + extends('python') + + 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-pyqt4/package.py new file mode 100644 index 0000000000..eeb1382560 --- /dev/null +++ b/var/spack/packages/py-pyqt4/package.py @@ -0,0 +1,18 @@ +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.""" + 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') + + 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])) + make() + make('install') diff --git a/var/spack/packages/py-pyside/package.py b/var/spack/packages/py-pyside/package.py new file mode 100644 index 0000000000..b01e16d7e6 --- /dev/null +++ b/var/spack/packages/py-pyside/package.py @@ -0,0 +1,18 @@ +from spack import * +import spack.package +import os + +class PyPyside(Package): + """array processing for numbers, strings, records, and objects.""" + homepage = "https://pypi.python.org/pypi/pyside" + url = "https://pypi.python.org/packages/source/P/PySide/PySide-1.2.2.tar.gz" + + version('1.2.2', 'c45bc400c8a86d6b35f34c29e379e44d') + + extends('python') + + def install(self, spec, prefix): + qmake_path = '/usr/lib64/qt4/bin/qmake' + if not os.path.exists(qmake_path): + raise spack.package.InstallError("Failed to find qmake in %s" % qmake_path) + python('setup.py', 'install', '--prefix=%s' % prefix, '--qmake=%s' % qmake_path) diff --git a/var/spack/packages/py-pytz/package.py b/var/spack/packages/py-pytz/package.py new file mode 100644 index 0000000000..80bcfe82ca --- /dev/null +++ b/var/spack/packages/py-pytz/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyPytz(Package): + """World timezone definitions, modern and historical.""" + homepage = "https://pypi.python.org/pypi/pytz" + url = "https://pypi.python.org/packages/source/p/pytz/pytz-2014.10.tar.gz" + + version('2014.10', 'eb1cb941a20c5b751352c52486aa1dd7') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-rpy2/package.py b/var/spack/packages/py-rpy2/package.py new file mode 100644 index 0000000000..3817059911 --- /dev/null +++ b/var/spack/packages/py-rpy2/package.py @@ -0,0 +1,14 @@ +from spack import * + +class PyRpy2(Package): + """rpy2 is a redesign and rewrite of rpy. It is providing a low-level interface to R from Python, a proposed high-level interface, including wrappers to graphical libraries, as well as R-like structures and functions.""" + homepage = "https://pypi.python.org/pypi/rpy2" + url = "https://pypi.python.org/packages/source/r/rpy2/rpy2-2.5.4.tar.gz" + + version('2.5.4', '115a20ac30883f096da2bdfcab55196d') + + extends('python') + depends_on('py-setuptools') + + 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 new file mode 100644 index 0000000000..73600e6cb9 --- /dev/null +++ b/var/spack/packages/py-scientificpython/package.py @@ -0,0 +1,13 @@ +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.""" + homepage = "https://sourcesup.renater.fr/projects/scientific-py/" + url = "https://sourcesup.renater.fr/frs/download.php/4411/ScientificPython-2.8.1.tar.gz" + + version('2.8.1', '73ee0df19c7b58cdf2954261f0763c77') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-scikit-learn/package.py b/var/spack/packages/py-scikit-learn/package.py new file mode 100644 index 0000000000..c59c05a619 --- /dev/null +++ b/var/spack/packages/py-scikit-learn/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PyScikitLearn(Package): + """""" + homepage = "https://pypi.python.org/pypi/scikit-learn" + url = "https://pypi.python.org/packages/source/s/scikit-learn/scikit-learn-0.15.2.tar.gz" + + version('0.15.2', 'd9822ad0238e17b382a3c756ea94fe0d') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-scipy/package.py b/var/spack/packages/py-scipy/package.py new file mode 100644 index 0000000000..b5325b919f --- /dev/null +++ b/var/spack/packages/py-scipy/package.py @@ -0,0 +1,15 @@ +from spack import * + +class PyScipy(Package): + """Scientific Library for Python.""" + homepage = "https://pypi.python.org/pypi/scipy" + url = "https://pypi.python.org/packages/source/s/scipy/scipy-0.15.0.tar.gz" + + version('0.15.0', '639112f077f0aeb6d80718dc5019dc7a') + + extends('python') + depends_on('py-nose') + depends_on('py-numpy') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-setuptools/package.py b/var/spack/packages/py-setuptools/package.py new file mode 100644 index 0000000000..755288d55c --- /dev/null +++ b/var/spack/packages/py-setuptools/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PySetuptools(Package): + """Easily download, build, install, upgrade, and uninstall Python packages.""" + homepage = "https://pypi.python.org/pypi/setuptools" + url = "https://pypi.python.org/packages/source/s/setuptools/setuptools-11.3.tar.gz" + + version('11.3.1', '01f69212e019a2420c1693fb43593930') + + extends('python') + + 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 new file mode 100644 index 0000000000..06aea35a74 --- /dev/null +++ b/var/spack/packages/py-sip/package.py @@ -0,0 +1,15 @@ +from spack import * + +class PySip(Package): + """SIP is a tool that makes it very easy to create Python bindings for C and C++ libraries.""" + homepage = "http://www.riverbankcomputing.com/software/sip/intro" + url = "http://sourceforge.net/projects/pyqt/files/sip/sip-4.16.5/sip-4.16.5.tar.gz" + + version('4.16.5', '6d01ea966a53e4c7ae5c5e48c40e49e5') + + extends('python') + + def install(self, spec, prefix): + python('configure.py') + make() + make('install') diff --git a/var/spack/packages/py-six/package.py b/var/spack/packages/py-six/package.py new file mode 100644 index 0000000000..04d29adced --- /dev/null +++ b/var/spack/packages/py-six/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PySix(Package): + """Python 2 and 3 compatibility utilities.""" + homepage = "https://pypi.python.org/pypi/six" + url = "https://pypi.python.org/packages/source/s/six/six-1.9.0.tar.gz" + + version('1.9.0', '476881ef4012262dfc8adc645ee786c4') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-sympy/package.py b/var/spack/packages/py-sympy/package.py new file mode 100644 index 0000000000..c17e35b95f --- /dev/null +++ b/var/spack/packages/py-sympy/package.py @@ -0,0 +1,13 @@ +from spack import * + +class PySympy(Package): + """SymPy is a Python library for symbolic mathematics.""" + homepage = "https://pypi.python.org/pypi/sympy" + url = "https://pypi.python.org/packages/source/s/sympy/sympy-0.7.6.tar.gz" + + version('0.7.6', '3d04753974306d8a13830008e17babca') + + extends('python') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/py-virtualenv/package.py b/var/spack/packages/py-virtualenv/package.py new file mode 100644 index 0000000000..c1b359e164 --- /dev/null +++ b/var/spack/packages/py-virtualenv/package.py @@ -0,0 +1,18 @@ +from spack import * +import shutil + +class PyVirtualenv(Package): + """virtualenv is a tool to create isolated Python environments.""" + homepage = "http://virtualenv.readthedocs.org/projects/virtualenv/" + url = "https://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.11.6.tar.gz" + + version('1.11.6', 'f61cdd983d2c4e6aeabb70b1060d6f49') + + extends('python') + + def clean(self): + if os.path.exists('build'): + shutil.rmtree('build') + + def install(self, spec, prefix): + python('setup.py', 'install', '--prefix=%s' % prefix) diff --git a/var/spack/packages/python/package.py b/var/spack/packages/python/package.py index e6c3e28820..8a6d574d9b 100644 --- a/var/spack/packages/python/package.py +++ b/var/spack/packages/python/package.py @@ -1,10 +1,17 @@ from spack import * +import spack +import os +import re +from contextlib import closing + class Python(Package): """The Python programming language.""" homepage = "http://www.python.org" url = "http://www.python.org/ftp/python/2.7.8/Python-2.7.8.tar.xz" + extendable = True + version('2.7.8', 'd235bdfa75b8396942e360a70487ee00') depends_on("openssl") @@ -23,3 +30,118 @@ class Python(Package): "--enable-shared") make() make("install") + + + # ======================================================================== + # Set up environment to make install easy for python extensions. + # ======================================================================== + + @property + def python_lib_dir(self): + return os.path.join('lib', 'python%d.%d' % self.version[:2]) + + + @property + def site_packages_dir(self): + return os.path.join(self.python_lib_dir, 'site-packages') + + + def setup_extension_environment(self, module, spec, ext_spec): + """Called before python modules' install() methods. + + In most cases, extensions will only need to have one line:: + + python('setup.py', 'install', '--prefix=%s' % prefix) + """ + # Python extension builds can have a global python executable function + 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) + + # Add site packages directory to the PYTHONPATH + os.environ['PYTHONPATH'] = module.site_packages_dir + + # Make the site packages directory if it does not exist already. + mkdirp(module.site_packages_dir) + + + # ======================================================================== + # Handle specifics of activating and deactivating python modules. + # ======================================================================== + + 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 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 (any(re.search(p, filename) for p in patterns) or + orig_ignore(filename)) + + return ignore + + + def write_easy_install_pth(self, extensions): + paths = [] + for ext in extensions: + ext_site_packages = os.path.join(ext.prefix, self.site_packages_dir) + easy_pth = "%s/easy-install.pth" % ext_site_packages + + if not os.path.isfile(easy_pth): + continue + + with closing(open(easy_pth)) as f: + for line in f: + line = line.rstrip() + + # Skip lines matching these criteria + if not line: continue + if re.search(r'^(import|#)', line): continue + if (ext.name != 'py-setuptools' and + re.search(r'setuptools.*egg$', line)): continue + + paths.append(line) + + site_packages = os.path.join(self.prefix, self.site_packages_dir) + main_pth = "%s/easy-install.pth" % site_packages + + if not paths: + if os.path.isfile(main_pth): + os.remove(main_pth) + + else: + with closing(open(main_pth, 'w')) as f: + f.write("import sys; sys.__plen = len(sys.path)\n") + for path in paths: + f.write("%s\n" % path) + f.write("import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; " + "p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)\n") + + + def activate(self, ext_pkg, **args): + 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) + + + 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) diff --git a/var/spack/packages/qt/package.py b/var/spack/packages/qt/package.py index 4f82a9d9d6..c8b19d07f5 100644 --- a/var/spack/packages/qt/package.py +++ b/var/spack/packages/qt/package.py @@ -1,4 +1,5 @@ from spack import * +import os class Qt(Package): """Qt is a comprehensive cross-platform C++ application framework.""" @@ -29,7 +30,16 @@ class Qt(Package): depends_on("libmng") depends_on("jpeg") - depends_on("gperf") # Needed to build Qt with webkit. + # Webkit + # depends_on("gperf") + # depends_on("flex") + # depends_on("bison") + # depends_on("ruby") + # depends_on("icu4c") + + # OpenGL hardware acceleration + depends_on("mesa") + depends_on("libxcb") def patch(self): if self.spec.satisfies('@4'): @@ -45,46 +55,25 @@ class Qt(Package): filter_file(r'^QMAKE_CXX *=.*$', 'QMAKE_CXX = c++', qmake_conf) - @property - def common_config_args(self): - return [ - '-prefix', self.prefix, - '-v', - '-opensource', - "-release", - '-shared', - '-confirm-license', - '-openssl-linked', - '-dbus-linked', - '-optimized-qmake', - '-no-openvg', - '-no-pch', - # For now, disable all the database drivers - "-no-sql-db2", "-no-sql-ibase", "-no-sql-mysql", "-no-sql-oci", "-no-sql-odbc", - "-no-sql-psql", "-no-sql-sqlite", "-no-sql-sqlite2", "-no-sql-tds", - # NIS is deprecated in more recent glibc - "-no-nis"] - - - @when('@4') - def configure(self): - configure('-no-phonon', - '-no-phonon-backend', - '-fast', - *self.common_config_args) - - - @when('@5') - def configure(self): - configure('-no-eglfs', - '-no-directfb', - '-qt-xcb', - # If someone wants to get a webkit build working, be my guest! - '-skip', 'qtwebkit', - *self.common_config_args) - - def install(self, spec, prefix): - self.configure() + # Apparently this is the only way to + # "truly" get rid of webkit compiles now... + os.rename("qtwebkit","no-qtwebkit") + os.rename("qtwebkit-examples","no-qtwebkit-examples") + configure('-v', + '-confirm-license', + '-opensource', + '-prefix', prefix, + '-openssl-linked', + '-dbus-linked', + #'-fast', + '-opengl', + '-qt-xcb', + '-optimized-qmake', + '-no-pch', +# phonon required for py-pyqt4 +# '-no-phonon', +# '-no-phonon-backend', + '-no-openvg') make() make("install") diff --git a/var/spack/packages/ruby/package.py b/var/spack/packages/ruby/package.py new file mode 100644 index 0000000000..718fd0a3be --- /dev/null +++ b/var/spack/packages/ruby/package.py @@ -0,0 +1,16 @@ +from spack import * + +class Ruby(Package): + """A dynamic, open source programming language with a focus on + simplicity and productivity.""" + + homepage = "https://www.ruby-lang.org/" + url = "http://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.0.tar.gz" + + version('2.2.0', 'cd03b28fd0b555970f5c4fd481700852') + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") diff --git a/var/spack/packages/util-linux/package.py b/var/spack/packages/util-linux/package.py new file mode 100644 index 0000000000..cb7ceabf57 --- /dev/null +++ b/var/spack/packages/util-linux/package.py @@ -0,0 +1,20 @@ +from spack import * +import os + +class UtilLinux(Package): + """Util-linux is a suite of essential utilities for any Linux system.""" + + homepage = "http://freecode.com/projects/util-linux" + url = "https://www.kernel.org/pub/linux/utils/util-linux/v2.25/util-linux-2.25.tar.gz" + + version('2.25', 'f6d7fc6952ec69c4dc62c8d7c59c1d57') + + depends_on("python@2.7:") + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix, + "PKG_CONFIG_PATH=%s/pkgconfig" % spec['python'].prefix.lib, + "--disable-use-tty-group") + + make() + make("install") diff --git a/var/spack/packages/xcb-proto/package.py b/var/spack/packages/xcb-proto/package.py new file mode 100644 index 0000000000..17a94bd892 --- /dev/null +++ b/var/spack/packages/xcb-proto/package.py @@ -0,0 +1,15 @@ +from spack import * + +class XcbProto(Package): + """Protocol for libxcb""" + + homepage = "http://xcb.freedesktop.org/" + url = "http://xcb.freedesktop.org/dist/xcb-proto-1.11.tar.gz" + + version('1.11', 'c8c6cb72c84f58270f4db1f39607f66a') + + def install(self, spec, prefix): + configure("--prefix=%s" % prefix) + + make() + make("install") |