env: add spack env command, along with env.yaml schema and tests

Co-authored-by: Elizabeth Fischer <rpf2116@columbia.edu>
This commit is contained in:
Peter Josef Scheibel 2018-05-18 17:53:58 -07:00 committed by Todd Gamblin
parent 4b2f51d063
commit 31cb2041c3
4 changed files with 988 additions and 0 deletions

1
.gitignore vendored
View file

@ -1,6 +1,7 @@
/db /db
/var/spack/stage /var/spack/stage
/var/spack/cache /var/spack/cache
/var/spack/environments
/var/spack/repos/*/index.yaml /var/spack/repos/*/index.yaml
/var/spack/repos/*/lock /var/spack/repos/*/lock
*.pyc *.pyc

795
lib/spack/spack/cmd/env.py Normal file
View file

@ -0,0 +1,795 @@
# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import llnl.util.tty as tty
import spack
import llnl.util.filesystem as fs
import spack.modules
import spack.util.spack_json as sjson
import spack.util.spack_yaml as syaml
import spack.schema.env
import spack.config
import spack.cmd.spec
import spack.cmd.install
import spack.cmd.uninstall
import spack.cmd.module
import spack.cmd.common.arguments as arguments
from spack.config import ConfigScope
from spack.spec import Spec, CompilerSpec, FlagMap
from spack.repo import Repo
from spack.version import VersionList
from contextlib import contextmanager
import argparse
try:
from itertools import izip_longest as zip_longest
except ImportError:
from itertools import zip_longest
import os
import sys
import shutil
description = "group a subset of packages"
section = "environment"
level = "long"
_db_dirname = fs.join_path(spack.paths.var_path, 'environments')
def get_env_root(name):
"""Given an environment name, determines its root directory"""
return fs.join_path(_db_dirname, name)
def get_dotenv_dir(env_root):
"""@return Directory in an environment that is owned by Spack"""
return fs.join_path(env_root, '.env')
def get_write_paths(env_root):
"""Determines the names of temporary and permanent directories to
write machine-generated environment info."""
tmp_new = fs.join_path(env_root, '.env.new')
dest = get_dotenv_dir(env_root)
tmp_old = fs.join_path(env_root, '.env.old')
return tmp_new, dest, tmp_old
class Environment(object):
def clear(self):
self.user_specs = list()
self.concretized_order = list()
self.specs_by_hash = dict()
def __init__(self, name):
self.name = name
self.clear()
# Default config
self.yaml = {
'configs': ['<env>'],
'specs': []
}
@property
def path(self):
return get_env_root(self.name)
def repo_path(self):
return fs.join_path(get_dotenv_dir(self.path), 'repo')
def add(self, user_spec, report_existing=True):
"""Add a single user_spec (non-concretized) to the Environment"""
query_spec = Spec(user_spec)
existing = set(x for x in self.user_specs
if Spec(x).name == query_spec.name)
if existing:
if report_existing:
tty.die("Package {0} was already added to {1}"
.format(query_spec.name, self.name))
else:
tty.msg("Package {0} was already added to {1}"
.format(query_spec.name, self.name))
else:
tty.msg('Adding %s to environment %s' % (user_spec, self.name))
self.user_specs.append(user_spec)
def remove(self, query_spec):
"""Remove specs from an environment that match a query_spec"""
query_spec = Spec(query_spec)
match_index = -1
for i, spec in enumerate(self.user_specs):
if Spec(spec).name == query_spec.name:
match_index = i
break
if match_index < 0:
tty.die("Not found: {0}".format(query_spec))
del self.user_specs[match_index]
if match_index < len(self.concretized_order):
spec_hash = self.concretized_order[match_index]
del self.concretized_order[match_index]
del self.specs_by_hash[spec_hash]
def concretize(self, force=False):
"""Concretize user_specs in an Environment, creating (fully
concretized) specs.
force: bool
If set, re-concretize ALL specs, even those that were
already concretized.
"""
if force:
# Clear previously concretized specs
self.specs_by_hash = dict()
self.concretized_order = list()
num_concretized = len(self.concretized_order)
new_specs = list()
for user_spec in self.user_specs[num_concretized:]:
tty.msg('Concretizing %s' % user_spec)
spec = spack.cmd.parse_specs(user_spec)[0]
spec.concretize()
new_specs.append(spec)
dag_hash = spec.dag_hash()
self.specs_by_hash[dag_hash] = spec
self.concretized_order.append(spec.dag_hash())
# Display concretized spec to the user
sys.stdout.write(spec.tree(
recurse_dependencies=True, install_status=True,
hashlen=7, hashes=True))
return new_specs
def install(self, install_args=None):
"""Do a `spack install` on all the (concretized)
specs in an Environment."""
# Make sure log directory exists
logs = fs.join_path(self.path, 'logs')
try:
os.makedirs(logs)
except OSError:
if not os.path.isdir(logs):
raise
for concretized_hash in self.concretized_order:
spec = self.specs_by_hash[concretized_hash]
# Parse cli arguments and construct a dictionary
# that will be passed to Package.do_install API
kwargs = dict()
if install_args:
spack.cmd.install.update_kwargs_from_args(install_args, kwargs)
with pushd(self.path):
spec.package.do_install(**kwargs)
# Link the resulting log file into logs dir
logname = '%s-%s.log' % (spec.name, spec.dag_hash(7))
logpath = fs.join_path(logs, logname)
try:
os.remove(logpath)
except OSError:
pass
os.symlink(spec.package.build_log_path, logpath)
def uninstall(self, args):
"""Uninstall all the specs in an Environment."""
specs = self._get_environment_specs(recurse_dependencies=True)
args.all = False
spack.cmd.uninstall.uninstall_specs(args, specs)
def list(self, stream, **kwargs):
"""List the specs in an environment."""
for user_spec, concretized_hash in zip_longest(
self.user_specs, self.concretized_order):
stream.write('========= {0}\n'.format(user_spec))
if concretized_hash:
concretized_spec = self.specs_by_hash[concretized_hash]
stream.write(concretized_spec.tree(**kwargs))
def upgrade_dependency(self, dep_name, dry_run=False):
# TODO: if you have
# w -> x -> y
# and
# v -> x -> y
# then it would be desirable to ensure that w and v refer to the
# same x after upgrading y. This is not currently guaranteed.
new_order = list()
new_deps = list()
for i, spec_hash in enumerate(self.concretized_order):
spec = self.specs_by_hash[spec_hash]
if dep_name in spec:
if dry_run:
tty.msg("Would upgrade {0} for {1}"
.format(spec[dep_name].format(), spec.format()))
else:
new_spec = upgrade_dependency_version(spec, dep_name)
new_order.append(new_spec.dag_hash())
self.specs_by_hash[new_spec.dag_hash()] = new_spec
new_deps.append(new_spec[dep_name])
else:
new_order.append(spec_hash)
if not dry_run:
self.concretized_order = new_order
return new_deps[0] if new_deps else None
def reset_os_and_compiler(self, compiler=None):
new_order = list()
new_specs_by_hash = {}
for spec_hash in self.concretized_order:
spec = self.specs_by_hash[spec_hash]
new_spec = reset_os_and_compiler(spec, compiler)
new_order.append(new_spec.dag_hash())
new_specs_by_hash[new_spec.dag_hash()] = new_spec
self.concretized_order = new_order
self.specs_by_hash = new_specs_by_hash
def _get_environment_specs(self, recurse_dependencies=True):
"""Returns the specs of all the packages in an environment.
If these specs appear under different user_specs, only one copy
is added to the list returned."""
package_to_spec = {}
spec_list = list()
for spec_hash in self.concretized_order:
spec = self.specs_by_hash[spec_hash]
specs = spec.traverse(deptype=('link', 'run')) \
if recurse_dependencies else (spec,)
for dep in specs:
if dep.name in package_to_spec:
tty.warn("{0} takes priority over {1}"
.format(package_to_spec[dep.name].format(),
dep.format()))
else:
package_to_spec[dep.name] = dep
spec_list.append(dep)
return spec_list
def to_dict(self):
"""Used in serializing to JSON"""
concretized_order = list(self.concretized_order)
concrete_specs = dict()
for spec in self.specs_by_hash.values():
for s in spec.traverse():
if s.dag_hash() not in concrete_specs:
concrete_specs[s.dag_hash()] = (
s.to_node_dict(all_deps=True))
format = {
'user_specs': self.user_specs,
'concretized_order': concretized_order,
'concrete_specs': concrete_specs,
}
return format
@staticmethod
def from_dict(name, d):
"""Used in deserializing from JSON"""
env = Environment(name)
env.user_specs = list(d['user_specs'])
env.concretized_order = list(d['concretized_order'])
specs_dict = d['concrete_specs']
hash_to_node_dict = specs_dict
root_hashes = set(env.concretized_order)
specs_by_hash = {}
for dag_hash, node_dict in hash_to_node_dict.items():
specs_by_hash[dag_hash] = Spec.from_node_dict(node_dict)
for dag_hash, node_dict in hash_to_node_dict.items():
for dep_name, dep_hash, deptypes in (
Spec.dependencies_from_node_dict(node_dict)):
specs_by_hash[dag_hash]._add_dependency(
specs_by_hash[dep_hash], deptypes)
env.specs_by_hash = dict(
(x, y) for x, y in specs_by_hash.items() if x in root_hashes)
return env
def reset_os_and_compiler(spec, compiler=None):
spec = spec.copy()
for x in spec.traverse():
x.compiler = None
x.architecture = None
x.compiler_flags = FlagMap(x)
x._concrete = False
x._hash = None
if compiler:
spec.compiler = CompilerSpec(compiler)
spec.concretize()
return spec
def upgrade_dependency_version(spec, dep_name):
spec = spec.copy()
for x in spec.traverse():
x._concrete = False
x._normal = False
x._hash = None
spec[dep_name].versions = VersionList(':')
spec.concretize()
return spec
def check_consistent_env(env_root):
tmp_new, dest, tmp_old = get_write_paths(env_root)
if os.path.exists(tmp_new) or os.path.exists(tmp_old):
tty.die("Partial write state, run 'spack env repair'")
def write(environment, new_repo=None):
"""Writes an in-memory environment back to its location on disk,
in an atomic manner."""
tmp_new, dest, tmp_old = get_write_paths(get_env_root(environment.name))
# Write the machine-generated stuff
fs.mkdirp(tmp_new)
# create one file for the environment object
with open(fs.join_path(tmp_new, 'environment.json'), 'w') as f:
sjson.dump(environment.to_dict(), stream=f)
dest_repo_dir = fs.join_path(tmp_new, 'repo')
if new_repo:
shutil.copytree(new_repo.root, dest_repo_dir)
elif os.path.exists(environment.repo_path()):
shutil.copytree(environment.repo_path(), dest_repo_dir)
# Swap in new directory atomically
if os.path.exists(dest):
shutil.move(dest, tmp_old)
shutil.move(tmp_new, dest)
if os.path.exists(tmp_old):
shutil.rmtree(tmp_old)
def repair(environment_name):
"""Recovers from crash during critical section of write().
Possibilities:
tmp_new, dest
tmp_new, tmp_old
tmp_old, dest
"""
tmp_new, dest, tmp_old = get_write_paths(get_env_root(environment_name))
if os.path.exists(tmp_old):
if not os.path.exists(dest):
shutil.move(tmp_new, dest)
else:
shutil.rmtree(tmp_old)
tty.info("Previous update completed")
elif os.path.exists(tmp_new):
tty.info("Previous update did not complete")
else:
tty.info("Previous update may have completed")
if os.path.exists(tmp_new):
shutil.rmtree(tmp_new)
def read(environment_name):
# Check that env is in a consistent state on disk
env_root = get_env_root(environment_name)
# Read env.yaml file
env_yaml = spack.config._read_config_file(
fs.join_path(env_root, 'env.yaml'),
spack.schema.env.schema)
dotenv_dir = get_dotenv_dir(env_root)
with open(fs.join_path(dotenv_dir, 'environment.json'), 'r') as f:
environment_dict = sjson.load(f)
environment = Environment.from_dict(environment_name, environment_dict)
if env_yaml:
environment.yaml = env_yaml['env']
return environment
# =============== Modifies Environment
def environment_create(args):
if os.path.exists(get_env_root(args.environment)):
raise tty.die("Environment already exists: " + args.environment)
_environment_create(args.environment)
def _environment_create(name, init_config=None):
environment = Environment(name)
user_specs = list()
config_sections = {}
if init_config:
for key, val in init_config.items():
if key == 'user_specs':
user_specs.extend(val)
else:
config_sections[key] = val
for user_spec in user_specs:
environment.add(user_spec)
write(environment)
# When creating the environment, the user may specify configuration
# to place in the environment initially. Spack does not interfere
# with this configuration after initialization so it is handled here
if len(config_sections) > 0:
config_basedir = fs.join_path(environment.path, 'config')
os.mkdir(config_basedir)
for key, val in config_sections.items():
yaml_section = syaml.dump({key: val}, default_flow_style=False)
yaml_file = '{0}.yaml'.format(key)
yaml_path = fs.join_path(config_basedir, yaml_file)
with open(yaml_path, 'w') as f:
f.write(yaml_section)
def environment_add(args):
check_consistent_env(get_env_root(args.environment))
environment = read(args.environment)
parsed_specs = spack.cmd.parse_specs(args.package)
if args.all:
# Don't allow command-line specs with --all
if len(parsed_specs) > 0:
tty.die('Cannot specify --all and specs too on the command line')
yaml_specs = environment.yaml['specs']
if len(yaml_specs) == 0:
tty.msg('No specs to add from env.yaml')
# Add list of specs from env.yaml file
for user_spec, _ in yaml_specs.items(): # OrderedDict
environment.add(str(user_spec), report_existing=False)
else:
for spec in parsed_specs:
environment.add(str(spec))
write(environment)
def environment_remove(args):
check_consistent_env(get_env_root(args.environment))
environment = read(args.environment)
if args.all:
environment.clear()
else:
for spec in spack.cmd.parse_specs(args.package):
environment.remove(spec.format())
write(environment)
def environment_spec(args):
environment = read(args.environment)
prepare_repository(environment, use_repo=args.use_repo)
prepare_config_scope(environment)
spack.cmd.spec.spec(None, args)
def environment_concretize(args):
check_consistent_env(get_env_root(args.environment))
environment = read(args.environment)
_environment_concretize(
environment, use_repo=args.use_repo, force=args.force)
def _environment_concretize(environment, use_repo=False, force=False):
"""Function body separated out to aid in testing."""
# Change global search paths
repo = prepare_repository(environment, use_repo=use_repo)
prepare_config_scope(environment)
new_specs = environment.concretize(force=force)
for spec in new_specs:
for dep in spec.traverse():
dump_to_environment_repo(dep, repo)
# Moves <env>/.env.new to <env>/.env
write(environment, repo)
# =============== Does not Modify Environment
def environment_install(args):
check_consistent_env(get_env_root(args.environment))
environment = read(args.environment)
prepare_repository(environment, use_repo=args.use_repo)
environment.install(args)
def environment_uninstall(args):
check_consistent_env(get_env_root(args.environment))
environment = read(args.environment)
prepare_repository(environment)
environment.uninstall(args)
# =======================================
def dump_to_environment_repo(spec, repo):
dest_pkg_dir = repo.dirname_for_package_name(spec.name)
if not os.path.exists(dest_pkg_dir):
spack.repo.path.dump_provenance(spec, dest_pkg_dir)
def prepare_repository(environment, remove=None, use_repo=False):
"""Adds environment's repository to the global search path of repos"""
import tempfile
repo_stage = tempfile.mkdtemp()
new_repo_dir = fs.join_path(repo_stage, 'repo')
if os.path.exists(environment.repo_path()):
shutil.copytree(environment.repo_path(), new_repo_dir)
else:
spack.repo.create_repo(new_repo_dir, environment.name)
if remove:
remove_dirs = []
repo = Repo(new_repo_dir)
for pkg_name in remove:
remove_dirs.append(repo.dirname_for_package_name(pkg_name))
for d in remove_dirs:
shutil.rmtree(d)
repo = Repo(new_repo_dir)
if use_repo:
spack.repo.put_first(repo)
return repo
def prepare_config_scope(environment):
"""Adds environment's scope to the global search path
of configuration scopes"""
# Load up configs
for config_spec in environment.yaml['configs']:
config_name = os.path.split(config_spec)[1]
if config_name == '<env>':
# Use default config for the environment; doesn't have to exist
config_dir = fs.join_path(environment.path, 'config')
if not os.path.isdir(config_dir):
continue
config_name = environment.name
else:
# Use external user-provided config
config_dir = os.path.normpath(os.path.join(
environment.path, config_spec.format(**os.environ)))
if not os.path.isdir(config_dir):
tty.die('Spack config %s (%s) not found' %
(config_name, config_dir))
tty.msg('Using Spack config %s scope at %s' %
(config_name, config_dir))
spack.config.config.push_scope(ConfigScope(config_name, config_dir))
def environment_relocate(args):
environment = read(args.environment)
prepare_repository(environment, use_repo=args.use_repo)
environment.reset_os_and_compiler(compiler=args.compiler)
write(environment)
def environment_list(args):
# TODO? option to list packages w/ multiple instances?
environment = read(args.environment)
import sys
environment.list(
sys.stdout, recurse_dependencies=args.recurse_dependencies,
hashes=args.long or args.very_long,
hashlen=None if args.very_long else 7,
install_status=args.install_status)
def environment_stage(args):
environment = read(args.environment)
prepare_repository(environment, use_repo=args.use_repo)
for spec in environment.specs_by_hash.values():
for dep in spec.traverse():
dep.package.do_stage()
def environment_location(args):
environment = read(args.environment)
print(environment.path)
@contextmanager
def redirect_stdout(ofname):
"""Redirects STDOUT to (by default) a file within the environment;
or else a user-specified filename."""
with open(ofname, 'w') as f:
original = sys.stdout
sys.stdout = f
yield
sys.stdout = original
@contextmanager
def pushd(dir):
original = os.getcwd()
os.chdir(dir)
yield
os.chdir(original)
def environment_loads(args):
# Set the module types that have been selected
module_types = args.module_type
if module_types is None:
# If no selection has been made select all of them
module_types = ['tcl']
module_types = list(set(module_types))
environment = read(args.environment)
recurse_dependencies = args.recurse_dependencies
args.recurse_dependencies = False
ofname = fs.join_path(environment.path, 'loads')
with redirect_stdout(ofname):
specs = environment._get_environment_specs(
recurse_dependencies=recurse_dependencies)
spack.cmd.module.loads(module_types, specs, args)
print('To load this environment, type:')
print(' source %s' % ofname)
def environment_upgrade_dependency(args):
environment = read(args.environment)
repo = prepare_repository(
environment, use_repo=args.use_repo, remove=[args.dep_name])
new_dep = environment.upgrade_dependency(args.dep_name, args.dry_run)
if not args.dry_run and new_dep:
dump_to_environment_repo(new_dep, repo)
write(environment, repo)
def add_use_repo_argument(cmd_parser):
cmd_parser.add_argument(
'--use-env-repo', action='store_true', dest='use_repo',
help='Use package definitions stored in the environment'
)
def setup_parser(subparser):
subparser.add_argument(
'environment',
help="The environment you are working with"
)
sp = subparser.add_subparsers(
metavar='SUBCOMMAND', dest='environment_command')
create_parser = sp.add_parser('create', help='Make an environment')
create_parser.add_argument(
'--init-file', dest='init_file',
help='File with user specs to add and configuration yaml to use'
)
add_parser = sp.add_parser('add', help='Add a spec to an environment')
add_parser.add_argument(
'-a', '--all', action='store_true', dest='all',
help="Add all specs listed in env.yaml")
add_parser.add_argument(
'package',
nargs=argparse.REMAINDER,
help="Spec of the package to add"
)
remove_parser = sp.add_parser(
'remove', help='Remove a spec from this environment')
remove_parser.add_argument(
'-a', '--all', action='store_true', dest='all',
help="Remove all specs from (clear) the environment")
remove_parser.add_argument(
'package',
nargs=argparse.REMAINDER,
help="Spec of the package to remove"
)
spec_parser = sp.add_parser(
'spec', help='Concretize sample spec')
spack.cmd.spec.add_common_arguments(spec_parser)
add_use_repo_argument(spec_parser)
concretize_parser = sp.add_parser(
'concretize', help='Concretize user specs')
concretize_parser.add_argument(
'-f', '--force', action='store_true',
help="Re-concretize even if already concretized.")
add_use_repo_argument(concretize_parser)
relocate_parser = sp.add_parser(
'relocate',
help='Reconcretize environment with new OS and/or compiler')
relocate_parser.add_argument(
'--compiler',
help="Compiler spec to use"
)
add_use_repo_argument(relocate_parser)
list_parser = sp.add_parser('list', help='List specs in an environment')
arguments.add_common_arguments(
list_parser,
['recurse_dependencies', 'long', 'very_long', 'install_status'])
loads_parser = sp.add_parser(
'loads',
help='List modules for an installed environment '
'(see spack module loads)')
spack.cmd.modules.add_loads_arguments(loads_parser)
sp.add_parser(
'location',
help='Print the root directory of the environment')
upgrade_parser = sp.add_parser(
'upgrade',
help='''Upgrade a dependency package in an environment to the latest
version''')
upgrade_parser.add_argument(
'dep_name', help='Dependency package to upgrade')
upgrade_parser.add_argument(
'--dry-run', action='store_true', dest='dry_run',
help="Just show the updates that would take place")
add_use_repo_argument(upgrade_parser)
stage_parser = sp.add_parser(
'stage',
help='Download all source files for all packages in an environment')
add_use_repo_argument(stage_parser)
config_update_parser = sp.add_parser(
'update-config',
help='Add config yaml file to environment')
config_update_parser.add_argument(
'config_files',
nargs=argparse.REMAINDER,
help="Configuration files to add"
)
install_parser = sp.add_parser(
'install',
help='Install all concretized specs in an environment')
spack.cmd.install.add_common_arguments(install_parser)
add_use_repo_argument(install_parser)
uninstall_parser = sp.add_parser(
'uninstall',
help='Uninstall all concretized specs in an environment')
spack.cmd.uninstall.add_common_arguments(uninstall_parser)
def env(parser, args, **kwargs):
action = {
'create': environment_create,
'add': environment_add,
'spec': environment_spec,
'concretize': environment_concretize,
'list': environment_list,
'loads': environment_loads,
'location': environment_location,
'remove': environment_remove,
'relocate': environment_relocate,
'upgrade': environment_upgrade_dependency,
'stage': environment_stage,
'install': environment_install,
'uninstall': environment_uninstall
}
action[args.environment_command](args)

View file

@ -0,0 +1,43 @@
# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Schema for env.yaml configuration file.
.. literalinclude:: ../spack/schema/env.py
:lines: 32-
"""
schema = {
'$schema': 'http://json-schema.org/schema#',
'title': 'Spack Environments user configuration file schema',
'type': 'object',
'additionalProperties': False,
'properties': {
'env': {
'type': 'object',
'default': {},
'properties': {
'configs': {
'type': 'array',
'default': [],
'items': {'type': 'string'}
},
'specs': {
'type': 'object',
'default': {},
'additionalProperties': False,
'patternProperties': {
r'\w[\w-]*': { # user spec
'type': 'object',
'default': {},
'additionalProperties': False,
}
}
}
}
}
}
}

View file

@ -0,0 +1,149 @@
# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import unittest
import tempfile
import shutil
import pytest
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
import spack.cmd.env
import spack.modules
import spack.util.spack_yaml as syaml
from spack.cmd.env import (Environment, prepare_repository,
_environment_concretize, prepare_config_scope,
_environment_create)
from spack.version import Version
class TestEnvironment(unittest.TestCase):
def setUp(self):
self.env_dir = spack.cmd.env._db_dirname
spack.cmd.env._db_dirname = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(spack.cmd.env._db_dirname)
spack.cmd.env._db_dirname = self.env_dir
def test_add(self):
c = Environment('test')
c.add('mpileaks')
assert 'mpileaks' in c.user_specs
@pytest.mark.usefixtures('config', 'mutable_mock_packages')
def test_concretize(self):
c = Environment('test')
c.add('mpileaks')
c.concretize()
env_specs = c._get_environment_specs()
assert any(x.name == 'mpileaks' for x in env_specs)
@pytest.mark.usefixtures('config', 'mutable_mock_packages',
'install_mockery', 'mock_fetch')
def test_env_install(self):
c = Environment('test')
c.add('cmake-client')
c.concretize()
c.install()
env_specs = c._get_environment_specs()
spec = next(x for x in env_specs if x.name == 'cmake-client')
assert spec.package.installed
@pytest.mark.usefixtures('config', 'mutable_mock_packages')
def test_remove_after_concretize(self):
c = Environment('test')
c.add('mpileaks')
c.concretize()
c.add('python')
c.concretize()
c.remove('mpileaks')
env_specs = c._get_environment_specs()
assert not any(x.name == 'mpileaks' for x in env_specs)
@pytest.mark.usefixtures('config', 'mutable_mock_packages')
def test_reset_compiler(self):
c = Environment('test')
c.add('mpileaks')
c.concretize()
first_spec = c.specs_by_hash[c.concretized_order[0]]
available = set(['gcc', 'clang'])
available.remove(first_spec.compiler.name)
new_compiler = next(iter(available))
c.reset_os_and_compiler(compiler=new_compiler)
new_spec = c.specs_by_hash[c.concretized_order[0]]
assert new_spec.compiler != first_spec.compiler
@pytest.mark.usefixtures('config', 'mutable_mock_packages')
def test_environment_list(self):
c = Environment('test')
c.add('mpileaks')
c.concretize()
c.add('python')
mock_stream = StringIO()
c.list(mock_stream)
list_content = mock_stream.getvalue()
assert 'mpileaks' in list_content
assert 'python' in list_content
mpileaks_spec = c.specs_by_hash[c.concretized_order[0]]
assert mpileaks_spec.format() in list_content
@pytest.mark.usefixtures('config', 'mutable_mock_packages')
def test_upgrade_dependency(self):
c = Environment('test')
c.add('mpileaks ^callpath@0.9')
c.concretize()
c.upgrade_dependency('callpath')
env_specs = c._get_environment_specs()
callpath_dependents = list(x for x in env_specs if 'callpath' in x)
assert callpath_dependents
for spec in callpath_dependents:
assert spec['callpath'].version == Version('1.0')
@pytest.mark.usefixtures('config', 'mutable_mock_packages')
def test_init_config(self):
test_config = """user_specs:
- mpileaks
packages:
mpileaks:
version: [2.2]
"""
spack.package_prefs.PackagePrefs._packages_config_cache = None
spack.package_prefs.PackagePrefs._spec_cache = {}
_environment_create('test', syaml.load(StringIO(test_config)))
c = spack.cmd.env.read('test')
prepare_config_scope(c)
c.concretize()
assert any(x.satisfies('mpileaks@2.2')
for x in c._get_environment_specs())
@pytest.mark.usefixtures('config', 'mutable_mock_packages')
def test_to_dict(self):
c = Environment('test')
c.add('mpileaks')
c.concretize()
context_dict = c.to_dict()
c_copy = Environment.from_dict('test_copy', context_dict)
assert c.specs_by_hash == c_copy.specs_by_hash
@pytest.mark.usefixtures('config', 'mutable_mock_packages')
def test_prepare_repo(self):
c = Environment('testx')
c.add('mpileaks')
_environment_concretize(c)
repo = None
try:
repo = prepare_repository(c)
package = repo.get(spack.spec.Spec('mpileaks'))
assert package.namespace.split('.')[-1] == 'testx'
finally:
if repo:
shutil.rmtree(repo.root)