diff --git a/conan/tools/apple/apple.py b/conan/tools/apple/apple.py index 76afe748206..9725f30230f 100644 --- a/conan/tools/apple/apple.py +++ b/conan/tools/apple/apple.py @@ -1,9 +1,12 @@ +from conans.errors import ConanException + + def is_apple_os(os_): """returns True if OS is Apple one (Macos, iOS, watchOS or tvOS""" return str(os_) in ['Macos', 'iOS', 'watchOS', 'tvOS'] -def to_apple_arch(arch): +def to_apple_arch(arch, default=None): """converts conan-style architecture into Apple-style arch""" return {'x86': 'i386', 'x86_64': 'x86_64', @@ -12,45 +15,32 @@ def to_apple_arch(arch): 'armv8_32': 'arm64_32', 'armv8.3': 'arm64e', 'armv7s': 'armv7s', - 'armv7k': 'armv7k'}.get(str(arch)) - + 'armv7k': 'armv7k'}.get(arch, default) -def _guess_apple_sdk_name(os_, arch): - if str(arch).startswith('x86'): - return {'Macos': 'macosx', - 'iOS': 'iphonesimulator', - 'watchOS': 'watchsimulator', - 'tvOS': 'appletvsimulator'}.get(str(os_)) - else: - return {'Macos': 'macosx', - 'iOS': 'iphoneos', - 'watchOS': 'watchos', - 'tvOS': 'appletvos'}.get(str(os_), None) +def get_apple_sdk_name(conanfile): + """ + Returns the 'os.sdk' (SDK name) field value. Every user should specify it because + there could be several ones depending on the OS architecture. -def apple_sdk_name(settings): - """returns proper SDK name suitable for OS and architecture - we're building for (considering simulators)""" - arch = settings.get_safe('arch') - os_ = settings.get_safe('os') - os_sdk = settings.get_safe('os.sdk') - return os_sdk or _guess_apple_sdk_name(os_, arch) + Note: In case of MacOS it'll be the same for all the architectures. + """ + os_ = conanfile.settings.get_safe('os') + os_sdk = conanfile.settings.get_safe('os.sdk') + if os_sdk: + return os_sdk + elif os_ == "Macos": # it has only a single value for all the architectures + return "macosx" + elif is_apple_os(os_): + raise ConanException("Please, specify a suitable value for os.sdk.") -def apple_min_version_flag(conanfile): +def apple_min_version_flag(os_version, os_sdk, subsystem): """compiler flag name which controls deployment target""" - os_version = conanfile.settings.get_safe("os.version") - if not os_version: + if not os_version or not os_sdk: return '' - os_ = conanfile.settings.get_safe("os") - os_sdk = conanfile.settings.get_safe("os.sdk") - os_subsystem = conanfile.settings.get_safe("os.subsystem") - arch = conanfile.settings.get_safe("arch") - - if not os_version: - return '' - os_sdk = os_sdk if os_sdk else _guess_apple_sdk_name(os_, arch) + # FIXME: This guess seems wrong, nothing has to be guessed, but explicit flag = {'macosx': '-mmacosx-version-min', 'iphoneos': '-mios-version-min', 'iphonesimulator': '-mios-simulator-version-min', @@ -58,91 +48,9 @@ def apple_min_version_flag(conanfile): 'watchsimulator': '-mwatchos-simulator-version-min', 'appletvos': '-mtvos-version-min', 'appletvsimulator': '-mtvos-simulator-version-min'}.get(str(os_sdk)) - if os_subsystem == 'catalyst': + if subsystem == 'catalyst': # especial case, despite Catalyst is macOS, it requires an iOS version argument flag = '-mios-version-min' if not flag: return '' return "%s=%s" % (flag, os_version) - - -def apple_sdk_path(conanfile): - sdk_path = conanfile.conf["tools.apple:sdk_path"] - if not sdk_path: - sdk_path = XCRun(conanfile.settings).sdk_path - return sdk_path - - -class XCRun(object): - - def __init__(self, settings, sdk=None): - """sdk=False will skip the flag - sdk=None will try to adjust it automatically""" - if sdk is None and settings: - sdk = apple_sdk_name(settings) - self.sdk = sdk - - def _invoke(self, args): - def cmd_output(cmd): - from conans.util.runners import check_output_runner - return check_output_runner(cmd).strip() - - command = ['xcrun'] - if self.sdk: - command.extend(['-sdk', self.sdk]) - command.extend(args) - return cmd_output(command) - - def find(self, tool): - """find SDK tools (e.g. clang, ar, ranlib, lipo, codesign, etc.)""" - return self._invoke(['--find', tool]) - - @property - def sdk_path(self): - """obtain sdk path (aka apple sysroot or -isysroot""" - return self._invoke(['--show-sdk-path']) - - @property - def sdk_version(self): - """obtain sdk version""" - return self._invoke(['--show-sdk-version']) - - @property - def sdk_platform_path(self): - """obtain sdk platform path""" - return self._invoke(['--show-sdk-platform-path']) - - @property - def sdk_platform_version(self): - """obtain sdk platform version""" - return self._invoke(['--show-sdk-platform-version']) - - @property - def cc(self): - """path to C compiler (CC)""" - return self.find('clang') - - @property - def cxx(self): - """path to C++ compiler (CXX)""" - return self.find('clang++') - - @property - def ar(self): - """path to archiver (AR)""" - return self.find('ar') - - @property - def ranlib(self): - """path to archive indexer (RANLIB)""" - return self.find('ranlib') - - @property - def strip(self): - """path to symbol removal utility (STRIP)""" - return self.find('strip') - - @property - def libtool(self): - """path to libtool""" - return self.find('libtool') diff --git a/conan/tools/apple/xcodedeps.py b/conan/tools/apple/xcodedeps.py index 4a0a73c0e65..214c2847c2a 100644 --- a/conan/tools/apple/xcodedeps.py +++ b/conan/tools/apple/xcodedeps.py @@ -71,7 +71,7 @@ def __init__(self, conanfile): self.configuration = conanfile.settings.get_safe("build_type") arch = conanfile.settings.get_safe("arch") - self.architecture = to_apple_arch(arch) or arch + self.architecture = to_apple_arch(arch, default=arch) # TODO: check if it makes sense to add a subsetting for sdk version # related to: https://github.com/conan-io/conan/issues/9608 diff --git a/conan/tools/build/flags.py b/conan/tools/build/flags.py index 8c7b5bd65b7..bef6e535978 100644 --- a/conan/tools/build/flags.py +++ b/conan/tools/build/flags.py @@ -1,4 +1,4 @@ -from conans.client.tools import to_apple_arch +from conan.tools.apple.apple import to_apple_arch from conans.model.version import Version @@ -23,6 +23,8 @@ def architecture_flag(settings): # FIXME: This might be conflicting with Autotools --target cli arg apple_arch = to_apple_arch(arch) if apple_arch: + # TODO: Could we define anything like `to_apple_target()`? + # Check https://github.com/rust-lang/rust/issues/48862 return '--target=%s-apple-ios-macabi' % apple_arch elif arch in ['x86_64', 'sparcv9', 's390x']: return '-m64' diff --git a/conan/tools/cmake/toolchain.py b/conan/tools/cmake/toolchain.py index 7268eb62d8e..b9c5f972dc8 100644 --- a/conan/tools/cmake/toolchain.py +++ b/conan/tools/cmake/toolchain.py @@ -5,7 +5,7 @@ from jinja2 import Template -from conan.tools.apple.apple import is_apple_os +from conan.tools.apple.apple import is_apple_os, to_apple_arch, get_apple_sdk_name from conan.tools.build.flags import architecture_flag, libcxx_flag from conan.tools.build import build_jobs, use_win_mingw, cross_building from conan.tools.cmake.utils import is_multi_configuration @@ -322,41 +322,18 @@ class AppleSystemBlock(Block): {% endif %} """) - def _get_architecture(self): - # check valid combinations of architecture - os ? - # for iOS a FAT library valid for simulator and device - # can be generated if multiple archs are specified: - # "-DCMAKE_OSX_ARCHITECTURES=armv7;armv7s;arm64;i386;x86_64" - arch = self._conanfile.settings.get_safe("arch") - return {"x86": "i386", - "x86_64": "x86_64", - "armv8": "arm64", - "armv8_32": "arm64_32"}.get(arch, arch) - - def _apple_sdk_name(self): - """ - Returns the 'os.sdk' (SDK name) field value. Every user should specify it because - there could be several ones depending on the OS architecture. - - Note: In case of MacOS it'll be the same for all the architectures. - """ - os_ = self._conanfile.settings.get_safe('os') - os_sdk = self._conanfile.settings.get_safe('os.sdk') - if os_sdk: - return os_sdk - elif os_ == "Macos": # it has only a single value for all the architectures for now - return "macosx" - else: - raise ConanException("Please, specify a suitable value for os.sdk.") - def context(self): os_ = self._conanfile.settings.get_safe("os") if os_ not in ['Macos', 'iOS', 'watchOS', 'tvOS']: return None - host_architecture = self._get_architecture() + arch = self._conanfile.settings.get_safe("arch") + # check valid combinations of architecture - os ? + # for iOS a FAT library valid for simulator and device can be generated + # if multiple archs are specified "-DCMAKE_OSX_ARCHITECTURES=armv7;armv7s;arm64;i386;x86_64" + host_architecture = to_apple_arch(arch, default=arch) host_os_version = self._conanfile.settings.get_safe("os.version") - host_sdk_name = self._apple_sdk_name() + host_sdk_name = get_apple_sdk_name(self._conanfile) ctxt_toolchain = {} if host_sdk_name: diff --git a/conan/tools/gnu/autotoolstoolchain.py b/conan/tools/gnu/autotoolstoolchain.py index 4c6f9b62df7..a0e8bd1e61c 100644 --- a/conan/tools/gnu/autotoolstoolchain.py +++ b/conan/tools/gnu/autotoolstoolchain.py @@ -1,12 +1,12 @@ from conan.tools import args_to_string -from conan.tools.apple.apple import apple_min_version_flag, to_apple_arch, \ - apple_sdk_path +from conan.tools.apple.apple import apple_min_version_flag, to_apple_arch, get_apple_sdk_name from conan.tools.build.cross_building import cross_building from conan.tools.build.flags import architecture_flag, build_type_flags, cppstd_flag, libcxx_flag, \ build_type_link_flags from conan.tools.env import Environment from conan.tools.files import save_toolchain_args from conan.tools.gnu.get_gnu_triplet import _get_gnu_triplet +from conans.errors import ConanException class AutotoolsToolchain: @@ -47,7 +47,12 @@ def __init__(self, conanfile, namespace=None): self.apple_arch_flag = self.apple_isysroot_flag = None - self.apple_min_version_flag = apple_min_version_flag(self._conanfile) + os_sdk = get_apple_sdk_name(conanfile) + os_version = conanfile.settings.get_safe("os.version") + subsystem = conanfile.settings.get_safe("os.subsystem") + + self.apple_min_version_flag = apple_min_version_flag(os_version, os_sdk, subsystem) + if cross_building(self._conanfile): os_host = conanfile.settings.get_safe("os") arch_host = conanfile.settings.get_safe("arch") @@ -59,8 +64,11 @@ def __init__(self, conanfile, namespace=None): # Apple Stuff if os_build == "Macos": - sdk_path = apple_sdk_path(conanfile) - apple_arch = to_apple_arch(self._conanfile.settings.get_safe("arch")) + # SDK path is mandatory for cross-building + sdk_path = conanfile.conf["tools.apple:sdk_path"] + if not sdk_path: + raise ConanException("You must provide a valid SDK path for cross-compilation.") + apple_arch = to_apple_arch(arch_host) # https://man.archlinux.org/man/clang.1.en#Target_Selection_Options self.apple_arch_flag = "-arch {}".format(apple_arch) if apple_arch else None # -isysroot makes all includes for your library relative to the build directory diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py index e00ae957489..29a8ad1202a 100644 --- a/conan/tools/meson/toolchain.py +++ b/conan/tools/meson/toolchain.py @@ -3,7 +3,8 @@ from jinja2 import Template from conan.tools.build.cross_building import cross_building -from conan.tools.apple.apple import to_apple_arch, is_apple_os, apple_min_version_flag +from conan.tools.apple.apple import to_apple_arch, is_apple_os, apple_min_version_flag, \ + get_apple_sdk_name from conan.tools.env import VirtualBuildEnv from conan.tools.meson.helpers import * from conan.tools.microsoft import VCVars, msvc_runtime_flag @@ -158,14 +159,13 @@ def _add_apple_flags(self): if not sdk_path and self.cross_build: raise ConanException("You must provide a valid SDK path for cross-compilation.") - # TODO: Delete this os_sdk check whenever the _guess_apple_sdk_name() function disappears - os_sdk = conanfile.settings.get_safe('os.sdk') - if not os_sdk and os_ != "Macos": - raise ConanException("Please, specify a suitable value for os.sdk.") - + os_sdk = get_apple_sdk_name(conanfile) arch = to_apple_arch(conanfile.settings.get_safe("arch")) + os_version = conanfile.settings.get_safe("os.version") + subsystem = conanfile.settings.get_safe("os.subsystem") + # Calculating the main Apple flags - deployment_target_flag = apple_min_version_flag(conanfile) + deployment_target_flag = apple_min_version_flag(os_version, os_sdk, subsystem) sysroot_flag = "-isysroot " + sdk_path if sdk_path else "" arch_flag = "-arch " + arch if arch else "" diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py index b4619d99834..92649c7bac7 100644 --- a/conans/client/conf/__init__.py +++ b/conans/client/conf/__init__.py @@ -18,7 +18,6 @@ Linux: Macos: version: [None, "10.6", "10.7", "10.8", "10.9", "10.10", "10.11", "10.12", "10.13", "10.14", "10.15", "11.0", "12.0", "13.0"] - sdk: [None, "macosx"] subsystem: [None, catalyst] Android: api_level: ANY @@ -27,16 +26,16 @@ "11.0", "11.1", "11.2", "11.3", "11.4", "12.0", "12.1", "12.2", "12.3", "12.4", "13.0", "13.1", "13.2", "13.3", "13.4", "13.5", "13.6", "13.7", "14.0", "14.1", "14.2", "14.3", "14.4", "14.5", "14.6", "14.7", "14.8", "15.0", "15.1"] - sdk: [None, "iphoneos", "iphonesimulator"] + sdk: ["iphoneos", "iphonesimulator"] watchOS: version: ["4.0", "4.1", "4.2", "4.3", "5.0", "5.1", "5.2", "5.3", "6.0", "6.1", "6.2", "7.0", "7.1", "7.2", "7.3", "7.4", "7.5", "7.6", "8.0", "8.1"] - sdk: [None, "watchos", "watchsimulator"] + sdk: ["watchos", "watchsimulator"] tvOS: version: ["11.0", "11.1", "11.2", "11.3", "11.4", "12.0", "12.1", "12.2", "12.3", "12.4", "13.0", "13.2", "13.3", "13.4", "14.0", "14.2", "14.3", "14.4", "14.5", "14.6", "14.7", "15.0", "15.1"] - sdk: [None, "appletvos", "appletvsimulator"] + sdk: ["appletvos", "appletvsimulator"] FreeBSD: SunOS: AIX: diff --git a/conans/client/tools/__init__.py b/conans/client/tools/__init__.py index cff43f60298..4977153fdde 100644 --- a/conans/client/tools/__init__.py +++ b/conans/client/tools/__init__.py @@ -1,6 +1,4 @@ # noinspection PyUnresolvedReferences -from .apple import * -# noinspection PyUnresolvedReferences from .files import * # noinspection PyUnresolvedReferences from .scm import * diff --git a/conans/client/tools/apple.py b/conans/client/tools/apple.py deleted file mode 100644 index 102e58591fc..00000000000 --- a/conans/client/tools/apple.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import os - -from conans.util.runners import check_output_runner - - -def is_apple_os(os_): - """returns True if OS is Apple one (Macos, iOS, watchOS or tvOS""" - return str(os_) in ['Macos', 'iOS', 'watchOS', 'tvOS'] - - -def to_apple_arch(arch): - """converts conan-style architecture into Apple-style arch""" - return {'x86': 'i386', - 'x86_64': 'x86_64', - 'armv7': 'armv7', - 'armv8': 'arm64', - 'armv8_32': 'arm64_32', - 'armv8.3': 'arm64e', - 'armv7s': 'armv7s', - 'armv7k': 'armv7k'}.get(str(arch)) - - -def _guess_apple_sdk_name(os_, arch): - if str(arch).startswith('x86'): - return {'Macos': 'macosx', - 'iOS': 'iphonesimulator', - 'watchOS': 'watchsimulator', - 'tvOS': 'appletvsimulator'}.get(str(os_)) - else: - return {'Macos': 'macosx', - 'iOS': 'iphoneos', - 'watchOS': 'watchos', - 'tvOS': 'appletvos'}.get(str(os_), None) - - -def apple_sdk_name(settings): - """returns proper SDK name suitable for OS and architecture - we're building for (considering simulators)""" - arch = settings.get_safe('arch') - os_ = settings.get_safe('os') - os_sdk = settings.get_safe('os.sdk') - return os_sdk or _guess_apple_sdk_name(os_, arch) - - -def apple_deployment_target_env(os_, os_version): - """environment variable name which controls deployment target""" - env_name = {'Macos': 'MACOSX_DEPLOYMENT_TARGET', - 'iOS': 'IOS_DEPLOYMENT_TARGET', - 'watchOS': 'WATCHOS_DEPLOYMENT_TARGET', - 'tvOS': 'TVOS_DEPLOYMENT_TARGET'}.get(str(os_)) - if not env_name: - return {} - return {env_name: os_version} - - -def apple_deployment_target_flag(os_, os_version, os_sdk=None, os_subsystem=None, arch=None): - """compiler flag name which controls deployment target""" - os_sdk = os_sdk if os_sdk else _guess_apple_sdk_name(os_, arch) - flag = {'macosx': '-mmacosx-version-min', - 'iphoneos': '-mios-version-min', - 'iphonesimulator': '-mios-simulator-version-min', - 'watchos': '-mwatchos-version-min', - 'watchsimulator': '-mwatchos-simulator-version-min', - 'appletvos': '-mtvos-version-min', - 'appletvsimulator': '-mtvos-simulator-version-min'}.get(str(os_sdk)) - if os_subsystem == 'catalyst': - # especial case, despite Catalyst is macOS, it requires an iOS version argument - flag = '-mios-version-min' - if not flag: - return '' - return "%s=%s" % (flag, os_version) - - -class XCRun(object): - - def __init__(self, settings, sdk=None): - """sdk=False will skip the flag - sdk=None will try to adjust it automatically""" - if sdk is None and settings: - sdk = apple_sdk_name(settings) - self.sdk = sdk - - def _invoke(self, args): - def cmd_output(cmd): - return check_output_runner(cmd).strip() - - command = ['xcrun'] - if self.sdk: - command.extend(['-sdk', self.sdk]) - command.extend(args) - return cmd_output(command) - - def find(self, tool): - """find SDK tools (e.g. clang, ar, ranlib, lipo, codesign, etc.)""" - return self._invoke(['--find', tool]) - - @property - def sdk_path(self): - """obtain sdk path (aka apple sysroot or -isysroot""" - return self._invoke(['--show-sdk-path']) - - @property - def sdk_version(self): - """obtain sdk version""" - return self._invoke(['--show-sdk-version']) - - @property - def sdk_platform_path(self): - """obtain sdk platform path""" - return self._invoke(['--show-sdk-platform-path']) - - @property - def sdk_platform_version(self): - """obtain sdk platform version""" - return self._invoke(['--show-sdk-platform-version']) - - @property - def cc(self): - """path to C compiler (CC)""" - return self.find('clang') - - @property - def cxx(self): - """path to C++ compiler (CXX)""" - return self.find('clang++') - - @property - def ar(self): - """path to archiver (AR)""" - return self.find('ar') - - @property - def ranlib(self): - """path to archive indexer (RANLIB)""" - return self.find('ranlib') - - @property - def strip(self): - """path to symbol removal utility (STRIP)""" - return self.find('strip') - - @property - def libtool(self): - """path to libtool""" - return self.find('libtool') - - -def apple_dot_clean(folder): - files = os.listdir(folder) - for f in files: - full_name = os.path.join(folder, f) - if os.path.isdir(full_name): - apple_dot_clean(full_name) - elif f.startswith("._"): - if os.path.exists(os.path.join(folder, f[2:])): - os.remove(full_name) diff --git a/conans/test/functional/toolchains/cmake/cmakedeps/test_apple_frameworks.py b/conans/test/functional/toolchains/cmake/cmakedeps/test_apple_frameworks.py index e652afc4350..757dc1ef89e 100644 --- a/conans/test/functional/toolchains/cmake/cmakedeps/test_apple_frameworks.py +++ b/conans/test/functional/toolchains/cmake/cmakedeps/test_apple_frameworks.py @@ -3,8 +3,9 @@ import pytest -from conans.client.tools.apple import XCRun, to_apple_arch +from conan.tools.apple.apple import to_apple_arch from conans.test.assets.sources import gen_function_cpp +from conans.test.utils.apple import XCRun from conans.test.utils.tools import TestClient diff --git a/conans/test/functional/toolchains/gnu/autotools/test_apple_toolchain.py b/conans/test/functional/toolchains/gnu/autotools/test_apple_toolchain.py index 30fbc5977a5..8527f3f5f20 100644 --- a/conans/test/functional/toolchains/gnu/autotools/test_apple_toolchain.py +++ b/conans/test/functional/toolchains/gnu/autotools/test_apple_toolchain.py @@ -1,12 +1,13 @@ import os -import textwrap import platform +import textwrap import pytest from conan.tools.apple.apple import to_apple_arch from conans.test.assets.autotools import gen_makefile from conans.test.assets.sources import gen_function_h, gen_function_cpp +from conans.test.utils.apple import XCRun from conans.test.utils.tools import TestClient makefile = gen_makefile(apps=["app"], libs=["hello"]) @@ -32,22 +33,32 @@ def build(self): @pytest.mark.skipif(platform.system() != "Darwin", reason="Only OSX") -@pytest.mark.parametrize("config", [("x86_64", "Macos", "10.14"), - ("armv8", "iOS", "10.0"), - ("armv7", "iOS", "10.0"), - ("x86", "iOS", "10.0"), - ("x86_64", "iOS", "10.0"), - ("armv8", "Macos", "10.14") # M1 +@pytest.mark.parametrize("config", [("x86_64", "Macos", "10.14", None), + ("armv8", "iOS", "10.0", "iphoneos"), + ("armv7", "iOS", "10.0", "iphoneos"), + ("x86", "iOS", "10.0", "iphonesimulator"), + ("x86_64", "iOS", "10.0", "iphonesimulator"), + ("armv8", "Macos", "10.14", None) # M1 ]) def test_makefile_arch(config): - arch, os_, os_version = config + arch, os_, os_version, os_sdk = config + + xcrun = XCRun(None, os_sdk) + sdk_path = xcrun.sdk_path + profile = textwrap.dedent(""" include(default) [settings] os = {os} + {os_sdk} os.version = {os_version} arch = {arch} - """).format(os=os_, arch=arch, os_version=os_version) + + [conf] + tools.apple:sdk_path={sdk_path} + """).format(os=os_, arch=arch, + os_version=os_version, os_sdk="os.sdk = " + os_sdk if os_sdk else "", + sdk_path=sdk_path) t = TestClient() hello_h = gen_function_h(name="hello") @@ -81,15 +92,20 @@ def test_makefile_arch(config): @pytest.mark.skipif(platform.system() != "Darwin", reason="Only OSX") @pytest.mark.parametrize("arch", ["x86_64", "armv8"]) def test_catalyst(arch): + xcrun = XCRun(None) + sdk_path = xcrun.sdk_path + profile = textwrap.dedent(""" include(default) [settings] os = Macos os.version = 13.0 - os.sdk = macosx os.subsystem = catalyst arch = {arch} - """).format(arch=arch) + + [conf] + tools.apple:sdk_path={sdk_path} + """).format(arch=arch, sdk_path=sdk_path) t = TestClient() hello_h = gen_function_h(name="hello") diff --git a/conans/test/functional/toolchains/gnu/autotools/test_ios.py b/conans/test/functional/toolchains/gnu/autotools/test_ios.py index 4840939fd99..0bb1fcd1fbe 100644 --- a/conans/test/functional/toolchains/gnu/autotools/test_ios.py +++ b/conans/test/functional/toolchains/gnu/autotools/test_ios.py @@ -4,20 +4,16 @@ import pytest from conan.tools.files import load_toolchain_args -from conans.client.tools.apple import XCRun, to_apple_arch from conans.test.assets.autotools import gen_makefile_am, gen_configure_ac from conans.test.assets.sources import gen_function_cpp +from conans.test.utils.apple import XCRun from conans.test.utils.tools import TestClient @pytest.mark.skipif(platform.system() != "Darwin", reason="Requires Xcode") def test_ios(): xcrun = XCRun(None, sdk='iphoneos') - cflags = "" - cflags += " -isysroot " + xcrun.sdk_path - cflags += " -arch " + to_apple_arch('armv8') - cxxflags = cflags - ldflags = cflags + sdk_path = xcrun.sdk_path profile = textwrap.dedent(""" include(default) @@ -26,13 +22,10 @@ def test_ios(): os.sdk=iphoneos os.version=12.0 arch=armv8 - [env] - CC={cc} - CXX={cxx} - CFLAGS={cflags} - CXXFLAGS={cxxflags} - LDFLAGS={ldflags} - """).format(cc=xcrun.cc, cxx=xcrun.cxx, cflags=cflags, cxxflags=cxxflags, ldflags=ldflags) + + [conf] + tools.apple:sdk_path={sdk_path} + """).format(sdk_path=sdk_path) client = TestClient(path_with_spaces=False) client.save({"m1": profile}, clean_first=True) diff --git a/conans/test/functional/toolchains/meson/test_cross_compilation.py b/conans/test/functional/toolchains/meson/test_cross_compilation.py index c50f191e524..fb8adbfd4af 100644 --- a/conans/test/functional/toolchains/meson/test_cross_compilation.py +++ b/conans/test/functional/toolchains/meson/test_cross_compilation.py @@ -7,8 +7,9 @@ import pytest from parameterized import parameterized -from conans.client.tools.apple import XCRun, to_apple_arch +from conan.tools.apple.apple import to_apple_arch from conans.test.assets.sources import gen_function_cpp, gen_function_h +from conans.test.utils.apple import XCRun from conans.test.utils.tools import TestClient _conanfile_py = textwrap.dedent(""" @@ -54,21 +55,21 @@ def build(self): @pytest.mark.tool_meson @pytest.mark.skipif(sys.version_info.major == 2, reason="Meson not supported in Py2") @pytest.mark.skipif(platform.system() != "Darwin", reason="requires Xcode") -@pytest.mark.parametrize("arch, os_, os_version, sdk", [ +@pytest.mark.parametrize("arch, os_, os_version, os_sdk", [ ('armv8', 'iOS', '10.0', 'iphoneos'), ('armv7', 'iOS', '10.0', 'iphoneos'), ('x86', 'iOS', '10.0', 'iphonesimulator'), ('x86_64', 'iOS', '10.0', 'iphonesimulator'), ('armv8', 'Macos', None, None) # MacOS M1 ]) -def test_apple_meson_toolchain_cross_compiling(arch, os_, os_version, sdk): +def test_apple_meson_toolchain_cross_compiling(arch, os_, os_version, os_sdk): profile = textwrap.dedent(""" include(default) [settings] os = {os} os.version = {os_version} - os.sdk = {os_sdk} + {os_sdk} arch = {arch} compiler = apple-clang compiler.version = 12.0 @@ -78,7 +79,7 @@ def test_apple_meson_toolchain_cross_compiling(arch, os_, os_version, sdk): tools.apple:sdk_path={sdk_path} """) - xcrun = XCRun(None, sdk) + xcrun = XCRun(None, os_sdk) sdk_path = xcrun.sdk_path hello_h = gen_function_h(name="hello") @@ -87,7 +88,7 @@ def test_apple_meson_toolchain_cross_compiling(arch, os_, os_version, sdk): profile = profile.format( os=os_, os_version=os_version, - os_sdk=sdk, + os_sdk="os.sdk = " + os_sdk if os_sdk else "", arch=arch, sdk_path=sdk_path) diff --git a/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py b/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py index c34585c7b91..3c8d12bac90 100644 --- a/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py +++ b/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py @@ -272,6 +272,7 @@ def test_apple_arch_flag(): {"build_type": "Debug", "os": "iOS", "os.version": "14", + "os.sdk": "iphoneos", "arch": "armv8"}) be = AutotoolsToolchain(conanfile) expected = "-arch arm64" @@ -324,6 +325,7 @@ def test_apple_isysrootflag(): conanfile.settings = MockSettings( {"build_type": "Debug", "os": "iOS", + "os.sdk": "iphoneos", "os.version": "14", "arch": "armv8"}) be = AutotoolsToolchain(conanfile) @@ -357,6 +359,7 @@ def test_custom_defines(): conanfile.settings = MockSettings( {"build_type": "RelWithDebInfo", "os": "iOS", + "os.sdk": "iphoneos", "os.version": "14", "arch": "armv8"}) conanfile.settings_build = MockSettings({"os": "Macos", "arch": "armv8"}) @@ -374,6 +377,7 @@ def test_custom_cxxflags(): conanfile.settings = MockSettings( {"build_type": "RelWithDebInfo", "os": "iOS", + "os.sdk": "iphoneos", "os.version": "14", "arch": "armv8"}) conanfile.settings_build = MockSettings({"os": "Macos", "arch": "armv8"}) @@ -394,6 +398,7 @@ def test_custom_cflags(): conanfile.settings = MockSettings( {"build_type": "RelWithDebInfo", "os": "iOS", + "os.sdk": "iphoneos", "os.version": "14", "arch": "armv8"}) conanfile.settings_build = MockSettings({"os": "Macos", "arch": "armv8"}) @@ -414,6 +419,7 @@ def test_custom_ldflags(): conanfile.settings = MockSettings( {"build_type": "RelWithDebInfo", "os": "iOS", + "os.sdk": "iphoneos", "os.version": "14", "arch": "armv8"}) conanfile.settings_build = MockSettings({"os": "Macos", "arch": "armv8"}) diff --git a/conans/test/unittests/client/tools/test_files.py b/conans/test/unittests/client/tools/test_files.py deleted file mode 100644 index 6749c5ce775..00000000000 --- a/conans/test/unittests/client/tools/test_files.py +++ /dev/null @@ -1,44 +0,0 @@ -# coding=utf-8 - -import os -import unittest - -from conans.client.tools.files import save, chdir -from conans.client.tools import apple_dot_clean -from conans.test.utils.test_files import temp_folder - - -class DotCleanTest(unittest.TestCase): - - def _run_test(self, tuples): - tmp_folder = temp_folder() - with chdir(tmp_folder): - for f, _ in tuples: - save(f, "") - - apple_dot_clean(".") - - for f, expected_after in tuples: - self.assertEqual(expected_after, os.path.exists(f)) - - def test_removal_normal(self): - files = [("file.txt", True), - ("._file.txt", False), - ("folder/file.txt", True), - ("folder/._file.txt", False),] - self._run_test(tuples=files) - - def test_only_remove_matching_ones(self): - files = [("file.txt", True), - ("._file.txt", False), - ("._other.txt", True)] - self._run_test(tuples=files) - - def test_handle_dirs(self): - files = [("folder/file.txt", True), - ("folder/._file.txt", False), - ("._folder/file.txt", True), - ("._folder/._file.txt", False), - ("._other/._file.txt", True), - ("._other2/file.txt", True)] - self._run_test(tuples=files) diff --git a/conans/test/unittests/tools/cmake/test_cmaketoolchain.py b/conans/test/unittests/tools/cmake/test_cmaketoolchain.py index 52e843e67de..97c84127308 100644 --- a/conans/test/unittests/tools/cmake/test_cmaketoolchain.py +++ b/conans/test/unittests/tools/cmake/test_cmaketoolchain.py @@ -375,7 +375,8 @@ def test_apple_cmake_osx_sysroot(os, os_sdk, arch, expected_sdk): c.settings = "os", "compiler", "build_type", "arch" c.settings = Settings.loads(get_default_settings_yml()) c.settings.os = os - c.settings.os.sdk = os_sdk + if os_sdk: + c.settings.os.sdk = os_sdk c.settings.build_type = "Release" c.settings.arch = arch c.settings.compiler = "apple-clang" @@ -394,12 +395,12 @@ def test_apple_cmake_osx_sysroot(os, os_sdk, arch, expected_sdk): assert 'set(CMAKE_OSX_SYSROOT %s CACHE STRING "" FORCE)' % expected_sdk in content -@pytest.mark.parametrize("os,os_sdk,arch,expected_sdk", [ - ("iOS", None, "x86_64", ""), - ("watchOS", None, "armv8", ""), - ("tvOS", None, "x86_64", "") +@pytest.mark.parametrize("os,arch,expected_sdk", [ + ("iOS", "x86_64", ""), + ("watchOS", "armv8", ""), + ("tvOS", "x86_64", "") ]) -def test_apple_cmake_osx_sysroot_sdk_mandatory(os, os_sdk, arch, expected_sdk): +def test_apple_cmake_osx_sysroot_sdk_mandatory(os, arch, expected_sdk): """ Testing if CMAKE_OSX_SYSROOT is correctly set. Issue related: https://github.com/conan-io/conan/issues/10275 @@ -408,7 +409,6 @@ def test_apple_cmake_osx_sysroot_sdk_mandatory(os, os_sdk, arch, expected_sdk): c.settings = "os", "compiler", "build_type", "arch" c.settings = Settings.loads(get_default_settings_yml()) c.settings.os = os - c.settings.os.sdk = os_sdk c.settings.build_type = "Release" c.settings.arch = arch c.settings.compiler = "apple-clang" diff --git a/conans/test/unittests/util/apple_test.py b/conans/test/unittests/util/apple_test.py index 77eea8371e6..cd715932275 100644 --- a/conans/test/unittests/util/apple_test.py +++ b/conans/test/unittests/util/apple_test.py @@ -7,14 +7,18 @@ import pytest -from conans.client import tools +from conan.tools.apple.apple import to_apple_arch, apple_min_version_flag, \ + is_apple_os +from conans.test.utils.apple import XCRun class FakeSettings(object): - def __init__(self, _os, _arch, _os_sdk=None): + def __init__(self, _os, arch, os_sdk=None, os_version=None, subsystem=None): self._os = _os - self._arch = _arch - self._os_sdk = _os_sdk + self._arch = arch + self._os_sdk = os_sdk + self._os_version = os_version + self._os_subystem = subsystem def get_safe(self, name): if name == 'os': @@ -23,105 +27,54 @@ def get_safe(self, name): return self._arch elif name == 'os.sdk': return self._os_sdk + elif name == "os.version": + return self._os_version + elif name == "os.subsystem": + return self._os_subystem + + +class TestApple: + @pytest.mark.parametrize("os_, version, sdk, subsystem, flag", + [("Macos", "10.1", "macosx", None, '-mmacosx-version-min=10.1'), + ("iOS", "10.1", "iphoneos", None, '-mios-version-min=10.1'), + ("iOS", "10.1", "iphonesimulator", None, + '-mios-simulator-version-min=10.1'), + ("watchOS", "10.1", "watchos", None, '-mwatchos-version-min=10.1'), + ("watchOS", "10.1", "watchsimulator", None, + '-mwatchos-simulator-version-min=10.1'), + ("tvOS", "10.1", "appletvos", None, '-mtvos-version-min=10.1'), + ("tvOS", "10.1", "appletvsimulator", None, + '-mtvos-simulator-version-min=10.1'), + ("Macos", "10.1", "macosx", "catalyst", '-mios-version-min=10.1'), + ("Solaris", "10.1", None, None, ''), + ("Macos", "10.1", None, None, ''), + ("Macos", None, "macosx", None, '') + ]) + def test_deployment_target_flag_name(self, os_, version, sdk, subsystem, flag): + assert apple_min_version_flag(version, sdk, subsystem) == flag class AppleTest(unittest.TestCase): def test_is_apple_os(self): - self.assertTrue(tools.is_apple_os('iOS')) - self.assertTrue(tools.is_apple_os('tvOS')) - self.assertTrue(tools.is_apple_os('watchOS')) - self.assertTrue(tools.is_apple_os('Macos')) - self.assertFalse(tools.is_apple_os('Windows')) - self.assertFalse(tools.is_apple_os('Linux')) - self.assertFalse(tools.is_apple_os('Android')) + self.assertTrue(is_apple_os('iOS')) + self.assertTrue(is_apple_os('tvOS')) + self.assertTrue(is_apple_os('watchOS')) + self.assertTrue(is_apple_os('Macos')) + self.assertFalse(is_apple_os('Windows')) + self.assertFalse(is_apple_os('Linux')) + self.assertFalse(is_apple_os('Android')) def test_to_apple_arch(self): - self.assertEqual(tools.to_apple_arch('x86'), 'i386') - self.assertEqual(tools.to_apple_arch('x86_64'), 'x86_64') - self.assertEqual(tools.to_apple_arch('armv7'), 'armv7') - self.assertEqual(tools.to_apple_arch('armv7s'), 'armv7s') - self.assertEqual(tools.to_apple_arch('armv7k'), 'armv7k') - self.assertEqual(tools.to_apple_arch('armv8'), 'arm64') - self.assertEqual(tools.to_apple_arch('armv8.3'), 'arm64e') - self.assertEqual(tools.to_apple_arch('armv8_32'), 'arm64_32') - self.assertIsNone(tools.to_apple_arch('mips')) - - def test_apple_sdk_name(self): - self.assertEqual(tools.apple_sdk_name(FakeSettings('Macos', 'x86')), 'macosx') - self.assertEqual(tools.apple_sdk_name(FakeSettings('Macos', 'x86_64')), 'macosx') - self.assertEqual(tools.apple_sdk_name(FakeSettings('iOS', 'x86_64')), 'iphonesimulator') - self.assertEqual(tools.apple_sdk_name(FakeSettings('iOS', 'armv7')), 'iphoneos') - self.assertEqual(tools.apple_sdk_name(FakeSettings('watchOS', 'x86_64')), 'watchsimulator') - self.assertEqual(tools.apple_sdk_name(FakeSettings('watchOS', 'armv7k')), 'watchos') - self.assertEqual(tools.apple_sdk_name(FakeSettings('tvOS', 'x86')), 'appletvsimulator') - self.assertEqual(tools.apple_sdk_name(FakeSettings('tvOS', 'armv8')), 'appletvos') - self.assertIsNone(tools.apple_sdk_name(FakeSettings('Windows', 'x86'))) - - self.assertEqual(tools.apple_sdk_name(FakeSettings('iOS', 'armv8')), 'iphoneos') - self.assertEqual(tools.apple_sdk_name(FakeSettings('iOS', 'armv8', 'iphoneos')), - 'iphoneos') - self.assertEqual(tools.apple_sdk_name(FakeSettings('iOS', 'armv8', 'iphonesimulator')), - 'iphonesimulator') - - def test_apple_sdk_name_custom_settings(self): - self.assertEqual(tools.apple_sdk_name(FakeSettings('Macos', 'ios_fat')), 'macosx') - self.assertEqual(tools.apple_sdk_name(FakeSettings('iOS', 'ios_fat')), 'iphoneos') - self.assertEqual(tools.apple_sdk_name(FakeSettings('watchOS', 'ios_fat')), 'watchos') - self.assertEqual(tools.apple_sdk_name(FakeSettings('tvOS', 'ios_fat')), 'appletvos') - self.assertIsNone(tools.apple_sdk_name(FakeSettings('ConanOS', 'ios_fat'))) - - def test_deployment_target_env_name(self): - self.assertEqual(tools.apple_deployment_target_env('Macos', "10.1"), - {"MACOSX_DEPLOYMENT_TARGET": "10.1"}) - self.assertEqual(tools.apple_deployment_target_env('iOS', "10.1"), - {"IOS_DEPLOYMENT_TARGET": "10.1"}) - self.assertEqual(tools.apple_deployment_target_env('watchOS', "10.1"), - {"WATCHOS_DEPLOYMENT_TARGET": "10.1"}) - self.assertEqual(tools.apple_deployment_target_env('tvOS', "10.1"), - {"TVOS_DEPLOYMENT_TARGET": "10.1"}) - self.assertEqual(tools.apple_deployment_target_env('Linux', "10.1"), {}) - - def test_deployment_target_flag_name(self): - self.assertEqual(tools.apple_deployment_target_flag('Macos', "10.1"), - '-mmacosx-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('Macos', "10.1", 'macosx'), - '-mmacosx-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('iOS', "10.1"), - '-mios-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('iOS', "10.1", 'iphoneos'), - '-mios-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('iOS', "10.1", 'iphonesimulator'), - '-mios-simulator-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('watchOS', "10.1"), - '-mwatchos-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('watchOS', "10.1", 'watchos'), - '-mwatchos-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('watchOS', "10.1", 'watchsimulator'), - '-mwatchos-simulator-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('tvOS', "10.1"), - '-mtvos-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('tvOS', "10.1", 'appletvos'), - '-mtvos-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag('tvOS', "10.1", 'appletvsimulator'), - '-mtvos-simulator-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag("Macos", "10.1", None, "catalyst"), - '-mios-version-min=10.1') - - self.assertEqual(tools.apple_deployment_target_flag("Macos", "10.1", "macosx", "catalyst"), - '-mios-version-min=10.1') - - self.assertEqual('', tools.apple_deployment_target_flag('Solaris', "10.1")) + self.assertEqual(to_apple_arch('x86'), 'i386') + self.assertEqual(to_apple_arch('x86_64'), 'x86_64') + self.assertEqual(to_apple_arch('armv7'), 'armv7') + self.assertEqual(to_apple_arch('armv7s'), 'armv7s') + self.assertEqual(to_apple_arch('armv7k'), 'armv7k') + self.assertEqual(to_apple_arch('armv8'), 'arm64') + self.assertEqual(to_apple_arch('armv8.3'), 'arm64e') + self.assertEqual(to_apple_arch('armv8_32'), 'arm64_32') + self.assertIsNone(to_apple_arch('mips')) + self.assertEqual(to_apple_arch('mips', default='mips'), 'mips') @pytest.mark.skipif(platform.system() != "Darwin", reason="Requires OSX") def test_xcrun(self): @@ -136,26 +89,26 @@ def _common_asserts(xcrun_): self.assertTrue(os.path.isdir(xcrun_.sdk_path)) settings = FakeSettings('Macos', 'x86') - xcrun = tools.XCRun(settings) + xcrun = XCRun(settings) _common_asserts(xcrun) settings = FakeSettings('iOS', 'x86') - xcrun = tools.XCRun(settings, sdk='macosx') + xcrun = XCRun(settings, sdk='macosx') _common_asserts(xcrun) # Simulator self.assertNotIn("iPhoneOS", xcrun.sdk_path) - settings = FakeSettings('iOS', 'armv7') - xcrun = tools.XCRun(settings) + settings = FakeSettings('iOS', 'armv7', os_sdk="iphoneos") + xcrun = XCRun(settings) _common_asserts(xcrun) self.assertIn("iPhoneOS", xcrun.sdk_path) - settings = FakeSettings('watchOS', 'armv7') - xcrun = tools.XCRun(settings) + settings = FakeSettings('watchOS', 'armv7', os_sdk="watchos") + xcrun = XCRun(settings) _common_asserts(xcrun) self.assertIn("WatchOS", xcrun.sdk_path) # Default one settings = FakeSettings(None, None) - xcrun = tools.XCRun(settings) + xcrun = XCRun(settings) _common_asserts(xcrun) diff --git a/conans/test/utils/apple.py b/conans/test/utils/apple.py new file mode 100644 index 00000000000..e6834a50790 --- /dev/null +++ b/conans/test/utils/apple.py @@ -0,0 +1,74 @@ + +class XCRun(object): + + def __init__(self, settings, sdk=None): + """sdk=False will skip the flag + sdk=None will try to adjust it automatically""" + if sdk is None and settings: + sdk = settings.get_safe('os.sdk') + self.sdk = sdk + + def _invoke(self, args): + def cmd_output(cmd): + from conans.util.runners import check_output_runner + return check_output_runner(cmd).strip() + + command = ['xcrun'] + if self.sdk: + command.extend(['-sdk', self.sdk]) + command.extend(args) + return cmd_output(command) + + def find(self, tool): + """find SDK tools (e.g. clang, ar, ranlib, lipo, codesign, etc.)""" + return self._invoke(['--find', tool]) + + @property + def sdk_path(self): + """obtain sdk path (aka apple sysroot or -isysroot""" + return self._invoke(['--show-sdk-path']) + + @property + def sdk_version(self): + """obtain sdk version""" + return self._invoke(['--show-sdk-version']) + + @property + def sdk_platform_path(self): + """obtain sdk platform path""" + return self._invoke(['--show-sdk-platform-path']) + + @property + def sdk_platform_version(self): + """obtain sdk platform version""" + return self._invoke(['--show-sdk-platform-version']) + + @property + def cc(self): + """path to C compiler (CC)""" + return self.find('clang') + + @property + def cxx(self): + """path to C++ compiler (CXX)""" + return self.find('clang++') + + @property + def ar(self): + """path to archiver (AR)""" + return self.find('ar') + + @property + def ranlib(self): + """path to archive indexer (RANLIB)""" + return self.find('ranlib') + + @property + def strip(self): + """path to symbol removal utility (STRIP)""" + return self.find('strip') + + @property + def libtool(self): + """path to libtool""" + return self.find('libtool') diff --git a/conans/tools.py b/conans/tools.py index 3b1658f3568..f5f267eaf18 100644 --- a/conans/tools.py +++ b/conans/tools.py @@ -15,7 +15,6 @@ from conans.client.tools.scm import * # pylint: disable=unused-import from conans.client.tools.settings import * # pylint: disable=unused-import -from conans.client.tools.apple import * # Tools form conans.util from conans.util.files import _generic_algorithm_sum, load, md5, md5sum, mkdir, rmdir, save as files_save, save_append, sha1sum, sha256sum, to_file_bytes, touch