From 4eac1e5f7d5f493ae40c4a4982eaf0973f725ab3 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 19 Jun 2025 13:16:21 +0300 Subject: [PATCH 1/4] pass deployment target based on profile to xcodebuild --- conan/tools/apple/apple.py | 10 ++++++++++ conan/tools/apple/xcodebuild.py | 9 ++++++++- conan/tools/apple/xcodetoolchain.py | 12 +++--------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/conan/tools/apple/apple.py b/conan/tools/apple/apple.py index 0e3d7efcebd..844084387f4 100644 --- a/conan/tools/apple/apple.py +++ b/conan/tools/apple/apple.py @@ -108,6 +108,16 @@ def resolve_apple_flags(conanfile, is_cross_building=False): return min_version_flag, apple_arch_flag, apple_isysroot_flag +def xcodebuild_deployment_target_key(os_name): + return { + "Macos": "MACOSX_DEPLOYMENT_TARGET", + "iOS": "IPHONEOS_DEPLOYMENT_TARGET", + "tvOS": "TVOS_DEPLOYMENT_TARGET", + "watchOS": "WATCHOS_DEPLOYMENT_TARGET", + "visionOS": "XROS_DEPLOYMENT_TARGET", + }.get(os_name) if os_name else None + + class XCRun: """ XCRun is a wrapper for the Apple **xcrun** tool used to get information for building. diff --git a/conan/tools/apple/xcodebuild.py b/conan/tools/apple/xcodebuild.py index 6aa9420ef82..bc9d3785945 100644 --- a/conan/tools/apple/xcodebuild.py +++ b/conan/tools/apple/xcodebuild.py @@ -1,4 +1,4 @@ -from conan.tools.apple import to_apple_arch +from conan.tools.apple.apple import to_apple_arch, xcodebuild_deployment_target_key class XcodeBuild(object): @@ -8,6 +8,8 @@ def __init__(self, conanfile): self._arch = to_apple_arch(self._conanfile) self._sdk = conanfile.settings.get_safe("os.sdk") or "" self._sdk_version = conanfile.settings.get_safe("os.sdk_version") or "" + self._os = conanfile.settings.get_safe("os") + self._os_version = conanfile.settings.get_safe("os.version") @property def _verbosity(self): @@ -40,4 +42,9 @@ def build(self, xcodeproj, target=None): cmd = "xcodebuild -project {} -configuration {} -arch {} " \ "{} {} {}".format(xcodeproj, self._build_type, self._arch, self._sdkroot, self._verbosity, target) + + deployment_target_key = xcodebuild_deployment_target_key(self._os) + if deployment_target_key and self._os_version: + cmd += f" {deployment_target_key}={self._os_version}" + self._conanfile.run(cmd) diff --git a/conan/tools/apple/xcodetoolchain.py b/conan/tools/apple/xcodetoolchain.py index 194aa3af6c6..b6aca20e339 100644 --- a/conan/tools/apple/xcodetoolchain.py +++ b/conan/tools/apple/xcodetoolchain.py @@ -1,7 +1,7 @@ import textwrap from conan.internal import check_duplicated_generator -from conan.tools.apple.apple import to_apple_arch +from conan.tools.apple.apple import to_apple_arch, xcodebuild_deployment_target_key from conan.tools.apple.xcodedeps import GLOBAL_XCCONFIG_FILENAME, GLOBAL_XCCONFIG_TEMPLATE, \ _add_includes_to_file_or_create, _xcconfig_settings_filename, _xcconfig_conditional from conan.internal.util.files import save @@ -64,16 +64,10 @@ def _cppstd(self): @property def _apple_deployment_target(self): - deployment_target_key = { - "Macos": "MACOSX_DEPLOYMENT_TARGET", - "iOS": "IPHONEOS_DEPLOYMENT_TARGET", - "tvOS": "TVOS_DEPLOYMENT_TARGET", - "watchOS": "WATCHOS_DEPLOYMENT_TARGET", - "visionOS": "XROS_DEPLOYMENT_TARGET", - }.get(str(self._conanfile.settings.get_safe("os"))) + deployment_target_key = xcodebuild_deployment_target_key(self._conanfile.settings.get_safe("os")) return '{}{}={}'.format(deployment_target_key, _xcconfig_conditional(self._conanfile.settings, self.configuration), - self.os_version) if self.os_version else "" + self.os_version) if deployment_target_key and self.os_version else "" @property def _clang_cxx_library(self): From 6ec24545d7fbcc990f75cb94f4b37034f457cb64 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 19 Jun 2025 13:19:59 +0300 Subject: [PATCH 2/4] quote project path --- conan/tools/apple/xcodebuild.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan/tools/apple/xcodebuild.py b/conan/tools/apple/xcodebuild.py index bc9d3785945..78a6f0c3139 100644 --- a/conan/tools/apple/xcodebuild.py +++ b/conan/tools/apple/xcodebuild.py @@ -39,7 +39,7 @@ def build(self, xcodeproj, target=None): :return: the return code for the launched ``xcodebuild`` command. """ target = "-target {}".format(target) if target else "-alltargets" - cmd = "xcodebuild -project {} -configuration {} -arch {} " \ + cmd = "xcodebuild -project '{}' -configuration {} -arch {} " \ "{} {} {}".format(xcodeproj, self._build_type, self._arch, self._sdkroot, self._verbosity, target) From 0ac62707e7593940ec57d2667ca0a8d31323a93a Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 17 Jul 2025 09:25:55 +0300 Subject: [PATCH 3/4] quote target param --- conan/tools/apple/xcodebuild.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan/tools/apple/xcodebuild.py b/conan/tools/apple/xcodebuild.py index 78a6f0c3139..637c4739d17 100644 --- a/conan/tools/apple/xcodebuild.py +++ b/conan/tools/apple/xcodebuild.py @@ -38,7 +38,7 @@ def build(self, xcodeproj, target=None): will build all the targets passing the ``-alltargets`` argument instead. :return: the return code for the launched ``xcodebuild`` command. """ - target = "-target {}".format(target) if target else "-alltargets" + target = "-target '{}'".format(target) if target else "-alltargets" cmd = "xcodebuild -project '{}' -configuration {} -arch {} " \ "{} {} {}".format(xcodeproj, self._build_type, self._arch, self._sdkroot, self._verbosity, target) From 63183573a5dff95f936059b53cb0474fc90d725c Mon Sep 17 00:00:00 2001 From: Carlos Zoido Date: Mon, 21 Jul 2025 15:16:40 +0200 Subject: [PATCH 4/4] add tests --- .../toolchains/apple/test_xcodebuild.py | 5 +-- .../client/tools/apple/test_xcodebuild.py | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/test/functional/toolchains/apple/test_xcodebuild.py b/test/functional/toolchains/apple/test_xcodebuild.py index a63e501f07b..74c7c069050 100644 --- a/test/functional/toolchains/apple/test_xcodebuild.py +++ b/test/functional/toolchains/apple/test_xcodebuild.py @@ -91,11 +91,12 @@ def package_info(self): client.run("install . --build=missing") client.run("install . -s build_type=Debug --build=missing") client.run_command("xcodegen generate") - client.run("create . --build=missing -c tools.build:verbosity=verbose -c tools.compilation:verbosity=verbose") + client.run("create . --build=missing -s os.version=15.0 -c tools.build:verbosity=verbose -c tools.compilation:verbosity=verbose") + assert "MACOSX_DEPLOYMENT_TARGET=15.0" in client.out assert "xcodebuild: error: invalid option" not in client.out assert "hello/0.1: Hello World Release!" in client.out assert "App Release!" in client.out - client.run("create . -s build_type=Debug --build=missing") + client.run("create . -s build_type=Debug -s os.version=15.0 --build=missing") assert "hello/0.1: Hello World Debug!" in client.out assert "App Debug!" in client.out diff --git a/test/unittests/client/tools/apple/test_xcodebuild.py b/test/unittests/client/tools/apple/test_xcodebuild.py index 32cf2575bfa..947a00443a7 100644 --- a/test/unittests/client/tools/apple/test_xcodebuild.py +++ b/test/unittests/client/tools/apple/test_xcodebuild.py @@ -64,3 +64,39 @@ def test_sdk(): xcodebuild = XcodeBuild(conanfile) xcodebuild.build("app.xcodeproj") assert "SDKROOT" not in conanfile.command + + +@pytest.mark.parametrize("os_name, os_version, expected_key", [ + ("Macos", "14.0", "MACOSX_DEPLOYMENT_TARGET"), + ("iOS", "15.1", "IPHONEOS_DEPLOYMENT_TARGET"), + ("watchOS", "8.0", "WATCHOS_DEPLOYMENT_TARGET"), + ("tvOS", "15.0", "TVOS_DEPLOYMENT_TARGET"), + ("visionOS", "1.0", "XROS_DEPLOYMENT_TARGET") +]) +def test_deployment_target_and_quoting(os_name, os_version, expected_key): + """ + Checks that the correct deployment target is passed and that paths are quoted. + """ + conanfile = ConanFileMock() + conanfile.settings = MockSettings({"os": os_name, "os.version": os_version}) + xcodebuild = XcodeBuild(conanfile) + + xcodebuild.build("My Project.xcodeproj", target="My Target") + + expected_arg = f" {expected_key}={os_version}" + assert expected_arg in conanfile.command + + assert "-project 'My Project.xcodeproj'" in conanfile.command + assert "-target 'My Target'" in conanfile.command + + +def test_no_deployment_target_if_version_is_missing(): + """ + Checks that the deployment target argument is not added if os.version is missing. + """ + conanfile = ConanFileMock() + conanfile.settings = MockSettings({"os": "Macos"}) + xcodebuild = XcodeBuild(conanfile) + xcodebuild.build("app.xcodeproj") + + assert "MACOSX_DEPLOYMENT_TARGET" not in conanfile.command