brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.2 KiB · 5e2aeba Raw
428 lines · python
1#!/usr/bin/env python32 3#4# //===----------------------------------------------------------------------===//5# //6# // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.7# // See https://llvm.org/LICENSE.txt for license information.8# // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception9# //10# //===----------------------------------------------------------------------===//11#12 13import argparse14import os15import platform16import re17import sys18from libomputils import ScriptError, error19 20 21class TargetPlatform:22    """Convenience class for handling the target platform for configuration/compilation"""23 24    system_override = None25    """26    Target system name override by the user.27    It follows the conventions from https://docs.python.org/3/library/platform.html#platform.system28    """29 30    def set_system_override(override_system):31        """32        Set a system override for the target.33        Please follow the style from https://docs.python.org/3/library/platform.html#platform.system34        """35        TargetPlatform.system_override = override_system36 37    def system():38        """39        Target System name.40        It follows the conventions from https://docs.python.org/3/library/platform.html#platform.system41        """42        if TargetPlatform.system_override is None:43            return platform.system()44        return TargetPlatform.system_override45 46 47class ParseMessageDataError(ScriptError):48    """Convenience class for parsing message data file errors"""49 50    def __init__(self, filename, line, msg):51        super(ParseMessageDataError, self).__init__(msg)52        self.filename = filename53        self.line = line54 55 56def parse_error(filename, line, msg):57    raise ParseMessageDataError(filename, line, msg)58 59 60def display_language_id(inputFile):61    """Quickly parse file for LangId and print it"""62    regex = re.compile(r'^LangId\s+"([0-9]+)"')63    with open(inputFile, encoding="utf-8") as f:64        for line in f:65            m = regex.search(line)66            if not m:67                continue68            print(m.group(1))69 70 71class Message(object):72    special = {73        "n": "\n",74        "t": "\t",75    }76 77    def __init__(self, lineNumber, name, text):78        self.lineNumber = lineNumber79        self.name = name80        self.text = text81 82    def toSrc(self):83        if TargetPlatform.system().casefold() == "Windows".casefold():84            return re.sub(r"%([0-9])\$(s|l?[du])", r"%\1!\2!", self.text)85        return str(self.text)86 87    def toMC(self):88        retval = self.toSrc()89        for special, substitute in Message.special.items():90            retval = re.sub(r"\\{}".format(special), substitute, retval)91        return retval92 93 94class MessageData(object):95    """96    Convenience class representing message data parsed from i18n/* files97 98    Generate these objects using static create() factory method99    """100 101    sectionInfo = {102        "meta": {"short": "prp", "long": "meta", "set": 1, "base": 1 << 16},103        "strings": {"short": "str", "long": "strings", "set": 2, "base": 2 << 16},104        "formats": {"short": "fmt", "long": "formats", "set": 3, "base": 3 << 16},105        "messages": {"short": "msg", "long": "messages", "set": 4, "base": 4 << 16},106        "hints": {"short": "hnt", "long": "hints", "set": 5, "base": 5 << 16},107    }108    orderedSections = ["meta", "strings", "formats", "messages", "hints"]109 110    def __init__(self):111        self.filename = None112        self.sections = {}113 114    def getMeta(self, name):115        metaList = self.sections["meta"]116        for meta in metaList:117            if meta.name == name:118                return meta.text119        error(120            'No "{}" detected in meta data' " for file {}".format(name, self.filename)121        )122 123    @staticmethod124    def create(inputFile):125        """Creates MessageData object from inputFile"""126        data = MessageData()127        data.filename = os.path.abspath(inputFile)128        obsolete = 1129        sectionRegex = re.compile(r"-\*- ([a-zA-Z0-9_]+) -\*-")130        keyValueRegex = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*)\s+"(.*)"')131        moreValueRegex = re.compile(r'"(.*)"')132 133        with open(inputFile, "r", encoding="utf-8") as f:134            currentSection = None135            currentKey = None136            for lineNumber, line in enumerate(f, 1):137                line = line.strip()138                # Skip empty lines139                if not line:140                    continue141                # Skip comment lines142                if line.startswith("#"):143                    continue144                # Matched a section header145                match = sectionRegex.search(line)146                if match:147                    currentSection = match.group(1).lower()148                    if currentSection in data.sections:149                        parse_error(150                            inputFile,151                            lineNumber,152                            "section: {} already defined".format(currentSection),153                        )154                    data.sections[currentSection] = []155                    continue156                # Matched a Key "Value" line (most lines)157                match = keyValueRegex.search(line)158                if match:159                    if not currentSection:160                        parse_error(inputFile, lineNumber, "no section defined yet.")161                    key = match.group(1)162                    if key == "OBSOLETE":163                        key = "OBSOLETE{}".format(obsolete)164                        obsolete += 1165                    value = match.group(2)166                    currentKey = key167                    data.sections[currentSection].append(168                        Message(lineNumber, key, value)169                    )170                    continue171                # Matched a Continuation of string line172                match = moreValueRegex.search(line)173                if match:174                    value = match.group(1)175                    if not currentSection:176                        parse_error(inputFile, lineNumber, "no section defined yet.")177                    if not currentKey:178                        parse_error(inputFile, lineNumber, "no key defined yet.")179                    data.sections[currentSection][-1].text += value180                    continue181                # Unknown line syntax182                parse_error(inputFile, lineNumber, "bad line:\n{}".format(line))183        return data184 185 186def insert_header(f, data, commentChar="//"):187    f.write(188        "{0} Do not edit this file! {0}\n"189        "{0} The file was generated from"190        " {1} by {2}. {0}\n\n".format(191            commentChar,192            os.path.basename(data.filename),193            os.path.basename(__file__),194        )195    )196 197 198def generate_enum_file(enumFile, prefix, data):199    """Create the include file with message enums"""200    global g_sections201    with open(enumFile, "w") as f:202        insert_header(f, data)203        f.write(204            "enum {0}_id {1}\n"205            "\n"206            "    // A special id for absence of message.\n"207            "    {0}_null = 0,\n"208            "\n".format(prefix, "{")209        )210        for section in MessageData.orderedSections:211            messages = data.sections[section]212            info = MessageData.sectionInfo[section]213            shortName = info["short"]214            longName = info["long"]215            base = info["base"]216            setIdx = info["set"]217            f.write(218                "    // Set #{}, {}.\n"219                "    {}_{}_first = {},\n".format(220                    setIdx, longName, prefix, shortName, base221                )222            )223            for message in messages:224                f.write("    {}_{}_{},\n".format(prefix, shortName, message.name))225            f.write("    {}_{}_last,\n\n".format(prefix, shortName))226        f.write(227            "    {0}_xxx_lastest\n\n"228            "{1}; // enum {0}_id\n\n"229            "typedef enum {0}_id  {0}_id_t;\n\n\n"230            "// end of file //\n".format(prefix, "}")231        )232 233 234def generate_signature_file(signatureFile, data):235    """Create the signature file"""236    sigRegex = re.compile(r"(%[0-9]\$(s|l?[du]))")237    with open(signatureFile, "w") as f:238        f.write("// message catalog signature file //\n\n")239        for section in MessageData.orderedSections:240            messages = data.sections[section]241            longName = MessageData.sectionInfo[section]["long"]242            f.write("-*- {}-*-\n\n".format(longName.upper()))243            for message in messages:244                sigs = sorted(list(set([a for a, b in sigRegex.findall(message.text)])))245                i = 0246                # Insert empty placeholders if necessary247                while i != len(sigs):248                    num = i + 1249                    if not sigs[i].startswith("%{}".format(num)):250                        sigs.insert(i, "%{}$-".format(num))251                    else:252                        i += 1253                f.write("{:<40} {}\n".format(message.name, " ".join(sigs)))254            f.write("\n")255        f.write("// end of file //\n")256 257 258def generate_default_messages_file(defaultFile, prefix, data):259    """Create the include file with message strings organized"""260    with open(defaultFile, "w", encoding="utf-8") as f:261        insert_header(f, data)262        for section in MessageData.orderedSections:263            f.write(264                "static char const *\n"265                "__{}_default_{}[] =\n"266                "    {}\n"267                "        NULL,\n".format(prefix, section, "{")268            )269            messages = data.sections[section]270            for message in messages:271                f.write('        "{}",\n'.format(message.toSrc()))272            f.write("        NULL\n" "    {};\n\n".format("}"))273        f.write(274            "struct kmp_i18n_section {0}\n"275            "    int           size;\n"276            "    char const ** str;\n"277            "{1}; // struct kmp_i18n_section\n"278            "typedef struct kmp_i18n_section  kmp_i18n_section_t;\n\n"279            "static kmp_i18n_section_t\n"280            "__{2}_sections[] =\n"281            "    {0}\n"282            "        {0} 0, NULL {1},\n".format("{", "}", prefix)283        )284 285        for section in MessageData.orderedSections:286            messages = data.sections[section]287            f.write(288                "        {} {}, __{}_default_{} {},\n".format(289                    "{", len(messages), prefix, section, "}"290                )291            )292        numSections = len(MessageData.orderedSections)293        f.write(294            "        {0} 0, NULL {1}\n"295            "    {1};\n\n"296            "struct kmp_i18n_table {0}\n"297            "    int                   size;\n"298            "    kmp_i18n_section_t *  sect;\n"299            "{1}; // struct kmp_i18n_table\n"300            "typedef struct kmp_i18n_table  kmp_i18n_table_t;\n\n"301            "static kmp_i18n_table_t __kmp_i18n_default_table =\n"302            "    {0}\n"303            "        {3},\n"304            "        __{2}_sections\n"305            "    {1};\n\n"306            "// end of file //\n".format("{", "}", prefix, numSections)307        )308 309 310def generate_message_file_unix(messageFile, data):311    """312    Create the message file for Unix OSes313 314    Encoding is in UTF-8315    """316    with open(messageFile, "w", encoding="utf-8") as f:317        insert_header(f, data, commentChar="$")318        f.write('$quote "\n\n')319        for section in MessageData.orderedSections:320            setIdx = MessageData.sectionInfo[section]["set"]321            f.write(322                "$ ------------------------------------------------------------------------------\n"323                "$ {}\n"324                "$ ------------------------------------------------------------------------------\n\n"325                "$set {}\n\n".format(section, setIdx)326            )327            messages = data.sections[section]328            for num, message in enumerate(messages, 1):329                f.write('{} "{}"\n'.format(num, message.toSrc()))330            f.write("\n")331        f.write("\n$ end of file $")332 333 334def generate_message_file_windows(messageFile, data):335    """336    Create the message file for Windows OS337 338    Encoding is in UTF-16LE339    """340    language = data.getMeta("Language")341    langId = data.getMeta("LangId")342    with open(messageFile, "w", encoding="utf-16-le") as f:343        insert_header(f, data, commentChar=";")344        f.write("\nLanguageNames = ({0}={1}:msg_{1})\n\n".format(language, langId))345        f.write("FacilityNames=(\n")346        for section in MessageData.orderedSections:347            setIdx = MessageData.sectionInfo[section]["set"]348            shortName = MessageData.sectionInfo[section]["short"]349            f.write(" {}={}\n".format(shortName, setIdx))350        f.write(")\n\n")351 352        for section in MessageData.orderedSections:353            shortName = MessageData.sectionInfo[section]["short"]354            n = 0355            messages = data.sections[section]356            for message in messages:357                n += 1358                f.write(359                    "MessageId={}\n"360                    "Facility={}\n"361                    "Language={}\n"362                    "{}\n.\n\n".format(n, shortName, language, message.toMC())363                )364        f.write("\n; end of file ;")365 366 367def main():368    parser = argparse.ArgumentParser(description="Generate message data files")369    parser.add_argument(370        "--lang-id",371        action="store_true",372        help="Print language identifier of the message catalog source file",373    )374    parser.add_argument(375        "--prefix",376        default="kmp_i18n",377        help="Prefix to be used for all C identifiers (type and variable names)"378        " in enum and default message files.",379    )380    parser.add_argument("--enum", metavar="FILE", help="Generate enum file named FILE")381    parser.add_argument(382        "--default", metavar="FILE", help="Generate default messages file named FILE"383    )384    parser.add_argument(385        "--signature", metavar="FILE", help="Generate signature file named FILE"386    )387    parser.add_argument(388        "--message", metavar="FILE", help="Generate message file named FILE"389    )390    parser.add_argument(391        "--target-system-override",392        metavar="TARGET_SYSTEM_NAME",393        help="Target System override.\n"394        "By default the target system is the host system\n"395        "See possible values at https://docs.python.org/3/library/platform.html#platform.system",396    )397    parser.add_argument("inputfile")398    commandArgs = parser.parse_args()399 400    if commandArgs.lang_id:401        display_language_id(commandArgs.inputfile)402        return403    data = MessageData.create(commandArgs.inputfile)404    prefix = commandArgs.prefix405    if commandArgs.target_system_override:406        TargetPlatform.set_system_override(commandArgs.target_system_override)407    if commandArgs.enum:408        generate_enum_file(commandArgs.enum, prefix, data)409    if commandArgs.default:410        generate_default_messages_file(commandArgs.default, prefix, data)411    if commandArgs.signature:412        generate_signature_file(commandArgs.signature, data)413    if commandArgs.message:414        if TargetPlatform.system().casefold() == "Windows".casefold():415            generate_message_file_windows(commandArgs.message, data)416        else:417            generate_message_file_unix(commandArgs.message, data)418 419 420if __name__ == "__main__":421    try:422        main()423    except ScriptError as e:424        print("error: {}".format(e))425        sys.exit(1)426 427# end of file428