blob: 2217d910d0a1d4e5f40581fb57f868a6f92a1f09 [file] [log] [blame]
#!/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 //sdk/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())