Merge branch 'develop'

This commit is contained in:
Todd Gamblin 2014-09-18 23:54:26 -07:00
commit de030c9932
140 changed files with 6585 additions and 942 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@
.idea
/etc/spackconfig
/share/spack/dotkit
/share/spack/modules

View File

@ -35,4 +35,20 @@ for Spack is also available.
Authors
----------------
Spack was written by Todd Gamblin, tgamblin@llnl.gov.
LLNL-CODE-647188
Significant contributions were also made by the following awesome
people:
* David Beckingsale
* David Boehme
* Luc Jaulmes
* Matt Legendre
* Greg Lee
* Adam Moody
Release
----------------
Spack is released under an LGPL license. For more details see the
LICENSE file.
``LLNL-CODE-647188``

View File

@ -24,11 +24,10 @@
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import sys
if not sys.version_info[:2] >= (2,7):
sys.exit("Spack requires Python 2.7. Version was %s." % sys.version_info)
if not sys.version_info[:2] >= (2,6):
sys.exit("Spack requires Python 2.6. Version was %s." % sys.version_info)
import os
import argparse
# Find spack's location and its prefix.
SPACK_FILE = os.path.realpath(os.path.expanduser(__file__))
@ -51,6 +50,7 @@ del SPACK_FILE, SPACK_PREFIX, SPACK_LIB_PATH
import llnl.util.tty as tty
import spack
from spack.error import SpackError
from external import argparse
# Command parsing
parser = argparse.ArgumentParser(
@ -75,6 +75,13 @@ for cmd in spack.cmd.commands:
module = spack.cmd.get_module(cmd)
subparser = subparsers.add_parser(cmd, help=module.description)
module.setup_parser(subparser)
# Just print help and exit if run with no arguments at all
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
# actually parse the args.
args = parser.parse_args()
# Set up environment based on args.

6
lib/spack/env/cc vendored
View File

@ -1,12 +1,11 @@
#!/usr/bin/env python
import sys
if not sys.version_info[:2] >= (2,7):
sys.exit("Spack requires Python 2.7. Version was %s." % sys.version_info)
if not sys.version_info[:2] >= (2,6):
sys.exit("Spack requires Python 2.6. Version was %s." % sys.version_info)
import os
import re
import subprocess
import argparse
from contextlib import closing
# Import spack parameters through the build environment.
@ -18,6 +17,7 @@ if not spack_lib:
# Grab a minimal set of spack packages
sys.path.append(spack_lib)
from spack.compilation import *
from external import argparse
import llnl.util.tty as tty
spack_prefix = get_env_var("SPACK_PREFIX")

33
lib/spack/external/__init__.py vendored Normal file
View File

@ -0,0 +1,33 @@
##############################################################################
# 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 module contains external, potentially separately licensed,
packages that are included in spack.
So far:
argparse: We include our own version to be Python 2.6 compatible.
pyqver2: External script to query required python version of python source code.
Used for ensuring 2.6 compatibility.
"""

2382
lib/spack/external/argparse.py vendored Normal file

File diff suppressed because it is too large Load Diff

30
lib/spack/external/functools.py vendored Normal file
View File

@ -0,0 +1,30 @@
#
# Backport of Python 2.7's total_ordering.
#
def total_ordering(cls):
"""Class decorator that fills in missing ordering methods"""
convert = {
'__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
('__le__', lambda self, other: self < other or self == other),
('__ge__', lambda self, other: not self < other)],
'__le__': [('__ge__', lambda self, other: not self <= other or self == other),
('__lt__', lambda self, other: self <= other and not self == other),
('__gt__', lambda self, other: not self <= other)],
'__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
('__ge__', lambda self, other: self > other or self == other),
('__le__', lambda self, other: not self > other)],
'__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
('__gt__', lambda self, other: self >= other and not self == other),
('__lt__', lambda self, other: not self >= other)]
}
roots = set(dir(cls)) & set(convert)
if not roots:
raise ValueError('must define at least one ordering operation: < > <= >=')
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
for opname, opfunc in convert[root]:
if opname not in roots:
opfunc.__name__ = opname
opfunc.__doc__ = getattr(int, opname).__doc__
setattr(cls, opname, opfunc)
return cls

262
lib/spack/external/ordereddict.py vendored Normal file
View File

@ -0,0 +1,262 @@
#
# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
# Passes Python2.7's test suite and incorporates all the latest updates.
#
# From http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/
# This file is in the public domain, and has no particular license.
#
try:
from thread import get_ident as _get_ident
except ImportError:
from dummy_thread import get_ident as _get_ident
try:
from _abcoll import KeysView, ValuesView, ItemsView
except ImportError:
pass
class OrderedDict(dict):
'Dictionary that remembers insertion order'
# An inherited dict maps keys to values.
# The inherited dict provides __getitem__, __len__, __contains__, and get.
# The remaining methods are order-aware.
# Big-O running times for all methods are the same as for regular dictionaries.
# The internal self.__map dictionary maps keys to links in a doubly linked list.
# The circular doubly linked list starts and ends with a sentinel element.
# The sentinel element never gets deleted (this simplifies the algorithm).
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
def __init__(self, *args, **kwds):
'''Initialize an ordered dictionary. Signature is the same as for
regular dictionaries, but keyword arguments are not recommended
because their insertion order is arbitrary.
'''
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__root
except AttributeError:
self.__root = root = [] # sentinel node
root[:] = [root, root, None]
self.__map = {}
self.__update(*args, **kwds)
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
'od.__setitem__(i, y) <==> od[i]=y'
# Setting a new item creates a new link which goes at the end of the linked
# list, and the inherited dictionary is updated with the new key/value pair.
if key not in self:
root = self.__root
last = root[0]
last[1] = root[0] = self.__map[key] = [last, root, key]
dict_setitem(self, key, value)
def __delitem__(self, key, dict_delitem=dict.__delitem__):
'od.__delitem__(y) <==> del od[y]'
# Deleting an existing item uses self.__map to find the link which is
# then removed by updating the links in the predecessor and successor nodes.
dict_delitem(self, key)
link_prev, link_next, key = self.__map.pop(key)
link_prev[1] = link_next
link_next[0] = link_prev
def __iter__(self):
'od.__iter__() <==> iter(od)'
root = self.__root
curr = root[1]
while curr is not root:
yield curr[2]
curr = curr[1]
def __reversed__(self):
'od.__reversed__() <==> reversed(od)'
root = self.__root
curr = root[0]
while curr is not root:
yield curr[2]
curr = curr[0]
def clear(self):
'od.clear() -> None. Remove all items from od.'
try:
for node in self.__map.itervalues():
del node[:]
root = self.__root
root[:] = [root, root, None]
self.__map.clear()
except AttributeError:
pass
dict.clear(self)
def popitem(self, last=True):
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
Pairs are returned in LIFO order if last is true or FIFO order if false.
'''
if not self:
raise KeyError('dictionary is empty')
root = self.__root
if last:
link = root[0]
link_prev = link[0]
link_prev[1] = root
root[0] = link_prev
else:
link = root[1]
link_next = link[1]
root[1] = link_next
link_next[0] = root
key = link[2]
del self.__map[key]
value = dict.pop(self, key)
return key, value
# -- the following methods do not depend on the internal structure --
def keys(self):
'od.keys() -> list of keys in od'
return list(self)
def values(self):
'od.values() -> list of values in od'
return [self[key] for key in self]
def items(self):
'od.items() -> list of (key, value) pairs in od'
return [(key, self[key]) for key in self]
def iterkeys(self):
'od.iterkeys() -> an iterator over the keys in od'
return iter(self)
def itervalues(self):
'od.itervalues -> an iterator over the values in od'
for k in self:
yield self[k]
def iteritems(self):
'od.iteritems -> an iterator over the (key, value) items in od'
for k in self:
yield (k, self[k])
def update(*args, **kwds):
'''od.update(E, **F) -> None. Update od from dict/iterable E and F.
If E is a dict instance, does: for k in E: od[k] = E[k]
If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
Or if E is an iterable of items, does: for k, v in E: od[k] = v
In either case, this is followed by: for k, v in F.items(): od[k] = v
'''
if len(args) > 2:
raise TypeError('update() takes at most 2 positional '
'arguments (%d given)' % (len(args),))
elif not args:
raise TypeError('update() takes at least 1 argument (0 given)')
self = args[0]
# Make progressively weaker assumptions about "other"
other = ()
if len(args) == 2:
other = args[1]
if isinstance(other, dict):
for key in other:
self[key] = other[key]
elif hasattr(other, 'keys'):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value
__update = update # let subclasses override update without breaking __init__
__marker = object()
def pop(self, key, default=__marker):
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised.
'''
if key in self:
result = self[key]
del self[key]
return result
if default is self.__marker:
raise KeyError(key)
return default
def setdefault(self, key, default=None):
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
if key in self:
return self[key]
self[key] = default
return default
def __repr__(self, _repr_running={}):
'od.__repr__() <==> repr(od)'
call_key = id(self), _get_ident()
if call_key in _repr_running:
return '...'
_repr_running[call_key] = 1
try:
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
finally:
del _repr_running[call_key]
def __reduce__(self):
'Return state information for pickling'
items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
for k in vars(OrderedDict()):
inst_dict.pop(k, None)
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def copy(self):
'od.copy() -> a shallow copy of od'
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
and values equal to v (which defaults to None).
'''
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
while comparison to a regular mapping is order-insensitive.
'''
if isinstance(other, OrderedDict):
return len(self)==len(other) and self.items() == other.items()
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other
# -- the following methods are only used in Python 2.7 --
def viewkeys(self):
"od.viewkeys() -> a set-like object providing a view on od's keys"
return KeysView(self)
def viewvalues(self):
"od.viewvalues() -> an object providing a view on od's values"
return ValuesView(self)
def viewitems(self):
"od.viewitems() -> a set-like object providing a view on od's items"
return ItemsView(self)

393
lib/spack/external/pyqver2.py vendored Executable file
View File

@ -0,0 +1,393 @@
#!/usr/bin/env python
#
# pyqver2.py
# by Greg Hewgill
# https://github.com/ghewgill/pyqver
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the author be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
#
# Copyright (c) 2009-2013 Greg Hewgill http://hewgill.com
#
import compiler
import platform
import sys
StandardModules = {
"__future__": (2, 1),
"abc": (2, 6),
"argparse": (2, 7),
"ast": (2, 6),
"atexit": (2, 0),
"bz2": (2, 3),
"cgitb": (2, 2),
"collections": (2, 4),
"contextlib": (2, 5),
"cookielib": (2, 4),
"cProfile": (2, 5),
"csv": (2, 3),
"ctypes": (2, 5),
"datetime": (2, 3),
"decimal": (2, 4),
"difflib": (2, 1),
"DocXMLRPCServer": (2, 3),
"dummy_thread": (2, 3),
"dummy_threading": (2, 3),
"email": (2, 2),
"fractions": (2, 6),
"functools": (2, 5),
"future_builtins": (2, 6),
"hashlib": (2, 5),
"heapq": (2, 3),
"hmac": (2, 2),
"hotshot": (2, 2),
"HTMLParser": (2, 2),
"importlib": (2, 7),
"inspect": (2, 1),
"io": (2, 6),
"itertools": (2, 3),
"json": (2, 6),
"logging": (2, 3),
"modulefinder": (2, 3),
"msilib": (2, 5),
"multiprocessing": (2, 6),
"netrc": (1, 5, 2),
"numbers": (2, 6),
"optparse": (2, 3),
"ossaudiodev": (2, 3),
"pickletools": (2, 3),
"pkgutil": (2, 3),
"platform": (2, 3),
"pydoc": (2, 1),
"runpy": (2, 5),
"sets": (2, 3),
"shlex": (1, 5, 2),
"SimpleXMLRPCServer": (2, 2),
"spwd": (2, 5),
"sqlite3": (2, 5),
"ssl": (2, 6),
"stringprep": (2, 3),
"subprocess": (2, 4),
"sysconfig": (2, 7),
"tarfile": (2, 3),
"textwrap": (2, 3),
"timeit": (2, 3),
"unittest": (2, 1),
"uuid": (2, 5),
"warnings": (2, 1),
"weakref": (2, 1),
"winsound": (1, 5, 2),
"wsgiref": (2, 5),
"xml.dom": (2, 0),
"xml.dom.minidom": (2, 0),
"xml.dom.pulldom": (2, 0),
"xml.etree.ElementTree": (2, 5),
"xml.parsers.expat":(2, 0),
"xml.sax": (2, 0),
"xml.sax.handler": (2, 0),
"xml.sax.saxutils": (2, 0),
"xml.sax.xmlreader":(2, 0),
"xmlrpclib": (2, 2),
"zipfile": (1, 6),
"zipimport": (2, 3),
"_ast": (2, 5),
"_winreg": (2, 0),
}
Functions = {
"all": (2, 5),
"any": (2, 5),
"collections.Counter": (2, 7),
"collections.defaultdict": (2, 5),
"collections.OrderedDict": (2, 7),
"functools.total_ordering": (2, 7),
"enumerate": (2, 3),
"frozenset": (2, 4),
"itertools.compress": (2, 7),
"math.erf": (2, 7),
"math.erfc": (2, 7),
"math.expm1": (2, 7),
"math.gamma": (2, 7),
"math.lgamma": (2, 7),
"memoryview": (2, 7),
"next": (2, 6),
"os.getresgid": (2, 7),
"os.getresuid": (2, 7),
"os.initgroups": (2, 7),
"os.setresgid": (2, 7),
"os.setresuid": (2, 7),
"reversed": (2, 4),
"set": (2, 4),
"subprocess.check_call": (2, 5),
"subprocess.check_output": (2, 7),
"sum": (2, 3),
"symtable.is_declared_global": (2, 7),
"weakref.WeakSet": (2, 7),
}
Identifiers = {
"False": (2, 2),
"True": (2, 2),
}
def uniq(a):
if len(a) == 0:
return []
else:
return [a[0]] + uniq([x for x in a if x != a[0]])
class NodeChecker(object):
def __init__(self):
self.vers = dict()
self.vers[(2,0)] = []
def add(self, node, ver, msg):
if ver not in self.vers:
self.vers[ver] = []
self.vers[ver].append((node.lineno, msg))
def default(self, node):
for child in node.getChildNodes():
self.visit(child)
def visitCallFunc(self, node):
def rollup(n):
if isinstance(n, compiler.ast.Name):
return n.name
elif isinstance(n, compiler.ast.Getattr):
r = rollup(n.expr)
if r:
return r + "." + n.attrname
name = rollup(node.node)
if name:
v = Functions.get(name)
if v is not None:
self.add(node, v, name)
self.default(node)
def visitClass(self, node):
if node.bases:
self.add(node, (2,2), "new-style class")
if node.decorators:
self.add(node, (2,6), "class decorator")
self.default(node)
def visitDictComp(self, node):
self.add(node, (2,7), "dictionary comprehension")
self.default(node)
def visitFloorDiv(self, node):
self.add(node, (2,2), "// operator")
self.default(node)
def visitFrom(self, node):
v = StandardModules.get(node.modname)
if v is not None:
self.add(node, v, node.modname)
for n in node.names:
name = node.modname + "." + n[0]
v = Functions.get(name)
if v is not None:
self.add(node, v, name)
def visitFunction(self, node):
if node.decorators:
self.add(node, (2,4), "function decorator")
self.default(node)
def visitGenExpr(self, node):
self.add(node, (2,4), "generator expression")
self.default(node)
def visitGetattr(self, node):
if (isinstance(node.expr, compiler.ast.Const)
and isinstance(node.expr.value, str)
and node.attrname == "format"):
self.add(node, (2,6), "string literal .format()")
self.default(node)
def visitIfExp(self, node):
self.add(node, (2,5), "inline if expression")
self.default(node)
def visitImport(self, node):
for n in node.names:
v = StandardModules.get(n[0])
if v is not None:
self.add(node, v, n[0])
self.default(node)
def visitName(self, node):
v = Identifiers.get(node.name)
if v is not None:
self.add(node, v, node.name)
self.default(node)
def visitSet(self, node):
self.add(node, (2,7), "set literal")
self.default(node)
def visitSetComp(self, node):
self.add(node, (2,7), "set comprehension")
self.default(node)
def visitTryFinally(self, node):
# try/finally with a suite generates a Stmt node as the body,
# but try/except/finally generates a TryExcept as the body
if isinstance(node.body, compiler.ast.TryExcept):
self.add(node, (2,5), "try/except/finally")
self.default(node)
def visitWith(self, node):
if isinstance(node.body, compiler.ast.With):
self.add(node, (2,7), "with statement with multiple contexts")
else:
self.add(node, (2,5), "with statement")
self.default(node)
def visitYield(self, node):
self.add(node, (2,2), "yield expression")
self.default(node)
def get_versions(source):
"""Return information about the Python versions required for specific features.
The return value is a dictionary with keys as a version number as a tuple
(for example Python 2.6 is (2,6)) and the value are a list of features that
require the indicated Python version.
"""
tree = compiler.parse(source)
checker = compiler.walk(tree, NodeChecker())
return checker.vers
def v27(source):
if sys.version_info >= (2, 7):
return qver(source)
else:
print >>sys.stderr, "Not all features tested, run --test with Python 2.7"
return (2, 7)
def qver(source):
"""Return the minimum Python version required to run a particular bit of code.
>>> qver('print "hello world"')
(2, 0)
>>> qver('class test(object): pass')
(2, 2)
>>> qver('yield 1')
(2, 2)
>>> qver('a // b')
(2, 2)
>>> qver('True')
(2, 2)
>>> qver('enumerate(a)')
(2, 3)
>>> qver('total = sum')
(2, 0)
>>> qver('sum(a)')
(2, 3)
>>> qver('(x*x for x in range(5))')
(2, 4)
>>> qver('class C:\\n @classmethod\\n def m(): pass')
(2, 4)
>>> qver('y if x else z')
(2, 5)
>>> qver('import hashlib')
(2, 5)
>>> qver('from hashlib import md5')
(2, 5)
>>> qver('import xml.etree.ElementTree')
(2, 5)
>>> qver('try:\\n try: pass;\\n except: pass;\\nfinally: pass')
(2, 0)
>>> qver('try: pass;\\nexcept: pass;\\nfinally: pass')
(2, 5)
>>> qver('from __future__ import with_statement\\nwith x: pass')
(2, 5)
>>> qver('collections.defaultdict(list)')
(2, 5)
>>> qver('from collections import defaultdict')
(2, 5)
>>> qver('"{0}".format(0)')
(2, 6)
>>> qver('memoryview(x)')
(2, 7)
>>> v27('{1, 2, 3}')
(2, 7)
>>> v27('{x for x in s}')
(2, 7)
>>> v27('{x: y for x in s}')
(2, 7)
>>> qver('from __future__ import with_statement\\nwith x:\\n with y: pass')
(2, 5)
>>> v27('from __future__ import with_statement\\nwith x, y: pass')
(2, 7)
>>> qver('@decorator\\ndef f(): pass')
(2, 4)
>>> qver('@decorator\\nclass test:\\n pass')
(2, 6)
#>>> qver('0o0')
#(2, 6)
#>>> qver('@foo\\nclass C: pass')
#(2, 6)
"""
return max(get_versions(source).keys())
if __name__ == '__main__':
Verbose = False
MinVersion = (2, 3)
Lint = False
files = []
i = 1
while i < len(sys.argv):
a = sys.argv[i]
if a == "--test":
import doctest
doctest.testmod()
sys.exit(0)
if a == "-v" or a == "--verbose":
Verbose = True
elif a == "-l" or a == "--lint":
Lint = True
elif a == "-m" or a == "--min-version":
i += 1
MinVersion = tuple(map(int, sys.argv[i].split(".")))
else:
files.append(a)
i += 1
if not files:
print >>sys.stderr, """Usage: %s [options] source ...
Report minimum Python version required to run given source files.
-m x.y or --min-version x.y (default 2.3)
report version triggers at or above version x.y in verbose mode
-v or --verbose
print more detailed report of version triggers for each version
""" % sys.argv[0]
sys.exit(1)
for fn in files:
try:
f = open(fn)
source = f.read()
f.close()
ver = get_versions(source)
if Verbose:
print fn
for v in sorted([k for k in ver.keys() if k >= MinVersion], reverse=True):
reasons = [x for x in uniq(ver[v]) if x]
if reasons:
# each reason is (lineno, message)
print "\t%s\t%s" % (".".join(map(str, v)), ", ".join([x[1] for x in reasons]))
elif Lint:
for v in sorted([k for k in ver.keys() if k >= MinVersion], reverse=True):
reasons = [x for x in uniq(ver[v]) if x]
for r in reasons:
# each reason is (lineno, message)
print "%s:%s: %s %s" % (fn, r[0], ".".join(map(str, v)), r[1])
else:
print "%s\t%s" % (".".join(map(str, max(ver.keys()))), fn)
except SyntaxError, x:
print "%s: syntax error compiling with Python %s: %s" % (fn, platform.python_version(), x)

View File

@ -124,8 +124,19 @@ def expand_user(path):
return path.replace('%u', username)
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)
@contextmanager
def working_dir(dirname):
def working_dir(dirname, **kwargs):
if kwargs.get('create', False):
mkdirp(dirname)
orig_dir = os.getcwd()
os.chdir(dirname)
yield
@ -137,14 +148,6 @@ def touch(path):
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:

View File

@ -119,9 +119,8 @@ def caller_locals():
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.
"""Make sure that the caller is a class definition, and return the
module's name.
"""
stack = inspect.stack()
try:
@ -144,8 +143,9 @@ def get_calling_package_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__))
raise RequiredAttributeError(
"No required attribute '%s' in class '%s'"
% (attr_name, obj.__class__.__name__))
def attr_setdefault(obj, name, value):
@ -259,3 +259,8 @@ def in_function(function_name):
return False
finally:
del stack
class RequiredAttributeError(ValueError):
def __init__(self, message):
super(RequiredAttributeError, self).__init__(message)

View File

@ -22,20 +22,6 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
#
# When packages call 'from spack import *', this is what is brought in.
#
# Spack internal code calls 'import spack' and accesses other
# variables (spack.db, paths, etc.) directly.
#
# TODO: maybe this should be separated out and should go in build_environment.py?
# TODO: it's not clear where all the stuff that needs to be included in packages
# should live. This file is overloaded for spack core vs. for packages.
__all__ = ['Package', 'when', 'provides', 'depends_on',
'patch', 'Version', 'working_dir', 'which', 'Executable',
'filter_file', 'change_sed_delimiter']
import os
import tempfile
from llnl.util.filesystem import *
@ -58,7 +44,6 @@
stage_path = join_path(var_path, "stage")
install_path = join_path(prefix, "opt")
share_path = join_path(prefix, "share", "spack")
dotkit_path = join_path(share_path, "dotkit")
#
# Set up the packages database.
@ -81,7 +66,7 @@
# stage directories.
#
from spack.directory_layout import SpecHashDirectoryLayout
install_layout = SpecHashDirectoryLayout(install_path, prefix_size=6)
install_layout = SpecHashDirectoryLayout(install_path)
#
# This controls how things are concretized in spack.
@ -141,11 +126,30 @@
#
sys_type = None
#
# Extra imports that should be generally usable from package.py files.
# When packages call 'from spack import *', this extra stuff is brought in.
#
from llnl.util.filesystem import working_dir
# Spack internal code should call 'import spack' and accesses other
# variables (spack.db, paths, etc.) directly.
#
# TODO: maybe this should be separated out and should go in build_environment.py?
# TODO: it's not clear where all the stuff that needs to be included in packages
# should live. This file is overloaded for spack core vs. for packages.
#
__all__ = ['Package', 'Version', 'when']
from spack.package import Package
from spack.relations import depends_on, provides, patch
from spack.multimethod import when
from spack.version import Version
from spack.multimethod import when
import llnl.util.filesystem
from llnl.util.filesystem import *
__all__ += llnl.util.filesystem.__all__
import spack.relations
from spack.relations import *
__all__ += spack.relations.__all__
import spack.util.executable
from spack.util.executable import *
__all__ += spack.util.executable.__all__

View File

@ -84,7 +84,7 @@ def __call__(self, *args, **kwargs):
def set_compiler_environment_variables(pkg):
assert(pkg.spec.concrete)
compiler = compilers.compiler_for_spec(pkg.spec.compiler)
compiler = pkg.compiler
# Set compiler variables used by CMake and autotools
os.environ['CC'] = 'cc'
@ -122,7 +122,7 @@ def set_build_environment_variables(pkg):
# Prefixes of all of the package's dependencies go in
# SPACK_DEPENDENCIES
dep_prefixes = [d.package.prefix for d in pkg.spec.dependencies.values()]
dep_prefixes = [d.prefix for d in pkg.spec.traverse(root=False)]
path_set(SPACK_DEPENDENCIES, dep_prefixes)
# Install prefix
@ -143,6 +143,18 @@ def set_build_environment_variables(pkg):
os.environ[SPACK_SPEC] = str(pkg.spec)
os.environ[SPACK_DEBUG_LOG_DIR] = spack.spack_working_dir
# Add dependencies to CMAKE_PREFIX_PATH
path_set("CMAKE_PREFIX_PATH", dep_prefixes)
# Add any pkgconfig directories to PKG_CONFIG_PATH
pkg_config_dirs = []
for p in dep_prefixes:
for libdir in ('lib', 'lib64'):
pcdir = join_path(p, libdir, 'pkgconfig')
if os.path.isdir(pcdir):
pkg_config_dirs.append(pcdir)
path_set("PKG_CONFIG_PATH", pkg_config_dirs)
def set_module_variables_for_package(pkg):
"""Populate the module scope of install() with some useful functions.
@ -153,6 +165,9 @@ def set_module_variables_for_package(pkg):
m.make = MakeExecutable('make', pkg.parallel)
m.gmake = MakeExecutable('gmake', pkg.parallel)
# easy shortcut to os.environ
m.env = os.environ
# number of jobs spack prefers to build with.
m.make_jobs = multiprocessing.cpu_count()
@ -168,7 +183,7 @@ def set_module_variables_for_package(pkg):
# standard CMake arguments
m.std_cmake_args = ['-DCMAKE_INSTALL_PREFIX=%s' % pkg.prefix,
'-DCMAKE_BUILD_TYPE=None']
'-DCMAKE_BUILD_TYPE=RelWithDebInfo']
if platform.mac_ver()[0]:
m.std_cmake_args.append('-DCMAKE_FIND_FRAMEWORK=LAST')

View File

@ -23,12 +23,13 @@
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import os
from subprocess import check_call, check_output
from subprocess import check_call
import llnl.util.tty as tty
from llnl.util.filesystem import join_path
from llnl.util.filesystem import join_path, mkdirp
import spack
from spack.util.executable import which
description = "Create a new installation of spack in another prefix"
@ -38,8 +39,10 @@ def setup_parser(subparser):
def get_origin_url():
git_dir = join_path(spack.prefix, '.git')
origin_url = check_output(
['git', '--git-dir=%s' % git_dir, 'config', '--get', 'remote.origin.url'])
git = which('git', required=True)
origin_url = git(
'--git-dir=%s' % git_dir, 'config', '--get', 'remote.origin.url',
return_output=True)
return origin_url.strip()
@ -49,6 +52,11 @@ def bootstrap(parser, args):
tty.msg("Fetching spack from origin: %s" % origin_url)
if os.path.isfile(prefix):
tty.die("There is already a file at %s" % prefix)
mkdirp(prefix)
if os.path.exists(join_path(prefix, '.git')):
tty.die("There already seems to be a git repository in %s" % prefix)
@ -62,10 +70,11 @@ def bootstrap(parser, args):
"%s/lib/spack/..." % prefix)
os.chdir(prefix)
check_call(['git', 'init', '--shared', '-q'])
check_call(['git', 'remote', 'add', 'origin', origin_url])
check_call(['git', 'fetch', 'origin', 'master:refs/remotes/origin/master', '-n', '-q'])
check_call(['git', 'reset', '--hard', 'origin/master', '-q'])
git = which('git', required=True)
git('init', '--shared', '-q')
git('remote', 'add', 'origin', origin_url)
git('fetch', 'origin', 'master:refs/remotes/origin/master', '-n', '-q')
git('reset', '--hard', 'origin/master', '-q')
tty.msg("Successfully created a new spack in %s" % prefix,
"Run %s/bin/spack to use this installation." % prefix)

38
lib/spack/spack/cmd/cd.py Normal file
View File

@ -0,0 +1,38 @@
##############################################################################
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import spack.cmd.location
import spack.modules
description="cd to spack directories in the shell."
def setup_parser(subparser):
"""This is for decoration -- spack cd is used through spack's
shell support. This allows spack cd to print a descriptive
help message when called with -h."""
spack.cmd.location.setup_parser(subparser)
def cd(parser, args):
spack.modules.print_help()

View File

@ -24,7 +24,7 @@
##############################################################################
import os
import re
import argparse
from external import argparse
import hashlib
from pprint import pprint
from subprocess import CalledProcessError

View File

@ -22,7 +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 argparse
from external import argparse
import llnl.util.tty as tty

View File

@ -22,7 +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 argparse
from external import argparse
import llnl.util.tty as tty
from llnl.util.tty.colify import colify

View File

@ -23,7 +23,7 @@
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import sys
import argparse
from external import argparse
import llnl.util.tty as tty

View File

@ -70,7 +70,7 @@ class ${class_name}(Package):
homepage = "http://www.example.com"
url = "${url}"
versions = ${versions}
${versions}
def install(self, spec, prefix):
# FIXME: Modify the configure line to suit your build system here.
@ -87,6 +87,9 @@ def setup_parser(subparser):
subparser.add_argument(
'--keep-stage', action='store_true', dest='keep_stage',
help="Don't clean up staging area when command completes.")
subparser.add_argument(
'-n', '--name', dest='alternate_name', default=None,
help="Override the autodetected name for the created package.")
subparser.add_argument(
'-f', '--force', action='store_true', dest='force',
help="Overwrite any existing package file with the same name.")
@ -114,25 +117,11 @@ def __call__(self, stage):
self.configure = '%s\n # %s' % (autotools, cmake)
def make_version_dict(ver_hash_tuples):
max_len = max(len(str(v)) for v,hfg in ver_hash_tuples)
width = max_len + 2
format = "%-" + str(width) + "s : '%s',"
sep = '\n '
return '{ ' + sep.join(format % ("'%s'" % v, h)
for v, h in ver_hash_tuples) + ' }'
def get_name():
"""Prompt user to input a package name."""
name = ""
while not name:
new_name = raw_input("Name: ")
if spack.db.valid_name(name):
name = new_name
else:
print "Package name can only contain A-Z, a-z, 0-9, '_' and '-'"
return name
def make_version_calls(ver_hash_tuples):
"""Adds a version() call to the package for each version found."""
max_len = max(len(str(v)) for v, h in ver_hash_tuples)
format = " version(%%-%ds, '%%s')" % (max_len + 2)
return '\n'.join(format % ("'%s'" % v, h) for v, h in ver_hash_tuples)
def create(parser, args):
@ -140,13 +129,22 @@ def create(parser, args):
# Try to deduce name and version of the new package from the URL
name, version = spack.url.parse_name_and_version(url)
if not name:
tty.msg("Couldn't guess a name for this package.")
name = get_name()
# Use a user-supplied name if one is present
name = kwargs.get(args, 'alternate_name', False)
if args.name:
name = args.name
if not version:
tty.die("Couldn't guess a version string from %s." % url)
if not name:
tty.die("Couldn't guess a name for this package. Try running:", "",
"spack create --name <name> <url>")
if not spack.db.valid_name(name):
tty.die("Package name can only contain A-Z, a-z, 0-9, '_' and '-'")
tty.msg("This looks like a URL for %s version %s." % (name, version))
tty.msg("Creating template for package %s" % name)
@ -195,7 +193,7 @@ def create(parser, args):
configure=guesser.configure,
class_name=mod_to_class(name),
url=url,
versions=make_version_dict(ver_hash_tuples)))
versions=make_version_calls(ver_hash_tuples)))
# If everything checks out, go ahead and edit.
spack.editor(pkg_path)

View File

@ -22,14 +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
##############################################################################
import argparse
from external import argparse
import llnl.util.tty as tty
import spack
import spack.cmd
description = "Show dependent packages."
description = "Show installed packages that depend on another."
def setup_parser(subparser):
subparser.add_argument(
@ -42,5 +42,5 @@ def dependents(parser, args):
tty.die("spack dependents takes only one spec.")
fmt = '$_$@$%@$+$=$#'
deps = [d.format(fmt) for d in specs[0].package.installed_dependents]
tty.msg("Dependents of %s" % specs[0].format(fmt), *deps)
deps = [d.format(fmt, color=True) for d in specs[0].package.installed_dependents]
tty.msg("Dependents of %s" % specs[0].format(fmt, color=True), *deps)

View File

@ -1,99 +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 os
import shutil
import argparse
import llnl.util.tty as tty
from llnl.util.lang import partition_list
from llnl.util.filesystem import mkdirp
import spack.cmd
import spack.hooks.dotkit
from spack.spec import Spec
description ="Find dotkits for packages if they exist."
def setup_parser(subparser):
subparser.add_argument(
'--refresh', action='store_true', help='Regenerate all dotkits')
subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help='spec to find a dotkit for.')
def dotkit_find(parser, args):
if not args.spec:
parser.parse_args(['dotkit', '-h'])
spec = spack.cmd.parse_specs(args.spec)
if len(spec) > 1:
tty.die("You can only pass one spec.")
spec = spec[0]
if not spack.db.exists(spec.name):
tty.die("No such package: %s" % spec.name)
specs = [s for s in spack.db.installed_package_specs() if s.satisfies(spec)]
if len(specs) == 0:
tty.die("No installed packages match spec %s" % spec)
if len(specs) > 1:
tty.error("Multiple matches for spec %s. Choose one:" % spec)
for s in specs:
sys.stderr.write(s.tree(color=True))
sys.exit(1)
match = specs[0]
if not os.path.isfile(spack.hooks.dotkit.dotkit_file(match.package)):
tty.die("No dotkit is installed for package %s." % spec)
print match.format('$_$@$+$%@$=$#')
def dotkit_refresh(parser, args):
query_specs = spack.cmd.parse_specs(args.spec)
specs = spack.db.installed_package_specs()
if query_specs:
specs = [s for s in specs
if any(s.satisfies(q) for q in query_specs)]
else:
shutil.rmtree(spack.dotkit_path, ignore_errors=False)
mkdirp(spack.dotkit_path)
for spec in specs:
spack.hooks.dotkit.post_install(spec.package)
def dotkit(parser, args):
if args.refresh:
dotkit_refresh(parser, args)
else:
dotkit_find(parser, args)

View File

@ -44,7 +44,7 @@ class ${class_name}(Package):
homepage = "http://www.example.com"
url = "http://www.example.com/${name}-1.0.tar.gz"
versions = { '1.0' : '0123456789abcdef0123456789abcdef' }
version('1.0', '0123456789abcdef0123456789abcdef')
def install(self, spec, prefix):
configure("--prefix=%s" % prefix)

View File

@ -22,7 +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 argparse
from external import argparse
import spack
import spack.cmd

View File

@ -24,7 +24,7 @@
##############################################################################
import sys
import collections
import argparse
from external import argparse
from StringIO import StringIO
import llnl.util.tty as tty
@ -89,7 +89,7 @@ def find(parser, args):
format = " %-{}s%s".format(width)
for abbrv, spec in zip(abbreviated, specs):
print format % (abbrv, spec.package.prefix)
print format % (abbrv, spec.prefix)
elif args.full_specs:
for spec in specs:

View File

@ -37,15 +37,17 @@ def info(parser, args):
package = spack.db.get(args.name)
print "Package: ", package.name
print "Homepage: ", package.homepage
print "Download: ", package.url
print
print "Safe versions: "
if package.versions:
colify(reversed(sorted(package.versions)), indent=4)
if not package.versions:
print("None.")
else:
print "None. Use spack versions %s to get a list of downloadable versions." % package.name
maxlen = max(len(str(v)) for v in package.versions)
fmt = "%%-%ss" % maxlen
for v in reversed(sorted(package.versions)):
print " " + (fmt % v) + " " + package.url_for_version(v)
print
print "Dependencies:"

View File

@ -23,7 +23,7 @@
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import sys
import argparse
from external import argparse
import spack
import spack.cmd

View File

@ -0,0 +1,38 @@
##############################################################################
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by David Beckingsale, david@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from external import argparse
import spack.modules
description ="Add package to environment using modules."
def setup_parser(subparser):
"""Parser is only constructed so that this prints a nice help
message with -h. """
subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help='Spec of package to load with modules.')
def load(parser, args):
spack.modules.print_help()

View File

@ -0,0 +1,93 @@
##############################################################################
# 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
from external import argparse
import llnl.util.tty as tty
from llnl.util.filesystem import join_path
import spack
import spack.cmd
description="Print out locations of various diectories used by Spack"
def setup_parser(subparser):
global directories
directories = subparser.add_mutually_exclusive_group()
directories.add_argument(
'-m', '--module-dir', action='store_true', help="Spack python module directory.")
directories.add_argument(
'-r', '--spack-root', action='store_true', help="Spack installation root.")
directories.add_argument(
'-i', '--install-dir', action='store_true',
help="Install prefix for spec (spec need not be installed).")
directories.add_argument(
'-p', '--package-dir', action='store_true',
help="Directory enclosing a spec's package.py file.")
directories.add_argument(
'-s', '--stage-dir', action='store_true', help="Stage directory for a spec.")
directories.add_argument(
'-b', '--build-dir', action='store_true',
help="Expanded archive directory for a spec (requires it to be staged first).")
subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help="spec of package to fetch directory for.")
def location(parser, args):
if args.module_dir:
print spack.module_path
elif args.spack_root:
print spack.prefix
else:
specs = spack.cmd.parse_specs(args.spec, concretize=True)
if not specs:
tty.die("You must supply a spec.")
if len(specs) != 1:
tty.die("Too many specs. Need only one.")
spec = specs[0]
if args.install_dir:
print spec.prefix
elif args.package_dir:
print join_path(spack.db.root, spec.name)
else:
pkg = spack.db.get(spec)
if args.stage_dir:
print pkg.stage.path
else: # args.build_dir is the default.
if not os.listdir(pkg.stage.path):
tty.die("Build directory does not exist yet. Run this to create it:",
"spack stage " + " ".join(args.spec))
print pkg.stage.expanded_archive_path

View File

@ -24,10 +24,10 @@
##############################################################################
import os
import shutil
import argparse
from datetime import datetime
from contextlib import closing
from external import argparse
import llnl.util.tty as tty
from llnl.util.tty.colify import colify
from llnl.util.filesystem import mkdirp, join_path
@ -41,7 +41,7 @@
from spack.util.compression import extension
description = "Manage spack mirrors."
description = "Manage mirrors."
def setup_parser(subparser):
subparser.add_argument(

View File

@ -0,0 +1,107 @@
##############################################################################
# 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 os
import shutil
from external import argparse
import llnl.util.tty as tty
from llnl.util.lang import partition_list
from llnl.util.filesystem import mkdirp
import spack.cmd
from spack.modules import module_types
from spack.util.string import *
from spack.spec import Spec
description ="Manipulate modules and dotkits."
def setup_parser(subparser):
sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='module_command')
refresh_parser = sp.add_parser('refresh', help='Regenerate all module files.')
find_parser = sp.add_parser('find', help='Find module files for packages.')
find_parser.add_argument(
'module_type', help="Type of module to find file for. [" + '|'.join(module_types) + "]")
find_parser.add_argument('spec', nargs='+', help='spec to find a module file for.')
def module_find(mtype, spec_array):
"""Look at all installed packages and see if the spec provided
matches any. If it does, check whether there is a module file
of type <mtype> there, and print out the name that the user
should type to use that package's module.
"""
if mtype not in module_types:
tty.die("Invalid module type: '%s'. Options are %s." % (mtype, comma_or(module_types)))
specs = spack.cmd.parse_specs(spec_array)
if len(specs) > 1:
tty.die("You can only pass one spec.")
spec = specs[0]
specs = [s for s in spack.db.installed_package_specs() if s.satisfies(spec)]
if len(specs) == 0:
tty.die("No installed packages match spec %s" % spec)
if len(specs) > 1:
tty.error("Multiple matches for spec %s. Choose one:" % spec)
for s in specs:
sys.stderr.write(s.tree(color=True))
sys.exit(1)
mt = module_types[mtype]
mod = mt(specs[0])
if not os.path.isfile(mod.file_name):
tty.die("No %s module is installed for %s." % (mtype, spec))
print mod.use_name
def module_refresh():
"""Regenerate all module files for installed packages known to
spack (some packages may no longer exist)."""
specs = [s for s in spack.db.installed_known_package_specs()]
for name, cls in module_types.items():
tty.msg("Regenerating %s module files." % name)
if os.path.isdir(cls.path):
shutil.rmtree(cls.path, ignore_errors=False)
mkdirp(cls.path)
for spec in specs:
tty.debug(" Writing file for %s." % spec)
cls(spec).write()
def module(parser, args):
if args.module_command == 'refresh':
module_refresh()
elif args.module_command == 'find':
module_find(args.module_type, args.spec)

View File

@ -22,7 +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 argparse
from external import argparse
import spack.cmd
import spack

124
lib/spack/spack/cmd/pkg.py Normal file
View File

@ -0,0 +1,124 @@
##############################################################################
# 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
from external import argparse
import llnl.util.tty as tty
from llnl.util.tty.colify import colify
import spack
from spack.util.executable import *
description = "Query packages associated with particular git revisions in spack."
def setup_parser(subparser):
sp = subparser.add_subparsers(
metavar='SUBCOMMAND', dest='pkg_command')
list_parser = sp.add_parser('list', help=pkg_list.__doc__)
list_parser.add_argument('rev', default='HEAD', nargs='?',
help="Revision to list packages for.")
diff_parser = sp.add_parser('diff', help=pkg_diff.__doc__)
diff_parser.add_argument('rev1', nargs='?', default='HEAD^',
help="Revision to compare against.")
diff_parser.add_argument('rev2', nargs='?', default='HEAD',
help="Revision to compare to rev1 (default is HEAD).")
add_parser = sp.add_parser('added', help=pkg_added.__doc__)
add_parser.add_argument('rev1', nargs='?', default='HEAD^',
help="Revision to compare against.")
add_parser.add_argument('rev2', nargs='?', default='HEAD',
help="Revision to compare to rev1 (default is HEAD).")
rm_parser = sp.add_parser('removed', help=pkg_removed.__doc__)
rm_parser.add_argument('rev1', nargs='?', default='HEAD^',
help="Revision to compare against.")
rm_parser.add_argument('rev2', nargs='?', default='HEAD',
help="Revision to compare to rev1 (default is HEAD).")
def get_git():
# cd to spack prefix to do git operations
os.chdir(spack.prefix)
# If this is a non-git version of spack, give up.
if not os.path.isdir('.git'):
tty.die("No git repo in %s. Can't use 'spack pkg'" % spack.prefix)
return which("git", required=True)
def list_packages(rev):
git = get_git()
relpath = spack.packages_path[len(spack.prefix + os.path.sep):] + os.path.sep
output = git('ls-tree', '--full-tree', '--name-only', rev, relpath,
return_output=True)
return sorted(line[len(relpath):] for line in output.split('\n') if line)
def pkg_list(args):
"""List packages associated with a particular spack git revision."""
colify(list_packages(args.rev))
def diff_packages(rev1, rev2):
p1 = set(list_packages(rev1))
p2 = set(list_packages(rev2))
return p1.difference(p2), p2.difference(p1)
def pkg_diff(args):
"""Compare packages available in two different git revisions."""
u1, u2 = diff_packages(args.rev1, args.rev2)
if u1:
print "%s:" % args.rev1
colify(sorted(u1), indent=4)
if u1: print
if u2:
print "%s:" % args.rev2
colify(sorted(u2), indent=4)
def pkg_removed(args):
"""Show packages removed since a commit."""
u1, u2 = diff_packages(args.rev1, args.rev2)
if u1: colify(sorted(u1))
def pkg_added(args):
"""Show packages added since a commit."""
u1, u2 = diff_packages(args.rev1, args.rev2)
if u2: colify(sorted(u2))
def pkg(parser, args):
action = { 'diff' : pkg_diff,
'list' : pkg_list,
'removed' : pkg_removed,
'added' : pkg_added }
action[args.pkg_command](args)

View File

@ -23,7 +23,7 @@
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import os
import argparse
from external import argparse
from llnl.util.tty.colify import colify

View File

@ -25,7 +25,7 @@
import os
import sys
import code
import argparse
from external import argparse
import platform
from contextlib import closing

View File

@ -22,7 +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 argparse
from external import argparse
import spack.cmd
import llnl.util.tty as tty

View File

@ -22,8 +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 argparse
import os
from external import argparse
import llnl.util.tty as tty
import spack
import spack.cmd
@ -33,18 +35,21 @@ def setup_parser(subparser):
subparser.add_argument(
'-n', '--no-checksum', action='store_true', dest='no_checksum',
help="Do not check downloaded packages against checksum")
dir_parser = subparser.add_mutually_exclusive_group()
subparser.add_argument(
'packages', nargs=argparse.REMAINDER, help="specs of packages to stage")
'specs', nargs=argparse.REMAINDER, help="specs of packages to stage")
def stage(parser, args):
if not args.packages:
if not args.specs:
tty.die("stage requires at least one package argument")
if args.no_checksum:
spack.do_checksum = False
specs = spack.cmd.parse_specs(args.packages, concretize=True)
specs = spack.cmd.parse_specs(args.specs, concretize=True)
for spec in specs:
package = spack.db.get(spec)
package.do_stage()

View File

@ -22,12 +22,13 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import argparse
from external import argparse
import llnl.util.tty as tty
import spack
import spack.cmd
import spack.packages
description="Remove an installed package"
@ -68,7 +69,15 @@ def uninstall(parser, args):
if len(matching_specs) == 0:
tty.die("%s does not match any installed packages." % spec)
pkgs.extend(spack.db.get(s) for s in matching_specs)
for s in matching_specs:
try:
# should work if package is known to spack
pkgs.append(spack.db.get(s))
except spack.packages.UnknownPackageError, e:
# The package.py file has gone away -- but still want to uninstall.
spack.Package(s).do_uninstall(force=True)
# Sort packages to be uninstalled by the number of installed dependents
# This ensures we do things in the right order

View File

@ -0,0 +1,38 @@
##############################################################################
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by David Beckingsale, david@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from external import argparse
import spack.modules
description ="Remove package from environment using module."
def setup_parser(subparser):
"""Parser is only constructed so that this prints a nice help
message with -h. """
subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help='Spec of package to unload with modules.')
def unload(parser, args):
spack.modules.print_help()

View File

@ -22,15 +22,17 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import argparse
import spack.cmd.use
from external import argparse
import spack.modules
description ="Remove package from environment using dotkit."
def setup_parser(subparser):
"""Parser is only constructed so that this prints a nice help
message with -h. """
subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help='Spec of package to remove.')
'spec', nargs=argparse.REMAINDER, help='Spec of package to unuse with dotkit.')
def unuse(parser, args):
spack.cmd.use.print_help()
spack.modules.print_help()

View File

@ -22,29 +22,17 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import argparse
import llnl.util.tty as tty
import spack
from external import argparse
import spack.modules
description ="Add package to environment using dotkit."
def setup_parser(subparser):
"""Parser is only constructed so that this prints a nice help
message with -h. """
subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help='Spec of package to add.')
def print_help():
tty.msg("Spack dotkit support is not initialized.",
"",
"To use dotkit with Spack, you must first run the command",
"below, which you can copy and paste:",
"",
"For bash:",
" . %s/setup-env.bash" % spack.share_path,
"",
"ksh/csh/tcsh shells are currently unsupported",
"")
'spec', nargs=argparse.REMAINDER, help='Spec of package to use with dotkit.')
def use(parser, args):
print_help()
spack.modules.print_help()

View File

@ -94,6 +94,9 @@ class Compiler(object):
# Names of generic arguments used by this compiler
arg_rpath = '-Wl,-rpath,%s'
# argument used to get C++11 options
cxx11_flag = "-std=c++11"
def __init__(self, cspec, cc, cxx, f77, fc):
def check(exe):
@ -189,7 +192,7 @@ def check(key):
return None
successful = [key for key in parmap(check, checks) if key is not None]
return { (v, p, s) : path for v, p, s, path in successful }
return dict(((v, p, s), path) for v, p, s, path in successful)
@classmethod
def find(cls, *path):

View File

@ -176,7 +176,7 @@ def compilers_for_spec(compiler_spec):
config = _get_config()
def get_compiler(cspec):
items = { k:v for k,v in config.items('compiler "%s"' % cspec) }
items = dict((k,v) for k,v in config.items('compiler "%s"' % cspec))
if not all(n in items for n in _required_instance_vars):
raise InvalidCompilerConfigurationError(cspec)

View File

@ -22,7 +22,9 @@
# 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 spack.compiler import *
from spack.version import ver
class Gcc(Compiler):
# Subclasses use possible names of C compiler
@ -40,6 +42,15 @@ class Gcc(Compiler):
# MacPorts builds gcc versions with prefixes and -mp-X.Y suffixes.
suffixes = [r'-mp-\d\.\d']
@property
def cxx11_flag(self):
if self.version < ver('4.3'):
tty.die("Only gcc 4.3 and above support c++11.")
elif self.version < ver('4.7'):
return "-std=gnu++0x"
else:
return "-std=gnu++11"
@classmethod
def fc_version(cls, fc):
return get_compiler_version(

View File

@ -37,6 +37,15 @@ class Intel(Compiler):
# Subclasses use possible names of Fortran 90 compiler
fc_names = ['ifort']
@property
def cxx11_flag(self):
if self.version < ver('11.1'):
tty.die("Only intel 11.1 and above support c++11.")
elif self.version < ver('13'):
return "-std=c++0x"
else:
return "-std=c++11"
@classmethod
def default_version(cls, comp):

View File

@ -72,7 +72,7 @@ def concretize_version(self, spec):
if valid_versions:
spec.versions = ver([valid_versions[-1]])
else:
spec.versions = ver([pkg.default_version])
raise NoValidVerionError(spec)
def concretize_architecture(self, spec):
@ -118,7 +118,7 @@ def concretize_compiler(self, spec):
return
try:
nearest = next(p for p in spec.preorder_traversal(direction='parents')
nearest = next(p for p in spec.traverse(direction='parents')
if p.compiler is not None).compiler
if not nearest in all_compilers:
@ -158,3 +158,11 @@ def __init__(self, compiler_spec):
super(UnavailableCompilerVersionError, self).__init__(
"No available compiler version matches '%s'" % compiler_spec,
"Run 'spack compilers' to see available compiler Options.")
class NoValidVerionError(spack.error.SpackError):
"""Raised when there is no available version for a package that
satisfies a spec."""
def __init__(self, spec):
super(NoValidVerionError, self).__init__(
"No available version of %s matches '%s'" % (spec.name, spec.versions))

View File

@ -84,10 +84,9 @@
import re
import inspect
import ConfigParser as cp
from collections import OrderedDict
from external.ordereddict import OrderedDict
from llnl.util.lang import memoized
import spack.error
__all__ = [
@ -222,7 +221,6 @@ class SpackConfigParser(cp.RawConfigParser):
"""
# Slightly modify Python option expressions to allow leading whitespace
OPTCRE = re.compile(r'\s*' + cp.RawConfigParser.OPTCRE.pattern)
OPTCRE_NV = re.compile(r'\s*' + cp.RawConfigParser.OPTCRE_NV.pattern)
def __init__(self, file_or_files):
cp.RawConfigParser.__init__(self, dict_type=OrderedDict)
@ -341,14 +339,13 @@ def write(self, path_or_fp=None):
def _read(self, fp, fpname):
"""This is a copy of Python 2.7's _read() method, with support for
continuation lines removed.
"""
cursect = None # None, or a dictionary
"""This is a copy of Python 2.6's _read() method, with support for
continuation lines removed."""
cursect = None # None, or a dictionary
optname = None
lineno = 0
comment = 0
e = None # None, or an exception
lineno = 0
e = None # None, or an exception
while True:
line = fp.readline()
if not line:
@ -359,7 +356,6 @@ def _read(self, fp, fpname):
(line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR")):
self._sections["comment-%d" % comment] = line
comment += 1
continue
# a section header or option header?
else:
# is it a section header?
@ -381,27 +377,21 @@ def _read(self, fp, fpname):
raise cp.MissingSectionHeaderError(fpname, lineno, line)
# an option line?
else:
mo = self._optcre.match(line)
mo = self.OPTCRE.match(line)
if mo:
optname, vi, optval = mo.group('option', 'vi', 'value')
if vi in ('=', ':') and ';' in optval:
# ';' is a comment delimiter only if it follows
# a spacing character
pos = optval.find(';')
if pos != -1 and optval[pos-1].isspace():
optval = optval[:pos]
optval = optval.strip()
# allow empty values
if optval == '""':
optval = ''
optname = self.optionxform(optname.rstrip())
# This check is fine because the OPTCRE cannot
# match if it would set optval to None
if optval is not None:
if vi in ('=', ':') and ';' in optval:
# ';' is a comment delimiter only if it follows
# a spacing character
pos = optval.find(';')
if pos != -1 and optval[pos-1].isspace():
optval = optval[:pos]
optval = optval.strip()
# allow empty values
if optval == '""':
optval = ''
cursect[optname] = [optval]
else:
# valueless option handling
cursect[optname] = optval
cursect[optname] = optval
else:
# a non-fatal parsing error occurred. set up the
# exception but keep going. the exception will be
@ -414,23 +404,13 @@ def _read(self, fp, fpname):
if e:
raise e
# join the multi-line values collected while reading
all_sections = [self._defaults]
all_sections.extend(self._sections.values())
for options in all_sections:
# skip comments
if isinstance(options, basestring):
continue
for name, val in options.items():
if isinstance(val, list):
options[name] = '\n'.join(val)
def _write(self, fp):
"""Write an .ini-format representation of the configuration state.
This is taken from the default Python 2.7 source. It writes 4
This is taken from the default Python 2.6 source. It writes 4
spaces at the beginning of lines instead of no leading space.
"""
if self._defaults:
@ -449,11 +429,9 @@ def _write(self, fp):
# Allow leading whitespace
fp.write("[%s]\n" % section)
for (key, value) in self._sections[section].items():
if key == "__name__":
continue
if (value is not None) or (self._optcre == self.OPTCRE):
key = " = ".join((key, str(value).replace('\n', '\n\t')))
fp.write(" %s\n" % (key))
if key != "__name__":
fp.write(" %s = %s\n" %
(key, str(value).replace('\n', '\n\t')))
class SpackConfigurationError(spack.error.SpackError):

View File

@ -29,7 +29,10 @@
import shutil
from contextlib import closing
import llnl.util.tty as tty
from llnl.util.filesystem import join_path, mkdirp
import spack
from spack.spec import Spec
from spack.error import SpackError
@ -131,12 +134,9 @@ def __init__(self, root, **kwargs):
"""Prefix size is number of characters in the SHA-1 prefix to use
to make each hash unique.
"""
prefix_size = kwargs.get('prefix_size', 8)
spec_file = kwargs.get('spec_file', '.spec')
spec_file_name = kwargs.get('spec_file_name', '.spec')
super(SpecHashDirectoryLayout, self).__init__(root)
self.prefix_size = prefix_size
self.spec_file = spec_file
self.spec_file_name = spec_file_name
def relative_path_for_spec(self, spec):
@ -154,15 +154,36 @@ def write_spec(self, spec, path):
def read_spec(self, path):
"""Read the contents of a file and parse them as a spec"""
with closing(open(path)) as spec_file:
string = spec_file.read().replace('\n', '')
return Spec(string)
# Specs from files are assumed normal and concrete
spec = Spec(spec_file.read().replace('\n', ''))
# If we do not have a package on hand for this spec, we know
# it is concrete, and we *assume* that it is normal. This
# prevents us from trying to fetch a non-existing package, and
# allows best effort for commands like spack find.
if not spack.db.exists(spec.name):
spec._normal = True
spec._concrete = True
else:
spec.normalize()
if not spec.concrete:
tty.warn("Spec read from installed package is not concrete:",
path, spec)
return spec
def spec_file_path(self, spec):
"""Gets full path to spec file"""
_check_concrete(spec)
return join_path(self.path_for_spec(spec), self.spec_file_name)
def make_path_for_spec(self, spec):
_check_concrete(spec)
path = self.path_for_spec(spec)
spec_file_path = join_path(path, self.spec_file)
spec_file_path = self.spec_file_path(spec)
if os.path.isdir(path):
if not os.path.isfile(spec_file_path):
@ -176,8 +197,7 @@ def make_path_for_spec(self, spec):
spec_hash = self.hash_spec(spec)
installed_hash = self.hash_spec(installed_spec)
if installed_spec == spec_hash:
raise SpecHashCollisionError(
installed_hash, spec_hash, self.prefix_size)
raise SpecHashCollisionError(installed_hash, spec_hash)
else:
raise InconsistentInstallDirectoryError(
'Spec file in %s does not match SHA-1 hash!'
@ -194,7 +214,7 @@ def all_specs(self):
for path in traverse_dirs_at_depth(self.root, 3):
arch, compiler, last_dir = path
spec_file_path = join_path(
self.root, arch, compiler, last_dir, self.spec_file)
self.root, arch, compiler, last_dir, self.spec_file_name)
if os.path.exists(spec_file_path):
spec = self.read_spec(spec_file_path)
yield spec
@ -208,10 +228,10 @@ def __init__(self, message):
class SpecHashCollisionError(DirectoryLayoutError):
"""Raised when there is a hash collision in an SpecHashDirectoryLayout."""
def __init__(self, installed_spec, new_spec, prefix_size):
def __init__(self, installed_spec, new_spec):
super(SpecHashDirectoryLayout, self).__init__(
'Specs %s and %s have the same %d character SHA-1 prefix!'
% prefix_size, installed_spec, new_spec)
'Specs %s and %s have the same SHA-1 prefix!'
% installed_spec, new_spec)
class InconsistentInstallDirectoryError(DirectoryLayoutError):

View File

@ -28,7 +28,8 @@ class SpackError(Exception):
Subclasses can be found in the modules they have to do with.
"""
def __init__(self, message, long_message=None):
super(SpackError, self).__init__(message)
super(SpackError, self).__init__()
self.message = message
self.long_message = long_message

View File

@ -22,62 +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
##############################################################################
import os
import re
import textwrap
import shutil
from contextlib import closing
from llnl.util.filesystem import join_path, mkdirp
import spack
def dotkit_file(pkg):
dk_file_name = pkg.spec.format('$_$@$%@$+$=$#') + ".dk"
return join_path(spack.dotkit_path, dk_file_name)
import spack.modules
def post_install(pkg):
if not os.path.exists(spack.dotkit_path):
mkdirp(spack.dotkit_path)
alterations = []
for var, path in [
('PATH', pkg.prefix.bin),
('MANPATH', pkg.prefix.man),
('MANPATH', pkg.prefix.share_man),
('LD_LIBRARY_PATH', pkg.prefix.lib),
('LD_LIBRARY_PATH', pkg.prefix.lib64)]:
if os.path.isdir(path):
alterations.append("dk_alter %s %s\n" % (var, path))
if not alterations:
return
alterations.append("dk_alter CMAKE_PREFIX_PATH %s\n" % pkg.prefix)
dk_file = dotkit_file(pkg)
with closing(open(dk_file, 'w')) as dk:
# Put everything in the spack category.
dk.write('#c spack\n')
dk.write('#d %s\n' % pkg.spec.format("$_ $@"))
# Recycle the description
if pkg.__doc__:
doc = re.sub(r'\s+', ' ', pkg.__doc__)
for line in textwrap.wrap(doc, 72):
dk.write("#h %s\n" % line)
# Write alterations
for alter in alterations:
dk.write(alter)
dk = spack.modules.Dotkit(pkg.spec)
dk.write()
def post_uninstall(pkg):
dk_file = dotkit_file(pkg)
if os.path.exists(dk_file):
shutil.rmtree(dk_file, ignore_errors=True)
dk = spack.modules.Dotkit(pkg.spec)
dk.remove()

View File

@ -0,0 +1,35 @@
##############################################################################
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by David Beckingsale, david@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import spack.modules
def post_install(pkg):
dk = spack.modules.TclModule(pkg.spec)
dk.write()
def post_uninstall(pkg):
dk = spack.modules.TclModule(pkg.spec)
dk.remove()

247
lib/spack/spack/modules.py Normal file
View File

@ -0,0 +1,247 @@
##############################################################################
# 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 module contains code for creating environment modules, which
can include dotkits, tcl modules, lmod, and others.
The various types of modules are installed by post-install hooks and
removed after an uninstall by post-uninstall hooks. This class
consolidates the logic for creating an abstract description of the
information that module systems need. Currently that includes a
number directories to be appended to paths in the user's environment:
* /bin directories to be appended to PATH
* /lib* directories for LD_LIBRARY_PATH
* /man* and /share/man* directories for LD_LIBRARY_PATH
* the package prefix for CMAKE_PREFIX_PATH
This module also includes logic for coming up with unique names for
the module files so that they can be found by the various
shell-support files in $SPACK/share/spack/setup-env.*.
Each hook in hooks/ implements the logic for writing its specific type
of module file.
"""
__all__ = ['EnvModule', 'Dotkit', 'TclModule']
import os
import re
import textwrap
import shutil
from contextlib import closing
import llnl.util.tty as tty
from llnl.util.filesystem import join_path, mkdirp
import spack
"""Registry of all types of modules. Entries created by EnvModule's
metaclass."""
module_types = {}
def print_help():
"""For use by commands to tell user how to activate shell support."""
tty.msg("This command requires spack's shell integration.",
"",
"To initialize spack's shell commands, you must run one of",
"the commands below. Choose the right command for your shell.",
"",
"For bash and zsh:",
" . %s/setup-env.sh" % spack.share_path,
"",
"For csh and tcsh:",
" setenv SPACK_ROOT %s" % spack.prefix,
" source %s/setup-env.csh" % spack.share_path,
"")
class EnvModule(object):
name = 'env_module'
class __metaclass__(type):
def __init__(cls, name, bases, dict):
type.__init__(cls, name, bases, dict)
if cls.name != 'env_module':
module_types[cls.name] = cls
def __init__(self, spec=None):
# category in the modules system
# TODO: come up with smarter category names.
self.category = "spack"
# Descriptions for the module system's UI
self.short_description = ""
self.long_description = ""
# dict pathname -> list of directories to be prepended to in
# the module file.
self._paths = None
self.spec = spec
@property
def paths(self):
if self._paths is None:
self._paths = {}
def add_path(path_name, directory):
path = self._paths.setdefault(path_name, [])
path.append(directory)
# Add paths if they exist.
for var, directory in [
('PATH', self.spec.prefix.bin),
('MANPATH', self.spec.prefix.man),
('MANPATH', self.spec.prefix.share_man),
('LD_LIBRARY_PATH', self.spec.prefix.lib),
('LD_LIBRARY_PATH', self.spec.prefix.lib64)]:
if os.path.isdir(directory):
add_path(var, directory)
# short description is just the package + version
# TODO: maybe packages can optionally provide it.
self.short_description = self.spec.format("$_ $@")
# long description is the docstring with reduced whitespace.
if self.spec.package.__doc__:
self.long_description = re.sub(r'\s+', ' ', self.spec.package.__doc__)
return self._paths
def write(self):
"""Write out a module file for this object."""
module_dir = os.path.dirname(self.file_name)
if not os.path.exists(module_dir):
mkdirp(module_dir)
# If there are no paths, no need for a dotkit.
if not self.paths:
return
with closing(open(self.file_name, 'w')) as f:
self._write(f)
def _write(self, stream):
"""To be implemented by subclasses."""
raise NotImplementedError()
@property
def file_name(self):
"""Subclasses should implement this to return the name of the file
where this module lives."""
raise NotImplementedError()
@property
def use_name(self):
"""Subclasses should implement this to return the name the
module command uses to refer to the package."""
raise NotImplementedError()
def remove(self):
mod_file = self.file_name
if os.path.exists(mod_file):
shutil.rmtree(mod_file, ignore_errors=True)
class Dotkit(EnvModule):
name = 'dotkit'
path = join_path(spack.share_path, "dotkit")
@property
def file_name(self):
return join_path(Dotkit.path, self.spec.architecture,
self.spec.format('$_$@$%@$+$#.dk'))
@property
def use_name(self):
return self.spec.format('$_$@$%@$+$#')
def _write(self, dk_file):
# Category
if self.category:
dk_file.write('#c %s\n' % self.category)
# Short description
if self.short_description:
dk_file.write('#d %s\n' % self.short_description)
# Long description
if self.long_description:
for line in textwrap.wrap(self.long_description, 72):
dk_file.write("#h %s\n" % line)
# Path alterations
for var, dirs in self.paths.items():
for directory in dirs:
dk_file.write("dk_alter %s %s\n" % (var, directory))
# Let CMake find this package.
dk_file.write("dk_alter CMAKE_PREFIX_PATH %s\n" % self.spec.prefix)
class TclModule(EnvModule):
name = 'tcl'
path = join_path(spack.share_path, "modules")
@property
def file_name(self):
return join_path(TclModule.path, self.spec.architecture, self.use_name)
@property
def use_name(self):
return self.spec.format('$_$@$%@$+$#')
def _write(self, m_file):
# TODO: cateogry?
m_file.write('#%Module1.0\n')
# Short description
if self.short_description:
m_file.write('module-whatis \"%s\"\n\n' % self.short_description)
# Long description
if self.long_description:
m_file.write('proc ModulesHelp { } {\n')
doc = re.sub(r'"', '\"', self.long_description)
m_file.write("puts stderr \"%s\"\n" % doc)
m_file.write('}\n\n')
# Path alterations
for var, dirs in self.paths.items():
for directory in dirs:
m_file.write("prepend-path %s \"%s\"\n" % (var, directory))
m_file.write("prepend-path CMAKE_PREFIX_PATH \"%s\"\n" % self.spec.prefix)

View File

@ -48,6 +48,7 @@
import spack
import spack.spec
import spack.error
import spack.compilers
import spack.hooks
import spack.build_environment as build_env
import spack.url as url
@ -57,7 +58,7 @@
from spack.util.compression import allowed_archive, extension
"""Allowed URL schemes for spack packages."""
_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file"]
_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"]
class Package(object):
@ -296,9 +297,12 @@ class SomePackage(Package):
"""
#
# These variables are defaults for the various relations defined on
# packages. Subclasses will have their own versions of these.
# These variables are defaults for the various "relations".
#
"""Map of information about Versions of this package.
Map goes: Version -> VersionDescriptor"""
versions = {}
"""Specs of dependency packages, keyed by name."""
dependencies = {}
@ -317,17 +321,8 @@ class SomePackage(Package):
"""By default we build in parallel. Subclasses can override this."""
parallel = True
"""Dirty hack for forcing packages with uninterpretable URLs
TODO: get rid of this.
"""
force_url = False
def __init__(self, spec):
# These attributes are required for all packages.
attr_required(self.__class__, 'homepage')
attr_required(self.__class__, 'url')
# this determines how the package should be built.
self.spec = spec
@ -337,28 +332,36 @@ def __init__(self, spec):
if '.' in self.name:
self.name = self.name[self.name.rindex('.') + 1:]
# Make sure URL is an allowed type
validate_package_url(self.url)
# patch up the URL with a new version if the spec version is concrete
if self.spec.versions.concrete:
self.url = self.url_for_version(self.spec.version)
# This is set by scraping a web page.
self._available_versions = None
# versions should be a dict from version to checksum, for safe versions
# of this package. If it's not present, make it an empty dict.
if not hasattr(self, 'versions'):
self.versions = {}
# Sanity check some required variables that could be
# overridden by package authors.
def sanity_check_dict(attr_name):
if not hasattr(self, attr_name):
raise PackageError("Package %s must define %s" % attr_name)
if not isinstance(self.versions, dict):
raise ValueError("versions attribute of package %s must be a dict!"
% self.name)
attr = getattr(self, attr_name)
if not isinstance(attr, dict):
raise PackageError("Package %s has non-dict %s attribute!"
% (self.name, attr_name))
sanity_check_dict('versions')
sanity_check_dict('dependencies')
sanity_check_dict('conflicted')
sanity_check_dict('patches')
# Check versions in the versions dict.
for v in self.versions:
assert(isinstance(v, Version))
# Check version descriptors
for v in sorted(self.versions):
vdesc = self.versions[v]
assert(isinstance(vdesc, spack.relations.VersionDescriptor))
# Version-ize the keys in versions dict
try:
self.versions = { Version(v):h for v,h in self.versions.items() }
self.versions = dict((Version(v), h) for v,h in self.versions.items())
except ValueError:
raise ValueError("Keys of versions dict in package %s must be versions!"
% self.name)
@ -366,6 +369,10 @@ def __init__(self, spec):
# stage used to build this package.
self._stage = None
# patch up self.url based on the actual version
if self.spec.concrete:
self.url = self.url_for_version(self.version)
# Set a default list URL (place to find available versions)
if not hasattr(self, 'list_url'):
self.list_url = None
@ -374,18 +381,6 @@ def __init__(self, spec):
self.list_depth = 1
@property
def default_version(self):
"""Get the version in the default URL for this package,
or fails."""
try:
return url.parse_version(self.__class__.url)
except UndetectableVersionError:
raise PackageError(
"Couldn't extract a default version from %s." % self.url,
" You must specify it explicitly in the package file.")
@property
def version(self):
if not self.spec.concrete:
@ -399,9 +394,13 @@ def stage(self):
raise ValueError("Can only get a stage for a concrete package.")
if self._stage is None:
if not self.url:
raise PackageVersionError(self.version)
# TODO: move this logic into a mirror module.
mirror_path = "%s/%s" % (self.name, "%s-%s.%s" % (
self.name, self.version, extension(self.url)))
self._stage = Stage(
self.url, mirror_path=mirror_path, name=self.spec.short_spec)
return self._stage
@ -496,7 +495,7 @@ def installed_dependents(self):
on this one."""
dependents = []
for spec in spack.db.installed_package_specs():
if self.spec != spec and self.spec in spec:
if self.name != spec.name and self.spec in spec:
dependents.append(spec)
return dependents
@ -507,6 +506,14 @@ def prefix(self):
return self.spec.prefix
@property
def compiler(self):
"""Get the spack.compiler.Compiler object used to build this package."""
if not self.spec.concrete:
raise ValueError("Can only get a compiler for a concrete package.")
return spack.compilers.compiler_for_spec(self.spec.compiler)
def url_version(self, version):
"""Given a version, this returns a string that should be substituted into the
package's URL to download that version.
@ -514,16 +521,48 @@ def url_version(self, version):
override this, e.g. for boost versions where you need to ensure that there
are _'s in the download URL.
"""
if self.force_url:
return self.default_version
return str(version)
def url_for_version(self, version):
"""Gives a URL that you can download a new version of this package from."""
if self.force_url:
return self.url
return url.substitute_version(self.__class__.url, self.url_version(version))
"""Returns a URL that you can download a new version of this package from."""
if not isinstance(version, Version):
version = Version(version)
def nearest_url(version):
"""Finds the URL for the next lowest version with a URL.
If there is no lower version with a URL, uses the
package url property. If that isn't there, uses a
*higher* URL, and if that isn't there raises an error.
"""
url = getattr(self, 'url', None)
for v in sorted(self.versions):
if v > version and url:
break
if self.versions[v].url:
url = self.versions[v].url
return url
if version in self.versions:
vdesc = self.versions[version]
if not vdesc.url:
base_url = nearest_url(version)
vdesc.url = url.substitute_version(
base_url, self.url_version(version))
return vdesc.url
else:
return nearest_url(version)
@property
def default_url(self):
if self.concrete:
return self.url_for_version(self.version)
else:
url = getattr(self, 'url', None)
if url:
return url
def remove_prefix(self):
@ -548,7 +587,7 @@ def do_fetch(self):
self.stage.fetch()
if spack.do_checksum and self.version in self.versions:
digest = self.versions[self.version]
digest = self.versions[self.version].checksum
self.stage.check(digest)
tty.msg("Checksum passed for %s@%s" % (self.name, self.version))
@ -743,7 +782,7 @@ def do_uninstall(self, **kwargs):
' '.join(formatted_deps))
self.remove_prefix()
tty.msg("Successfully uninstalled %s." % self.spec)
tty.msg("Successfully uninstalled %s." % self.spec.short_spec)
# Once everything else is done, run post install hooks
spack.hooks.post_uninstall(self)
@ -779,6 +818,9 @@ def do_clean_dist(self):
def fetch_available_versions(self):
if not hasattr(self, 'url'):
raise VersionFetchError(self.__class__)
# If not, then try to fetch using list_url
if not self._available_versions:
try:
@ -865,7 +907,6 @@ def print_pkg(message):
print message
class FetchError(spack.error.SpackError):
"""Raised when something goes wrong during fetch."""
def __init__(self, message, long_msg=None):
@ -889,3 +930,19 @@ class InvalidPackageDependencyError(PackageError):
its dependencies."""
def __init__(self, message):
super(InvalidPackageDependencyError, self).__init__(message)
class PackageVersionError(PackageError):
"""Raised when a version URL cannot automatically be determined."""
def __init__(self, version):
super(PackageVersionError, self).__init__(
"Cannot determine a URL automatically for version %s." % version,
"Please provide a url for this version in the package.py file.")
class VersionFetchError(PackageError):
"""Raised when a version URL cannot automatically be determined."""
def __init__(self, cls):
super(VersionFetchError, self).__init__(
"Cannot fetch version for package %s " % cls.__name__ +
"because it does not define a default url.")

View File

@ -69,9 +69,12 @@ def get(self, spec):
if not spec in self.instances:
package_class = self.get_class_for_package_name(spec.name)
self.instances[spec.name] = package_class(spec)
try:
self.instances[spec.copy()] = package_class(spec)
except Exception, e:
raise FailedConstructorError(spec.name, e)
return self.instances[spec.name]
return self.instances[spec]
@_autospec
@ -115,7 +118,23 @@ def installed_package_specs(self):
"""Read installed package names straight from the install directory
layout.
"""
return spack.install_layout.all_specs()
# Get specs from the directory layout but ensure that they're
# all normalized properly.
installed = []
for spec in spack.install_layout.all_specs():
spec.normalize()
installed.append(spec)
return installed
def installed_known_package_specs(self):
"""Read installed package names straight from the install
directory layout, but return only specs for which the
package is known to this version of spack.
"""
for spec in spack.install_layout.all_specs():
if self.exists(spec.name):
yield spec
@memoized
@ -179,24 +198,6 @@ def get_class_for_package_name(self, pkg_name):
return cls
def compute_dependents(self):
"""Reads in all package files and sets dependence information on
Package objects in memory.
"""
if not hasattr(compute_dependents, index):
compute_dependents.index = {}
for pkg in all_packages():
if pkg._dependents is None:
pkg._dependents = []
for name, dep in pkg.dependencies.iteritems():
dpkg = self.get(name)
if dpkg._dependents is None:
dpkg._dependents = []
dpkg._dependents.append(pkg.name)
def graph_dependencies(self, out=sys.stdout):
"""Print out a graph of all the dependencies between package.
Graph is in dot format."""
@ -211,10 +212,17 @@ def quote(string):
return '"%s"' % string
deps = []
for pkg in all_packages():
for pkg in self.all_packages():
out.write(' %-30s [label="%s"]\n' % (quote(pkg.name), pkg.name))
# Add edges for each depends_on in the package.
for dep_name, dep in pkg.dependencies.iteritems():
deps.append((pkg.name, dep_name))
# If the package provides something, add an edge for that.
for provider in set(p.name for p in pkg.provided):
deps.append((provider, pkg.name))
out.write('\n')
for pair in deps:
@ -227,3 +235,12 @@ class UnknownPackageError(spack.error.SpackError):
def __init__(self, name):
super(UnknownPackageError, self).__init__("Package %s not found." % name)
self.name = name
class FailedConstructorError(spack.error.SpackError):
"""Raised when a package's class constructor fails."""
def __init__(self, name, reason):
super(FailedConstructorError, self).__init__(
"Class constructor failed for package '%s'." % name,
str(reason))
self.name = name

View File

@ -68,23 +68,48 @@ class Mpileaks(Package):
spack install mpileaks ^mvapich
spack install mpileaks ^mpich
"""
__all__ = [ 'depends_on', 'provides', 'patch', 'version' ]
import re
import inspect
import importlib
from llnl.util.lang import *
import spack
import spack.spec
import spack.error
import spack.url
from spack.version import Version
from spack.patch import Patch
from spack.spec import Spec, parse_anonymous_spec
"""Adds a dependencies local variable in the locals of
the calling class, based on args. """
class VersionDescriptor(object):
"""A VersionDescriptor contains information to describe a
particular version of a package. That currently includes a URL
for the version along with a checksum."""
def __init__(self, checksum, url):
self.checksum = checksum
self.url = url
def version(ver, checksum, **kwargs):
"""Adds a version and associated metadata to the package."""
pkg = caller_locals()
versions = pkg.setdefault('versions', {})
patches = pkg.setdefault('patches', {})
ver = Version(ver)
url = kwargs.get('url', None)
versions[ver] = VersionDescriptor(checksum, url)
def depends_on(*specs):
"""Adds a dependencies local variable in the locals of
the calling class, based on args. """
pkg = get_calling_package_name()
dependencies = caller_locals().setdefault('dependencies', {})

View File

@ -94,6 +94,7 @@
import itertools
import hashlib
from StringIO import StringIO
from operator import attrgetter
import llnl.util.tty as tty
from llnl.util.lang import *
@ -309,9 +310,8 @@ def concrete(self):
def __str__(self):
sorted_dep_names = sorted(self.keys())
return ''.join(
["^" + str(self[name]) for name in sorted_dep_names])
["^" + str(self[name]) for name in sorted(self.keys())])
@key_ordering
@ -345,6 +345,13 @@ def __init__(self, spec_like, *dep_like, **kwargs):
self.compiler = other.compiler
self.dependencies = other.dependencies
# Specs are by default not assumed to be normal, but in some
# cases we've read them from a file want to assume normal.
# This allows us to manipulate specs that Spack doesn't have
# package.py files for.
self._normal = kwargs.get('normal', False)
self._concrete = kwargs.get('concrete', False)
# This allows users to construct a spec DAG with literals.
# Note that given two specs a and b, Spec(a) copies a, but
# Spec(a, b) will copy a but just add b as a dep.
@ -432,17 +439,30 @@ def concrete(self):
If any of the name, version, architecture, compiler, or depdenencies
are ambiguous,then it is not concrete.
"""
return bool(not self.virtual
and self.versions.concrete
and self.architecture
and self.compiler and self.compiler.concrete
and self.dependencies.concrete)
if self._concrete:
return True
self._concrete = bool(not self.virtual
and self.versions.concrete
and self.architecture
and self.compiler and self.compiler.concrete
and self.dependencies.concrete)
return self._concrete
def preorder_traversal(self, visited=None, d=0, **kwargs):
"""Generic preorder traversal of the DAG represented by this spec.
def traverse(self, visited=None, d=0, **kwargs):
"""Generic traversal of the DAG represented by this spec.
This will yield each node in the spec. Options:
order [=pre|post]
Order to traverse spec nodes. Defaults to preorder traversal.
Options are:
'pre': Pre-order traversal; each node is yielded before its
children in the dependency DAG.
'post': Post-order traversal; each node is yielded after its
children in the dependency DAG.
cover [=nodes|edges|paths]
Determines how extensively to cover the dag. Possible vlaues:
@ -460,7 +480,7 @@ def preorder_traversal(self, visited=None, d=0, **kwargs):
spec, but also their depth from the root in a (depth, node)
tuple.
keyfun [=id]
key [=id]
Allow a custom key function to track the identity of nodes
in the traversal.
@ -472,44 +492,57 @@ def preorder_traversal(self, visited=None, d=0, **kwargs):
'parents', traverses upwards in the DAG towards the root.
"""
# get initial values for kwargs
depth = kwargs.get('depth', False)
key_fun = kwargs.get('key', id)
if isinstance(key_fun, basestring):
key_fun = attrgetter(key_fun)
yield_root = kwargs.get('root', True)
cover = kwargs.get('cover', 'nodes')
direction = kwargs.get('direction', 'children')
order = kwargs.get('order', 'pre')
cover_values = ('nodes', 'edges', 'paths')
if cover not in cover_values:
raise ValueError("Invalid value for cover: %s. Choices are %s"
% (cover, ",".join(cover_values)))
direction_values = ('children', 'parents')
if direction not in direction_values:
raise ValueError("Invalid value for direction: %s. Choices are %s"
% (direction, ",".join(direction_values)))
# Make sure kwargs have legal values; raise ValueError if not.
def validate(name, val, allowed_values):
if val not in allowed_values:
raise ValueError("Invalid value for %s: %s. Choices are %s"
% (name, val, ",".join(allowed_values)))
validate('cover', cover, ('nodes', 'edges', 'paths'))
validate('direction', direction, ('children', 'parents'))
validate('order', order, ('pre', 'post'))
if visited is None:
visited = set()
result = (d, self) if depth else self
key = key_fun(self)
if key in visited:
if cover == 'nodes': return
if yield_root or d > 0: yield result
if cover == 'edges': return
else:
if yield_root or d > 0: yield result
# Node traversal does not yield visited nodes.
if key in visited and cover == 'nodes':
return
successors = self.dependencies
if direction == 'parents':
successors = self.dependents
# Determine whether and what to yield for this node.
yield_me = yield_root or d > 0
result = (d, self) if depth else self
visited.add(key)
for name in sorted(successors):
child = successors[name]
for elt in child.preorder_traversal(visited, d+1, **kwargs):
yield elt
# Preorder traversal yields before successors
if yield_me and order == 'pre':
yield result
# Edge traversal yields but skips children of visited nodes
if not (key in visited and cover == 'edges'):
# This code determines direction and yields the children/parents
successors = self.dependencies
if direction == 'parents':
successors = self.dependents
visited.add(key)
for name in sorted(successors):
child = successors[name]
for elt in child.traverse(visited, d+1, **kwargs):
yield elt
# Postorder traversal yields after successors
if yield_me and order == 'post':
yield result
@property
@ -525,13 +558,14 @@ def prefix(self):
def dep_hash(self, length=None):
"""Return a hash representing the dependencies of this spec
This will always normalize first so that the hash is consistent.
"""
self.normalize()
"""Return a hash representing all dependencies of this spec
(direct and indirect).
If you want this hash to be consistent, you should
concretize the spec first so that it is not ambiguous.
"""
sha = hashlib.sha1()
sha.update(str(self.dependencies))
sha.update(self.dep_string())
full_hash = sha.hexdigest()
return full_hash[:length]
@ -594,7 +628,7 @@ def _expand_virtual_packages(self):
a problem.
"""
while True:
virtuals =[v for v in self.preorder_traversal() if v.virtual]
virtuals =[v for v in self.traverse() if v.virtual]
if not virtuals:
return
@ -605,8 +639,8 @@ def _expand_virtual_packages(self):
spec._replace_with(concrete)
# If there are duplicate providers or duplicate provider deps, this
# consolidates them and merges constraints.
self.normalize()
# consolidates them and merge constraints.
self.normalize(force=True)
def concretize(self):
@ -621,9 +655,13 @@ def concretize(self):
with requirements of its pacakges. See flatten() and normalize() for
more details on this.
"""
if self._concrete:
return
self.normalize()
self._expand_virtual_packages()
self._concretize_helper()
self._concrete = True
def concretized(self):
@ -635,47 +673,51 @@ def concretized(self):
return clone
def flat_dependencies(self):
"""Return a DependencyMap containing all of this spec's dependencies
with their constraints merged. If there are any conflicts, throw
an exception.
def flat_dependencies(self, **kwargs):
"""Return a DependencyMap containing all of this spec's
dependencies with their constraints merged.
This will work even on specs that are not normalized; i.e. specs
that have two instances of the same dependency in the DAG.
This is used as the first step of normalization.
If copy is True, returns merged copies of its dependencies
without modifying the spec it's called on.
If copy is False, clears this spec's dependencies and
returns them.
"""
# This ensures that the package descriptions themselves are consistent
if not self.virtual:
self.package.validate_dependencies()
copy = kwargs.get('copy', True)
# Once that is guaranteed, we know any constraint violations are due
# to the spec -- so they're the user's fault, not Spack's.
flat_deps = DependencyMap()
try:
for spec in self.preorder_traversal():
for spec in self.traverse(root=False):
if spec.name not in flat_deps:
new_spec = spec.copy(dependencies=False)
flat_deps[spec.name] = new_spec
if copy:
flat_deps[spec.name] = spec.copy(deps=False)
else:
flat_deps[spec.name] = spec
else:
flat_deps[spec.name].constrain(spec)
except UnsatisfiableSpecError, e:
# This REALLY shouldn't happen unless something is wrong in spack.
# It means we got a spec DAG with two instances of the same package
# that had inconsistent constraints. There's no way for a user to
# produce a spec like this (the parser adds all deps to the root),
# so this means OUR code is not sane!
raise InconsistentSpecError("Invalid Spec DAG: %s" % e.message)
if not copy:
for dep in flat_deps.values():
dep.dependencies.clear()
dep.dependents.clear()
self.dependencies.clear()
return flat_deps
return flat_deps
except UnsatisfiableSpecError, e:
# Here, the DAG contains two instances of the same package
# with inconsistent constraints. Users cannot produce
# inconsistent specs like this on the command line: the
# parser doesn't allow it. Spack must be broken!
raise InconsistentSpecError("Invalid Spec DAG: %s" % e.message)
def flatten(self):
"""Pull all dependencies up to the root (this spec).
Merge constraints for dependencies with the same name, and if they
conflict, throw an exception. """
self.dependencies = self.flat_dependencies()
for dep in self.flat_dependencies(copy=False):
self._add_dependency(dep)
def _normalize_helper(self, visited, spec_deps, provider_index):
@ -754,7 +796,7 @@ def _normalize_helper(self, visited, spec_deps, provider_index):
dependency._normalize_helper(visited, spec_deps, provider_index)
def normalize(self):
def normalize(self, **kwargs):
"""When specs are parsed, any dependencies specified are hanging off
the root, and ONLY the ones that were explicitly provided are there.
Normalization turns a partial flat spec into a DAG, where:
@ -772,14 +814,18 @@ def normalize(self):
TODO: normalize should probably implement some form of cycle detection,
to ensure that the spec is actually a DAG.
"""
if self._normal and not kwargs.get('force', False):
return
# Ensure first that all packages & compilers in the DAG exist.
self.validate_names()
# Then ensure that the packages referenced are sane, that the
# provided spec is sane, and that all dependency specs are in the
# root node of the spec. flat_dependencies will do this for us.
spec_deps = self.flat_dependencies()
self.dependencies.clear()
# Ensure that the package & dep descriptions are consistent & sane
if not self.virtual:
self.package.validate_dependencies()
# Get all the dependencies into one DependencyMap
spec_deps = self.flat_dependencies(copy=False)
# Figure out which of the user-provided deps provide virtual deps.
# Remove virtual deps that are already provided by something in the spec
@ -792,7 +838,7 @@ def normalize(self):
# If there are deps specified but not visited, they're not
# actually deps of this package. Raise an error.
extra = set(spec_deps.viewkeys()).difference(visited)
extra = set(spec_deps.keys()).difference(visited)
# Also subtract out all the packags that provide a needed vpkg
vdeps = [v for v in self.package.virtual_dependencies()]
@ -805,6 +851,9 @@ def normalize(self):
raise InvalidDependencyException(
self.name + " does not depend on " + comma_or(extra))
# Mark the spec as normal once done.
self._normal = True
def normalized(self):
"""Return a normalized copy of this spec without modifying this spec."""
@ -818,7 +867,7 @@ def validate_names(self):
If they're not, it will raise either UnknownPackageError or
UnsupportedCompilerError.
"""
for spec in self.preorder_traversal():
for spec in self.traverse():
# Don't get a package for a virtual name.
if not spec.virtual:
spack.db.get(spec.name)
@ -886,17 +935,17 @@ def _constrain_dependencies(self, other):
def common_dependencies(self, other):
"""Return names of dependencies that self an other have in common."""
common = set(
s.name for s in self.preorder_traversal(root=False))
s.name for s in self.traverse(root=False))
common.intersection_update(
s.name for s in other.preorder_traversal(root=False))
s.name for s in other.traverse(root=False))
return common
def dep_difference(self, other):
"""Returns dependencies in self that are not in other."""
mine = set(s.name for s in self.preorder_traversal(root=False))
mine = set(s.name for s in self.traverse(root=False))
mine.difference_update(
s.name for s in other.preorder_traversal(root=False))
s.name for s in other.traverse(root=False))
return mine
@ -955,8 +1004,8 @@ def satisfies_dependencies(self, other):
return False
# For virtual dependencies, we need to dig a little deeper.
self_index = ProviderIndex(self.preorder_traversal(), restrict=True)
other_index = ProviderIndex(other.preorder_traversal(), restrict=True)
self_index = ProviderIndex(self.traverse(), restrict=True)
other_index = ProviderIndex(other.traverse(), restrict=True)
# This handles cases where there are already providers for both vpkgs
if not self_index.satisfies(other_index):
@ -978,7 +1027,7 @@ def satisfies_dependencies(self, other):
def virtual_dependencies(self):
"""Return list of any virtual deps in this spec."""
return [spec for spec in self.preorder_traversal() if spec.virtual]
return [spec for spec in self.traverse() if spec.virtual]
def _dup(self, other, **kwargs):
@ -993,21 +1042,31 @@ def _dup(self, other, **kwargs):
Whether deps should be copied too. Set to false to copy a
spec but not its dependencies.
"""
# TODO: this needs to handle DAGs.
# Local node attributes get copied first.
self.name = other.name
self.versions = other.versions.copy()
self.variants = other.variants.copy()
self.architecture = other.architecture
self.compiler = None
if other.compiler:
self.compiler = other.compiler.copy()
self.compiler = other.compiler.copy() if other.compiler else None
self.dependents = DependencyMap()
copy_deps = kwargs.get('dependencies', True)
if copy_deps:
self.dependencies = other.dependencies.copy()
else:
self.dependencies = DependencyMap()
self.dependencies = DependencyMap()
# If we copy dependencies, preserve DAG structure in the new spec
if kwargs.get('deps', True):
# This copies the deps from other using _dup(deps=False)
new_nodes = other.flat_dependencies()
new_nodes[self.name] = self
# Hook everything up properly here by traversing.
for spec in other.traverse(cover='nodes'):
parent = new_nodes[spec.name]
for child in spec.dependencies:
if child not in parent.dependencies:
parent._add_dependency(new_nodes[child])
# Since we preserved structure, we can copy _normal safely.
self._normal = other._normal
self._concrete = other._concrete
def copy(self, **kwargs):
@ -1029,7 +1088,7 @@ def version(self):
def __getitem__(self, name):
"""TODO: reconcile __getitem__, _add_dependency, __contains__"""
for spec in self.preorder_traversal():
for spec in self.traverse():
if spec.name == name:
return spec
@ -1040,15 +1099,82 @@ def __contains__(self, spec):
"""True if this spec has any dependency that satisfies the supplied
spec."""
spec = self._autospec(spec)
for s in self.preorder_traversal():
for s in self.traverse():
if s.satisfies(spec):
return True
return False
def _cmp_key(self):
def sorted_deps(self):
"""Return a list of all dependencies sorted by name."""
deps = self.flat_dependencies()
return tuple(deps[name] for name in sorted(deps))
def _eq_dag(self, other, vs, vo):
"""Recursive helper for eq_dag and ne_dag. Does the actual DAG
traversal."""
vs.add(id(self))
vo.add(id(other))
if self.ne_node(other):
return False
if len(self.dependencies) != len(other.dependencies):
return False
ssorted = [self.dependencies[name] for name in sorted(self.dependencies)]
osorted = [other.dependencies[name] for name in sorted(other.dependencies)]
for s, o in zip(ssorted, osorted):
visited_s = id(s) in vs
visited_o = id(o) in vo
# Check for duplicate or non-equal dependencies
if visited_s != visited_o: return False
# Skip visited nodes
if visited_s or visited_o: continue
# Recursive check for equality
if not s._eq_dag(o, vs, vo):
return False
return True
def eq_dag(self, other):
"""True if the full dependency DAGs of specs are equal"""
return self._eq_dag(other, set(), set())
def ne_dag(self, other):
"""True if the full dependency DAGs of specs are not equal"""
return not self.eq_dag(other)
def _cmp_node(self):
"""Comparison key for just *this node* and not its deps."""
return (self.name, self.versions, self.variants,
self.architecture, self.compiler, self.dependencies)
self.architecture, self.compiler)
def eq_node(self, other):
"""Equality with another spec, not including dependencies."""
return self._cmp_node() == other._cmp_node()
def ne_node(self, other):
"""Inequality with another spec, not including dependencies."""
return self._cmp_node() != other._cmp_node()
def _cmp_key(self):
"""Comparison key for this node and all dependencies *without*
considering structure. This is the default, as
normalization will restore structure.
"""
return self._cmp_node() + (self.sorted_deps(),)
def colorized(self):
@ -1151,12 +1277,12 @@ def write(s, c):
return result
def dep_string(self):
return ''.join("^" + dep.format() for dep in self.sorted_deps())
def __str__(self):
by_name = lambda d: d.name
deps = self.preorder_traversal(key=by_name, root=False)
sorted_deps = sorted(deps, key=by_name)
dep_string = ''.join("^" + dep.format() for dep in sorted_deps)
return self.format() + dep_string
return self.format() + self.dep_string()
def tree(self, **kwargs):
@ -1172,7 +1298,7 @@ def tree(self, **kwargs):
out = ""
cur_id = 0
ids = {}
for d, node in self.preorder_traversal(cover=cover, depth=True):
for d, node in self.traverse(order='pre', cover=cover, depth=True):
out += " " * indent
if depth:
out += "%-4d" % d
@ -1261,6 +1387,9 @@ def spec(self):
spec.dependents = DependencyMap()
spec.dependencies = DependencyMap()
spec._normal = False
spec._concrete = False
# record this so that we know whether version is
# unspecified or not.
added_version = False

View File

@ -120,8 +120,7 @@ def _need_to_create_path(self):
if spack.use_tmp_stage:
# If we're using a tmp dir, it's a link, and it points at the right spot,
# then keep it.
if (os.path.commonprefix((real_path, real_tmp)) == real_tmp
and os.path.exists(real_path)):
if (real_path.startswith(real_tmp) and os.path.exists(real_path)):
return False
else:
# otherwise, just unlink it and start over.

View File

@ -45,7 +45,9 @@
'multimethod',
'install',
'package_sanity',
'config']
'config',
'directory_layout',
'python_version']
def list_tests():
@ -70,7 +72,7 @@ def run(names, verbose=False):
runner = unittest.TextTestRunner(verbosity=verbosity)
testsRun = errors = failures = skipped = 0
testsRun = errors = failures = 0
for test in names:
module = 'spack.test.' + test
print module
@ -81,12 +83,10 @@ def run(names, verbose=False):
testsRun += result.testsRun
errors += len(result.errors)
failures += len(result.failures)
skipped += len(result.skipped)
succeeded = not errors and not failures
tty.msg("Tests Complete.",
"%5d tests run" % testsRun,
"%5d skipped" % skipped,
"%5d failures" % failures,
"%5d errors" % errors)

View File

@ -134,29 +134,29 @@ def test_concretize_with_provides_when(self):
def test_virtual_is_fully_expanded_for_callpath(self):
# force dependence on fake "zmpi" by asking for MPI 10.0
spec = Spec('callpath ^mpi@10.0')
self.assertIn('mpi', spec.dependencies)
self.assertNotIn('fake', spec)
self.assertTrue('mpi' in spec.dependencies)
self.assertFalse('fake' in spec)
spec.concretize()
self.assertIn('zmpi', spec.dependencies)
self.assertNotIn('mpi', spec)
self.assertIn('fake', spec.dependencies['zmpi'])
self.assertTrue('zmpi' in spec.dependencies)
self.assertFalse('mpi' in spec)
self.assertTrue('fake' in spec.dependencies['zmpi'])
def test_virtual_is_fully_expanded_for_mpileaks(self):
spec = Spec('mpileaks ^mpi@10.0')
self.assertIn('mpi', spec.dependencies)
self.assertNotIn('fake', spec)
self.assertTrue('mpi' in spec.dependencies)
self.assertFalse('fake' in spec)
spec.concretize()
self.assertIn('zmpi', spec.dependencies)
self.assertIn('callpath', spec.dependencies)
self.assertIn('zmpi', spec.dependencies['callpath'].dependencies)
self.assertIn('fake', spec.dependencies['callpath'].dependencies['zmpi'].dependencies)
self.assertTrue('zmpi' in spec.dependencies)
self.assertTrue('callpath' in spec.dependencies)
self.assertTrue('zmpi' in spec.dependencies['callpath'].dependencies)
self.assertTrue('fake' in spec.dependencies['callpath'].dependencies['zmpi'].dependencies)
self.assertNotIn('mpi', spec)
self.assertFalse('mpi' in spec)
def test_my_dep_depends_on_provider_of_my_virtual_dep(self):

View File

@ -0,0 +1,155 @@
##############################################################################
# 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 test verifies that the Spack directory layout works properly.
"""
import unittest
import tempfile
import shutil
import os
from contextlib import closing
from llnl.util.filesystem import *
import spack
from spack.spec import Spec
from spack.packages import PackageDB
from spack.directory_layout import SpecHashDirectoryLayout
class DirectoryLayoutTest(unittest.TestCase):
"""Tests that a directory layout works correctly and produces a
consistent install path."""
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
self.layout = SpecHashDirectoryLayout(self.tmpdir)
def tearDown(self):
shutil.rmtree(self.tmpdir, ignore_errors=True)
self.layout = None
def test_read_and_write_spec(self):
"""This goes through each package in spack and creates a directory for
it. It then ensures that the spec for the directory's
installed package can be read back in consistently, and
finally that the directory can be removed by the directory
layout.
"""
for pkg in spack.db.all_packages():
spec = pkg.spec
# If a spec fails to concretize, just skip it. If it is a
# real error, it will be caught by concretization tests.
try:
spec.concretize()
except:
continue
self.layout.make_path_for_spec(spec)
install_dir = self.layout.path_for_spec(spec)
spec_path = self.layout.spec_file_path(spec)
# Ensure directory has been created in right place.
self.assertTrue(os.path.isdir(install_dir))
self.assertTrue(install_dir.startswith(self.tmpdir))
# Ensure spec file exists when directory is created
self.assertTrue(os.path.isfile(spec_path))
self.assertTrue(spec_path.startswith(install_dir))
# Make sure spec file can be read back in to get the original spec
spec_from_file = self.layout.read_spec(spec_path)
self.assertEqual(spec, spec_from_file)
self.assertTrue(spec.eq_dag, spec_from_file)
self.assertTrue(spec_from_file.concrete)
# Ensure that specs that come out "normal" are really normal.
with closing(open(spec_path)) as spec_file:
read_separately = Spec(spec_file.read())
read_separately.normalize()
self.assertEqual(read_separately, spec_from_file)
read_separately.concretize()
self.assertEqual(read_separately, spec_from_file)
# Make sure the dep hash of the read-in spec is the same
self.assertEqual(spec.dep_hash(), spec_from_file.dep_hash())
# Ensure directories are properly removed
self.layout.remove_path_for_spec(spec)
self.assertFalse(os.path.isdir(install_dir))
self.assertFalse(os.path.exists(install_dir))
def test_handle_unknown_package(self):
"""This test ensures that spack can at least do *some*
operations with packages that are installed but that it
does not know about. This is actually not such an uncommon
scenario with spack; it can happen when you switch from a
git branch where you're working on a new package.
This test ensures that the directory layout stores enough
information about installed packages' specs to uninstall
or query them again if the package goes away.
"""
mock_db = PackageDB(spack.mock_packages_path)
not_in_mock = set(spack.db.all_package_names()).difference(
set(mock_db.all_package_names()))
# Create all the packages that are not in mock.
installed_specs = {}
for pkg_name in not_in_mock:
spec = spack.db.get(pkg_name).spec
# If a spec fails to concretize, just skip it. If it is a
# real error, it will be caught by concretization tests.
try:
spec.concretize()
except:
continue
self.layout.make_path_for_spec(spec)
installed_specs[spec] = self.layout.path_for_spec(spec)
tmp = spack.db
spack.db = mock_db
# Now check that even without the package files, we know
# enough to read a spec from the spec file.
for spec, path in installed_specs.items():
spec_from_file = self.layout.read_spec(join_path(path, '.spec'))
# To satisfy these conditions, directory layouts need to
# read in concrete specs from their install dirs somehow.
self.assertEqual(path, self.layout.path_for_spec(spec_from_file))
self.assertEqual(spec, spec_from_file)
self.assertEqual(spec.dep_hash(), spec_from_file.dep_hash())
spack.db = tmp

View File

@ -25,15 +25,18 @@
import os
import unittest
import shutil
import tempfile
from contextlib import closing
from llnl.util.filesystem import *
import spack
from spack.stage import Stage
from spack.directory_layout import SpecHashDirectoryLayout
from spack.util.executable import which
from spack.test.mock_packages_test import *
dir_name = 'trivial-1.0'
archive_name = 'trivial-1.0.tar.gz'
install_test_package = 'trivial_install_test_package'
@ -66,9 +69,16 @@ def setUp(self):
tar = which('tar')
tar('-czf', archive_name, dir_name)
# We use a fake pacakge, so skip the checksum.
# We use a fake package, so skip the checksum.
spack.do_checksum = False
# Use a fake install directory to avoid conflicts bt/w
# installed pkgs and mock packages.
self.tmpdir = tempfile.mkdtemp()
self.orig_layout = spack.install_layout
spack.install_layout = SpecHashDirectoryLayout(self.tmpdir)
def tearDown(self):
super(InstallTest, self).tearDown()
@ -78,6 +88,10 @@ def tearDown(self):
# Turn checksumming back on
spack.do_checksum = True
# restore spack's layout.
spack.install_layout = self.orig_layout
shutil.rmtree(self.tmpdir, ignore_errors=True)
def test_install_and_uninstall(self):
# Get a basic concrete spec for the trivial install package.

View File

@ -39,7 +39,6 @@ def set_pkg_dep(pkg, spec):
class MockPackagesTest(unittest.TestCase):
@classmethod
def setUp(self):
# Use the mock packages database for these tests. This allows
# us to set up contrived packages that don't interfere with
@ -52,7 +51,7 @@ def setUp(self):
'site' : spack.mock_site_config,
'user' : spack.mock_user_config }
@classmethod
def tearDown(self):
"""Restore the real packages path after any test."""
spack.db = self.real_db

View File

@ -29,19 +29,35 @@
import spack
import spack.url as url
from spack.packages import PackageDB
class PackageSanityTest(unittest.TestCase):
def test_get_all_packages(self):
"""Get all packages once and make sure that works."""
def check_db(self):
"""Get all packages in a DB to make sure they work."""
for name in spack.db.all_package_names():
spack.db.get(name)
def test_get_all_packages(self):
"""Get all packages once and make sure that works."""
self.check_db()
def test_get_all_mock_packages(self):
"""Get the mock packages once each too."""
tmp = spack.db
spack.db = PackageDB(spack.mock_packages_path)
self.check_db()
spack.db = tmp
def test_url_versions(self):
"""Ensure that url_for_version does the right thing for at least the
default version of each package.
"""
"""Check URLs for regular packages, if they are explicitly defined."""
for pkg in spack.db.all_packages():
v = url.parse_version(pkg.url)
self.assertEqual(pkg.url, pkg.url_for_version(v))
for v, vdesc in pkg.versions.items():
if vdesc.url:
# If there is a url for the version check it.
v_url = pkg.url_for_version(v)
self.assertEqual(vdesc.url, v_url)

View File

@ -0,0 +1,97 @@
##############################################################################
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
"""
This test ensures that all Spack files are Python version 2.6 or less.
Spack was originally 2.7, but enough systems in 2014 are still using
2.6 on their frontend nodes that we need 2.6 to get adopted.
"""
import unittest
import os
import re
from contextlib import closing
import llnl.util.tty as tty
from external import pyqver2
import spack
spack_max_version = (2,6)
class PythonVersionTest(unittest.TestCase):
def spack_python_files(self):
# first file is the spack script.
yield spack.spack_file
yield os.path.join(spack.build_env_path, 'cc')
# Next files are all the source files and package files.
search_paths = [spack.lib_path, spack.var_path]
# Iterate through the whole spack source tree.
for path in search_paths:
for root, dirnames, filenames in os.walk(path):
for filename in filenames:
if re.match(r'^[^.#].*\.py$', filename):
yield os.path.join(root, filename)
def test_python_versions(self):
# dict version -> filename -> reasons
all_issues = {}
for fn in self.spack_python_files():
with closing(open(fn)) as pyfile:
versions = pyqver2.get_versions(pyfile.read())
for ver, reasons in versions.items():
if ver > spack_max_version:
if not ver in all_issues:
all_issues[ver] = {}
all_issues[ver][fn] = reasons
if all_issues:
tty.error("Spack must run on Python version %d.%d"
% spack_max_version)
for v in sorted(all_issues.keys(), reverse=True):
msgs = []
for fn in sorted(all_issues[v].keys()):
short_fn = fn
if fn.startswith(spack.prefix):
short_fn = fn[len(spack.prefix):]
reasons = [r for r in set(all_issues[v][fn]) if r]
for r in reasons:
msgs.append(("%s:%s" % ('spack' + short_fn, r[0]), r[1]))
tty.error("These files require version %d.%d:" % v)
maxlen = max(len(f) for f, prob in msgs)
fmt = "%%-%ds%%s" % (maxlen+3)
print fmt % ('File', 'Reason')
print fmt % ('-' * (maxlen), '-' * 20)
for msg in msgs:
print fmt % msg
self.assertTrue(len(all_issues) == 0)

View File

@ -48,7 +48,7 @@ def test_conflicting_package_constraints(self):
spec.package.validate_dependencies)
def test_unique_node_traversal(self):
def test_preorder_node_traversal(self):
dag = Spec('mpileaks ^zmpi')
dag.normalize()
@ -56,14 +56,14 @@ def test_unique_node_traversal(self):
'zmpi', 'fake']
pairs = zip([0,1,2,3,4,2,3], names)
traversal = dag.preorder_traversal()
self.assertListEqual([x.name for x in traversal], names)
traversal = dag.traverse()
self.assertEqual([x.name for x in traversal], names)
traversal = dag.preorder_traversal(depth=True)
self.assertListEqual([(x, y.name) for x,y in traversal], pairs)
traversal = dag.traverse(depth=True)
self.assertEqual([(x, y.name) for x,y in traversal], pairs)
def test_unique_edge_traversal(self):
def test_preorder_edge_traversal(self):
dag = Spec('mpileaks ^zmpi')
dag.normalize()
@ -71,14 +71,14 @@ def test_unique_edge_traversal(self):
'libelf', 'zmpi', 'fake', 'zmpi']
pairs = zip([0,1,2,3,4,3,2,3,1], names)
traversal = dag.preorder_traversal(cover='edges')
self.assertListEqual([x.name for x in traversal], names)
traversal = dag.traverse(cover='edges')
self.assertEqual([x.name for x in traversal], names)
traversal = dag.preorder_traversal(cover='edges', depth=True)
self.assertListEqual([(x, y.name) for x,y in traversal], pairs)
traversal = dag.traverse(cover='edges', depth=True)
self.assertEqual([(x, y.name) for x,y in traversal], pairs)
def test_unique_path_traversal(self):
def test_preorder_path_traversal(self):
dag = Spec('mpileaks ^zmpi')
dag.normalize()
@ -86,11 +86,56 @@ def test_unique_path_traversal(self):
'libelf', 'zmpi', 'fake', 'zmpi', 'fake']
pairs = zip([0,1,2,3,4,3,2,3,1,2], names)
traversal = dag.preorder_traversal(cover='paths')
self.assertListEqual([x.name for x in traversal], names)
traversal = dag.traverse(cover='paths')
self.assertEqual([x.name for x in traversal], names)
traversal = dag.preorder_traversal(cover='paths', depth=True)
self.assertListEqual([(x, y.name) for x,y in traversal], pairs)
traversal = dag.traverse(cover='paths', depth=True)
self.assertEqual([(x, y.name) for x,y in traversal], pairs)
def test_postorder_node_traversal(self):
dag = Spec('mpileaks ^zmpi')
dag.normalize()
names = ['libelf', 'libdwarf', 'dyninst', 'fake', 'zmpi',
'callpath', 'mpileaks']
pairs = zip([4,3,2,3,2,1,0], names)
traversal = dag.traverse(order='post')
self.assertEqual([x.name for x in traversal], names)
traversal = dag.traverse(depth=True, order='post')
self.assertEqual([(x, y.name) for x,y in traversal], pairs)
def test_postorder_edge_traversal(self):
dag = Spec('mpileaks ^zmpi')
dag.normalize()
names = ['libelf', 'libdwarf', 'libelf', 'dyninst', 'fake', 'zmpi',
'callpath', 'zmpi', 'mpileaks']
pairs = zip([4,3,3,2,3,2,1,1,0], names)
traversal = dag.traverse(cover='edges', order='post')
self.assertEqual([x.name for x in traversal], names)
traversal = dag.traverse(cover='edges', depth=True, order='post')
self.assertEqual([(x, y.name) for x,y in traversal], pairs)
def test_postorder_path_traversal(self):
dag = Spec('mpileaks ^zmpi')
dag.normalize()
names = ['libelf', 'libdwarf', 'libelf', 'dyninst', 'fake', 'zmpi',
'callpath', 'fake', 'zmpi', 'mpileaks']
pairs = zip([4,3,3,2,3,2,1,2,1,0], names)
traversal = dag.traverse(cover='paths', order='post')
self.assertEqual([x.name for x in traversal], names)
traversal = dag.traverse(cover='paths', depth=True, order='post')
self.assertEqual([(x, y.name) for x,y in traversal], pairs)
def test_conflicting_spec_constraints(self):
@ -142,7 +187,7 @@ def test_normalize_with_virtual_spec(self):
# make sure nothing with the same name occurs twice
counts = {}
for spec in dag.preorder_traversal(keyfun=id):
for spec in dag.traverse(key=id):
if not spec.name in counts:
counts[spec.name] = 0
counts[spec.name] += 1
@ -152,15 +197,15 @@ def test_normalize_with_virtual_spec(self):
def check_links(self, spec_to_check):
for spec in spec_to_check.preorder_traversal():
for spec in spec_to_check.traverse():
for dependent in spec.dependents.values():
self.assertIn(
spec.name, dependent.dependencies,
self.assertTrue(
spec.name in dependent.dependencies,
"%s not in dependencies of %s" % (spec.name, dependent.name))
for dependency in spec.dependencies.values():
self.assertIn(
spec.name, dependency.dependents,
self.assertTrue(
spec.name in dependency.dependents,
"%s not in dependents of %s" % (spec.name, dependency.name))
@ -221,30 +266,53 @@ def test_invalid_dep(self):
def test_equal(self):
spec = Spec('mpileaks ^callpath ^libelf ^libdwarf')
self.assertNotEqual(spec, Spec(
'mpileaks', Spec('callpath',
Spec('libdwarf',
Spec('libelf')))))
self.assertNotEqual(spec, Spec(
'mpileaks', Spec('callpath',
Spec('libelf',
Spec('libdwarf')))))
# Different spec structures to test for equality
flat = Spec('mpileaks ^callpath ^libelf ^libdwarf')
self.assertEqual(spec, Spec(
'mpileaks', Spec('callpath'), Spec('libdwarf'), Spec('libelf')))
flat_init = Spec(
'mpileaks', Spec('callpath'), Spec('libdwarf'), Spec('libelf'))
self.assertEqual(spec, Spec(
'mpileaks', Spec('libelf'), Spec('libdwarf'), Spec('callpath')))
flip_flat = Spec(
'mpileaks', Spec('libelf'), Spec('libdwarf'), Spec('callpath'))
dag = Spec('mpileaks', Spec('callpath',
Spec('libdwarf',
Spec('libelf'))))
flip_dag = Spec('mpileaks', Spec('callpath',
Spec('libelf',
Spec('libdwarf'))))
# All these are equal to each other with regular ==
specs = (flat, flat_init, flip_flat, dag, flip_dag)
for lhs, rhs in zip(specs, specs):
self.assertEqual(lhs, rhs)
self.assertEqual(str(lhs), str(rhs))
# Same DAGs constructed different ways are equal
self.assertTrue(flat.eq_dag(flat_init))
# order at same level does not matter -- (dep on same parent)
self.assertTrue(flat.eq_dag(flip_flat))
# DAGs should be unequal if nesting is different
self.assertFalse(flat.eq_dag(dag))
self.assertFalse(flat.eq_dag(flip_dag))
self.assertFalse(flip_flat.eq_dag(dag))
self.assertFalse(flip_flat.eq_dag(flip_dag))
self.assertFalse(dag.eq_dag(flip_dag))
def test_normalize_mpileaks(self):
# Spec parsed in from a string
spec = Spec('mpileaks ^mpich ^callpath ^dyninst ^libelf@1.8.11 ^libdwarf')
# What that spec should look like after parsing
expected_flat = Spec(
'mpileaks', Spec('mpich'), Spec('callpath'), Spec('dyninst'),
Spec('libelf@1.8.11'), Spec('libdwarf'))
# What it should look like after normalization
mpich = Spec('mpich')
libelf = Spec('libelf@1.8.11')
expected_normalized = Spec(
@ -257,7 +325,10 @@ def test_normalize_mpileaks(self):
mpich),
mpich)
expected_non_unique_nodes = Spec(
# Similar to normalized spec, but now with copies of the same
# libelf node. Normalization should result in a single unique
# node for each package, so this is the wrong DAG.
non_unique_nodes = Spec(
'mpileaks',
Spec('callpath',
Spec('dyninst',
@ -267,21 +338,33 @@ def test_normalize_mpileaks(self):
mpich),
Spec('mpich'))
self.assertEqual(expected_normalized, expected_non_unique_nodes)
self.assertEqual(str(expected_normalized), str(expected_non_unique_nodes))
self.assertEqual(str(spec), str(expected_non_unique_nodes))
self.assertEqual(str(expected_normalized), str(spec))
# All specs here should be equal under regular equality
specs = (spec, expected_flat, expected_normalized, non_unique_nodes)
for lhs, rhs in zip(specs, specs):
self.assertEqual(lhs, rhs)
self.assertEqual(str(lhs), str(rhs))
# Test that equal and equal_dag are doing the right thing
self.assertEqual(spec, expected_flat)
self.assertNotEqual(spec, expected_normalized)
self.assertNotEqual(spec, expected_non_unique_nodes)
self.assertTrue(spec.eq_dag(expected_flat))
self.assertEqual(spec, expected_normalized)
self.assertFalse(spec.eq_dag(expected_normalized))
self.assertEqual(spec, non_unique_nodes)
self.assertFalse(spec.eq_dag(non_unique_nodes))
spec.normalize()
self.assertNotEqual(spec, expected_flat)
# After normalizing, spec_dag_equal should match the normalized spec.
self.assertEqual(spec, expected_flat)
self.assertFalse(spec.eq_dag(expected_flat))
self.assertEqual(spec, expected_normalized)
self.assertEqual(spec, expected_non_unique_nodes)
self.assertTrue(spec.eq_dag(expected_normalized))
self.assertEqual(spec, non_unique_nodes)
self.assertFalse(spec.eq_dag(non_unique_nodes))
def test_normalize_with_virtual_package(self):
@ -302,10 +385,63 @@ def test_normalize_with_virtual_package(self):
def test_contains(self):
spec = Spec('mpileaks ^mpi ^libelf@1.8.11 ^libdwarf')
self.assertIn(Spec('mpi'), spec)
self.assertIn(Spec('libelf'), spec)
self.assertIn(Spec('libelf@1.8.11'), spec)
self.assertNotIn(Spec('libelf@1.8.12'), spec)
self.assertIn(Spec('libdwarf'), spec)
self.assertNotIn(Spec('libgoblin'), spec)
self.assertIn(Spec('mpileaks'), spec)
self.assertTrue(Spec('mpi') in spec)
self.assertTrue(Spec('libelf') in spec)
self.assertTrue(Spec('libelf@1.8.11') in spec)
self.assertFalse(Spec('libelf@1.8.12') in spec)
self.assertTrue(Spec('libdwarf') in spec)
self.assertFalse(Spec('libgoblin') in spec)
self.assertTrue(Spec('mpileaks') in spec)
def test_copy_simple(self):
orig = Spec('mpileaks')
copy = orig.copy()
self.check_links(copy)
self.assertEqual(orig, copy)
self.assertTrue(orig.eq_dag(copy))
self.assertEqual(orig._normal, copy._normal)
self.assertEqual(orig._concrete, copy._concrete)
# ensure no shared nodes bt/w orig and copy.
orig_ids = set(id(s) for s in orig.traverse())
copy_ids = set(id(s) for s in copy.traverse())
self.assertFalse(orig_ids.intersection(copy_ids))
def test_copy_normalized(self):
orig = Spec('mpileaks')
orig.normalize()
copy = orig.copy()
self.check_links(copy)
self.assertEqual(orig, copy)
self.assertTrue(orig.eq_dag(copy))
self.assertEqual(orig._normal, copy._normal)
self.assertEqual(orig._concrete, copy._concrete)
# ensure no shared nodes bt/w orig and copy.
orig_ids = set(id(s) for s in orig.traverse())
copy_ids = set(id(s) for s in copy.traverse())
self.assertFalse(orig_ids.intersection(copy_ids))
def test_copy_concretized(self):
orig = Spec('mpileaks')
orig.concretize()
copy = orig.copy()
self.check_links(copy)
self.assertEqual(orig, copy)
self.assertTrue(orig.eq_dag(copy))
self.assertEqual(orig._normal, copy._normal)
self.assertEqual(orig._concrete, copy._concrete)
# ensure no shared nodes bt/w orig and copy.
orig_ids = set(id(s) for s in orig.traverse())
copy_ids = set(id(s) for s in copy.traverse())
self.assertFalse(orig_ids.intersection(copy_ids))

View File

@ -51,28 +51,20 @@
stage_name = 'spack-test-stage'
class with_tmp(object):
"""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.
@contextmanager
def use_tmp(use_tmp):
"""Allow some test code to be executed with spack.use_tmp_stage
set to a certain value. Context manager makes sure it's reset
on failure.
"""
def __init__(self, use_tmp):
self.use_tmp = use_tmp
def __call__(self, fun):
use_tmp = self.use_tmp
def new_test_function(self):
old_tmp = spack.use_tmp_stage
spack.use_tmp_stage = use_tmp
fun(self)
spack.use_tmp_stage = old_tmp
return new_test_function
old_tmp = spack.use_tmp_stage
spack.use_tmp_stage = use_tmp
yield
spack.use_tmp_stage = old_tmp
class StageTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
def setUp(self):
"""This sets up a mock archive to fetch, and a mock temp space for use
by the Stage class. It doesn't actually create the Stage -- that
is done by individual tests.
@ -92,52 +84,58 @@ def setUpClass(cls):
tar('czf', archive_name, archive_dir)
# Make spack use the test environment for tmp stuff.
cls.old_tmp_dirs = spack.tmp_dirs
self.old_tmp_dirs = spack.tmp_dirs
spack.tmp_dirs = [test_tmp_path]
# record this since this test changes to directories that will
# be removed.
self.working_dir = os.getcwd()
@classmethod
def tearDownClass(cls):
def tearDown(self):
"""Blows away the test environment directory."""
shutil.rmtree(test_files_dir)
# chdir back to original working dir
os.chdir(self.working_dir)
# restore spack's original tmp environment
spack.tmp_dirs = cls.old_tmp_dirs
spack.tmp_dirs = self.old_tmp_dirs
def get_stage_path(self, stage, stage_name):
"""Figure out based on a stage and an intended name where it should
be living. This depends on whether it's named or not.
"""Figure out where a stage should be living. This depends on
whether it's named.
"""
if stage_name:
if stage_name is not None:
# If it is a named stage, we know where the stage should be
stage_path = join_path(spack.stage_path, stage_name)
return 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
self.assertIsNotNone(stage_path)
self.assertEqual(
os.path.commonprefix((stage_path, spack.stage_path)),
spack.stage_path)
return stage_path
self.assertTrue(stage.path is not None)
self.assertTrue(stage.path.startswith(spack.stage_path))
return stage.path
def check_setup(self, stage, stage_name):
"""Figure out whether a stage was set up correctly."""
stage_path = self.get_stage_path(stage, stage_name)
# Ensure stage was created in the spack stage directory
self.assertTrue(os.path.isdir(stage_path))
if spack.use_tmp_stage:
# Make sure everything was created and linked correctly for
# a tmp stage.
# Check that the stage dir is really a symlink.
self.assertTrue(os.path.islink(stage_path))
# Make sure it points to a valid directory
target = os.path.realpath(stage_path)
self.assertTrue(os.path.isdir(target))
self.assertFalse(os.path.islink(target))
self.assertEqual(
os.path.commonprefix((target, test_tmp_path)),
test_tmp_path)
# Make sure the directory is in the place we asked it to
# be (see setUp and tearDown)
self.assertTrue(target.startswith(test_tmp_path))
else:
# Make sure the stage path is NOT a link for a non-tmp stage
@ -146,15 +144,15 @@ def check_setup(self, stage, stage_name):
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.assertTrue(archive_name in os.listdir(stage_path))
self.assertEqual(join_path(stage_path, archive_name),
stage.archive_file)
def check_expand_archive(self, stage, stage_name):
stage_path = self.get_stage_path(stage, stage_name)
self.assertIn(archive_name, os.listdir(stage_path))
self.assertIn(archive_dir, os.listdir(stage_path))
self.assertTrue(archive_name in os.listdir(stage_path))
self.assertTrue(archive_dir in os.listdir(stage_path))
self.assertEqual(
join_path(stage_path, archive_dir),
@ -192,32 +190,40 @@ def check_destroy(self, stage, stage_name):
self.assertFalse(os.path.exists(target))
def checkSetupAndDestroy(self, stage_name=None):
stage = Stage(archive_url, name=stage_name)
self.check_setup(stage, stage_name)
stage.destroy()
self.check_destroy(stage, stage_name)
@with_tmp(True)
def test_setup_and_destroy_name_with_tmp(self):
self.checkSetupAndDestroy(stage_name)
with use_tmp(True):
stage = Stage(archive_url, name=stage_name)
self.check_setup(stage, stage_name)
stage.destroy()
self.check_destroy(stage, stage_name)
@with_tmp(False)
def test_setup_and_destroy_name_without_tmp(self):
self.checkSetupAndDestroy(stage_name)
with use_tmp(False):
stage = Stage(archive_url, name=stage_name)
self.check_setup(stage, stage_name)
stage.destroy()
self.check_destroy(stage, stage_name)
@with_tmp(True)
def test_setup_and_destroy_no_name_with_tmp(self):
self.checkSetupAndDestroy(None)
with use_tmp(True):
stage = Stage(archive_url)
self.check_setup(stage, None)
stage.destroy()
self.check_destroy(stage, None)
@with_tmp(False)
def test_setup_and_destroy_no_name_without_tmp(self):
self.checkSetupAndDestroy(None)
with use_tmp(False):
stage = Stage(archive_url)
self.check_setup(stage, None)
stage.destroy()
self.check_destroy(stage, None)
def test_chdir(self):
@ -286,7 +292,7 @@ def test_restage(self):
with closing(open('foobar', 'w')) as file:
file.write("this file is to be destroyed.")
self.assertIn('foobar', os.listdir(stage.expanded_archive_path))
self.assertTrue('foobar' in os.listdir(stage.expanded_archive_path))
# Make sure the file is not there after restage.
stage.restage()
@ -295,7 +301,7 @@ def test_restage(self):
stage.chdir_to_archive()
self.check_chdir_to_archive(stage, stage_name)
self.assertNotIn('foobar', os.listdir(stage.expanded_archive_path))
self.assertFalse('foobar' in os.listdir(stage.expanded_archive_path))
stage.destroy()
self.check_destroy(stage, stage_name)

View File

@ -82,12 +82,16 @@ def parse_version_string_with_indices(path):
"""Try to extract a version string from a filename or URL. This is taken
largely from Homebrew's Version class."""
if os.path.isdir(path):
stem = os.path.basename(path)
elif re.search(r'((?:sourceforge.net|sf.net)/.*)/download$', path):
stem = comp.stem(os.path.dirname(path))
else:
stem = comp.stem(path)
# Strip off sourceforge download stuffix.
if re.search(r'((?:sourceforge.net|sf.net)/.*)/download$', path):
path = os.path.dirname(path)
# Strip archive extension
path = comp.strip_extension(path)
# Take basename to avoid including parent dirs in version name
# Remember the offset of the stem in the full path.
stem = os.path.basename(path)
version_types = [
# GitHub tarballs, e.g. v1.2.3
@ -137,10 +141,10 @@ def parse_version_string_with_indices(path):
(r'_((\d+\.)+\d+[a-z]?)[.]orig$', stem),
# e.g. http://www.openssl.org/source/openssl-0.9.8s.tar.gz
(r'-([^-]+)', stem),
(r'-([^-]+(-alpha|-beta)?)', stem),
# e.g. astyle_1.23_macosx.tar.gz
(r'_([^_]+)', stem),
(r'_([^_]+(_alpha|_beta)?)', stem),
# e.g. http://mirrors.jenkins-ci.org/war/1.486/jenkins.war
(r'\/(\d\.\d+)\/', path),
@ -152,7 +156,9 @@ def parse_version_string_with_indices(path):
regex, match_string = vtype[:2]
match = re.search(regex, match_string)
if match and match.group(1) is not None:
return match.group(1), match.start(1), match.end(1)
version = match.group(1)
start = path.index(version)
return version, start, start+len(version)
raise UndetectableVersionError(path)

View File

@ -48,7 +48,7 @@ def decompressor_for(path):
return tar
def stem(path):
def strip_extension(path):
"""Get the part of a path that does not include its compressed
type extension."""
for type in ALLOWED_ARCHIVE_TYPES:

View File

@ -35,7 +35,7 @@
hashlib.sha512 ]
"""Index for looking up hasher for a digest."""
_size_to_hash = { h().digest_size : h for h in _acceptable_hashes }
_size_to_hash = dict((h().digest_size, h) for h in _acceptable_hashes)
def checksum(hashlib_algo, filename, **kwargs):

View File

@ -121,7 +121,7 @@ def which(name, **kwargs):
for dir in path:
exe = os.path.join(dir, name)
if os.access(exe, os.X_OK):
if os.path.isfile(exe) and os.access(exe, os.X_OK):
return Executable(exe)
if required:

View File

@ -33,7 +33,9 @@ def comma_list(sequence, article=''):
return sequence[0]
else:
out = ', '.join(str(s) for s in sequence[:-1])
out += ', '
if len(sequence) != 2:
out += ',' # oxford comma
out += ' '
if article:
out += article + ' '
out += str(sequence[-1])

View File

@ -47,7 +47,8 @@
import sys
import re
from bisect import bisect_left
from functools import total_ordering, wraps
from functools import wraps
from external.functools import total_ordering
import llnl.util.compare.none_high as none_high
import llnl.util.compare.none_low as none_low
@ -181,7 +182,7 @@ def a_or_n(seg):
# Add possible alpha or beta indicator at the end of each segemnt
# We treat these specially b/c they're so common.
wc += '[ab]?)?' * (len(segments) - 1)
wc += '(?:[a-z]|alpha|beta)?)?' * (len(segments) - 1)
return wc

View File

@ -0,0 +1,23 @@
########################################################################
# Prepends directories to path, if they exist.
# pathadd /path/to/dir # add to PATH
# or pathadd OTHERPATH /path/to/dir # add to OTHERPATH
########################################################################
# If no variable name is supplied, just append to PATH
# otherwise append to that variable.
set _pa_varname = PATH;
set _pa_new_path = $_pa_args[1];
[ $#_pa_args -gt 1 ] && set _pa_varname = $_pa_args[1] && set _pa_new_path = $_pa_args[2];
# Check whether the variable is set yet.
set _pa_old_value = ""
eval set _pa_set = '$?'$_pa_varname
[ $_pa_set -eq 1 ] && eval set _pa_old_value='$'$_pa_varname;
# Do the actual prepending here, if it is a dir and not already in the path
if ( -d $_pa_new_path && \:$_pa_old_value\: !~ *\:$_pa_new_path\:* ) then
[ -n "$_pa_old_value" ] && setenv $_pa_varname $_pa_new_path\:$_pa_old_value
[ -z "$_pa_old_value" ] && setenv $_pa_varname $_pa_new_path
endif
unset _pa_args _pa_new_path _pa_old_value _pa_set _pa_varname

101
share/spack/csh/spack.csh Normal file
View File

@ -0,0 +1,101 @@
########################################################################
# This is a wrapper around the spack command that forwards calls to
# 'spack use' and 'spack unuse' to shell functions. This in turn
# allows them to be used to invoke dotkit functions.
#
# 'spack use' is smarter than just 'use' because it converts its
# arguments into a unique spack spec that is then passed to dotkit
# commands. This allows the user to use packages without knowing all
# their installation details.
#
# e.g., rather than requring a full spec for libelf, the user can type:
#
# spack use libelf
#
# This will first find the available libelf dotkits and use a
# matching one. If there are two versions of libelf, the user would
# need to be more specific, e.g.:
#
# spack use libelf@0.8.13
#
# This is very similar to how regular spack commands work and it
# avoids the need to come up with a user-friendly naming scheme for
# spack dotfiles.
########################################################################
# accumulate initial flags for main spack command
set _sp_flags = ""
while ( $#_sp_args > 0 )
if ( "$_sp_args[1]" !~ "-*" ) break
set _sp_flags = "$_sp_flags $_sp_args[1]"
shift _sp_args
end
# h and V flags don't require further output parsing.
if ( "$_sp_flags" =~ *h* || "$_sp_flags" =~ *V* ) then
\spack $_sp_flags $_sp_args
goto _sp_end
endif
# Set up args -- we want a subcommand and a spec.
set _sp_subcommand=""
set _sp_spec=""
[ $#_sp_args -gt 0 ] && set _sp_subcommand = ($_sp_args[1])
[ $#_sp_args -gt 1 ] && set _sp_spec = ($_sp_args[2-])
# Figure out what type of module we're running here.
set _sp_modtype = ""
switch ($_sp_subcommand)
case cd:
shift _sp_args
cd `spack location $_sp_args`
breaksw
case use:
case unuse:
case load:
case unload:
set _sp_module_args=""""
if ( "$_sp_spec" =~ "-*" ) then
set _sp_module_args = $_sp_spec[1]
shift _sp_spec
set _sp_spec = ($_sp_spec)
endif
# Here the user has run use or unuse with a spec. Find a matching
# spec using 'spack module find', then use the appropriate module
# tool's commands to add/remove the result from the environment.
switch ($_sp_subcommand)
case "use":
set _sp_full_spec = ( "`\spack $_sp_flags module find dotkit $_sp_spec`" )
if ( $? == 0 ) then
use $_sp_module_args $_sp_full_spec
endif
breaksw
case "unuse":
set _sp_full_spec = ( "`\spack $_sp_flags module find dotkit $_sp_spec`" )
if ( $? == 0 ) then
unuse $_sp_module_args $_sp_full_spec
endif
breaksw
case "load":
set _sp_full_spec = ( "`\spack $_sp_flags module find tcl $_sp_spec`" )
if ( $? == 0 ) then
module load $_sp_module_args $_sp_full_spec
endif
breaksw
case "unload":
set _sp_full_spec = ( "`\spack $_sp_flags module find tcl $_sp_spec`" )
if ( $? == 0 ) then
module unload $_sp_module_args $_sp_full_spec
endif
breaksw
endsw
breaksw
default:
\spack $_sp_args
breaksw
endsw
_sp_end:
unset _sp_args _sp_full_spec _sp_modtype _sp_module_args
unset _sp_sh_cmd _sp_spec _sp_subcommand _sp_flags

View File

@ -1,132 +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 is part of Spack and sets up the spack environment for
# bash shells. This includes dotkit support as well as putting spack
# in your path. Source it like this:
#
# . /path/to/spack/share/spack/setup-env.bash
#
#
########################################################################
# This is a wrapper around the spack command that forwards calls to
# 'spack use' and 'spack unuse' to shell functions. This in turn
# allows them to be used to invoke dotkit functions.
#
# 'spack use' is smarter than just 'use' because it converts its
# arguments into a unique spack spec that is then passed to dotkit
# commands. This allows the user to use packages without knowing all
# their installation details.
#
# e.g., rather than requring a full spec for libelf, the user can type:
#
# spack use libelf
#
# This will first find the available libelf dotkits and use a
# matching one. If there are two versions of libelf, the user would
# need to be more specific, e.g.:
#
# spack use libelf@0.8.13
#
# This is very similar to how regular spack commands work and it
# avoids the need to come up with a user-friendly naming scheme for
# spack dotfiles.
########################################################################
function spack {
_spack_subcommand=$1; shift
_spack_spec="$@"
# Filter out use and unuse. For any other commands, just run the
# command.
case $_spack_subcommand in
"use") ;;
"unuse") ;;
*)
command spack $_spack_subcommand "$@"
return
;;
esac
# If no args or -h, just run that command as well.
if [ -z "$1" -o "$1" = "-h" ]; then
command spack $_spack_subcommand -h
return
fi
# Shift any other args for use off before parsing spec.
_spack_use_args=""
if [[ "$1" =~ ^- ]]; then
_spack_use_args="$1"; shift
_spack_spec="$@"
fi
# Here the user has run use or unuse with a spec. Find a matching
# spec with a dotkit using spack dotkit, then use or unuse the
# result. If spack dotkit comes back with an error, do nothing.
if _spack_full_spec=$(command spack dotkit $_spack_spec); then
$_spack_subcommand $_spack_use_args $_spack_full_spec
fi
}
########################################################################
# Prepends directories to path, if they exist.
# pathadd /path/to/dir # add to PATH
# or pathadd OTHERPATH /path/to/dir # add to OTHERPATH
########################################################################
function _spack_pathadd {
# If no variable name is supplied, just append to PATH
# otherwise append to that variable.
varname=PATH
path="$1"
if [ -n "$2" ]; then
varname="$1"
path="$2"
fi
# Do the actual prepending here.
eval "oldvalue=\"\$$varname\""
if [ -d "$path" ] && [[ ":$oldvalue:" != *":$path:"* ]]; then
if [ -n "$oldvalue" ]; then
eval "export $varname=\"$path:$oldvalue\""
else
export $varname="$path"
fi
fi
}
#
# Set up dotkit and path in the user environment
#
_spack_share_dir="$(dirname ${BASH_SOURCE[0]})"
_spack_prefix="$(dirname $(dirname $_spack_share_dir))"
_spack_pathadd DK_NODE "$_spack_share_dir/dotkit"
_spack_pathadd PATH "$_spack_prefix/bin"

47
share/spack/setup-env.csh Executable file
View File

@ -0,0 +1,47 @@
##############################################################################
# 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 is part of Spack and sets up the spack environment for
# csh and tcsh. This includes dotkit support, module support, and
# it also puts spack in your path. Source it like this:
#
# setenv SPACK_ROOT /path/to/spack
# source $SPACK_ROOT/share/spack/setup-env.csh
#
if ($?SPACK_ROOT) then
set _spack_source_file = $SPACK_ROOT/share/spack/setup-env.csh
set _spack_share_dir = $SPACK_ROOT/share/spack
# Command aliases point at separate source files
alias spack 'set _sp_args = (\!*); source $_spack_share_dir/csh/spack.csh'
alias _spack_pathadd 'set _pa_args = (\!*) && source $_spack_share_dir/csh/pathadd.csh'
# Set up modules and dotkit search paths in the user environment
# TODO: fix SYS_TYPE to something non-LLNL-specific
_spack_pathadd DK_NODE "$_spack_share_dir/dotkit/$SYS_TYPE"
_spack_pathadd MODULEPATH "$_spack_share_dir/modules/$SYS_TYPE"
_spack_pathadd PATH "$SPACK_ROOT/bin"
endif

169
share/spack/setup-env.sh Executable file
View File

@ -0,0 +1,169 @@
##############################################################################
# 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 is part of Spack and sets up the spack environment for
# bash and zsh. This includes dotkit support, module support, and
# it also puts spack in your path. Source it like this:
#
# . /path/to/spack/share/spack/setup-env.sh
#
########################################################################
# This is a wrapper around the spack command that forwards calls to
# 'spack use' and 'spack unuse' to shell functions. This in turn
# allows them to be used to invoke dotkit functions.
#
# 'spack use' is smarter than just 'use' because it converts its
# arguments into a unique spack spec that is then passed to dotkit
# commands. This allows the user to use packages without knowing all
# their installation details.
#
# e.g., rather than requring a full spec for libelf, the user can type:
#
# spack use libelf
#
# This will first find the available libelf dotkits and use a
# matching one. If there are two versions of libelf, the user would
# need to be more specific, e.g.:
#
# spack use libelf@0.8.13
#
# This is very similar to how regular spack commands work and it
# avoids the need to come up with a user-friendly naming scheme for
# spack dotfiles.
########################################################################
function spack {
# accumulate initial flags for main spack command
_sp_flags=""
while [[ "$1" =~ ^- ]]; do
_sp_flags="$_sp_flags $1"
shift
done
# h and V flags don't require further output parsing.
if [[ "$_sp_flags" =~ *h* || "$_sp_flags" =~ *V* ]]; then
command spack $_sp_flags "$@"
return
fi
_sp_subcommand=$1; shift
_sp_spec="$@"
# Filter out use and unuse. For any other commands, just run the
# command.
case $_sp_subcommand in
"cd")
cd $(spack location "$@")
return
;;
"use"|"unuse"|"load"|"unload")
# Shift any other args for use off before parsing spec.
_sp_module_args=""
if [[ "$1" =~ ^- ]]; then
_sp_module_args="$1"; shift
_sp_spec="$@"
fi
# Here the user has run use or unuse with a spec. Find a matching
# spec using 'spack module find', then use the appropriate module
# tool's commands to add/remove the result from the environment.
# If spack module command comes back with an error, do nothing.
case $_sp_subcommand in
"use")
if _sp_full_spec=$(command spack $_sp_flags module find dotkit $_sp_spec); then
use $_sp_module_args $_sp_full_spec
fi ;;
"unuse")
if _sp_full_spec=$(command spack $_sp_flags module find dotkit $_sp_spec); then
unuse $_sp_module_args $_sp_full_spec
fi ;;
"load")
if _sp_full_spec=$(command spack $_sp_flags module find dotkit $_sp_spec); then
module load $_sp_module_args $_sp_full_spec
fi ;;
"unload")
if _sp_full_spec=$(command spack $_sp_flags module find dotkit $_sp_spec); then
module unload $_sp_module_args $_sp_full_spec
fi ;;
esac
;;
*)
command spack $_sp_flags $_sp_subcommand $_sp_spec
;;
esac
}
########################################################################
# Prepends directories to path, if they exist.
# pathadd /path/to/dir # add to PATH
# or pathadd OTHERPATH /path/to/dir # add to OTHERPATH
########################################################################
function _spack_pathadd {
# If no variable name is supplied, just append to PATH
# otherwise append to that variable.
_pa_varname=PATH
_pa_new_path="$1"
if [ -n "$2" ]; then
_pa_varname="$1"
_pa_new_path="$2"
fi
# Do the actual prepending here.
eval "_pa_oldvalue=\$${_pa_varname}"
if [ -d "$_pa_new_path" ] && [[ ":$_pa_oldvalue:" != *":$_pa_new_path:"* ]]; then
if [ -n "$_pa_oldvalue" ]; then
eval "export $_pa_varname=\"$_pa_new_path:$_pa_oldvalue\""
else
export $_pa_varname="$_pa_new_path"
fi
fi
}
#
# Figure out where this file is. Below code needs to be portable to
# bash and zsh.
#
_sp_source_file="${BASH_SOURCE[0]}" # Bash's location of last sourced file.
if [ -z "$_sp_source_file" ]; then
_sp_source_file="$0:A" # zsh way to do it
if [[ "$_sp_source_file" == *":A" ]]; then
# Not zsh either... bail out with plain old $0,
# which WILL NOT work if this is sourced indirectly.
_sp_source_file="$0"
fi
fi
#
# Set up modules and dotkit search paths in the user environment
#
_sp_share_dir="$(dirname $_sp_source_file)"
_sp_prefix="$(dirname $(dirname $_sp_share_dir))"
# TODO: fix SYS_TYPE to something non-LLNL-specific
_spack_pathadd DK_NODE "$_sp_share_dir/dotkit/$SYS_TYPE"
_spack_pathadd MODULEPATH "$_sp_share_dir/modules/$SYS_TYPE"
_spack_pathadd PATH "$_sp_prefix/bin"

View File

@ -28,9 +28,9 @@ class Callpath(Package):
homepage = "https://github.com/tgamblin/callpath"
url = "http://github.com/tgamblin/callpath-1.0.tar.gz"
versions = { 0.8 : 'foobarbaz',
0.9 : 'foobarbaz',
1.0 : 'foobarbaz' }
version(0.8, 'foobarbaz')
version(0.9, 'foobarbaz')
version(1.0, 'foobarbaz')
depends_on("dyninst")
depends_on("mpi")

View File

@ -28,7 +28,7 @@ class DirectMpich(Package):
homepage = "http://www.example.com"
url = "http://www.example.com/direct_mpich-1.0.tar.gz"
versions = { 1.0 : 'foobarbaz' }
version('1.0', 'foobarbaz')
depends_on('mpich')

View File

@ -29,9 +29,8 @@ class Dyninst(Package):
url = "http://www.dyninst.org/sites/default/files/downloads/dyninst/8.1.2/DyninstAPI-8.1.2.tgz"
list_url = "http://www.dyninst.org/downloads/dyninst-8.x"
versions = {
'8.1.2' : 'bf03b33375afa66fe0efa46ce3f4b17a',
'8.1.1' : '1f8743e3a5662b25ce64a7edf647e77d' }
version('8.1.2', 'bf03b33375afa66fe0efa46ce3f4b17a')
version('8.1.1', '1f8743e3a5662b25ce64a7edf647e77d')
depends_on("libelf")
depends_on("libdwarf")

View File

@ -27,7 +27,8 @@
class Fake(Package):
homepage = "http://www.fake-spack-example.org"
url = "http://www.fake-spack-example.org/downloads/fake-1.0.tar.gz"
versions = { '1.0' : 'foobarbaz' }
version('1.0', 'foobarbaz')
def install(self, spec, prefix):
pass

View File

@ -32,7 +32,7 @@ class IndirectMpich(Package):
homepage = "http://www.example.com"
url = "http://www.example.com/indirect_mpich-1.0.tar.gz"
versions = { 1.0 : 'foobarbaz' }
version(1.0, 'foobarbaz')
depends_on('mpi')
depends_on('direct_mpich')

View File

@ -33,10 +33,10 @@ class Libdwarf(Package):
url = "http://www.prevanders.net/libdwarf-20130729.tar.gz"
list_url = homepage
versions = { 20130729 : "64b42692e947d5180e162e46c689dfbf",
20130207 : 'foobarbaz',
20111030 : 'foobarbaz',
20070703 : 'foobarbaz' }
version(20130729, "64b42692e947d5180e162e46c689dfbf")
version(20130207, 'foobarbaz')
version(20111030, 'foobarbaz')
version(20070703, 'foobarbaz')
depends_on("libelf")

View File

@ -28,9 +28,9 @@ class Libelf(Package):
homepage = "http://www.mr511.de/software/english.html"
url = "http://www.mr511.de/software/libelf-0.8.13.tar.gz"
versions = {'0.8.13' : '4136d7b4c04df68b686570afa26988ac',
'0.8.12' : 'e21f8273d9f5f6d43a59878dc274fec7',
'0.8.10' : '9db4d36c283d9790d8fa7df1f4d7b4d9' }
version('0.8.13', '4136d7b4c04df68b686570afa26988ac')
version('0.8.12', 'e21f8273d9f5f6d43a59878dc274fec7')
version('0.8.10', '9db4d36c283d9790d8fa7df1f4d7b4d9')
def install(self, spec, prefix):
configure("--prefix=%s" % prefix,

View File

@ -30,11 +30,11 @@ class Mpich(Package):
list_url = "http://www.mpich.org/static/downloads/"
list_depth = 2
versions = { '3.0.4' : '9c5d5d4fe1e17dd12153f40bc5b6dbc0',
'3.0.3' : 'foobarbaz',
'3.0.2' : 'foobarbaz',
'3.0.1' : 'foobarbaz',
'3.0' : 'foobarbaz' }
version('3.0.4', '9c5d5d4fe1e17dd12153f40bc5b6dbc0')
version('3.0.3', 'foobarbaz')
version('3.0.2', 'foobarbaz')
version('3.0.1', 'foobarbaz')
version('3.0', 'foobarbaz')
provides('mpi@:3', when='@3:')
provides('mpi@:1', when='@1:')

View File

@ -30,12 +30,12 @@ class Mpich2(Package):
list_url = "http://www.mpich.org/static/downloads/"
list_depth = 2
versions = { '1.5' : '9c5d5d4fe1e17dd12153f40bc5b6dbc0',
'1.4' : 'foobarbaz',
'1.3' : 'foobarbaz',
'1.2' : 'foobarbaz',
'1.1' : 'foobarbaz',
'1.0' : 'foobarbaz' }
version('1.5', '9c5d5d4fe1e17dd12153f40bc5b6dbc0')
version('1.4', 'foobarbaz')
version('1.3', 'foobarbaz')
version('1.2', 'foobarbaz')
version('1.1', 'foobarbaz')
version('1.0', 'foobarbaz')
provides('mpi@:2.0')
provides('mpi@:2.1', when='@1.1:')

View File

@ -28,10 +28,10 @@ class Mpileaks(Package):
homepage = "http://www.llnl.gov"
url = "http://www.llnl.gov/mpileaks-1.0.tar.gz"
versions = { 1.0 : 'foobarbaz',
2.1 : 'foobarbaz',
2.2 : 'foobarbaz',
2.3 : 'foobarbaz' }
version(1.0, 'foobarbaz')
version(2.1, 'foobarbaz')
version(2.2, 'foobarbaz')
version(2.3, 'foobarbaz')
depends_on("mpi")
depends_on("callpath")

View File

@ -30,7 +30,7 @@ class TrivialInstallTestPackage(Package):
homepage = "http://www.example.com/trivial_install"
url = "http://www.unit-test-should-replace-this-url/trivial_install-1.0.tar.gz"
versions = { '1.0' : 'foobarbaz' }
version('1.0', 'foobarbaz')
def install(self, spec, prefix):
configure('--prefix=%s' % prefix)

View File

@ -30,7 +30,7 @@ class Zmpi(Package):
homepage = "http://www.spack-fake-zmpi.org"
url = "http://www.spack-fake-zmpi.org/downloads/zmpi-1.0.tar.gz"
versions = { '1.0' : 'foobarbaz' }
version('1.0', 'foobarbaz')
provides('mpi@:10.0')
depends_on('fake')

View File

@ -11,17 +11,15 @@ class Samrai(Package):
url = "https://computation-rnd.llnl.gov/SAMRAI/download/SAMRAI-v3.7.3.tar.gz"
list_url = homepage
versions = {
'3.7.3' : '12d574eacadf8c9a70f1bb4cd1a69df6',
'3.7.2' : 'f6a716f171c9fdbf3cb12f71fa6e2737',
'3.6.3-beta' : 'ef0510bf2893042daedaca434e5ec6ce',
'3.5.2-beta' : 'd072d9d681eeb9ada15ce91bea784274',
'3.5.0-beta' : '1ad18a319fc573e12e2b1fbb6f6b0a19',
'3.4.1-beta' : '00814cbee2cb76bf8302aff56bbb385b',
'3.3.3-beta' : '1db3241d3e1cab913dc310d736c34388',
'3.3.2-beta' : 'e598a085dab979498fcb6c110c4dd26c',
'2.4.4' : '04fb048ed0efe7c531ac10c81cc5f6ac',
}
version('3.7.3', '12d574eacadf8c9a70f1bb4cd1a69df6')
version('3.7.2', 'f6a716f171c9fdbf3cb12f71fa6e2737')
version('3.6.3-beta', 'ef0510bf2893042daedaca434e5ec6ce')
version('3.5.2-beta', 'd072d9d681eeb9ada15ce91bea784274')
version('3.5.0-beta', '1ad18a319fc573e12e2b1fbb6f6b0a19')
version('3.4.1-beta', '00814cbee2cb76bf8302aff56bbb385b')
version('3.3.3-beta', '1db3241d3e1cab913dc310d736c34388')
version('3.3.2-beta', 'e598a085dab979498fcb6c110c4dd26c')
version('2.4.4', '04fb048ed0efe7c531ac10c81cc5f6ac')
depends_on("mpi")
depends_on("zlib")

View File

@ -0,0 +1,41 @@
##############################################################################
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack import *
class AdeptUtils(Package):
"""Utility libraries for LLNL performance tools."""
homepage = "https://github.com/scalability-llnl/adept-utils"
url = "https://github.com/scalability-llnl/adept-utils/archive/v1.0.tar.gz"
version('1.0', '5c6cd9badce56c945ac8551e34804397')
depends_on("boost")
depends_on("mpi")
def install(self, spec, prefix):
cmake(*std_cmake_args)
make()
make("install")

View File

@ -14,32 +14,31 @@ class Boost(Package):
list_url = "http://sourceforge.net/projects/boost/files/boost/"
list_depth = 2
versions = {
'1.55.0' : 'd6eef4b4cacb2183f2bf265a5a03a354',
'1.54.0' : '15cb8c0803064faef0c4ddf5bc5ca279',
'1.53.0' : 'a00d22605d5dbcfb4c9936a9b35bc4c2',
'1.52.0' : '3a855e0f919107e0ca4de4d84ad3f750',
'1.51.0' : '4b6bd483b692fd138aef84ed2c8eb679',
'1.50.0' : '52dd00be775e689f55a987baebccc462',
'1.49.0' : '0d202cb811f934282dea64856a175698',
'1.48.0' : 'd1e9a7a7f532bb031a3c175d86688d95',
'1.47.0' : 'a2dc343f7bc7f83f8941e47ed4a18200',
'1.46.1' : '7375679575f4c8db605d426fc721d506',
'1.46.0' : '37b12f1702319b73876b0097982087e0',
'1.45.0' : 'd405c606354789d0426bc07bea617e58',
'1.44.0' : 'f02578f5218f217a9f20e9c30e119c6a',
'1.43.0' : 'dd49767bfb726b0c774f7db0cef91ed1',
'1.42.0' : '7bf3b4eb841b62ffb0ade2b82218ebe6',
'1.41.0' : '8bb65e133907db727a2a825c5400d0a6',
'1.40.0' : 'ec3875caeac8c52c7c129802a8483bd7',
'1.39.0' : 'a17281fd88c48e0d866e1a12deecbcc0',
'1.38.0' : '5eca2116d39d61382b8f8235915cb267',
'1.37.0' : '8d9f990bfb7e83769fa5f1d6f065bc92',
'1.36.0' : '328bfec66c312150e4c2a78dcecb504b',
'1.35.0' : 'dce952a7214e72d6597516bcac84048b',
'1.34.1' : '2d938467e8a448a2c9763e0a9f8ca7e5',
'1.34.0' : 'ed5b9291ffad776f8757a916e1726ad0'
}
version('1.55.0', 'd6eef4b4cacb2183f2bf265a5a03a354')
version('1.54.0', '15cb8c0803064faef0c4ddf5bc5ca279')
version('1.53.0', 'a00d22605d5dbcfb4c9936a9b35bc4c2')
version('1.52.0', '3a855e0f919107e0ca4de4d84ad3f750')
version('1.51.0', '4b6bd483b692fd138aef84ed2c8eb679')
version('1.50.0', '52dd00be775e689f55a987baebccc462')
version('1.49.0', '0d202cb811f934282dea64856a175698')
version('1.48.0', 'd1e9a7a7f532bb031a3c175d86688d95')
version('1.47.0', 'a2dc343f7bc7f83f8941e47ed4a18200')
version('1.46.1', '7375679575f4c8db605d426fc721d506')
version('1.46.0', '37b12f1702319b73876b0097982087e0')
version('1.45.0', 'd405c606354789d0426bc07bea617e58')
version('1.44.0', 'f02578f5218f217a9f20e9c30e119c6a')
version('1.43.0', 'dd49767bfb726b0c774f7db0cef91ed1')
version('1.42.0', '7bf3b4eb841b62ffb0ade2b82218ebe6')
version('1.41.0', '8bb65e133907db727a2a825c5400d0a6')
version('1.40.0', 'ec3875caeac8c52c7c129802a8483bd7')
version('1.39.0', 'a17281fd88c48e0d866e1a12deecbcc0')
version('1.38.0', '5eca2116d39d61382b8f8235915cb267')
version('1.37.0', '8d9f990bfb7e83769fa5f1d6f065bc92')
version('1.36.0', '328bfec66c312150e4c2a78dcecb504b')
version('1.35.0', 'dce952a7214e72d6597516bcac84048b')
version('1.34.1', '2d938467e8a448a2c9763e0a9f8ca7e5')
version('1.34.0', 'ed5b9291ffad776f8757a916e1726ad0')
def url_for_version(self, version):
"""Handle Boost's weird URLs, which write the version two different ways."""

View File

@ -25,13 +25,20 @@
from spack import *
class Callpath(Package):
homepage = "https://github.com/tgamblin/callpath"
url = "http://github.com/tgamblin/callpath-0.2.tar.gz"
"""Library for representing callpaths consistently in
distributed-memory performance tools."""
homepage = "https://github.com/scalability-llnl/callpath"
url = "https://github.com/scalability-llnl/callpath/archive/v1.0.1.tar.gz"
version('1.0.1', '0047983d2a52c5c335f8ba7f5bab2325')
depends_on("dyninst")
depends_on("adept-utils")
depends_on("mpi")
def install(self, spec, prefix):
configure("--prefix=" + prefix)
# TODO: offer options for the walker used.
cmake('.', "-DCALLPATH_WALKER=dyninst", *std_cmake_args)
make()
make("install")

View File

@ -0,0 +1,47 @@
##############################################################################
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://scalability-llnl.github.io/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License (as published by
# the Free Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack import *
class Clang(Package):
"""The goal of the Clang project is to create a new C, C++,
Objective C and Objective C++ front-end for the LLVM compiler.
"""
homepage = "http://clang.llvm.org"
url = "http://llvm.org/releases/3.4.2/cfe-3.4.2.src.tar.gz"
depends_on("llvm")
version('3.4.2', '87945973b7c73038871c5f849a818588')
def install(self, spec, prefix):
env['CXXFLAGS'] = self.compiler.cxx11_flag
with working_dir('spack-build', create=True):
cmake('..',
'-DCLANG_PATH_TO_LLVM_BUILD=%s' % spec['llvm'].prefix,
'-DLLVM_MAIN_SRC_DIR=%s' % spec['llvm'].prefix,
*std_cmake_args)
make()
make("install")

View File

@ -27,7 +27,8 @@
class Cmake(Package):
homepage = 'https://www.cmake.org'
url = 'http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz'
versions = { '2.8.10.2' : '097278785da7182ec0aea8769d06860c' }
version('2.8.10.2', '097278785da7182ec0aea8769d06860c')
def install(self, spec, prefix):
configure('--prefix=' + prefix,

View File

@ -0,0 +1,55 @@
# FIXME: Add copyright statement
#
from spack import *
from contextlib import closing
class Cube(Package):
"""Cube the profile viewer for Score-P and Scalasca profiles. It
displays a multi-dimensional performance space consisting
of the dimensions (i) performance metric, (ii) call path,
and (iii) system resource."""
homepage = "http://www.scalasca.org/software/cube-4.x/download.html"
url = "http://apps.fz-juelich.de/scalasca/releases/cube/4.2/dist/cube-4.2.3.tar.gz"
version('4.2.3', '8f95b9531f5a8f8134f279c2767c9b20')
version('4.3TP1', 'a2090fbc7b2ba394bd5c09ba971e237f',
url = 'http://apps.fz-juelich.de/scalasca/releases/cube/4.3/dist/cube-4.3-TP1.tar.gz')
# Using CC as C++ compiler provides quirky workaround for a Score-P build system attempt
# to guess a matching C compiler when configuring scorep-score
backend_user_provided = """\
CC=cc
CXX=CC
F77=f77
FC=f90
#CFLAGS=-fPIC
#CXXFLAGS=-fPIC
"""
frontend_user_provided = """\
CC_FOR_BUILD=cc
CXX_FOR_BUILD=CC
F77_FOR_BUILD=f70
FC_FOR_BUILD=f90
"""
def install(self, spec, prefix):
# Use a custom compiler configuration, otherwise the score-p
# build system messes with spack's compiler settings.
# Create these three files in the build directory
with closing(open("vendor/common/build-config/platforms/platform-backend-user-provided", "w")) as backend_file:
backend_file.write(self.backend_user_provided)
with closing(open("vendor/common/build-config/platforms/platform-frontend-user-provided", "w")) as frontend_file:
frontend_file.write(self.frontend_user_provided)
configure_args = ["--prefix=%s" % prefix,
"--with-custom-compilers",
"--without-paraver",
"--without-gui"]
configure(*configure_args)
make(parallel=False)
make("install", parallel=False)

View File

@ -0,0 +1,20 @@
import os
from spack import *
class Dtcmp(Package):
"""The Datatype Comparison Library provides comparison operations and
parallel sort algorithms for MPI applications."""
homepage = "https://github.com/hpc/dtcmp"
url = "https://github.com/hpc/dtcmp/releases/download/v1.0.3/dtcmp-1.0.3.tar.gz"
version('1.0.3', 'cdd8ccf71e8ff67de2558594a7fcd317')
depends_on('mpi')
depends_on('lwgrp')
def install(self, spec, prefix):
configure("--prefix=" + prefix,
"--with-lwgrp=" + spec['lwgrp'].prefix)
make()
make("install")

View File

@ -29,8 +29,8 @@ class Dyninst(Package):
url = "http://www.dyninst.org/sites/default/files/downloads/dyninst/8.1.2/DyninstAPI-8.1.2.tgz"
list_url = "http://www.dyninst.org/downloads/dyninst-8.x"
versions = {'8.1.2' : 'bf03b33375afa66fe0efa46ce3f4b17a',
'8.1.1' : '1f8743e3a5662b25ce64a7edf647e77d' }
version('8.1.2', 'bf03b33375afa66fe0efa46ce3f4b17a')
version('8.1.1', '1f8743e3a5662b25ce64a7edf647e77d')
depends_on("libelf")
depends_on("libdwarf")

View File

@ -0,0 +1,38 @@
from spack import *
class Extrae(Package):
"""Extrae is the package devoted to generate tracefiles which can
be analyzed later by Paraver. Extrae is a tool that uses
different interposition mechanisms to inject probes into the
target application so as to gather information regarding the
application performance. The Extrae instrumentation package can
instrument the MPI programin model, and the following parallel
programming models either alone or in conjunction with MPI :
OpenMP, CUDA, OpenCL, pthread, OmpSs"""
homepage = "http://www.bsc.es/computer-sciences/extrae"
url = "http://www.bsc.es/ssl/apps/performanceTools/files/extrae-2.5.1.tar.bz2"
version('2.5.1', '422376b9c68243bd36a8a73fa62de106')
#depends_on("mpi")
depends_on("openmpi@:1.6")
depends_on("dyninst")
depends_on("libunwind")
depends_on("boost")
depends_on("libdwarf")
depends_on("papi")
def install(self, spec, prefix):
if 'openmpi' in spec:
mpi = spec['openmpi']
#if spec.satisfies('@2.5.1') and spec.satisfies('^openmpi@1.6.5'):
# tty.error("Some headers conflict when using OpenMPI 1.6.5. Please use 1.6 instead.")
elif 'mpich' in spec:
mpi = spec['mpich']
elif 'mvapich2' in spec:
mpi = spec['mvapich2']
configure("--prefix=%s" % prefix, "--with-mpi=%s" % mpi.prefix, "--with-unwind=%s" % spec['libunwind'].prefix, "--with-dyninst=%s" % spec['dyninst'].prefix, "--with-boost=%s" % spec['boost'].prefix, "--with-dwarf=%s" % spec['libdwarf'].prefix, "--with-papi=%s" % spec['papi'].prefix, "--with-dyninst-headers=%s" % spec['dyninst'].prefix.include, "--with-dyninst-libs=%s" % spec['dyninst'].prefix.lib)
make()
make("install", parallel=False)

View File

@ -5,7 +5,7 @@ class Graphlib(Package):
homepage = "http://https://github.com/lee218llnl/graphlib"
url = "https://github.com/lee218llnl/graphlib/archive/v2.0.0.tar.gz"
versions = { '2.0.0' : '43c6df84f1d38ba5a5dce0ae19371a70', }
version('2.0.0', '43c6df84f1d38ba5a5dce0ae19371a70')
def install(self, spec, prefix):
cmake(".", *std_cmake_args)

Some files were not shown because too many files have changed in this diff Show More