summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/spec.py24
-rw-r--r--lib/spack/spack/test/cmd/env.py35
2 files changed, 56 insertions, 3 deletions
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index cc2d5ca552..8c3969b8c9 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -1008,7 +1008,7 @@ class Spec(object):
self._normal = normal
self._concrete = concrete
self.external_path = external_path
- self.external_modules = external_modules
+ self.external_modules = Spec._format_module_list(external_modules)
self._full_hash = full_hash
# This attribute is used to store custom information for
@@ -1025,6 +1025,24 @@ class Spec(object):
elif spec_like is not None:
raise TypeError("Can't make spec out of %s" % type(spec_like))
+ @staticmethod
+ def _format_module_list(modules):
+ """Return a module list that is suitable for YAML serialization
+ and hash computation.
+
+ Given a module list, possibly read from a configuration file,
+ return an object that serializes to a consistent YAML string
+ before/after round-trip serialization to/from a Spec dictionary
+ (stored in JSON format): when read in, the module list may
+ contain YAML formatting that is discarded (non-essential)
+ when stored as a Spec dictionary; we take care in this function
+ to discard such formatting such that the Spec hash does not
+ change before/after storage in JSON.
+ """
+ if modules:
+ modules = list(modules)
+ return modules
+
@property
def external(self):
return bool(self.external_path) or bool(self.external_modules)
@@ -1383,8 +1401,8 @@ class Spec(object):
"""
# TODO: curently we strip build dependencies by default. Rethink
# this when we move to using package hashing on all specs.
- yaml_text = syaml.dump(
- self.to_node_dict(hash=hash), default_flow_style=True)
+ node_dict = self.to_node_dict(hash=hash)
+ yaml_text = syaml.dump(node_dict, default_flow_style=True)
sha = hashlib.sha1(yaml_text.encode('utf-8'))
b32_hash = base64.b32encode(sha.digest()).lower()
diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py
index b7aa02e607..5b719b2403 100644
--- a/lib/spack/spack/test/cmd/env.py
+++ b/lib/spack/spack/test/cmd/env.py
@@ -2154,3 +2154,38 @@ spack:
# Check that an update does not raise
env('update', '-y', str(abspath.dirname))
+
+
+@pytest.mark.regression('18338')
+def test_newline_in_commented_sequence_is_not_an_issue(tmpdir):
+ spack_yaml = """
+spack:
+ specs:
+ - dyninst
+ packages:
+ libelf:
+ externals:
+ - spec: libelf@0.8.13
+ modules:
+ - libelf/3.18.1
+
+ concretization: together
+"""
+ abspath = tmpdir.join('spack.yaml')
+ abspath.write(spack_yaml)
+
+ def extract_build_hash(environment):
+ _, dyninst = next(iter(environment.specs_by_hash.items()))
+ return dyninst['libelf'].build_hash()
+
+ # Concretize a first time and create a lockfile
+ with ev.Environment(str(tmpdir)) as e:
+ concretize()
+ libelf_first_hash = extract_build_hash(e)
+
+ # Check that a second run won't error
+ with ev.Environment(str(tmpdir)) as e:
+ concretize()
+ libelf_second_hash = extract_build_hash(e)
+
+ assert libelf_first_hash == libelf_second_hash