mirror of
https://github.com/FreeRTOS/FreeRTOS.git
synced 2026-08-04 04:04:54 +00:00
* Rename both Demo/FreeRTOS-IoT-Libraries and Source/FreeRTOS_IoT_Libraries directories to FreeRTOS-IoT-Libraries-LTS-Beta1 * Rename directories within IDE project and source files * Remove backup files pushed to remote accidentally * Remaining directory name replacements in file texts
313 lines
12 KiB
Python
313 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import io
|
|
import os
|
|
import re
|
|
import json
|
|
import argparse
|
|
import subprocess
|
|
from json_report_generator import update_json_report
|
|
from makefile_generator import generate_makefile_from_template
|
|
|
|
|
|
__THIS_FILE_PATH__ = os.path.dirname(os.path.abspath(__file__))
|
|
__MAKE_FILE_TEMPLATE__ = os.path.join(__THIS_FILE_PATH__, 'template', 'Makefile.template')
|
|
__GENERATED_MAKE_FILE__ = os.path.join(__THIS_FILE_PATH__, 'Makefile')
|
|
|
|
__JSON_REPORT_TEMPLATE__ = os.path.join(__THIS_FILE_PATH__, 'template', 'report.json.template')
|
|
__GENERATED_JSON_REPORT__ = os.path.join(__THIS_FILE_PATH__, 'freertos_lts_memory_estimates.json')
|
|
__REPORT_LIBS_JSON__ = os.path.join(__THIS_FILE_PATH__, 'template', 'report_libs.json')
|
|
|
|
__FREERTOS_SRC_DIR__ = os.path.join('FreeRTOS', 'Source')
|
|
__FREERTOS_PLUS_SRC_DIR__ = os.path.join('FreeRTOS-Plus', 'Source')
|
|
__IOT_LIBS_DIR__ = os.path.join(__FREERTOS_PLUS_SRC_DIR__, 'FreeRTOS-IoT-Libraries-LTS-Beta1')
|
|
|
|
__LIB_NAME_TO_SRC_DIRS_MAPPING__ = {
|
|
'light-mqtt' : [
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'standard', 'mqtt', 'src', 'iot_mqtt_lightweight_api.c'),
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'standard', 'mqtt', 'src', 'iot_mqtt_helper.c')
|
|
],
|
|
'mqtt' : [
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'standard', 'mqtt', 'src')
|
|
],
|
|
'https' : [
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'standard', 'https', 'src'),
|
|
os.path.join(__FREERTOS_PLUS_SRC_DIR__, 'http-parser')
|
|
],
|
|
'shadow' : [
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'shadow', 'src'),
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'common', 'src')
|
|
],
|
|
'jobs' : [
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'jobs', 'src'),
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'common', 'src')
|
|
],
|
|
'ota-mqtt' : [
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'ota', 'src', 'aws_iot_ota_agent.c'),
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'ota', 'src', 'aws_iot_ota_interface.c'),
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'ota', 'src', 'mqtt', 'aws_iot_ota_mqtt.c'),
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'ota', 'src', 'mqtt', 'aws_iot_ota_cbor.c')
|
|
],
|
|
'ota-http' : [
|
|
os.path.join(__IOT_LIBS_DIR__, 'c_sdk', 'aws', 'ota', 'src')
|
|
],
|
|
'kernel' : [
|
|
os.path.join(__FREERTOS_SRC_DIR__, 'event_groups.c'),
|
|
os.path.join(__FREERTOS_SRC_DIR__, 'list.c'),
|
|
os.path.join(__FREERTOS_SRC_DIR__, 'queue.c'),
|
|
os.path.join(__FREERTOS_SRC_DIR__, 'stream_buffer.c'),
|
|
os.path.join(__FREERTOS_SRC_DIR__, 'tasks.c'),
|
|
os.path.join(__FREERTOS_SRC_DIR__, 'timers.c'),
|
|
os.path.join(__FREERTOS_SRC_DIR__, 'portable', 'GCC', 'ARM_CM4F', 'port.c')
|
|
]
|
|
}
|
|
|
|
|
|
def apply_patches(freertos_lts, lib_name):
|
|
patches = {}
|
|
|
|
# This should ideally be empty as we should be able to control all the
|
|
# configurations via config files. However, OTA currently defines the
|
|
# logging level in a header file as opposed to taking it from a config file.
|
|
# As a result, we need to patch the header file before calculating the size
|
|
# as we want to turn off logging while calculating size. Later when logging
|
|
# level is moved to a config file, it will not be needed and we will turn
|
|
# off logging in the header file tools\memory_estimator\config_files\aws_ota_agent_config.h.
|
|
if 'ota' in lib_name:
|
|
ota_agent_header_file = os.path.join(freertos_lts, __IOT_LIBS_DIR__, 'c_sdk', 'aws', 'ota', 'include', 'aws_iot_ota_agent.h')
|
|
|
|
with open(ota_agent_header_file) as ota_agent_header_file_handle:
|
|
original_content = ota_agent_header_file_handle.read()
|
|
|
|
# Turn off logging in the OTA code while calculating sizes.
|
|
patched_content = original_content.replace('#define OTA_DEBUG_LOG_LEVEL 1', '#define OTA_DEBUG_LOG_LEVEL 0')
|
|
|
|
with open(ota_agent_header_file, 'w') as ota_agent_header_file_handle:
|
|
ota_agent_header_file_handle.write(patched_content)
|
|
|
|
patches[ota_agent_header_file] = original_content
|
|
|
|
return patches
|
|
|
|
|
|
def revert_patches(patches):
|
|
for patched_file in patches:
|
|
original_content = patches[patched_file]
|
|
with open(patched_file, 'w') as patched_file_handle:
|
|
patched_file_handle.write(original_content)
|
|
|
|
|
|
def calculate_sizes(freertos_lts, optimization, lib_name, compiler, sizetool, dontclean):
|
|
# Apply the required patches to the source code.
|
|
patches = apply_patches(freertos_lts, lib_name)
|
|
|
|
# Generate Makefile.
|
|
generate_makefile(freertos_lts, optimization, lib_name)
|
|
|
|
# Run make build.
|
|
warnings = make(compiler)
|
|
|
|
# Run make size.
|
|
calculated_sizes = calculate_size(sizetool)
|
|
|
|
# Remove the generated artifacts.
|
|
if not dontclean:
|
|
clean()
|
|
remove_generated_makefile()
|
|
|
|
# Print the generated warnings.
|
|
pretty_print_warnings(warnings)
|
|
|
|
# Print the calculated sizes.
|
|
pretty_print_sizes(calculated_sizes)
|
|
|
|
# Revert the applied patches to the source code.
|
|
revert_patches(patches)
|
|
|
|
return calculated_sizes
|
|
|
|
|
|
def generate_makefile(freertos_lts, optimization, lib_name):
|
|
freertos_src_dir = os.path.join(freertos_lts, __FREERTOS_SRC_DIR__)
|
|
freertos_plus_src_dir = os.path.join(freertos_lts, __FREERTOS_PLUS_SRC_DIR__)
|
|
|
|
# Generate include dirs list.
|
|
include_dirs = []
|
|
for root, dirs, files in os.walk(freertos_plus_src_dir):
|
|
for dir in dirs:
|
|
include_dirs.append(os.path.join(root, dir))
|
|
|
|
# Add FreeRTOS include dirs for Cortex-M4 to include dirs list.
|
|
include_dirs.append(os.path.join(freertos_src_dir, 'include'))
|
|
include_dirs.append(os.path.join(freertos_src_dir, 'portable', 'GCC', 'ARM_CM4F'))
|
|
|
|
# Add config files dir to include dirs list.
|
|
include_dirs.append(os.path.join(__THIS_FILE_PATH__, 'config_files'))
|
|
|
|
# Generate source files list.
|
|
if lib_name in __LIB_NAME_TO_SRC_DIRS_MAPPING__:
|
|
source_files=[]
|
|
for lib_src_location in __LIB_NAME_TO_SRC_DIRS_MAPPING__[lib_name]:
|
|
lib_src_full_path = os.path.join(freertos_lts, lib_src_location)
|
|
|
|
# Traverse if the location is a directory, otherwise just append it.
|
|
if os.path.isdir(lib_src_full_path):
|
|
for root, dirs, files in os.walk(lib_src_full_path):
|
|
for file in files:
|
|
if file.endswith('.c'):
|
|
source_files.append(os.path.join(root, file))
|
|
else:
|
|
source_files.append(lib_src_full_path)
|
|
else:
|
|
raise ValueError('Library name: {} is invalid'.format(lib_name))
|
|
|
|
# Generate makefile.
|
|
generate_makefile_from_template(source_files,
|
|
include_dirs,
|
|
optimization,
|
|
__MAKE_FILE_TEMPLATE__,
|
|
__GENERATED_MAKE_FILE__)
|
|
|
|
|
|
def make(compiler):
|
|
compiler_command='CC={}'.format(compiler)
|
|
|
|
print('---- Starting Build. Compiler: {} ---- \n'.format(compiler))
|
|
|
|
proc = subprocess.Popen(['make', '-f', __GENERATED_MAKE_FILE__, compiler_command],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT)
|
|
|
|
warnings = []
|
|
for line in iter(proc.stdout.readline, b''):
|
|
line = line.decode('utf-8').strip()
|
|
print(line)
|
|
if re.match('.*: warning: .*', line):
|
|
warnings.append(line.strip())
|
|
|
|
print('---- Build Finished ---- \n')
|
|
return warnings
|
|
|
|
|
|
def calculate_size(sizetool):
|
|
sizetool_command='SIZETOOL={}'.format(sizetool)
|
|
|
|
proc = subprocess.Popen(['make', '-f', __GENERATED_MAKE_FILE__, 'size', sizetool_command],
|
|
stdout=subprocess.PIPE)
|
|
|
|
calculated_sizes = []
|
|
for line in iter(proc.stdout.readline, b''):
|
|
line = line.decode('utf-8').strip()
|
|
calculated_sizes.append(line)
|
|
|
|
return calculated_sizes
|
|
|
|
|
|
def clean():
|
|
proc = subprocess.Popen(['make', '-f', __GENERATED_MAKE_FILE__, 'clean'],
|
|
stdout=subprocess.PIPE)
|
|
|
|
for line in iter(proc.stdout.readline, b''):
|
|
line = line.decode('utf-8').strip()
|
|
print(line)
|
|
|
|
|
|
def remove_generated_makefile():
|
|
os.remove(__GENERATED_MAKE_FILE__)
|
|
|
|
|
|
def pretty_print_warnings(warnings):
|
|
if len(warnings) > 0:
|
|
print('\n\n---- Warnings ---- ')
|
|
print('------------------')
|
|
for warning in warnings:
|
|
print(warning)
|
|
print('------------------')
|
|
|
|
|
|
def pretty_print_sizes(sizes):
|
|
print('\n\n---- Sizes ---- ')
|
|
print('---------------')
|
|
for size in sizes[1:]:
|
|
print(size)
|
|
print('---------------')
|
|
|
|
|
|
def parse_arguments():
|
|
parser = argparse.ArgumentParser(description='Memory Estimator.')
|
|
|
|
parser.add_argument('-p', '--lts-path', required=True, help='Path to FreeRTOS LTS directory.')
|
|
parser.add_argument('-o', '--optimization', default='O0', help='Compiler optimization (O0, Os etc.).')
|
|
parser.add_argument('-l', '--lib', default='mqtt', help='Library name to generate the memory estimate for.')
|
|
parser.add_argument('-c', '--compiler', default='arm-none-eabi-gcc', help='Compiler to use.')
|
|
parser.add_argument('-s', '--sizetool', default='arm-none-eabi-size', help='Size tool to use.')
|
|
parser.add_argument('-d', '--dontclean', action='store_true', help='Do not clean the generated artifacts.')
|
|
parser.add_argument('-g', '--generate-report', action='store_true', help='Generate the JSON report.')
|
|
|
|
return vars(parser.parse_args())
|
|
|
|
|
|
|
|
def main():
|
|
args = parse_arguments()
|
|
|
|
if args['generate_report']:
|
|
# Start with an empty JSON report which is populated as sizes are
|
|
# calculated.
|
|
generated_json_report = {}
|
|
|
|
# Read the libraries which are to be included in the report.
|
|
with open(__REPORT_LIBS_JSON__) as report_libs_file:
|
|
report_libs_json_data = json.load(report_libs_file)
|
|
report_libs = report_libs_json_data['libraries']
|
|
|
|
# Read the template to obtain the library report format.
|
|
with open(__JSON_REPORT_TEMPLATE__) as json_report_template_handle:
|
|
json_report_template_data = json.load(json_report_template_handle)
|
|
lib_report_template = json_report_template_data['lib']
|
|
|
|
# JSON report has sizes for all the libraries and for both O1 and Os
|
|
# Optimizations. Therefore, values for --lib and --optimization are
|
|
# ignored.
|
|
# Compiled objects files for 'O1' optimization needs to be cleaned
|
|
# before 'Os' optimization and therefore, value for --dontclean is
|
|
# ignored.
|
|
for report_lib in report_libs:
|
|
lib_name = report_lib['lib_name']
|
|
size_description = report_lib['size_description']
|
|
|
|
o1_sizes = calculate_sizes(args['lts_path'],
|
|
'O1',
|
|
lib_name,
|
|
args['compiler'],
|
|
args['sizetool'],
|
|
False)
|
|
|
|
os_sizes = calculate_sizes(args['lts_path'],
|
|
'Os',
|
|
lib_name,
|
|
args['compiler'],
|
|
args['sizetool'],
|
|
False)
|
|
|
|
update_json_report(lib_name,
|
|
o1_sizes,
|
|
os_sizes,
|
|
size_description,
|
|
lib_report_template,
|
|
generated_json_report)
|
|
|
|
# Write the generated report to the output file.
|
|
with open(__GENERATED_JSON_REPORT__, 'w') as json_report_file_handle:
|
|
json_report_file_handle.write(json.dumps(generated_json_report, indent=4, sort_keys=True))
|
|
else:
|
|
calculate_sizes(args['lts_path'],
|
|
args['optimization'],
|
|
args['lib'],
|
|
args['compiler'],
|
|
args['sizetool'],
|
|
args['dontclean'])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main() |