diff --git a/cmake/Modules/GetTripleCMakeSystemName.cmake b/cmake/Modules/GetTripleCMakeSystemName.cmake new file mode 100644 index 000000000000..8ad725f34175 --- /dev/null +++ b/cmake/Modules/GetTripleCMakeSystemName.cmake @@ -0,0 +1,40 @@ +#===--------------------------------------------------------------------===// +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for details. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===--------------------------------------------------------------------===// + +# Map a target triple to the corresponding CMake system name. +# +# Usage: +# get_triple_cmake_system_name( ) +# +# Sets to the CMake-style system name +# (e.g. "x86_64-pc-linux-gnu" -> Linux, "arm64-apple-macos" -> +# "Darwin"). A triple with an OS cmake does not recognize maps to +# "Generic". +# +# The OS/environment -> system name data is derived from +# llvm/include/llvm/TargetParser/TripleName.def + +get_filename_component(_gtcsn_llvm_dir "${CMAKE_CURRENT_LIST_DIR}/../../llvm" + ABSOLUTE) +set(_gtcsn_script "${_gtcsn_llvm_dir}/utils/get_triple_system_name.py") + +function(get_triple_cmake_system_name triple out_var) + execute_process( + COMMAND "${Python3_EXECUTABLE}" "${_gtcsn_script}" "--triple" "${triple}" + OUTPUT_VARIABLE _name + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _result) + if(NOT _result EQUAL 0) + message(FATAL_ERROR "Failed to derive CMake system name for '${triple}'") + endif() + if(_name) + set(${out_var} "${_name}" PARENT_SCOPE) + else() + set(${out_var} "${CMAKE_HOST_SYSTEM_NAME}" PARENT_SCOPE) + endif() +endfunction() diff --git a/llvm/include/llvm/TargetParser/TripleName.def b/llvm/include/llvm/TargetParser/TripleName.def index bbc03b318801..95026bd39db7 100644 --- a/llvm/include/llvm/TargetParser/TripleName.def +++ b/llvm/include/llvm/TargetParser/TripleName.def @@ -5,15 +5,21 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +// Also parsed by llvm/utils/get_triple_system_name.py, so keep the macro +// invocations one per line with string literals. +// +//===----------------------------------------------------------------------===// // NOTE: NO INCLUDE GUARD DESIRED! //===----------------------------------------------------------------------===// // OS names. // -// TRIPLE_OS(Enum, Name) -// Enum - the Triple::OSType enumerator -// Name - the canonical string +// TRIPLE_OS(Enum, Name, CMakeName) +// Enum - the Triple::OSType enumerator +// Name - the canonical string +// CMakeName - the CMake system name, or "Generic" if there is no equivalent // // TRIPLE_OS_ALIAS(Enum, AliasName) // An additional prefix maps to an OSType. @@ -23,64 +29,64 @@ // ===----------------------------------------------------------------------===// #ifndef TRIPLE_OS -#define TRIPLE_OS(Enum, Name) +#define TRIPLE_OS(Enum, Name, CMakeName) #endif #ifndef TRIPLE_OS_ALIAS #define TRIPLE_OS_ALIAS(Enum, AliasName) #endif -TRIPLE_OS(Darwin, "darwin") -TRIPLE_OS(DragonFly, "dragonfly") -TRIPLE_OS(FreeBSD, "freebsd") -TRIPLE_OS(Fuchsia, "fuchsia") -TRIPLE_OS(IOS, "ios") -TRIPLE_OS(KFreeBSD, "kfreebsd") -TRIPLE_OS(Linux, "linux") -TRIPLE_OS(Lv2, "lv2") -TRIPLE_OS(MacOSX, "macosx") +TRIPLE_OS(Darwin, "darwin", "Darwin") +TRIPLE_OS(DragonFly, "dragonfly", "DragonFly") +TRIPLE_OS(FreeBSD, "freebsd", "FreeBSD") +TRIPLE_OS(Fuchsia, "fuchsia", "Fuchsia") +TRIPLE_OS(IOS, "ios", "iOS") +TRIPLE_OS(KFreeBSD, "kfreebsd", "FreeBSD") +TRIPLE_OS(Linux, "linux", "Linux") +TRIPLE_OS(Lv2, "lv2", "Generic") +TRIPLE_OS(MacOSX, "macosx", "Darwin") TRIPLE_OS_ALIAS(MacOSX, "macos") -TRIPLE_OS(Managarm, "managarm") -TRIPLE_OS(NetBSD, "netbsd") -TRIPLE_OS(OpenBSD, "openbsd") -TRIPLE_OS(Solaris, "solaris") -TRIPLE_OS(UEFI, "uefi") -TRIPLE_OS(Win32, "windows") +TRIPLE_OS(Managarm, "managarm", "Generic") +TRIPLE_OS(NetBSD, "netbsd", "NetBSD") +TRIPLE_OS(OpenBSD, "openbsd", "OpenBSD") +TRIPLE_OS(Solaris, "solaris", "SunOS") +TRIPLE_OS(UEFI, "uefi", "Generic") +TRIPLE_OS(Win32, "windows", "Windows") TRIPLE_OS_ALIAS(Win32, "win32") -TRIPLE_OS(ZOS, "zos") -TRIPLE_OS(Haiku, "haiku") -TRIPLE_OS(RTEMS, "rtems") -TRIPLE_OS(AIX, "aix") -TRIPLE_OS(CUDA, "cuda") -TRIPLE_OS(NVCL, "nvcl") -TRIPLE_OS(AMDHSA, "amdhsa") -TRIPLE_OS(PS4, "ps4") -TRIPLE_OS(PS5, "ps5") -TRIPLE_OS(ELFIAMCU, "elfiamcu") -TRIPLE_OS(TvOS, "tvos") -TRIPLE_OS(WatchOS, "watchos") -TRIPLE_OS(BridgeOS, "bridgeos") -TRIPLE_OS(DriverKit, "driverkit") -TRIPLE_OS(XROS, "xros") +TRIPLE_OS(ZOS, "zos", "OS390") +TRIPLE_OS(Haiku, "haiku", "Haiku") +TRIPLE_OS(RTEMS, "rtems", "RTEMS") +TRIPLE_OS(AIX, "aix", "AIX") +TRIPLE_OS(CUDA, "cuda", "Generic") +TRIPLE_OS(NVCL, "nvcl", "Generic") +TRIPLE_OS(AMDHSA, "amdhsa", "Generic") +TRIPLE_OS(PS4, "ps4", "Generic") +TRIPLE_OS(PS5, "ps5", "Generic") +TRIPLE_OS(ELFIAMCU, "elfiamcu", "Generic") +TRIPLE_OS(TvOS, "tvos", "tvOS") +TRIPLE_OS(WatchOS, "watchos", "watchOS") +TRIPLE_OS(BridgeOS, "bridgeos", "Darwin") +TRIPLE_OS(DriverKit, "driverkit", "Darwin") +TRIPLE_OS(XROS, "xros", "visionOS") TRIPLE_OS_ALIAS(XROS, "visionos") -TRIPLE_OS(Mesa3D, "mesa3d") -TRIPLE_OS(AMDPAL, "amdpal") -TRIPLE_OS(HermitCore, "hermit") -TRIPLE_OS(Hurd, "hurd") -TRIPLE_OS(WASIp1, "wasip1") -TRIPLE_OS(WASIp2, "wasip2") -TRIPLE_OS(WASIp3, "wasip3") -TRIPLE_OS(WASI, "wasi") -TRIPLE_OS(Emscripten, "emscripten") -TRIPLE_OS(ShaderModel, "shadermodel") -TRIPLE_OS(LiteOS, "liteos") -TRIPLE_OS(Serenity, "serenity") -TRIPLE_OS(Vulkan, "vulkan") -TRIPLE_OS(CheriotRTOS, "cheriotrtos") -TRIPLE_OS(OpenCL, "opencl") -TRIPLE_OS(ChipStar, "chipstar") -TRIPLE_OS(Firmware, "firmware") -TRIPLE_OS(QURT, "qurt") -TRIPLE_OS(H2, "h2") +TRIPLE_OS(Mesa3D, "mesa3d", "Generic") +TRIPLE_OS(AMDPAL, "amdpal", "Generic") +TRIPLE_OS(HermitCore, "hermit", "Generic") +TRIPLE_OS(Hurd, "hurd", "GNU") +TRIPLE_OS(WASIp1, "wasip1", "WASI") +TRIPLE_OS(WASIp2, "wasip2", "WASI") +TRIPLE_OS(WASIp3, "wasip3", "WASI") +TRIPLE_OS(WASI, "wasi", "WASI") +TRIPLE_OS(Emscripten, "emscripten", "Emscripten") +TRIPLE_OS(ShaderModel, "shadermodel", "Generic") +TRIPLE_OS(LiteOS, "liteos", "Generic") +TRIPLE_OS(Serenity, "serenity", "SerenityOS") +TRIPLE_OS(Vulkan, "vulkan", "Generic") +TRIPLE_OS(CheriotRTOS, "cheriotrtos", "Generic") +TRIPLE_OS(OpenCL, "opencl", "Generic") +TRIPLE_OS(ChipStar, "chipstar", "Generic") +TRIPLE_OS(Firmware, "firmware", "Generic") +TRIPLE_OS(QURT, "qurt", "Generic") +TRIPLE_OS(H2, "h2", "Generic") #undef TRIPLE_OS #undef TRIPLE_OS_ALIAS @@ -88,71 +94,73 @@ TRIPLE_OS(H2, "h2") //===----------------------------------------------------------------------===// // Environment names. // -// TRIPLE_ENV(Enum, Name) -// Enum - the Triple::EnvironmentType enumerator. -// Name - the canonical string +// TRIPLE_ENV(Enum, Name, CMakeOverride) +// Enum - the Triple::EnvironmentType enumerator. +// Name - the canonical string +// CMakeOverride - a CMake system name this environment forces regardless of +// the OS, or "" for no override // // Order is significant for the same prefix-matching reason as OS (eabihf before // eabi; the gnu*/musl* variants before gnu/musl). //===----------------------------------------------------------------------===// #ifndef TRIPLE_ENV -#define TRIPLE_ENV(Enum, Name) +#define TRIPLE_ENV(Enum, Name, CMakeOverride) #endif -TRIPLE_ENV(EABIHF, "eabihf") -TRIPLE_ENV(EABI, "eabi") -TRIPLE_ENV(GNUABIN32, "gnuabin32") -TRIPLE_ENV(GNUABI64, "gnuabi64") -TRIPLE_ENV(GNUEABIHFT64, "gnueabihft64") -TRIPLE_ENV(GNUEABIHF, "gnueabihf") -TRIPLE_ENV(GNUEABIT64, "gnueabit64") -TRIPLE_ENV(GNUEABI, "gnueabi") -TRIPLE_ENV(GNUF32, "gnuf32") -TRIPLE_ENV(GNUF64, "gnuf64") -TRIPLE_ENV(GNUSF, "gnusf") -TRIPLE_ENV(GNUX32, "gnux32") -TRIPLE_ENV(GNUILP32, "gnu_ilp32") -TRIPLE_ENV(CODE16, "code16") -TRIPLE_ENV(GNUT64, "gnut64") -TRIPLE_ENV(GNU, "gnu") -TRIPLE_ENV(Android, "android") -TRIPLE_ENV(MuslABIN32, "muslabin32") -TRIPLE_ENV(MuslABI64, "muslabi64") -TRIPLE_ENV(MuslEABIHF, "musleabihf") -TRIPLE_ENV(MuslEABI, "musleabi") -TRIPLE_ENV(MuslF32, "muslf32") -TRIPLE_ENV(MuslSF, "muslsf") -TRIPLE_ENV(MuslX32, "muslx32") -TRIPLE_ENV(MuslWALI, "muslwali") -TRIPLE_ENV(Musl, "musl") -TRIPLE_ENV(MSVC, "msvc") -TRIPLE_ENV(Itanium, "itanium") -TRIPLE_ENV(Cygnus, "cygnus") -TRIPLE_ENV(CoreCLR, "coreclr") -TRIPLE_ENV(Simulator, "simulator") -TRIPLE_ENV(MacABI, "macabi") -TRIPLE_ENV(Pixel, "pixel") -TRIPLE_ENV(Vertex, "vertex") -TRIPLE_ENV(Geometry, "geometry") -TRIPLE_ENV(Hull, "hull") -TRIPLE_ENV(Domain, "domain") -TRIPLE_ENV(Compute, "compute") -TRIPLE_ENV(Library, "library") -TRIPLE_ENV(RayGeneration, "raygeneration") -TRIPLE_ENV(Intersection, "intersection") -TRIPLE_ENV(AnyHit, "anyhit") -TRIPLE_ENV(ClosestHit, "closesthit") -TRIPLE_ENV(Miss, "miss") -TRIPLE_ENV(Callable, "callable") -TRIPLE_ENV(Mesh, "mesh") -TRIPLE_ENV(Amplification, "amplification") -TRIPLE_ENV(RootSignature, "rootsignature") -TRIPLE_ENV(OpenHOS, "ohos") -TRIPLE_ENV(PAuthTest, "pauthtest") -TRIPLE_ENV(LLVM, "llvm") -TRIPLE_ENV(Mlibc, "mlibc") -TRIPLE_ENV(MTIA, "mtia") +TRIPLE_ENV(EABIHF, "eabihf", "") +TRIPLE_ENV(EABI, "eabi", "") +TRIPLE_ENV(GNUABIN32, "gnuabin32", "") +TRIPLE_ENV(GNUABI64, "gnuabi64", "") +TRIPLE_ENV(GNUEABIHFT64, "gnueabihft64", "") +TRIPLE_ENV(GNUEABIHF, "gnueabihf", "") +TRIPLE_ENV(GNUEABIT64, "gnueabit64", "") +TRIPLE_ENV(GNUEABI, "gnueabi", "") +TRIPLE_ENV(GNUF32, "gnuf32", "") +TRIPLE_ENV(GNUF64, "gnuf64", "") +TRIPLE_ENV(GNUSF, "gnusf", "") +TRIPLE_ENV(GNUX32, "gnux32", "") +TRIPLE_ENV(GNUILP32, "gnu_ilp32", "") +TRIPLE_ENV(CODE16, "code16", "") +TRIPLE_ENV(GNUT64, "gnut64", "") +TRIPLE_ENV(GNU, "gnu", "") +TRIPLE_ENV(Android, "android", "Android") +TRIPLE_ENV(MuslABIN32, "muslabin32", "") +TRIPLE_ENV(MuslABI64, "muslabi64", "") +TRIPLE_ENV(MuslEABIHF, "musleabihf", "") +TRIPLE_ENV(MuslEABI, "musleabi", "") +TRIPLE_ENV(MuslF32, "muslf32", "") +TRIPLE_ENV(MuslSF, "muslsf", "") +TRIPLE_ENV(MuslX32, "muslx32", "") +TRIPLE_ENV(MuslWALI, "muslwali", "") +TRIPLE_ENV(Musl, "musl", "") +TRIPLE_ENV(MSVC, "msvc", "") +TRIPLE_ENV(Itanium, "itanium", "") +TRIPLE_ENV(Cygnus, "cygnus", "CYGWIN") +TRIPLE_ENV(CoreCLR, "coreclr", "") +TRIPLE_ENV(Simulator, "simulator", "") +TRIPLE_ENV(MacABI, "macabi", "") +TRIPLE_ENV(Pixel, "pixel", "") +TRIPLE_ENV(Vertex, "vertex", "") +TRIPLE_ENV(Geometry, "geometry", "") +TRIPLE_ENV(Hull, "hull", "") +TRIPLE_ENV(Domain, "domain", "") +TRIPLE_ENV(Compute, "compute", "") +TRIPLE_ENV(Library, "library", "") +TRIPLE_ENV(RayGeneration, "raygeneration", "") +TRIPLE_ENV(Intersection, "intersection", "") +TRIPLE_ENV(AnyHit, "anyhit", "") +TRIPLE_ENV(ClosestHit, "closesthit", "") +TRIPLE_ENV(Miss, "miss", "") +TRIPLE_ENV(Callable, "callable", "") +TRIPLE_ENV(Mesh, "mesh", "") +TRIPLE_ENV(Amplification, "amplification", "") +TRIPLE_ENV(RootSignature, "rootsignature", "") +TRIPLE_ENV(OpenHOS, "ohos", "") +TRIPLE_ENV(PAuthTest, "pauthtest", "") +TRIPLE_ENV(LLVM, "llvm", "") +TRIPLE_ENV(Mlibc, "mlibc", "") +TRIPLE_ENV(MTIA, "mtia", "") #undef TRIPLE_ENV diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp index af15ab93b238..1e421c428d8b 100644 --- a/llvm/lib/TargetParser/Triple.cpp +++ b/llvm/lib/TargetParser/Triple.cpp @@ -392,7 +392,7 @@ StringRef Triple::getOSTypeName(OSType Kind) { switch (Kind) { case UnknownOS: return "unknown"; -#define TRIPLE_OS(Enum, Name) \ +#define TRIPLE_OS(Enum, Name, CMakeName) \ case Enum: \ return Name; #include "llvm/TargetParser/TripleName.def" @@ -405,7 +405,7 @@ StringRef Triple::getEnvironmentTypeName(EnvironmentType Kind) { switch (Kind) { case UnknownEnvironment: return "unknown"; -#define TRIPLE_ENV(Enum, Name) \ +#define TRIPLE_ENV(Enum, Name, CMakeOverride) \ case Enum: \ return Name; #include "llvm/TargetParser/TripleName.def" @@ -703,7 +703,7 @@ static Triple::VendorType parseVendor(StringRef VendorName) { static Triple::OSType parseOS(StringRef OSName) { return StringSwitch(OSName) -#define TRIPLE_OS(Enum, Name) .StartsWith(Name, Triple::Enum) +#define TRIPLE_OS(Enum, Name, CMakeName) .StartsWith(Name, Triple::Enum) #define TRIPLE_OS_ALIAS(Enum, AliasName) .StartsWith(AliasName, Triple::Enum) #include "llvm/TargetParser/TripleName.def" .Default(Triple::UnknownOS); @@ -711,7 +711,7 @@ static Triple::OSType parseOS(StringRef OSName) { static Triple::EnvironmentType parseEnvironment(StringRef EnvironmentName) { return StringSwitch(EnvironmentName) -#define TRIPLE_ENV(Enum, Name) .StartsWith(Name, Triple::Enum) +#define TRIPLE_ENV(Enum, Name, CMakeOverride) .StartsWith(Name, Triple::Enum) #include "llvm/TargetParser/TripleName.def" .Default(Triple::UnknownEnvironment); } diff --git a/llvm/test/tools/TargetParser/get-triple-system-name.test b/llvm/test/tools/TargetParser/get-triple-system-name.test new file mode 100644 index 000000000000..8fac8c316890 --- /dev/null +++ b/llvm/test/tools/TargetParser/get-triple-system-name.test @@ -0,0 +1,6 @@ +# Run the unit tests for get_triple_system_name.py, which parses +# TripleName.def and must stay in sync with llvm::Triple. + +# RUN: "%python" %S/get_triple_system_name_test.py 2>&1 | FileCheck %s + +# CHECK: OK diff --git a/llvm/test/tools/TargetParser/get_triple_system_name_test.py b/llvm/test/tools/TargetParser/get_triple_system_name_test.py new file mode 100644 index 000000000000..c6ffd1c7fbe6 --- /dev/null +++ b/llvm/test/tools/TargetParser/get_triple_system_name_test.py @@ -0,0 +1,206 @@ +# ===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ===----------------------------------------------------------------------===## +"""Tests for get_triple_system_name. + +The parser must stay in sync with TripleName.def, and system_name() must +classify the same OS that Triple::normalize picks, including for the +unnormalized/legacy triple shapes the runtimes build may feed in. +""" + +import os +import sys +import unittest + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "utils")) + +import get_triple_system_name as g + + +class Test(unittest.TestCase): + def setUp(self): + self.names = g.parse(g.find_def_file()) + + def test_all_def_rows_parsed(self): + # Guard against the regex silently dropping a .def row the C++ accepts. + with open(g.find_def_file()) as f: + lines = [l for l in f if "#define" not in l] + + def count(macro): + return sum(1 for l in lines if l.lstrip().startswith(macro + "(")) + + self.assertEqual( + len(self.names.os), count("TRIPLE_OS") + count("TRIPLE_OS_ALIAS") + ) + self.assertEqual(len(self.names.env), count("TRIPLE_ENV")) + + def test_prefix_ordering(self): + # A name that is a prefix of a LATER name would shadow it under the + # StartsWith matching both the C++ and this script rely on. + for table in (self.names.os, self.names.env): + names = [n for n, _ in table] + for i, short in enumerate(names): + for longer in names[i + 1 :]: + self.assertFalse( + longer.startswith(short) and longer != short, + f"'{short}' precedes and shadows '{longer}'", + ) + + def test_every_os_row(self): + # Data-driven: a canonical arch-unknown- triple must map to the + # CMake name the .def declares for every OS (and alias) row. This is + # the exhaustive counterpart to the hand-picked cases below; it fails + # automatically when a new OS is added without a CMake mapping. + for name, cmake_name in self.names.os: + # firmware/driverkit/bridgeos also have vendor-specific behavior + # exercised separately; here the plain arch-unknown form suffices. + triple = f"arch-unknown-{name}" + self.assertEqual(g.system_name(self.names, triple), cmake_name, triple) + + def test_known_triples(self): + cases = { + "x86_64-unknown-linux-gnu": "Linux", + "arm64-apple-darwin": "Darwin", + "s390x-ibm-zos": "OS390", + "aarch64-unknown-linux-android": "Android", + "x86_64-pc-windows-cygnus": "CYGWIN", + "wasm32-unknown-wasip1": "WASI", + "arm64-apple-ios": "iOS", + "riscv64-unknown-elf": "Generic", + } + for triple, want in cases.items(): + self.assertEqual(g.system_name(self.names, triple), want, triple) + + def test_os_aliases(self): + # The TRIPLE_OS_ALIAS rows resolve to their target OS's CMake name. + cases = { + "x86_64-apple-macos": "Darwin", # -> MacOSX + "x86_64-pc-win32": "Windows", # -> Win32 + "arm64-apple-visionos": "visionOS", # -> XROS + } + for triple, want in cases.items(): + self.assertEqual(g.system_name(self.names, triple), want, triple) + + def test_non_canonical_triples(self): + # Vendor omitted / non-standard, os or env not in the fixed position, + # version suffixes, and GCC-legacy spellings. + cases = { + "aarch64-linux-android21": "Android", + "aarch64-unknown-linux-android21": "Android", + "arm-linux-androideabi": "Android", + "armv7a-linux-androideabi29": "Android", + # Vendor omitted, os in the vendor slot. + "x86_64-linux-gnu": "Linux", + "riscv64-linux-gnu": "Linux", + # OS version suffix. + "x86_64-apple-macosx10.15": "Darwin", + "armv7-apple-ios13.0": "iOS", + # Non-standard vendor, still resolvable by os component. + "x86_64-pc-freebsd14": "FreeBSD", + } + for triple, want in cases.items(): + self.assertEqual(g.system_name(self.names, triple), want, triple) + + def test_matches_normalize_funky_triples(self): + # The "real-world funky triples" from TripleTest.cpp's Normalization + # test. system_name must classify the same OS that Triple::normalize + # picks, including os-in-vendor-slot and GCC-legacy windows spellings + # (mingw* -> windows-gnu, cygwin*/msys -> windows-cygnus). + cases = { + "i386-mingw32": "Windows", # -> i386-unknown-windows-gnu + "x86_64-linux-gnu": "Linux", # -> x86_64-unknown-linux-gnu + "i486-linux-gnu": "Linux", # -> i486-unknown-linux-gnu + "i386-redhat-linux": "Linux", # -> i386-redhat-linux + "i686-linux": "Linux", # -> i686-unknown-linux + "arm-none-eabi": "Generic", # -> arm-unknown-none-eabi (no OS) + "ve-linux": "Linux", # -> ve-unknown-linux + "wasm32-wasi": "WASI", # -> wasm32-unknown-wasi + "wasm64-wasi": "WASI", # -> wasm64-unknown-wasi + "x86_64-pc-cygwin": "CYGWIN", # -> x86_64-pc-windows-cygnus + "x86_64-pc-msys": "CYGWIN", # -> x86_64-pc-windows-cygnus + "x86_64-w64-mingw32": "Windows", # -> x86_64-w64-windows-gnu + "i686-w64-mingw32": "Windows", + } + for triple, want in cases.items(): + self.assertEqual(g.system_name(self.names, triple), want, triple) + + def test_normalize_special_cases(self): + # The OS/environment special-cases that Triple::normalize applies, one + # per category, to ensure the script tracks each rewrite it performs. + cases = { + # Win32 normalizes to "windows" regardless of the incoming spelling + # or msvc/gnu environment. + "x86_64-pc-windows": "Windows", + "x86_64-pc-windows-msvc": "Windows", + "x86_64-pc-windows-gnu": "Windows", + # mingw* -> windows-gnu; cygwin*/msys -> windows-cygnus. + "x86_64-w64-mingw32": "Windows", + "x86_64-pc-cygwin": "CYGWIN", + "x86_64-pc-msys": "CYGWIN", + # androideabi keeps the Android environment override. + "arm-linux-androideabi": "Android", + # The full Apple OS family. driverkit/bridgeos map to Darwin + # directly in the table (they are always Apple); firmware has no + # dedicated CMake name and maps to Darwin via the apple-vendor + # catch-all. + "x86_64-apple-macosx": "Darwin", + "arm-apple-darwin": "Darwin", + "arm-apple-ios": "iOS", + "arm-apple-tvos": "tvOS", + "arm-apple-watchos": "watchOS", + "arm-apple-xros": "visionOS", + "arm64-apple-visionos": "visionOS", + "arm-apple-driverkit": "Darwin", + "arm-apple-bridgeos": "Darwin", + "arm-apple-firmware": "Darwin", + } + for triple, want in cases.items(): + self.assertEqual(g.system_name(self.names, triple), want, triple) + + def test_firmware_requires_apple_vendor(self): + # firmware has no dedicated CMake name and only maps to Darwin for the + # apple vendor. With any other vendor it is Generic. (For a non-apple + # vendor Triple::normalize actually fatal-errors; here we only need to + # ensure we never claim Darwin.) + for vendor in ("none", "unknown", "pc"): + triple = f"arm-{vendor}-firmware" + self.assertEqual(g.system_name(self.names, triple), "Generic", triple) + + def test_driverkit_bridgeos_always_darwin(self): + # driverkit and bridgeos are always Apple, so they map to Darwin + # directly in the table regardless of the vendor component. + for os_name in ("driverkit", "bridgeos"): + for vendor in ("apple", "none", "unknown", "pc"): + triple = f"arm-{vendor}-{os_name}" + self.assertEqual(g.system_name(self.names, triple), "Darwin", triple) + + def test_bare_os_no_arch(self): + # A lone os component with no arch is a valid clang target (e.g. + # --target=darwin -> unknown-unknown-darwin), so it must classify by + # the first (and only) component rather than being skipped as the arch. + cases = { + "darwin": "Darwin", + "linux": "Linux", + "ios": "iOS", + "freebsd": "FreeBSD", + "wasi": "WASI", + "zos": "OS390", + "mingw32": "Windows", + "cygwin": "CYGWIN", + } + for triple, want in cases.items(): + self.assertEqual(g.system_name(self.names, triple), want, triple) + + def test_no_classifiable_os_falls_back(self): + # Only when no component classifies as an OS does the caller fall back + # to the host system name. + self.assertIsNone(g.system_name(self.names, "x86_64")) + self.assertIsNone(g.system_name(self.names, "arm-none")) + + +if __name__ == "__main__": + unittest.main() diff --git a/llvm/utils/get_triple_system_name.py b/llvm/utils/get_triple_system_name.py new file mode 100755 index 000000000000..369c57ada428 --- /dev/null +++ b/llvm/utils/get_triple_system_name.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""Derive a CMake system name from a target triple. + +The recognized OS and environment names are defined in +llvm/include/llvm/TargetParser/TripleName.def. This script parses that +file so the CMake runtimes build can map a (possibly unnormalized) +triple to a CMake system name without running a compiled tool. CMake +invokes it once per target with --triple and reads the printed name. + +Modes: + --triple TRIPLE Print the CMake system name for TRIPLE (empty if unknown or + the triple has too few components; the caller then falls back + to the host system name). + +Options: + --def-file PATH Path to TripleName.def to parse instead of the copy located + relative to this script (in llvm/include/llvm/TargetParser). + +""" + +import argparse +import os +import re +import sys + +_OS_RE = re.compile(r'^\s*TRIPLE_OS\(\s*(\w+)\s*,\s*"([^"]*)"\s*,\s*"([^"]*)"\s*\)') +_OS_ALIAS_RE = re.compile(r'^\s*TRIPLE_OS_ALIAS\(\s*(\w+)\s*,\s*"([^"]*)"\s*\)') +_ENV_RE = re.compile(r'^\s*TRIPLE_ENV\(\s*(\w+)\s*,\s*"([^"]*)"\s*,\s*"([^"]*)"\s*\)') + + +class TripleNames: + def __init__(self): + # Ordered lists of (prefix, value) preserving .def order, which matters + # for prefix matching (longer prefixes precede shorter ones). + self.os = [] # (name, cmake_name) + self.env = [] # (name, cmake_override) + + +def find_def_file(): + # This script lives in llvm/utils/, the .def in llvm/include/... + llvm_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + return os.path.join(llvm_dir, "include", "llvm", "TargetParser", "TripleName.def") + + +def parse(def_path): + names = TripleNames() + with open(def_path) as f: + lines = f.readlines() + + # First pass: map each OS enum to its CMake name so aliases can reuse it. + os_cmake = {} + for line in lines: + if m := _OS_RE.match(line): + enum, _name, cmake_name = m.groups() + os_cmake[enum] = cmake_name + + # Second pass: build the ordered lists (order matters for prefix matching). + for line in lines: + if m := _OS_RE.match(line): + _enum, name, cmake_name = m.groups() + names.os.append((name, cmake_name)) + elif m := _OS_ALIAS_RE.match(line): + enum, alias = m.groups() + names.os.append((alias, os_cmake.get(enum, "Generic"))) + elif m := _ENV_RE.match(line): + _enum, name, override = m.groups() + names.env.append((name, override)) + return names + + +def system_name(names, triple): + # Mirror the component classification of Triple::normalize: do not + # trust fixed positions. A triple may omit the vendor + # (aarch64-linux-android), put the os in the vendor slot + # (wasm32-wasi, i686-linux), or even be a bare os with no arch at + # all (clang accepts --target=darwin as unknown-unknown-darwin), + # so scan every component and use the first that classifies. No + # arch name is a prefix of any os/env name, so scanning the first + # component too cannot misclassify a real arch. This makes the + # result match what normalize would produce even for unnormalized + # inputs. + parts = triple.split("-") + + # Environment override takes precedence over the OS mapping + # (e.g. android wins over the linux in aarch64-linux-android), so + # scan for it first. + for comp in parts: + for name, override in names.env: + if override and comp.startswith(name): + return override + + # Find the first component that classifies as an OS. A specific + # (non-Generic) name wins immediately. A "Generic" match means the + # component is a recognized OS with no CMake equivalent. + found_generic_os = False + for comp in parts: + matched = False + for name, cmake_name in names.os: + if comp.startswith(name): + if cmake_name != "Generic": + return cmake_name + found_generic_os = True + matched = True + break + if matched: + break + + # Handle special case legacy os spellings that normalize rewrites + # to windows. + for comp in parts: + if comp.startswith("mingw"): + return "Windows" + if comp.startswith("cygwin") or comp.startswith("msys"): + return "CYGWIN" + + # Apple vendor triples with no specific CMake OS name (e.g. + # firmware) map to Darwin. + if len(parts) > 1 and parts[1] == "apple": + return "Darwin" + + if found_generic_os: + return "Generic" + + # No OS could be identified. Too few components to have one -> let + # the caller fall back to the host system name. + if len(parts) < 3: + return None + return "Generic" + + +def main(): + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--def-file", default=None, help="Path to TripleName.def") + ap.add_argument( + "--triple", required=True, help="Print the CMake system name for this triple" + ) + args = ap.parse_args() + + def_path = args.def_file or find_def_file() + names = parse(def_path) + + result = system_name(names, args.triple) + print(result if result is not None else "") + return 0 + + +if __name__ == "__main__": + sys.exit(main())