https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/208773
This is a reimplementation of the cmake functionality first implemented in 6e4e181c83, which has now been reverted twice. Implement the raw cmake functionality without introducing the uses yet. The runtimes build needs to translate the build target (configured with a target triple) to cmake's naming scheme, to use for CMAKE_SYSTEM_NAME. Use the OS/environment list from TargetParser as the source of truth; add an additional entry for the cmake name to ensure the build system and compiler always recognize the same set of names. Upgrade the previous cmake regexes to a new python script which parses the authoritative def file. The new script logic should match Triple::normalize's permissiveness for various legacy and malformed triple shapes. This should be more maintainable than the previous cmake regexes, since there's now a unit test mirroring the triple unit test. Co-Authored-By: Claude (Opus 4.8) <[email protected]> >From e6dcf892c8c988430b0369da8b10f370f419a419 Mon Sep 17 00:00:00 2001 From: Matt Arsenault <[email protected]> Date: Fri, 10 Jul 2026 15:13:36 +0200 Subject: [PATCH] cmake: Derive CMake system name from a triple via new mechanism This is a reimplementation of the cmake functionality first implemented in 6e4e181c83, which has now been reverted twice. Implement the raw cmake functionality without introducing the uses yet. The runtimes build needs to translate the build target (configured with a target triple) to cmake's naming scheme, to use for CMAKE_SYSTEM_NAME. Use the OS/environment list from TargetParser as the source of truth; add an additional entry for the cmake name to ensure the build system and compiler always recognize the same set of names. Upgrade the previous cmake regexes to a new python script which parses the authoritative def file. The new script logic should match Triple::normalize's permissiveness for various legacy and malformed triple shapes. This should be more maintainable than the previous cmake regexes, since there's now a unit test mirroring the triple unit test. Co-Authored-By: Claude (Opus 4.8) <[email protected]> --- cmake/Modules/GetTripleCMakeSystemName.cmake | 40 +++ llvm/include/llvm/TargetParser/TripleName.def | 228 +++++++++--------- llvm/lib/TargetParser/Triple.cpp | 8 +- .../TargetParser/get-triple-system-name.test | 6 + llvm/utils/get_triple_system_name.py | 157 ++++++++++++ llvm/utils/get_triple_system_name_test.py | 198 +++++++++++++++ 6 files changed, 523 insertions(+), 114 deletions(-) create mode 100644 cmake/Modules/GetTripleCMakeSystemName.cmake create mode 100644 llvm/test/tools/TargetParser/get-triple-system-name.test create mode 100755 llvm/utils/get_triple_system_name.py create mode 100755 llvm/utils/get_triple_system_name_test.py diff --git a/cmake/Modules/GetTripleCMakeSystemName.cmake b/cmake/Modules/GetTripleCMakeSystemName.cmake new file mode 100644 index 0000000000000..8ad725f34175c --- /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(<triple> <out_var>) +# +# Sets <out_var> 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 bbc03b3188019..95026bd39db78 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 356782c1f6ad4..fbeeb35405dba 100644 --- a/llvm/lib/TargetParser/Triple.cpp +++ b/llvm/lib/TargetParser/Triple.cpp @@ -435,7 +435,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" @@ -448,7 +448,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" @@ -746,7 +746,7 @@ static Triple::VendorType parseVendor(StringRef VendorName) { static Triple::OSType parseOS(StringRef OSName) { return StringSwitch<Triple::OSType>(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); @@ -754,7 +754,7 @@ static Triple::OSType parseOS(StringRef OSName) { static Triple::EnvironmentType parseEnvironment(StringRef EnvironmentName) { return StringSwitch<Triple::EnvironmentType>(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 0000000000000..45190183fcf70 --- /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" %llvm_src_root/utils/get_triple_system_name_test.py 2>&1 | FileCheck %s + +# CHECK: OK diff --git a/llvm/utils/get_triple_system_name.py b/llvm/utils/get_triple_system_name.py new file mode 100755 index 0000000000000..a87300a6423cc --- /dev/null +++ b/llvm/utils/get_triple_system_name.py @@ -0,0 +1,157 @@ +#!/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: + m = _OS_RE.match(line) + if m: + 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: + m = _OS_RE.match(line) + if m: + _enum, name, cmake_name = m.groups() + names.os.append((name, cmake_name)) + continue + m = _OS_ALIAS_RE.match(line) + if m: + enum, alias = m.groups() + names.os.append((alias, os_cmake.get(enum, "Generic"))) + continue + m = _ENV_RE.match(line) + if m: + _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("-") + rest = parts + + # 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 rest: + 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 rest: + 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 rest: + 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()) diff --git a/llvm/utils/get_triple_system_name_test.py b/llvm/utils/get_triple_system_name_test.py new file mode 100755 index 0000000000000..ebbb79ce925b8 --- /dev/null +++ b/llvm/utils/get_triple_system_name_test.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +# ===----------------------------------------------------------------------===## +# +# 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. + +These guard against the Python .def parser drifting from TripleNames.def +The C++ side is kept in sync by the compiler (the +generated switch cases must cover every enum), so the risk here is the regex +silently dropping a row the C++ accepts. We check that every TRIPLE_OS / +TRIPLE_OS_ALIAS / TRIPLE_ENV macro line in the .def is captured by the parser. +""" + +import re +import unittest + +import get_triple_system_name as g + + +class ParseTest(unittest.TestCase): + def setUp(self): + self.def_path = g.find_def_file() + self.names = g.parse(self.def_path) + with open(self.def_path) as f: + self.lines = f.readlines() + + def _count(self, macro): + # Count real macro invocations, skipping the #define lines. + pat = re.compile(r"^\s*" + macro + r"\(") + return sum( + 1 + for line in self.lines + if pat.match(line) and "#define" not in line + ) + + def test_all_os_rows_parsed(self): + expected = self._count("TRIPLE_OS") + self._count("TRIPLE_OS_ALIAS") + self.assertEqual(len(self.names.os), expected) + + def test_all_env_rows_parsed(self): + self.assertEqual(len(self.names.env), self._count("TRIPLE_ENV")) + + def test_no_empty_names(self): + for name, _ in self.names.os: + self.assertTrue(name) + for name, _ in self.names.env: + self.assertTrue(name) + + def test_prefix_ordering(self): + # A name that is a prefix of an earlier name is fine; a name that is a + # prefix of a LATER name would shadow it under StartsWith matching. + 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_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_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 in ("driverkit", "bridgeos"): + for vendor in ("apple", "none", "unknown", "pc"): + triple = f"arm-{vendor}-{os}" + 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_too_few_components_returns_none(self): + # Only when there is no classifiable OS in any component 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() _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
