Repository: mesonbuild/meson

Issue:
Introspection API: missing info about `install_subdir`
`meson-info/intro-installed.json` does not contain any information about files/directories installed via `install_subdir`.

PS: `meson-logs/install-log.txt` contains such info, so it probably could be used as a reference.

Discussion:

Patch:
diff --git a/docs/markdown/Installing.md b/docs/markdown/Installing.md
index 66d090dbf..5abfdd452 100644
--- a/docs/markdown/Installing.md
+++ b/docs/markdown/Installing.md
@@ -115,3 +115,24 @@ command in the build tree:
 ```console
 $ meson install --no-rebuild --only-changed
 ```
+
+## Finer control over install locations
+
+Sometimes it is necessary to only install a subsection of output files
+or install them in different directories. This can be done by
+specifying `install_dir` as an array rather than a single string. The
+array must have as many items as there are outputs and each entry
+specifies how the corresponding output file should be installed. For
+example:
+
+```meson
+custom_target(...
+    output: ['file1', 'file2', 'file3'],
+    install_dir: ['path1', false, 'path3'],
+    ...
+)
+```
+
+In this case `file1` would be installed to `/prefix/path1/file1`,
+`file2` would not be installed at all and `file3` would be installed
+to `/prefix/path3/file3'.
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index 7c6625081..5bd810611 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -738,7 +738,7 @@ class XCodeBackend(backends.Backend):
                 ldstr = ' '.join(ldargs)
                 valid = self.buildconfmap[target_name][buildtype]
                 langargs = {}
-                for lang in self.environment.coredata.compilers:
+                for lang in self.environment.coredata.compilers[target.for_machine]:
                     if lang not in langnamemap:
                         continue
                     # Add compile args added using add_project_arguments()
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
index b700b003a..e81a8e146 100644
--- a/mesonbuild/mintro.py
+++ b/mesonbuild/mintro.py
@@ -123,6 +123,8 @@ def list_installed(installdata):
             res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path))
         for path, installpath, _ in installdata.man:
             res[path] = os.path.join(installdata.prefix, installpath)
+        for path, installpath, _, _ in installdata.install_subdirs:
+            res[path] = os.path.join(installdata.prefix, installpath)
     return res
 
 def list_targets_from_source(intr: IntrospectionInterpreter):
diff --git a/mesonbuild/scripts/dist.py b/mesonbuild/scripts/dist.py
index 47fde2379..1a62c7068 100644
--- a/mesonbuild/scripts/dist.py
+++ b/mesonbuild/scripts/dist.py
@@ -144,11 +144,15 @@ def create_dist_hg(dist_name, src_root, bld_root, dist_sub, dist_scripts):
     return (xzname, )
 
 
-def check_dist(packagename, meson_command):
+def check_dist(packagename, meson_command, privdir):
     print('Testing distribution package %s' % packagename)
-    unpackdir = tempfile.mkdtemp()
-    builddir = tempfile.mkdtemp()
-    installdir = tempfile.mkdtemp()
+    unpackdir = os.path.join(privdir, 'dist-unpack')
+    builddir = os.path.join(privdir, 'dist-build')
+    installdir = os.path.join(privdir, 'dist-install')
+    for p in (unpackdir, builddir, installdir):
+        if os.path.exists(p):
+            shutil.rmtree(p)
+        os.mkdir(p)
     ninja_bin = detect_ninja()
     try:
         tf = tarfile.open(packagename)
@@ -200,7 +204,7 @@ def run(args):
         return 1
     error_count = 0
     for name in names:
-        rc = check_dist(name, meson_command) # Check only one.
+        rc = check_dist(name, meson_command, priv_dir) # Check only one.
         if rc == 0:
             create_hash(name)
         error_count += rc


Test patch:
diff --git a/run_unittests.py b/run_unittests.py
index 602c6b73e..90e5c9d8b 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1695,6 +1695,34 @@ class AllPlatformTests(BasePlatformTests):
         self.assertPathListEqual(intro[0]['install_filename'], ['/usr/lib/libstat.a'])
         self.assertPathListEqual(intro[1]['install_filename'], ['/usr/bin/prog' + exe_suffix])
 
+    def test_install_subdir_introspection(self):
+        '''
+        Test that the Meson introspection API also contains subdir install information
+        https://github.com/mesonbuild/meson/issues/5556
+        '''
+        testdir = os.path.join(self.common_test_dir, '63 install subdir')
+        self.init(testdir)
+        intro = self.introspect('--installed')
+        expected = {
+            'sub2': 'share/sub2',
+            'subdir/sub1': 'share/sub1',
+            'subdir/sub_elided': 'share',
+            'sub1': 'share/sub1',
+            'sub/sub1': 'share/sub1',
+            'sub_elided': 'share',
+            'nested_elided/sub': 'share',
+        }
+
+        self.assertEqual(len(intro), len(expected))
+
+        # Convert expected to PurePath
+        expected_converted = {PurePath(os.path.join(testdir, key)): PurePath(os.path.join(self.prefix, val)) for key, val in expected.items()}
+        intro_converted = {PurePath(key): PurePath(val) for key, val in intro.items()}
+
+        for src, dst in expected_converted.items():
+            self.assertIn(src, intro_converted)
+            self.assertEqual(dst, intro_converted[src])
+
     def test_install_introspection_multiple_outputs(self):
         '''
         Tests that the Meson introspection API exposes multiple install filenames correctly without crashing