From 81126fa4102362fe799b2ce2f125558a2f5b7d6b Mon Sep 17 00:00:00 2001 From: Peter Josef Scheibel Date: Thu, 8 Feb 2024 17:44:50 -0800 Subject: [PATCH] add spack-python script to output diff in tree format --- lib/scripts/altdiff.py | 127 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 lib/scripts/altdiff.py diff --git a/lib/scripts/altdiff.py b/lib/scripts/altdiff.py new file mode 100644 index 000000000..b252e3bfe --- /dev/null +++ b/lib/scripts/altdiff.py @@ -0,0 +1,127 @@ +import spack.cmd +import spack.environment as ev +import spack.traverse as traverse +from llnl.util.tty.color import cwrite + +import sys + + +def diff_specs(spec_a, spec_b): + def highlight(element): + cwrite("@R{%s}" % str(element)) + + def _write(s): + print(s, end="") + + def _variant_str(v): + if isinstance(v, spack.variant.BoolValuedVariant): + return str(v) + else: + return " " + str(v) + + class VariantsComparator: + def __init__(self, spec): + self.variants = spec.variants + + def compare(self, other_spec): + if not self.variants: + return + for k, v in self.variants.items(): + if k not in other_spec.variants: + highlight(_variant_str(v)) + elif not v.satisfies(other_spec.variants[k]): + highlight(_variant_str(v)) + else: + _write(_variant_str(v)) + + class VersionComparator: + def __init__(self, spec): + self.version = spec.version + + def compare(self, other_spec): + other_version = other_spec.version + if self.version.satisfies(other_version): + _write(f"@{self.version}") + else: + highlight(f"@{self.version}") + + class CompilerComparator: + def __init__(self, spec): + self.compiler = spec.compiler + + def compare(self, other_spec): + other_cmp = other_spec.compiler + if self.compiler.name == other_cmp.name: + _write(f"%{self.compiler.name}") + if self.compiler.version.satisfies(other_cmp.version): + _write(f"@{self.compiler.version}") + else: + highlight(f"@{self.compiler.version}") + else: + highlight(f"%{self.compiler}") + + class DepsComparator: + def __init__(self, spec, newline_cb): + self.spec = spec + self.newline_cb = newline_cb + + def compare(self, other_spec): + self_deps = set(x.name for x in self.spec.dependencies()) + other_deps = set(x.name for x in other_spec.dependencies()) + extra = list(sorted(self_deps - other_deps)) + if extra: + self.newline_cb() + highlight(f"-> [{' '.join(extra)}]") + + class NewlineWithDepthIndent: + def __init__(self): + self.depth = 0 + + def __call__(self): + print() + _write(" " * self.depth) + + nl_cb = NewlineWithDepthIndent() + + def decompose(spec): + return [ + VersionComparator(spec), + CompilerComparator(spec), + VariantsComparator(spec), + DepsComparator(spec, nl_cb) + ] + + for depth, dep_spec in traverse.traverse_tree([spec_a], deptype=("link", "run"), depth_first=True): + indent = " " * depth + nl_cb.depth = depth + node = dep_spec.spec + _write(indent) + if node.name in spec_b: + _write(node.name) + + comparators = decompose(node) + for c in comparators: + c.compare(spec_b[node.name]) + else: + highlight(node.name) + print() # New line + +def main(): + env = ev.active_environment() + + specs = [] + for spec in spack.cmd.parse_specs(sys.argv[1:]): + # If the spec has a hash, check it before disambiguating + spec.replace_hash() + if spec.concrete: + specs.append(spec) + else: + specs.append(spack.cmd.disambiguate_spec(spec, env)) + + if len(specs) != 2: + raise Exception("Need two specs") + + diff_specs(specs[0], specs[1]) + +if __name__ == "__main__": + main() \ No newline at end of file