generate_pins.py 6.8 KB
Newer Older
1 2
#!/usr/bin/env python

3
# Copyright JS Foundation and other contributors, http://js.foundation
4 5 6 7 8 9 10 11 12 13 14 15 16
#
# 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.
"""
17
Generate pins.cpp for a specified target, using target definitions from the
18 19 20 21 22 23 24
mbed OS source tree.

It's expecting to be run from the targets/mbedos5 directory.
"""

from __future__ import print_function

25
import argparse
26 27 28 29 30
import ast

import sys
import os

31
from pycparserext.ext_c_parser import GnuCParser
32
from pycparser import parse_file, c_ast
33

34 35 36 37
# import mbed tools
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'mbed-os'))
from tools.targets import Target

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
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.
 */
    '''

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

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
71
        dirs[:] = [directory for directory in dirs if directory in directories or not directory.startswith('TARGET_')]
72 73 74 75

        if name in files:
            return os.path.join(root, name)

76

77 78 79 80 81
def enumerate_includes(root_dir, directories):
    """
    Walk through the directory tree, starting at root_dir, and enumerate all
    valid include directories.
    """
82
    for root, dirs, _ in os.walk(root_dir, topdown=True):
83
        # modify dirs in place
84 85 86 87
        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_'))]
88 89 90 91
        yield root


class TypeDeclVisitor(c_ast.NodeVisitor):
92 93 94 95 96
    """
    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 []
97 98 99 100 101

    def visit(self, node):
        value = None

        if node.__class__.__name__ == "TypeDecl":
102
            value = self.visit_typedecl(node)
103 104

        if value is None:
105 106
            for _, child_node in node.children():
                value = value or self.visit(child_node)
107 108 109

        return value

110 111 112 113
    def visit_typedecl(self, node):
        """
        Visit a node.
        """
114
        if node.declname in self.names:
115
            return [pin.name for pin in node.type.values.enumerators]
116

117

118 119 120 121 122 123 124
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']

125 126
    gcc_args = ['-E', '-fmerge-all-constants']
    gcc_args += ['-I' + directory for directory in include_dirs]
127 128

    gcc_args += ['-D' + definition for definition in definitions]
129 130 131 132 133
    parsed_ast = parse_file(c_source_file,
                            use_cpp=True,
                            cpp_path='arm-none-eabi-gcc',
                            cpp_args=gcc_args,
                            parser=GnuCParser())
134 135

    # now, walk the AST
136 137
    visitor = TypeDeclVisitor(['PinName'])
    return visitor.visit(parsed_ast)
138 139


140
def write_pins_to_file(pins, pins_file, out_cpp_file):
141
    """
142
    Write the generated pins for a specified mbed board into the output C++ file.
143
    """
144 145

    include = '\n#include "../{}"'.format(pins_file)
146 147 148 149 150

    count = '''
unsigned int jsmbed_js_magic_string_count = {};
    '''.format(len(pins))

151
    lengths = ',\n    '.join(str(len(pin)) for pin in pins)
152 153 154 155 156 157
    lenghts_source = '''
unsigned int jsmbed_js_magic_string_lengths[] = {
    %s
};
    ''' % lengths

158
    magic_values = ',\n    '.join(pins)
159 160 161 162 163 164
    magic_source = '''
unsigned int jsmbed_js_magic_string_values[] = {
    %s
};
    ''' % magic_values

165
    magic_strings = ',\n    '.join('"' + pin + '"' for pin in pins)
166 167 168 169 170 171
    magic_string_source = '''
const char * jsmbed_js_magic_strings[] = {
    %s
};
    ''' % magic_strings

172
    out_cpp_file.write(LICENSE + include + count + lenghts_source + magic_source + magic_string_source)
173 174 175 176 177 178


def main():
    """
    Perform the main function of this program
    """
179 180 181 182 183 184
    if not os.path.exists('./mbed-os'):
        print("Fatal: mbed-os directory does not exist.")
        print("Try running 'make getlibs'")
        sys.exit(1)

    description = """
185
    Generate pins.cpp for a specified mbed board, using target definitions from the
186 187 188 189 190 191 192 193 194 195 196 197 198 199
    mbed OS source tree.
    """

    parser = argparse.ArgumentParser(description=description)

    parser.add_argument('board', help='mbed board name')
    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()

200
    target = Target.get_target(board_name)
201

202
    directory_labels = ['TARGET_' + label for label in target.labels] + target.macros
203

204
    targets_dir = os.path.join('.', 'mbed-os', 'targets')
205

206
    pins_file = find_file(targets_dir, directory_labels, 'PinNames.h')
207 208 209 210 211 212 213

    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)

214
    # first sort alphabetically, then by length.
215
    pins = sorted(pins, key=lambda x: (len(x), x.lower()))
216

217
    write_pins_to_file(pins, pins_file, args.c)
218 219


220 221
if __name__ == "__main__":
    main()