1#!/usr/bin/env python3
2#
3# Arm SCP/MCP Software
4# Copyright (c) 2015-2021, Arm Limited and Contributors. All rights reserved.
5#
6# SPDX-License-Identifier: BSD-3-Clause
7#
8# Description:
9#   This tool takes a list of module names and generates two files:
10#   * fwk_modules_idx.h: Contains an enumeration giving the modules' indices.
11#   * fwk_modules_list.c: Contains a table of pointers to a module descriptor.
12#
13# Note: The files are updated only if their contents will differ, relative to
14#   the last time the tool was run.
15#
16
17import argparse
18import os
19import sys
20import tempfile
21
22DEFAULT_PATH = 'build/'
23
24FILENAME_H = "fwk_module_idx.h"
25TEMPLATE_H = "/* This file was auto generated using {} */\n" \
26             "#ifndef FWK_MODULE_IDX_H\n" \
27             "#define FWK_MODULE_IDX_H\n" \
28             "\n" \
29             "#include <fwk_id.h>\n" \
30             "\n" \
31             "enum fwk_module_idx {{\n" \
32             "{}" \
33             "}};\n" \
34             "\n" \
35             "{}" \
36             "\n" \
37             "#endif /* FWK_MODULE_IDX_H */\n"
38
39FILENAME_C = "fwk_module_list.c"
40TEMPLATE_C = "/* This file was auto generated using {} */\n" \
41             "#include <stddef.h>\n" \
42             "#include <fwk_module.h>\n" \
43             "\n" \
44             "{}" \
45             "\n" \
46             "const struct fwk_module *module_table[] = {{\n" \
47             "{}" \
48             "}};\n" \
49             "\n" \
50             "const struct fwk_module_config *module_config_table[] = {{\n" \
51             "{}" \
52             "}};\n"
53
54
55def generate_file(path, filename, content):
56    full_filename = os.path.join(path, filename)
57
58    try:
59        with open(full_filename) as f:
60            rewrite = f.read() != content
61    except FileNotFoundError:
62        rewrite = True
63
64    if rewrite:
65        with tempfile.NamedTemporaryFile(prefix="gen-module-code",
66                                         dir=path,
67                                         delete=False,
68                                         mode="wt") as f:
69            print("[GEN] {}...".format(full_filename))
70            f.write(content)
71        os.replace(f.name, full_filename)
72
73
74def generate_header(path, modules):
75    enum = ""
76    const = ""
77    for idx, module in enumerate(modules):
78        enum += "    FWK_MODULE_IDX_{} = {},\n".format(module.upper(), idx)
79        const += "static const fwk_id_t fwk_module_id_{} = " \
80                 "FWK_ID_MODULE_INIT(FWK_MODULE_IDX_{});\n".format(module,
81                                                                   module
82                                                                   .upper())
83
84    enum += "    FWK_MODULE_IDX_COUNT = {},\n".format(idx + 1)
85
86    content = TEMPLATE_H.format(sys.argv[0], enum, const)
87    generate_file(path, FILENAME_H, content)
88
89
90def generate_c(path, modules):
91    module_entry = ""
92    config_entry = ""
93    extern_entry = ""
94    for module in modules:
95        extern_entry += "extern const struct fwk_module module_{};\n"\
96            .format(module.lower())
97        extern_entry += "extern const struct fwk_module_config config_{};\n"\
98            .format(module.lower())
99        module_entry += "    &module_{},\n".format(module.lower())
100        config_entry += "    &config_{},\n".format(module.lower())
101
102    content = TEMPLATE_C.format(sys.argv[0], extern_entry, module_entry,
103                                config_entry)
104    generate_file(path, FILENAME_C, content)
105
106
107def main():
108    parser = argparse.ArgumentParser(description="Generates a header file and \
109        source file enumerating the modules that are included in a firmware.")
110
111    parser.add_argument('modules',
112                        metavar='module',
113                        type=str,
114                        nargs='*',
115                        help='A list of module names that are included in a \
116                            firmware.')
117
118    parser.add_argument('-p', '--path',
119                        help='Path to the location where generated files are \
120                        written. If the files exist then they will be \
121                        overwritten.',
122                        default=DEFAULT_PATH)
123
124    args = parser.parse_args()
125
126    modules = args.modules
127
128    generate_header(args.path, modules)
129    generate_c(args.path, modules)
130
131
132if __name__ == "__main__":
133    main()
134