-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmake_kaizen.py
195 lines (162 loc) · 7.4 KB
/
make_kaizen.py
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import datetime
import fnmatch
import re
import os
PRAGMA_ONCE = "#pragma once"
# Collects header files from specified dirs
def collect_main_header_files(dirs):
header_files = []
alpha_header = None # separate out the alpha.h file
for dir in dirs:
for filename in os.listdir(dir):
if filename.endswith('.h'):
file_path = os.path.join(dir, filename)
if filename == 'alpha.h' and dir.endswith('zen/datas'):
alpha_header = file_path
else:
header_files.append(file_path)
return header_files, alpha_header
def collect_composite_headers(zen_composites):
header_files = []
composite_includes = set()
for filename in os.listdir(zen_composites):
if filename.endswith('.h'):
header_file = os.path.join(zen_composites, filename)
include_directives, _ = parse_header_file(header_file)
composite_includes.update(include_directives)
header_files.append(header_file)
return header_files, composite_includes
# Separates license, include directives and code
def parse_header_file(header_file):
include_directives = set()
code_content = []
with open(header_file, 'r') as input_file:
lines = input_file.readlines()
skipping_license = True # to skip license comments at the top of files
for line in lines:
if skipping_license:
if line.strip().startswith('//'):
continue # license part, so skip
else:
skipping_license = False # end of license comment section, prepare for reading code
if PRAGMA_ONCE in line:
continue
# If line #includes any non-standard C++ headers (like Kaizen-internal), skip it
if re.match(r'#include\s+"(.*)"', line):
continue # skip non-standard headers
match_include = re.match(r'#include\s+[<](.*)[>]', line)
if match_include:
include_directives.add(line.strip())
else:
code_content.append(line)
return include_directives, code_content
# Reads license and turns it into a ready comment
def read_license(filename):
with open(filename, 'r') as license_file:
lines = license_file.readlines()
# Return with comment characters added if they're not present
return ['// ' + line if not line.startswith('// ') else line for line in lines]
def compact_namespace_zen(code_content):
first_namespace_found = False
last_closing_namespace_idx = None
compacted_code_content = []
# Find the index of the last "} // namespace zen"
for idx, line in reversed(list(enumerate(code_content))):
if "} // namespace zen" in line:
last_closing_namespace_idx = idx
break
# Create a new list for compacted code content
for idx, line in enumerate(code_content):
if "namespace zen {" in line:
if not first_namespace_found:
first_namespace_found = True
compacted_code_content.append(line)
continue
elif "} // namespace zen" in line:
if idx == last_closing_namespace_idx:
compacted_code_content.append(line)
continue
else:
compacted_code_content.append(line)
return compacted_code_content
def deflate(code_content):
deflated_code_content = []
prev_was_empty = False # keep track of whether the previous line was empty
for line in code_content:
if line.strip() == '':
if not prev_was_empty:
deflated_code_content.append(line)
prev_was_empty = True
else:
deflated_code_content.append(line)
prev_was_empty = False
return deflated_code_content
# Produces the final resulting kaizen library single header file
def write_output_file(filename, license_text, include_directives, code_content):
code_content = compact_namespace_zen(code_content)
code_content = deflate(code_content)
with open(filename, 'w') as output_file:
now = datetime.datetime.now()
output_file.write('// FILE GENERATED ON: ' + now.strftime("%d.%m.%Y %H:%M:%S") + '\n//\n')
output_file.writelines(license_text)
output_file.write(f'\n{PRAGMA_ONCE} \n\n')
output_file.write('// Since the order of these #includes doesn\'t matter,\n// they\'re sorted in descending length for aesthetics\n')
for include_directive in sorted(include_directives, key=len, reverse=True):
output_file.write(include_directive + '\n')
# Remove all leading empty lines but one that come right after the #include directives:
while code_content and code_content[0].strip() == '':
code_content.pop(0)
code_content.insert(0, '\n')
output_file.writelines(code_content)
os.chmod(filename, 0o444) # make readonly
# check if pragma_once is present in file
def is_pragma_once_present(file_path):
with open(file_path, 'r') as file:
lines = file.readlines()
for line in lines:
linestrip = line.strip()
if linestrip == PRAGMA_ONCE:
return True
elif not linestrip.startswith(('//', '/*', '*/', '*')) and linestrip:
return False
return False
# checks for header
def check_headers_in(directory):
for root, _, files in os.walk(directory):
for file in fnmatch.filter(files, '*.h*'): # .h and .hpp files
file_path = os.path.join(root, file)
if not is_pragma_once_present(file_path):
print(f'WARNING: {PRAGMA_ONCE} NOT FOUND IN KAIZEN HEADER {file_path}')
if __name__ == '__main__':
project_dir = os.path.dirname(os.path.abspath(__file__))
zen_datas = os.path.join(project_dir, 'zen/datas')
zen_functions = os.path.join(project_dir, 'zen/functions')
zen_composites = os.path.join(project_dir, 'zen/composites')
# checks for headers
check_headers_in(zen_datas)
check_headers_in(zen_functions)
check_headers_in(zen_composites)
license_file = os.path.join(project_dir, 'LICENSE.txt')
header_files, alpha_header = collect_main_header_files([zen_datas, zen_functions])
composite_headers, composite_includes = collect_composite_headers(zen_composites)
license_text = read_license(license_file)
all_include_directives = set()
all_code_content = []
# Process 'alpha.h' separately and ensure its content is added first
if alpha_header:
_, alpha_content = parse_header_file(alpha_header)
all_code_content.extend(alpha_content) # ensure alpha.h content is first
# Process regular headers
for header_file in header_files:
include_directives, code_content = parse_header_file(header_file)
all_include_directives.update(include_directives)
all_code_content.extend(code_content)
# Process composite headers
for composite_header in composite_headers:
_, code_content = parse_header_file(composite_header)
all_code_content.extend(code_content)
# Remove headers included in composite headers
all_include_directives -= composite_includes
# Generate the final result of the Kaizen library header file
write_output_file('kaizen.h', license_text, all_include_directives, all_code_content)
# end of make_kayzen.py