diff options
-rw-r--r-- | lib/spack/spack/spec.py | 24 | ||||
-rw-r--r-- | lib/spack/spack/test/cmd/env.py | 35 |
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 |