Repository: spack/spack

Issue:
buildcache install failure when one of the dependency is changed
I have created buildcache for boost package and has following files in buildcache:

```
$ ll /gpfs/xxx/deployment/mirror/binaries/build_cache/linux-rhel7-x86_64/intel-18.0.3/boost-1.68.0/linux-rhel7-x86_64-intel-18.0.3-boost-1.68.0-oe6l6yzbge7wywbn2yvpqk6hftv3nztz.spack
$ ll /gpfs/xxx/deployment/mirror/binaries/build_cache/linux-rhel7-x86_64-intel-18.0.3-boost-1.68.0-oe6l6yzbge7wywbn2yvpqk6hftv3nztz.spec.yaml

# bzip2 node in this spec.yaml has following hash:

- bzip2:
    arch: {platform: linux, platform_os: rhel7, target: x86_64}
    compiler: {name: intel, version: 18.0.3}
    external: {module: null, path: /gpfs/xxx/deployment/install/serial-libraries/1970-01-01/linux-rhel7-x86_64/gcc-6.4.0/bzip2-1.0.6-lcfurlokv3}
    hash: 74e2ialv5akdiz3yr2lyb75ovntxggab
```

I have my own packages.yaml and now want to install following boost spec (note that the bzip2 node has different hash than the one in buildcache): 

```
$ spack spec -I -l boost@1.68.0%intel@18.0.3
Input spec
--------------------------------
 -   boost@1.68.0%intel@18.0.3

Concretized
--------------------------------
 -   7aya6ug  boost@1.68.0%intel@18.0.3+atomic+chrono~clanglibcpp cxxstd=default +date_time~debug+exception+filesystem+graph~icu+iostreams+locale+log+math~mpi+multithreaded~numpy patches=2ab6c72d03dec6a4ae20220a9dfd5c8c572c5294252155b85c6874d97c323199 ~pic+program_options~python+random+regex+serialization+shared+signals~singlethreaded+system~taggedlayout+test+thread+timer~versionedlayout+wave arch=linux-rhel7-x86_64
[+]  kqccwex      ^bzip2@1.0.6%intel@18.0.3+shared arch=linux-rhel7-x86_64
[+]  3a6c7hf      ^zlib@1.2.11%intel@18.0.3+optimize+pic+shared arch=linux-rhel7-x86_64
```

As there is no matching spec in the buildcache, I expect installation from source. But, I see following:

```
$ spack install boost@1.68.0%intel@18.0.3
....
==> Searching for binary cache of boost
==> Finding buildcaches in /gpfs/xxx/deployment/mirror/binaries/build_cache
==> Finding buildcaches in /gpfs/xxx/deployment/mirror/sources/build_cache
==> Installing boost from binary cache
==> Fetching file:///gpfs/xxx/deployment/mirror/binaries/build_cache/linux-rhel7-x86_64/intel-18.0.3/boost-1.68.0/linux-rhel7-x86_64-intel-18.0.3-boost-1.68.0-7aya6uglmwxxfrrjcf7xqntff7wxubu4.spack
curl: (37) Couldn't open file /gpfs/xxx/deployment/mirror/binaries/build_cache/linux-rhel7-x86_64/intel-18.0.3/boost-1.68.0/linux-rhel7-x86_64-intel-18.0.3-boost-1.68.0-7aya6uglmwxxfrrjcf7xqntff7wxubu4.spack
==> Fetching from file:///gpfs/xxx/deployment/mirror/binaries/build_cache/linux-rhel7-x86_64/intel-18.0.3/boost-1.68.0/linux-rhel7-x86_64-intel-18.0.3-boost-1.68.0-7aya6uglmwxxfrrjcf7xqntff7wxubu4.spack failed.
==> Fetching file:///gpfs/xxx/deployment/mirror/sources/build_cache/linux-rhel7-x86_64/intel-18.0.3/boost-1.68.0/linux-rhel7-x86_64-intel-18.0.3-boost-1.68.0-7aya6uglmwxxfrrjcf7xqntff7wxubu4.spack
curl: (37) Couldn't open file /gpfs/xxx/deployment/mirror/sources/build_cache/linux-rhel7-x86_64/intel-18.0.3/boost-1.68.0/linux-rhel7-x86_64-intel-18.0.3-boost-1.68.0-7aya6uglmwxxfrrjcf7xqntff7wxubu4.spack
==> Fetching from file:///gpfs/xxx/deployment/mirror/sources/build_cache/linux-rhel7-x86_64/intel-18.0.3/boost-1.68.0/linux-rhel7-x86_64-intel-18.0.3-boost-1.68.0-7aya6uglmwxxfrrjcf7xqntff7wxubu4.spack failed.
==> Error: 'NoneType' object has no attribute 'rfind'
```

I don't expect spack to continue with binary cache installation because there is *no* corresponding buildcache for the spec. I think this issue is coming from [comping non-concretised specs](https://github.com/spack/spack/blob/develop/lib/spack/spack/package.py#L1300,L1305) (without hash).

If one of the node is changed (e.g. if I marked external in packages.yaml), we will get different hash and hence install failure.


Discussion:

Resolving patch:
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -1358,8 +1358,13 @@ def try_install_from_binary_cache(self, explicit):
         binary_spec._mark_concrete()
         if binary_spec not in specs:
             return False
-        tty.msg('Installing %s from binary cache' % self.name)
         tarball = binary_distribution.download_tarball(binary_spec)
+        # see #10063 : install from source if tarball doesn't exist
+        if tarball is None:
+            tty.msg('%s exist in binary cache but with different hash' %
+                    self.name)
+            return False
+        tty.msg('Installing %s from binary cache' % self.name)
         binary_distribution.extract_tarball(
             binary_spec, tarball, allow_root=False,
             unsigned=False, force=False)


Test patch: