Repository: spack/spack

Issue:
Ability to diff specs from files
### Summary

In spack v0.18.1 (and not only), there is `spack diff` however that is limited in its functionality. It would be very nice to be able to diff arbitrary specs from files

### Rationale

One could be running spack on two different machines and experiencing different behaviors. The administrator could save the spec from one machine into a file, bring it to the other machine and run `spack diff` on that file to compare it with the local one (which might be exported to a file too -- the mechanics are unimportant).

Similarly, one could more easily diff specs from separate environments on the same spack installation.

### Description of an alternative

Manually or semi-manually running various `diff` tools on the output of `spack spec` and/or on the content of the lock files for environments

### Additional information

@scheibelp this is the followup as it was discussed at today's concall 

### General information

- [X] I have run `spack --version` and reported the version of Spack
- [X] I have searched the issues of this repo and believe this is not a duplicate


Discussion:
somewhat related: https://github.com/spack/spack/pull/26469 and https://github.com/spack/spack/pull/28885
I think another useful case is if you get an error like 

```
==> Error: No valid version for...
```

inside of an environment, but you can concretize the spec fine outside of it: I want to be able to easily compare the two specs to get hints about what to tweak inside my environment.
Apparently you can `spack diff <file> <file>` for any specs retrieved from Spack, which can be from different environments or installations (extracted with `spack spec --yaml`).
Thanks. That does not seem to work as I expected:

```
spack diff spec_for_p3-8-13_g12-1-0_in_lock.yaml spec_for_p3-8-13_g12-1-0_in_nonlock.yaml
==> Error: Spec 'yaml' matches no installed packages.
```

If I understand correctly, this works only if the two yaml files are coming from the same spack installation (in two different environments) and are diff'ed in same spack installation? And not if you move them around on different spack installations or machines?

Also, is there a way to get the `spack spec --yaml` for "everything" whether or not it's in an environment? I guess not, so as a workaround, is there a simple way to create a "dummy" environment containing everything? The hard way would be to manually parse the output of `spack find` (about 1000 things in my case) and create an environment with exactly all of those, but it's tedious and error prone (I could mistype something and install something different or not include the right one)
> If I understand correctly, this works only if the two yaml files are coming from the same spack installation (in two different environments) and are diff'ed in same spack installation? And not if you move them around on different spack installations or machines?

That looks right and moreover, if you have an environment active, you may have to deactivate it (otherwise the spec queries may automatically constrain themselves to the active environment). I think we can significantly improve this: right now it appears that `cmd/diff.py` always calls `disambiguate_spec` for all specs retrieved from `parse_specs`, even if those specs are concrete (unambiguous). In other words, I think we can easily make this do what you want (with an option, although IMO it should skip disambiguation whenever the spec is concrete).

> is there a simple way to create a "dummy" environment containing everything? 

No, but hopefully given the above we can avoid the need for it

> Also, is there a way to get the spack spec --yaml for "everything" whether or not it's in an environment? 

This could probably be scripted like:

```
x=`spack find --format "{hash}"`
for y in $x; do # this is pseudo code
  spack spec --yaml "/$y" > $y.yaml
done
```

Resolving patch:
diff --git a/lib/spack/spack/cmd/diff.py b/lib/spack/spack/cmd/diff.py
--- a/lib/spack/spack/cmd/diff.py
+++ b/lib/spack/spack/cmd/diff.py
@@ -198,10 +198,12 @@ def diff(parser, args):
     if len(args.specs) != 2:
         tty.die("You must provide two specs to diff.")
 
-    specs = [
-        spack.cmd.disambiguate_spec(spec, env, first=args.load_first)
-        for spec in spack.cmd.parse_specs(args.specs)
-    ]
+    specs = []
+    for spec in spack.cmd.parse_specs(args.specs):
+        if spec.concrete:
+            specs.append(spec)
+        else:
+            specs.append(spack.cmd.disambiguate_spec(spec, env, first=args.load_first))
 
     # Calculate the comparison (c)
     color = False if args.dump_json else get_color_when()


Test patch: