1425bb815Sopenharmony_ci#!/usr/bin/env python
2425bb815Sopenharmony_ci
3425bb815Sopenharmony_ci# Copyright JS Foundation and other contributors, http://js.foundation
4425bb815Sopenharmony_ci#
5425bb815Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
6425bb815Sopenharmony_ci# you may not use this file except in compliance with the License.
7425bb815Sopenharmony_ci# You may obtain a copy of the License at
8425bb815Sopenharmony_ci#
9425bb815Sopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
10425bb815Sopenharmony_ci#
11425bb815Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
12425bb815Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS
13425bb815Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14425bb815Sopenharmony_ci# See the License for the specific language governing permissions and
15425bb815Sopenharmony_ci# limitations under the License.
16425bb815Sopenharmony_ci"""
17425bb815Sopenharmony_ciGenerate pins.cpp for a specified target, using target definitions from the
18425bb815Sopenharmony_cimbed OS source tree.
19425bb815Sopenharmony_ci
20425bb815Sopenharmony_ciIt's expecting to be run from the targets/mbedos5 directory.
21425bb815Sopenharmony_ci"""
22425bb815Sopenharmony_ci
23425bb815Sopenharmony_cifrom __future__ import print_function
24425bb815Sopenharmony_ci
25425bb815Sopenharmony_ciimport argparse
26425bb815Sopenharmony_ciimport ast
27425bb815Sopenharmony_ci
28425bb815Sopenharmony_ciimport sys
29425bb815Sopenharmony_ciimport os
30425bb815Sopenharmony_ci
31425bb815Sopenharmony_cifrom pycparserext.ext_c_parser import GnuCParser
32425bb815Sopenharmony_cifrom pycparser import parse_file, c_ast
33425bb815Sopenharmony_ci
34425bb815Sopenharmony_ci# import mbed tools
35425bb815Sopenharmony_cisys.path.append(os.path.join(os.path.dirname(__file__), '..', 'mbed-os'))
36425bb815Sopenharmony_cifrom tools.targets import Target
37425bb815Sopenharmony_ci
38425bb815Sopenharmony_ciLICENSE = '''/* Copyright JS Foundation and other contributors, http://js.foundation
39425bb815Sopenharmony_ci *
40425bb815Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the \"License\");
41425bb815Sopenharmony_ci * you may not use this file except in compliance with the License.
42425bb815Sopenharmony_ci * You may obtain a copy of the License at
43425bb815Sopenharmony_ci *
44425bb815Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
45425bb815Sopenharmony_ci *
46425bb815Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
47425bb815Sopenharmony_ci * distributed under the License is distributed on an \"AS IS\" BASIS
48425bb815Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49425bb815Sopenharmony_ci * See the License for the specific language governing permissions and
50425bb815Sopenharmony_ci * limitations under the License.
51425bb815Sopenharmony_ci *
52425bb815Sopenharmony_ci * This file is generated by generate_pins.py. Please do not modify.
53425bb815Sopenharmony_ci */
54425bb815Sopenharmony_ci    '''
55425bb815Sopenharmony_ci
56425bb815Sopenharmony_ci
57425bb815Sopenharmony_cidef find_file(root_dir, directories, name):
58425bb815Sopenharmony_ci    """
59425bb815Sopenharmony_ci    Find the first instance of file with name 'name' in the directory tree
60425bb815Sopenharmony_ci    starting with 'root_dir'.
61425bb815Sopenharmony_ci
62425bb815Sopenharmony_ci    Filter out directories that are not in directories, or do not start with
63425bb815Sopenharmony_ci    TARGET_.
64425bb815Sopenharmony_ci
65425bb815Sopenharmony_ci    Since this looks in (essentially )the same directories as the compiler would
66425bb815Sopenharmony_ci    when compiling mbed OS, we should only find one PinNames.h.
67425bb815Sopenharmony_ci    """
68425bb815Sopenharmony_ci
69425bb815Sopenharmony_ci    for root, dirs, files in os.walk(root_dir, topdown=True):
70425bb815Sopenharmony_ci        # modify dirs in place
71425bb815Sopenharmony_ci        dirs[:] = [directory for directory in dirs if directory in directories or not directory.startswith('TARGET_')]
72425bb815Sopenharmony_ci
73425bb815Sopenharmony_ci        if name in files:
74425bb815Sopenharmony_ci            return os.path.join(root, name)
75425bb815Sopenharmony_ci
76425bb815Sopenharmony_ci
77425bb815Sopenharmony_cidef enumerate_includes(root_dir, directories):
78425bb815Sopenharmony_ci    """
79425bb815Sopenharmony_ci    Walk through the directory tree, starting at root_dir, and enumerate all
80425bb815Sopenharmony_ci    valid include directories.
81425bb815Sopenharmony_ci    """
82425bb815Sopenharmony_ci    for root, dirs, _ in os.walk(root_dir, topdown=True):
83425bb815Sopenharmony_ci        # modify dirs in place
84425bb815Sopenharmony_ci        dirs[:] = [dir_label for dir_label in dirs
85425bb815Sopenharmony_ci                   if dir_label in directories
86425bb815Sopenharmony_ci                   or (not dir_label.startswith('TARGET_')
87425bb815Sopenharmony_ci                       and not dir_label.startswith('TOOLCHAIN_'))]
88425bb815Sopenharmony_ci        yield root
89425bb815Sopenharmony_ci
90425bb815Sopenharmony_ci
91425bb815Sopenharmony_ciclass TypeDeclVisitor(c_ast.NodeVisitor):
92425bb815Sopenharmony_ci    """
93425bb815Sopenharmony_ci    A TypeDecl visitor class that walks the ast and calls a visitor function for every node found.
94425bb815Sopenharmony_ci    """
95425bb815Sopenharmony_ci    def __init__(self, filter_names=None):
96425bb815Sopenharmony_ci        self.names = filter_names or []
97425bb815Sopenharmony_ci
98425bb815Sopenharmony_ci    def visit(self, node):
99425bb815Sopenharmony_ci        value = None
100425bb815Sopenharmony_ci
101425bb815Sopenharmony_ci        if node.__class__.__name__ == "TypeDecl":
102425bb815Sopenharmony_ci            value = self.visit_typedecl(node)
103425bb815Sopenharmony_ci
104425bb815Sopenharmony_ci        if value is None:
105425bb815Sopenharmony_ci            for _, child_node in node.children():
106425bb815Sopenharmony_ci                value = value or self.visit(child_node)
107425bb815Sopenharmony_ci
108425bb815Sopenharmony_ci        return value
109425bb815Sopenharmony_ci
110425bb815Sopenharmony_ci    def visit_typedecl(self, node):
111425bb815Sopenharmony_ci        """
112425bb815Sopenharmony_ci        Visit a node.
113425bb815Sopenharmony_ci        """
114425bb815Sopenharmony_ci        if node.declname in self.names:
115425bb815Sopenharmony_ci            return [pin.name for pin in node.type.values.enumerators]
116425bb815Sopenharmony_ci
117425bb815Sopenharmony_ci
118425bb815Sopenharmony_cidef enumerate_pins(c_source_file, include_dirs, definitions):
119425bb815Sopenharmony_ci    """
120425bb815Sopenharmony_ci    Enumerate pins specified in PinNames.h, by looking for a PinName enum
121425bb815Sopenharmony_ci    typedef somewhere in the file.
122425bb815Sopenharmony_ci    """
123425bb815Sopenharmony_ci    definitions += ['__attribute(x)__=', '__extension__(x)=', 'register=', '__IO=', 'uint32_t=unsigned int']
124425bb815Sopenharmony_ci
125425bb815Sopenharmony_ci    gcc_args = ['-E', '-fmerge-all-constants']
126425bb815Sopenharmony_ci    gcc_args += ['-I' + directory for directory in include_dirs]
127425bb815Sopenharmony_ci
128425bb815Sopenharmony_ci    gcc_args += ['-D' + definition for definition in definitions]
129425bb815Sopenharmony_ci    parsed_ast = parse_file(c_source_file,
130425bb815Sopenharmony_ci                            use_cpp=True,
131425bb815Sopenharmony_ci                            cpp_path='arm-none-eabi-gcc',
132425bb815Sopenharmony_ci                            cpp_args=gcc_args,
133425bb815Sopenharmony_ci                            parser=GnuCParser())
134425bb815Sopenharmony_ci
135425bb815Sopenharmony_ci    # now, walk the AST
136425bb815Sopenharmony_ci    visitor = TypeDeclVisitor(['PinName'])
137425bb815Sopenharmony_ci    return visitor.visit(parsed_ast)
138425bb815Sopenharmony_ci
139425bb815Sopenharmony_ci
140425bb815Sopenharmony_cidef write_pins_to_file(pins, pins_file, out_cpp_file):
141425bb815Sopenharmony_ci    """
142425bb815Sopenharmony_ci    Write the generated pins for a specified mbed board into the output C++ file.
143425bb815Sopenharmony_ci    """
144425bb815Sopenharmony_ci
145425bb815Sopenharmony_ci    include = '\n#include "../{}"'.format(pins_file)
146425bb815Sopenharmony_ci
147425bb815Sopenharmony_ci    count = '''
148425bb815Sopenharmony_ciunsigned int jsmbed_js_magic_string_count = {};
149425bb815Sopenharmony_ci    '''.format(len(pins))
150425bb815Sopenharmony_ci
151425bb815Sopenharmony_ci    lengths = ',\n    '.join(str(len(pin)) for pin in pins)
152425bb815Sopenharmony_ci    lenghts_source = '''
153425bb815Sopenharmony_ciunsigned int jsmbed_js_magic_string_lengths[] = {
154425bb815Sopenharmony_ci    %s
155425bb815Sopenharmony_ci};
156425bb815Sopenharmony_ci    ''' % lengths
157425bb815Sopenharmony_ci
158425bb815Sopenharmony_ci    magic_values = ',\n    '.join(pins)
159425bb815Sopenharmony_ci    magic_source = '''
160425bb815Sopenharmony_ciunsigned int jsmbed_js_magic_string_values[] = {
161425bb815Sopenharmony_ci    %s
162425bb815Sopenharmony_ci};
163425bb815Sopenharmony_ci    ''' % magic_values
164425bb815Sopenharmony_ci
165425bb815Sopenharmony_ci    magic_strings = ',\n    '.join('"' + pin + '"' for pin in pins)
166425bb815Sopenharmony_ci    magic_string_source = '''
167425bb815Sopenharmony_ciconst char * jsmbed_js_magic_strings[] = {
168425bb815Sopenharmony_ci    %s
169425bb815Sopenharmony_ci};
170425bb815Sopenharmony_ci    ''' % magic_strings
171425bb815Sopenharmony_ci
172425bb815Sopenharmony_ci    out_cpp_file.write(LICENSE + include + count + lenghts_source + magic_source + magic_string_source)
173425bb815Sopenharmony_ci
174425bb815Sopenharmony_ci
175425bb815Sopenharmony_cidef main():
176425bb815Sopenharmony_ci    """
177425bb815Sopenharmony_ci    Perform the main function of this program
178425bb815Sopenharmony_ci    """
179425bb815Sopenharmony_ci    if not os.path.exists('./mbed-os'):
180425bb815Sopenharmony_ci        print("Fatal: mbed-os directory does not exist.")
181425bb815Sopenharmony_ci        print("Try running 'make getlibs'")
182425bb815Sopenharmony_ci        sys.exit(1)
183425bb815Sopenharmony_ci
184425bb815Sopenharmony_ci    description = """
185425bb815Sopenharmony_ci    Generate pins.cpp for a specified mbed board, using target definitions from the
186425bb815Sopenharmony_ci    mbed OS source tree.
187425bb815Sopenharmony_ci    """
188425bb815Sopenharmony_ci
189425bb815Sopenharmony_ci    parser = argparse.ArgumentParser(description=description)
190425bb815Sopenharmony_ci
191425bb815Sopenharmony_ci    parser.add_argument('board', help='mbed board name')
192425bb815Sopenharmony_ci    parser.add_argument('-c',
193425bb815Sopenharmony_ci                        help='Output C++ file (default: %(default)s)',
194425bb815Sopenharmony_ci                        default='source/pins.cpp',
195425bb815Sopenharmony_ci                        type=argparse.FileType('w'))
196425bb815Sopenharmony_ci
197425bb815Sopenharmony_ci    args = parser.parse_args()
198425bb815Sopenharmony_ci    board_name = args.board.upper()
199425bb815Sopenharmony_ci
200425bb815Sopenharmony_ci    target = Target.get_target(board_name)
201425bb815Sopenharmony_ci
202425bb815Sopenharmony_ci    directory_labels = ['TARGET_' + label for label in target.labels] + target.macros
203425bb815Sopenharmony_ci
204425bb815Sopenharmony_ci    targets_dir = os.path.join('.', 'mbed-os', 'targets')
205425bb815Sopenharmony_ci
206425bb815Sopenharmony_ci    pins_file = find_file(targets_dir, directory_labels, 'PinNames.h')
207425bb815Sopenharmony_ci
208425bb815Sopenharmony_ci    includes = enumerate_includes(targets_dir, directory_labels)
209425bb815Sopenharmony_ci    defines = list(directory_labels)
210425bb815Sopenharmony_ci
211425bb815Sopenharmony_ci    # enumerate pins from PinNames.h
212425bb815Sopenharmony_ci    pins = enumerate_pins(pins_file, ['./tools'] + list(includes), defines)
213425bb815Sopenharmony_ci
214425bb815Sopenharmony_ci    # first sort alphabetically, then by length.
215425bb815Sopenharmony_ci    pins = sorted(pins, key=lambda x: (len(x), x.lower()))
216425bb815Sopenharmony_ci
217425bb815Sopenharmony_ci    write_pins_to_file(pins, pins_file, args.c)
218425bb815Sopenharmony_ci
219425bb815Sopenharmony_ci
220425bb815Sopenharmony_ciif __name__ == "__main__":
221425bb815Sopenharmony_ci    main()
222