| #!/usr/bin/env python3.8 |
| # 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 re |
| import sys |
| import subprocess |
| |
| _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_' |
| |
| |
| 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.lstrip() |
| if line.endswith('\\'): |
| continuation = (line[:-1]).rstrip() |
| 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(f'#define {guard}') or \ |
| line.startswith(f'#define SYSROOT_{guard}'): |
| continue |
| # ignore function-like macros |
| if not re.match(r'#define\s+[A-Za-z0-9_]+\(', line): |
| raise ValueError(f'Unrecognized line: {line} in {header}') |
| |
| # quadratic time is best time |
| public_constants = [ |
| (k[len(_PREFIX):], c_to_dart_value(v, defines)) |
| for k, v in defines |
| if k.startswith(_PREFIX) |
| ] |
| private_constants = [ |
| (k, c_to_dart_value(v, defines)) |
| for k, v in defines |
| if not k.startswith(_PREFIX) |
| ] |
| return public_constants + private_constants |
| |
| |
| def c_to_dart_value(value, defines): |
| """Convert a C macro value to something that can be a Dart constant.""" |
| # 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) |
| |
| # strip _PREFIX from references to other constants |
| value = re.sub(r'\b' + _PREFIX + r'(\w*)\b', r'\1', value) |
| |
| return value |
| |
| |
| class DartWriter(object): |
| |
| def __init__(self, path, dart): |
| dest = open(path, 'w') |
| |
| # The undocumented --no-analytics flag is the only way to avoid |
| # the creation of $BUILD_DIR/.dart/ when 'dart format' is called. |
| # See https://fxbug.dev/80787 for details. |
| self.popen = subprocess.Popen( |
| [dart, '--no-analytics', 'format', '-o', 'show'], |
| stdin=subprocess.PIPE, |
| stdout=dest, |
| text=True) |
| |
| 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( |
| zircon_errors, zircon_types, zircon_rights, dart_constants, dart): |
| error_defines = extract_defines(zircon_errors, 'ZIRCON_ERRORS_H_') |
| type_defines = extract_defines(zircon_types, 'ZIRCON_TYPES_H_') |
| right_defines = extract_defines(zircon_rights, 'ZIRCON_RIGHTS_H_') |
| with DartWriter(dart_constants, dart) as f: |
| f.write(_FILE_HEADER) |
| f.write('abstract class ZX {\n') |
| f.write(' ZX._();') |
| for symbol, value in error_defines + type_defines + right_defines: |
| # PAGE_SIZE is from limits.h. It contains arch specific defines |
| # that are hard to parse here, so skip until affected constants are |
| # needed in Dart. |
| if value == 'PAGE_SIZE': |
| continue |
| if symbol.startswith('_'): |
| # private constant, it's okay if it's unused. |
| f.write(' // ignore: unused_field\n') |
| f.write(f' static const int {symbol} = {value};\n') |
| f.write('}\n') |
| |
| f.write('String getStringForStatus(int status) {\n') |
| f.write(' switch(status) {\n') |
| for symbol, value in error_defines: |
| f.write(f" case ZX.{symbol}: return '{symbol}';\n") |
| f.write(" default: return '(unknown: $status)';\n") |
| f.write(' }\n') |
| f.write('}\n') |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description= |
| 'Generates dart constants from a parsing of corresponding values in zircon' |
| ) |
| parser.add_argument( |
| '--errors', |
| required=True, |
| help='Path to the zircon errors.h header file') |
| parser.add_argument( |
| '--types', required=True, help='Path to the zircon types.h header file') |
| parser.add_argument( |
| '--rights', |
| required=True, |
| help='Path to the zircon rights.h header file') |
| parser.add_argument('--dart', required=True, help='Path to the dart tool') |
| parser.add_argument( |
| '--dart-constants', |
| required=True, |
| help='Where to generate the dart constants file') |
| args = parser.parse_args() |
| |
| write_constants( |
| zircon_errors=args.errors, |
| zircon_types=args.types, |
| zircon_rights=args.rights, |
| dart_constants=args.dart_constants, |
| dart=args.dart, |
| ) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |