diff --git a/crossenv/__init__.py b/crossenv/__init__.py index b1119f1..eab8f5f 100644 --- a/crossenv/__init__.py +++ b/crossenv/__init__.py @@ -464,15 +464,11 @@ def run_compiler(arg): ) def _split_apple_os_version(self, value): - # Split the Apple OS version from the OS name prefix. - if value.startswith("ios"): - offset = 3 - elif value.startswith("tvos"): - offset = 4 - elif value.startswith("watchos"): - offset = 7 - else: - raise ValueError("Unknown Apple compiler triple.") + # Split the Apple OS version from the OS name prefix (e.g. ios13.0 -> + # ios, 13.0). No Apple OS name contains a digit, so the version starts + # at the first one. On macOS the version is the Darwin kernel version + # (e.g. darwin23.4.0), not the macOS version. + offset = next((i for i, c in enumerate(value) if c.isdigit()), len(value)) return value[:offset], value[offset:] def _clean_triple(self, triple): diff --git a/tests/test_triples.py b/tests/test_triples.py new file mode 100644 index 0000000..1f8f170 --- /dev/null +++ b/tests/test_triples.py @@ -0,0 +1,59 @@ +################################################# +# Compiler triple parsing (no crossenv required) +################################################# + +import pytest + +from crossenv import CrossEnvBuilder + + +@pytest.fixture +def builder(): + # These helpers only parse strings, so skip __init__ and its environment + # checks. They run on any platform. + return CrossEnvBuilder.__new__(CrossEnvBuilder) + + +def test_split_apple_os_version(builder): + expected = { + "ios13.0": ("ios", "13.0"), + "ios": ("ios", ""), + "tvos17.0": ("tvos", "17.0"), + "tvos": ("tvos", ""), + "watchos10.0": ("watchos", "10.0"), + "watchos": ("watchos", ""), + "darwin23.4.0": ("darwin", "23.4.0"), + "darwin": ("darwin", ""), + # Previously rejected as unknown + "macos14.0": ("macos", "14.0"), + "macosx14.0": ("macosx", "14.0"), + "xros1.0": ("xros", "1.0"), + } + actual = {value: builder._split_apple_os_version(value) for value in expected} + assert actual == expected + + +def test_clean_triple(builder): + expected = { + # Apple: arm64 -> aarch64, OS version removed. The clang -dumpmachine + # and HOST_GNU_TYPE forms must clean to the same value. + "arm64-apple-ios13.0": ["aarch64", "apple", "ios"], + "aarch64-apple-ios": ["aarch64", "apple", "ios"], + "arm64-apple-tvos17.0": ["aarch64", "apple", "tvos"], + "arm64-apple-watchos10.0": ["aarch64", "apple", "watchos"], + "arm64-apple-darwin23.4.0": ["aarch64", "apple", "darwin"], + "aarch64-apple-darwin": ["aarch64", "apple", "darwin"], + "x86_64-apple-darwin23.4.0": ["x86_64", "apple", "darwin"], + # Apple simulator: 4 parts, not compared + "arm64-apple-ios13.0-simulator": None, + # Non-Apple: vendor dropped from 4-part triples + "x86_64-linux-gnu": ["x86_64", "linux", "gnu"], + "x86_64-pc-linux-gnu": ["x86_64", "linux", "gnu"], + "aarch64-unknown-linux-gnu": ["aarch64", "linux", "gnu"], + # Other forms + "x86_64": None, + "aarch64-linux": None, + "a-b-c-d-e": None, + } + actual = {triple: builder._clean_triple(triple) for triple in expected} + assert actual == expected