jerryscript/targets/mbedos5/tools/generate_pins.py
Robert Sipka fb432a8bd7 Eliminate pylint warnings from generate_pins.py (#1731)
It also contains small refactoring steps:

* Added a main() function - instead of just writing all the code into the
  "if name" block - to avoids creating global variables that could affect
  other functions.

* Added a `write_pins_to_files` function - which writes the generated pins
  into the output JS and C++ files - to avoid too many local variables.

JerryScript-DCO-1.0-Signed-off-by: Robert Sipka rsipka.uszeged@partner.samsung.com
2017-04-26 15:18:20 +02:00

248 lines
7.8 KiB
Python

#!/usr/bin/env python
# Copyright JS Foundation and other contributors, http://js.foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Generate pins.js for a specified target, using target definitions from the
mbed OS source tree.
It's expecting to be run from the targets/mbedos5 directory.
"""
from __future__ import print_function
import argparse
import ast
import sys
import os
from simpleeval import SimpleEval, DEFAULT_OPERATORS
from pycparserext.ext_c_parser import GnuCParser
from pycparser import parse_file, c_ast, c_generator
# import mbed tools
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'mbed-os'))
from tools.targets import Target
LICENSE = '''/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the \"License\");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an \"AS IS\" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is generated by generate_pins.py. Please do not modify.
*/
'''
def find_file(root_dir, directories, name):
"""
Find the first instance of file with name 'name' in the directory tree
starting with 'root_dir'.
Filter out directories that are not in directories, or do not start with
TARGET_.
Since this looks in (essentially )the same directories as the compiler would
when compiling mbed OS, we should only find one PinNames.h.
"""
for root, dirs, files in os.walk(root_dir, topdown=True):
# modify dirs in place
dirs[:] = [directory for directory in dirs if directory in directories or not directory.startswith('TARGET_')]
if name in files:
return os.path.join(root, name)
def enumerate_includes(root_dir, directories):
"""
Walk through the directory tree, starting at root_dir, and enumerate all
valid include directories.
"""
for root, dirs, _ in os.walk(root_dir, topdown=True):
# modify dirs in place
dirs[:] = [dir_label for dir_label in dirs
if dir_label in directories
or (not dir_label.startswith('TARGET_')
and not dir_label.startswith('TOOLCHAIN_'))]
yield root
class TypeDeclVisitor(c_ast.NodeVisitor):
"""
A TypeDecl visitor class that walks the ast and calls a visitor function for every node found.
"""
def __init__(self, filter_names=None):
self.names = filter_names or []
def visit(self, node):
value = None
if node.__class__.__name__ == "TypeDecl":
value = self.visit_typedecl(node)
if value is None:
for _, child_node in node.children():
value = value or self.visit(child_node)
return value
def visit_typedecl(self, node):
"""
Visit a node.
"""
if node.declname in self.names:
c_gen = c_generator.CGenerator()
pins = {}
operators = DEFAULT_OPERATORS
operators[ast.BitOr] = lambda a, b: a | b
operators[ast.LShift] = lambda a, b: a << b
operators[ast.RShift] = lambda a, b: a << b
evaluator = SimpleEval(DEFAULT_OPERATORS)
for pin in node.type.values.enumerators:
expr = c_gen.visit(pin.value)
if "(int)" in expr:
expr = expr.replace('(int)', '')
if expr in pins:
pins[pin.name] = pins[expr]
else:
pins[pin.name] = evaluator.eval(expr.strip())
return pins
def enumerate_pins(c_source_file, include_dirs, definitions):
"""
Enumerate pins specified in PinNames.h, by looking for a PinName enum
typedef somewhere in the file.
"""
definitions += ['__attribute(x)__=', '__extension__(x)=', 'register=', '__IO=', 'uint32_t=unsigned int']
gcc_args = ['-E', '-fmerge-all-constants']
gcc_args += ['-I' + directory for directory in include_dirs]
gcc_args += ['-D' + definition for definition in definitions]
parsed_ast = parse_file(c_source_file,
use_cpp=True,
cpp_path='arm-none-eabi-gcc',
cpp_args=gcc_args,
parser=GnuCParser())
# now, walk the AST
visitor = TypeDeclVisitor(['PinName'])
return visitor.visit(parsed_ast)
def write_pins_to_files(pins, out_js_file, out_cpp_file):
"""
Write the generated pins for a specified mbed board into the output JS and C++ files.
"""
out_js = '\r\n'.join(['var %s = %s;' % pin for pin in pins])
out_js_file.write(out_js)
count = '''
unsigned int jsmbed_js_magic_string_count = {};
'''.format(len(pins))
lengths = ',\n '.join(str(len(pin[0])) for pin in pins)
lenghts_source = '''
unsigned int jsmbed_js_magic_string_lengths[] = {
%s
};
''' % lengths
magic_values = ',\n '.join(str(pin[1]) for pin in pins)
magic_source = '''
unsigned int jsmbed_js_magic_string_values[] = {
%s
};
''' % magic_values
magic_strings = ',\n '.join('"' + pin[0] + '"' for pin in pins)
magic_string_source = '''
const char * jsmbed_js_magic_strings[] = {
%s
};
''' % magic_strings
out_cpp_file.write(LICENSE + count + lenghts_source + magic_source + magic_string_source)
def main():
"""
Perform the main function of this program
"""
if not os.path.exists('./mbed-os'):
print("Fatal: mbed-os directory does not exist.")
print("Try running 'make getlibs'")
sys.exit(1)
description = """
Generate pins.js for a specified mbed board, using target definitions from the
mbed OS source tree.
"""
parser = argparse.ArgumentParser(description=description)
parser.add_argument('board', help='mbed board name')
parser.add_argument('-o',
help='Output JavaScript file (default: %(default)s)',
default='js/pins.js',
type=argparse.FileType('w'))
parser.add_argument('-c',
help='Output C++ file (default: %(default)s)',
default='source/pins.cpp',
type=argparse.FileType('w'))
args = parser.parse_args()
board_name = args.board.upper()
target = Target.get_target(board_name)
directory_labels = ['TARGET_' + label for label in target.labels] + target.macros
targets_dir = os.path.join('.', 'mbed-os', 'targets')
pins_file = find_file(targets_dir, directory_labels, 'PinNames.h')
includes = enumerate_includes(targets_dir, directory_labels)
defines = list(directory_labels)
# enumerate pins from PinNames.h
pins = enumerate_pins(pins_file, ['./tools'] + list(includes), defines)
# first sort alphabetically, then by length.
pins = [(x, pins[x]) for x in pins] # turn dict into tuples, which can be sorted
pins = sorted(pins, key=lambda x: (len(x[0]), x[0].lower()))
write_pins_to_files(pins, args.o, args.c)
if __name__ == "__main__":
main()