Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 1 | """ |
| 2 | This script processes the output from the C preprocessor and extracts all |
| 3 | qstr. Each qstr is transformed into a qstr definition of the form 'Q(...)'. |
| 4 | |
| 5 | This script works with Python 2.6, 2.7, 3.3 and 3.4. |
| 6 | """ |
| 7 | |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 8 | import re |
| 9 | import argparse |
| 10 | import os |
| 11 | |
| 12 | # Blacklist of qstrings that are specially handled in further |
| 13 | # processing and should be ignored |
| 14 | QSTRING_BLACK_LIST = {'NULL', 'number_of', } |
| 15 | |
| 16 | |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 17 | def write_out(fname, output): |
| 18 | if output: |
stijn | 9264d42 | 2016-04-23 18:36:07 +0200 | [diff] [blame^] | 19 | for m, r in [("/", "__"), ("\\", "__"), (":", "@"), ("..", "@@")]: |
| 20 | fname = fname.replace(m, r) |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 21 | with open(args.output_dir + "/" + fname + ".qstr", "w") as f: |
| 22 | f.write("\n".join(output) + "\n") |
| 23 | |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 24 | def process_file(f): |
| 25 | output = [] |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 26 | last_fname = None |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 27 | for line in f: |
stijn | 9264d42 | 2016-04-23 18:36:07 +0200 | [diff] [blame^] | 28 | # match gcc-like output (# n "file") and msvc-like output (#line n "file") |
| 29 | if line and (line[0:2] == "# " or line[0:5] == "#line"): |
| 30 | m = re.match(r"#[line]*\s\d+\s\"([^\"]+)\"", line) |
| 31 | assert m is not None |
| 32 | fname = m.group(1) |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 33 | if fname[0] == "/" or not fname.endswith(".c"): |
| 34 | continue |
| 35 | if fname != last_fname: |
| 36 | write_out(last_fname, output) |
| 37 | output = [] |
| 38 | last_fname = fname |
| 39 | continue |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 40 | for match in re.findall(r'MP_QSTR_[_a-zA-Z0-9]+', line): |
| 41 | name = match.replace('MP_QSTR_', '') |
| 42 | if name not in QSTRING_BLACK_LIST: |
| 43 | output.append('Q(' + name + ')') |
| 44 | |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 45 | write_out(last_fname, output) |
| 46 | return "" |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 47 | |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 48 | |
| 49 | def cat_together(): |
| 50 | import glob |
| 51 | import hashlib |
| 52 | hasher = hashlib.md5() |
| 53 | all_lines = [] |
| 54 | outf = open(args.output_dir + "/out", "wb") |
| 55 | for fname in glob.glob(args.output_dir + "/*.qstr"): |
| 56 | with open(fname, "rb") as f: |
| 57 | lines = f.readlines() |
| 58 | all_lines += lines |
| 59 | all_lines.sort() |
| 60 | all_lines = b"\n".join(all_lines) |
| 61 | outf.write(all_lines) |
| 62 | outf.close() |
| 63 | hasher.update(all_lines) |
| 64 | new_hash = hasher.hexdigest() |
| 65 | #print(new_hash) |
| 66 | old_hash = None |
| 67 | try: |
| 68 | with open(args.output_file + ".hash") as f: |
| 69 | old_hash = f.read() |
| 70 | except IOError: |
| 71 | pass |
| 72 | if old_hash != new_hash: |
| 73 | print("QSTR updated") |
stijn | 9264d42 | 2016-04-23 18:36:07 +0200 | [diff] [blame^] | 74 | try: |
| 75 | # rename below might fail if file exists |
| 76 | os.remove(args.output_file) |
| 77 | except: |
| 78 | pass |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 79 | os.rename(args.output_dir + "/out", args.output_file) |
| 80 | with open(args.output_file + ".hash", "w") as f: |
| 81 | f.write(new_hash) |
| 82 | else: |
| 83 | print("QSTR not updated") |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 84 | |
| 85 | |
| 86 | if __name__ == "__main__": |
| 87 | parser = argparse.ArgumentParser(description='Generates qstr definitions from a specified source') |
| 88 | |
Paul Sokolovsky | 1b60a6d | 2016-04-19 14:39:08 +0300 | [diff] [blame] | 89 | parser.add_argument('command', |
| 90 | help='Command (split/cat)') |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 91 | parser.add_argument('input_filename', |
| 92 | help='Name of the input file (when not specified, the script reads standard input)') |
| 93 | parser.add_argument('output_dir', |
| 94 | help='Output directory to store individual qstr files') |
| 95 | parser.add_argument('output_file', |
| 96 | help='Name of the output file with collected qstrs') |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 97 | |
| 98 | args = parser.parse_args() |
Paul Sokolovsky | c618f91 | 2016-04-19 11:30:06 +0300 | [diff] [blame] | 99 | try: |
| 100 | os.makedirs(args.output_dir) |
| 101 | except OSError: |
| 102 | pass |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 103 | |
Paul Sokolovsky | 1b60a6d | 2016-04-19 14:39:08 +0300 | [diff] [blame] | 104 | if args.command == "split": |
| 105 | with open(args.input_filename) as infile: |
| 106 | process_file(infile) |
Pavel Moravec | dbbf082 | 2016-03-11 16:12:59 +0000 | [diff] [blame] | 107 | |
Paul Sokolovsky | 1b60a6d | 2016-04-19 14:39:08 +0300 | [diff] [blame] | 108 | if args.command == "cat": |
| 109 | cat_together() |