Skip to content

Commit 691ed9b

Browse files
authored
prepare for datecodes, don't update versions for submodules (FreeRTOS#373)
1 parent 5a8f03b commit 691ed9b

File tree

2 files changed

+317
-246
lines changed

2 files changed

+317
-246
lines changed
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
#!/usr/bin/env python3
2+
import os
3+
import re
4+
import argparse
5+
from collections import defaultdict
6+
7+
8+
_AFR_COMPONENTS = [
9+
os.path.join('FreeRTOS-Labs','Source','FreeRTOS-IoT-Libraries','abstractions','platform','freertos'),
10+
os.path.join('FreeRTOS-Labs','Source','FreeRTOS-IoT-Libraries','abstractions','platform','freertos','private','taskpool'),
11+
os.path.join('FreeRTOS-Labs','Source','FreeRTOS-IoT-Libraries','c_sdk','aws','jobs'),
12+
os.path.join('FreeRTOS-Labs','Source','FreeRTOS-IoT-Libraries','c_sdk','aws','shadow'),
13+
os.path.join('FreeRTOS-Labs','Source','FreeRTOS-IoT-Libraries','c_sdk','aws','common'),
14+
os.path.join('FreeRTOS-Labs','Source','FreeRTOS-IoT-Libraries','c_sdk','standard','common'),
15+
os.path.join('FreeRTOS-Labs','Source','FreeRTOS-IoT-Libraries','c_sdk','standard','https'),
16+
os.path.join('FreeRTOS-Labs','Source','FreeRTOS-IoT-Libraries','c_sdk','standard','mqtt'),
17+
os.path.join('FreeRTOS-Labs','Demo','FreeRTOS_IoT_Libraries'),
18+
os.path.join('FreeRTOS-Plus','Source','FreeRTOS-Plus-TCP'),
19+
os.path.join('FreeRTOS-Plus','Source','FreeRTOS-Plus-TCP','portable'),
20+
]
21+
22+
_AFR_EXCLUDE_DIRS = [
23+
'portable',
24+
'third_party'
25+
]
26+
27+
_FREERTOS_COMPONENTS = [
28+
os.path.join('FreeRTOS'),
29+
os.path.join('FreeRTOS-Plus/Demo'),
30+
os.path.join('FreeRTOS-Plus/Source'),
31+
os.path.join('FreeRTOS-Plus/Test')
32+
]
33+
_FREERTOS_VERSION_MACRO_FILE = os.path.join('FreeRTOS', 'Source', 'include', 'task.h')
34+
35+
def ask_question(question):
36+
answer = input('{}: '.format(question))
37+
return answer.strip()
38+
39+
40+
def ask_multiple_choice_question(question, choices):
41+
while True:
42+
print('{}?'.format(question))
43+
for i in range(len(choices)):
44+
print('{}. {}'.format(i, choices[i]))
45+
46+
try:
47+
user_choice = int(ask_question('Enter Choice'))
48+
except ValueError:
49+
user_choice = None
50+
51+
if user_choice in range(len(choices)):
52+
break
53+
else:
54+
print('Incorrect choice. Please choose a number between 0 and {}'.format(len(choices) - 1))
55+
return user_choice
56+
57+
58+
def ask_yes_no_question(question):
59+
while True:
60+
answer = ask_question('{} (Y/N)'.format(question))
61+
if answer.lower() == 'y':
62+
answer = 'yes'
63+
break
64+
elif answer.lower() == 'n':
65+
answer = 'no'
66+
break
67+
else:
68+
print('Incorrect response. Please answer Y/N.')
69+
return answer
70+
71+
72+
def list_files_in_a_component(component, afr_path, exclude_dirs=[], ext_filter=['.c', '.h']):
73+
'''
74+
Returns a list of all the files in a component.
75+
'''
76+
list_of_files = []
77+
search_path = os.path.join(afr_path, component)
78+
79+
for root, dirs, files in os.walk(search_path, topdown=True):
80+
dirs[:] = [d for d in dirs if d not in exclude_dirs]
81+
82+
# Do not include hidden files and folders.
83+
dirs[:] = [d for d in dirs if not d[0] == '.']
84+
files = [f for f in files if not f[0] == '.']
85+
86+
for f in files:
87+
if ext_filter != None:
88+
ext = '.' + f.split('.')[-1]
89+
if ext in ext_filter:
90+
list_of_files.append(os.path.join(os.path.relpath(root, afr_path), f))
91+
else:
92+
list_of_files.append(os.path.join(os.path.relpath(root, afr_path), f))
93+
94+
return list_of_files
95+
96+
97+
def extract_version_number_from_file(file_path):
98+
'''
99+
Extracts version number from the License header in a file.
100+
'''
101+
with open(file_path, encoding='utf-8', errors='ignore') as f:
102+
content = f.read()
103+
match = re.search('\s*\*\s*(Amazon FreeRTOS.*V(.*))', content, re.MULTILINE)
104+
# Is it a kernel file?
105+
if match is None:
106+
match = re.search('\s*\*\s*(FreeRTOS Kernel.*V(.*))', content, re.MULTILINE)
107+
# Is it s FreeRTOS+TCP file?
108+
if match is None:
109+
match = re.search('\s*\*\s*(FreeRTOS\+TCP.*V(.*))', content, re.MULTILINE)
110+
# AWS library from C SDK
111+
if match is None:
112+
match = re.search('\s*\*\s*(AWS IoT.*V(.*))', content, re.MULTILINE)
113+
# IoT library from C SDK
114+
if match is None:
115+
match = re.search('\s*\*\s*(IoT.*V(.*))', content, re.MULTILINE)
116+
return (match.group(1), match.group(2)) if match is not None else (None, None)
117+
118+
def update_version_number_in_files(file_paths, old_version_line, new_version_line):
119+
'''
120+
Replaces old_version_line with new_version_line in all the files specified
121+
by file_paths.
122+
'''
123+
for file_path in file_paths:
124+
with open(file_path, encoding='utf-8', errors='ignore', newline='') as f:
125+
content = f.read()
126+
content = content.replace(old_version_line, new_version_line)
127+
with open(file_path, 'w', newline='') as f:
128+
f.write(content)
129+
130+
def update_version_number_in_a_component(component, afr_path, exclude_dirs=[]):
131+
'''
132+
Updates version numbers in all the files of an AFR component based on user
133+
choices.
134+
'''
135+
# Get all the files in the component.
136+
files_in_component = list_files_in_a_component(component, afr_path, exclude_dirs)
137+
138+
version_numbers = defaultdict(list)
139+
140+
# Extract version numbers from all the files.
141+
for f in files_in_component:
142+
file_path = os.path.join(afr_path, f)
143+
version_number = extract_version_number_from_file(file_path)
144+
145+
if version_number[0] != None and version_number[1] != None:
146+
version_numbers[version_number].append(file_path)
147+
148+
for key in version_numbers.keys():
149+
v0 = key[0]
150+
v1 = key[1]
151+
f = version_numbers[key]
152+
print('\n{} files have the following version: {}\n'.format(len(f), v0))
153+
154+
options = [ 'Update version number [i.e. update "{}"].'.format(v1),
155+
'Update version line [i.e. update "{}"].'.format(v0),
156+
'List files.',
157+
'Do not update.' ]
158+
159+
while True:
160+
user_selected_option = ask_multiple_choice_question('What do you want to do', options)
161+
162+
if user_selected_option == 0:
163+
new_version_number = ask_question('Enter new version number')
164+
new_version_line = v0.replace(v1, new_version_number)
165+
print('Old version line: "{}". New version line: "{}".'.format(v0, new_version_line))
166+
confirm = ask_yes_no_question('Does it look good')
167+
if confirm == 'yes':
168+
update_version_number_in_files(f, v0, new_version_line)
169+
print('Updated version line to "{}".\n'.format(new_version_line))
170+
break
171+
elif user_selected_option == 1:
172+
new_version_line = ask_question('Enter new version line')
173+
print('Old version line: "{}". New version line: "{}".'.format(v0, new_version_line))
174+
confirm = ask_yes_no_question('Does it look good')
175+
if confirm == 'yes':
176+
update_version_number_in_files(f, v0, new_version_line)
177+
print('Updated version line to "{}".\n'.format(new_version_line))
178+
break
179+
elif user_selected_option == 2:
180+
for item in f:
181+
print(item)
182+
print('\n')
183+
else:
184+
print('Skipping update of {}.\n'.format(v0))
185+
break
186+
187+
def process_components(root_dir, components, exclude_dirs=[]):
188+
for c in components:
189+
print('\n---------------------------------------------')
190+
print('Component: {}'.format(c))
191+
print('---------------------------------------------\n')
192+
wanna_update_version = ask_yes_no_question('Do you want to update the component "{}"'.format(c))
193+
if wanna_update_version == 'yes':
194+
update_version_number_in_a_component(c, root_dir, exclude_dirs=exclude_dirs)
195+
196+
def update_freertos_version_macros(path_macrofile, major, minor, build):
197+
print('\nUpdating preprocessor version macros...')
198+
with open(path_macrofile, encoding='utf-8', errors='ignore', newline='') as macro_file:
199+
macro_file_content = macro_file.read()
200+
match_version = re.search(r'(^.*#define *tskKERNEL_VERSION_NUMBER *(".*")$)', macro_file_content, re.MULTILINE)
201+
match_major = re.search(r'(^.*#define *tskKERNEL_VERSION_MAJOR *(.*)$)', macro_file_content, re.MULTILINE)
202+
match_minor = re.search(r'(^.*#define *tskKERNEL_VERSION_MINOR *(.*)$)', macro_file_content, re.MULTILINE)
203+
match_build = re.search(r'(^.*#define *tskKERNEL_VERSION_BUILD *(.*)$)', macro_file_content, re.MULTILINE)
204+
205+
if match_version.groups() and match_major.groups() and match_minor.groups() and match_build.groups():
206+
(old_version_string, old_version_number) = match_version.groups()
207+
new_version_string = old_version_string.replace(old_version_number, '"V%s.%s.%s"' % (major, minor, build))
208+
macro_file_content = macro_file_content.replace(old_version_string, new_version_string)
209+
210+
(old_major_string, old_major_number) = match_major.groups()
211+
new_major_string = old_major_string.replace(old_major_number, str(major))
212+
macro_file_content = macro_file_content.replace(old_major_string, new_major_string)
213+
214+
(old_minor_string, old_minor_number) = match_minor.groups()
215+
new_minor_string = old_minor_string.replace(old_minor_number, str(minor))
216+
macro_file_content = macro_file_content.replace(old_minor_string, new_minor_string)
217+
218+
(old_build_string, old_build_number) = match_build.groups()
219+
new_build_string = old_build_string.replace(old_build_number, str(build))
220+
macro_file_content = macro_file_content.replace(old_build_string, new_build_string)
221+
222+
with open(path_macrofile, 'w', newline='') as macro_file:
223+
macro_file.write(macro_file_content)
224+
225+
print('Done. Replaced "%s" --> "V%s.%s.%s".' % (old_version_number, major, minor, build))
226+
227+
def update_version_number_in_freertos_component(component, root_dir, old_version, new_version, verbose=False):
228+
print('Updating "%s"...' % component)
229+
component_files = list_files_in_a_component(component, root_dir, ext_filter=None)
230+
version_numbers = defaultdict(list)
231+
n_updated = 0
232+
233+
# Extract version numbers from all the files.
234+
for f in component_files:
235+
file_path = os.path.join(root_dir, f)
236+
version_number = extract_version_number_from_file(file_path)
237+
238+
if version_number[0] != None and version_number[1] != None:
239+
version_numbers[version_number].append(file_path)
240+
241+
for vkey in version_numbers.keys():
242+
old_version_string = vkey[0]
243+
new_version_string = new_version
244+
245+
if old_version in old_version_string:
246+
if old_version_string != new_version_string:
247+
files_using_old_version = version_numbers[vkey]
248+
249+
if verbose:
250+
print('"%s" --> "%s":\n %s' %
251+
(old_version_string,
252+
new_version_string,
253+
'\n '.join(files_using_old_version)))
254+
255+
update_version_number_in_files(files_using_old_version, old_version_string, new_version_string)
256+
n_updated += len(files_using_old_version)
257+
258+
print('Updated "%d" files.' % n_updated)
259+
return n_updated
260+
261+
def process_freertos_components(root_dir, components, old_version, new_version, verbose=False):
262+
print('\nUpdating file header version numbers...')
263+
for c in components:
264+
print('\n---------------------------------------------')
265+
print('Component: {}'.format(c))
266+
print('---------------------------------------------')
267+
update_version_number_in_freertos_component(c, root_dir, old_version, new_version, verbose)
268+
print('Done.')
269+
270+
def parse_freertos_version_number(version_str):
271+
components = version_str.split('.') if version_str else []
272+
if len(components) == 3:
273+
return components
274+
else:
275+
return (None, None, None)
276+
277+
def configure_arg_parser():
278+
'''
279+
Return arg parser, configured for this program
280+
'''
281+
parser = argparse.ArgumentParser(description='Updates file headers with new version numbers for AFR or FreeRTOS. '
282+
'Ideal for preparing upcoming release')
283+
parser.add_argument('--afr', metavar='AFR_DIR', default='', help='Location of the AFR Code')
284+
parser.add_argument('--freertos-dir', metavar='FR_DIR', default='', help='Location of FreeRTOS Code')
285+
parser.add_argument('--freertos-old-version', metavar='FR_OLD_VERSION', default=None, help='New FreeRTOS version (Ex. "FreeRTOS V202011.00"')
286+
parser.add_argument('--freertos-new-version', metavar='FR_NEW_VERSION', default=None, help='Previos FreeRTOS version (Ex. "FreeRTOS Kernel V10.3.0"')
287+
parser.add_argument('--verbose', action='store_true', default=False, help='Increase verbosity of command')
288+
289+
return parser
290+
291+
def main():
292+
'''
293+
Main entry point.
294+
'''
295+
parser = configure_arg_parser()
296+
args = parser.parse_args()
297+
298+
afr_path = args.afr
299+
freertos_path = args.freertos_dir
300+
301+
if afr_path:
302+
print('AFR Code: {}'.format(afr_path))
303+
process_components(afr_path, _AFR_COMPONENTS, exclude_dirs=_AFR_EXCLUDE_DIRS)
304+
305+
if freertos_path:
306+
# Freertos version is the 'kernel version' string. Since that's a required arg, we replace all kernel version
307+
# strings with the cmd input, instead of requiring user to input that into every version string match
308+
print('FreeRTOS Code:\n %s' % freertos_path)
309+
print('Old Version:\n %s' % args.freertos_old_version)
310+
print('New Version:\n %s' % args.freertos_new_version)
311+
process_freertos_components(freertos_path, _FREERTOS_COMPONENTS, args.freertos_old_version.strip(), args.freertos_new_version.strip(), verbose=args.verbose)
312+
313+
if not afr_path and not freertos_path:
314+
parser.print_help()
315+
316+
if __name__ == '__main__':
317+
main()

0 commit comments

Comments
 (0)