summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGreg Becker <becker33@llnl.gov>2023-04-28 14:33:05 -0700
committerGitHub <noreply@github.com>2023-04-28 23:33:05 +0200
commit21cadf96e02da56a13bc9657e5740a770763e8f9 (patch)
tree2654a9e5b2c2261a967a41abf5c7c97ada6e036a /lib
parentcceeb96e0661793e395ade1c139f7261a97eeaae (diff)
downloadspack-21cadf96e02da56a13bc9657e5740a770763e8f9.tar.gz
spack-21cadf96e02da56a13bc9657e5740a770763e8f9.tar.bz2
spack-21cadf96e02da56a13bc9657e5740a770763e8f9.tar.xz
spack-21cadf96e02da56a13bc9657e5740a770763e8f9.zip
Spec.format: fix bug in dependency hash formatting (#37073)
Co-authored-by: becker33 <becker33@users.noreply.github.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/spec.py23
-rw-r--r--lib/spack/spack/test/spec_semantics.py95
2 files changed, 58 insertions, 60 deletions
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index ced35d5c7d..78530f7a7c 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -4115,18 +4115,10 @@ class Spec(object):
clr.cwrite(f, stream=out, color=color)
def write_attribute(spec, attribute, color):
- current = spec
- if attribute.startswith("^"):
- attribute = attribute[1:]
- dep, attribute = attribute.split(".", 1)
- current = self[dep]
-
- if attribute == "":
- raise SpecFormatStringError("Format string attributes must be non-empty")
attribute = attribute.lower()
sig = ""
- if attribute[0] in "@%/":
+ if attribute.startswith(("@", "%", "/")):
# color sigils that are inside braces
sig = attribute[0]
attribute = attribute[1:]
@@ -4134,6 +4126,15 @@ class Spec(object):
sig = " arch=" # include space as separator
attribute = attribute[5:]
+ current = spec
+ if attribute.startswith("^"):
+ attribute = attribute[1:]
+ dep, attribute = attribute.split(".", 1)
+ current = self[dep]
+
+ if attribute == "":
+ raise SpecFormatStringError("Format string attributes must be non-empty")
+
parts = attribute.split(".")
assert parts
@@ -4162,9 +4163,9 @@ class Spec(object):
col = "#"
if ":" in attribute:
_, length = attribute.split(":")
- write(sig + morph(spec, spec.dag_hash(int(length))), col)
+ write(sig + morph(spec, current.dag_hash(int(length))), col)
else:
- write(sig + morph(spec, spec.dag_hash()), col)
+ write(sig + morph(spec, current.dag_hash()), col)
return
# Iterate over components using getattr to get next element
diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py
index 63e2253b68..01a8de5e46 100644
--- a/lib/spack/spack/test/spec_semantics.py
+++ b/lib/spack/spack/test/spec_semantics.py
@@ -621,65 +621,62 @@ class TestSpecSemantics(object):
# Testing named strings ie {string} and whether we get
# the correct component
# Mixed case intentional to test both
+ # Fields are as follow
+ # fmt_str: the format string to test
+ # sigil: the portion that is a sigil (may be empty string)
+ # prop: the property to get
+ # component: subcomponent of spec from which to get property
package_segments = [
- ("{NAME}", "name"),
- ("{VERSION}", "versions"),
- ("{compiler}", "compiler"),
- ("{compiler_flags}", "compiler_flags"),
- ("{variants}", "variants"),
- ("{architecture}", "architecture"),
+ ("{NAME}", "", "name", lambda spec: spec),
+ ("{VERSION}", "", "versions", lambda spec: spec),
+ ("{compiler}", "", "compiler", lambda spec: spec),
+ ("{compiler_flags}", "", "compiler_flags", lambda spec: spec),
+ ("{variants}", "", "variants", lambda spec: spec),
+ ("{architecture}", "", "architecture", lambda spec: spec),
+ ("{@VERSIONS}", "@", "version", lambda spec: spec),
+ ("{%compiler}", "%", "compiler", lambda spec: spec),
+ ("{arch=architecture}", "arch=", "architecture", lambda spec: spec),
+ ("{compiler.name}", "", "name", lambda spec: spec.compiler),
+ ("{compiler.version}", "", "versions", lambda spec: spec.compiler),
+ ("{%compiler.name}", "%", "name", lambda spec: spec.compiler),
+ ("{@compiler.version}", "@", "version", lambda spec: spec.compiler),
+ ("{architecture.platform}", "", "platform", lambda spec: spec.architecture),
+ ("{architecture.os}", "", "os", lambda spec: spec.architecture),
+ ("{architecture.target}", "", "target", lambda spec: spec.architecture),
+ ("{prefix}", "", "prefix", lambda spec: spec),
]
- sigil_package_segments = [
- ("{@VERSIONS}", "@" + str(spec.version)),
- ("{%compiler}", "%" + str(spec.compiler)),
- ("{arch=architecture}", "arch=" + str(spec.architecture)),
- ]
-
- compiler_segments = [("{compiler.name}", "name"), ("{compiler.version}", "versions")]
-
- sigil_compiler_segments = [
- ("{%compiler.name}", "%" + spec.compiler.name),
- ("{@compiler.version}", "@" + str(spec.compiler.version)),
- ]
-
- architecture_segments = [
- ("{architecture.platform}", "platform"),
- ("{architecture.os}", "os"),
- ("{architecture.target}", "target"),
+ hash_segments = [
+ ("{hash:7}", "", lambda s: s.dag_hash(7)),
+ ("{/hash}", "/", lambda s: "/" + s.dag_hash()),
]
other_segments = [
("{spack_root}", spack.paths.spack_root),
("{spack_install}", spack.store.layout.root),
- ("{hash:7}", spec.dag_hash(7)),
- ("{/hash}", "/" + spec.dag_hash()),
]
- for named_str, prop in package_segments:
- expected = getattr(spec, prop, "")
- actual = spec.format(named_str)
- assert str(expected).strip() == actual
-
- for named_str, expected in sigil_package_segments:
- actual = spec.format(named_str)
- assert expected == actual
-
- compiler = spec.compiler
- for named_str, prop in compiler_segments:
- expected = getattr(compiler, prop, "")
- actual = spec.format(named_str)
- assert str(expected) == actual
-
- for named_str, expected in sigil_compiler_segments:
- actual = spec.format(named_str)
- assert expected == actual
-
- arch = spec.architecture
- for named_str, prop in architecture_segments:
- expected = getattr(arch, prop, "")
- actual = spec.format(named_str)
- assert str(expected) == actual
+ def depify(depname, fmt_str, sigil):
+ sig = len(sigil)
+ opening = fmt_str[: 1 + sig]
+ closing = fmt_str[1 + sig :]
+ return spec[depname], opening + f"^{depname}." + closing
+
+ def check_prop(check_spec, fmt_str, prop, getter):
+ actual = spec.format(fmt_str)
+ expected = getter(check_spec)
+ assert actual == str(expected).strip()
+
+ for named_str, sigil, prop, get_component in package_segments:
+ getter = lambda s: sigil + str(getattr(get_component(s), prop, ""))
+ check_prop(spec, named_str, prop, getter)
+ mpi, fmt_str = depify("mpi", named_str, sigil)
+ check_prop(mpi, fmt_str, prop, getter)
+
+ for named_str, sigil, getter in hash_segments:
+ assert spec.format(named_str) == getter(spec)
+ callpath, fmt_str = depify("callpath", named_str, sigil)
+ assert spec.format(fmt_str) == getter(callpath)
for named_str, expected in other_segments:
actual = spec.format(named_str)