Repository: mesonbuild/meson

Issue:
ldconfig during install causes exception during cross build
**Describe the bug**
Since https://github.com/mesonbuild/meson/commit/2c3a68bc30cf6e08c2e3b77b64718ef3a0e5d78c Meson attempts to update the ld.so cache during `meson install`. In https://github.com/mesonbuild/meson/commit/6b7d43ebe0d77ac85fa2c01e0e358efc64418126 errors from ldconfig are ignored but if the ldconfig binary is not compatible with the host during a cross build then Popen throws an exception that is not caught:

`OSError: [Errno 8] Exec format error: 'ldconfig'`

The issue *can* be avoided by configuring binfmt on the host system but that wasn't necessary prior to this change (https://github.com/mesonbuild/meson/commit/2c3a68bc30cf6e08c2e3b77b64718ef3a0e5d78c).

The issue is also being tracked in the NixOS project: https://github.com/NixOS/nixpkgs/pull/144779

I'm not a meson expert or even a NixOS expert so creating a simple test project was a bit rough. Let me know if you need more information. Let me know if you need more information:

**To Reproduce**

On a Linux machine during an cross build.
Also the machine's binmft is either not configured for the target system architecture or is disabled via:
`echo 0 | sudo tee /proc/sys/fs/binfmt_misc/status`.

Then the error will occur during the install action:
`meson install --no-rebuild`

src: nixos.c
```
#include <stdio.h>

int main(int argc, char **argv) {
    printf("Hello World");
}
```

meson.build
```
project('nixos', 'c')

cc = meson.get_compiler('c')
dirs = []
executable('nixos', 'nixos.c')
```

cross-file:
```
[properties]
needs_exe_wrapper = true

[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'aarch64'
endian = 'little'

[binaries]
llvm-config = 'llvm-config-native'
```
Meson/NixOS Log:

```
meson flags: --buildtype=plain         --libdir=/nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/lib --libexecdir=/nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/libexec         --bindir=/nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/bin --sbindir=/nix/store/nqmnb64pxxzqyac4xdnsdl55  caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/sbin         --includedir=/nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/include         --mandir=/nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/share/man --infodir=/nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/share/info         --l  ocaledir=/nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/share/locale         -Dauto_features=enabled         -Dwrap_mode=nodownload         --prefix=/nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01 --cross-file=/nix/store/0d6bflf42rgrrxhp10visp67scqncd4g-cross-file.conf
  The Meson build system
  Version: 0.60.2
  Source dir: /build/crossmeson
  Build dir: /build/crossmeson/build
  Build type: cross build
  Project name: nixos
  Project version: undefined
  C compiler for the host machine: aarch64-unknown-linux-gnu-gcc (gcc 9.4.0 "aarch64-unknown-linux-gnu-gcc (GCC) 9.4.0")
  C linker for the host machine: aarch64-unknown-linux-gnu-gcc ld.bfd 2.35.2
  Compiler for language c for the build machine not found.
  Build machine cpu family: x86_64
  Build machine cpu: x86_64
  Host machine cpu family: aarch64
  Host machine cpu: aarch64
  Target machine cpu family: aarch64
  Target machine cpu: aarch64
  Build targets in project: 1

  nixos undefined

    User defined options
      Cross files  : /nix/store/0d6bflf42rgrrxhp10visp67scqncd4g-cross-file.conf
      auto_features: enabled
      bindir       : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/bin
      buildtype    : plain
      includedir   : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/include
      infodir      : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/share/info
      libdir       : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/lib
      libexecdir   : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/libexec
      localedir    : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/share/locale
      mandir       : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/share/man
      prefix       : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01
      sbindir      : /nix/store/nqmnb64pxxzqyac4xdnsdl55caf462h9-nixos-aarch64-unknown-linux-gnu-0.01/sbin
      wrap_mode    : nodownload

  Found ninja-1.10.2 at /nix/store/dw1apaarjckv3myghjmw7q2y1ivpv3xi-ninja-1.10.2/bin/ninja
  meson: enabled parallel building
  @nix { "action": "setPhase", "phase": "buildPhase" }
  building
  build flags: -j16 -l16
  ^M[0/2] Compiling C object nixos.p/nixos.c.o^M[1/2] Compiling C object nixos.p/nixos.c.o^M[1/2] Linking target nixos^M[2/2] Linking target nixos
  @nix { "action": "setPhase", "phase": "installPhase" }
  installing
  install flags: install
  ^M[0/1] Installing files.
  Traceback (most recent call last):
    File "/nix/store/7wll645a5mii618r6yknzm43x39ccvhv-meson-0.60.2/lib/python3.9/site-packages/mesonbuild/mesonmain.py", line 138, in run
      return options.run_func(options)
    File "/nix/store/7wll645a5mii618r6yknzm43x39ccvhv-meson-0.60.2/lib/python3.9/site-packages/mesonbuild/minstall.py", line 771, in run
      installer.do_install(datafilename)
    File "/nix/store/7wll645a5mii618r6yknzm43x39ccvhv-meson-0.60.2/lib/python3.9/site-packages/mesonbuild/minstall.py", line 554, in do_install
      self.apply_ldconfig(dm, destdir)
    File "/nix/store/7wll645a5mii618r6yknzm43x39ccvhv-meson-0.60.2/lib/python3.9/site-packages/mesonbuild/minstall.py", line 377, in apply_ldconfig
      apply_ldconfig(dm)
    File "/nix/store/7wll645a5mii618r6yknzm43x39ccvhv-meson-0.60.2/lib/python3.9/site-packages/mesonbuild/minstall.py", line 259, in apply_ldconfig
      proc, out, err = Popen_safe(['ldconfig', '-v'])
    File "/nix/store/7wll645a5mii618r6yknzm43x39ccvhv-meson-0.60.2/lib/python3.9/site-packages/mesonbuild/mesonlib/universal.py", line 1393, in Popen_safe
      p = subprocess.Popen(args, universal_newlines=True, close_fds=False,
    File "/nix/store/jqcpjgqzcci7284r7bp3xjy47w1qpj8y-python3-3.9.9/lib/python3.9/subprocess.py", line 951, in __init__
      self._execute_child(args, executable, preexec_fn, close_fds,
    File "/nix/store/jqcpjgqzcci7284r7bp3xjy47w1qpj8y-python3-3.9.9/lib/python3.9/subprocess.py", line 1821, in _execute_child
      raise child_exception_type(errno_num, err_msg, err_filename)
  OSError: [Errno 8] Exec format error: 'ldconfig'
```

**Expected behavior**
The project should install without error. Meson should probably skip running ldconfig on cross builds.

**System Parameters**
* Cross build: building aarch64 on x86_64
* OS: NixOS 21.11
* Python 3.9.6
* Meson 0.60.2
* Ninja 1.10.2


Discussion:

Resolving patch:
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -109,7 +109,8 @@ def __init__(self, build_dir: str, trees: T.List[str]):
 class InstallData:
     def __init__(self, source_dir: str, build_dir: str, prefix: str, libdir: str,
                  strip_bin: T.List[str], install_umask: T.Union[str, int],
-                 mesonintrospect: T.List[str], version: str):
+                 mesonintrospect: T.List[str], version: str,
+                 is_cross_build: bool):
         # TODO: in python 3.8 or with typing_Extensions install_umask could be:
         # `T.Union[T.Literal['preserve'], int]`, which would be more accurate.
         self.source_dir = source_dir
@@ -128,6 +129,7 @@ def __init__(self, source_dir: str, build_dir: str, prefix: str, libdir: str,
         self.install_subdirs: T.List[SubdirInstallData] = []
         self.mesonintrospect = mesonintrospect
         self.version = version
+        self.is_cross_build = is_cross_build
 
 class TargetInstallData:
 
@@ -1510,7 +1512,8 @@ def create_install_data(self) -> InstallData:
                         strip_bin,
                         umask,
                         self.environment.get_build_command() + ['introspect'],
-                        self.environment.coredata.version)
+                        self.environment.coredata.version,
+                        self.environment.is_cross_build())
         self.generate_depmf_install(d)
         self.generate_target_install(d)
         self.generate_header_install(d)
diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py
--- a/mesonbuild/minstall.py
+++ b/mesonbuild/minstall.py
@@ -383,9 +383,10 @@ def restore_selinux_contexts(self, destdir: str) -> None:
         if not self.dry_run and not destdir:
             restore_selinux_contexts()
 
-    def apply_ldconfig(self, dm: DirMaker, destdir: str, libdir: str) -> None:
-        if not self.dry_run and not destdir:
-            apply_ldconfig(dm, libdir)
+    def apply_ldconfig(self, dm: DirMaker, destdir: str, is_cross_build: bool, libdir: str) -> None:
+        if any([self.dry_run, destdir, is_cross_build]):
+            return
+        apply_ldconfig(dm, libdir)
 
     def Popen_safe(self, *args: T.Any, **kwargs: T.Any) -> T.Tuple[int, str, str]:
         if not self.dry_run:
@@ -589,7 +590,7 @@ def do_install(self, datafilename: str) -> None:
                 self.install_data(d, dm, destdir, fullprefix)
                 self.install_symlinks(d, dm, destdir, fullprefix)
                 self.restore_selinux_contexts(destdir)
-                self.apply_ldconfig(dm, destdir, libdir)
+                self.apply_ldconfig(dm, destdir, d.is_cross_build, libdir)
                 self.run_install_script(d, destdir, fullprefix)
                 if not self.did_install_something:
                     self.log('Nothing to install.')


Test patch: