From 9d01df9e8aca7e1ddd88d9c9b1f42ca048878635 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Wed, 12 Mar 2014 22:24:47 -0400 Subject: Moving utilities to a common LLNL package. --- lib/spack/env/cc | 2 +- lib/spack/llnl/__init__.py | 0 lib/spack/llnl/util/__init__.py | 0 lib/spack/llnl/util/compare/__init__.py | 0 lib/spack/llnl/util/compare/none_high.py | 70 +++++++++++ lib/spack/llnl/util/compare/none_low.py | 70 +++++++++++ lib/spack/llnl/util/filesystem.py | 90 ++++++++++++++ lib/spack/llnl/util/lang.py | 187 ++++++++++++++++++++++++++++ lib/spack/llnl/util/tty/__init__.py | 95 ++++++++++++++ lib/spack/llnl/util/tty/colify.py | 193 +++++++++++++++++++++++++++++ lib/spack/llnl/util/tty/color.py | 190 ++++++++++++++++++++++++++++ lib/spack/spack/architecture.py | 3 +- lib/spack/spack/cmd/__init__.py | 5 +- lib/spack/spack/cmd/bootstrap.py | 10 +- lib/spack/spack/cmd/checksum.py | 5 +- lib/spack/spack/cmd/clean.py | 3 +- lib/spack/spack/cmd/compilers.py | 5 +- lib/spack/spack/cmd/create.py | 3 +- lib/spack/spack/cmd/edit.py | 3 +- lib/spack/spack/cmd/find.py | 7 +- lib/spack/spack/cmd/info.py | 3 +- lib/spack/spack/cmd/list.py | 2 +- lib/spack/spack/cmd/mirror.py | 10 +- lib/spack/spack/cmd/providers.py | 3 +- lib/spack/spack/cmd/spec.py | 3 +- lib/spack/spack/cmd/test.py | 8 +- lib/spack/spack/cmd/uninstall.py | 3 +- lib/spack/spack/cmd/versions.py | 2 +- lib/spack/spack/colify.py | 193 ----------------------------- lib/spack/spack/color.py | 191 ---------------------------- lib/spack/spack/compilers/__init__.py | 3 +- lib/spack/spack/directory_layout.py | 11 +- lib/spack/spack/globals.py | 25 ++-- lib/spack/spack/multimethod.py | 3 +- lib/spack/spack/package.py | 78 ++++++++---- lib/spack/spack/packages/__init__.py | 11 +- lib/spack/spack/patch.py | 7 +- lib/spack/spack/relations.py | 3 +- lib/spack/spack/spec.py | 7 +- lib/spack/spack/stage.py | 16 +-- lib/spack/spack/test/__init__.py | 6 +- lib/spack/spack/test/mock_packages_test.py | 6 +- lib/spack/spack/test/packages.py | 6 +- lib/spack/spack/test/spec_dag.py | 3 +- lib/spack/spack/test/stage.py | 30 +++-- lib/spack/spack/tty.py | 107 ---------------- lib/spack/spack/url.py | 6 +- lib/spack/spack/util/compression.py | 11 ++ lib/spack/spack/util/executable.py | 2 +- lib/spack/spack/util/filesystem.py | 104 ---------------- lib/spack/spack/util/lang.py | 189 ---------------------------- lib/spack/spack/util/none_high.py | 70 ----------- lib/spack/spack/util/none_low.py | 70 ----------- lib/spack/spack/util/prefix.py | 40 +++--- lib/spack/spack/util/web.py | 3 +- lib/spack/spack/validate.py | 38 ------ lib/spack/spack/version.py | 4 +- 57 files changed, 1110 insertions(+), 1108 deletions(-) create mode 100644 lib/spack/llnl/__init__.py create mode 100644 lib/spack/llnl/util/__init__.py create mode 100644 lib/spack/llnl/util/compare/__init__.py create mode 100644 lib/spack/llnl/util/compare/none_high.py create mode 100644 lib/spack/llnl/util/compare/none_low.py create mode 100644 lib/spack/llnl/util/filesystem.py create mode 100644 lib/spack/llnl/util/lang.py create mode 100644 lib/spack/llnl/util/tty/__init__.py create mode 100644 lib/spack/llnl/util/tty/colify.py create mode 100644 lib/spack/llnl/util/tty/color.py delete mode 100644 lib/spack/spack/colify.py delete mode 100644 lib/spack/spack/color.py delete mode 100644 lib/spack/spack/tty.py delete mode 100644 lib/spack/spack/util/filesystem.py delete mode 100644 lib/spack/spack/util/lang.py delete mode 100644 lib/spack/spack/util/none_high.py delete mode 100644 lib/spack/spack/util/none_low.py delete mode 100644 lib/spack/spack/validate.py (limited to 'lib') diff --git a/lib/spack/env/cc b/lib/spack/env/cc index 0245e06531..06592febaa 100755 --- a/lib/spack/env/cc +++ b/lib/spack/env/cc @@ -18,7 +18,7 @@ if not spack_lib: # Grab a minimal set of spack packages sys.path.append(spack_lib) from spack.compilation import * -import spack.tty as tty +import llnl.util.tty as tty spack_prefix = get_env_var("SPACK_PREFIX") spack_build_root = get_env_var("SPACK_BUILD_ROOT") diff --git a/lib/spack/llnl/__init__.py b/lib/spack/llnl/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/spack/llnl/util/__init__.py b/lib/spack/llnl/util/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/spack/llnl/util/compare/__init__.py b/lib/spack/llnl/util/compare/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/spack/llnl/util/compare/none_high.py b/lib/spack/llnl/util/compare/none_high.py new file mode 100644 index 0000000000..78b41cbaf6 --- /dev/null +++ b/lib/spack/llnl/util/compare/none_high.py @@ -0,0 +1,70 @@ +############################################################################## +# 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 +############################################################################## +""" +Functions for comparing values that may potentially be None. +These none_high functions consider None as greater than all other values. +""" + +# Preserve builtin min and max functions +_builtin_min = min +_builtin_max = max + + +def lt(lhs, rhs): + """Less-than comparison. None is greater than any value.""" + return lhs != rhs and (rhs is None or (lhs is not None and lhs < rhs)) + + +def le(lhs, rhs): + """Less-than-or-equal comparison. None is greater than any value.""" + return lhs == rhs or lt(lhs, rhs) + + +def gt(lhs, rhs): + """Greater-than comparison. None is greater than any value.""" + return lhs != rhs and not lt(lhs, rhs) + + +def ge(lhs, rhs): + """Greater-than-or-equal comparison. None is greater than any value.""" + return lhs == rhs or gt(lhs, rhs) + + +def min(lhs, rhs): + """Minimum function where None is greater than any value.""" + if lhs is None: + return rhs + elif rhs is None: + return lhs + else: + return _builtin_min(lhs, rhs) + + +def max(lhs, rhs): + """Maximum function where None is greater than any value.""" + if lhs is None or rhs is None: + return None + else: + return _builtin_max(lhs, rhs) diff --git a/lib/spack/llnl/util/compare/none_low.py b/lib/spack/llnl/util/compare/none_low.py new file mode 100644 index 0000000000..307bcc8a26 --- /dev/null +++ b/lib/spack/llnl/util/compare/none_low.py @@ -0,0 +1,70 @@ +############################################################################## +# 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 +############################################################################## +""" +Functions for comparing values that may potentially be None. +These none_low functions consider None as less than all other values. +""" + +# Preserve builtin min and max functions +_builtin_min = min +_builtin_max = max + + +def lt(lhs, rhs): + """Less-than comparison. None is lower than any value.""" + return lhs != rhs and (lhs is None or (rhs is not None and lhs < rhs)) + + +def le(lhs, rhs): + """Less-than-or-equal comparison. None is less than any value.""" + return lhs == rhs or lt(lhs, rhs) + + +def gt(lhs, rhs): + """Greater-than comparison. None is less than any value.""" + return lhs != rhs and not lt(lhs, rhs) + + +def ge(lhs, rhs): + """Greater-than-or-equal comparison. None is less than any value.""" + return lhs == rhs or gt(lhs, rhs) + + +def min(lhs, rhs): + """Minimum function where None is less than any value.""" + if lhs is None or rhs is None: + return None + else: + return _builtin_min(lhs, rhs) + + +def max(lhs, rhs): + """Maximum function where None is less than any value.""" + if lhs is None: + return rhs + elif rhs is None: + return lhs + else: + return _builtin_max(lhs, rhs) diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py new file mode 100644 index 0000000000..2ec8c61cfa --- /dev/null +++ b/lib/spack/llnl/util/filesystem.py @@ -0,0 +1,90 @@ +############################################################################## +# 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 re +import shutil +import errno +import getpass +from contextlib import contextmanager, closing + +import llnl.util.tty as tty +from spack.util.compression import ALLOWED_ARCHIVE_TYPES + + +def install(src, dest): + """Manually install a file to a particular location.""" + tty.info("Installing %s to %s" % (src, dest)) + shutil.copy(src, dest) + + +def expand_user(path): + """Find instances of '%u' in a path and replace with the current user's + username.""" + username = getpass.getuser() + if not username and '%u' in path: + tty.die("Couldn't get username to complete path '%s'" % path) + + return path.replace('%u', username) + + +@contextmanager +def working_dir(dirname): + orig_dir = os.getcwd() + os.chdir(dirname) + yield + os.chdir(orig_dir) + + +def touch(path): + with closing(open(path, 'a')) as file: + os.utime(path, None) + + +def mkdirp(*paths): + for path in paths: + if not os.path.exists(path): + os.makedirs(path) + elif not os.path.isdir(path): + raise OSError(errno.EEXIST, "File alredy exists", path) + + +def join_path(prefix, *args): + path = str(prefix) + for elt in args: + path = os.path.join(path, str(elt)) + return path + + +def ancestor(dir, n=1): + """Get the nth ancestor of a directory.""" + parent = os.path.abspath(dir) + for i in range(n): + parent = os.path.dirname(parent) + return parent + + +def can_access(file_name): + """True if we have read/write access to the file.""" + return os.access(file_name, os.R_OK|os.W_OK) diff --git a/lib/spack/llnl/util/lang.py b/lib/spack/llnl/util/lang.py new file mode 100644 index 0000000000..034c3b374d --- /dev/null +++ b/lib/spack/llnl/util/lang.py @@ -0,0 +1,187 @@ +############################################################################## +# 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 re +import sys +import functools +import inspect + +# Ignore emacs backups when listing modules +ignore_modules = [r'^\.#', '~$'] + +def caller_locals(): + """This will return the locals of the *parent* of the caller. + This allows a fucntion to insert variables into its caller's + scope. Yes, this is some black magic, and yes it's useful + for implementing things like depends_on and provides. + """ + stack = inspect.stack() + try: + return stack[2][0].f_locals + finally: + del stack + + +def get_calling_package_name(): + """Make sure that the caller is a class definition, and return + the module's name. This is useful for getting the name of + spack packages from inside a relation function. + """ + stack = inspect.stack() + try: + # get calling function name (the relation) + relation = stack[1][3] + + # Make sure locals contain __module__ + caller_locals = stack[2][0].f_locals + finally: + del stack + + if not '__module__' in caller_locals: + raise ScopeError(relation) + + module_name = caller_locals['__module__'] + base_name = module_name.split('.')[-1] + return base_name + + +def attr_required(obj, attr_name): + """Ensure that a class has a required attribute.""" + if not hasattr(obj, attr_name): + tty.die("No required attribute '%s' in class '%s'" + % (attr_name, obj.__class__.__name__)) + + +def attr_setdefault(obj, name, value): + """Like dict.setdefault, but for objects.""" + if not hasattr(obj, name): + setattr(obj, name, value) + return getattr(obj, name) + + +def has_method(cls, name): + for base in inspect.getmro(cls): + if base is object: + continue + if name in base.__dict__: + return True + return False + + +def memoized(obj): + """Decorator that caches the results of a function, storing them + in an attribute of that function.""" + cache = obj.cache = {} + @functools.wraps(obj) + def memoizer(*args, **kwargs): + if args not in cache: + cache[args] = obj(*args, **kwargs) + return cache[args] + return memoizer + + +def list_modules(directory, **kwargs): + """Lists all of the modules, excluding __init__.py, in a + particular directory. Listed packages have no particular + order.""" + list_directories = kwargs.setdefault('directories', True) + + for name in os.listdir(directory): + if name == '__init__.py': + continue + + path = os.path.join(directory, name) + if list_directories and os.path.isdir(path): + init_py = os.path.join(path, '__init__.py') + if os.path.isfile(init_py): + yield name + + elif name.endswith('.py'): + if not any(re.search(pattern, name) for pattern in ignore_modules): + yield re.sub('.py$', '', name) + + +def key_ordering(cls): + """Decorates a class with extra methods that implement rich comparison + operations and __hash__. The decorator assumes that the class + implements a function called _cmp_key(). The rich comparison operations + will compare objects using this key, and the __hash__ function will + return the hash of this key. + + If a class already has __eq__, __ne__, __lt__, __le__, __gt__, or __ge__ + defined, this decorator will overwrite them. If the class does not + have a _cmp_key method, then this will raise a TypeError. + """ + def setter(name, value): + value.__name__ = name + setattr(cls, name, value) + + if not has_method(cls, '_cmp_key'): + raise TypeError("'%s' doesn't define _cmp_key()." % cls.__name__) + + setter('__eq__', lambda s,o: o is not None and s._cmp_key() == o._cmp_key()) + setter('__lt__', lambda s,o: o is not None and s._cmp_key() < o._cmp_key()) + setter('__le__', lambda s,o: o is not None and s._cmp_key() <= o._cmp_key()) + + setter('__ne__', lambda s,o: o is None or s._cmp_key() != o._cmp_key()) + setter('__gt__', lambda s,o: o is None or s._cmp_key() > o._cmp_key()) + setter('__ge__', lambda s,o: o is None or s._cmp_key() >= o._cmp_key()) + + setter('__hash__', lambda self: hash(self._cmp_key())) + + return cls + + +@key_ordering +class HashableMap(dict): + """This is a hashable, comparable dictionary. Hash is performed on + a tuple of the values in the dictionary.""" + def _cmp_key(self): + return tuple(sorted(self.values())) + + + def copy(self): + """Type-agnostic clone method. Preserves subclass type.""" + # Construct a new dict of my type + T = type(self) + clone = T() + + # Copy everything from this dict into it. + for key in self: + clone[key] = self[key].copy() + return clone + + +def in_function(function_name): + """True if the caller was called from some function with + the supplied Name, False otherwise.""" + stack = inspect.stack() + try: + for elt in stack[2:]: + if elt[3] == function_name: + return True + return False + finally: + del stack diff --git a/lib/spack/llnl/util/tty/__init__.py b/lib/spack/llnl/util/tty/__init__.py new file mode 100644 index 0000000000..afc4b59a63 --- /dev/null +++ b/lib/spack/llnl/util/tty/__init__.py @@ -0,0 +1,95 @@ +############################################################################## +# 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 llnl.util.tty.color import * + +debug = False +verbose = False +indent = " " + +def msg(message, *args): + cprint("@*b{==>} %s" % cescape(message)) + for arg in args: + print indent + str(arg) + + +def info(message, *args, **kwargs): + format = kwargs.get('format', '*b') + cprint("@%s{==>} %s" % (format, cescape(str(message)))) + for arg in args: + print indent + str(arg) + + +def verbose(message, *args): + if verbose: + info(message, *args, format='c') + + +def debug(*args): + if debug: + info("Debug: " + message, *args, format='*g') + + +def error(message, *args): + info("Error: " + str(message), *args, format='*r') + + +def warn(message, *args): + info("Warning: " + str(message), *args, format='*Y') + + +def die(message, *args): + error(message, *args) + sys.exit(1) + + +def get_number(prompt, **kwargs): + default = kwargs.get('default', None) + abort = kwargs.get('abort', None) + + if default is not None and abort is not None: + prompt += ' (default is %s, %s to abort) ' % (default, abort) + elif default is not None: + prompt += ' (default is %s) ' % default + elif abort is not None: + prompt += ' (%s to abort) ' % abort + + number = None + while number is None: + ans = raw_input(prompt) + if ans == str(abort): + return None + + if ans: + try: + number = int(ans) + if number < 1: + msg("Please enter a valid number.") + number = None + except ValueError: + msg("Please enter a valid number.") + elif default is not None: + number = default + return number diff --git a/lib/spack/llnl/util/tty/colify.py b/lib/spack/llnl/util/tty/colify.py new file mode 100644 index 0000000000..1f66416e69 --- /dev/null +++ b/lib/spack/llnl/util/tty/colify.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 +############################################################################## +# colify +# By Todd Gamblin, tgamblin@llnl.gov +# +# Takes a list of items as input and finds a good columnization of them, +# similar to how gnu ls does. You can pipe output to this script and +# get a tight display for it. This supports both uniform-width and +# variable-width (tighter) columns. +# +# Run colify -h for more information. +# +import os +import sys +import fcntl +import termios +import struct + +def get_terminal_size(): + """Get the dimensions of the console.""" + def ioctl_GWINSZ(fd): + try: + cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + except: + return + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + + return int(cr[1]), int(cr[0]) + + +class ColumnConfig: + def __init__(self, cols): + self.cols = cols + self.line_length = 0 + self.valid = True + self.widths = [0] * cols + + def __repr__(self): + attrs = [(a,getattr(self, a)) for a in dir(self) if not a.startswith("__")] + return "" % ", ".join("%s: %r" % a for a in attrs) + + +def config_variable_cols(elts, console_cols, padding): + # Get a bound on the most columns we could possibly have. + lengths = [len(elt) for elt in elts] + max_cols = max(1, console_cols / (min(lengths) + padding)) + max_cols = min(len(elts), max_cols) + + configs = [ColumnConfig(c) for c in xrange(1, max_cols+1)] + for elt, length in enumerate(lengths): + for i, conf in enumerate(configs): + if conf.valid: + col = elt / ((len(elts) + i) / (i + 1)) + padded = length + if col < i: + padded += padding + + if conf.widths[col] < padded: + conf.line_length += padded - conf.widths[col] + conf.widths[col] = padded + conf.valid = (conf.line_length < console_cols) + + try: + config = next(conf for conf in reversed(configs) if conf.valid) + except StopIteration: + # If nothing was valid the screen was too narrow -- just use 1 col. + config = configs[0] + + config.widths = [w for w in config.widths if w != 0] + config.cols = len(config.widths) + return config + + +def config_uniform_cols(elts, console_cols, padding): + max_len = max(len(elt) for elt in elts) + padding + cols = max(1, console_cols / max_len) + cols = min(len(elts), cols) + config = ColumnConfig(cols) + config.widths = [max_len] * cols + return config + + +def isatty(ostream): + force = os.environ.get('COLIFY_TTY', 'false').lower() != 'false' + return force or ostream.isatty() + + +def colify(elts, **options): + # Get keyword arguments or set defaults + output = options.get("output", sys.stdout) + indent = options.get("indent", 0) + padding = options.get("padding", 2) + + # elts needs to be an array of strings so we can count the elements + elts = [str(elt) for elt in elts] + if not elts: + return + + if not isatty(output): + for elt in elts: + output.write("%s\n" % elt) + return + + console_cols = options.get("cols", None) + if not console_cols: + console_cols, console_rows = get_terminal_size() + elif type(console_cols) != int: + raise ValueError("Number of columns must be an int") + console_cols = max(1, console_cols - indent) + + method = options.get("method", "variable") + if method == "variable": + config = config_variable_cols(elts, console_cols, padding) + elif method == "uniform": + config = config_uniform_cols(elts, console_cols, padding) + else: + raise ValueError("method must be one of: " + allowed_methods) + + cols = config.cols + formats = ["%%-%ds" % width for width in config.widths[:-1]] + formats.append("%s") # last column has no trailing space + + rows = (len(elts) + cols - 1) / cols + rows_last_col = len(elts) % rows + + for row in xrange(rows): + output.write(" " * indent) + for col in xrange(cols): + elt = col * rows + row + output.write(formats[col] % elts[elt]) + + output.write("\n") + row += 1 + if row == rows_last_col: + cols -= 1 + + +if __name__ == "__main__": + import optparse + + cols, rows = get_terminal_size() + parser = optparse.OptionParser() + parser.add_option("-u", "--uniform", action="store_true", default=False, + help="Use uniformly sized columns instead of variable-size.") + parser.add_option("-p", "--padding", metavar="PADDING", action="store", + type=int, default=2, help="Spaces to add between columns. Default is 2.") + parser.add_option("-i", "--indent", metavar="SPACES", action="store", + type=int, default=0, help="Indent the output by SPACES. Default is 0.") + parser.add_option("-w", "--width", metavar="COLS", action="store", + type=int, default=cols, help="Indent the output by SPACES. Default is 0.") + options, args = parser.parse_args() + + method = "variable" + if options.uniform: + method = "uniform" + + if sys.stdin.isatty(): + parser.print_help() + sys.exit(1) + else: + colify([line.strip() for line in sys.stdin], method=method, **options.__dict__) diff --git a/lib/spack/llnl/util/tty/color.py b/lib/spack/llnl/util/tty/color.py new file mode 100644 index 0000000000..14974a1014 --- /dev/null +++ b/lib/spack/llnl/util/tty/color.py @@ -0,0 +1,190 @@ +############################################################################## +# 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 +############################################################################## +""" +This file implements an expression syntax, similar to printf, for adding +ANSI colors to text. + +See colorize(), cwrite(), and cprint() for routines that can generate +colored output. + +colorize will take a string and replace all color expressions with +ANSI control codes. If the isatty keyword arg is set to False, then +the color expressions will be converted to null strings, and the +returned string will have no color. + +cwrite and cprint are equivalent to write() and print() calls in +python, but they colorize their output. If the stream argument is +not supplied, they write to sys.stdout. + +Here are some example color expressions: + + @r Turn on red coloring + @R Turn on bright red coloring + @*{foo} Bold foo, but don't change text color + @_{bar} Underline bar, but don't change text color + @*b Turn on bold, blue text + @_B Turn on bright blue text with an underline + @. Revert to plain formatting + @*g{green} Print out 'green' in bold, green text, then reset to plain. + @*ggreen@. Print out 'green' in bold, green text, then reset to plain. + +The syntax consists of: + + color-expr = '@' [style] color-code '{' text '}' | '@.' | '@@' + style = '*' | '_' + color-code = [krgybmcwKRGYBMCW] + text = .* + +'@' indicates the start of a color expression. It can be followed +by an optional * or _ that indicates whether the font should be bold or +underlined. If * or _ is not provided, the text will be plain. Then +an optional color code is supplied. This can be [krgybmcw] or [KRGYBMCW], +where the letters map to black(k), red(r), green(g), yellow(y), blue(b), +magenta(m), cyan(c), and white(w). Lowercase letters denote normal ANSI +colors and capital letters denote bright ANSI colors. + +Finally, the color expression can be followed by text enclosed in {}. If +braces are present, only the text in braces is colored. If the braces are +NOT present, then just the control codes to enable the color will be output. +The console can be reset later to plain text with '@.'. + +To output an @, use '@@'. To output a } inside braces, use '}}'. +""" +import re +import sys + +class ColorParseError(Exception): + """Raised when a color format fails to parse.""" + def __init__(self, message): + super(ColorParseError, self).__init__(message) + +# Text styles for ansi codes +styles = {'*' : '1', # bold + '_' : '4', # underline + None : '0' } # plain + +# Dim and bright ansi colors +colors = {'k' : 30, 'K' : 90, # black + 'r' : 31, 'R' : 91, # red + 'g' : 32, 'G' : 92, # green + 'y' : 33, 'Y' : 93, # yellow + 'b' : 34, 'B' : 94, # blue + 'm' : 35, 'M' : 95, # magenta + 'c' : 36, 'C' : 96, # cyan + 'w' : 37, 'W' : 97 } # white + +# Regex to be used for color formatting +color_re = r'@(?:@|\.|([*_])?([a-zA-Z])?(?:{((?:[^}]|}})*)})?)' + + +class match_to_ansi(object): + def __init__(self, color=True): + self.color = color + + def escape(self, s): + """Returns a TTY escape sequence for a color""" + if self.color: + return "\033[%sm" % s + else: + return '' + + def __call__(self, match): + """Convert a match object generated by color_re into an ansi color code + This can be used as a handler in re.sub. + """ + style, color, text = match.groups() + m = match.group(0) + + if m == '@@': + return '@' + elif m == '@.': + return self.escape(0) + elif m == '@': + raise ColorParseError("Incomplete color format: '%s' in %s" + % (m, match.string)) + + string = styles[style] + if color: + if color not in colors: + raise ColorParseError("invalid color specifier: '%s' in '%s'" + % (color, match.string)) + string += ';' + str(colors[color]) + + colored_text = '' + if text: + colored_text = text + self.escape(0) + + return self.escape(string) + colored_text + + +def colorize(string, **kwargs): + """Take a string and replace all color expressions with ANSI control + codes. Return the resulting string. + If color=False is supplied, output will be plain text without + control codes, for output to non-console devices. + """ + color = kwargs.get('color', True) + return re.sub(color_re, match_to_ansi(color), string) + + +def cwrite(string, stream=sys.stdout, color=None): + """Replace all color expressions in string with ANSI control + codes and write the result to the stream. If color is + False, this will write plain text with o color. If True, + then it will always write colored output. If not supplied, + then it will be set based on stream.isatty(). + """ + if color is None: + color = stream.isatty() + stream.write(colorize(string, color=color)) + + +def cprint(string, stream=sys.stdout, color=None): + """Same as cwrite, but writes a trailing newline to the stream.""" + cwrite(string + "\n", stream, color) + +def cescape(string): + """Replace all @ with @@ in the string provided.""" + return str(string).replace('@', '@@') + + +class ColorStream(object): + def __init__(self, stream, color=None): + self.__class__ = type(stream.__class__.__name__, + (self.__class__, stream.__class__), {}) + self.__dict__ = stream.__dict__ + self.color = color + self.stream = stream + + def write(self, string, **kwargs): + if kwargs.get('raw', False): + super(ColorStream, self).write(string) + else: + cwrite(string, self.stream, self.color) + + def writelines(self, sequence, **kwargs): + raw = kwargs.get('raw', False) + for string in sequence: + self.write(string, self.color, raw=raw) diff --git a/lib/spack/spack/architecture.py b/lib/spack/spack/architecture.py index 92e4b2742d..7fbf1dadcf 100644 --- a/lib/spack/spack/architecture.py +++ b/lib/spack/spack/architecture.py @@ -25,10 +25,11 @@ import os import platform as py_platform +from llnl.util.lang import memoized + import spack import spack.error as serr from spack.version import Version -from spack.util.lang import memoized class InvalidSysTypeError(serr.SpackError): diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py index bfd69bbe56..537db536dd 100644 --- a/lib/spack/spack/cmd/__init__.py +++ b/lib/spack/spack/cmd/__init__.py @@ -26,10 +26,11 @@ import os import re import sys +import llnl.util.tty as tty +from llnl.util.lang import attr_setdefault + import spack import spack.spec -import spack.tty as tty -from spack.util.lang import attr_setdefault # cmd has a submodule called "list" so preserve the python list module python_list = list diff --git a/lib/spack/spack/cmd/bootstrap.py b/lib/spack/spack/cmd/bootstrap.py index 381f4c717c..11fc67e519 100644 --- a/lib/spack/spack/cmd/bootstrap.py +++ b/lib/spack/spack/cmd/bootstrap.py @@ -24,9 +24,11 @@ ############################################################################## import os from subprocess import check_call, check_output + +import llnl.util.tty as tty + import spack -from spack import new_path -import spack.tty as tty +from spack import join_path description = "Create a new installation of spack in another prefix" @@ -35,7 +37,7 @@ def setup_parser(subparser): def get_origin_url(): - git_dir = new_path(spack.prefix, '.git') + git_dir = join_path(spack.prefix, '.git') origin_url = check_output( ['git', '--git-dir=%s' % git_dir, 'config', '--get', 'remote.origin.url']) return origin_url.strip() @@ -47,7 +49,7 @@ def bootstrap(parser, args): tty.msg("Fetching spack from origin: %s" % origin_url) - if os.path.exists(new_path(prefix, '.git')): + if os.path.exists(join_path(prefix, '.git')): tty.die("There already seems to be a git repository in %s" % prefix) files_in_the_way = os.listdir(prefix) diff --git a/lib/spack/spack/cmd/checksum.py b/lib/spack/spack/cmd/checksum.py index abd161182c..4867d1b520 100644 --- a/lib/spack/spack/cmd/checksum.py +++ b/lib/spack/spack/cmd/checksum.py @@ -29,13 +29,14 @@ import hashlib from pprint import pprint from subprocess import CalledProcessError +import llnl.util.tty as tty +from llnl.util.tty.colify import colify + import spack import spack.cmd -import spack.tty as tty import spack.packages as packages import spack.util.crypto from spack.stage import Stage, FailedDownloadError -from spack.colify import colify from spack.version import * description ="Checksum available versions of a package to update a package file." diff --git a/lib/spack/spack/cmd/clean.py b/lib/spack/spack/cmd/clean.py index b86c3e502f..d54b529d3c 100644 --- a/lib/spack/spack/cmd/clean.py +++ b/lib/spack/spack/cmd/clean.py @@ -24,9 +24,10 @@ ############################################################################## import argparse +import llnl.util.tty as tty + import spack.cmd import spack.packages as packages -import spack.tty as tty import spack.stage as stage description = "Remove staged files for packages" diff --git a/lib/spack/spack/cmd/compilers.py b/lib/spack/spack/cmd/compilers.py index 8b1fb4d03a..c3f204e3d3 100644 --- a/lib/spack/spack/cmd/compilers.py +++ b/lib/spack/spack/cmd/compilers.py @@ -22,9 +22,10 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## +import llnl.util.tty as tty +from llnl.util.tty.colify import colify + import spack.compilers -import spack.tty as tty -from spack.colify import colify description = "List available compilers" diff --git a/lib/spack/spack/cmd/create.py b/lib/spack/spack/cmd/create.py index 6d0b3f095e..236485f9a1 100644 --- a/lib/spack/spack/cmd/create.py +++ b/lib/spack/spack/cmd/create.py @@ -28,11 +28,12 @@ import hashlib import re from contextlib import closing +import llnl.util.tty as tty + import spack import spack.cmd import spack.package import spack.packages as packages -import spack.tty as tty import spack.url import spack.util.crypto as crypto import spack.cmd.checksum diff --git a/lib/spack/spack/cmd/edit.py b/lib/spack/spack/cmd/edit.py index b04899ad84..b6373872c0 100644 --- a/lib/spack/spack/cmd/edit.py +++ b/lib/spack/spack/cmd/edit.py @@ -26,9 +26,10 @@ import os import string from contextlib import closing +import llnl.util.tty as tty + import spack import spack.packages as packages -import spack.tty as tty description = "Open package files in $EDITOR" diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py index 95abbb72bd..3db0608d6b 100644 --- a/lib/spack/spack/cmd/find.py +++ b/lib/spack/spack/cmd/find.py @@ -26,12 +26,13 @@ import collections import argparse from StringIO import StringIO +from llnl.util.tty.colify import colify +from llnl.util.tty.color import * + import spack import spack.spec import spack.packages as packages -import spack.colify -from spack.color import * -from spack.colify import colify + description ="Find installed spack packages" diff --git a/lib/spack/spack/cmd/info.py b/lib/spack/spack/cmd/info.py index b4d9a8339d..3a17b2ce3c 100644 --- a/lib/spack/spack/cmd/info.py +++ b/lib/spack/spack/cmd/info.py @@ -24,10 +24,9 @@ ############################################################################## import re import textwrap - +from llnl.util.tty.colify import colify import spack import spack.packages as packages -from spack.colify import colify description = "Get detailed information on a particular package" diff --git a/lib/spack/spack/cmd/list.py b/lib/spack/spack/cmd/list.py index 2cfdd9863d..15e1581868 100644 --- a/lib/spack/spack/cmd/list.py +++ b/lib/spack/spack/cmd/list.py @@ -23,7 +23,7 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## import spack.packages as packages -from spack.colify import colify +from llnl.util.tty.colify import colify description ="List available spack packages" diff --git a/lib/spack/spack/cmd/mirror.py b/lib/spack/spack/cmd/mirror.py index ec91ef0fd5..098b1c3bf8 100644 --- a/lib/spack/spack/cmd/mirror.py +++ b/lib/spack/spack/cmd/mirror.py @@ -26,12 +26,14 @@ import os import shutil import argparse +import llnl.util.tty as tty +from llnl.util.filesystem import mkdirp, join_path + import spack.packages as packages import spack.cmd -import spack.tty as tty from spack.stage import Stage -from spack.util.filesystem import mkdirp, new_path + description = "Create a directory full of package tarballs that can be used as a spack mirror." @@ -66,7 +68,7 @@ def mirror(parser, args): continue # create a subdir for the current package. - pkg_path = new_path(args.directory, pkg_name) + pkg_path = join_path(args.directory, pkg_name) mkdirp(pkg_path) # Download all the tarballs using Stages, then move them into place @@ -76,7 +78,7 @@ def mirror(parser, args): try: stage.fetch() basename = os.path.basename(stage.archive_file) - final_dst = new_path(pkg_path, basename) + final_dst = join_path(pkg_path, basename) os.chdir(working_dir) shutil.move(stage.archive_file, final_dst) diff --git a/lib/spack/spack/cmd/providers.py b/lib/spack/spack/cmd/providers.py index dc40b6b9ff..b59950aeb3 100644 --- a/lib/spack/spack/cmd/providers.py +++ b/lib/spack/spack/cmd/providers.py @@ -25,9 +25,10 @@ import os import argparse +from llnl.util.tty.colify import colify + import spack.cmd import spack.packages -from spack.colify import colify description ="List packages that provide a particular virtual package" diff --git a/lib/spack/spack/cmd/spec.py b/lib/spack/spack/cmd/spec.py index fcff743026..fb5a900c3f 100644 --- a/lib/spack/spack/cmd/spec.py +++ b/lib/spack/spack/cmd/spec.py @@ -25,7 +25,8 @@ import argparse import spack.cmd -import spack.tty as tty +import llnl.util.tty as tty + import spack.url as url import spack diff --git a/lib/spack/spack/cmd/test.py b/lib/spack/spack/cmd/test.py index 41cba4b66c..16d693aa21 100644 --- a/lib/spack/spack/cmd/test.py +++ b/lib/spack/spack/cmd/test.py @@ -22,12 +22,14 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## +from pprint import pprint + +from llnl.util.tty.colify import colify +from llnl.util.lang import list_modules + import spack import spack.packages as packages import spack.test -from spack.util.lang import list_modules -from spack.colify import colify -from pprint import pprint description ="Run unit tests" diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py index c30746b0d6..e47b4d7f7a 100644 --- a/lib/spack/spack/cmd/uninstall.py +++ b/lib/spack/spack/cmd/uninstall.py @@ -24,8 +24,9 @@ ############################################################################## import argparse +import llnl.util.tty as tty + import spack.cmd -import spack.tty as tty import spack.packages as packages description="Remove an installed package" diff --git a/lib/spack/spack/cmd/versions.py b/lib/spack/spack/cmd/versions.py index 9ecb9875c2..1f0da9fbd8 100644 --- a/lib/spack/spack/cmd/versions.py +++ b/lib/spack/spack/cmd/versions.py @@ -23,8 +23,8 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## import os +from llnl.util.tty.colify import colify import spack.packages as packages -from spack.colify import colify description ="List available versions of a package" diff --git a/lib/spack/spack/colify.py b/lib/spack/spack/colify.py deleted file mode 100644 index 1f66416e69..0000000000 --- a/lib/spack/spack/colify.py +++ /dev/null @@ -1,193 +0,0 @@ -############################################################################## -# 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 -############################################################################## -# colify -# By Todd Gamblin, tgamblin@llnl.gov -# -# Takes a list of items as input and finds a good columnization of them, -# similar to how gnu ls does. You can pipe output to this script and -# get a tight display for it. This supports both uniform-width and -# variable-width (tighter) columns. -# -# Run colify -h for more information. -# -import os -import sys -import fcntl -import termios -import struct - -def get_terminal_size(): - """Get the dimensions of the console.""" - def ioctl_GWINSZ(fd): - try: - cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) - except: - return - return cr - cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) - if not cr: - try: - fd = os.open(os.ctermid(), os.O_RDONLY) - cr = ioctl_GWINSZ(fd) - os.close(fd) - except: - pass - if not cr: - cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) - - return int(cr[1]), int(cr[0]) - - -class ColumnConfig: - def __init__(self, cols): - self.cols = cols - self.line_length = 0 - self.valid = True - self.widths = [0] * cols - - def __repr__(self): - attrs = [(a,getattr(self, a)) for a in dir(self) if not a.startswith("__")] - return "" % ", ".join("%s: %r" % a for a in attrs) - - -def config_variable_cols(elts, console_cols, padding): - # Get a bound on the most columns we could possibly have. - lengths = [len(elt) for elt in elts] - max_cols = max(1, console_cols / (min(lengths) + padding)) - max_cols = min(len(elts), max_cols) - - configs = [ColumnConfig(c) for c in xrange(1, max_cols+1)] - for elt, length in enumerate(lengths): - for i, conf in enumerate(configs): - if conf.valid: - col = elt / ((len(elts) + i) / (i + 1)) - padded = length - if col < i: - padded += padding - - if conf.widths[col] < padded: - conf.line_length += padded - conf.widths[col] - conf.widths[col] = padded - conf.valid = (conf.line_length < console_cols) - - try: - config = next(conf for conf in reversed(configs) if conf.valid) - except StopIteration: - # If nothing was valid the screen was too narrow -- just use 1 col. - config = configs[0] - - config.widths = [w for w in config.widths if w != 0] - config.cols = len(config.widths) - return config - - -def config_uniform_cols(elts, console_cols, padding): - max_len = max(len(elt) for elt in elts) + padding - cols = max(1, console_cols / max_len) - cols = min(len(elts), cols) - config = ColumnConfig(cols) - config.widths = [max_len] * cols - return config - - -def isatty(ostream): - force = os.environ.get('COLIFY_TTY', 'false').lower() != 'false' - return force or ostream.isatty() - - -def colify(elts, **options): - # Get keyword arguments or set defaults - output = options.get("output", sys.stdout) - indent = options.get("indent", 0) - padding = options.get("padding", 2) - - # elts needs to be an array of strings so we can count the elements - elts = [str(elt) for elt in elts] - if not elts: - return - - if not isatty(output): - for elt in elts: - output.write("%s\n" % elt) - return - - console_cols = options.get("cols", None) - if not console_cols: - console_cols, console_rows = get_terminal_size() - elif type(console_cols) != int: - raise ValueError("Number of columns must be an int") - console_cols = max(1, console_cols - indent) - - method = options.get("method", "variable") - if method == "variable": - config = config_variable_cols(elts, console_cols, padding) - elif method == "uniform": - config = config_uniform_cols(elts, console_cols, padding) - else: - raise ValueError("method must be one of: " + allowed_methods) - - cols = config.cols - formats = ["%%-%ds" % width for width in config.widths[:-1]] - formats.append("%s") # last column has no trailing space - - rows = (len(elts) + cols - 1) / cols - rows_last_col = len(elts) % rows - - for row in xrange(rows): - output.write(" " * indent) - for col in xrange(cols): - elt = col * rows + row - output.write(formats[col] % elts[elt]) - - output.write("\n") - row += 1 - if row == rows_last_col: - cols -= 1 - - -if __name__ == "__main__": - import optparse - - cols, rows = get_terminal_size() - parser = optparse.OptionParser() - parser.add_option("-u", "--uniform", action="store_true", default=False, - help="Use uniformly sized columns instead of variable-size.") - parser.add_option("-p", "--padding", metavar="PADDING", action="store", - type=int, default=2, help="Spaces to add between columns. Default is 2.") - parser.add_option("-i", "--indent", metavar="SPACES", action="store", - type=int, default=0, help="Indent the output by SPACES. Default is 0.") - parser.add_option("-w", "--width", metavar="COLS", action="store", - type=int, default=cols, help="Indent the output by SPACES. Default is 0.") - options, args = parser.parse_args() - - method = "variable" - if options.uniform: - method = "uniform" - - if sys.stdin.isatty(): - parser.print_help() - sys.exit(1) - else: - colify([line.strip() for line in sys.stdin], method=method, **options.__dict__) diff --git a/lib/spack/spack/color.py b/lib/spack/spack/color.py deleted file mode 100644 index 9d75824aa8..0000000000 --- a/lib/spack/spack/color.py +++ /dev/null @@ -1,191 +0,0 @@ -############################################################################## -# 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 -############################################################################## -""" -This file implements an expression syntax, similar to printf, for adding -ANSI colors to text. - -See colorize(), cwrite(), and cprint() for routines that can generate -colored output. - -colorize will take a string and replace all color expressions with -ANSI control codes. If the isatty keyword arg is set to False, then -the color expressions will be converted to null strings, and the -returned string will have no color. - -cwrite and cprint are equivalent to write() and print() calls in -python, but they colorize their output. If the stream argument is -not supplied, they write to sys.stdout. - -Here are some example color expressions: - - @r Turn on red coloring - @R Turn on bright red coloring - @*{foo} Bold foo, but don't change text color - @_{bar} Underline bar, but don't change text color - @*b Turn on bold, blue text - @_B Turn on bright blue text with an underline - @. Revert to plain formatting - @*g{green} Print out 'green' in bold, green text, then reset to plain. - @*ggreen@. Print out 'green' in bold, green text, then reset to plain. - -The syntax consists of: - - color-expr = '@' [style] color-code '{' text '}' | '@.' | '@@' - style = '*' | '_' - color-code = [krgybmcwKRGYBMCW] - text = .* - -'@' indicates the start of a color expression. It can be followed -by an optional * or _ that indicates whether the font should be bold or -underlined. If * or _ is not provided, the text will be plain. Then -an optional color code is supplied. This can be [krgybmcw] or [KRGYBMCW], -where the letters map to black(k), red(r), green(g), yellow(y), blue(b), -magenta(m), cyan(c), and white(w). Lowercase letters denote normal ANSI -colors and capital letters denote bright ANSI colors. - -Finally, the color expression can be followed by text enclosed in {}. If -braces are present, only the text in braces is colored. If the braces are -NOT present, then just the control codes to enable the color will be output. -The console can be reset later to plain text with '@.'. - -To output an @, use '@@'. To output a } inside braces, use '}}'. -""" -import re -import sys -import spack.error - -class ColorParseError(spack.error.SpackError): - """Raised when a color format fails to parse.""" - def __init__(self, message): - super(ColorParseError, self).__init__(message) - -# Text styles for ansi codes -styles = {'*' : '1', # bold - '_' : '4', # underline - None : '0' } # plain - -# Dim and bright ansi colors -colors = {'k' : 30, 'K' : 90, # black - 'r' : 31, 'R' : 91, # red - 'g' : 32, 'G' : 92, # green - 'y' : 33, 'Y' : 93, # yellow - 'b' : 34, 'B' : 94, # blue - 'm' : 35, 'M' : 95, # magenta - 'c' : 36, 'C' : 96, # cyan - 'w' : 37, 'W' : 97 } # white - -# Regex to be used for color formatting -color_re = r'@(?:@|\.|([*_])?([a-zA-Z])?(?:{((?:[^}]|}})*)})?)' - - -class match_to_ansi(object): - def __init__(self, color=True): - self.color = color - - def escape(self, s): - """Returns a TTY escape sequence for a color""" - if self.color: - return "\033[%sm" % s - else: - return '' - - def __call__(self, match): - """Convert a match object generated by color_re into an ansi color code - This can be used as a handler in re.sub. - """ - style, color, text = match.groups() - m = match.group(0) - - if m == '@@': - return '@' - elif m == '@.': - return self.escape(0) - elif m == '@': - raise ColorParseError("Incomplete color format: '%s' in %s" - % (m, match.string)) - - string = styles[style] - if color: - if color not in colors: - raise ColorParseError("invalid color specifier: '%s' in '%s'" - % (color, match.string)) - string += ';' + str(colors[color]) - - colored_text = '' - if text: - colored_text = text + self.escape(0) - - return self.escape(string) + colored_text - - -def colorize(string, **kwargs): - """Take a string and replace all color expressions with ANSI control - codes. Return the resulting string. - If color=False is supplied, output will be plain text without - control codes, for output to non-console devices. - """ - color = kwargs.get('color', True) - return re.sub(color_re, match_to_ansi(color), string) - - -def cwrite(string, stream=sys.stdout, color=None): - """Replace all color expressions in string with ANSI control - codes and write the result to the stream. If color is - False, this will write plain text with o color. If True, - then it will always write colored output. If not supplied, - then it will be set based on stream.isatty(). - """ - if color is None: - color = stream.isatty() - stream.write(colorize(string, color=color)) - - -def cprint(string, stream=sys.stdout, color=None): - """Same as cwrite, but writes a trailing newline to the stream.""" - cwrite(string + "\n", stream, color) - -def cescape(string): - """Replace all @ with @@ in the string provided.""" - return str(string).replace('@', '@@') - - -class ColorStream(object): - def __init__(self, stream, color=None): - self.__class__ = type(stream.__class__.__name__, - (self.__class__, stream.__class__), {}) - self.__dict__ = stream.__dict__ - self.color = color - self.stream = stream - - def write(self, string, **kwargs): - if kwargs.get('raw', False): - super(ColorStream, self).write(string) - else: - cwrite(string, self.stream, self.color) - - def writelines(self, sequence, **kwargs): - raw = kwargs.get('raw', False) - for string in sequence: - self.write(string, self.color, raw=raw) diff --git a/lib/spack/spack/compilers/__init__.py b/lib/spack/spack/compilers/__init__.py index 369171a59b..5448e709c4 100644 --- a/lib/spack/spack/compilers/__init__.py +++ b/lib/spack/spack/compilers/__init__.py @@ -25,10 +25,9 @@ # # This needs to be expanded for full compiler support. # - +from llnl.util.lang import memoized, list_modules import spack import spack.compilers.gcc -from spack.util.lang import memoized, list_modules @memoized def supported_compilers(): diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py index bc4ecf802a..2e4b6f89c8 100644 --- a/lib/spack/spack/directory_layout.py +++ b/lib/spack/spack/directory_layout.py @@ -24,12 +24,13 @@ ############################################################################## import re import os -import os.path import exceptions import hashlib +import shutil +from contextlib import closing +from llnl.util.filesystem import join_path, mkdirp from spack.spec import Spec -from spack.util.filesystem import * from spack.error import SpackError @@ -138,7 +139,7 @@ class SpecHashDirectoryLayout(DirectoryLayout): def relative_path_for_spec(self, spec): _check_concrete(spec) - path = new_path( + path = join_path( spec.architecture, spec.compiler, "%s@%s%s" % (spec.name, spec.version, spec.variants)) @@ -168,7 +169,7 @@ class SpecHashDirectoryLayout(DirectoryLayout): _check_concrete(spec) path = self.path_for_spec(spec) - spec_file_path = new_path(path, self.spec_file) + spec_file_path = join_path(path, self.spec_file) if os.path.isdir(path): if not os.path.isfile(spec_file_path): @@ -199,7 +200,7 @@ class SpecHashDirectoryLayout(DirectoryLayout): for path in traverse_dirs_at_depth(self.root, 3): arch, compiler, last_dir = path - spec_file_path = new_path( + spec_file_path = join_path( self.root, arch, compiler, last_dir, self.spec_file) if os.path.exists(spec_file_path): spec = self.read_spec(spec_file_path) diff --git a/lib/spack/spack/globals.py b/lib/spack/spack/globals.py index 40bb50f219..990a2ff417 100644 --- a/lib/spack/spack/globals.py +++ b/lib/spack/spack/globals.py @@ -24,8 +24,9 @@ ############################################################################## import os +from llnl.util.filesystem import * + from spack.version import Version -from spack.util.filesystem import * from spack.util.executable import * from spack.directory_layout import SpecHashDirectoryLayout from spack.concretize import DefaultConcretizer @@ -34,20 +35,20 @@ from spack.concretize import DefaultConcretizer prefix = ancestor(__file__, 4) # The spack script itself -spack_file = new_path(prefix, "bin", "spack") +spack_file = join_path(prefix, "bin", "spack") # spack directory hierarchy -lib_path = new_path(prefix, "lib", "spack") -env_path = new_path(lib_path, "env") -module_path = new_path(lib_path, "spack") -packages_path = new_path(module_path, "packages") -compilers_path = new_path(module_path, "compilers") -test_path = new_path(module_path, "test") +lib_path = join_path(prefix, "lib", "spack") +env_path = join_path(lib_path, "env") +module_path = join_path(lib_path, "spack") +packages_path = join_path(module_path, "packages") +compilers_path = join_path(module_path, "compilers") +test_path = join_path(module_path, "test") -var_path = new_path(prefix, "var", "spack") -stage_path = new_path(var_path, "stage") +var_path = join_path(prefix, "var", "spack") +stage_path = join_path(var_path, "stage") -install_path = new_path(prefix, "opt") +install_path = join_path(prefix, "opt") # # This controls how spack lays out install prefixes and @@ -116,7 +117,7 @@ sys_type = None # For no mirrors: # mirrors = [] # -mirrors = [] +mirrors = ['file:///Users/gamblin2/mirror'] # Important environment variables SPACK_NO_PARALLEL_MAKE = 'SPACK_NO_PARALLEL_MAKE' diff --git a/lib/spack/spack/multimethod.py b/lib/spack/spack/multimethod.py index 710c790b63..8d91e4f86d 100644 --- a/lib/spack/spack/multimethod.py +++ b/lib/spack/spack/multimethod.py @@ -47,9 +47,10 @@ import sys import functools import collections +from llnl.util.lang import * + import spack.architecture import spack.error -from spack.util.lang import * from spack.spec import parse_anonymous_spec, Spec diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index 24c3ffd35b..a88fbc4863 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -39,23 +39,28 @@ import re import subprocess import platform as py_platform import shutil +import multiprocessing +from urlparse import urlparse + +import llnl.util.tty as tty +from llnl.util.tty.color import cwrite +from llnl.util.filesystem import touch +from llnl.util.lang import * from spack import * import spack.spec import spack.error -import packages -import tty -import validate -import multiprocessing -import url - +import spack.packages as packages +import spack.url as url import spack.util.crypto as crypto from spack.version import * from spack.stage import Stage -from spack.util.lang import * from spack.util.web import get_pages from spack.util.environment import * -from spack.util.filesystem import touch +from spack.util.compression import allowed_archive + +"""Allowed URL schemes for spack packages.""" +_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file"] class Package(object): @@ -337,7 +342,7 @@ class Package(object): self.name = self.name[self.name.rindex('.') + 1:] # Make sure URL is an allowed type - validate.url(self.url) + validate_package_url(self.url) # patch up the URL with a new version if the spec version is concrete if self.spec.versions.concrete: @@ -620,8 +625,8 @@ class Package(object): # Construct paths to special files in the archive dir used to # keep track of whether patches were successfully applied. archive_dir = self.stage.expanded_archive_path - good_file = new_path(archive_dir, '.spack_patched') - bad_file = new_path(archive_dir, '.spack_patch_failed') + good_file = join_path(archive_dir, '.spack_patched') + bad_file = join_path(archive_dir, '.spack_patch_failed') # If we encounter an archive that failed to patch, restage it # so that we can apply all the patches again. @@ -664,7 +669,7 @@ class Package(object): if os.path.exists(self.prefix): tty.msg("%s is already installed." % self.name) - tty.pkg(self.prefix) + print_pkg(self.prefix) return if not self.ignore_dependencies: @@ -678,27 +683,28 @@ class Package(object): self.add_commands_to_module() tty.msg("Building %s." % self.name) - try: - # create the install directory (allow the layout to handle this in - # case it needs to add extra files) - spack.install_layout.make_path_for_spec(self.spec) + # create the install directory (allow the layout to handle this in + # case it needs to add extra files) + spack.install_layout.make_path_for_spec(self.spec) + + try: self.install(self.spec, self.prefix) if not os.path.isdir(self.prefix): tty.die("Install failed for %s. No install dir created." % self.name) + tty.msg("Successfully installed %s" % self.name) + print_pkg(self.prefix) + except Exception, e: - if not self.dirty: - self.remove_prefix() + self.remove_prefix() raise - tty.msg("Successfully installed %s" % self.name) - tty.pkg(self.prefix) - - # Once the install is done, destroy the stage where we built it, - # unless the user wants it kept around. - if not self.dirty: - self.stage.destroy() + finally: + # Once the install is done, destroy the stage where we built it, + # unless the user wants it kept around. + if not self.dirty: + self.stage.destroy() def setup_install_environment(self): @@ -713,7 +719,7 @@ class Package(object): # in directories called "case*" within the env directory. env_paths = [env_path] for file in os.listdir(env_path): - path = new_path(env_path, file) + path = join_path(env_path, file) if file.startswith("case") and os.path.isdir(path): env_paths.append(path) path_put_first("PATH", env_paths) @@ -883,6 +889,26 @@ class MakeExecutable(Executable): super(MakeExecutable, self).__call__(*args, **kwargs) +def validate_package_url(url_string): + """Determine whether spack can handle a particular URL or not.""" + url = urlparse(url_string) + if url.scheme not in _ALLOWED_URL_SCHEMES: + tty.die("Invalid protocol in URL: '%s'" % url_string) + + if not allowed_archive(url_string): + tty.die("Invalid file type in URL: '%s'" % url_string) + + +def print_pkg(message): + """Outputs a message with a package icon.""" + mac_ver = py_platform.mac_ver()[0] + if mac_ver and Version(mac_ver) >= Version('10.7'): + print u"\U0001F4E6" + tty.indent, + else: + cwrite('@*g{[+]} ') + print message + + class InvalidPackageDependencyError(spack.error.SpackError): """Raised when package specification is inconsistent with requirements of its dependencies.""" diff --git a/lib/spack/spack/packages/__init__.py b/lib/spack/spack/packages/__init__.py index 488aea2423..5731fb0d99 100644 --- a/lib/spack/spack/packages/__init__.py +++ b/lib/spack/spack/packages/__init__.py @@ -29,12 +29,13 @@ import string import inspect import glob +import llnl.util.tty as tty +from llnl.util.filesystem import join_path +from llnl.util.lang import list_modules + import spack import spack.error import spack.spec -import spack.tty as tty -from spack.util.filesystem import new_path -from spack.util.lang import list_modules # Valid package names can contain '-' but can't start with it. valid_package_re = r'^\w[\w-]*$' @@ -212,7 +213,7 @@ def validate_package_name(pkg_name): def dirname_for_package_name(pkg_name): """Get the directory name for a particular package would use, even if it's a foo.py package and not a directory with a foo/__init__.py file.""" - return new_path(spack.packages_path, pkg_name) + return join_path(spack.packages_path, pkg_name) def filename_for_package_name(pkg_name): @@ -236,7 +237,7 @@ def filename_for_package_name(pkg_name): pkg_dir = dirname_for_package_name(pkg_name) if os.path.isdir(pkg_dir): - init_file = new_path(pkg_dir, '__init__.py') + init_file = join_path(pkg_dir, '__init__.py') return init_file else: pkg_file = "%s.py" % pkg_dir diff --git a/lib/spack/spack/patch.py b/lib/spack/spack/patch.py index 82a3a92449..2638000b27 100644 --- a/lib/spack/spack/patch.py +++ b/lib/spack/spack/patch.py @@ -24,14 +24,15 @@ ############################################################################## import os +import llnl.util.tty as tty +from llnl.util.filesystem import join_path + import spack import spack.stage import spack.error import spack.packages as packages -import spack.tty as tty from spack.util.executable import which -from spack.util.filesystem import new_path # Patch tool for patching archives. _patch = which("patch", required=True) @@ -55,7 +56,7 @@ class Patch(object): self.url = path_or_url else: pkg_dir = packages.dirname_for_package_name(pkg_name) - self.path = new_path(pkg_dir, path_or_url) + self.path = join_path(pkg_dir, path_or_url) if not os.path.isfile(self.path): raise NoSuchPatchFileError(pkg_name, self.path) diff --git a/lib/spack/spack/relations.py b/lib/spack/spack/relations.py index 28c9bf0363..3950bd1a20 100644 --- a/lib/spack/spack/relations.py +++ b/lib/spack/spack/relations.py @@ -72,6 +72,8 @@ import re import inspect import importlib +from llnl.util.lang import * + import spack import spack.spec import spack.error @@ -79,7 +81,6 @@ import spack.error from spack.patch import Patch from spack.spec import Spec, parse_anonymous_spec from spack.packages import packages_module -from spack.util.lang import * """Adds a dependencies local variable in the locals of diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index 8d84abcc20..a660e840d0 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -95,16 +95,17 @@ import itertools import hashlib from StringIO import StringIO +import llnl.util.tty as tty +from llnl.util.lang import * +from llnl.util.tty.color import * + import spack.parse import spack.error import spack.compilers import spack.compilers.gcc import spack.packages as packages -import spack.tty as tty from spack.version import * -from spack.color import * -from spack.util.lang import * from spack.util.string import * from spack.util.prefix import Prefix diff --git a/lib/spack/spack/stage.py b/lib/spack/spack/stage.py index 9777ca5d5a..fe99c0ad8a 100644 --- a/lib/spack/spack/stage.py +++ b/lib/spack/spack/stage.py @@ -27,11 +27,11 @@ import re import shutil import tempfile +import llnl.util.tty as tty +from llnl.util.filesystem import * + import spack import spack.error as serr -import spack.tty as tty - -from spack.util.filesystem import * from spack.util.compression import decompressor_for STAGE_PREFIX = 'spack-stage-' @@ -88,7 +88,7 @@ class Stage(object): def _cleanup_dead_links(self): """Remove any dead links in the stage directory.""" for file in os.listdir(spack.stage_path): - path = new_path(spack.stage_path, file) + path = join_path(spack.stage_path, file) if os.path.islink(path): real_path = os.path.realpath(path) if not os.path.exists(path): @@ -150,7 +150,7 @@ class Stage(object): # If this is a named stage, then construct a named path. if self.name is not None: - self.path = new_path(spack.stage_path, self.name) + self.path = join_path(spack.stage_path, self.name) # If this is a temporary stage, them make the temp directory tmp_dir = None @@ -159,7 +159,7 @@ class Stage(object): # Unnamed tmp root. Link the path in tmp_dir = tempfile.mkdtemp('', STAGE_PREFIX, self.tmp_root) self.name = os.path.basename(tmp_dir) - self.path = new_path(spack.stage_path, self.name) + self.path = join_path(spack.stage_path, self.name) if self._need_to_create_path(): os.symlink(tmp_dir, self.path) @@ -200,7 +200,7 @@ class Stage(object): return None for file in os.listdir(self.path): - archive_path = spack.new_path(self.path, file) + archive_path = join_path(self.path, file) if os.path.isdir(archive_path): return archive_path return None @@ -333,7 +333,7 @@ def purge(): """Remove all build directories in the top-level stage path.""" if os.path.isdir(spack.stage_path): for stage_dir in os.listdir(spack.stage_path): - stage_path = spack.new_path(spack.stage_path, stage_dir) + stage_path = join_path(spack.stage_path, stage_dir) remove_linked_tree(stage_path) diff --git a/lib/spack/spack/test/__init__.py b/lib/spack/spack/test/__init__.py index bf8dd6ea96..2aadf40382 100644 --- a/lib/spack/spack/test/__init__.py +++ b/lib/spack/spack/test/__init__.py @@ -25,9 +25,11 @@ import sys import unittest +import llnl.util.tty as tty +from llnl.util.tty.colify import colify + import spack -from spack.colify import colify -import spack.tty as tty + """Names of tests to be included in Spack's test suite""" test_names = ['versions', diff --git a/lib/spack/spack/test/mock_packages_test.py b/lib/spack/spack/test/mock_packages_test.py index 441f4b7fbe..69bd120c58 100644 --- a/lib/spack/spack/test/mock_packages_test.py +++ b/lib/spack/spack/test/mock_packages_test.py @@ -24,12 +24,14 @@ ############################################################################## import unittest +from llnl.util.lang import list_modules +from llnl.util.filesystem import join_path + import spack import spack.packages as packages from spack.spec import Spec -from spack.util.lang import new_path, list_modules -mock_packages_path = new_path(spack.module_path, 'test', 'mock_packages') +mock_packages_path = join_path(spack.module_path, 'test', 'mock_packages') original_deps = None diff --git a/lib/spack/spack/test/packages.py b/lib/spack/spack/test/packages.py index 9541c54c13..1a1f9c9bf4 100644 --- a/lib/spack/spack/test/packages.py +++ b/lib/spack/spack/test/packages.py @@ -40,7 +40,7 @@ class PackagesTest(MockPackagesTest): def test_regular_package_filename(self): filename = packages.filename_for_package_name('mpich') - self.assertEqual(filename, new_path(mock_packages_path, 'mpich.py')) + self.assertEqual(filename, join_path(mock_packages_path, 'mpich.py')) def test_regular_package_name(self): @@ -61,9 +61,9 @@ class PackagesTest(MockPackagesTest): def test_directory_package_filename(self): filename = packages.filename_for_package_name('directory-pkg') - self.assertEqual(filename, new_path(mock_packages_path, 'directory-pkg/__init__.py')) + self.assertEqual(filename, join_path(mock_packages_path, 'directory-pkg/__init__.py')) def test_nonexisting_package_filename(self): filename = packages.filename_for_package_name('some-nonexisting-package') - self.assertEqual(filename, new_path(mock_packages_path, 'some-nonexisting-package.py')) + self.assertEqual(filename, join_path(mock_packages_path, 'some-nonexisting-package.py')) diff --git a/lib/spack/spack/test/spec_dag.py b/lib/spack/spack/test/spec_dag.py index da0b878ce1..d03d146310 100644 --- a/lib/spack/spack/test/spec_dag.py +++ b/lib/spack/spack/test/spec_dag.py @@ -32,7 +32,8 @@ import spack import spack.package import spack.packages as packages -from spack.util.lang import new_path, list_modules +from llnl.util.lang import list_modules + from spack.spec import Spec from spack.test.mock_packages_test import * diff --git a/lib/spack/spack/test/stage.py b/lib/spack/spack/test/stage.py index 102a692ca2..08899f9810 100644 --- a/lib/spack/spack/test/stage.py +++ b/lib/spack/spack/test/stage.py @@ -31,28 +31,32 @@ import os import getpass from contextlib import * +from llnl.util.filesystem import * + import spack from spack.stage import Stage -from spack.util.filesystem import * from spack.util.executable import which -test_files_dir = new_path(spack.stage_path, '.test') -test_tmp_path = new_path(test_files_dir, 'tmp') +test_files_dir = join_path(spack.stage_path, '.test') +test_tmp_path = join_path(test_files_dir, 'tmp') archive_dir = 'test-files' archive_name = archive_dir + '.tar.gz' -archive_dir_path = new_path(test_files_dir, archive_dir) -archive_url = 'file://' + new_path(test_files_dir, archive_name) +archive_dir_path = join_path(test_files_dir, archive_dir) +archive_url = 'file://' + join_path(test_files_dir, archive_name) readme_name = 'README.txt' -test_readme = new_path(archive_dir_path, readme_name) +test_readme = join_path(archive_dir_path, readme_name) readme_text = "hello world!\n" stage_name = 'spack-test-stage' class with_tmp(object): - """Decorator that executes a function with or without spack set - to use a temp dir.""" + """Decorator that executes a function with or without spack set to use + a temp dir. Spack allows builds to happen directly in the + stage directory or in a tmp dir and symlinked into the stage + directory, so this lets us use the same test in both cases. + """ def __init__(self, use_tmp): self.use_tmp = use_tmp @@ -107,7 +111,7 @@ class StageTest(unittest.TestCase): """ if stage_name: # If it is a named stage, we know where the stage should be - stage_path = new_path(spack.stage_path, stage_name) + stage_path = join_path(spack.stage_path, stage_name) else: # If it's unnamed, ensure that we ran mkdtemp in the right spot. stage_path = stage.path @@ -143,7 +147,7 @@ class StageTest(unittest.TestCase): def check_fetch(self, stage, stage_name): stage_path = self.get_stage_path(stage, stage_name) self.assertIn(archive_name, os.listdir(stage_path)) - self.assertEqual(new_path(stage_path, archive_name), + self.assertEqual(join_path(stage_path, archive_name), stage.archive_file) @@ -153,10 +157,10 @@ class StageTest(unittest.TestCase): self.assertIn(archive_dir, os.listdir(stage_path)) self.assertEqual( - new_path(stage_path, archive_dir), + join_path(stage_path, archive_dir), stage.expanded_archive_path) - readme = new_path(stage_path, archive_dir, readme_name) + readme = join_path(stage_path, archive_dir, readme_name) self.assertTrue(os.path.isfile(readme)) with closing(open(readme)) as file: @@ -171,7 +175,7 @@ class StageTest(unittest.TestCase): def check_chdir_to_archive(self, stage, stage_name): stage_path = self.get_stage_path(stage, stage_name) self.assertEqual( - new_path(os.path.realpath(stage_path), archive_dir), + join_path(os.path.realpath(stage_path), archive_dir), os.getcwd()) diff --git a/lib/spack/spack/tty.py b/lib/spack/spack/tty.py deleted file mode 100644 index b9ad3f12bf..0000000000 --- a/lib/spack/spack/tty.py +++ /dev/null @@ -1,107 +0,0 @@ -############################################################################## -# 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 -import spack -from spack.color import * - -indent = " " - -def msg(message, *args): - cprint("@*b{==>} %s" % cescape(message)) - for arg in args: - print indent + str(arg) - - -def info(message, *args, **kwargs): - format = kwargs.get('format', '*b') - cprint("@%s{==>} %s" % (format, cescape(str(message)))) - for arg in args: - print indent + str(arg) - - -def verbose(message, *args): - if spack.verbose: - info(str(message), *args, format='c') - - -def debug(*args): - if spack.debug: - info("Debug: " + str(message), *args, format='*g') - - -def error(message, *args): - info("Error: " + str(message), *args, format='*r') - - -def warn(message, *args): - info("Warning: " + str(message), *args, format='*Y') - - -def die(message, *args): - error(message, *args) - sys.exit(1) - - -def pkg(message): - """Outputs a message with a package icon.""" - import platform - from version import Version - - mac_ver = platform.mac_ver()[0] - if mac_ver and Version(mac_ver) >= Version('10.7'): - print u"\U0001F4E6" + indent, - else: - cwrite('@*g{[+]} ') - print message - - -def get_number(prompt, **kwargs): - default = kwargs.get('default', None) - abort = kwargs.get('abort', None) - - if default is not None and abort is not None: - prompt += ' (default is %s, %s to abort) ' % (default, abort) - elif default is not None: - prompt += ' (default is %s) ' % default - elif abort is not None: - prompt += ' (%s to abort) ' % abort - - number = None - while number is None: - ans = raw_input(prompt) - if ans == str(abort): - return None - - if ans: - try: - number = int(ans) - if number < 1: - msg("Please enter a valid number.") - number = None - except ValueError: - msg("Please enter a valid number.") - elif default is not None: - number = default - return number diff --git a/lib/spack/spack/url.py b/lib/spack/spack/url.py index a4c32eb37b..f56aaee493 100644 --- a/lib/spack/spack/url.py +++ b/lib/spack/spack/url.py @@ -48,7 +48,7 @@ import os import re import spack.error -import spack.util.filesystem as fs +import spack.util.compression as comp from spack.version import Version # @@ -85,9 +85,9 @@ def parse_version_string_with_indices(path): if os.path.isdir(path): stem = os.path.basename(path) elif re.search(r'((?:sourceforge.net|sf.net)/.*)/download$', path): - stem = fs.stem(os.path.dirname(path)) + stem = comp.stem(os.path.dirname(path)) else: - stem = fs.stem(path) + stem = comp.stem(path) version_types = [ # GitHub tarballs, e.g. v1.2.3 diff --git a/lib/spack/spack/util/compression.py b/lib/spack/spack/util/compression.py index 2b8b05eb28..427a9cf020 100644 --- a/lib/spack/spack/util/compression.py +++ b/lib/spack/spack/util/compression.py @@ -22,6 +22,7 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## +import re from itertools import product from spack.util.executable import which @@ -45,3 +46,13 @@ def decompressor_for(path): tar = which('tar', required=True) tar.add_default_arg('-xf') return tar + + +def stem(path): + """Get the part of a path that does not include its compressed + type extension.""" + for type in ALLOWED_ARCHIVE_TYPES: + suffix = r'\.%s$' % type + if re.search(suffix, path): + return re.sub(suffix, "", path) + return path diff --git a/lib/spack/spack/util/executable.py b/lib/spack/spack/util/executable.py index 4d5eff0bd8..ac329445f7 100644 --- a/lib/spack/spack/util/executable.py +++ b/lib/spack/spack/util/executable.py @@ -27,7 +27,7 @@ import sys import re import subprocess -import spack.tty as tty +import llnl.util.tty as tty from spack.error import SpackError diff --git a/lib/spack/spack/util/filesystem.py b/lib/spack/spack/util/filesystem.py deleted file mode 100644 index c84a9fd608..0000000000 --- a/lib/spack/spack/util/filesystem.py +++ /dev/null @@ -1,104 +0,0 @@ -############################################################################## -# 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 re -import shutil -import errno -import getpass -from contextlib import contextmanager, closing - -import spack.tty as tty -from spack.util.compression import ALLOWED_ARCHIVE_TYPES - - -def install(src, dest): - """Manually install a file to a particular location.""" - tty.info("Installing %s to %s" % (src, dest)) - shutil.copy(src, dest) - - -def expand_user(path): - """Find instances of '%u' in a path and replace with the current user's - username.""" - username = getpass.getuser() - if not username and '%u' in path: - tty.die("Couldn't get username to complete path '%s'" % path) - - return path.replace('%u', username) - - -@contextmanager -def working_dir(dirname): - orig_dir = os.getcwd() - os.chdir(dirname) - yield - os.chdir(orig_dir) - - -def touch(path): - with closing(open(path, 'a')) as file: - os.utime(path, None) - - -def mkdirp(*paths): - for path in paths: - if not os.path.exists(path): - os.makedirs(path) - elif not os.path.isdir(path): - raise OSError(errno.EEXIST, "File alredy exists", path) - - -def new_path(prefix, *args): - path = str(prefix) - for elt in args: - path = os.path.join(path, str(elt)) - - if re.search(r'\s', path): - tty.die("Invalid path: '%s'. Use a path without whitespace." % path) - - return path - - -def ancestor(dir, n=1): - """Get the nth ancestor of a directory.""" - parent = os.path.abspath(dir) - for i in range(n): - parent = os.path.dirname(parent) - return parent - - -def stem(path): - """Get the part of a path that does not include its compressed - type extension.""" - for type in ALLOWED_ARCHIVE_TYPES: - suffix = r'\.%s$' % type - if re.search(suffix, path): - return re.sub(suffix, "", path) - return path - - -def can_access(file_name): - """True if we have read/write access to the file.""" - return os.access(file_name, os.R_OK|os.W_OK) diff --git a/lib/spack/spack/util/lang.py b/lib/spack/spack/util/lang.py deleted file mode 100644 index b77182ba7f..0000000000 --- a/lib/spack/spack/util/lang.py +++ /dev/null @@ -1,189 +0,0 @@ -############################################################################## -# 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 re -import sys -import functools -import inspect -from spack.util.filesystem import new_path - -# Ignore emacs backups when listing modules -ignore_modules = [r'^\.#', '~$'] - - -def caller_locals(): - """This will return the locals of the *parent* of the caller. - This allows a fucntion to insert variables into its caller's - scope. Yes, this is some black magic, and yes it's useful - for implementing things like depends_on and provides. - """ - stack = inspect.stack() - try: - return stack[2][0].f_locals - finally: - del stack - - -def get_calling_package_name(): - """Make sure that the caller is a class definition, and return - the module's name. This is useful for getting the name of - spack packages from inside a relation function. - """ - stack = inspect.stack() - try: - # get calling function name (the relation) - relation = stack[1][3] - - # Make sure locals contain __module__ - caller_locals = stack[2][0].f_locals - finally: - del stack - - if not '__module__' in caller_locals: - raise ScopeError(relation) - - module_name = caller_locals['__module__'] - base_name = module_name.split('.')[-1] - return base_name - - -def attr_required(obj, attr_name): - """Ensure that a class has a required attribute.""" - if not hasattr(obj, attr_name): - tty.die("No required attribute '%s' in class '%s'" - % (attr_name, obj.__class__.__name__)) - - -def attr_setdefault(obj, name, value): - """Like dict.setdefault, but for objects.""" - if not hasattr(obj, name): - setattr(obj, name, value) - return getattr(obj, name) - - -def has_method(cls, name): - for base in inspect.getmro(cls): - if base is object: - continue - if name in base.__dict__: - return True - return False - - -def memoized(obj): - """Decorator that caches the results of a function, storing them - in an attribute of that function.""" - cache = obj.cache = {} - @functools.wraps(obj) - def memoizer(*args, **kwargs): - if args not in cache: - cache[args] = obj(*args, **kwargs) - return cache[args] - return memoizer - - -def list_modules(directory, **kwargs): - """Lists all of the modules, excluding __init__.py, in a - particular directory. Listed packages have no particular - order.""" - list_directories = kwargs.setdefault('directories', True) - - for name in os.listdir(directory): - if name == '__init__.py': - continue - - path = new_path(directory, name) - if list_directories and os.path.isdir(path): - init_py = new_path(path, '__init__.py') - if os.path.isfile(init_py): - yield name - - elif name.endswith('.py'): - if not any(re.search(pattern, name) for pattern in ignore_modules): - yield re.sub('.py$', '', name) - - -def key_ordering(cls): - """Decorates a class with extra methods that implement rich comparison - operations and __hash__. The decorator assumes that the class - implements a function called _cmp_key(). The rich comparison operations - will compare objects using this key, and the __hash__ function will - return the hash of this key. - - If a class already has __eq__, __ne__, __lt__, __le__, __gt__, or __ge__ - defined, this decorator will overwrite them. If the class does not - have a _cmp_key method, then this will raise a TypeError. - """ - def setter(name, value): - value.__name__ = name - setattr(cls, name, value) - - if not has_method(cls, '_cmp_key'): - raise TypeError("'%s' doesn't define _cmp_key()." % cls.__name__) - - setter('__eq__', lambda s,o: o is not None and s._cmp_key() == o._cmp_key()) - setter('__lt__', lambda s,o: o is not None and s._cmp_key() < o._cmp_key()) - setter('__le__', lambda s,o: o is not None and s._cmp_key() <= o._cmp_key()) - - setter('__ne__', lambda s,o: o is None or s._cmp_key() != o._cmp_key()) - setter('__gt__', lambda s,o: o is None or s._cmp_key() > o._cmp_key()) - setter('__ge__', lambda s,o: o is None or s._cmp_key() >= o._cmp_key()) - - setter('__hash__', lambda self: hash(self._cmp_key())) - - return cls - - -@key_ordering -class HashableMap(dict): - """This is a hashable, comparable dictionary. Hash is performed on - a tuple of the values in the dictionary.""" - def _cmp_key(self): - return tuple(sorted(self.values())) - - - def copy(self): - """Type-agnostic clone method. Preserves subclass type.""" - # Construct a new dict of my type - T = type(self) - clone = T() - - # Copy everything from this dict into it. - for key in self: - clone[key] = self[key].copy() - return clone - - -def in_function(function_name): - """True if the caller was called from some function with - the supplied Name, False otherwise.""" - stack = inspect.stack() - try: - for elt in stack[2:]: - if elt[3] == function_name: - return True - return False - finally: - del stack diff --git a/lib/spack/spack/util/none_high.py b/lib/spack/spack/util/none_high.py deleted file mode 100644 index 78b41cbaf6..0000000000 --- a/lib/spack/spack/util/none_high.py +++ /dev/null @@ -1,70 +0,0 @@ -############################################################################## -# 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 -############################################################################## -""" -Functions for comparing values that may potentially be None. -These none_high functions consider None as greater than all other values. -""" - -# Preserve builtin min and max functions -_builtin_min = min -_builtin_max = max - - -def lt(lhs, rhs): - """Less-than comparison. None is greater than any value.""" - return lhs != rhs and (rhs is None or (lhs is not None and lhs < rhs)) - - -def le(lhs, rhs): - """Less-than-or-equal comparison. None is greater than any value.""" - return lhs == rhs or lt(lhs, rhs) - - -def gt(lhs, rhs): - """Greater-than comparison. None is greater than any value.""" - return lhs != rhs and not lt(lhs, rhs) - - -def ge(lhs, rhs): - """Greater-than-or-equal comparison. None is greater than any value.""" - return lhs == rhs or gt(lhs, rhs) - - -def min(lhs, rhs): - """Minimum function where None is greater than any value.""" - if lhs is None: - return rhs - elif rhs is None: - return lhs - else: - return _builtin_min(lhs, rhs) - - -def max(lhs, rhs): - """Maximum function where None is greater than any value.""" - if lhs is None or rhs is None: - return None - else: - return _builtin_max(lhs, rhs) diff --git a/lib/spack/spack/util/none_low.py b/lib/spack/spack/util/none_low.py deleted file mode 100644 index 307bcc8a26..0000000000 --- a/lib/spack/spack/util/none_low.py +++ /dev/null @@ -1,70 +0,0 @@ -############################################################################## -# 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 -############################################################################## -""" -Functions for comparing values that may potentially be None. -These none_low functions consider None as less than all other values. -""" - -# Preserve builtin min and max functions -_builtin_min = min -_builtin_max = max - - -def lt(lhs, rhs): - """Less-than comparison. None is lower than any value.""" - return lhs != rhs and (lhs is None or (rhs is not None and lhs < rhs)) - - -def le(lhs, rhs): - """Less-than-or-equal comparison. None is less than any value.""" - return lhs == rhs or lt(lhs, rhs) - - -def gt(lhs, rhs): - """Greater-than comparison. None is less than any value.""" - return lhs != rhs and not lt(lhs, rhs) - - -def ge(lhs, rhs): - """Greater-than-or-equal comparison. None is less than any value.""" - return lhs == rhs or gt(lhs, rhs) - - -def min(lhs, rhs): - """Minimum function where None is less than any value.""" - if lhs is None or rhs is None: - return None - else: - return _builtin_min(lhs, rhs) - - -def max(lhs, rhs): - """Maximum function where None is less than any value.""" - if lhs is None: - return rhs - elif rhs is None: - return lhs - else: - return _builtin_max(lhs, rhs) diff --git a/lib/spack/spack/util/prefix.py b/lib/spack/spack/util/prefix.py index 09a04676f7..1b5ab5933f 100644 --- a/lib/spack/spack/util/prefix.py +++ b/lib/spack/spack/util/prefix.py @@ -25,7 +25,7 @@ """ This file contains utilities to help with installing packages. """ -from spack.util.filesystem import new_path +from llnl.util.filesystem import join_path class Prefix(str): """This class represents an installation prefix, but provides useful @@ -59,23 +59,23 @@ class Prefix(str): def __new__(cls, path): s = super(Prefix, cls).__new__(cls, path) - s.bin = new_path(s, 'bin') - s.sbin = new_path(s, 'sbin') - s.etc = new_path(s, 'etc') - s.include = new_path(s, 'include') - s.lib = new_path(s, 'lib') - s.lib64 = new_path(s, 'lib64') - s.libexec = new_path(s, 'libexec') - s.share = new_path(s, 'share') - s.doc = new_path(s.share, 'doc') - s.info = new_path(s.share, 'info') - s.man = new_path(s.share, 'man') - s.man1 = new_path(s.man, 'man1') - s.man2 = new_path(s.man, 'man2') - s.man3 = new_path(s.man, 'man3') - s.man4 = new_path(s.man, 'man4') - s.man5 = new_path(s.man, 'man5') - s.man6 = new_path(s.man, 'man6') - s.man7 = new_path(s.man, 'man7') - s.man8 = new_path(s.man, 'man8') + s.bin = join_path(s, 'bin') + s.sbin = join_path(s, 'sbin') + s.etc = join_path(s, 'etc') + s.include = join_path(s, 'include') + s.lib = join_path(s, 'lib') + s.lib64 = join_path(s, 'lib64') + s.libexec = join_path(s, 'libexec') + s.share = join_path(s, 'share') + s.doc = join_path(s.share, 'doc') + s.info = join_path(s.share, 'info') + s.man = join_path(s.share, 'man') + s.man1 = join_path(s.man, 'man1') + s.man2 = join_path(s.man, 'man2') + s.man3 = join_path(s.man, 'man3') + s.man4 = join_path(s.man, 'man4') + s.man5 = join_path(s.man, 'man5') + s.man6 = join_path(s.man, 'man6') + s.man7 = join_path(s.man, 'man7') + s.man8 = join_path(s.man, 'man8') return s diff --git a/lib/spack/spack/util/web.py b/lib/spack/spack/util/web.py index 62c4fa6aa1..b5104eb076 100644 --- a/lib/spack/spack/util/web.py +++ b/lib/spack/spack/util/web.py @@ -29,9 +29,10 @@ import urlparse from multiprocessing import Pool from HTMLParser import HTMLParser +import llnl.util.tty as tty + import spack import spack.error -import spack.tty as tty from spack.util.compression import ALLOWED_ARCHIVE_TYPES # Timeout in seconds for web requests diff --git a/lib/spack/spack/validate.py b/lib/spack/spack/validate.py deleted file mode 100644 index 5bdc7b7f66..0000000000 --- a/lib/spack/spack/validate.py +++ /dev/null @@ -1,38 +0,0 @@ -############################################################################## -# 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 tty -from urlparse import urlparse - -from spack.util.compression import allowed_archive - -ALLOWED_SCHEMES = ["http", "https", "ftp", "file"] - -def url(url_string): - url = urlparse(url_string) - if url.scheme not in ALLOWED_SCHEMES: - tty.die("Invalid protocol in URL: '%s'" % url_string) - - if not allowed_archive(url_string): - tty.die("Invalid file type in URL: '%s'" % url_string) diff --git a/lib/spack/spack/version.py b/lib/spack/spack/version.py index 8108c0f60b..1f44c5f39b 100644 --- a/lib/spack/spack/version.py +++ b/lib/spack/spack/version.py @@ -49,8 +49,8 @@ import re from bisect import bisect_left from functools import total_ordering, wraps -import spack.util.none_high as none_high -import spack.util.none_low as none_low +import llnl.util.compare.none_high as none_high +import llnl.util.compare.none_low as none_low import spack.error # Valid version characters -- cgit v1.2.3-60-g2f50