LLVM OpenMP 22.0.0git
message-converter.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
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-exception
9# //
10# //===----------------------------------------------------------------------===//
11#
12
13import argparse
14import os
15import platform
16import re
17import sys
18from libomputils import ScriptError, error
19
20
22 """Convenience class for handling the target platform for configuration/compilation"""
23
24 system_override = None
25 """
26 Target system name override by the user.
27 It follows the conventions from https://docs.python.org/3/library/platform.html#platform.system
28 """
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.system
34 """
35 TargetPlatform.system_override = override_system
36
37 def system():
38 """
39 Target System name.
40 It follows the conventions from https://docs.python.org/3/library/platform.html#platform.system
41 """
42 if TargetPlatform.system_override is None:
43 return platform.system()
44 return TargetPlatform.system_override
45
46
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 = filename
53 self.line = line
54
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 continue
68 print(m.group(1))
69
70
72 special = {
73 "n": "\n",
74 "t": "\t",
75 }
76
77 def __init__(self, lineNumber, name, text):
78 self.lineNumber = lineNumber
79 self.name = name
80 self.text = text
81
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 retval
92
93
95 """
96 Convenience class representing message data parsed from i18n/* files
97
98 Generate these objects using static create() factory method
99 """
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 = None
112 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.text
119 error(
120 'No "{}" detected in meta data' " for file {}".format(name, self.filename)
121 )
122
123 @staticmethod
124 def create(inputFile):
125 """Creates MessageData object from inputFile"""
126 data = MessageData()
127 data.filename = os.path.abspath(inputFile)
128 obsolete = 1
129 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 = None
135 currentKey = None
136 for lineNumber, line in enumerate(f, 1):
137 line = line.strip()
138 # Skip empty lines
139 if not line:
140 continue
141 # Skip comment lines
142 if line.startswith("#"):
143 continue
144 # Matched a section header
145 match = sectionRegex.search(line)
146 if match:
147 currentSection = match.group(1).lower()
148 if currentSection in data.sections:
150 inputFile,
151 lineNumber,
152 "section: {} already defined".format(currentSection),
153 )
154 data.sections[currentSection] = []
155 continue
156 # 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 += 1
165 value = match.group(2)
166 currentKey = key
167 data.sections[currentSection].append(
168 Message(lineNumber, key, value)
169 )
170 continue
171 # Matched a Continuation of string line
172 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 += value
180 continue
181 # Unknown line syntax
182 parse_error(inputFile, lineNumber, "bad line:\n{}".format(line))
183 return data
184
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_sections
201 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, base
221 )
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 = 0
246 # Insert empty placeholders if necessary
247 while i != len(sigs):
248 num = i + 1
249 if not sigs[i].startswith("%{}".format(num)):
250 sigs.insert(i, "%{}$-".format(num))
251 else:
252 i += 1
253 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 OSes
313
314 Encoding is in UTF-8
315 """
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 OS
337
338 Encoding is in UTF-16LE
339 """
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 = 0
355 messages = data.sections[section]
356 for message in messages:
357 n += 1
358 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 return
403 data = MessageData.create(commandArgs.inputfile)
404 prefix = commandArgs.prefix
405 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 file
__init__(self, lineNumber, name, text)
generate_enum_file(enumFile, prefix, data)
generate_default_messages_file(defaultFile, prefix, data)
generate_signature_file(signatureFile, data)
display_language_id(inputFile)
generate_message_file_unix(messageFile, data)
generate_message_file_windows(messageFile, data)
parse_error(filename, line, msg)
insert_header(f, data, commentChar="//")