Repository: conan-io/conan
Pull request #16389 (2024-06-03 11:39:08)

[MesonToolchain] Added extra_xxxxx flags
Changelog: Feature: Added `extra_xxxx` flags to MesonToolchain as done in other toolchains like AutotoolsToolchain, CMakeToolchain, etc.
Docs: https://github.com/conan-io/docs/pull/XXXX
(docs for this should be autogenerated but I am keeping this just in case)
Related to: https://github.com/conan-io/conan/issues/16169



Patch:
diff --git a/conan/internal/api/detect_api.py b/conan/internal/api/detect_api.py
index e90894b29..aebad6e72 100644
--- a/conan/internal/api/detect_api.py
+++ b/conan/internal/api/detect_api.py
@@ -581,3 +581,12 @@ def default_compiler_version(compiler, version):
     elif compiler == "intel-cc":
         return major
     return version
+
+
+def detect_sdk_version(sdk):
+    if platform.system() != "Darwin":
+        return
+    cmd = f'xcrun -sdk {sdk} --show-sdk-version'
+    result = check_output_runner(cmd)
+    result = result.strip()
+    return result
diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py
index d00f29145..1a47b382f 100644
--- a/conan/tools/meson/toolchain.py
+++ b/conan/tools/meson/toolchain.py
@@ -194,6 +194,17 @@ class MesonToolchain(object):
             if vscrt:
                 self._b_vscrt = str(vscrt).lower()
 
+        # Extra flags
+        #: List of extra CXX flags. Added to cpp_args
+        self.extra_cxxflags = []
+        #: List of extra C flags. Added to c_args
+        self.extra_cflags = []
+        #: List of extra linker flags. Added to c_link_args and cpp_link_args
+        self.extra_ldflags = []
+        #: List of extra preprocessor definitions. Added to c_args and cpp_args with the
+        #: format -DFLAG1
+        self.extra_defines = []
+
         #: Dict-like object that defines Meson``properties`` with ``key=value`` format
         self.properties = {}
         #: Dict-like object that defines Meson ``project options`` with ``key=value`` format
@@ -408,13 +419,14 @@ class MesonToolchain(object):
         exelinkflags = self._conanfile_conf.get("tools.build:exelinkflags", default=[], check_type=list)
         linker_scripts = self._conanfile_conf.get("tools.build:linker_scripts", default=[], check_type=list)
         linker_script_flags = ['-T"' + linker_script + '"' for linker_script in linker_scripts]
-        defines = [f"-D{d}" for d in self._conanfile.conf.get("tools.build:defines", default=[], check_type=list)]
+        defines = self._conanfile.conf.get("tools.build:defines", default=[], check_type=list)
         sys_root = [f"--sysroot={self._sys_root}"] if self._sys_root else [""]
         return {
-            "cxxflags": cxxflags + sys_root,
-            "cflags": cflags + sys_root,
-            "ldflags": sharedlinkflags + exelinkflags + linker_script_flags + sys_root,
-            "defines": defines
+            "cxxflags": cxxflags + sys_root + self.extra_cxxflags,
+            "cflags": cflags + sys_root + self.extra_cflags,
+            "ldflags": sharedlinkflags + exelinkflags + linker_script_flags
+                       + sys_root + self.extra_ldflags,
+            "defines": [f"-D{d}" for d in (defines + self.extra_defines)]
         }
 
     @staticmethod
diff --git a/conans/client/graph/graph_binaries.py b/conans/client/graph/graph_binaries.py
index c343ccd2b..db8a6bd13 100644
--- a/conans/client/graph/graph_binaries.py
+++ b/conans/client/graph/graph_binaries.py
@@ -1,6 +1,7 @@
 import json
 import os
 from collections import OrderedDict
+from multiprocessing.pool import ThreadPool
 
 from conan.api.output import ConanOutput
 from conan.internal.cache.home_paths import HomePaths
@@ -143,8 +144,8 @@ class GraphBinariesAnalyzer(object):
         conanfile.output.info(f"Checking {len(compatibles)} compatible configurations")
         for package_id, compatible_package in compatibles.items():
             if should_update_reference(node.ref, update):
-                conanfile.output.info(f"'{package_id}': "
-                                      f"{conanfile.info.dump_diff(compatible_package)}")
+                conanfile.output.verbose(f"'{package_id}': "
+                                         f"{conanfile.info.dump_diff(compatible_package)}")
             node._package_id = package_id  # Modifying package id under the hood, FIXME
             node.binary = None  # Invalidate it
             self._process_compatible_node(node, remotes, update)  # TODO: what if BINARY_BUILD
@@ -154,8 +155,8 @@ class GraphBinariesAnalyzer(object):
         if not should_update_reference(conanfile.ref, update):
             conanfile.output.info(f"Compatible configurations not found in cache, checking servers")
             for package_id, compatible_package in compatibles.items():
-                conanfile.output.info(f"'{package_id}': "
-                                      f"{conanfile.info.dump_diff(compatible_package)}")
+                conanfile.output.verbose(f"'{package_id}': "
+                                         f"{conanfile.info.dump_diff(compatible_package)}")
                 node._package_id = package_id  # Modifying package id under the hood, FIXME
                 node.binary = None  # Invalidate it
                 self._evaluate_download(node, remotes, update)
@@ -383,21 +384,24 @@ class GraphBinariesAnalyzer(object):
 
         levels = deps_graph.by_levels()
         config_version = self._config_version()
+        # This overcounts when there are multiple identical tool requires
+        max_level_length = max(len(level) for level in levels)
+        evaluate_pool = ThreadPool(min(16, max_level_length))
         for level in levels[:-1]:  # all levels but the last one, which is the single consumer
             for node in level:
                 self._evaluate_package_id(node, config_version)
-            # group by pref to paralelize, so evaluation is done only 1 per pref
+            # group by pref to parallelize, so evaluation is done only 1 per pref
             nodes = {}
             for node in level:
                 nodes.setdefault(node.pref, []).append(node)
-            # PARALLEL, this is the slow part that can query servers for packages, and compatibility
-            for pref, pref_nodes in nodes.items():
-                _evaluate_single(pref_nodes[0])
-            # END OF PARALLEL
+            # Evaluate the first node of each pref
+            evaluate_pool.map(_evaluate_single, [pref_nodes[0] for pref_nodes in nodes.values()])
+
             # Evaluate the possible nodes with repeated "prefs" that haven't been evaluated
             for pref, pref_nodes in nodes.items():
                 for n in pref_nodes[1:]:
                     _evaluate_single(n)
+        evaluate_pool.close()
 
         # Last level is always necessarily a consumer or a virtual
         assert len(levels[-1]) == 1


Test patch:
diff --git a/test/functional/test_profile_detect_api.py b/test/functional/test_profile_detect_api.py
index 24f6b3f8d..1bac560e8 100644
--- a/test/functional/test_profile_detect_api.py
+++ b/test/functional/test_profile_detect_api.py
@@ -76,3 +76,17 @@ class TestProfileDetectAPI:
             user.confvar:libc_version={libc_version}
             """)
         assert expected in client.out
+
+    @pytest.mark.skipif(platform.system() != "Darwin", reason="Only OSX")
+    def test_profile_detect_darwin_sdk(self):
+        client = TestClient()
+        tpl1 = textwrap.dedent("""\
+            [settings]
+            os = "Macos"
+            os.sdk_version = {{ detect_api.detect_sdk_version(sdk="macosx")  }}
+            """)
+
+        client.save({"profile1": tpl1})
+        client.run("profile show -pr=profile1")
+        sdk_version = detect_api.detect_sdk_version(sdk="macosx")
+        assert f"os.sdk_version={sdk_version}" in client.out
diff --git a/test/integration/toolchains/meson/test_mesontoolchain.py b/test/integration/toolchains/meson/test_mesontoolchain.py
index 602b4bb9a..588873848 100644
--- a/test/integration/toolchains/meson/test_mesontoolchain.py
+++ b/test/integration/toolchains/meson/test_mesontoolchain.py
@@ -105,6 +105,46 @@ def test_extra_flags_via_conf():
     assert "cpp_link_args = ['-flag0', '-other=val', '-flag5', '-flag6']" in content
 
 
+def test_extra_flags_via_toolchain():
+    profile = textwrap.dedent("""
+        [settings]
+        os=Windows
+        arch=x86_64
+        compiler=gcc
+        compiler.version=9
+        compiler.cppstd=17
+        compiler.libcxx=libstdc++
+        build_type=Release
+
+        [buildenv]
+        CFLAGS=-flag0 -other=val
+        CXXFLAGS=-flag0 -other=val
+        LDFLAGS=-flag0 -other=val
+   """)
+    t = TestClient()
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+    from conan.tools.meson import MesonToolchain
+    class Pkg(ConanFile):
+        settings = "os", "compiler", "arch", "build_type"
+        def generate(self):
+            tc = MesonToolchain(self)
+            tc.extra_cxxflags = ["-flag1", "-flag2"]
+            tc.extra_cflags = ["-flag3", "-flag4"]
+            tc.extra_ldflags = ["-flag5", "-flag6"]
+            tc.extra_defines = ["define1=0"]
+            tc.generate()
+    """)
+    t.save({"conanfile.py": conanfile,
+            "profile": profile})
+    t.run("install . -pr:h=profile -pr:b=profile")
+    content = t.load(MesonToolchain.native_filename)
+    assert "cpp_args = ['-flag0', '-other=val', '-flag1', '-flag2', '-Ddefine1=0', '-D_GLIBCXX_USE_CXX11_ABI=0']" in content
+    assert "c_args = ['-flag0', '-other=val', '-flag3', '-flag4', '-Ddefine1=0']" in content
+    assert "c_link_args = ['-flag0', '-other=val', '-flag5', '-flag6']" in content
+    assert "cpp_link_args = ['-flag0', '-other=val', '-flag5', '-flag6']" in content
+
+
 def test_linker_scripts_via_conf():
     profile = textwrap.dedent("""
         [settings]