summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/docs/basic_usage.rst25
-rw-r--r--lib/spack/docs/packaging_guide.rst13
-rw-r--r--lib/spack/spack/build_environment.py11
-rw-r--r--lib/spack/spack/cmd/compiler.py12
-rw-r--r--lib/spack/spack/cmd/create.py187
-rw-r--r--lib/spack/spack/cmd/list.py50
-rw-r--r--lib/spack/spack/compilers/__init__.py18
-rw-r--r--lib/spack/spack/compilers/gcc.py4
-rw-r--r--lib/spack/spack/environment.py135
-rw-r--r--lib/spack/spack/test/data/sourceme_first.sh3
-rw-r--r--lib/spack/spack/test/data/sourceme_second.sh3
-rw-r--r--lib/spack/spack/test/environment.py67
-rw-r--r--var/spack/repos/builtin/packages/cantera/package.py197
-rw-r--r--var/spack/repos/builtin/packages/py-3to2/package.py40
-rw-r--r--var/spack/repos/builtin/packages/py-unittest2/package.py41
-rw-r--r--var/spack/repos/builtin/packages/py-unittest2py3k/package.py42
-rw-r--r--var/spack/repos/builtin/packages/serf/package.py5
-rw-r--r--var/spack/repos/builtin/packages/sundials/package.py148
-rw-r--r--var/spack/repos/builtin/packages/superlu-mt/package.py134
19 files changed, 1023 insertions, 112 deletions
diff --git a/lib/spack/docs/basic_usage.rst b/lib/spack/docs/basic_usage.rst
index 50c48b802b..ec193e767d 100644
--- a/lib/spack/docs/basic_usage.rst
+++ b/lib/spack/docs/basic_usage.rst
@@ -24,12 +24,29 @@ Spack can install:
.. command-output:: spack list
-The packages are listed by name in alphabetical order. You can also
-do wildcats searches using ``*``:
+The packages are listed by name in alphabetical order. If you specify a
+pattern to match, it will follow this set of rules. A pattern with no
+wildcards, ``*`` or ``?``, will be treated as though it started and ended with
+``*``, so ``util`` is equivalent to ``*util*``. A pattern with no capital
+letters will be treated as case-insensitive. You can also add the ``-i`` flag
+to specify a case insensitive search, or ``-d`` to search the description of
+the package in addition to the name. Some examples:
-.. command-output:: spack list m*
+All packages whose names contain "sql" case insensitive:
-.. command-output:: spack list *util*
+.. command-output:: spack list sql
+
+All packages whose names start with a capital M:
+
+.. command-output:: spack list 'M*'
+
+All packages whose names or descriptions contain Documentation:
+
+.. command-output:: spack list -d Documentation
+
+All packages whose names contain documentation case insensitive:
+
+.. command-output:: spack list -d documentation
.. _spack-info:
diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst
index 1f83f611b0..54b886310a 100644
--- a/lib/spack/docs/packaging_guide.rst
+++ b/lib/spack/docs/packaging_guide.rst
@@ -1950,6 +1950,19 @@ instead of hard-coding ``join_path(self.spec['mpi'].prefix.bin, 'mpicc')`` for
the reasons outlined above.
+Blas and Lapack libraries
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Different packages provide implementation of ``Blas`` and ``Lapack`` routines.
+The names of the resulting static and/or shared libraries differ from package
+to package. In order to make ``install()`` method indifferent to the
+choice of ``Blas`` implementation, each package which provides it
+sets up ``self.spec.blas_shared_lib`` and ``self.spec.blas_static_lib `` to
+point to the shared and static ``Blas`` libraries, respectively. The same
+applies to packages which provide ``Lapack``. Package developers are advised to
+use these variables, for example ``spec['blas'].blas_shared_lib`` instead of
+hard-coding ``join_path(spec['blas'].prefix.lib, 'libopenblas.so')``.
+
+
Forking ``install()``
~~~~~~~~~~~~~~~~~~~~~
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index 3fcfb151b8..60703a9df9 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -63,7 +63,7 @@ from llnl.util.filesystem import *
import spack
from spack.environment import EnvironmentModifications, validate
from spack.util.environment import *
-from spack.util.executable import Executable, which
+from spack.util.executable import Executable
#
# This can be set by the user to globally disable parallel builds.
@@ -130,8 +130,8 @@ def load_module(mod):
text = modulecmd('show', mod, output=str, error=str).split()
for i, word in enumerate(text):
if word == 'conflict':
- exec(compile(modulecmd('unload', text[
- i + 1], output=str, error=str), '<string>', 'exec'))
+ exec(compile(modulecmd('unload', text[i + 1], output=str,
+ error=str), '<string>', 'exec'))
# Load the module now that there are no conflicts
load = modulecmd('load', mod, output=str, error=str)
exec(compile(load, '<string>', 'exec'))
@@ -286,7 +286,6 @@ def set_build_environment_variables(pkg, env):
for directory in ('lib', 'lib64', 'share'):
pcdir = join_path(pre, directory, 'pkgconfig')
if os.path.isdir(pcdir):
- # pkg_config_dirs.append(pcdir)
env.prepend_path('PKG_CONFIG_PATH', pcdir)
if pkg.spec.architecture.target.module_name:
@@ -299,7 +298,7 @@ def set_module_variables_for_package(pkg, module):
"""Populate the module scope of install() with some useful functions.
This makes things easier for package writers.
"""
- # number of jobs spack will to build with.
+ # number of jobs spack will build with.
jobs = multiprocessing.cpu_count()
if not pkg.parallel:
jobs = 1
@@ -312,6 +311,7 @@ def set_module_variables_for_package(pkg, module):
# TODO: make these build deps that can be installed if not found.
m.make = MakeExecutable('make', jobs)
m.gmake = MakeExecutable('gmake', jobs)
+ m.scons = MakeExecutable('scons', jobs)
# easy shortcut to os.environ
m.env = os.environ
@@ -325,6 +325,7 @@ def set_module_variables_for_package(pkg, module):
# TODO: Currently, everything is a link dependency, but tools like
# TODO: this shouldn't be.
m.cmake = Executable('cmake')
+ m.ctest = Executable('ctest')
# standard CMake arguments
m.std_cmake_args = ['-DCMAKE_INSTALL_PREFIX=%s' % pkg.prefix,
diff --git a/lib/spack/spack/cmd/compiler.py b/lib/spack/spack/cmd/compiler.py
index c95045ef85..c325628ebf 100644
--- a/lib/spack/spack/cmd/compiler.py
+++ b/lib/spack/spack/cmd/compiler.py
@@ -76,10 +76,16 @@ def compiler_find(args):
if not paths:
paths = get_path('PATH')
- compilers = [c for c in spack.compilers.find_compilers(*args.add_paths)
- if c.spec not in spack.compilers.all_compilers(scope=args.scope)]
+ # Don't initialize compilers config via compilers.get_compiler_config.
+ # Just let compiler_find do the
+ # entire process and return an empty config from all_compilers
+ # Default for any other process is init_config=True
+ compilers = [c for c in spack.compilers.find_compilers(*paths)
+ if c.spec not in spack.compilers.all_compilers(
+ scope=args.scope, init_config=False)]
if compilers:
- spack.compilers.add_compilers_to_config(compilers, scope=args.scope)
+ spack.compilers.add_compilers_to_config(compilers, scope=args.scope,
+ init_config=False)
n = len(compilers)
s = 's' if n > 1 else ''
filename = spack.config.get_config_filename(args.scope, 'compilers')
diff --git a/lib/spack/spack/cmd/create.py b/lib/spack/spack/cmd/create.py
index 41bfa741f6..8cbb367f86 100644
--- a/lib/spack/spack/cmd/create.py
+++ b/lib/spack/spack/cmd/create.py
@@ -1,4 +1,3 @@
-_copyright = """\
##############################################################################
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
@@ -23,10 +22,8 @@ _copyright = """\
# 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 string
import os
-import hashlib
import re
from ordereddict_backport import OrderedDict
@@ -41,16 +38,37 @@ import spack.util.web
from spack.spec import Spec
from spack.util.naming import *
from spack.repository import Repo, RepoError
-import spack.util.crypto as crypto
from spack.util.executable import which
-from spack.stage import Stage
description = "Create a new package file from an archive URL"
-package_template = string.Template(
- _copyright + """
+package_template = string.Template("""\
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/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 Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, 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 Lesser 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 is a template package file for Spack. We've put "FIXME"
# next to all the things you'll want to change. Once you've handled
@@ -68,24 +86,21 @@ package_template = string.Template(
#
from spack import *
+
class ${class_name}(Package):
- ""\"FIXME: put a proper description of your package here.""\"
- # FIXME: add a proper url for your package's homepage here.
+ ""\"FIXME: Put a proper description of your package here.""\"
+
+ # FIXME: Add a proper url for your package's homepage here.
homepage = "http://www.example.com"
url = "${url}"
${versions}
-
+${extends}
# FIXME: Add dependencies if this package requires them.
# depends_on("foo")
def install(self, spec, prefix):
- # FIXME: Modify the configure line to suit your build system here.
- ${configure}
-
- # FIXME: Add logic to build and install here
- make()
- make("install")
+${install}
""")
@@ -120,41 +135,95 @@ def setup_parser(subparser):
class ConfigureGuesser(object):
def __call__(self, stage):
- """Try to guess the type of build system used by the project, and return
- an appropriate configure line.
- """
- autotools = "configure('--prefix=%s' % prefix)"
- cmake = "cmake('.', *std_cmake_args)"
- python = "python('setup.py', 'install', '--prefix=%s' % prefix)"
- r = "R('CMD', 'INSTALL', '--library=%s' % self.module.r_lib_dir, '%s' % self.stage.archive_file)"
-
- config_lines = ((r'/configure$', 'autotools', autotools),
- (r'/CMakeLists.txt$', 'cmake', cmake),
- (r'/setup.py$', 'python', python),
- (r'/NAMESPACE$', 'r', r))
-
- # Peek inside the tarball.
- tar = which('tar')
- output = tar(
- "--exclude=*/*/*", "-tf", stage.archive_file, output=str)
- lines = output.split("\n")
-
- # Set the configure line to the one that matched.
- for pattern, bs, cl in config_lines:
+ """Try to guess the type of build system used by the project.
+ Set the appropriate default installation instructions and any
+ necessary extensions for Python and R."""
+
+ # Default installation instructions
+ installDict = {
+ 'autotools': """\
+ # FIXME: Modify the configure line to suit your build system here.
+ configure('--prefix={0}'.format(prefix))
+
+ # FIXME: Add logic to build and install here.
+ make()
+ make('install')""",
+
+ 'cmake': """\
+ with working_dir('spack-build', create=True):
+ # FIXME: Modify the cmake line to suit your build system here.
+ cmake('..', *std_cmake_args)
+
+ # FIXME: Add logic to build and install here.
+ make()
+ make('install')""",
+
+ 'scons': """\
+ # FIXME: Add logic to build and install here.
+ scons('prefix={0}'.format(prefix))
+ scons('install')""",
+
+ 'python': """\
+ # FIXME: Add logic to build and install here.
+ python('setup.py', 'install', '--prefix={0}'.format(prefix))""",
+
+ 'R': """\
+ # FIXME: Add logic to build and install here.
+ R('CMD', 'INSTALL', '--library={0}'.format(self.module.r_lib_dir),
+ self.stage.source_path)""",
+
+ 'unknown': """\
+ # FIXME: Unknown build system
+ make()
+ make('install')"""
+ }
+
+ # A list of clues that give us an idea of the build system a package
+ # uses. If the regular expression matches a file contained in the
+ # archive, the corresponding build system is assumed.
+ clues = [
+ (r'/configure$', 'autotools'),
+ (r'/CMakeLists.txt$', 'cmake'),
+ (r'/SConstruct$', 'scons'),
+ (r'/setup.py$', 'python'),
+ (r'/NAMESPACE$', 'R')
+ ]
+
+ # Peek inside the compressed file.
+ if stage.archive_file.endswith('.zip'):
+ try:
+ unzip = which('unzip')
+ output = unzip('-l', stage.archive_file, output=str)
+ except:
+ output = ''
+ else:
+ try:
+ tar = which('tar')
+ output = tar('--exclude=*/*/*', '-tf',
+ stage.archive_file, output=str)
+ except:
+ output = ''
+ lines = output.split('\n')
+
+ # Determine the build system based on the files contained
+ # in the archive.
+ build_system = 'unknown'
+ for pattern, bs in clues:
if any(re.search(pattern, l) for l in lines):
- config_line = cl
build_system = bs
- break
- else:
- # None matched -- just put both, with cmake commented out
- config_line = "# FIXME: Spack couldn't guess one, so here are some options:\n"
- config_line += " # " + autotools + "\n"
- config_line += " # " + cmake
- build_system = 'unknown'
- self.configure = config_line
self.build_system = build_system
+ # Set the appropriate default installation instructions
+ self.install = installDict[build_system]
+
+ # Set any necessary extensions for Python and R
+ extensions = ''
+ if build_system in ['python', 'R']:
+ extensions = "\n extends('{0}')\n".format(build_system)
+
+ self.extends = extensions
+
def guess_name_and_version(url, args):
# Try to deduce name and version of the new package from the URL
@@ -168,7 +237,7 @@ def guess_name_and_version(url, args):
else:
try:
name = spack.url.parse_name(url, version)
- except spack.url.UndetectableNameError, e:
+ except spack.url.UndetectableNameError:
# Use a user-supplied name if one is present
tty.die("Couldn't guess a name for this package. Try running:", "",
"spack create --name <name> <url>")
@@ -182,7 +251,8 @@ def guess_name_and_version(url, args):
def find_repository(spec, args):
# figure out namespace for spec
if spec.namespace and args.namespace and spec.namespace != args.namespace:
- tty.die("Namespaces '%s' and '%s' do not match." % (spec.namespace, args.namespace))
+ tty.die("Namespaces '%s' and '%s' do not match." % (spec.namespace,
+ args.namespace))
if not spec.namespace and args.namespace:
spec.namespace = args.namespace
@@ -193,8 +263,8 @@ def find_repository(spec, args):
try:
repo = Repo(repo_path)
if spec.namespace and spec.namespace != repo.namespace:
- tty.die("Can't create package with namespace %s in repo with namespace %s"
- % (spec.namespace, repo.namespace))
+ tty.die("Can't create package with namespace %s in repo with "
+ "namespace %s" % (spec.namespace, repo.namespace))
except RepoError as e:
tty.die(str(e))
else:
@@ -214,11 +284,7 @@ def find_repository(spec, args):
def fetch_tarballs(url, name, version):
"""Try to find versions of the supplied archive by scraping the web.
-
- Prompts the user to select how many to download if many are found.
-
-
- """
+ Prompts the user to select how many to download if many are found."""
versions = spack.util.web.find_versions_of_archive(url)
rkeys = sorted(versions.keys(), reverse=True)
versions = OrderedDict(zip(rkeys, (versions[v] for v in rkeys)))
@@ -226,11 +292,11 @@ def fetch_tarballs(url, name, version):
archives_to_fetch = 1
if not versions:
# If the fetch failed for some reason, revert to what the user provided
- versions = { version : url }
+ versions = {version: url}
elif len(versions) > 1:
tty.msg("Found %s versions of %s:" % (len(versions), name),
*spack.cmd.elide_list(
- ["%-10s%s" % (v,u) for v, u in versions.iteritems()]))
+ ["%-10s%s" % (v, u) for v, u in versions.iteritems()]))
print
archives_to_fetch = tty.get_number(
"Include how many checksums in the package file?",
@@ -277,7 +343,7 @@ def create(parser, args):
name = 'py-%s' % name
# Prepend 'r-' to R package names, by convention.
- if guesser.build_system == 'r':
+ if guesser.build_system == 'R':
name = 'r-%s' % name
# Create a directory for the new package.
@@ -292,10 +358,11 @@ def create(parser, args):
pkg_file.write(
package_template.substitute(
name=name,
- configure=guesser.configure,
class_name=mod_to_class(name),
url=url,
- versions=make_version_calls(ver_hash_tuples)))
+ versions=make_version_calls(ver_hash_tuples),
+ extends=guesser.extends,
+ install=guesser.install))
# If everything checks out, go ahead and edit.
spack.editor(pkg_path)
diff --git a/lib/spack/spack/cmd/list.py b/lib/spack/spack/cmd/list.py
index 1e3699cee0..c921efd1bd 100644
--- a/lib/spack/spack/cmd/list.py
+++ b/lib/spack/spack/cmd/list.py
@@ -29,36 +29,62 @@ from llnl.util.tty.colify import colify
import spack
import fnmatch
+import re
+
+description = "List available spack packages"
-description ="List available spack packages"
def setup_parser(subparser):
subparser.add_argument(
'filter', nargs=argparse.REMAINDER,
help='Optional glob patterns to filter results.')
subparser.add_argument(
- '-i', '--insensitive', action='store_true', default=False,
- help='Filtering will be case insensitive.')
+ '-s', '--sensitive', action='store_true', default=False,
+ help='Use case-sensitive filtering. Default is case sensitive, '
+ 'unless the query contains a capital letter.')
+ subparser.add_argument(
+ '-d', '--search-description', action='store_true', default=False,
+ help='Filtering will also search the description for a match.')
def list(parser, args):
# Start with all package names.
- pkgs = spack.repo.all_package_names()
+ pkgs = set(spack.repo.all_package_names())
# filter if a filter arg was provided
if args.filter:
- def match(p, f):
- if args.insensitive:
- p = p.lower()
- f = f.lower()
- return fnmatch.fnmatchcase(p, f)
- pkgs = [p for p in pkgs if any(match(p, f) for f in args.filter)]
+ res = []
+ for f in args.filter:
+ if '*' not in f and '?' not in f:
+ r = fnmatch.translate('*' + f + '*')
+ else:
+ r = fnmatch.translate(f)
+
+ re_flags = re.I
+ if any(l.isupper for l in f) or args.sensitive:
+ re_flags = 0
+ rc = re.compile(r, flags=re_flags)
+ res.append(rc)
+
+ if args.search_description:
+ def match(p, f):
+ if f.match(p):
+ return True
+
+ pkg = spack.repo.get(p)
+ if pkg.__doc__:
+ return f.match(pkg.__doc__)
+ return False
+ else:
+ def match(p, f):
+ return f.match(p)
+ pkgs = [p for p in pkgs if any(match(p, f) for f in res)]
# sort before displaying.
- sorted_packages = sorted(pkgs, key=lambda s:s.lower())
+ sorted_packages = sorted(pkgs, key=lambda s: s.lower())
# Print all the package names in columns
- indent=0
+ indent = 0
if sys.stdout.isatty():
tty.msg("%d packages." % len(sorted_packages))
colify(sorted_packages, indent=indent)
diff --git a/lib/spack/spack/compilers/__init__.py b/lib/spack/spack/compilers/__init__.py
index ae72b743b2..a70d42982f 100644
--- a/lib/spack/spack/compilers/__init__.py
+++ b/lib/spack/spack/compilers/__init__.py
@@ -83,7 +83,7 @@ def _to_dict(compiler):
return {'compiler': d}
-def get_compiler_config(scope=None):
+def get_compiler_config(scope=None, init_config=True):
"""Return the compiler configuration for the specified architecture.
"""
def init_compiler_config():
@@ -93,12 +93,12 @@ def get_compiler_config(scope=None):
for compiler in compilers:
compilers_dict.append(_to_dict(compiler))
spack.config.update_config('compilers', compilers_dict, scope=scope)
-
+
config = spack.config.get_config('compilers', scope=scope)
# Update the configuration if there are currently no compilers
# configured. Avoid updating automatically if there ARE site
# compilers configured but no user ones.
- if not config:
+ if not config and init_config:
if scope is None:
# We know no compilers were configured in any scope.
init_compiler_config()
@@ -117,14 +117,14 @@ def get_compiler_config(scope=None):
return [] # Return empty list which we will later append to.
-def add_compilers_to_config(compilers, scope=None):
+def add_compilers_to_config(compilers, scope=None, init_config=True):
"""Add compilers to the config for the specified architecture.
Arguments:
- compilers: a list of Compiler objects.
- scope: configuration scope to modify.
"""
- compiler_config = get_compiler_config(scope)
+ compiler_config = get_compiler_config(scope, init_config)
for compiler in compilers:
compiler_config.append(_to_dict(compiler))
global _cache_config_file
@@ -153,23 +153,23 @@ def remove_compiler_from_config(compiler_spec, scope=None):
spack.config.update_config('compilers', filtered_compiler_config, scope)
-def all_compilers_config(scope=None):
+def all_compilers_config(scope=None, init_config=True):
"""Return a set of specs for all the compiler versions currently
available to build with. These are instances of CompilerSpec.
"""
# Get compilers for this architecture.
global _cache_config_file #Create a cache of the config file so we don't load all the time.
if not _cache_config_file:
- _cache_config_file = get_compiler_config(scope)
+ _cache_config_file = get_compiler_config(scope, init_config)
return _cache_config_file
else:
return _cache_config_file
-def all_compilers(scope=None):
+def all_compilers(scope=None, init_config=True):
# Return compiler specs from the merged config.
return [spack.spec.CompilerSpec(s['compiler']['spec'])
- for s in all_compilers_config(scope)]
+ for s in all_compilers_config(scope, init_config)]
def default_compiler():
diff --git a/lib/spack/spack/compilers/gcc.py b/lib/spack/spack/compilers/gcc.py
index 3f552eaece..2fae6688db 100644
--- a/lib/spack/spack/compilers/gcc.py
+++ b/lib/spack/spack/compilers/gcc.py
@@ -77,9 +77,9 @@ class Gcc(Compiler):
return get_compiler_version(
fc, '-dumpversion',
# older gfortran versions don't have simple dumpversion output.
- r'(?:GNU Fortran \(GCC\))?(\d+\.\d+(?:\.\d+)?)', module)
+ r'(?:GNU Fortran \(GCC\))?(\d+\.\d+(?:\.\d+)?)')
@classmethod
def f77_version(cls, f77):
- return cls.fc_version(f77, module)
+ return cls.fc_version(f77)
diff --git a/lib/spack/spack/environment.py b/lib/spack/spack/environment.py
index af642dcc9b..30c6228ca4 100644
--- a/lib/spack/spack/environment.py
+++ b/lib/spack/spack/environment.py
@@ -1,4 +1,4 @@
-##############################################################################
+#
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
@@ -21,14 +21,17 @@
# 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 collections
import inspect
+import json
import os
import os.path
+import subprocess
class NameModifier(object):
+
def __init__(self, name, **kwargs):
self.name = name
self.args = {'name': name}
@@ -36,6 +39,7 @@ class NameModifier(object):
class NameValueModifier(object):
+
def __init__(self, name, value, **kwargs):
self.name = name
self.value = value
@@ -45,23 +49,27 @@ class NameValueModifier(object):
class SetEnv(NameValueModifier):
+
def execute(self):
os.environ[self.name] = str(self.value)
class UnsetEnv(NameModifier):
+
def execute(self):
# Avoid throwing if the variable was not set
os.environ.pop(self.name, None)
class SetPath(NameValueModifier):
+
def execute(self):
string_path = concatenate_paths(self.value, separator=self.separator)
os.environ[self.name] = string_path
class AppendPath(NameValueModifier):
+
def execute(self):
environment_value = os.environ.get(self.name, '')
directories = environment_value.split(
@@ -71,6 +79,7 @@ class AppendPath(NameValueModifier):
class PrependPath(NameValueModifier):
+
def execute(self):
environment_value = os.environ.get(self.name, '')
directories = environment_value.split(
@@ -80,6 +89,7 @@ class PrependPath(NameValueModifier):
class RemovePath(NameValueModifier):
+
def execute(self):
environment_value = os.environ.get(self.name, '')
directories = environment_value.split(
@@ -90,6 +100,7 @@ class RemovePath(NameValueModifier):
class EnvironmentModifications(object):
+
"""
Keeps track of requests to modify the current environment.
@@ -240,6 +251,126 @@ class EnvironmentModifications(object):
for x in actions:
x.execute()
+ @staticmethod
+ def from_sourcing_files(*args, **kwargs):
+ """
+ Creates an instance of EnvironmentModifications that, if executed,
+ has the same effect on the environment as sourcing the files passed as
+ parameters
+
+ Args:
+ *args: list of files to be sourced
+
+ Returns:
+ instance of EnvironmentModifications
+ """
+ env = EnvironmentModifications()
+ # Check if the files are actually there
+ if not all(os.path.isfile(file) for file in args):
+ raise RuntimeError('trying to source non-existing files')
+ # Relevant kwd parameters and formats
+ info = dict(kwargs)
+ info.setdefault('shell', '/bin/bash')
+ info.setdefault('shell_options', '-c')
+ info.setdefault('source_command', 'source')
+ info.setdefault('suppress_output', '&> /dev/null')
+ info.setdefault('concatenate_on_success', '&&')
+
+ shell = '{shell}'.format(**info)
+ shell_options = '{shell_options}'.format(**info)
+ source_file = '{source_command} {file} {concatenate_on_success}'
+ dump_environment = 'python -c "import os, json; print json.dumps(dict(os.environ))"' # NOQA: ignore=E501
+ # Construct the command that will be executed
+ command = [source_file.format(file=file, **info) for file in args]
+ command.append(dump_environment)
+ command = ' '.join(command)
+ command = [
+ shell,
+ shell_options,
+ command
+ ]
+
+ # Try to source all the files,
+ proc = subprocess.Popen(
+ command, stdout=subprocess.PIPE, env=os.environ)
+ proc.wait()
+ if proc.returncode != 0:
+ raise RuntimeError('sourcing files returned a non-zero exit code')
+ output = ''.join([line for line in proc.stdout])
+ # Construct a dictionary with all the variables in the new environment
+ after_source_env = dict(json.loads(output))
+ this_environment = dict(os.environ)
+
+ # Filter variables that are not related to sourcing a file
+ to_be_filtered = 'SHLVL', '_', 'PWD', 'OLDPWD'
+ for d in after_source_env, this_environment:
+ for name in to_be_filtered:
+ d.pop(name, None)
+
+ # Fill the EnvironmentModifications instance
+
+ # New variables
+ new_variables = set(after_source_env) - set(this_environment)
+ for x in new_variables:
+ env.set(x, after_source_env[x])
+ # Variables that have been unset
+ unset_variables = set(this_environment) - set(after_source_env)
+ for x in unset_variables:
+ env.unset(x)
+ # Variables that have been modified
+ common_variables = set(this_environment).intersection(set(after_source_env)) # NOQA: ignore=E501
+ modified_variables = [x for x in common_variables if this_environment[x] != after_source_env[x]] # NOQA: ignore=E501
+
+ def return_separator_if_any(first_value, second_value):
+ separators = ':', ';'
+ for separator in separators:
+ if separator in first_value and separator in second_value:
+ return separator
+ return None
+
+ for x in modified_variables:
+ current = this_environment[x]
+ modified = after_source_env[x]
+ sep = return_separator_if_any(current, modified)
+ if sep is None:
+ # We just need to set the variable to the new value
+ env.set(x, after_source_env[x])
+ else:
+ current_list = current.split(sep)
+ modified_list = modified.split(sep)
+ # Paths that have been removed
+ remove_list = [
+ ii for ii in current_list if ii not in modified_list]
+ # Check that nothing has been added in the middle of vurrent
+ # list
+ remaining_list = [
+ ii for ii in current_list if ii in modified_list]
+ start = modified_list.index(remaining_list[0])
+ end = modified_list.index(remaining_list[-1])
+ search = sep.join(modified_list[start:end + 1])
+ if search not in current:
+ # We just need to set the variable to the new value
+ env.set(x, after_source_env[x])
+ break
+ else:
+ try:
+ prepend_list = modified_list[:start]
+ except KeyError:
+ prepend_list = []
+ try:
+ append_list = modified_list[end + 1:]
+ except KeyError:
+ append_list = []
+
+ for item in remove_list:
+ env.remove_path(x, item)
+ for item in append_list:
+ env.append_path(x, item)
+ for item in prepend_list:
+ env.prepend_path(x, item)
+
+ return env
+
def concatenate_paths(paths, separator=':'):
"""
diff --git a/lib/spack/spack/test/data/sourceme_first.sh b/lib/spack/spack/test/data/sourceme_first.sh
new file mode 100644
index 0000000000..800f639ac8
--- /dev/null
+++ b/lib/spack/spack/test/data/sourceme_first.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+export NEW_VAR='new'
+export UNSET_ME='overridden'
diff --git a/lib/spack/spack/test/data/sourceme_second.sh b/lib/spack/spack/test/data/sourceme_second.sh
new file mode 100644
index 0000000000..9955a0e6d6
--- /dev/null
+++ b/lib/spack/spack/test/data/sourceme_second.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+export PATH_LIST='/path/first:/path/second:/path/fourth'
+unset EMPTY_PATH_LIST \ No newline at end of file
diff --git a/lib/spack/spack/test/environment.py b/lib/spack/spack/test/environment.py
index a0d959db2f..2396961888 100644
--- a/lib/spack/spack/test/environment.py
+++ b/lib/spack/spack/test/environment.py
@@ -24,16 +24,21 @@
##############################################################################
import unittest
import os
-import copy
+
+from spack import spack_root
+from llnl.util.filesystem import join_path
from spack.environment import EnvironmentModifications
+from spack.environment import SetEnv, UnsetEnv
+from spack.environment import RemovePath, PrependPath, AppendPath
class EnvironmentTest(unittest.TestCase):
+
def setUp(self):
os.environ['UNSET_ME'] = 'foo'
os.environ['EMPTY_PATH_LIST'] = ''
os.environ['PATH_LIST'] = '/path/second:/path/third'
- os.environ['REMOVE_PATH_LIST'] = '/a/b:/duplicate:/a/c:/remove/this:/a/d:/duplicate/:/f/g'
+ os.environ['REMOVE_PATH_LIST'] = '/a/b:/duplicate:/a/c:/remove/this:/a/d:/duplicate/:/f/g' # NOQA: ignore=E501
def tearDown(self):
pass
@@ -77,9 +82,18 @@ class EnvironmentTest(unittest.TestCase):
env.remove_path('REMOVE_PATH_LIST', '/duplicate/')
env.apply_modifications()
- self.assertEqual('/path/first:/path/second:/path/third:/path/last', os.environ['PATH_LIST'])
- self.assertEqual('/path/first:/path/middle:/path/last', os.environ['EMPTY_PATH_LIST'])
- self.assertEqual('/path/first:/path/middle:/path/last', os.environ['NEWLY_CREATED_PATH_LIST'])
+ self.assertEqual(
+ '/path/first:/path/second:/path/third:/path/last',
+ os.environ['PATH_LIST']
+ )
+ self.assertEqual(
+ '/path/first:/path/middle:/path/last',
+ os.environ['EMPTY_PATH_LIST']
+ )
+ self.assertEqual(
+ '/path/first:/path/middle:/path/last',
+ os.environ['NEWLY_CREATED_PATH_LIST']
+ )
self.assertEqual('/a/b:/a/c:/a/d:/f/g', os.environ['REMOVE_PATH_LIST'])
def test_extra_arguments(self):
@@ -98,3 +112,46 @@ class EnvironmentTest(unittest.TestCase):
self.assertEqual(len(copy_construct), 2)
for x, y in zip(env, copy_construct):
assert x is y
+
+ def test_source_files(self):
+ datadir = join_path(spack_root, 'lib', 'spack',
+ 'spack', 'test', 'data')
+ files = [
+ join_path(datadir, 'sourceme_first.sh'),
+ join_path(datadir, 'sourceme_second.sh')
+ ]
+ env = EnvironmentModifications.from_sourcing_files(*files)
+ modifications = env.group_by_name()
+
+ # This is sensitive to the user's environment; can include
+ # spurious entries for things like PS1
+ #
+ # TODO: figure out how to make a bit more robust.
+ self.assertTrue(len(modifications) >= 4)
+
+ # Set new variables
+ self.assertEqual(len(modifications['NEW_VAR']), 1)
+ self.assertTrue(isinstance(modifications['NEW_VAR'][0], SetEnv))
+ self.assertEqual(modifications['NEW_VAR'][0].value, 'new')
+ # Unset variables
+ self.assertEqual(len(modifications['EMPTY_PATH_LIST']), 1)
+ self.assertTrue(isinstance(
+ modifications['EMPTY_PATH_LIST'][0], UnsetEnv))
+ # Modified variables
+ self.assertEqual(len(modifications['UNSET_ME']), 1)
+ self.assertTrue(isinstance(modifications['UNSET_ME'][0], SetEnv))
+ self.assertEqual(modifications['UNSET_ME'][0].value, 'overridden')
+
+ self.assertEqual(len(modifications['PATH_LIST']), 3)
+ self.assertTrue(
+ isinstance(modifications['PATH_LIST'][0], RemovePath)
+ )
+ self.assertEqual(modifications['PATH_LIST'][0].value, '/path/third')
+ self.assertTrue(
+ isinstance(modifications['PATH_LIST'][1], AppendPath)
+ )
+ self.assertEqual(modifications['PATH_LIST'][1].value, '/path/fourth')
+ self.assertTrue(
+ isinstance(modifications['PATH_LIST'][2], PrependPath)
+ )
+ self.assertEqual(modifications['PATH_LIST'][2].value, '/path/first')
diff --git a/var/spack/repos/builtin/packages/cantera/package.py b/var/spack/repos/builtin/packages/cantera/package.py
new file mode 100644
index 0000000000..e9e5da4486
--- /dev/null
+++ b/var/spack/repos/builtin/packages/cantera/package.py
@@ -0,0 +1,197 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/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 Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, 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 Lesser 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 *
+import os
+
+
+class Cantera(Package):
+ """Cantera is a suite of object-oriented software tools for problems
+ involving chemical kinetics, thermodynamics, and/or transport processes."""
+
+ homepage = "http://www.cantera.org/docs/sphinx/html/index.html"
+ url = "https://github.com/Cantera/cantera/archive/v2.2.1.tar.gz"
+
+ version('2.2.1', '9d1919bdef39ddec54485fc8a741a3aa')
+
+ variant('lapack', default=True, description='Build with external BLAS/LAPACK libraries')
+ variant('threadsafe', default=True, description='Build threadsafe, requires Boost')
+ variant('sundials', default=True, description='Build with external Sundials')
+ variant('python', default=False, description='Build the Cantera Python module')
+ variant('matlab', default=False, description='Build the Cantera Matlab toolbox')
+
+ # Required dependencies
+ depends_on('scons')
+
+ # Recommended dependencies
+ depends_on('blas', when='+lapack')
+ depends_on('lapack', when='+lapack')
+ depends_on('boost', when='+threadsafe')
+ depends_on('sundials', when='+sundials') # must be compiled with -fPIC
+
+ # Python module dependencies
+ extends('python', when='+python')
+ depends_on('py-numpy', when='+python')
+ depends_on('py-scipy', when='+python')
+ depends_on('py-cython', when='+python')
+ depends_on('py-3to2', when='+python')
+ # TODO: these "when" specs don't actually work
+ # depends_on('py-unittest2', when='+python^python@2.6')
+ # depends_on('py-unittest2py3k', when='+python^python@3.1')
+
+ # Matlab toolbox dependencies
+ # TODO: add Matlab package
+ # TODO: allow packages to extend multiple other packages
+ # extends('matlab', when='+matlab')
+
+ def install(self, spec, prefix):
+ # Required options
+ options = [
+ 'prefix={0}'.format(prefix),
+ 'CC={0}'.format(os.environ['CC']),
+ 'CXX={0}'.format(os.environ['CXX']),
+ 'F77={0}'.format(os.environ['F77']),
+ 'FORTRAN={0}'.format(os.environ['FC']),
+ 'cc_flags=-fPIC',
+ # Allow Spack environment variables to propagate through to SCons
+ 'env_vars=all'
+ ]
+
+ # BLAS/LAPACK support
+ if '+lapack' in spec:
+ options.extend([
+ 'blas_lapack_libs=lapack,blas',
+ 'blas_lapack_dir={0}'.format(spec['lapack'].prefix.lib)
+ ])
+
+ # Threadsafe build, requires Boost
+ if '+threadsafe' in spec:
+ options.extend([
+ 'build_thread_safe=yes',
+ 'boost_inc_dir={0}'.format(spec['boost'].prefix.include),
+ 'boost_lib_dir={0}'.format(spec['boost'].prefix.lib),
+ 'boost_thread_lib=boost_thread-mt,boost_system-mt'
+ ])
+ else:
+ options.append('build_thread_safe=no')
+
+ # Sundials support
+ if '+sundials' in spec:
+ options.extend([
+ 'use_sundials=y',
+ 'sundials_include={0}'.format(spec['sundials'].prefix.include),
+ 'sundials_libdir={0}'.format(spec['sundials'].prefix.lib),
+ 'sundials_license={0}'.format(
+ join_path(spec['sundials'].prefix, 'LICENSE'))
+ ])
+ else:
+ options.append('use_sundials=n')
+
+ # Python module
+ if '+python' in spec:
+ options.extend([
+ 'python_package=full',
+ 'python_cmd={0}'.format(
+ join_path(spec['python'].prefix.bin, 'python')),
+ 'python_array_home={0}'.format(spec['py-numpy'].prefix)
+ ])
+ if spec['python'].satisfies('@3'):
+ options.extend([
+ 'python3_package=y',
+ 'python3_cmd={0}'.format(
+ join_path(spec['python'].prefix.bin, 'python')),
+ 'python3_array_home={0}'.format(spec['py-numpy'].prefix)
+ ])
+ else:
+ options.append('python3_package=n')
+ else:
+ options.append('python_package=none')
+ options.append('python3_package=n')
+
+ # Matlab toolbox
+ if '+matlab' in spec:
+ options.extend([
+ 'matlab_toolbox=y',
+ 'matlab_path={0}'.format(spec['matlab'].prefix)
+ ])
+ else:
+ options.append('matlab_toolbox=n')
+
+ scons('build', *options)
+
+ if '+python' in spec:
+ # Tests will always fail if Python dependencies aren't built
+ # In addition, 3 of the tests fail when run in parallel
+ scons('test', parallel=False)
+
+ scons('install')
+
+ self.filter_compilers()
+
+ def filter_compilers(self):
+ """Run after install to tell the Makefile and SConstruct files to use
+ the compilers that Spack built the package with.
+
+ If this isn't done, they'll have CC, CXX, F77, and FC set to Spack's
+ generic cc, c++, f77, and f90. We want them to be bound to whatever
+ compiler they were built with."""
+
+ kwargs = {'ignore_absent': True, 'backup': False, 'string': True}
+ dirname = os.path.join(self.prefix, 'share/cantera/samples')
+
+ cc_files = [
+ 'cxx/rankine/Makefile', 'cxx/NASA_coeffs/Makefile',
+ 'cxx/kinetics1/Makefile', 'cxx/flamespeed/Makefile',
+ 'cxx/combustor/Makefile', 'f77/SConstruct'
+ ]
+
+ cxx_files = [
+ 'cxx/rankine/Makefile', 'cxx/NASA_coeffs/Makefile',
+ 'cxx/kinetics1/Makefile', 'cxx/flamespeed/Makefile',
+ 'cxx/combustor/Makefile'
+ ]
+
+ f77_files = [
+ 'f77/Makefile', 'f77/SConstruct'
+ ]
+
+ fc_files = [
+ 'f90/Makefile', 'f90/SConstruct'
+ ]
+
+ for filename in cc_files:
+ filter_file(os.environ['CC'], self.compiler.cc,
+ os.path.join(dirname, filename), **kwargs)
+
+ for filename in cxx_files:
+ filter_file(os.environ['CXX'], self.compiler.cxx,
+ os.path.join(dirname, filename), **kwargs)
+
+ for filename in f77_files:
+ filter_file(os.environ['F77'], self.compiler.f77,
+ os.path.join(dirname, filename), **kwargs)
+
+ for filename in fc_files:
+ filter_file(os.environ['FC'], self.compiler.fc,
+ os.path.join(dirname, filename), **kwargs)
diff --git a/var/spack/repos/builtin/packages/py-3to2/package.py b/var/spack/repos/builtin/packages/py-3to2/package.py
new file mode 100644
index 0000000000..1071a3c209
--- /dev/null
+++ b/var/spack/repos/builtin/packages/py-3to2/package.py
@@ -0,0 +1,40 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/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 Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, 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 Lesser 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 Py3to2(Package):
+ """lib3to2 is a set of fixers that are intended to backport code written
+ for Python version 3.x into Python version 2.x."""
+
+ homepage = "https://pypi.python.org/pypi/3to2"
+ url = "https://pypi.python.org/packages/source/3/3to2/3to2-1.1.1.zip"
+
+ version('1.1.1', 'cbeed28e350dbdaef86111ace3052824')
+
+ extends('python')
+
+ def install(self, spec, prefix):
+ python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/repos/builtin/packages/py-unittest2/package.py b/var/spack/repos/builtin/packages/py-unittest2/package.py
new file mode 100644
index 0000000000..f669a500ec
--- /dev/null
+++ b/var/spack/repos/builtin/packages/py-unittest2/package.py
@@ -0,0 +1,41 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/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 Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, 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 Lesser 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 PyUnittest2(Package):
+ """unittest2 is a backport of the new features added to the unittest
+ testing framework in Python 2.7 and onwards."""
+
+ homepage = "https://pypi.python.org/pypi/unittest2"
+ url = "https://pypi.python.org/packages/source/u/unittest2/unittest2-1.1.0.tar.gz"
+
+ version('1.1.0', 'f72dae5d44f091df36b6b513305ea000')
+
+ extends('python')
+ depends_on('py-setuptools')
+
+ def install(self, spec, prefix):
+ python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/repos/builtin/packages/py-unittest2py3k/package.py b/var/spack/repos/builtin/packages/py-unittest2py3k/package.py
new file mode 100644
index 0000000000..ca857395fb
--- /dev/null
+++ b/var/spack/repos/builtin/packages/py-unittest2py3k/package.py
@@ -0,0 +1,42 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/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 Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, 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 Lesser 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 PyUnittest2py3k(Package):
+ """unittest2 is a backport of the new features added to the unittest
+ testing framework in Python 2.7 and 3.2. This is a Python 3 compatible
+ version of unittest2."""
+
+ homepage = "https://pypi.python.org/pypi/unittest2py3k"
+ url = "https://pypi.python.org/packages/source/u/unittest2py3k/unittest2py3k-0.5.1.tar.gz"
+
+ version('0.5.1', '8824ff92044310d9365f90d892bf0f09')
+
+ extends('python')
+ depends_on('py-setuptools')
+
+ def install(self, spec, prefix):
+ python('setup.py', 'install', '--prefix=%s' % prefix)
diff --git a/var/spack/repos/builtin/packages/serf/package.py b/var/spack/repos/builtin/packages/serf/package.py
index 817db68241..ff6fd2da9b 100644
--- a/var/spack/repos/builtin/packages/serf/package.py
+++ b/var/spack/repos/builtin/packages/serf/package.py
@@ -28,10 +28,11 @@ from spack import *
class Serf(Package):
"""Apache Serf - a high performance C-based HTTP client library
built upon the Apache Portable Runtime (APR) library"""
+
homepage = 'https://serf.apache.org/'
url = 'https://archive.apache.org/dist/serf/serf-1.3.8.tar.bz2'
- version('1.3.8', '1d45425ca324336ce2f4ae7d7b4cfbc5567c5446')
+ version('1.3.8', '1d45425ca324336ce2f4ae7d7b4cfbc5567c5446')
depends_on('apr')
depends_on('apr-util')
@@ -41,8 +42,6 @@ class Serf(Package):
depends_on('zlib')
def install(self, spec, prefix):
- scons = which("scons")
-
options = ['PREFIX=%s' % prefix]
options.append('APR=%s' % spec['apr'].prefix)
options.append('APU=%s' % spec['apr-util'].prefix)
diff --git a/var/spack/repos/builtin/packages/sundials/package.py b/var/spack/repos/builtin/packages/sundials/package.py
index ba2340f74c..c55198a850 100644
--- a/var/spack/repos/builtin/packages/sundials/package.py
+++ b/var/spack/repos/builtin/packages/sundials/package.py
@@ -23,17 +23,151 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack import *
+import os
+
class Sundials(Package):
- """SUNDIALS (SUite of Nonlinear and DIfferential/ALgebraic equation Solvers)"""
+ """SUNDIALS (SUite of Nonlinear and DIfferential/ALgebraic equation
+ Solvers)"""
+
homepage = "http://computation.llnl.gov/casc/sundials/"
- url = "http://computation.llnl.gov/casc/sundials/download/code/sundials-2.5.0.tar.gz"
+ url = "http://computation.llnl.gov/projects/sundials-suite-nonlinear-differential-algebraic-equation-solvers/download/sundials-2.6.2.tar.gz"
+
+ version('2.6.2', '3deeb0ede9f514184c6bd83ecab77d95')
- version('2.5.0', 'aba8b56eec600de3109cfb967aa3ba0f')
+ variant('mpi', default=True, description='Enable MPI support')
+ variant('lapack', default=True, description='Build with external BLAS/LAPACK libraries')
+ variant('klu', default=False, description='Build with SuiteSparse KLU libraries')
+ variant('superlu', default=False, description='Build with SuperLU_MT libraries')
+ variant('openmp', default=False, description='Enable OpenMP support')
+ variant('pthread', default=True, description='Enable POSIX threads support')
- depends_on("mpi")
+ depends_on('mpi', when='+mpi')
+ depends_on('blas', when='+lapack')
+ depends_on('lapack', when='+lapack')
+ depends_on('suite-sparse', when='+klu')
+ depends_on('superlu-mt+openmp', when='+superlu+openmp')
+ depends_on('superlu-mt+pthread', when='+superlu+pthread')
def install(self, spec, prefix):
- configure("--prefix=%s" % prefix)
- make()
- make("install")
+ cmake_args = std_cmake_args[:]
+ cmake_args.extend([
+ '-DBUILD_SHARED_LIBS=ON',
+ '-DCMAKE_C_FLAGS=-fPIC',
+ '-DCMAKE_Fortran_FLAGS=-fPIC',
+ '-DEXAMPLES_ENABLE=ON',
+ '-DEXAMPLES_INSTALL=ON',
+ '-DFCMIX_ENABLE=ON'
+ ])
+
+ # MPI support
+ if '+mpi' in spec:
+ cmake_args.extend([
+ '-DMPI_ENABLE=ON',
+ '-DMPI_MPICC={0}'.format(spec['mpi'].mpicc),
+ '-DMPI_MPIF77={0}'.format(spec['mpi'].mpif77)
+ ])
+ else:
+ cmake_args.append('-DMPI_ENABLE=OFF')
+
+ # Building with LAPACK and BLAS
+ if '+lapack' in spec:
+ cmake_args.extend([
+ '-DLAPACK_ENABLE=ON',
+ '-DLAPACK_LIBRARIES={0};{1}'.format(
+ spec['lapack'].lapack_shared_lib,
+ spec['blas'].blas_shared_lib
+ )
+ ])
+ else:
+ cmake_args.append('-DLAPACK_ENABLE=OFF')
+
+ # Building with KLU
+ if '+klu' in spec:
+ cmake_args.extend([
+ '-DKLU_ENABLE=ON',
+ '-DKLU_INCLUDE_DIR={0}'.format(
+ spec['suite-sparse'].prefix.include),
+ '-DKLU_LIBRARY_DIR={0}'.format(
+ spec['suite-sparse'].prefix.lib)
+ ])
+ else:
+ cmake_args.append('-DKLU_ENABLE=OFF')
+
+ # Building with SuperLU_MT
+ if '+superlu' in spec:
+ cmake_args.extend([
+ '-DSUPERLUMT_ENABLE=ON',
+ '-DSUPERLUMT_INCLUDE_DIR={0}'.format(
+ spec['superlu-mt'].prefix.include),
+ '-DSUPERLUMT_LIBRARY_DIR={0}'.format(
+ spec['superlu-mt'].prefix.lib)
+ ])
+ if '+openmp' in spec:
+ cmake_args.append('-DSUPERLUMT_THREAD_TYPE=OpenMP')
+ elif '+pthread' in spec:
+ cmake_args.append('-DSUPERLUMT_THREAD_TYPE=Pthread')
+ else:
+ msg = 'You must choose either +openmp or +pthread when '
+ msg += 'building with SuperLU_MT'
+ raise RuntimeError(msg)
+ else:
+ cmake_args.append('-DSUPERLUMT_ENABLE=OFF')
+
+ # OpenMP support
+ if '+openmp' in spec:
+ cmake_args.append('-DOPENMP_ENABLE=ON')
+ else:
+ cmake_args.append('-DOPENMP_ENABLE=OFF')
+
+ # POSIX threads support
+ if '+pthread' in spec:
+ cmake_args.append('-DPTHREAD_ENABLE=ON')
+ else:
+ cmake_args.append('-DPTHREAD_ENABLE=OFF')
+
+ with working_dir('build', create=True):
+ cmake('..', *cmake_args)
+
+ make()
+ make('install')
+
+ install('LICENSE', prefix)
+
+ self.filter_compilers()
+
+ def filter_compilers(self):
+ """Run after install to tell the Makefiles to use
+ the compilers that Spack built the package with.
+
+ If this isn't done, they'll have CC, CPP, and F77 set to
+ Spack's generic cc and f77. We want them to be bound to
+ whatever compiler they were built with."""
+
+ kwargs = {'ignore_absent': True, 'backup': False, 'string': True}
+ dirname = os.path.join(self.prefix, 'examples')
+
+ cc_files = [
+ 'arkode/C_serial/Makefile', 'arkode/C_parallel/Makefile',
+ 'cvode/serial/Makefile', 'cvode/parallel/Makefile',
+ 'cvodes/serial/Makefile', 'cvodes/parallel/Makefile',
+ 'ida/serial/Makefile', 'ida/parallel/Makefile',
+ 'idas/serial/Makefile', 'idas/parallel/Makefile',
+ 'kinsol/serial/Makefile', 'kinsol/parallel/Makefile',
+ 'nvector/serial/Makefile', 'nvector/parallel/Makefile',
+ 'nvector/pthreads/Makefile'
+ ]
+
+ f77_files = [
+ 'arkode/F77_serial/Makefile', 'cvode/fcmix_serial/Makefile',
+ 'ida/fcmix_serial/Makefile', 'ida/fcmix_pthreads/Makefile',
+ 'kinsol/fcmix_serial/Makefile'
+ ]
+
+ for filename in cc_files:
+ filter_file(os.environ['CC'], self.compiler.cc,
+ os.path.join(dirname, filename), **kwargs)
+
+ for filename in f77_files:
+ filter_file(os.environ['F77'], self.compiler.f77,
+ os.path.join(dirname, filename), **kwargs)
diff --git a/var/spack/repos/builtin/packages/superlu-mt/package.py b/var/spack/repos/builtin/packages/superlu-mt/package.py
new file mode 100644
index 0000000000..5a9429d6e5
--- /dev/null
+++ b/var/spack/repos/builtin/packages/superlu-mt/package.py
@@ -0,0 +1,134 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/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 Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, 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 Lesser 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 *
+import glob
+import os
+
+
+class SuperluMt(Package):
+ """SuperLU is a general purpose library for the direct solution of large,
+ sparse, nonsymmetric systems of linear equations on high performance
+ machines. SuperLU_MT is designed for shared memory parallel machines."""
+
+ homepage = "http://crd-legacy.lbl.gov/~xiaoye/SuperLU/#superlu_mt"
+ url = "http://crd-legacy.lbl.gov/~xiaoye/SuperLU/superlu_mt_3.1.tar.gz"
+
+ version('3.1', '06ac62f1b4b7d17123fffa0d0c315e91')
+
+ variant('blas', default=True, description='Build with external BLAS library')
+
+ # Must choose one or the other
+ variant('openmp', default=False, description='Build with OpenMP support')
+ variant('pthread', default=True, description='Build with POSIX threads support')
+
+ # NOTE: must link with a single-threaded BLAS library
+ depends_on('blas', when='+blas')
+
+ # Cannot be built in parallel
+ parallel = False
+
+ def configure(self, spec):
+ # Validate chosen variants
+ if '+openmp' in spec and '+pthread' in spec:
+ msg = 'You cannot choose both +openmp and +pthread'
+ raise RuntimeError(msg)
+ if '~openmp' in spec and '~pthread' in spec:
+ msg = 'You must choose either +openmp or +pthread'
+ raise RuntimeError(msg)
+
+ # List of configuration options
+ config = []
+
+ # The machine (platform) identifier to append to the library names
+ if '+openmp' in spec:
+ # OpenMP
+ config.extend([
+ 'PLAT = _OPENMP',
+ 'TMGLIB = libtmglib.a',
+ 'MPLIB = {0}'.format(self.compiler.openmp_flag),
+ 'CFLAGS = {0}'.format(self.compiler.openmp_flag),
+ 'FFLAGS = {0}'.format(self.compiler.openmp_flag)
+ ])
+ elif '+pthread' in spec:
+ # POSIX threads
+ config.extend([
+ 'PLAT = _PTHREAD',
+ 'TMGLIB = libtmglib$(PLAT).a',
+ 'MPLIB = -lpthread'
+ ])
+
+ # The BLAS library
+ # NOTE: must link with a single-threaded BLAS library
+ if '+blas' in spec:
+ config.extend([
+ 'BLASDEF = -DUSE_VENDOR_BLAS',
+ 'BLASLIB = -L{0} -lblas'.format(spec['blas'].prefix.lib)
+ ])
+ else:
+ config.append('BLASLIB = ../lib/libblas$(PLAT).a')
+
+ # Generic options
+ config.extend([
+ # The name of the libraries to be created/linked to
+ 'SUPERLULIB = libsuperlu_mt$(PLAT).a',
+ 'MATHLIB = -lm',
+ # The archiver and the flag(s) to use when building archives
+ 'ARCH = ar',
+ 'ARCHFLAGS = cr',
+ 'RANLIB = {0}'.format('ranlib' if which('ranlib') else 'echo'),
+ # Definitions used by CPP
+ 'PREDEFS = -D_$(PLAT)',
+ # Compilers and flags
+ 'CC = {0}'.format(os.environ['CC']),
+ 'CFLAGS += $(PREDEFS) -D_LONGINT',
+ 'NOOPTS = -O0',
+ 'FORTRAN = {0}'.format(os.environ['FC']),
+ 'LOADER = {0}'.format(os.environ['CC']),
+ # C preprocessor defs for compilation
+ 'CDEFS = -DAdd_'
+ ])
+
+ # Write configuration options to include file
+ with open('make.inc', 'w') as inc:
+ for option in config:
+ inc.write('{0}\n'.format(option))
+
+ def install(self, spec, prefix):
+ # Set up make include file manually
+ self.configure(spec)
+
+ # BLAS needs to be compiled separately if using internal BLAS library
+ if '+blas' not in spec:
+ make('blaslib')
+
+ make()
+
+ # Install manually
+ install_tree('lib', prefix.lib)
+
+ headers = glob.glob(join_path('SRC', '*.h'))
+ mkdir(prefix.include)
+ for h in headers:
+ install(h, prefix.include)