Repository: spack/spack

Issue:
Error concretizing environment containing only hpctoolkit
I created a small environment containing only `hpctoolkit` and using the `x86_64` architecture for all targets in the `packages` section.  Here is the `spack.yaml` defining my environment:

```yaml
spack:
  packages:
    all:
      target: [x86_64]
  specs:
  - hpctoolkit %gcc@7.4.0 os=ubuntu18.04
  view: false
```

When I tried to concretize that environment, the command failed with the following stack trace:

```console
==> [2020-02-05-08:52:15.800194] Imported concretize from built-in commands
==> [2020-02-05-08:52:15.801124] Imported concretize from built-in commands
==> [2020-02-05-08:52:15.801615] WRITE LOCK: /data/scott/Documents/spack/ECP-2020/repro_tmp/env1/.spack-env/transaction_lock[0:0] [Acquiring]
==> [2020-02-05-08:52:15.801802] WRITE LOCK: /data/scott/Documents/spack/ECP-2020/repro_tmp/env1/.spack-env/transaction_lock[0:0] [Acquired]
==> [2020-02-05-08:52:15.806123] Reading config file /data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack.yaml
==> [2020-02-05-08:52:15.808586] Reading config file /data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/etc/spack/defaults/repos.yaml
==> [2020-02-05-08:52:15.933426] WRITE LOCK: /data/scott/Documents/spack/ECP-2020/repro_tmp/env1/.spack-env/transaction_lock[0:0] [Released]
Traceback (most recent call last):
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/bin/spack", line 64, in <module>
    sys.exit(spack.main.main())
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/main.py", line 752, in main
    return _invoke_command(command, parser, args, unknown)
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/main.py", line 486, in _invoke_command
    return_val = command(parser, args)
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/cmd/concretize.py", line 22, in concretize
    concretized_specs = env.concretize(force=args.force)
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/environment.py", line 948, in concretize
    return self._concretize_separately()
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/environment.py", line 1016, in _concretize_separately
    concrete = _concretize_from_constraints(uspec_constraints)
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/environment.py", line 1554, in _concretize_from_constraints
    return s.concretized()
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/spec.py", line 2300, in concretized
    clone.concretize()
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/spec.py", line 2150, in concretize
    user_spec_deps=user_spec_deps),
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/spec.py", line 2628, in normalize
    visited, all_spec_deps, provider_index, tests)
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/spec.py", line 2554, in _normalize_helper
    dep = self._evaluate_dependency_conditions(dep_name)
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/spec.py", line 2377, in _evaluate_dependency_conditions
    if self.satisfies(when_spec, strict=True):
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/spec.py", line 2881, in satisfies
    if not self.architecture.satisfies(other.architecture, strict):
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/spec.py", line 360, in satisfies
    return self._satisfies_target(other.target, strict=strict)
  File "/data/scott/Documents/spack/ECP-2020/repro_tmp/env1/spack/lib/spack/spack/spec.py", line 383, in _satisfies_target
    min_ok = self_target.microarchitecture >= t_min if t_min else True
AttributeError: 'NoneType' object has no attribute 'microarchitecture'
```

If I change the environment to put the architecture directly in the abstract spec string, however, it concretizes fine.  Here's the `spack.yaml` that works:

```yaml
spack:
  specs:
  - hpctoolkit %gcc@7.4.0 arch=linux-ubuntu18.04-x86_64
  view: false
```

This is happening on Ubuntu 18.04, here's some system information:

```
$ uname -a
Linux beast 4.15.0-76-generic #86-Ubuntu SMP Fri Jan 17 17:24:28 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"
```

I'm not sure whether this is an bug in spack core, or in the hpctoolkit package, or one of it's dependencies.  Please let me know if more information is needed.


Discussion:
@eugeneswalker I narrowed down this issue we encountered yesterday while trying to concretize your E4S stack, and I added it to the project we created.

Resolving patch:
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -369,6 +369,10 @@ def _satisfies_target(self, other_target, strict):
         if not need_to_check:
             return True
 
+        # self is not concrete, but other_target is there and strict=True
+        if self.target is None:
+            return False
+
         for target_range in str(other_target).split(','):
             t_min, sep, t_max = target_range.partition(':')
 


Test patch:
diff --git a/lib/spack/spack/test/architecture.py b/lib/spack/spack/test/architecture.py
--- a/lib/spack/spack/test/architecture.py
+++ b/lib/spack/spack/test/architecture.py
@@ -214,3 +214,16 @@ def test_optimization_flags_with_custom_versions(
         )
     opt_flags = target.optimization_flags(compiler)
     assert opt_flags == expected_flags
+
+
+@pytest.mark.regression('15306')
+@pytest.mark.parametrize('architecture_tuple,constraint_tuple', [
+    (('linux', 'ubuntu18.04', None), ('linux', None, 'x86_64')),
+    (('linux', 'ubuntu18.04', None), ('linux', None, 'x86_64:')),
+])
+def test_satisfy_strict_constraint_when_not_concrete(
+        architecture_tuple, constraint_tuple
+):
+    architecture = spack.spec.ArchSpec(architecture_tuple)
+    constraint = spack.spec.ArchSpec(constraint_tuple)
+    assert not architecture.satisfies(constraint, strict=True)