Repository: conan-io/conan
Pull request #16355 (2024-05-28 22:11:08)

OSX detect_api.sdk_version
Changelog: Feature: new ``detect_api.detect_sdk_version()`` method
Docs: https://github.com/conan-io/docs/pull/XXXX

Close https://github.com/conan-io/conan/issues/16258


jwillikers: I had one of our devs confirm that this is working on macOS.

Patch:
diff --git a/conan/api/subapi/new.py b/conan/api/subapi/new.py
index 573ce75d4..23eaab4fb 100644
--- a/conan/api/subapi/new.py
+++ b/conan/api/subapi/new.py
@@ -29,6 +29,7 @@ class NewAPI:
         from conan.internal.api.new.autotools_lib import autotools_lib_files
         from conan.internal.api.new.autoools_exe import autotools_exe_files
         from conan.internal.api.new.local_recipes_index import local_recipes_index_files
+        from conan.internal.api.new.qbs_lib import qbs_lib_files
         new_templates = {"basic": basic_file,
                          "cmake_lib": cmake_lib_files,
                          "cmake_exe": cmake_exe_files,
@@ -41,7 +42,8 @@ class NewAPI:
                          "autotools_lib": autotools_lib_files,
                          "autotools_exe": autotools_exe_files,
                          "alias": alias_file,
-                         "local_recipes_index": local_recipes_index_files}
+                         "local_recipes_index": local_recipes_index_files,
+                         "qbs_lib": qbs_lib_files}
         template_files = new_templates.get(template_name)
         return template_files
 
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/internal/api/new/qbs_lib.py b/conan/internal/api/new/qbs_lib.py
new file mode 100644
index 000000000..2c71e8ff5
--- /dev/null
+++ b/conan/internal/api/new/qbs_lib.py
@@ -0,0 +1,103 @@
+from conan.internal.api.new.cmake_lib import source_cpp, source_h, test_main
+
+
+conanfile_sources = '''
+import os
+
+from conan import ConanFile
+from conan.tools.qbs import Qbs
+
+
+class {{package_name}}Recipe(ConanFile):
+    name = "{{name}}"
+    version = "{{version}}"
+
+    exports_sources = "*.cpp", "*.h", "*.qbs"
+    settings = "os", "compiler", "arch"
+    options = {"shared": [True, False]}
+    default_options = {"shared": False}
+
+    def build(self):
+        qbs = Qbs(self)
+        qbs.profile = ""
+        qbs_config = {"products.{{name}}.isShared": "true" if self.options.shared else "false"}
+        qbs.add_configuration("default", qbs_config)
+        qbs.build()
+
+    def package(self):
+        qbs = Qbs(self)
+        qbs.profile = ""
+        qbs.install()
+
+    def package_info(self):
+        self.cpp_info.libs = ["{{name}}"]
+'''
+
+qbs_lib_file = '''
+    Library {
+        property bool isShared: true
+        name: "{{name}}"
+        type: isShared ? "dynamiclibrary" : "staticlibrary"
+        files: [ "{{name}}.cpp" ]
+        Group {
+            name: "headers"
+            files: [ "{{name}}.h" ]
+            qbs.install: true
+            qbs.installDir: "include"
+        }
+        Depends { name: "cpp" }
+        Depends { name: "bundle" }
+        bundle.isBundle: false
+        install: true
+        qbs.installPrefix: ""
+    }
+'''
+
+test_conanfile_v2 = """import os
+
+from conan import ConanFile
+from conan.tools.build import can_run
+from conan.tools.qbs import Qbs
+from conan.tools.build import cmd_args_to_string
+
+class {{package_name}}TestConan(ConanFile):
+    settings = "os", "compiler", "build_type", "arch"
+    generators = "PkgConfigDeps"
+
+    def requirements(self):
+        self.requires(self.tested_reference_str)
+
+    def build(self):
+        qbs = Qbs(self)
+        qbs.profile = ""
+        qbs.build()
+        qbs.install()
+
+    def test(self):
+        if can_run(self):
+            cmd = os.path.join(self.package_folder, "bin", "example")
+            self.run(cmd_args_to_string([cmd]), env="conanrun")
+"""
+
+qbs_test_file = '''
+    Application {
+        name: "example"
+        consoleApplication: true
+        files: [ "example.cpp" ]
+        Depends { name: "cpp" }
+        install: true
+        qbs.installPrefix: ""
+        // external dependency via pkg-config
+        qbsModuleProviders: ["qbspkgconfig"]
+        moduleProviders.qbspkgconfig.libDirs: path
+        Depends { name: "{{name}}" }
+    }
+'''
+
+qbs_lib_files = {"conanfile.py": conanfile_sources,
+                 "{{name}}.qbs": qbs_lib_file,
+                 "{{name}}.cpp": source_cpp,
+                 "{{name}}.h": source_h,
+                 "test_package/conanfile.py": test_conanfile_v2,
+                 "test_package/example.cpp": test_main,
+                 "test_package/example.qbs": qbs_test_file}
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index fbd719972..26cb7ba07 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -19,6 +19,7 @@ from conan.tools.microsoft.visual import msvc_version_to_toolset_version
 from conans.client.generators import relativize_path
 from conans.client.subsystems import deduce_subsystem, WINDOWS
 from conan.errors import ConanException
+from conans.model.version import Version
 from conans.util.files import load
 
 
@@ -931,6 +932,34 @@ class GenericSystemBlock(Block):
         return os_host in ('iOS', 'watchOS', 'tvOS', 'visionOS') or (
                 os_host == 'Macos' and (arch_host != arch_build or os_build != os_host))
 
+    def _get_darwin_version(self, os_name, os_version):
+        # version mapping from https://en.wikipedia.org/wiki/Darwin_(operating_system)
+        version_mapping = {
+            "Macos": {
+                "10.6": "10", "10.7": "11", "10.8": "12", "10.9": "13", "10.10": "14", "10.11": "15",
+                "10.12": "16", "10.13": "17", "10.14": "18", "10.15": "19", "11": "20", "12": "21",
+                "13": "22", "14": "23",
+            },
+            "iOS": {
+                "7": "14", "8": "14", "9": "15", "10": "16", "11": "17", "12": "18", "13": "19",
+                "14": "20", "15": "21", "16": "22", "17": "23"
+            },
+            "watchOS": {
+                "4": "17", "5": "18", "6": "19", "7": "20",
+                "8": "21", "9": "22", "10": "23"
+            },
+            "tvOS": {
+                "11": "17", "12": "18", "13": "19", "14": "20",
+                "15": "21", "16": "22", "17": "23"
+            },
+            "visionOS": {
+                "1": "23"
+            }
+        }
+        os_version = Version(os_version).major if os_name != "Macos" or (os_name == "Macos" and Version(
+            os_version) >= Version("11")) else os_version
+        return version_mapping.get(os_name, {}).get(str(os_version))
+
     def _get_cross_build(self):
         user_toolchain = self._conanfile.conf.get("tools.cmake.cmaketoolchain:user_toolchain")
 
@@ -942,6 +971,7 @@ class GenericSystemBlock(Block):
         if not user_toolchain and not is_universal_arch(self._conanfile.settings.get_safe("arch"),
                                                         self._conanfile.settings.possible_values().get("arch")):
             os_host = self._conanfile.settings.get_safe("os")
+            os_host_version = self._conanfile.settings.get_safe("os.version")
             arch_host = self._conanfile.settings.get_safe("arch")
             if arch_host == "armv8":
                 arch_host = {"Windows": "ARM64", "Macos": "arm64"}.get(os_host, "aarch64")
@@ -952,12 +982,12 @@ class GenericSystemBlock(Block):
                 if self._is_apple_cross_building():
                     # cross-build in Macos also for M1
                     system_name = {'Macos': 'Darwin'}.get(os_host, os_host)
-                    #  CMAKE_SYSTEM_VERSION for Apple sets the sdk version, not the os version
-                    _system_version = self._conanfile.settings.get_safe("os.sdk_version")
+                    #  CMAKE_SYSTEM_VERSION for Apple sets the Darwin version, not the os version
+                    _system_version = self._get_darwin_version(os_host, os_host_version)
                     _system_processor = to_apple_arch(self._conanfile)
                 elif os_host != 'Android':
                     system_name = self._get_generic_system_name()
-                    _system_version = self._conanfile.settings.get_safe("os.version")
+                    _system_version = os_host_version
                     _system_processor = arch_host
 
                 if system_name is not None and system_version is None:
@@ -1018,6 +1048,46 @@ class GenericSystemBlock(Block):
                 "winsdk_version": winsdk_version,
                 "gen_platform_sdk_version": gen_platform_sdk_version}
 
+class ExtraVariablesBlock(Block):
+    template = textwrap.dedent(r"""
+        {% if extra_variables %}
+        {% for key, value in extra_variables.items() %}
+        set({{ key }} {{ value }})
+        {% endfor %}
+        {% endif %}
+    """)
+
+    CMAKE_CACHE_TYPES = ["BOOL","FILEPATH", "PATH", "STRING", "INTERNAL"]
+
+    def get_exact_type(self, key, value):
+        if isinstance(value, str):
+            return f"\"{value}\""
+        elif isinstance(value, (int, float)):
+            return value
+        elif isinstance(value, dict):
+            var_value = self.get_exact_type(key, value.get("value"))
+            is_cache = value.get("cache")
+            if is_cache:
+                if not isinstance(is_cache, bool):
+                    raise ConanException(f'tools.cmake.cmaketoolchain:extra_variables "cache" must be a boolean (True/False)')
+                var_type = value.get("type")
+                if not var_type:
+                    raise ConanException(f'tools.cmake.cmaketoolchain:extra_variables needs "type" defined for cache variable "{key}"')
+                if var_type not in self.CMAKE_CACHE_TYPES:
+                    raise ConanException(f'tools.cmake.cmaketoolchain:extra_variables invalid type "{var_type}" for cache variable "{key}". Possible types: {", ".join(self.CMAKE_CACHE_TYPES)}')
+                # Set docstring as variable name if not defined
+                docstring = value.get("docstring") or key
+                return f"{var_value} CACHE {var_type} \"{docstring}\""
+            else:
+                return var_value
+
+    def context(self):
+        # Reading configuration from "tools.cmake.cmaketoolchain:extra_variables"
+        extra_variables = self._conanfile.conf.get("tools.cmake.cmaketoolchain:extra_variables", default={}, check_type=dict)
+        parsed_extra_variables = {}
+        for key, value in extra_variables.items():
+            parsed_extra_variables[key] = self.get_exact_type(key, value)
+        return {"extra_variables": parsed_extra_variables}
 
 class OutputDirsBlock(Block):
 
diff --git a/conan/tools/cmake/toolchain/toolchain.py b/conan/tools/cmake/toolchain/toolchain.py
index 6924a1d9f..0692751eb 100644
--- a/conan/tools/cmake/toolchain/toolchain.py
+++ b/conan/tools/cmake/toolchain/toolchain.py
@@ -9,7 +9,7 @@ from conan.internal import check_duplicated_generator
 from conan.tools.build import use_win_mingw
 from conan.tools.cmake.presets import write_cmake_presets
 from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
-from conan.tools.cmake.toolchain.blocks import ToolchainBlocks, UserToolchain, GenericSystemBlock, \
+from conan.tools.cmake.toolchain.blocks import ExtraVariablesBlock, ToolchainBlocks, UserToolchain, GenericSystemBlock, \
     AndroidSystemBlock, AppleSystemBlock, FPicBlock, ArchitectureBlock, GLibCXXBlock, VSRuntimeBlock, \
     CppStdBlock, ParallelBlock, CMakeFlagsInitBlock, TryCompileBlock, FindFiles, PkgConfigBlock, \
     SkipRPath, SharedLibBock, OutputDirsBlock, ExtraFlagsBlock, CompilersBlock, LinkerScriptsBlock, \
@@ -165,6 +165,7 @@ class CMakeToolchain(object):
                                        ("parallel", ParallelBlock),
                                        ("extra_flags", ExtraFlagsBlock),
                                        ("cmake_flags_init", CMakeFlagsInitBlock),
+                                       ("extra_variables", ExtraVariablesBlock),
                                        ("try_compile", TryCompileBlock),
                                        ("find_paths", FindFiles),
                                        ("pkg_config", PkgConfigBlock),
diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py
index 67713d769..d00f29145 100644
--- a/conan/tools/meson/toolchain.py
+++ b/conan/tools/meson/toolchain.py
@@ -245,6 +245,11 @@ class MesonToolchain(object):
             default_comp = "cl"
             default_comp_cpp = "cl"
 
+        # Read configuration for sys_root property (honoring existing conf)
+        self._sys_root = self._conanfile_conf.get("tools.build:sysroot", check_type=str)
+        if self._sys_root:
+            self.properties["sys_root"] = self._sys_root
+
         # Read configuration for compilers
         compilers_by_conf = self._conanfile_conf.get("tools.build:compiler_executables", default={},
                                                      check_type=dict)
@@ -404,10 +409,11 @@ class MesonToolchain(object):
         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)]
+        sys_root = [f"--sysroot={self._sys_root}"] if self._sys_root else [""]
         return {
-            "cxxflags": cxxflags,
-            "cflags": cflags,
-            "ldflags": sharedlinkflags + exelinkflags + linker_script_flags,
+            "cxxflags": cxxflags + sys_root,
+            "cflags": cflags + sys_root,
+            "ldflags": sharedlinkflags + exelinkflags + linker_script_flags + sys_root,
             "defines": defines
         }
 
diff --git a/conan/tools/qbs/qbs.py b/conan/tools/qbs/qbs.py
index db4bb87e0..d65a91b3f 100644
--- a/conan/tools/qbs/qbs.py
+++ b/conan/tools/qbs/qbs.py
@@ -1,6 +1,6 @@
 import os
 
-from conan.tools.build import build_jobs
+from conan.tools.build import build_jobs, cmd_args_to_string
 from conan.errors import ConanException
 
 
@@ -59,7 +59,7 @@ class Qbs(object):
             config = self._configuration[name]
             args.extend(_configuration_dict_to_commandlist(name, config))
 
-        cmd = 'qbs build %s' % (' '.join(args))
+        cmd = 'qbs build %s' % cmd_args_to_string(args)
         self._conanfile.run(cmd)
 
     def build_all(self):
@@ -79,7 +79,7 @@ class Qbs(object):
             config = self._configuration[name]
             args.extend(_configuration_dict_to_commandlist(name, config))
 
-        cmd = 'qbs build %s' % (' '.join(args))
+        cmd = 'qbs build %s' % cmd_args_to_string(args)
         self._conanfile.run(cmd)
 
     def install(self):
@@ -92,5 +92,5 @@ class Qbs(object):
         for name in self._configuration:
             args.append('config:%s' % name)
 
-        cmd = 'qbs install %s' % (' '.join(args))
+        cmd = 'qbs install %s' % cmd_args_to_string(args)
         self._conanfile.run(cmd)
diff --git a/conans/client/subsystems.py b/conans/client/subsystems.py
index daba56c06..ac7ca25c7 100644
--- a/conans/client/subsystems.py
+++ b/conans/client/subsystems.py
@@ -82,6 +82,7 @@ def _windows_bash_wrapper(conanfile, command, env, envfiles_folder):
         # - CHERE_INVOKING is necessary to keep the CWD and not change automatically to the user home
         msys2_mode_env.define("MSYSTEM", _msystem)
         msys2_mode_env.define("MSYS2_PATH_TYPE", "inherit")
+        msys2_mode_env.unset("ORIGINAL_PATH")
         # So --login do not change automatically to the user home
         msys2_mode_env.define("CHERE_INVOKING", "1")
         path = os.path.join(conanfile.generators_folder, "msys2_mode.bat")
diff --git a/conans/model/conf.py b/conans/model/conf.py
index 7809f4d9d..e11037845 100644
--- a/conans/model/conf.py
+++ b/conans/model/conf.py
@@ -73,6 +73,7 @@ BUILT_IN_CONFS = {
     "tools.cmake.cmaketoolchain:toolset_arch": "Toolset architecture to be used as part of CMAKE_GENERATOR_TOOLSET in CMakeToolchain",
     "tools.cmake.cmaketoolchain:toolset_cuda": "(Experimental) Path to a CUDA toolset to use, or version if installed at the system level",
     "tools.cmake.cmaketoolchain:presets_environment": "String to define wether to add or not the environment section to the CMake presets. Empty by default, will generate the environment section in CMakePresets. Can take values: 'disabled'.",
+    "tools.cmake.cmaketoolchain:extra_variables": "Dictionary with variables to be injected in CMakeToolchain (potential override of CMakeToolchain defined variables)",
     "tools.cmake.cmake_layout:build_folder_vars": "Settings and Options that will produce a different build folder and different CMake presets names",
     "tools.cmake.cmake_layout:build_folder": "(Experimental) Allow configuring the base folder of the build for local builds",
     "tools.cmake.cmake_layout:test_folder": "(Experimental) Allow configuring the base folder of the build for test_package",


Test patch:
diff --git a/conan/test/utils/scm.py b/conan/test/utils/scm.py
index 29b35d941..280a56314 100644
--- a/conan/test/utils/scm.py
+++ b/conan/test/utils/scm.py
@@ -2,7 +2,7 @@ import os
 
 from conan.test.utils.test_files import temp_folder
 from conans.util.files import save_files, chdir
-from conans.util.runners import check_output_runner
+from conans.util.runners import detect_runner
 
 
 def git_create_bare_repo(folder=None, reponame="repo.git"):
@@ -10,7 +10,7 @@ def git_create_bare_repo(folder=None, reponame="repo.git"):
     cwd = os.getcwd()
     try:
         os.chdir(folder)
-        check_output_runner('git init --bare {}'.format(reponame))
+        detect_runner('git init --bare {}'.format(reponame))
         return os.path.join(folder, reponame).replace("\\", "/")
     finally:
         os.chdir(cwd)
@@ -24,7 +24,8 @@ def create_local_git_repo(files=None, branch=None, submodules=None, folder=None,
 
     def _run(cmd, p):
         with chdir(p):
-            return check_output_runner("git {}".format(cmd)).strip()
+            _, out = detect_runner("git {}".format(cmd))
+            return out.strip()
 
     _run("init .", tmp)
     _run('config user.name "Your Name"', tmp)
@@ -56,9 +57,10 @@ def git_add_changes_commit(folder, msg="fix"):
     try:
         os.chdir(folder)
         # Make sure user and email exist, otherwise it can error
-        check_output_runner('git config user.name "Your Name"')
-        check_output_runner('git config user.email "you@example.com"')
-        check_output_runner('git add . && git commit -m "{}"'.format(msg))
-        return check_output_runner("git rev-parse HEAD").strip()
+        detect_runner('git config user.name "Your Name"')
+        detect_runner('git config user.email "you@example.com"')
+        detect_runner('git add . && git commit -m "{}"'.format(msg))
+        _, out = detect_runner("git rev-parse HEAD")
+        return out.strip()
     finally:
         os.chdir(cwd)
diff --git a/test/conftest.py b/test/conftest.py
index 7c668e666..bc148aabe 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -155,9 +155,13 @@ tools_locations = {
         "system": {"path": {'Windows': "C:/cygwin64/bin"}},
     },
     'bazel': {
-        "default": "system",
-        "system": {"path": {'Windows': 'C:/bazel/bin',
-                            "Darwin": '/Users/jenkins/bin'}},
+        "default": "6.3.2",
+        "6.3.2": {"path": {'Linux': '/usr/share/bazel-6.3.2/bin',
+                           'Windows': 'C:/bazel-6.3.2/bin',
+                           'Darwin': '/Users/jenkins/bazel-6.3.2/bin'}},
+        "7.1.2": {"path": {'Linux': '/usr/share/bazel-7.1.2/bin',
+                           'Windows': 'C:/bazel-7.1.2/bin',
+                           'Darwin': '/Users/jenkins/bazel-7.1.2/bin'}},
     },
     'premake': {
         "exe": "premake5",
@@ -177,6 +181,7 @@ tools_locations = {
             "path": {'Darwin': f'{homebrew_root}/share/android-ndk'}
         }
     },
+    "qbs": {"disabled": True},
     # TODO: Intel oneAPI is not installed in CI yet. Uncomment this line whenever it's done.
     # "intel_oneapi": {
     #     "default": "2021.3",
diff --git a/test/functional/command/profile_test.py b/test/functional/command/profile_test.py
index c700ca591..454df2325 100644
--- a/test/functional/command/profile_test.py
+++ b/test/functional/command/profile_test.py
@@ -1,6 +1,5 @@
 import os
 import platform
-import subprocess
 import textwrap
 import unittest
 
@@ -11,9 +10,10 @@ from conan.test.utils.mocks import RedirectedTestOutput
 from conan.test.utils.tools import TestClient, redirect_output
 from conans.util.env import environment_update
 from conans.util.files import save
-from conans.util.runners import check_output_runner
+from conans.util.runners import detect_runner
 from conan.tools.microsoft.visual import vcvars_command
 
+
 class TestProfile(unittest.TestCase):
 
     def test_list_empty(self):
@@ -100,7 +100,7 @@ class DetectCompilersTest(unittest.TestCase):
         Test if gcc in Mac OS X is using apple-clang as frontend
         """
         # See: https://github.com/conan-io/conan/issues/2231
-        output = check_output_runner("gcc --version", stderr=subprocess.STDOUT)
+        _, output = detect_runner("gcc --version")
 
         if "clang" not in output:
             # Not test scenario gcc should display clang in output
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/functional/toolchains/meson/test_meson.py b/test/functional/toolchains/meson/test_meson.py
index a24f79600..8298c17b9 100644
--- a/test/functional/toolchains/meson/test_meson.py
+++ b/test/functional/toolchains/meson/test_meson.py
@@ -6,10 +6,10 @@ import textwrap
 
 import pytest
 
-from conans.model.recipe_ref import RecipeReference
 from conan.test.assets.sources import gen_function_cpp, gen_function_h
-from test.functional.toolchains.meson._base import TestMesonBase
 from conan.test.utils.tools import TestClient
+from conans.model.recipe_ref import RecipeReference
+from test.functional.toolchains.meson._base import TestMesonBase
 
 
 class MesonToolchainTest(TestMesonBase):
diff --git a/test/functional/toolchains/qbs/test_qbs.py b/test/functional/toolchains/qbs/test_qbs.py
new file mode 100644
index 000000000..c61439f21
--- /dev/null
+++ b/test/functional/toolchains/qbs/test_qbs.py
@@ -0,0 +1,14 @@
+import pytest
+
+from conan.test.utils.tools import TestClient
+
+@pytest.mark.parametrize('shared', [
+    ('False'),
+    ('True'),
+])
+@pytest.mark.tool("qbs")
+def test_api_qbs_create_lib(shared):
+    client = TestClient()
+    client.run("new qbs_lib -d name=hello -d version=1.0")
+    client.run("create . -o:h &:shared={shared}".format(shared=shared))
+    assert "compiling hello.cpp" in client.out
\ No newline at end of file
diff --git a/test/functional/util/test_cmd_args_to_string.py b/test/functional/util/test_cmd_args_to_string.py
index a03d78c5c..ac2372e05 100644
--- a/test/functional/util/test_cmd_args_to_string.py
+++ b/test/functional/util/test_cmd_args_to_string.py
@@ -7,7 +7,7 @@ import pytest
 from conan.tools.build import cmd_args_to_string
 from conan.test.utils.tools import TestClient
 from conans.util.files import chdir
-from conans.util.runners import check_output_runner
+from conans.util.runners import detect_runner
 
 
 @pytest.fixture(scope="module")
@@ -66,7 +66,7 @@ def test_unix_cases(application_folder, _input, output):
 
     # Check calling the exe that the arguments are the same we tried to input
     with chdir(application_folder):
-        cmd_parsed_output = check_output_runner("./{}".format(output))
+        _, cmd_parsed_output = detect_runner("./{}".format(output))
         interpreted_args = cmd_parsed_output.splitlines()
         real_args = _input[1:]
         assert real_args == interpreted_args
@@ -90,7 +90,7 @@ def test_windows_cases(application_folder, _input, output):
 
     # Check calling the exe that the arguments are the same we tried to input
     with chdir(application_folder):
-        cmd_parsed_output = check_output_runner(output)
+        _, cmd_parsed_output = detect_runner(output)
         interpreted_args = cmd_parsed_output.splitlines()
         real_args = _input[1:]
         assert real_args == interpreted_args
diff --git a/test/integration/toolchains/cmake/test_cmaketoolchain.py b/test/integration/toolchains/cmake/test_cmaketoolchain.py
index 81ad493a1..c21b35ffc 100644
--- a/test/integration/toolchains/cmake/test_cmaketoolchain.py
+++ b/test/integration/toolchains/cmake/test_cmaketoolchain.py
@@ -68,7 +68,7 @@ def test_cross_build_linux_to_macos():
     macos_profile = textwrap.dedent("""
         [settings]
         os=Macos
-        os.sdk_version=13.1
+        os.version=13.1
         arch=x86_64
         compiler=apple-clang
         compiler.version=13
@@ -85,7 +85,7 @@ def test_cross_build_linux_to_macos():
     toolchain = client.load("conan_toolchain.cmake")
 
     assert "set(CMAKE_SYSTEM_NAME Darwin)" in toolchain
-    assert "set(CMAKE_SYSTEM_VERSION 13.1)" in toolchain
+    assert "set(CMAKE_SYSTEM_VERSION 22)" in toolchain
     assert "set(CMAKE_SYSTEM_PROCESSOR x86_64)" in toolchain
 
 
@@ -437,7 +437,7 @@ def test_cmaketoolchain_cmake_system_processor_cross_apple():
     client.run("install hello.py -pr:h=./profile_ios -pr:b=default -g CMakeToolchain")
     toolchain = client.load("conan_toolchain.cmake")
     assert "set(CMAKE_SYSTEM_NAME iOS)" in toolchain
-    assert "set(CMAKE_SYSTEM_VERSION 15.0)" in toolchain
+    assert "set(CMAKE_SYSTEM_VERSION 21)" in toolchain
     assert "set(CMAKE_SYSTEM_PROCESSOR arm64)" in toolchain
 
 
@@ -1501,7 +1501,6 @@ def test_toolchain_keep_absolute_paths():
     presets = json.loads(c.load(user_presets["include"][0]))
     assert os.path.isabs(presets["configurePresets"][0]["toolchainFile"])
 
-
 def test_output_dirs_gnudirs_local_default():
     # https://github.com/conan-io/conan/issues/14733
     c = TestClient()
@@ -1575,3 +1574,59 @@ def test_output_dirs_gnudirs_local_custom():
     c.run("build .")
     _assert_install(c.out)
     assert "CMAKE_INSTALL_PREFIX" not in c.out
+
+
+def test_toolchain_extra_variables():
+    windows_profile = textwrap.dedent("""
+        [settings]
+        os=Windows
+        arch=x86_64
+        [conf]
+        tools.cmake.cmaketoolchain:extra_variables={'CMAKE_GENERATOR_INSTANCE': '${GENERATOR_INSTANCE}/buildTools/', 'FOO': '42' }
+        """)
+
+    client = TestClient()
+    client.save({"conanfile.txt": "[generators]\nCMakeToolchain",
+                 "windows": windows_profile})
+
+    # Test passing extra_variables from pro ile
+    client.run("install . --profile:host=windows")
+    toolchain = client.load("conan_toolchain.cmake")
+    assert 'set(CMAKE_GENERATOR_INSTANCE "${GENERATOR_INSTANCE}/buildTools/")' in toolchain
+    assert 'set(FOO "42")' in toolchain
+
+    # Test input from command line passing dict between doble quotes
+    client.run(textwrap.dedent(r"""
+        install . -c tools.cmake.cmaketoolchain:extra_variables="{'CMAKE_GENERATOR_INSTANCE': '${GENERATOR_INSTANCE}/buildTools/', 'FOO': 42.2, 'DICT': {'value': 1}, 'CACHE_VAR': {'value': 'hello world', 'cache': True, 'type': 'BOOL', 'docstring': 'test variable'}}"
+    """)
+    )
+
+    toolchain = client.load("conan_toolchain.cmake")
+    assert 'set(CMAKE_GENERATOR_INSTANCE "${GENERATOR_INSTANCE}/buildTools/")' in toolchain
+    assert 'set(FOO 42.2)' in toolchain
+    assert 'set(DICT 1)' in toolchain
+    assert 'set(CACHE_VAR "hello world" CACHE BOOL "test variable")' in toolchain
+
+
+    client.run(textwrap.dedent("""
+        install . -c tools.cmake.cmaketoolchain:extra_variables="{'invalid': {'value': 'hello world', 'cache': 'true'}}"
+    """) , assert_error=True)
+    assert 'tools.cmake.cmaketoolchain:extra_variables "cache" must be a boolean (True/False)' in client.out
+
+    # Test invalid cache variable
+    client.run(textwrap.dedent("""
+        install . -c tools.cmake.cmaketoolchain:extra_variables="{'invalid': {'value': 'hello world', 'cache': True}}"
+    """) , assert_error=True)
+    assert 'tools.cmake.cmaketoolchain:extra_variables needs "type" defined for cache variable "invalid"' in client.out
+
+    client.run(textwrap.dedent("""
+        install . -c tools.cmake.cmaketoolchain:extra_variables="{'invalid': {'value': 'hello world', 'cache': True, 'type': 'INVALID_TYPE'}}"
+    """) , assert_error=True)
+    assert 'tools.cmake.cmaketoolchain:extra_variables invalid type "INVALID_TYPE" for cache variable "invalid". Possible types: BOOL, FILEPATH, PATH, STRING, INTERNAL' in client.out
+
+    client.run(textwrap.dedent("""
+        install . -c tools.cmake.cmaketoolchain:extra_variables="{'CACHE_VAR_DEFAULT_DOC': {'value': 'hello world', 'cache': True, 'type': 'PATH'}}"
+    """))
+    toolchain = client.load("conan_toolchain.cmake")
+    assert 'set(CACHE_VAR_DEFAULT_DOC "hello world" CACHE PATH "CACHE_VAR_DEFAULT_DOC")' in toolchain
+
diff --git a/test/integration/toolchains/meson/test_mesontoolchain.py b/test/integration/toolchains/meson/test_mesontoolchain.py
index b7a2742f7..602b4bb9a 100644
--- a/test/integration/toolchains/meson/test_mesontoolchain.py
+++ b/test/integration/toolchains/meson/test_mesontoolchain.py
@@ -1,13 +1,14 @@
 import os
 import platform
+import re
 import textwrap
 
 import pytest
 
 from conan.test.assets.genconanfile import GenConanfile
+from conan.test.utils.tools import TestClient
 from conan.tools.files import load
 from conan.tools.meson import MesonToolchain
-from conan.test.utils.tools import TestClient
 
 
 def test_apple_meson_keep_user_custom_flags():
@@ -534,3 +535,51 @@ def test_compiler_path_with_spaces():
     conan_meson_native = client.load("conan_meson_native.ini")
     assert "c = 'c compiler path with spaces'" in conan_meson_native
     assert "cpp = 'cpp compiler path with spaces'" in conan_meson_native
+
+
+def test_meson_sysroot_app():
+    """Testing when users pass tools.build:sysroot on the profile with Meson
+
+    The generated conan_meson_cross.ini needs to contain both sys_root property to fill the
+    PKG_CONFIG_PATH and the compiler flags with --sysroot.
+
+    When cross-building, Meson needs both compiler_executables in the config, otherwise it will fail
+    when running setup.
+    """
+    sysroot = "/my/new/sysroot/path"
+    client = TestClient()
+    profile = textwrap.dedent(f"""
+    [settings]
+    os = Macos
+    arch = armv8
+    compiler = apple-clang
+    compiler.version = 13.0
+    compiler.libcxx = libc++
+
+    [conf]
+    tools.build:sysroot={sysroot}
+    tools.build:verbosity=verbose
+    tools.compilation:verbosity=verbose
+    tools.apple:sdk_path=/my/sdk/path
+    """)
+    profile_build = textwrap.dedent(f"""
+    [settings]
+    os = Macos
+    arch = x86_64
+    compiler = apple-clang
+    compiler.version = 13.0
+    compiler.libcxx = libc++
+    """)
+    client.save({"conanfile.py": GenConanfile(name="hello", version="0.1")
+                .with_settings("os", "arch", "compiler", "build_type")
+                .with_generator("MesonToolchain"),
+                 "build": profile_build,
+                 "host": profile})
+    client.run("install . -pr:h host -pr:b build")
+    # Check the meson configuration file
+    conan_meson = client.load(os.path.join(client.current_folder, "conan_meson_cross.ini"))
+    assert f"sys_root = '{sysroot}'\n" in conan_meson
+    assert re.search(r"c_args =.+--sysroot={}.+".format(sysroot), conan_meson)
+    assert re.search(r"c_link_args =.+--sysroot={}.+".format(sysroot), conan_meson)
+    assert re.search(r"cpp_args =.+--sysroot={}.+".format(sysroot), conan_meson)
+    assert re.search(r"cpp_link_args =.+--sysroot={}.+".format(sysroot), conan_meson)