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