Subversion Repositories Kolibri OS

Rev

Rev 8841 | Rev 8844 | 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):
8842 Boppan 44
	asm_file_name = asm_file_name.replace("./", "")
8825 Boppan 45
	kernel_structure[asm_file_name] = [ [], [], [], [], [] ]
46
 
8841 Boppan 47
	variable_pattern = re.compile(r'^\s*([\w\.]+)\s+d([bwdq])\s+([^;]*)\s*([;].*)?')
8825 Boppan 48
	macro_pattern = re.compile(r'^\s*macro\s+([\w]+).*')
49
	proc_pattern = re.compile(r'^\s*proc\s+([\w\.]+).*')
50
	label_pattern = re.compile(r'^(?!;)\s*([\w\.]+):.*')
51
	struct_pattern = re.compile(r'^\s*struct\s+([\w]+).*')
52
 
53
	line_idx = 0
54
	lines = asm_file_contents.splitlines()
55
	while line_idx < len(lines):
56
		line = lines[line_idx]
57
 
58
		match = variable_pattern.findall(line)
59
		if len(match) > 0:
8841 Boppan 60
			(var_name, var_type, var_init, var_comm) = match[0]
61
			if var_comm == "":
62
				var_comm = "Undocumented"
63
			else:
64
				var_comm = var_comm[1:].lstrip()
65
				if (len(var_comm) == 0):
66
					var_comm = "!!! EMPTY_COMMENT"
67
				if var_comm[0].islower(): var_comm = "!!! LOWERCASE COMMENT " + var_comm
68
			if var_type == "b": var_type = "byte"
69
			if var_type == "w": var_type = "word"
70
			if var_type == "d": var_type = "dword"
71
			if var_type == "q": var_type = "qword"
72
			kernel_structure[asm_file_name][VARIABLES].append([ line_idx + 1, var_name, var_type, var_init, var_comm ])
8825 Boppan 73
			line_idx += 1
74
			continue
75
 
76
		match = macro_pattern.findall(line)
77
		if len(match) > 0:
78
			macro_name = match[0]
79
			kernel_structure[asm_file_name][MACROS].append([ line_idx + 1, macro_name ])
80
			end_of_macro = False
81
			while not end_of_macro:
82
				line = lines[line_idx]
83
				rbraces = re.finditer('}', line)
84
				for rbrace_match in rbraces:
85
					rbrace_idx = rbrace_match.start()
86
					if line[rbrace_idx - 1] != '\\':
87
						end_of_macro = True
88
				line_idx += 1
89
			continue
90
 
91
		match = proc_pattern.findall(line)
92
		if len(match) > 0:
93
			proc_name = match[0]
94
			kernel_structure[asm_file_name][PROCEDURES].append([ line_idx + 1, proc_name ])
95
			line_idx += 1
96
			continue
97
 
98
		match = label_pattern.findall(line)
99
		if len(match) > 0:
100
			label_name = match[0]
101
			# Don't count local labels
102
			if label_name[0] != '.':
103
				kernel_structure[asm_file_name][LABELS].append([ line_idx + 1, label_name ])
104
				line_idx += 1
105
				continue
106
 
107
		match = struct_pattern.findall(line)
108
		if len(match) > 0:
109
			struct_name = match[0]
110
			kernel_structure[asm_file_name][STRUCTURES].append([ line_idx + 1, struct_name ])
111
			end_of_struct = False
112
			while not end_of_struct:
113
				line = lines[line_idx]
114
				if re.match(r"^ends$", line) != None:
115
					end_of_struct = True
116
				line_idx += 1
117
			continue
118
 
119
		line_idx += 1
120
 
8834 Boppan 121
def handle_file(handled_files, asm_file_name, subdir = "."):
8841 Boppan 122
	if dump_symbols:
123
		print(f"Handling {asm_file_name}")
8825 Boppan 124
	handled_files.append(asm_file_name)
125
	try:
126
		asm_file_contents = open(asm_file_name, "r", encoding="utf-8").read()
127
	except:
128
		return
129
	get_declarations(asm_file_contents, asm_file_name)
130
	include_directive_pattern_1 = re.compile(r'include "(.*)"')
131
	include_directive_pattern_2 = re.compile(r'include \'(.*)\'')
132
	includes = include_directive_pattern_1.findall(asm_file_contents)
133
	includes += include_directive_pattern_2.findall(asm_file_contents)
134
	for include in includes:
135
		include = include.replace('\\', '/');
136
		full_path = subdir + '/' + include;
137
		if full_path not in handled_files:
138
			new_subdir = full_path.rsplit('/', 1)[0]
8834 Boppan 139
			handle_file(handled_files, full_path, new_subdir)
8825 Boppan 140
	return handled_files
141
 
142
kernel_files = []
143
 
8834 Boppan 144
handle_file(kernel_files, "./kernel.asm");
8825 Boppan 145
 
8834 Boppan 146
if dump_symbols:
147
	for source in kernel_structure:
148
		print(f"File: {source}")
149
		if len(kernel_structure[source][VARIABLES]) > 0:
150
			print(" Variables:")
151
			for variable in kernel_structure[source][VARIABLES]:
152
				print(f"  {variable[0]}: {variable[1]}")
153
		if len(kernel_structure[source][PROCEDURES]) > 0:
154
			print(" Procedures:")
155
			for procedure in kernel_structure[source][PROCEDURES]:
156
				print(f"  {procedure[0]}: {procedure[1]}")
157
		if len(kernel_structure[source][LABELS]) > 0:
158
			print(" Global labels:")
159
			for label in kernel_structure[source][LABELS]:
160
				print(f"  {label[0]}: {label[1]}")
161
		if len(kernel_structure[source][MACROS]) > 0:
162
			print(" Macroses:")
163
			for macro in kernel_structure[source][MACROS]:
164
				print(f"  {macro[0]}: {macro[1]}")
165
		if len(kernel_structure[source][STRUCTURES]) > 0:
166
			print(" Structures:")
167
			for struct in kernel_structure[source][STRUCTURES]:
168
				print(f"  {struct[0]}: {struct[1]}")
8825 Boppan 169
 
8834 Boppan 170
if print_stats:
171
	# Collect stats
172
	var_count = 0
173
	proc_count = 0
174
	label_count = 0
175
	macro_count = 0
176
	struct_count = 0
8825 Boppan 177
 
8834 Boppan 178
	for source in kernel_structure:
179
		var_count += len(kernel_structure[source][VARIABLES])
180
		proc_count += len(kernel_structure[source][PROCEDURES])
181
		label_count += len(kernel_structure[source][LABELS])
182
		macro_count += len(kernel_structure[source][MACROS])
183
		struct_count += len(kernel_structure[source][STRUCTURES])
8825 Boppan 184
 
8841 Boppan 185
	print(f"File count: {len(kernel_structure)}")
8834 Boppan 186
	print(f"Variable count: {var_count}")
187
	print(f"Procedures count: {proc_count}")
188
	print(f"Global labels count: {label_count}")
189
	print(f"Macroses count: {macro_count}")
190
	print(f"Structures count: {struct_count}")
8825 Boppan 191
 
8834 Boppan 192
print(f"Writing doumented sources to {doxygen_src_path}")
193
 
194
created_files = []
195
 
8842 Boppan 196
def write_something(source, somehing):
8834 Boppan 197
	full_path = doxygen_src_path + '/' + source
198
	# Remove the file on first access if it was created by previous generation
199
	if full_path not in created_files:
200
		if os.path.isfile(full_path):
201
			os.remove(full_path)
202
		created_files.append(full_path)
203
	# Only remove the file on 'clean_generated_stuff' flag (removed above, just return)
204
	if clean_generated_stuff: return
205
	# Create directories need for the file
206
	os.makedirs(os.path.dirname(full_path), exist_ok=True)
207
	f = open(full_path, "a")
8842 Boppan 208
	f.write(somehing)
8834 Boppan 209
	f.close()
210
 
8842 Boppan 211
def write_variable(source, line, name, type, init, brief):
212
	name = name.replace(".", "_")
213
	something = (f"/**\n" +
214
	             f" * @brief {brief}\n" +
215
	             f" * @par Initial value\n" +
216
	             f" * {init}\n" +
217
	             f" * @par Source\n" +
218
	             f" * {source}:{line}\n" +
219
	             f" */\n" +
220
	             f"{type} {name};\n\n")
221
	write_something(source, something)
222
 
223
def write_procedure(source, line, name, brief = "Undocumented"):
224
	name = name.replace(".", "_")
225
	something = (f"/**\n" +
226
	             f" * @brief {brief}\n" +
227
	             f" * @par Source\n" +
228
	             f" * {source}:{line}\n" +
229
	             f" */\n" +
230
	             f"void {name}();\n\n")
231
	write_something(source, something)
232
 
8834 Boppan 233
i = 1
234
for source in kernel_structure:
235
	# Print progress: current/total
236
	print(f"{i}/{len(kernel_structure)} Writing {source}")
237
	# Write variables doxygen of the source file
238
	if len(kernel_structure[source][VARIABLES]) > 0:
239
		for variable in kernel_structure[source][VARIABLES]:
8841 Boppan 240
			write_variable(source, variable[0], variable[1], variable[2], variable[3], variable[4])
8842 Boppan 241
	if len(kernel_structure[source][PROCEDURES]) > 0:
242
		for procedure in kernel_structure[source][PROCEDURES]:
243
			write_procedure(source, procedure[0], procedure[1])
8834 Boppan 244
	i += 1