| #!/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()) |