| #!/usr/bin/env python |
| # Copyright 2018 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import glob |
| import os |
| import re |
| import sys |
| import subprocess |
| |
| parser = argparse.ArgumentParser( |
| description="Generates dart constants from a parsing of corresponding values in zircon") |
| parser.add_argument('--dry-run', |
| help='Whether to run in dry-run mode; if true, no file will be generated', |
| action='store_true') |
| parser.add_argument('--errors', |
| help='Path to the zircon error header file') |
| parser.add_argument('--types', |
| help='Path to the zircon type header file') |
| parser.add_argument('--dartfmt', |
| help='Path to the dartfmt tool') |
| parser.add_argument('--dart-constants', |
| help='Where to generate the dart constants file') |
| parser.set_defaults(dry_run=False) |
| args = parser.parse_args() |
| |
| # If args are unset, assume the script is being run out of the checkout. |
| source_dir = os.path.dirname(__file__) |
| zircon_errors = args.errors |
| zircon_types = args.types |
| if not zircon_errors or not zircon_types: |
| zircon_include_dir = os.path.join( |
| source_dir, '../../../../zircon/system/public/zircon') |
| zircon_errors = zircon_errors or os.path.join(zircon_include_dir, 'errors.h') |
| zircon_types = zircon_types or os.path.join(zircon_include_dir, 'types.h') |
| |
| dartfmt = args.dartfmt |
| if not dartfmt: |
| dartfmt_globs = glob.glob(os.path.join( |
| source_dir, '../../../../prebuilt/third_party/dart/*/bin/dartfmt')) |
| dartfmt = dartfmt_globs[0] |
| |
| dart_constants = args.dart_constants or os.path.join(source_dir, 'lib/src/constants.dart') |
| |
| file_header = """// Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| part of zircon; |
| |
| // This is autogenerated from Zircon headers. Do not edit directly. |
| // Generated by //topaz/public/dart/zircon/extract-zircon-constants.py |
| |
| // ignore_for_file: constant_identifier_names |
| // ignore_for_file: public_member_api_docs |
| """ |
| |
| prefix = 'ZX_' |
| prefix_len = len(prefix) |
| |
| |
| def read_header(header): |
| """Read lines from a C header handle macro continuation backslashes.""" |
| continuation = '' |
| for line in open(header): |
| line = line.rstrip() |
| if continuation: |
| line = continuation + ' ' + line |
| if line.endswith('\\'): |
| continuation = line[:-1] |
| continue |
| else: |
| continuation = '' |
| yield line |
| assert continuation == '' |
| |
| |
| def extract_defines(header, guard): |
| """Extract the C macro defines from a header file.""" |
| defines = [] |
| for line in read_header(header): |
| line = line.rstrip() |
| if not line.startswith('#define'): |
| continue |
| match = re.match(r'#define\s+(?P<symbol>[A-Z0-9_]+)\s+(?P<value>\S.*)', |
| line) |
| if match: |
| defines.append((match.groupdict()['symbol'], match.groupdict()['value'])) |
| else: |
| # ignore the header guard |
| if line.startswith('#define %s' % guard) or line.startswith('#define SYSROOT_%s' % guard): |
| continue |
| # ignore function-like macros |
| if not re.match(r'#define\s+[A-Za-z0-9_]+\(', line): |
| print 'Unrecognized line: %s in %s' % (line, header) |
| sys.exit(1) |
| |
| # quadratic time is best time |
| return [(k[prefix_len:], c_to_dart_value(v, defines)) |
| for k, v in defines |
| if k.startswith(prefix)] |
| |
| |
| def c_to_dart_value(value, defines): |
| """Convert a C macro value to something that can be a Dart constant.""" |
| # expand macro references |
| for k, v in defines: |
| value = value.replace(k, v) |
| |
| # strip type casts |
| value = re.sub(r'\([a-z0-9_]+_t\)', '', value) |
| |
| # strip u suffix from decimal integers |
| value = re.sub(r'([0-9]+)u', r'\1', value) |
| |
| # strip u suffix from hex integers |
| value = re.sub(r'(0x[a-fA-F0-9]+)u', r'\1', value) |
| |
| # replace a C constant that's used in the headers |
| value = re.sub(r'UINT64_MAX', r'0xFFFFFFFFFFFFFFFF', value) |
| |
| # strip outer parens |
| value = re.sub(r'^\((.*)\)$', r'\1', value) |
| return value |
| |
| |
| class DartWriter(object): |
| |
| def __init__(self, path): |
| if args.dry_run: |
| path = '/dev/null' |
| dest = open(path, 'w') |
| self.popen = subprocess.Popen([dartfmt], stdin=subprocess.PIPE, stdout=dest) |
| |
| def write(self, string): |
| self.popen.stdin.write(string) |
| |
| def __enter__(self): |
| return self |
| |
| def __exit__(self, *args): |
| self.popen.stdin.close() |
| self.popen.wait() |
| assert self.popen.returncode == 0 |
| |
| |
| def write_constants(): |
| error_defines = extract_defines(zircon_errors, 'ZIRCON_ERRORS_H_') |
| type_defines = extract_defines(zircon_types, 'ZIRCON_TYPES_H_') |
| with DartWriter(dart_constants) as f: |
| f.write(file_header) |
| f.write('abstract class ZX {\n') |
| f.write(' ZX._();') |
| for symbol, value in error_defines + type_defines: |
| f.write(' static const int %s = %s;\n' % (symbol, value)) |
| f.write('}\n') |
| |
| f.write('String getStringForStatus(int status) {\n') |
| f.write(' switch(status) {\n') |
| for symbol, value in error_defines: |
| f.write(' case ZX.%s: return \'%s\';\n' % (symbol, symbol)) |
| f.write(' default: return \'(unknown: $status)\';\n') |
| f.write(' }\n') |
| f.write('}\n') |
| |
| |
| if __name__ == '__main__': |
| write_constants() |