Repository: spack/spack

Issue:
Installation issue: clingo-bootstrap
### Steps to reproduce the issue

```console
$ spack -d spec zlib
...
```


### Error message

<details><summary>Error message</summary>

```
==> [2022-05-10-13:16:17.515330] Installing clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt
==> [2022-05-10-13:16:17.515357] Searching for binary cache of clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt
==> [2022-05-10-13:16:18.886607] Did not find darwin-monterey-aarch64-apple-clang-13.1.6-clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt.spec.json on https://mirror.spack.io/build_cache/darwin-monterey-aarch64-apple-clang-13.1.6-clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt.spec.json
  Download failed: HTTP Error 404: Not Found
==> [2022-05-10-13:16:18.886855] Did not find darwin-monterey-aarch64-apple-clang-13.1.6-clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt.spec.json on https://mirror.spack.io/build_cache/darwin-monterey-aarch64-apple-clang-13.1.6-clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt.spec.yaml
  Download failed: HTTP Error 404: Not Found
==> [2022-05-10-13:16:18.887753] No binary for clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt found: installing from source
==> [2022-05-10-13:16:18.888053] Creating the installation directory /Users/adrien/.spack/bootstrap/store/darwin-monterey-aarch64/apple-clang-13.1.6/clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt
==> [2022-05-10-13:16:18.900169] '/usr/bin/git' '-C' '/Users/adrien/Work/Repos/spack' 'rev-parse' 'HEAD'
==> [2022-05-10-13:16:19.471446] Error: IndexError: list index out of range

/Users/adrien/Work/Repos/spack/var/spack/repos/builtin/packages/python/package.py:1081, in headers:
       1078        else:
       1079            headers = find_headers(
       1080                'pyconfig', self.prefix.include, recursive=True)
  >>   1081            config_h = headers[0]
       1082
       1083        headers.directories = [os.path.dirname(config_h)]
       1084        return headers


Traceback (most recent call last):
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/build_environment.py", line 1058, in _setup_pkg_and_run
    kwargs['env_modifications'] = setup_package(
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/build_environment.py", line 801, in setup_package
    set_wrapper_variables(pkg, env_mods)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/build_environment.py", line 454, in set_wrapper_variables
    update_compiler_args_for_dep(dspec)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/build_environment.py", line 448, in update_compiler_args_for_dep
    _prepend_all(include_dirs, query.headers.directories)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/spec.py", line 1060, in __get__
    value = f()
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/spec.py", line 1049, in <lambda>
    callbacks_chain.append(lambda: getattr(pkg, self.attribute_name))
  File "/Users/adrien/Work/Repos/spack/var/spack/repos/builtin/packages/python/package.py", line 1082, in headers
  File "/Users/adrien/Work/Repos/spack/lib/spack/llnl/util/filesystem.py", line 1446, in __getitem__
    return self.files[item]
IndexError: list index out of range
==> [2022-05-10-13:16:19.472643] Flagging clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt as failed: IndexError: list index out of range
==> [2022-05-10-13:16:19.478878] Creating stage lock spack-stage-clingo-bootstrap-spack-yiushdkdwx5vp6xlqw477xom7zulxlrt
==> [2022-05-10-13:16:19.479669] [BOOTSTRAP MODULE clingo] Unexpected error "Terminating after first install failure: IndexError: list index out of range"
==> [2022-05-10-13:16:19.484953] Reading config file /Users/adrien/Work/Repos/spack/etc/spack/defaults/config.yaml
Traceback (most recent call last):
  File "/Users/adrien/Work/Repos/spack/bin/spack", line 98, in <module>
    sys.exit(spack.main.main())
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/main.py", line 910, in main
    return _main(argv)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/main.py", line 865, in _main
    return finish_parse_and_run(parser, cmd_name, env_format_error)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/main.py", line 893, in finish_parse_and_run
    return _invoke_command(command, parser, args, unknown)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/main.py", line 550, in _invoke_command
    return_val = command(parser, args)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/cmd/spec.py", line 87, in spec
    specs = [(s, s.concretized()) for s in input_specs]
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/cmd/spec.py", line 87, in <listcomp>
    specs = [(s, s.concretized()) for s in input_specs]
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/spec.py", line 2947, in concretized
    clone.concretize(tests=tests)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/spec.py", line 2909, in concretize
    self._new_concretize(tests)
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/spec.py", line 2881, in _new_concretize
    solver = spack.solver.asp.Solver()
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/solver/asp.py", line 2128, in __init__
    self.driver = PyclingoDriver()
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/solver/asp.py", line 485, in __init__
    bootstrap_clingo()
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/solver/asp.py", line 466, in bootstrap_clingo
    spack.bootstrap.ensure_clingo_importable_or_raise()
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/bootstrap.py", line 752, in ensure_clingo_importable_or_raise
    ensure_module_importable_or_raise(
  File "/Users/adrien/Work/Repos/spack/lib/spack/spack/bootstrap.py", line 519, in ensure_module_importable_or_raise
    raise ImportError(msg)
ImportError: cannot bootstrap the "clingo" Python module from spec "clingo-bootstrap@spack+python %apple-clang target=aarch64" due to the following failures:
    'spack-install' raised InstallError: Terminating after first install failure: IndexError: list index out of range
    Please run `spack -d spec zlib` for more verbose error messages
```
</details>


### Information on your system

* **Spack:** 0.18.0.dev0 (060e88387e7fd27c6e13dc1ca222e3c6b7d7f1fe)
* **Python:** 3.8.9
* **Platform:** darwin-monterey-m1
* **Concretizer:** clingo

### Additional information

No such files to upload found.

@alalazo 

The issue relates to #28190, but I decided to install Xcode to have a trouble-free install of clingo.
I removed all installed specs in the bootstrap store. Install started from scratch again, with no external dependencies specified.

A first, non-reproducible error appeared when first trying to install perl. Re-trying passed. So not related to perl.

### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have run `spack maintainers <name-of-the-package>` and **@mentioned** any maintainers
- [X] I have uploaded the build log and environment files
- [X] I have searched the issues of this repo and believe this is not a duplicate


Discussion:
I just hit this same issue, this also relates to #30513. The "solution" in that PR was to install `file` so we could use pre-compiled binaries, but we don't have those for M1 yet. I think we need to fix external detection for Python's variants. I'll see what I can do.

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/python/package.py b/var/spack/repos/builtin/packages/python/package.py
--- a/var/spack/repos/builtin/packages/python/package.py
+++ b/var/spack/repos/builtin/packages/python/package.py
@@ -18,7 +18,7 @@
     is_nonsymlink_exe_with_shebang,
     path_contains_subdirectory,
 )
-from llnl.util.lang import match_predicate
+from llnl.util.lang import dedupe, match_predicate
 
 from spack import *
 from spack.build_environment import dso_suffix
@@ -1019,16 +1019,13 @@ def home(self):
         """
         return Prefix(self.config_vars['prefix'])
 
-    @property
-    def libs(self):
-        # Spack installs libraries into lib, except on openSUSE where it
-        # installs them into lib64. If the user is using an externally
-        # installed package, it may be in either lib or lib64, so we need
-        # to ask Python where its LIBDIR is.
+    def find_library(self, library):
+        # Spack installs libraries into lib, except on openSUSE where it installs them
+        # into lib64. If the user is using an externally installed package, it may be
+        # in either lib or lib64, so we need to ask Python where its LIBDIR is.
         libdir = self.config_vars['LIBDIR']
 
-        # In Ubuntu 16.04.6 and python 2.7.12 from the system, lib could be
-        # in LBPL
+        # In Ubuntu 16.04.6 and python 2.7.12 from the system, lib could be in LBPL
         # https://mail.python.org/pipermail/python-dev/2013-April/125733.html
         libpl = self.config_vars['LIBPL']
 
@@ -1044,50 +1041,74 @@ def libs(self):
         else:
             macos_developerdir = ''
 
+        # Windows libraries are installed directly to BINDIR
+        win_bin_dir = self.config_vars['BINDIR']
+
+        directories = [libdir, libpl, frameworkprefix, macos_developerdir, win_bin_dir]
+        for directory in directories:
+            path = os.path.join(directory, library)
+            if os.path.exists(path):
+                return LibraryList(path)
+
+    @property
+    def libs(self):
+        # The +shared variant isn't always reliable, as `spack external find`
+        # currently can't detect it. If +shared, prefer the shared libraries, but check
+        # for static if those aren't found. Vice versa for ~shared.
+
+        # The values of LDLIBRARY and LIBRARY also aren't reliable. Intel Python uses a
+        # static binary but installs shared libraries, so sysconfig reports
+        # libpythonX.Y.a but only libpythonX.Y.so exists.
+        shared_libs = [
+            self.config_vars['LDLIBRARY'],
+            'libpython{}.{}'.format(self.version.up_to(2), dso_suffix),
+        ]
+        static_libs = [
+            self.config_vars['LIBRARY'],
+            'libpython{}.a'.format(self.version.up_to(2)),
+        ]
         if '+shared' in self.spec:
-            ldlibrary = self.config_vars['LDLIBRARY']
-            win_bin_dir = self.config_vars['BINDIR']
-            if os.path.exists(os.path.join(libdir, ldlibrary)):
-                return LibraryList(os.path.join(libdir, ldlibrary))
-            elif os.path.exists(os.path.join(libpl, ldlibrary)):
-                return LibraryList(os.path.join(libpl, ldlibrary))
-            elif os.path.exists(os.path.join(frameworkprefix, ldlibrary)):
-                return LibraryList(os.path.join(frameworkprefix, ldlibrary))
-            elif macos_developerdir and \
-                    os.path.exists(os.path.join(macos_developerdir, ldlibrary)):
-                return LibraryList(os.path.join(macos_developerdir, ldlibrary))
-            elif is_windows and \
-                    os.path.exists(os.path.join(win_bin_dir, ldlibrary)):
-                return LibraryList(os.path.join(win_bin_dir, ldlibrary))
-            else:
-                msg = 'Unable to locate {0} libraries in {1}'
-                raise RuntimeError(msg.format(ldlibrary, libdir))
+            libraries = shared_libs + static_libs
         else:
-            library = self.config_vars['LIBRARY']
+            libraries = static_libs + shared_libs
+        libraries = dedupe(libraries)
 
-            if os.path.exists(os.path.join(libdir, library)):
-                return LibraryList(os.path.join(libdir, library))
-            elif os.path.exists(os.path.join(frameworkprefix, library)):
-                return LibraryList(os.path.join(frameworkprefix, library))
-            else:
-                msg = 'Unable to locate {0} libraries in {1}'
-                raise RuntimeError(msg.format(library, libdir))
+        for library in libraries:
+            lib = self.find_library(library)
+            if lib:
+                return lib
+
+        msg = 'Unable to locate {} libraries in {}'
+        libdir = self.config_vars['LIBDIR']
+        raise spack.error.NoLibrariesError(msg.format(self.name, libdir))
 
     @property
     def headers(self):
+        directory = self.config_vars['include']
         config_h = self.config_vars['config_h_filename']
 
         if os.path.exists(config_h):
             headers = HeaderList(config_h)
         else:
-            headers = find_headers(
-                'pyconfig', self.prefix.include, recursive=True)
-            config_h = headers[0]
+            headers = find_headers('pyconfig', directory)
+            if headers:
+                config_h = headers[0]
+            else:
+                msg = 'Unable to locate {} headers in {}'
+                raise spack.error.NoHeadersError(msg.format(self.name, directory))
 
         headers.directories = [os.path.dirname(config_h)]
         return headers
 
     # https://docs.python.org/3/library/sysconfig.html#installation-paths
+    # https://discuss.python.org/t/understanding-site-packages-directories/12959
+    # https://github.com/pypa/pip/blob/22.1/src/pip/_internal/locations/__init__.py
+    # https://github.com/pypa/installer/pull/103
+
+    # NOTE: XCode Python's sysconfing module was incorrectly patched, and hard-codes
+    # everything to be installed in /Library/Python. Therefore, we need to use a
+    # fallback in the following methods. For more information, see:
+    # https://github.com/pypa/pip/blob/22.1/src/pip/_internal/locations/__init__.py#L486
 
     @property
     def platlib(self):
@@ -1104,8 +1125,12 @@ def platlib(self):
         Returns:
             str: platform-specific site-packages directory
         """
-        return self.config_vars['platlib'].replace(
-            self.config_vars['platbase'] + os.sep, ''
+        prefix = self.config_vars['platbase'] + os.sep
+        path = self.config_vars['platlib']
+        if path.startswith(prefix):
+            return path.replace(prefix, '')
+        return os.path.join(
+            'lib64', 'python{}'.format(self.version.up_to(2)), 'site-packages'
         )
 
     @property
@@ -1122,8 +1147,12 @@ def purelib(self):
         Returns:
             str: platform-independent site-packages directory
         """
-        return self.config_vars['purelib'].replace(
-            self.config_vars['base'] + os.sep, ''
+        prefix = self.config_vars['base'] + os.sep
+        path = self.config_vars['purelib']
+        if path.startswith(prefix):
+            return path.replace(prefix, '')
+        return os.path.join(
+            'lib', 'python{}'.format(self.version.up_to(2)), 'site-packages'
         )
 
     @property
@@ -1142,9 +1171,11 @@ def include(self):
         Returns:
             str: platform-independent header file directory
         """
-        return self.config_vars['include'].replace(
-            self.config_vars['installed_base'] + os.sep, ''
-        )
+        prefix = self.config_vars['installed_base'] + os.sep
+        path = self.config_vars['include']
+        if path.startswith(prefix):
+            return path.replace(prefix, '')
+        return os.path.join('include', 'python{}'.format(self.version.up_to(2)))
 
     @property
     def easy_install_file(self):


Test patch: