summaryrefslogtreecommitdiff
path: root/tools/expo.py
blob: 44995f28a382e1d99904856cc43007d368981881 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0+

"""
Expo utility - used for testing of expo features

Copyright 2023 Google LLC
Written by Simon Glass <sjg@chromium.org>
"""

import argparse
import collections
import io
import re
import subprocess
import sys

#from u_boot_pylib import cros_subprocess
from u_boot_pylib import tools

# Parse:
#	SCENE1		= 7,
# or    SCENE1		= EXPOID_BASE_ID,
# or	SCENE2,
RE_ENUM = re.compile(r'(\S*)(\s*= ([0-9A-Z_]+))?,')

# Parse #define <name>  "string"
RE_DEF = re.compile(r'#define (\S*)\s*"(.*)"')

# Parse EXPOID_BASE_ID = 5,
RE_BASE_ID = re.compile(r'\s*EXPOID_BASE_ID\s*= (\d+),')

def calc_ids(fname, base_id):
    """Figure out the value of the enums in a C file

    Args:
        fname (str): Filename to parse
        base_id (int): Base ID (value of EXPOID_BASE_ID)

    Returns:
        OrderedDict():
            key (str): enum name
            value (int or str):
                Value of enum, if int
                Value of #define, if string
    """
    vals = collections.OrderedDict()
    with open(fname, 'r', encoding='utf-8') as inf:
        in_enum = False
        cur_id = 0
        for line in inf.readlines():
            line = line.strip()
            if line == 'enum {':
                in_enum = True
                continue
            if in_enum and line == '};':
                in_enum = False

            if in_enum:
                if not line or line.startswith('/*'):
                    continue
                m_enum = RE_ENUM.match(line)
                enum_name = m_enum.group(3)
                if enum_name:
                    if enum_name == 'EXPOID_BASE_ID':
                        cur_id = base_id
                    else:
                        cur_id = int(enum_name)
                vals[m_enum.group(1)] = cur_id
                cur_id += 1
            else:
                m_def = RE_DEF.match(line)
                if m_def:
                    vals[m_def.group(1)] = tools.to_bytes(m_def.group(2))

    return vals


def find_base_id():
    fname = 'include/expo.h'
    base_id = None
    with open(fname, 'r', encoding='utf-8') as inf:
        for line in inf.readlines():
            m_base_id = RE_BASE_ID.match(line)
            if m_base_id:
                base_id = int(m_base_id.group(1))
    if base_id is None:
        raise ValueError('EXPOID_BASE_ID not found in expo.h')
    #print(f'EXPOID_BASE_ID={base_id}')
    return base_id

def run_expo(args):
    """Run the expo program"""
    base_id = find_base_id()
    fname = args.enum_fname or args.layout
    ids = calc_ids(fname, base_id)
    if not ids:
        print(f"Warning: No enum ID values found in file '{fname}'")

    indata = tools.read_file(args.layout)

    outf = io.BytesIO()

    for name, val in ids.items():
        if isinstance(val, int):
            outval = b'%d' % val
        else:
            outval = b'"%s"' % val
        find_str = r'\b%s\b' % name
        indata = re.sub(tools.to_bytes(find_str), outval, indata)

    outf.write(indata)
    data = outf.getvalue()

    with open('/tmp/asc', 'wb') as outf:
        outf.write(data)
    proc = subprocess.run('dtc', input=data, capture_output=True)
    edtb = proc.stdout
    if proc.stderr:
        print(f"Devicetree compiler error:\n{proc.stderr.decode('utf-8')}")
        return 1
    tools.write_file(args.outfile, edtb)
    return 0


def parse_args(argv):
    """Parse the command-line arguments

    Args:
        argv (list of str): List of string arguments

    Returns:
        tuple: (options, args) with the command-line options and arugments.
            options provides access to the options (e.g. option.debug)
            args is a list of string arguments
    """
    parser = argparse.ArgumentParser()
    parser.add_argument('-D', '--debug', action='store_true',
        help='Enable full debug traceback')
    parser.add_argument('-e', '--enum-fname', type=str,
        help='.dts or C file containing enum declaration for expo items')
    parser.add_argument('-l', '--layout', type=str, required=True,
        help='Devicetree file source .dts for expo layout (and perhaps enums)')
    parser.add_argument('-o', '--outfile', type=str, required=True,
        help='Filename to write expo layout dtb')

    return parser.parse_args(argv)

def start_expo():
    """Start the expo program"""
    args = parse_args(sys.argv[1:])

    if not args.debug:
        sys.tracebacklimit = 0

    ret_code = run_expo(args)
    sys.exit(ret_code)


if __name__ == "__main__":
    start_expo()