#!/usr/bin/env python3 """Generate the compilation target feature printing source code. The source code for detecting target features is heavily redundant and copy-pasted, and is easier to maintain using a generative script. This script creates the source and the include files in its current directory. """ import argparse from pathlib import Path from typing import Dict, List, Iterable _CPP_BOILERPLATE: str = """\ #include #define TO_STRING_EXP(DEF) #DEF #define TO_STRING(DEF) TO_STRING_EXP(DEF) """ _FEATURES = { "Aarch64": [ "__ARM_FEATURE_AES", "__ARM_FEATURE_BTI", "__ARM_FEATURE_CRC32", "__ARM_FEATURE_CRYPTO", "__ARM_FEATURE_PAC_DEFAULT", "__ARM_FEATURE_SHA2", "__ARM_FEATURE_SHA3", "__ARM_FEATURE_SHA512", ], "Arm32": [ "__ARM_ARCH_ISA_THUMB", "__ARM_FEATURE_AES", "__ARM_FEATURE_BTI", "__ARM_FEATURE_CRC32", "__ARM_FEATURE_CRYPTO", "__ARM_FEATURE_PAC_DEFAULT", "__ARM_FEATURE_SHA2", ], "X86": [ "__AES__", "__AVX__", "__CRC32__", "__POPCNT__", "__SHA512__", "__SHA__", ], "Riscv": [ "__riscv_vector", ], } def _make_function_sig(name: str) -> str: return f"void print{name}TargetFeatures()" def check_template(define: str) -> List[str]: return [ f"#if defined({define})", f' printf("%s=%s\\n", TO_STRING_EXP({define}), TO_STRING({define}));', "#else", f' printf("%s not defined\\n", TO_STRING_EXP({define}));', "#endif", ] def generate_cpp_file(define_mapping: Dict[str, List[str]]) -> List[str]: out: List[str] = _CPP_BOILERPLATE.split("\n") for target, defines in define_mapping.items(): out.append("") out.extend(generate_print_function(target, defines)) return out def generate_print_function(name: str, defines: List[str]) -> List[str]: """Generate a printTargetFeatures function.""" function_body = [_make_function_sig(name) + " {"] for d in defines: function_body.extend(check_template(d)) function_body.append("}") return function_body def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "cpp_in", type=Path, help="Output path to generate the cpp file.", ) return parser.parse_args() def main() -> None: args = parse_args() printer_cpp_filepath = args.cpp_in printer_cpp_filepath.write_text( "\n".join(generate_cpp_file(_FEATURES)), encoding="utf-8" ) if __name__ == "__main__": main()