cmake: Derive CMake system name from a triple via new mechanism (#208773)

This commit is contained in:
Matt Arsenault 2026-08-01 09:41:32 +02:00 committed by GitHub
parent c27bee245f
commit 6b98f82fe9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 522 additions and 114 deletions

View file

@ -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()

View file

@ -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)
// 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)
// 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

View file

@ -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<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);
@ -711,7 +711,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);
}

View file

@ -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

View file

@ -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-<os> 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()

View file

@ -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())