| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- """
- This script processes the output from the C preprocessor and extracts all
- qstr. Each qstr is transformed into a qstr definition of the form 'Q(...)'.
- This script works with Python 2.6, 2.7, 3.3 and 3.4.
- """
- from __future__ import print_function
- import re
- import subprocess
- import sys
- import io
- import os
- # Extract MP_QSTR_FOO macros.
- _MODE_QSTR = "qstr"
- # Extract MP_COMPRESSED_ROM_TEXT("") macros. (Which come from MP_ERROR_TEXT)
- _MODE_COMPRESS = "compress"
- def preprocess():
- if any(src in args.dependencies for src in args.changed_sources):
- sources = args.sources
- elif any(args.changed_sources):
- sources = args.changed_sources
- else:
- sources = args.sources
- csources = []
- cxxsources = []
- for source in sources:
- if source.endswith(".cpp"):
- cxxsources.append(source)
- else:
- csources.append(source)
- try:
- os.makedirs(os.path.dirname(args.output[0]))
- except OSError:
- pass
- with open(args.output[0], "w") as out_file:
- if csources:
- subprocess.check_call(args.pp + args.cflags + csources, stdout=out_file)
- if cxxsources:
- subprocess.check_call(args.pp + args.cxxflags + cxxsources, stdout=out_file)
- def write_out(fname, output):
- if output:
- for m, r in [("/", "__"), ("\\", "__"), (":", "@"), ("..", "@@")]:
- fname = fname.replace(m, r)
- with open(args.output_dir + "/" + fname + "." + args.mode, "w") as f:
- f.write("\n".join(output) + "\n")
- def process_file(f):
- re_line = re.compile(r"#[line]*\s\d+\s\"([^\"]+)\"")
- if args.mode == _MODE_QSTR:
- re_match = re.compile(r"MP_QSTR_[_a-zA-Z0-9]+")
- elif args.mode == _MODE_COMPRESS:
- re_match = re.compile(r'MP_COMPRESSED_ROM_TEXT\("([^"]*)"\)')
- output = []
- last_fname = None
- for line in f:
- if line.isspace():
- continue
- # match gcc-like output (# n "file") and msvc-like output (#line n "file")
- if line.startswith(("# ", "#line")):
- m = re_line.match(line)
- assert m is not None
- fname = m.group(1)
- if os.path.splitext(fname)[1] not in [".c", ".cpp"]:
- continue
- if fname != last_fname:
- write_out(last_fname, output)
- output = []
- last_fname = fname
- continue
- for match in re_match.findall(line):
- if args.mode == _MODE_QSTR:
- name = match.replace("MP_QSTR_", "")
- output.append("Q(" + name + ")")
- elif args.mode == _MODE_COMPRESS:
- output.append(match)
- if last_fname:
- write_out(last_fname, output)
- return ""
- def cat_together():
- import glob
- import hashlib
- hasher = hashlib.md5()
- all_lines = []
- outf = open(args.output_dir + "/out", "wb")
- for fname in glob.glob(args.output_dir + "/*." + args.mode):
- with open(fname, "rb") as f:
- lines = f.readlines()
- all_lines += lines
- all_lines.sort()
- all_lines = b"\n".join(all_lines)
- outf.write(all_lines)
- outf.close()
- hasher.update(all_lines)
- new_hash = hasher.hexdigest()
- # print(new_hash)
- old_hash = None
- try:
- with open(args.output_file + ".hash") as f:
- old_hash = f.read()
- except IOError:
- pass
- mode_full = "QSTR"
- if args.mode == _MODE_COMPRESS:
- mode_full = "Compressed data"
- if old_hash != new_hash:
- print(mode_full, "updated")
- try:
- # rename below might fail if file exists
- os.remove(args.output_file)
- except:
- pass
- os.rename(args.output_dir + "/out", args.output_file)
- with open(args.output_file + ".hash", "w") as f:
- f.write(new_hash)
- else:
- print(mode_full, "not updated")
- if __name__ == "__main__":
- if len(sys.argv) < 6:
- print("usage: %s command mode input_filename output_dir output_file" % sys.argv[0])
- sys.exit(2)
- class Args:
- pass
- args = Args()
- args.command = sys.argv[1]
- if args.command == "pp":
- named_args = {
- s: []
- for s in [
- "pp",
- "output",
- "cflags",
- "cxxflags",
- "sources",
- "changed_sources",
- "dependencies",
- ]
- }
- for arg in sys.argv[1:]:
- if arg in named_args:
- current_tok = arg
- else:
- named_args[current_tok].append(arg)
- if not named_args["pp"] or len(named_args["output"]) != 1:
- print("usage: %s %s ..." % (sys.argv[0], " ... ".join(named_args)))
- sys.exit(2)
- for k, v in named_args.items():
- setattr(args, k, v)
- preprocess()
- sys.exit(0)
- args.mode = sys.argv[2]
- args.input_filename = sys.argv[3] # Unused for command=cat
- args.output_dir = sys.argv[4]
- args.output_file = None if len(sys.argv) == 5 else sys.argv[5] # Unused for command=split
- if args.mode not in (_MODE_QSTR, _MODE_COMPRESS):
- print("error: mode %s unrecognised" % sys.argv[2])
- sys.exit(2)
- try:
- os.makedirs(args.output_dir)
- except OSError:
- pass
- if args.command == "split":
- with io.open(args.input_filename, encoding="utf-8") as infile:
- process_file(infile)
- if args.command == "cat":
- cat_together()
|