Repository: conan-io/conan
Pull request #17985 (2025-03-18 11:33:27)

[MesonToolchain] Added subsystem field
Changelog: Feature: Added `subsystem` field in MesonToolchain if cross-compiling between Apple OSs.
Changelog: Feature: Added new kwarg `build_context`to `is_apple_os` helper function.
Docs: omit
Closes: https://github.com/conan-io/conan/issues/17873


**Note**: This field is new since Meson >= 1.2.0, but I've checked with Meson == 0.57.0 and it does nothing. It's not a breaking change 😁 



Patch:
diff --git a/conan/tools/apple/apple.py b/conan/tools/apple/apple.py
index 52a021817..4b0271891 100644
--- a/conan/tools/apple/apple.py
+++ b/conan/tools/apple/apple.py
@@ -6,10 +6,10 @@ from conan.tools.build import cmd_args_to_string
 from conan.errors import ConanException
 
 
-def is_apple_os(conanfile):
+def is_apple_os(conanfile, build_context=False):
     """returns True if OS is Apple one (Macos, iOS, watchOS, tvOS or visionOS)"""
-    os_ = conanfile.settings.get_safe("os")
-    return str(os_) in ['Macos', 'iOS', 'watchOS', 'tvOS', 'visionOS']
+    settings = conanfile.settings_build if build_context else conanfile.settings
+    return str(settings.get_safe("os")) in ['Macos', 'iOS', 'watchOS', 'tvOS', 'visionOS']
 
 
 def _to_apple_arch(arch, default=None):
diff --git a/conan/tools/meson/helpers.py b/conan/tools/meson/helpers.py
index 5f275849e..d97ab985c 100644
--- a/conan/tools/meson/helpers.py
+++ b/conan/tools/meson/helpers.py
@@ -68,6 +68,17 @@ _cppstd_map = {
 }
 
 
+def get_apple_subsystem(apple_sdk):
+    return {
+        "iphoneos": "ios",
+        "iphonesimulator": "ios-simulator",
+        "appletvos": "tvos",
+        "appletvsimulator": "tvos-simulator",
+        "watchos": "watchos",
+        "watchsimulator": "watchos-simulator",
+    }.get(apple_sdk, "macos")
+
+
 def to_meson_machine(machine_os, machine_arch):
     """Gets the OS system info as the Meson machine context.
 
diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py
index 62b9213dd..88bbc9b73 100644
--- a/conan/tools/meson/toolchain.py
+++ b/conan/tools/meson/toolchain.py
@@ -12,6 +12,7 @@ from conan.tools.build.cross_building import cross_building
 from conan.tools.build.flags import libcxx_flags, architecture_flag
 from conan.tools.env import VirtualBuildEnv
 from conan.tools.meson.helpers import *
+from conan.tools.meson.helpers import get_apple_subsystem
 from conan.tools.microsoft import VCVars, msvc_runtime_flag
 from conans.util.files import save
 
@@ -137,6 +138,9 @@ class MesonToolchain:
     {% for context, values in cross_build.items() %}
     [{{context}}_machine]
     system = '{{values["system"]}}'
+    {% if values.get("subsystem") %}
+    subsystem = '{{values["subsystem"]}}'
+    {% endif %}
     cpu_family = '{{values["cpu_family"]}}'
     cpu = '{{values["cpu"]}}'
     endian = '{{values["endian"]}}'
@@ -242,6 +246,15 @@ class MesonToolchain:
             arch_build = conanfile.settings_build.get_safe('arch')
             self.cross_build["build"] = to_meson_machine(os_build, arch_build)
             self.cross_build["host"] = to_meson_machine(os_host, arch_host)
+            # Check subsystem if it's Apple cross-building only. It requires Meson >= 1.2.0,
+            # but it does not break lower versions.
+            # See https://mesonbuild.com/Reference-tables.html#subsystem-names-since-120
+            # Issue: https://github.com/conan-io/conan/issues/17873
+            if self._is_apple_system and is_apple_os(conanfile, build_context=True):
+                sdk_build = conanfile.settings_build.get_safe("os.sdk")
+                sdk_host = conanfile.settings.get_safe("os.sdk")
+                self.cross_build["host"]["subsystem"] = get_apple_subsystem(sdk_host)
+                self.cross_build["build"]["subsystem"] = get_apple_subsystem(sdk_build)
             self.properties["needs_exe_wrapper"] = True
             if hasattr(conanfile, 'settings_target') and conanfile.settings_target:
                 settings_target = conanfile.settings_target


Test patch:
diff --git a/test/integration/toolchains/meson/test_mesontoolchain.py b/test/integration/toolchains/meson/test_mesontoolchain.py
index e5cb72953..06df19bd6 100644
--- a/test/integration/toolchains/meson/test_mesontoolchain.py
+++ b/test/integration/toolchains/meson/test_mesontoolchain.py
@@ -68,6 +68,71 @@ def test_apple_meson_keep_user_custom_flags():
     assert "cpp_link_args = ['-isysroot', '/other/sdk/path', '-arch', 'myarch', '-otherminversion=10.7', '-stdlib=libc++']" in content
 
 
+def test_apple_meson_cross_building_subsystem():
+    """
+    Issue related: https://github.com/conan-io/conan/issues/17873
+    """
+    default = textwrap.dedent("""
+    [settings]
+    os=Macos
+    arch=x86_64
+    compiler=apple-clang
+    compiler.version=12.0
+    compiler.libcxx=libc++
+    build_type=Release
+    """)
+    cross = textwrap.dedent("""
+    [settings]
+    os = iOS
+    os.version = 10.0
+    os.sdk = iphoneos
+    arch = armv8
+    compiler = apple-clang
+    compiler.version = 12.0
+    compiler.libcxx = libc++
+
+    [conf]
+    tools.apple:sdk_path=/my/sdk/path
+    """)
+    t = TestClient()
+    t.save({"conanfile.py": GenConanfile(name="app", version="1.0")
+                            .with_settings("os", "arch", "compiler", "build_type")
+                            .with_generator("MesonToolchain"),
+            "build": default,
+            "host": cross})
+    t.run("install . -pr:h host -pr:b build")
+    content = t.load(MesonToolchain.cross_filename)
+    machines_settings = textwrap.dedent("""\
+    [build_machine]
+    system = 'darwin'
+    subsystem = 'macos'
+    cpu_family = 'x86_64'
+    cpu = 'x86_64'
+    endian = 'little'
+    [host_machine]
+    system = 'darwin'
+    subsystem = 'ios'
+    cpu_family = 'aarch64'
+    cpu = 'armv8'
+    endian = 'little'""")
+    assert machines_settings in content
+    # Let's check that it does not appear if cross-compiling to other non-Apple-OS
+    cross = textwrap.dedent("""
+    [settings]
+    arch=armv8
+    build_type=Release
+    compiler=gcc
+    compiler.cppstd=gnu17
+    compiler.libcxx=libstdc++11
+    compiler.version=13
+    os=Linux
+    """)
+    t.save({"host": cross})
+    t.run("install . -pr:h host -pr:b build")
+    content = t.load(MesonToolchain.cross_filename)
+    assert "subsystem =" not in content
+
+
 def test_extra_flags_via_conf():
     profile = textwrap.dedent("""
         [settings]
diff --git a/test/unittests/tools/meson/test_meson.py b/test/unittests/tools/meson/test_meson.py
index 4f80ea8af..6d97d0706 100644
--- a/test/unittests/tools/meson/test_meson.py
+++ b/test/unittests/tools/meson/test_meson.py
@@ -1,8 +1,11 @@
 import textwrap
 
+import pytest
+
 from conan.tools.meson import Meson
 from conan.internal.model.conf import ConfDefinition
 from conan.test.utils.mocks import ConanFileMock, MockSettings
+from conan.tools.meson.helpers import get_apple_subsystem
 
 
 def test_meson_build():
@@ -25,3 +28,16 @@ def test_meson_build():
     meson.build()
 
     assert '-j10' in str(conanfile.command)
+
+
+@pytest.mark.parametrize("apple_sdk, subsystem", [
+    ("iphoneos", "ios"),
+    ("iphonesimulator", "ios-simulator"),
+    ("appletvos", "tvos"),
+    ("appletvsimulator", "tvos-simulator"),
+    ("watchos", "watchos"),
+    ("watchsimulator", "watchos-simulator"),
+    (None, "macos")
+])
+def test_meson_subsystem_helper(apple_sdk, subsystem):
+    assert get_apple_subsystem(apple_sdk) == subsystem