Rev 8835 | Rev 8841 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
8834 | Boppan | 1 | import re |
8825 | Boppan | 2 | import os |
8837 | Boppan | 3 | import argparse |
8825 | Boppan | 4 | |
8834 | Boppan | 5 | # Parameters |
8837 | Boppan | 6 | # Path to doxygen folder to make doxygen files in: -o |
8834 | Boppan | 7 | doxygen_src_path = 'docs/doxygen' |
8837 | Boppan | 8 | # Remove generated doxygen files: --clean |
9 | clean_generated_stuff = False |
||
10 | # Dump all defined symbols: --dump |
||
8834 | Boppan | 11 | dump_symbols = False |
8837 | Boppan | 12 | # Print symbol stats: --stats |
8834 | Boppan | 13 | print_stats = False |
8825 | Boppan | 14 | |
8837 | Boppan | 15 | # Constants |
16 | link_root = "http://websvn.kolibrios.org/filedetails.php?repname=Kolibri+OS&path=/kernel/trunk" |
||
17 | |||
18 | # Parse arguments |
||
19 | parser = argparse.ArgumentParser() |
||
20 | parser.add_argument("-o", help="Doxygen output folder") |
||
21 | parser.add_argument("--clean", help="Remove generated files", action="store_true") |
||
22 | parser.add_argument("--dump", help="Dump all defined symbols", action="store_true") |
||
23 | parser.add_argument("--stats", help="Print symbol stats", action="store_true") |
||
24 | args = parser.parse_args() |
||
25 | doxygen_src_path = args.o if args.o else 'docs/doxygen' |
||
26 | clean_generated_stuff = args.clean |
||
27 | dump_symbols = args.dump |
||
28 | print_stats = args.stats |
||
29 | |||
8825 | Boppan | 30 | # kernel_structure["filename"] = { |
31 | # [ [], # [0] Variables - [ line, name ] |
||
32 | # [], # [1] Macros - [ line, name ] |
||
33 | # [], # [2] Procedures - [ line, name ] |
||
34 | # [], # [3] Labels - [ line, name ] |
||
35 | # [] ] } # [4] Structures - [ line, name ] |
||
36 | VARIABLES = 0 |
||
37 | MACROS = 1 |
||
38 | PROCEDURES = 2 |
||
39 | LABELS = 3 |
||
40 | STRUCTURES = 4 |
||
41 | kernel_structure = {} |
||
42 | |||
43 | def get_declarations(asm_file_contents, asm_file_name): |
||
44 | kernel_structure[asm_file_name] = [ [], [], [], [], [] ] |
||
45 | |||
46 | variable_pattern = re.compile(r'^\s*([\w\.]+)\s+d[bwdq] .*') |
||
47 | macro_pattern = re.compile(r'^\s*macro\s+([\w]+).*') |
||
48 | proc_pattern = re.compile(r'^\s*proc\s+([\w\.]+).*') |
||
49 | label_pattern = re.compile(r'^(?!;)\s*([\w\.]+):.*') |
||
50 | struct_pattern = re.compile(r'^\s*struct\s+([\w]+).*') |
||
51 | |||
52 | line_idx = 0 |
||
53 | lines = asm_file_contents.splitlines() |
||
54 | while line_idx < len(lines): |
||
55 | line = lines[line_idx] |
||
56 | |||
57 | match = variable_pattern.findall(line) |
||
58 | if len(match) > 0: |
||
59 | var_name = match[0] |
||
60 | #print(f"Variable '{var_name}' at {line_idx + 1}") |
||
61 | kernel_structure[asm_file_name][VARIABLES].append([ line_idx + 1, var_name ]) |
||
62 | line_idx += 1 |
||
63 | continue |
||
64 | |||
65 | match = macro_pattern.findall(line) |
||
66 | if len(match) > 0: |
||
67 | macro_name = match[0] |
||
68 | kernel_structure[asm_file_name][MACROS].append([ line_idx + 1, macro_name ]) |
||
69 | end_of_macro = False |
||
70 | while not end_of_macro: |
||
71 | line = lines[line_idx] |
||
72 | rbraces = re.finditer('}', line) |
||
73 | for rbrace_match in rbraces: |
||
74 | rbrace_idx = rbrace_match.start() |
||
75 | if line[rbrace_idx - 1] != '\\': |
||
76 | end_of_macro = True |
||
77 | line_idx += 1 |
||
78 | continue |
||
79 | |||
80 | match = proc_pattern.findall(line) |
||
81 | if len(match) > 0: |
||
82 | proc_name = match[0] |
||
83 | kernel_structure[asm_file_name][PROCEDURES].append([ line_idx + 1, proc_name ]) |
||
84 | line_idx += 1 |
||
85 | continue |
||
86 | |||
87 | match = label_pattern.findall(line) |
||
88 | if len(match) > 0: |
||
89 | label_name = match[0] |
||
90 | # Don't count local labels |
||
91 | if label_name[0] != '.': |
||
92 | kernel_structure[asm_file_name][LABELS].append([ line_idx + 1, label_name ]) |
||
93 | line_idx += 1 |
||
94 | continue |
||
95 | |||
96 | match = struct_pattern.findall(line) |
||
97 | if len(match) > 0: |
||
98 | struct_name = match[0] |
||
99 | kernel_structure[asm_file_name][STRUCTURES].append([ line_idx + 1, struct_name ]) |
||
100 | end_of_struct = False |
||
101 | while not end_of_struct: |
||
102 | line = lines[line_idx] |
||
103 | if re.match(r"^ends$", line) != None: |
||
104 | end_of_struct = True |
||
105 | line_idx += 1 |
||
106 | continue |
||
107 | |||
108 | line_idx += 1 |
||
109 | |||
8834 | Boppan | 110 | def handle_file(handled_files, asm_file_name, subdir = "."): |
8825 | Boppan | 111 | print(f"Handling {asm_file_name}") |
112 | handled_files.append(asm_file_name) |
||
113 | try: |
||
114 | asm_file_contents = open(asm_file_name, "r", encoding="utf-8").read() |
||
115 | except: |
||
116 | return |
||
117 | get_declarations(asm_file_contents, asm_file_name) |
||
118 | include_directive_pattern_1 = re.compile(r'include "(.*)"') |
||
119 | include_directive_pattern_2 = re.compile(r'include \'(.*)\'') |
||
120 | includes = include_directive_pattern_1.findall(asm_file_contents) |
||
121 | includes += include_directive_pattern_2.findall(asm_file_contents) |
||
122 | for include in includes: |
||
123 | include = include.replace('\\', '/'); |
||
124 | full_path = subdir + '/' + include; |
||
125 | if full_path not in handled_files: |
||
126 | new_subdir = full_path.rsplit('/', 1)[0] |
||
8834 | Boppan | 127 | handle_file(handled_files, full_path, new_subdir) |
8825 | Boppan | 128 | return handled_files |
129 | |||
130 | kernel_files = [] |
||
131 | |||
8834 | Boppan | 132 | handle_file(kernel_files, "./kernel.asm"); |
8825 | Boppan | 133 | |
8834 | Boppan | 134 | if dump_symbols: |
135 | for source in kernel_structure: |
||
136 | print(f"File: {source}") |
||
137 | if len(kernel_structure[source][VARIABLES]) > 0: |
||
138 | print(" Variables:") |
||
139 | for variable in kernel_structure[source][VARIABLES]: |
||
140 | print(f" {variable[0]}: {variable[1]}") |
||
141 | if len(kernel_structure[source][PROCEDURES]) > 0: |
||
142 | print(" Procedures:") |
||
143 | for procedure in kernel_structure[source][PROCEDURES]: |
||
144 | print(f" {procedure[0]}: {procedure[1]}") |
||
145 | if len(kernel_structure[source][LABELS]) > 0: |
||
146 | print(" Global labels:") |
||
147 | for label in kernel_structure[source][LABELS]: |
||
148 | print(f" {label[0]}: {label[1]}") |
||
149 | if len(kernel_structure[source][MACROS]) > 0: |
||
150 | print(" Macroses:") |
||
151 | for macro in kernel_structure[source][MACROS]: |
||
152 | print(f" {macro[0]}: {macro[1]}") |
||
153 | if len(kernel_structure[source][STRUCTURES]) > 0: |
||
154 | print(" Structures:") |
||
155 | for struct in kernel_structure[source][STRUCTURES]: |
||
156 | print(f" {struct[0]}: {struct[1]}") |
||
8825 | Boppan | 157 | |
8834 | Boppan | 158 | if print_stats: |
159 | # Collect stats |
||
160 | var_count = 0 |
||
161 | proc_count = 0 |
||
162 | label_count = 0 |
||
163 | macro_count = 0 |
||
164 | struct_count = 0 |
||
8825 | Boppan | 165 | |
8834 | Boppan | 166 | for source in kernel_structure: |
167 | var_count += len(kernel_structure[source][VARIABLES]) |
||
168 | proc_count += len(kernel_structure[source][PROCEDURES]) |
||
169 | label_count += len(kernel_structure[source][LABELS]) |
||
170 | macro_count += len(kernel_structure[source][MACROS]) |
||
171 | struct_count += len(kernel_structure[source][STRUCTURES]) |
||
8825 | Boppan | 172 | |
8834 | Boppan | 173 | print(f"Variable count: {var_count}") |
174 | print(f"Procedures count: {proc_count}") |
||
175 | print(f"Global labels count: {label_count}") |
||
176 | print(f"Macroses count: {macro_count}") |
||
177 | print(f"Structures count: {struct_count}") |
||
8825 | Boppan | 178 | |
8834 | Boppan | 179 | print(f"Writing doumented sources to {doxygen_src_path}") |
180 | |||
181 | created_files = [] |
||
182 | |||
183 | def write_variable(source, line, name, type = "int", brief = "Undocumented", |
||
184 | init = None): |
||
185 | source = source.replace("./", "") |
||
186 | full_path = doxygen_src_path + '/' + source |
||
187 | # Remove the file on first access if it was created by previous generation |
||
188 | if full_path not in created_files: |
||
189 | if os.path.isfile(full_path): |
||
190 | os.remove(full_path) |
||
191 | created_files.append(full_path) |
||
192 | # Only remove the file on 'clean_generated_stuff' flag (removed above, just return) |
||
193 | if clean_generated_stuff: return |
||
194 | # Create directories need for the file |
||
195 | os.makedirs(os.path.dirname(full_path), exist_ok=True) |
||
196 | name = name.replace(".", "_") |
||
197 | f = open(full_path, "a") |
||
198 | f.write(f"/**\n") |
||
199 | f.write(f" * @brief {brief}\n") |
||
200 | f.write(f" * @par Initial value\n") |
||
201 | f.write(f" * {init}\n") |
||
202 | f.write(f" * @par Source\n") |
||
203 | f.write(f" * {source}:{line}\n") |
||
204 | f.write(f" */\n") |
||
205 | if init == None: |
||
206 | set_init = "" |
||
207 | else: |
||
208 | set_init = f" = {init}" |
||
209 | f.write(f"{type} {name}{set_init};\n\n") |
||
210 | f.close() |
||
211 | |||
212 | i = 1 |
||
213 | for source in kernel_structure: |
||
214 | # Print progress: current/total |
||
215 | print(f"{i}/{len(kernel_structure)} Writing {source}") |
||
216 | # Write variables doxygen of the source file |
||
217 | if len(kernel_structure[source][VARIABLES]) > 0: |
||
218 | for variable in kernel_structure[source][VARIABLES]: |
||
219 | write_variable(source, variable[0], variable[1]) |
||
220 | i += 1> |