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