1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0+
3
4"""
5Expo utility - used for testing of expo features
6
7Copyright 2023 Google LLC
8Written by Simon Glass <sjg@chromium.org>
9"""
10
11import argparse
12import collections
13import io
14import re
15import subprocess
16import sys
17
18#from u_boot_pylib import cros_subprocess
19from u_boot_pylib import tools
20
21# Parse:
22#	SCENE1		= 7,
23# or	SCENE2,
24RE_ENUM = re.compile(r'(\S*)(\s*= (\d))?,')
25
26# Parse #define <name>  "string"
27RE_DEF = re.compile(r'#define (\S*)\s*"(.*)"')
28
29def calc_ids(fname):
30    """Figure out the value of the enums in a C file
31
32    Args:
33        fname (str): Filename to parse
34
35    Returns:
36        OrderedDict():
37            key (str): enum name
38            value (int or str):
39                Value of enum, if int
40                Value of #define, if string
41    """
42    vals = collections.OrderedDict()
43    with open(fname, 'r', encoding='utf-8') as inf:
44        in_enum = False
45        cur_id = 0
46        for line in inf.readlines():
47            line = line.strip()
48            if line == 'enum {':
49                in_enum = True
50                continue
51            if in_enum and line == '};':
52                in_enum = False
53
54            if in_enum:
55                if not line or line.startswith('/*'):
56                    continue
57                m_enum = RE_ENUM.match(line)
58                if m_enum.group(3):
59                    cur_id = int(m_enum.group(3))
60                vals[m_enum.group(1)] = cur_id
61                cur_id += 1
62            else:
63                m_def = RE_DEF.match(line)
64                if m_def:
65                    vals[m_def.group(1)] = tools.to_bytes(m_def.group(2))
66
67    return vals
68
69
70def run_expo(args):
71    """Run the expo program"""
72    fname = args.enum_fname or args.layout
73    ids = calc_ids(fname)
74    if not ids:
75        print(f"Warning: No enum ID values found in file '{fname}'")
76
77    indata = tools.read_file(args.layout)
78
79    outf = io.BytesIO()
80
81    for name, val in ids.items():
82        if isinstance(val, int):
83            outval = b'%d' % val
84        else:
85            outval = b'"%s"' % val
86        find_str = r'\b%s\b' % name
87        indata = re.sub(tools.to_bytes(find_str), outval, indata)
88
89    outf.write(indata)
90    data = outf.getvalue()
91
92    with open('/tmp/asc', 'wb') as outf:
93        outf.write(data)
94    proc = subprocess.run('dtc', input=data, capture_output=True)
95    edtb = proc.stdout
96    if proc.stderr:
97        print(f"Devicetree compiler error:\n{proc.stderr.decode('utf-8')}")
98        return 1
99    tools.write_file(args.outfile, edtb)
100    return 0
101
102
103def parse_args(argv):
104    """Parse the command-line arguments
105
106    Args:
107        argv (list of str): List of string arguments
108
109    Returns:
110        tuple: (options, args) with the command-line options and arugments.
111            options provides access to the options (e.g. option.debug)
112            args is a list of string arguments
113    """
114    parser = argparse.ArgumentParser()
115    parser.add_argument('-D', '--debug', action='store_true',
116        help='Enable full debug traceback')
117    parser.add_argument('-e', '--enum-fname', type=str,
118        help='.dts or C file containing enum declaration for expo items')
119    parser.add_argument('-l', '--layout', type=str, required=True,
120        help='Devicetree file source .dts for expo layout (and perhaps enums)')
121    parser.add_argument('-o', '--outfile', type=str, required=True,
122        help='Filename to write expo layout dtb')
123
124    return parser.parse_args(argv)
125
126def start_expo():
127    """Start the expo program"""
128    args = parse_args(sys.argv[1:])
129
130    if not args.debug:
131        sys.tracebacklimit = 0
132
133    ret_code = run_expo(args)
134    sys.exit(ret_code)
135
136
137if __name__ == "__main__":
138    start_expo()
139