summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/docs/environments.rst36
-rw-r--r--lib/spack/spack/spec_list.py30
-rw-r--r--lib/spack/spack/test/cmd/env.py49
3 files changed, 110 insertions, 5 deletions
diff --git a/lib/spack/docs/environments.rst b/lib/spack/docs/environments.rst
index c26f81d914..b80e36b4b2 100644
--- a/lib/spack/docs/environments.rst
+++ b/lib/spack/docs/environments.rst
@@ -602,7 +602,7 @@ files are identical.
spack:
definitions:
- first: [libelf, libdwarf]
- - compilers: ['%gcc', '^intel']
+ - compilers: ['%gcc', '%intel']
- second:
- $first
- matrix:
@@ -676,6 +676,40 @@ The valid variables for a ``when`` clause are:
#. ``hostname``. The hostname of the system (if ``hostname`` is an
executable in the user's PATH).
+""""""""""""""""""""""""
+SpecLists as Constraints
+""""""""""""""""""""""""
+
+Dependencies and compilers in Spack can be both packages in an
+environment and constraints on other packages. References to SpecLists
+allow a shorthand to treat packages in a list as either a compiler or
+a dependency using the ``$%`` or ``$^`` syntax respectively.
+
+For example, the following environment has three root packages:
+``gcc@8.1.0``, ``mvapich2@2.3.1 %gcc@8.1.0``, and ``hdf5+mpi
+%gcc@8.1.0 ^mvapich2@2.3.1``.
+
+.. code-block:: yaml
+
+ spack:
+ definitions:
+ - compilers: [gcc@8.1.0]
+ - mpis: [mvapich2@2.3.1]
+ - packages: [hdf5+mpi]
+
+ specs:
+ - $compilers
+ - matrix:
+ - [$mpis]
+ - [$%compilers]
+ - matrix:
+ - [$packages]
+ - [$^mpis]
+ - [$%compilers]
+
+This allows for a much-needed reduction in redundancy between packages
+and constraints.
+
^^^^^^^^^^^^^^^^^^^^^^^^^
Environment-managed Views
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/lib/spack/spack/spec_list.py b/lib/spack/spack/spec_list.py
index 63bd29a7a9..de75458f32 100644
--- a/lib/spack/spack/spec_list.py
+++ b/lib/spack/spack/spec_list.py
@@ -124,12 +124,27 @@ class SpecList(object):
if isinstance(yaml, list):
for idx, item in enumerate(yaml):
if isinstance(item, string_types) and item.startswith('$'):
- name = item[1:]
+ # Reference type can add a constraint to items
+ if item[1] in ('^', '%'):
+ name = item[2:]
+ sigil = item[1]
+ else:
+ name = item[1:]
+ sigil = ''
if name in self._reference:
ret = [self._expand_references(i) for i in yaml[:idx]]
ret += self._reference[name].specs_as_yaml_list
ret += self._expand_references(yaml[idx + 1:])
- return ret
+
+ # Add the sigil if we're mapping a sigil to a ref
+ def sigilify(arg):
+ if isinstance(arg, dict):
+ if sigil:
+ arg['sigil'] = sigil
+ return arg
+ else:
+ return sigil + arg
+ return list(map(sigilify, ret))
else:
msg = 'SpecList %s refers to ' % self.name
msg += 'named list %s ' % name
@@ -159,13 +174,17 @@ def _expand_matrix_constraints(object, specify=True):
new_row = []
for r in row:
if isinstance(r, dict):
- new_row.extend(_expand_matrix_constraints(r, specify=False))
+ new_row.extend(
+ [[' '.join(c)]
+ for c in _expand_matrix_constraints(r, specify=False)])
else:
new_row.append([r])
expanded_rows.append(new_row)
- results = []
excludes = object.get('exclude', []) # only compute once
+ sigil = object.get('sigil', '')
+
+ results = []
for combo in itertools.product(*expanded_rows):
# Construct a combined spec to test against excludes
flat_combo = [constraint for list in combo for constraint in list]
@@ -174,6 +193,9 @@ def _expand_matrix_constraints(object, specify=True):
if any(test_spec.satisfies(x) for x in excludes):
continue
+ if sigil: # add sigil if necessary
+ ordered_combo[0] = sigil + ordered_combo[0]
+
# Add to list of constraints
if specify:
results.append([Spec(x) for x in ordered_combo])
diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py
index f35b0a4bc1..671d9869ed 100644
--- a/lib/spack/spack/test/cmd/env.py
+++ b/lib/spack/spack/test/cmd/env.py
@@ -1040,6 +1040,55 @@ env:
assert Spec('callpath') in test.user_specs
+def test_stack_yaml_definitions_as_constraints(tmpdir):
+ filename = str(tmpdir.join('spack.yaml'))
+ with open(filename, 'w') as f:
+ f.write("""\
+env:
+ definitions:
+ - packages: [mpileaks, callpath]
+ - mpis: [mpich, openmpi]
+ specs:
+ - matrix:
+ - [$packages]
+ - [$^mpis]
+""")
+ with tmpdir.as_cwd():
+ env('create', 'test', './spack.yaml')
+ test = ev.read('test')
+
+ assert Spec('mpileaks^mpich') in test.user_specs
+ assert Spec('callpath^mpich') in test.user_specs
+ assert Spec('mpileaks^openmpi') in test.user_specs
+ assert Spec('callpath^openmpi') in test.user_specs
+
+
+def test_stack_yaml_definitions_as_constraints_on_matrix(tmpdir):
+ filename = str(tmpdir.join('spack.yaml'))
+ with open(filename, 'w') as f:
+ f.write("""\
+env:
+ definitions:
+ - packages: [mpileaks, callpath]
+ - mpis:
+ - matrix:
+ - [mpich]
+ - ['@3.0.4', '@3.0.3']
+ specs:
+ - matrix:
+ - [$packages]
+ - [$^mpis]
+""")
+ with tmpdir.as_cwd():
+ env('create', 'test', './spack.yaml')
+ test = ev.read('test')
+
+ assert Spec('mpileaks^mpich@3.0.4') in test.user_specs
+ assert Spec('callpath^mpich@3.0.4') in test.user_specs
+ assert Spec('mpileaks^mpich@3.0.3') in test.user_specs
+ assert Spec('callpath^mpich@3.0.3') in test.user_specs
+
+
@pytest.mark.regression('12095')
def test_stack_yaml_definitions_write_reference(tmpdir):
filename = str(tmpdir.join('spack.yaml'))