mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #2047 from PrzemekWirkus/devel_mmap_proj
[Tools] Add summary for test buildingpull/2117/merge
commit
9f33ba87b0
|
@ -176,7 +176,7 @@ def build_project(src_path, build_path, target, toolchain_name,
|
||||||
|
|
||||||
if report != None:
|
if report != None:
|
||||||
start = time()
|
start = time()
|
||||||
|
|
||||||
# If project_id is specified, use that over the default name
|
# If project_id is specified, use that over the default name
|
||||||
id_name = project_id.upper() if project_id else name.upper()
|
id_name = project_id.upper() if project_id else name.upper()
|
||||||
description = project_description if project_description else name
|
description = project_description if project_description else name
|
||||||
|
@ -232,6 +232,7 @@ def build_project(src_path, build_path, target, toolchain_name,
|
||||||
cur_result["elapsed_time"] = end - start
|
cur_result["elapsed_time"] = end - start
|
||||||
cur_result["output"] = toolchain.get_output()
|
cur_result["output"] = toolchain.get_output()
|
||||||
cur_result["result"] = "OK"
|
cur_result["result"] = "OK"
|
||||||
|
cur_result["memory_usage"] = toolchain.map_outputs
|
||||||
|
|
||||||
add_result_to_report(report, cur_result)
|
add_result_to_report(report, cur_result)
|
||||||
|
|
||||||
|
@ -294,7 +295,7 @@ def build_library(src_paths, build_path, target, toolchain_name,
|
||||||
|
|
||||||
if report != None:
|
if report != None:
|
||||||
start = time()
|
start = time()
|
||||||
|
|
||||||
# If project_id is specified, use that over the default name
|
# If project_id is specified, use that over the default name
|
||||||
id_name = project_id.upper() if project_id else name.upper()
|
id_name = project_id.upper() if project_id else name.upper()
|
||||||
description = name
|
description = name
|
||||||
|
@ -377,7 +378,7 @@ def build_library(src_paths, build_path, target, toolchain_name,
|
||||||
toolchain.copy_files(resources.libraries, build_path, resources=resources)
|
toolchain.copy_files(resources.libraries, build_path, resources=resources)
|
||||||
if resources.linker_script:
|
if resources.linker_script:
|
||||||
toolchain.copy_files(resources.linker_script, build_path, resources=resources)
|
toolchain.copy_files(resources.linker_script, build_path, resources=resources)
|
||||||
|
|
||||||
if resource.hex_files:
|
if resource.hex_files:
|
||||||
toolchain.copy_files(resources.hex_files, build_path, resources=resources)
|
toolchain.copy_files(resources.hex_files, build_path, resources=resources)
|
||||||
|
|
||||||
|
@ -399,12 +400,12 @@ def build_library(src_paths, build_path, target, toolchain_name,
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if report != None:
|
if report != None:
|
||||||
end = time()
|
end = time()
|
||||||
|
|
||||||
if isinstance(e, ToolException):
|
if isinstance(e, ToolException):
|
||||||
cur_result["result"] = "FAIL"
|
cur_result["result"] = "FAIL"
|
||||||
elif isinstance(e, NotSupportedException):
|
elif isinstance(e, NotSupportedException):
|
||||||
cur_result["result"] = "NOT_SUPPORTED"
|
cur_result["result"] = "NOT_SUPPORTED"
|
||||||
|
|
||||||
cur_result["elapsed_time"] = end - start
|
cur_result["elapsed_time"] = end - start
|
||||||
|
|
||||||
toolchain_output = toolchain.get_output()
|
toolchain_output = toolchain.get_output()
|
||||||
|
@ -428,7 +429,7 @@ def build_lib(lib_id, target, toolchain_name, options=None, verbose=False, clean
|
||||||
if not lib.is_supported(target, toolchain_name):
|
if not lib.is_supported(target, toolchain_name):
|
||||||
print 'Library "%s" is not yet supported on target %s with toolchain %s' % (lib_id, target.name, toolchain)
|
print 'Library "%s" is not yet supported on target %s with toolchain %s' % (lib_id, target.name, toolchain)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# We need to combine macros from parameter list with macros from library definition
|
# We need to combine macros from parameter list with macros from library definition
|
||||||
MACROS = lib.macros if lib.macros else []
|
MACROS = lib.macros if lib.macros else []
|
||||||
if macros:
|
if macros:
|
||||||
|
@ -441,7 +442,7 @@ def build_lib(lib_id, target, toolchain_name, options=None, verbose=False, clean
|
||||||
dependencies_paths = lib.dependencies
|
dependencies_paths = lib.dependencies
|
||||||
inc_dirs = lib.inc_dirs
|
inc_dirs = lib.inc_dirs
|
||||||
inc_dirs_ext = lib.inc_dirs_ext
|
inc_dirs_ext = lib.inc_dirs_ext
|
||||||
|
|
||||||
""" src_path: the path of the source directory
|
""" src_path: the path of the source directory
|
||||||
build_path: the path of the build directory
|
build_path: the path of the build directory
|
||||||
target: ['LPC1768', 'LPC11U24', 'LPC2368']
|
target: ['LPC1768', 'LPC11U24', 'LPC2368']
|
||||||
|
@ -522,7 +523,7 @@ def build_lib(lib_id, target, toolchain_name, options=None, verbose=False, clean
|
||||||
# Copy Headers
|
# Copy Headers
|
||||||
for resource in resources:
|
for resource in resources:
|
||||||
toolchain.copy_files(resource.headers, build_path, resources=resource)
|
toolchain.copy_files(resource.headers, build_path, resources=resource)
|
||||||
|
|
||||||
dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
|
dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
|
||||||
|
|
||||||
# Compile Sources
|
# Compile Sources
|
||||||
|
@ -716,7 +717,7 @@ def mcu_toolchain_matrix(verbose_html=False, platform_filter=None):
|
||||||
perm_counter += 1
|
perm_counter += 1
|
||||||
else:
|
else:
|
||||||
text = "-"
|
text = "-"
|
||||||
|
|
||||||
row.append(text)
|
row.append(text)
|
||||||
pt.add_row(row)
|
pt.add_row(row)
|
||||||
|
|
||||||
|
@ -942,6 +943,49 @@ def print_build_results(result_list, build_name):
|
||||||
result += "\n"
|
result += "\n"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def print_build_memory_usage_results(report):
|
||||||
|
""" Generate result table with memory usage values for build results
|
||||||
|
Agregates (puts together) reports obtained from self.get_memory_summary()
|
||||||
|
@param report Report generated during build procedure. See
|
||||||
|
"""
|
||||||
|
from prettytable import PrettyTable
|
||||||
|
columns_text = ['name', 'target', 'toolchain']
|
||||||
|
columns_int = ['static_ram', 'stack', 'heap', 'total_ram', 'total_flash']
|
||||||
|
table = PrettyTable(columns_text + columns_int)
|
||||||
|
|
||||||
|
for col in columns_text:
|
||||||
|
table.align[col] = 'l'
|
||||||
|
|
||||||
|
for col in columns_int:
|
||||||
|
table.align[col] = 'r'
|
||||||
|
|
||||||
|
for target in report:
|
||||||
|
for toolchain in report[target]:
|
||||||
|
for name in report[target][toolchain]:
|
||||||
|
for dlist in report[target][toolchain][name]:
|
||||||
|
for dlistelem in dlist:
|
||||||
|
# Get 'memory_usage' record and build table with statistics
|
||||||
|
record = dlist[dlistelem]
|
||||||
|
if 'memory_usage' in record and record['memory_usage']:
|
||||||
|
# Note that summary should be in the last record of
|
||||||
|
# 'memory_usage' section. This is why we are grabbing
|
||||||
|
# last "[-1]" record.
|
||||||
|
row = [
|
||||||
|
record['description'],
|
||||||
|
record['target_name'],
|
||||||
|
record['toolchain_name'],
|
||||||
|
record['memory_usage'][-1]['summary']['static_ram'],
|
||||||
|
record['memory_usage'][-1]['summary']['stack'],
|
||||||
|
record['memory_usage'][-1]['summary']['heap'],
|
||||||
|
record['memory_usage'][-1]['summary']['total_ram'],
|
||||||
|
record['memory_usage'][-1]['summary']['total_flash'],
|
||||||
|
]
|
||||||
|
table.add_row(row)
|
||||||
|
|
||||||
|
result = "Memory map breakdown for built projects (values in Bytes):\n"
|
||||||
|
result += table.get_string(sortby='name')
|
||||||
|
return result
|
||||||
|
|
||||||
def write_build_report(build_report, template_filename, filename):
|
def write_build_report(build_report, template_filename, filename):
|
||||||
build_report_failing = []
|
build_report_failing = []
|
||||||
build_report_passing = []
|
build_report_passing = []
|
||||||
|
@ -963,14 +1007,14 @@ def write_build_report(build_report, template_filename, filename):
|
||||||
def scan_for_source_paths(path, exclude_paths=None):
|
def scan_for_source_paths(path, exclude_paths=None):
|
||||||
ignorepatterns = []
|
ignorepatterns = []
|
||||||
paths = []
|
paths = []
|
||||||
|
|
||||||
def is_ignored(file_path):
|
def is_ignored(file_path):
|
||||||
for pattern in ignorepatterns:
|
for pattern in ignorepatterns:
|
||||||
if fnmatch.fnmatch(file_path, pattern):
|
if fnmatch.fnmatch(file_path, pattern):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
""" os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
|
""" os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
|
||||||
When topdown is True, the caller can modify the dirnames list in-place
|
When topdown is True, the caller can modify the dirnames list in-place
|
||||||
(perhaps using del or slice assignment), and walk() will only recurse into
|
(perhaps using del or slice assignment), and walk() will only recurse into
|
||||||
|
|
|
@ -26,9 +26,9 @@ class MemapParser(object):
|
||||||
|
|
||||||
self.misc_flash_sections = ('.interrupts', '.flash_config')
|
self.misc_flash_sections = ('.interrupts', '.flash_config')
|
||||||
|
|
||||||
self.other_sections = ('.interrupts_ram', '.init', '.ARM.extab', \
|
self.other_sections = ('.interrupts_ram', '.init', '.ARM.extab',
|
||||||
'.ARM.exidx', '.ARM.attributes', '.eh_frame', \
|
'.ARM.exidx', '.ARM.attributes', '.eh_frame',
|
||||||
'.init_array', '.fini_array', '.jcr', '.stab', \
|
'.init_array', '.fini_array', '.jcr', '.stab',
|
||||||
'.stabstr', '.ARM.exidx', '.ARM')
|
'.stabstr', '.ARM.exidx', '.ARM')
|
||||||
|
|
||||||
# sections to print info (generic for all toolchains)
|
# sections to print info (generic for all toolchains)
|
||||||
|
@ -43,6 +43,9 @@ class MemapParser(object):
|
||||||
# list of all object files and mappting to module names
|
# list of all object files and mappting to module names
|
||||||
self.object_to_module = dict()
|
self.object_to_module = dict()
|
||||||
|
|
||||||
|
# Memory usage summary structure
|
||||||
|
self.mem_summary = dict()
|
||||||
|
|
||||||
def module_add(self, module_name, size, section):
|
def module_add(self, module_name, size, section):
|
||||||
"""
|
"""
|
||||||
Adds a module / section to the list
|
Adds a module / section to the list
|
||||||
|
@ -67,7 +70,7 @@ class MemapParser(object):
|
||||||
return i # should name of the section (assuming it's a known one)
|
return i # should name of the section (assuming it's a known one)
|
||||||
|
|
||||||
if line.startswith('.'):
|
if line.startswith('.'):
|
||||||
return 'unknown' # all others are clasified are unknown
|
return 'unknown' # all others are classified are unknown
|
||||||
else:
|
else:
|
||||||
return False # everything else, means no change in section
|
return False # everything else, means no change in section
|
||||||
|
|
||||||
|
@ -363,11 +366,12 @@ class MemapParser(object):
|
||||||
|
|
||||||
# Create table
|
# Create table
|
||||||
columns = ['Module']
|
columns = ['Module']
|
||||||
for i in list(self.print_sections):
|
columns.extend(self.print_sections)
|
||||||
columns.append(i)
|
|
||||||
|
|
||||||
table = PrettyTable(columns)
|
table = PrettyTable(columns)
|
||||||
table.align["Module"] = "l"
|
table.align["Module"] = "l"
|
||||||
|
for col in self.print_sections:
|
||||||
|
table.align[col] = 'r'
|
||||||
|
|
||||||
for i in list(self.print_sections):
|
for i in list(self.print_sections):
|
||||||
table.align[i] = 'r'
|
table.align[i] = 'r'
|
||||||
|
@ -388,8 +392,12 @@ class MemapParser(object):
|
||||||
for k in self.print_sections:
|
for k in self.print_sections:
|
||||||
row.append(self.modules[i][k])
|
row.append(self.modules[i][k])
|
||||||
|
|
||||||
json_obj.append({"module":i, "size":{\
|
json_obj.append({
|
||||||
k:self.modules[i][k] for k in self.print_sections}})
|
"module":i,
|
||||||
|
"size":{
|
||||||
|
k:self.modules[i][k] for k in self.print_sections
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
table.add_row(row)
|
table.add_row(row)
|
||||||
|
|
||||||
|
@ -399,16 +407,19 @@ class MemapParser(object):
|
||||||
|
|
||||||
table.add_row(subtotal_row)
|
table.add_row(subtotal_row)
|
||||||
|
|
||||||
if export_format == 'json':
|
summary = {
|
||||||
json_obj.append({\
|
'summary':{
|
||||||
'summary':{\
|
'static_ram':(subtotal['.data']+subtotal['.bss']),
|
||||||
'total_static_ram':(subtotal['.data']+subtotal['.bss']),\
|
'heap':(subtotal['.heap']),
|
||||||
'allocated_heap':(subtotal['.heap']),\
|
'stack':(subtotal['.stack']),
|
||||||
'allocated_stack':(subtotal['.stack']),\
|
'total_ram':(subtotal['.data']+subtotal['.bss']+subtotal['.heap']+subtotal['.stack']),
|
||||||
'total_ram':(subtotal['.data']+subtotal['.bss']+subtotal['.heap']+subtotal['.stack']),\
|
'total_flash':(subtotal['.text']+subtotal['.data']+misc_flash_mem),
|
||||||
'total_flash':(subtotal['.text']+subtotal['.data']+misc_flash_mem),}})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
file_desc.write(json.dumps(json_obj, indent=4))
|
if export_format == 'json':
|
||||||
|
json_to_file = json_obj + [summary]
|
||||||
|
file_desc.write(json.dumps(json_to_file, indent=4))
|
||||||
file_desc.write('\n')
|
file_desc.write('\n')
|
||||||
|
|
||||||
elif export_format == 'csv-ci': # CSV format for the CI system
|
elif export_format == 'csv-ci': # CSV format for the CI system
|
||||||
|
@ -467,33 +478,38 @@ class MemapParser(object):
|
||||||
if file_desc is not sys.stdout:
|
if file_desc is not sys.stdout:
|
||||||
file_desc.close()
|
file_desc.close()
|
||||||
|
|
||||||
|
self.mem_summary = json_obj + [summary]
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_memory_summary(self):
|
||||||
|
"""! Object is available only after self.generate_output('json') is called
|
||||||
|
@return Return memory summary object
|
||||||
|
"""
|
||||||
|
return self.mem_summary
|
||||||
|
|
||||||
def parse(self, mapfile, toolchain):
|
def parse(self, mapfile, toolchain):
|
||||||
"""
|
"""
|
||||||
Parse and decode map file depending on the toolchain
|
Parse and decode map file depending on the toolchain
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
result = True
|
||||||
try:
|
try:
|
||||||
file_input = open(mapfile, 'rt')
|
with open(mapfile, 'rt') as file_input:
|
||||||
|
if toolchain == "ARM" or toolchain == "ARM_STD" or toolchain == "ARM_MICRO":
|
||||||
|
self.search_objects(os.path.abspath(mapfile), "ARM")
|
||||||
|
self.parse_map_file_armcc(file_input)
|
||||||
|
elif toolchain == "GCC_ARM":
|
||||||
|
self.parse_map_file_gcc(file_input)
|
||||||
|
elif toolchain == "IAR":
|
||||||
|
self.search_objects(os.path.abspath(mapfile), toolchain)
|
||||||
|
self.parse_map_file_iar(file_input)
|
||||||
|
else:
|
||||||
|
result = False
|
||||||
except IOError as error:
|
except IOError as error:
|
||||||
print "I/O error({0}): {1}".format(error.errno, error.strerror)
|
print "I/O error({0}): {1}".format(error.errno, error.strerror)
|
||||||
return False
|
result = False
|
||||||
|
return result
|
||||||
if toolchain == "ARM" or toolchain == "ARM_STD" or toolchain == "ARM_MICRO":
|
|
||||||
self.search_objects(os.path.abspath(mapfile), "ARM")
|
|
||||||
self.parse_map_file_armcc(file_input)
|
|
||||||
elif toolchain == "GCC_ARM":
|
|
||||||
self.parse_map_file_gcc(file_input)
|
|
||||||
elif toolchain == "IAR":
|
|
||||||
self.search_objects(os.path.abspath(mapfile), toolchain)
|
|
||||||
self.parse_map_file_iar(file_input)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
file_input.close()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ sys.path.insert(0, ROOT)
|
||||||
from tools.test_api import test_path_to_name, find_tests, print_tests, build_tests, test_spec_from_test_builds
|
from tools.test_api import test_path_to_name, find_tests, print_tests, build_tests, test_spec_from_test_builds
|
||||||
from tools.options import get_default_options_parser
|
from tools.options import get_default_options_parser
|
||||||
from tools.build_api import build_project, build_library
|
from tools.build_api import build_project, build_library
|
||||||
|
from tools.build_api import print_build_memory_usage_results
|
||||||
from tools.targets import TARGET_MAP
|
from tools.targets import TARGET_MAP
|
||||||
from tools.utils import mkdir, ToolException, NotSupportedException
|
from tools.utils import mkdir, ToolException, NotSupportedException
|
||||||
from tools.test_exporters import ReportExporter, ResultExporterType
|
from tools.test_exporters import ReportExporter, ResultExporterType
|
||||||
|
@ -37,12 +38,12 @@ if __name__ == '__main__':
|
||||||
try:
|
try:
|
||||||
# Parse Options
|
# Parse Options
|
||||||
parser = get_default_options_parser()
|
parser = get_default_options_parser()
|
||||||
|
|
||||||
parser.add_option("-D", "",
|
parser.add_option("-D", "",
|
||||||
action="append",
|
action="append",
|
||||||
dest="macros",
|
dest="macros",
|
||||||
help="Add a macro definition")
|
help="Add a macro definition")
|
||||||
|
|
||||||
parser.add_option("-j", "--jobs",
|
parser.add_option("-j", "--jobs",
|
||||||
type="int",
|
type="int",
|
||||||
dest="jobs",
|
dest="jobs",
|
||||||
|
@ -60,25 +61,25 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
parser.add_option("-p", "--paths", dest="paths",
|
parser.add_option("-p", "--paths", dest="paths",
|
||||||
default=None, help="Limit the tests to those within the specified comma separated list of paths")
|
default=None, help="Limit the tests to those within the specified comma separated list of paths")
|
||||||
|
|
||||||
format_choices = ["list", "json"]
|
format_choices = ["list", "json"]
|
||||||
format_default_choice = "list"
|
format_default_choice = "list"
|
||||||
format_help = "Change the format in which tests are listed. Choices include: %s. Default: %s" % (", ".join(format_choices), format_default_choice)
|
format_help = "Change the format in which tests are listed. Choices include: %s. Default: %s" % (", ".join(format_choices), format_default_choice)
|
||||||
parser.add_option("-f", "--format", type="choice", dest="format",
|
parser.add_option("-f", "--format", type="choice", dest="format",
|
||||||
choices=format_choices, default=format_default_choice, help=format_help)
|
choices=format_choices, default=format_default_choice, help=format_help)
|
||||||
|
|
||||||
parser.add_option("--continue-on-build-fail", action="store_true", dest="continue_on_build_fail",
|
parser.add_option("--continue-on-build-fail", action="store_true", dest="continue_on_build_fail",
|
||||||
default=None, help="Continue trying to build all tests if a build failure occurs")
|
default=None, help="Continue trying to build all tests if a build failure occurs")
|
||||||
|
|
||||||
parser.add_option("-n", "--names", dest="names",
|
parser.add_option("-n", "--names", dest="names",
|
||||||
default=None, help="Limit the tests to a comma separated list of names")
|
default=None, help="Limit the tests to a comma separated list of names")
|
||||||
|
|
||||||
parser.add_option("--test-spec", dest="test_spec",
|
parser.add_option("--test-spec", dest="test_spec",
|
||||||
default=None, help="Destination path for a test spec file that can be used by the Greentea automated test tool")
|
default=None, help="Destination path for a test spec file that can be used by the Greentea automated test tool")
|
||||||
|
|
||||||
parser.add_option("--build-report-junit", dest="build_report_junit",
|
parser.add_option("--build-report-junit", dest="build_report_junit",
|
||||||
default=None, help="Destination path for a build report in the JUnit xml format")
|
default=None, help="Destination path for a build report in the JUnit xml format")
|
||||||
|
|
||||||
parser.add_option("-v", "--verbose",
|
parser.add_option("-v", "--verbose",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
dest="verbose",
|
dest="verbose",
|
||||||
|
@ -87,24 +88,24 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
# Filter tests by path if specified
|
# Filter tests by path if specified
|
||||||
if options.paths:
|
if options.paths:
|
||||||
all_paths = options.paths.split(",")
|
all_paths = options.paths.split(",")
|
||||||
else:
|
else:
|
||||||
all_paths = ["."]
|
all_paths = ["."]
|
||||||
|
|
||||||
all_tests = {}
|
all_tests = {}
|
||||||
tests = {}
|
tests = {}
|
||||||
|
|
||||||
# Find all tests in the relevant paths
|
# Find all tests in the relevant paths
|
||||||
for path in all_paths:
|
for path in all_paths:
|
||||||
all_tests.update(find_tests(path))
|
all_tests.update(find_tests(path))
|
||||||
|
|
||||||
# Filter tests by name if specified
|
# Filter tests by name if specified
|
||||||
if options.names:
|
if options.names:
|
||||||
all_names = options.names.split(",")
|
all_names = options.names.split(",")
|
||||||
all_names = [x.lower() for x in all_names]
|
all_names = [x.lower() for x in all_names]
|
||||||
|
|
||||||
for name in all_names:
|
for name in all_names:
|
||||||
if any(fnmatch.fnmatch(testname, name) for testname in all_tests):
|
if any(fnmatch.fnmatch(testname, name) for testname in all_tests):
|
||||||
for testname, test in all_tests.items():
|
for testname, test in all_tests.items():
|
||||||
|
@ -124,16 +125,16 @@ if __name__ == '__main__':
|
||||||
if not options.build_dir:
|
if not options.build_dir:
|
||||||
print "[ERROR] You must specify a build path"
|
print "[ERROR] You must specify a build path"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
base_source_paths = options.source_dir
|
base_source_paths = options.source_dir
|
||||||
|
|
||||||
# Default base source path is the current directory
|
# Default base source path is the current directory
|
||||||
if not base_source_paths:
|
if not base_source_paths:
|
||||||
base_source_paths = ['.']
|
base_source_paths = ['.']
|
||||||
|
|
||||||
|
|
||||||
target = options.mcu
|
target = options.mcu
|
||||||
|
|
||||||
build_report = {}
|
build_report = {}
|
||||||
build_properties = {}
|
build_properties = {}
|
||||||
|
|
||||||
|
@ -150,7 +151,7 @@ if __name__ == '__main__':
|
||||||
macros=options.macros,
|
macros=options.macros,
|
||||||
verbose=options.verbose,
|
verbose=options.verbose,
|
||||||
archive=False)
|
archive=False)
|
||||||
|
|
||||||
library_build_success = True
|
library_build_success = True
|
||||||
except ToolException, e:
|
except ToolException, e:
|
||||||
# ToolException output is handled by the build log
|
# ToolException output is handled by the build log
|
||||||
|
@ -161,7 +162,7 @@ if __name__ == '__main__':
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# Some other exception occurred, print the error message
|
# Some other exception occurred, print the error message
|
||||||
print e
|
print e
|
||||||
|
|
||||||
if not library_build_success:
|
if not library_build_success:
|
||||||
print "Failed to build library"
|
print "Failed to build library"
|
||||||
else:
|
else:
|
||||||
|
@ -175,32 +176,37 @@ if __name__ == '__main__':
|
||||||
verbose=options.verbose,
|
verbose=options.verbose,
|
||||||
jobs=options.jobs,
|
jobs=options.jobs,
|
||||||
continue_on_build_fail=options.continue_on_build_fail)
|
continue_on_build_fail=options.continue_on_build_fail)
|
||||||
|
|
||||||
# If a path to a test spec is provided, write it to a file
|
# If a path to a test spec is provided, write it to a file
|
||||||
if options.test_spec:
|
if options.test_spec:
|
||||||
test_spec_data = test_spec_from_test_builds(test_build)
|
test_spec_data = test_spec_from_test_builds(test_build)
|
||||||
|
|
||||||
# Create the target dir for the test spec if necessary
|
# Create the target dir for the test spec if necessary
|
||||||
# mkdir will not create the dir if it already exists
|
# mkdir will not create the dir if it already exists
|
||||||
test_spec_dir = os.path.dirname(options.test_spec)
|
test_spec_dir = os.path.dirname(options.test_spec)
|
||||||
if test_spec_dir:
|
if test_spec_dir:
|
||||||
mkdir(test_spec_dir)
|
mkdir(test_spec_dir)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(options.test_spec, 'w') as f:
|
with open(options.test_spec, 'w') as f:
|
||||||
f.write(json.dumps(test_spec_data, indent=2))
|
f.write(json.dumps(test_spec_data, indent=2))
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
print "[ERROR] Error writing test spec to file"
|
print "[ERROR] Error writing test spec to file"
|
||||||
print e
|
print e
|
||||||
|
|
||||||
# If a path to a JUnit build report spec is provided, write it to a file
|
# If a path to a JUnit build report spec is provided, write it to a file
|
||||||
if options.build_report_junit:
|
if options.build_report_junit:
|
||||||
report_exporter = ReportExporter(ResultExporterType.JUNIT, package="build")
|
report_exporter = ReportExporter(ResultExporterType.JUNIT, package="build")
|
||||||
report_exporter.report_to_file(build_report, options.build_report_junit, test_suite_properties=build_properties)
|
report_exporter.report_to_file(build_report, options.build_report_junit, test_suite_properties=build_properties)
|
||||||
|
|
||||||
|
# Print memory map summary on screen
|
||||||
|
if build_report:
|
||||||
|
print
|
||||||
|
print print_build_memory_usage_results(build_report)
|
||||||
|
|
||||||
print_report_exporter = ReportExporter(ResultExporterType.PRINT, package="build")
|
print_report_exporter = ReportExporter(ResultExporterType.PRINT, package="build")
|
||||||
status = print_report_exporter.report(build_report)
|
status = print_report_exporter.report(build_report)
|
||||||
|
|
||||||
if status:
|
if status:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -46,6 +46,7 @@ from tools.paths import HOST_TESTS
|
||||||
from tools.utils import ToolException
|
from tools.utils import ToolException
|
||||||
from tools.utils import NotSupportedException
|
from tools.utils import NotSupportedException
|
||||||
from tools.utils import construct_enum
|
from tools.utils import construct_enum
|
||||||
|
from tools.memap import MemapParser
|
||||||
from tools.targets import TARGET_MAP
|
from tools.targets import TARGET_MAP
|
||||||
from tools.test_db import BaseDBAccess
|
from tools.test_db import BaseDBAccess
|
||||||
from tools.build_api import build_project, build_mbed_libs, build_lib
|
from tools.build_api import build_project, build_mbed_libs, build_lib
|
||||||
|
@ -1970,12 +1971,12 @@ def test_path_to_name(path):
|
||||||
while (tail and tail != "."):
|
while (tail and tail != "."):
|
||||||
name_parts.insert(0, tail)
|
name_parts.insert(0, tail)
|
||||||
head, tail = os.path.split(head)
|
head, tail = os.path.split(head)
|
||||||
|
|
||||||
return "-".join(name_parts).lower()
|
return "-".join(name_parts).lower()
|
||||||
|
|
||||||
def find_tests(base_dir):
|
def find_tests(base_dir):
|
||||||
"""Given any directory, walk through the subdirectories and find all tests"""
|
"""Given any directory, walk through the subdirectories and find all tests"""
|
||||||
|
|
||||||
def find_test_in_directory(directory, tests_path):
|
def find_test_in_directory(directory, tests_path):
|
||||||
"""Given a 'TESTS' directory, return a dictionary of test names and test paths.
|
"""Given a 'TESTS' directory, return a dictionary of test names and test paths.
|
||||||
The formate of the dictionary is {"test-name": "./path/to/test"}"""
|
The formate of the dictionary is {"test-name": "./path/to/test"}"""
|
||||||
|
@ -1989,20 +1990,20 @@ def find_tests(base_dir):
|
||||||
"name": test_path_to_name(directory),
|
"name": test_path_to_name(directory),
|
||||||
"path": directory
|
"path": directory
|
||||||
}
|
}
|
||||||
|
|
||||||
return test
|
return test
|
||||||
|
|
||||||
tests_path = 'TESTS'
|
tests_path = 'TESTS'
|
||||||
tests = {}
|
tests = {}
|
||||||
dirs = scan_for_source_paths(base_dir)
|
dirs = scan_for_source_paths(base_dir)
|
||||||
|
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
test = find_test_in_directory(directory, tests_path)
|
test = find_test_in_directory(directory, tests_path)
|
||||||
if test:
|
if test:
|
||||||
tests[test['name']] = test['path']
|
tests[test['name']] = test['path']
|
||||||
|
|
||||||
return tests
|
return tests
|
||||||
|
|
||||||
def print_tests(tests, format="list", sort=True):
|
def print_tests(tests, format="list", sort=True):
|
||||||
"""Given a dictionary of tests (as returned from "find_tests"), print them
|
"""Given a dictionary of tests (as returned from "find_tests"), print them
|
||||||
in the specified format"""
|
in the specified format"""
|
||||||
|
@ -2033,12 +2034,11 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
|
||||||
continue_on_build_fail=False):
|
continue_on_build_fail=False):
|
||||||
"""Given the data structure from 'find_tests' and the typical build parameters,
|
"""Given the data structure from 'find_tests' and the typical build parameters,
|
||||||
build all the tests
|
build all the tests
|
||||||
|
|
||||||
Returns a tuple of the build result (True or False) followed by the test
|
Returns a tuple of the build result (True or False) followed by the test
|
||||||
build data structure"""
|
build data structure"""
|
||||||
|
|
||||||
execution_directory = "."
|
|
||||||
|
|
||||||
|
execution_directory = "."
|
||||||
base_path = norm_relative_path(build_path, execution_directory)
|
base_path = norm_relative_path(build_path, execution_directory)
|
||||||
|
|
||||||
target_name = target if isinstance(target, str) else target.name
|
target_name = target if isinstance(target, str) else target.name
|
||||||
|
@ -2051,9 +2051,10 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
|
||||||
"binary_type": "bootable",
|
"binary_type": "bootable",
|
||||||
"tests": {}
|
"tests": {}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = True
|
result = True
|
||||||
|
|
||||||
|
map_outputs_total = list()
|
||||||
for test_name, test_path in tests.iteritems():
|
for test_name, test_path in tests.iteritems():
|
||||||
test_build_path = os.path.join(build_path, test_path)
|
test_build_path = os.path.join(build_path, test_path)
|
||||||
src_path = base_source_paths + [test_path]
|
src_path = base_source_paths + [test_path]
|
||||||
|
@ -2072,21 +2073,21 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if not isinstance(e, NotSupportedException):
|
if not isinstance(e, NotSupportedException):
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
if continue_on_build_fail:
|
if continue_on_build_fail:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
# If a clean build was carried out last time, disable it for the next build.
|
# If a clean build was carried out last time, disable it for the next build.
|
||||||
# Otherwise the previously built test will be deleted.
|
# Otherwise the previously built test will be deleted.
|
||||||
if clean:
|
if clean:
|
||||||
clean = False
|
clean = False
|
||||||
|
|
||||||
# Normalize the path
|
# Normalize the path
|
||||||
if bin_file:
|
if bin_file:
|
||||||
bin_file = norm_relative_path(bin_file, execution_directory)
|
bin_file = norm_relative_path(bin_file, execution_directory)
|
||||||
|
|
||||||
test_build['tests'][test_name] = {
|
test_build['tests'][test_name] = {
|
||||||
"binaries": [
|
"binaries": [
|
||||||
{
|
{
|
||||||
|
@ -2094,15 +2095,15 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
print 'Image: %s'% bin_file
|
print 'Image: %s'% bin_file
|
||||||
|
|
||||||
test_builds = {}
|
test_builds = {}
|
||||||
test_builds["%s-%s" % (target_name, toolchain_name)] = test_build
|
test_builds["%s-%s" % (target_name, toolchain_name)] = test_build
|
||||||
|
|
||||||
|
|
||||||
return result, test_builds
|
return result, test_builds
|
||||||
|
|
||||||
|
|
||||||
def test_spec_from_test_builds(test_builds):
|
def test_spec_from_test_builds(test_builds):
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -233,28 +233,28 @@ class mbedToolchain:
|
||||||
def __init__(self, target, options=None, notify=None, macros=None, silent=False, extra_verbose=False):
|
def __init__(self, target, options=None, notify=None, macros=None, silent=False, extra_verbose=False):
|
||||||
self.target = target
|
self.target = target
|
||||||
self.name = self.__class__.__name__
|
self.name = self.__class__.__name__
|
||||||
|
|
||||||
# compile/assemble/link/binary hooks
|
# compile/assemble/link/binary hooks
|
||||||
self.hook = hooks.Hook(target, self)
|
self.hook = hooks.Hook(target, self)
|
||||||
|
|
||||||
# Toolchain flags
|
# Toolchain flags
|
||||||
self.flags = deepcopy(self.DEFAULT_FLAGS)
|
self.flags = deepcopy(self.DEFAULT_FLAGS)
|
||||||
|
|
||||||
# User-defined macros
|
# User-defined macros
|
||||||
self.macros = macros or []
|
self.macros = macros or []
|
||||||
|
|
||||||
# Macros generated from toolchain and target rules/features
|
# Macros generated from toolchain and target rules/features
|
||||||
self.symbols = None
|
self.symbols = None
|
||||||
|
|
||||||
# Labels generated from toolchain and target rules/features (used for selective build)
|
# Labels generated from toolchain and target rules/features (used for selective build)
|
||||||
self.labels = None
|
self.labels = None
|
||||||
|
|
||||||
# This will hold the configuration data (as returned by Config.get_config_data())
|
# This will hold the configuration data (as returned by Config.get_config_data())
|
||||||
self.config_data = None
|
self.config_data = None
|
||||||
|
|
||||||
# Non-incremental compile
|
# Non-incremental compile
|
||||||
self.build_all = False
|
self.build_all = False
|
||||||
|
|
||||||
# Build output dir
|
# Build output dir
|
||||||
self.build_dir = None
|
self.build_dir = None
|
||||||
self.timestamp = time()
|
self.timestamp = time()
|
||||||
|
@ -265,7 +265,7 @@ class mbedToolchain:
|
||||||
# Number of concurrent build jobs. 0 means auto (based on host system cores)
|
# Number of concurrent build jobs. 0 means auto (based on host system cores)
|
||||||
self.jobs = 0
|
self.jobs = 0
|
||||||
|
|
||||||
self.CHROOT = None
|
self.CHROOT = None
|
||||||
|
|
||||||
# Ignore patterns from .mbedignore files
|
# Ignore patterns from .mbedignore files
|
||||||
self.ignore_patterns = []
|
self.ignore_patterns = []
|
||||||
|
@ -280,12 +280,13 @@ class mbedToolchain:
|
||||||
self.notify_fun = self.print_notify_verbose
|
self.notify_fun = self.print_notify_verbose
|
||||||
else:
|
else:
|
||||||
self.notify_fun = self.print_notify
|
self.notify_fun = self.print_notify
|
||||||
|
|
||||||
# Silent builds (no output)
|
# Silent builds (no output)
|
||||||
self.silent = silent
|
self.silent = silent
|
||||||
|
|
||||||
# Print output buffer
|
# Print output buffer
|
||||||
self.output = ""
|
self.output = str()
|
||||||
|
self.map_outputs = list() # Place to store memmap scan results in JSON like data structures
|
||||||
|
|
||||||
# Build options passed by -o flag
|
# Build options passed by -o flag
|
||||||
self.options = options if options is not None else []
|
self.options = options if options is not None else []
|
||||||
|
@ -295,7 +296,7 @@ class mbedToolchain:
|
||||||
|
|
||||||
if self.options:
|
if self.options:
|
||||||
self.info("Build Options: %s" % (', '.join(self.options)))
|
self.info("Build Options: %s" % (', '.join(self.options)))
|
||||||
|
|
||||||
# uVisor spepcific rules
|
# uVisor spepcific rules
|
||||||
if 'UVISOR' in self.target.features and 'UVISOR_SUPPORTED' in self.target.extra_labels:
|
if 'UVISOR' in self.target.features and 'UVISOR_SUPPORTED' in self.target.extra_labels:
|
||||||
self.target.core = re.sub(r"F$", '', self.target.core)
|
self.target.core = re.sub(r"F$", '', self.target.core)
|
||||||
|
@ -310,10 +311,10 @@ class mbedToolchain:
|
||||||
|
|
||||||
if not self.VERBOSE and event['type'] == 'tool_error':
|
if not self.VERBOSE and event['type'] == 'tool_error':
|
||||||
msg = event['message']
|
msg = event['message']
|
||||||
|
|
||||||
elif event['type'] in ['info', 'debug']:
|
elif event['type'] in ['info', 'debug']:
|
||||||
msg = event['message']
|
msg = event['message']
|
||||||
|
|
||||||
elif event['type'] == 'cc':
|
elif event['type'] == 'cc':
|
||||||
event['severity'] = event['severity'].title()
|
event['severity'] = event['severity'].title()
|
||||||
event['file'] = basename(event['file'])
|
event['file'] = basename(event['file'])
|
||||||
|
@ -615,7 +616,7 @@ class mbedToolchain:
|
||||||
|
|
||||||
def relative_object_path(self, build_path, base_dir, source):
|
def relative_object_path(self, build_path, base_dir, source):
|
||||||
source_dir, name, _ = split_path(source)
|
source_dir, name, _ = split_path(source)
|
||||||
|
|
||||||
obj_dir = join(build_path, relpath(source_dir, base_dir))
|
obj_dir = join(build_path, relpath(source_dir, base_dir))
|
||||||
mkdir(obj_dir)
|
mkdir(obj_dir)
|
||||||
return join(obj_dir, name + '.o')
|
return join(obj_dir, name + '.o')
|
||||||
|
@ -627,7 +628,7 @@ class mbedToolchain:
|
||||||
cmd_list = []
|
cmd_list = []
|
||||||
for c in includes:
|
for c in includes:
|
||||||
if c:
|
if c:
|
||||||
cmd_list.append(('-I%s' % c).replace("\\", "/"))
|
cmd_list.append(('-I%s' % c).replace("\\", "/"))
|
||||||
string = " ".join(cmd_list)
|
string = " ".join(cmd_list)
|
||||||
f.write(string)
|
f.write(string)
|
||||||
return include_file
|
return include_file
|
||||||
|
@ -822,12 +823,12 @@ class mbedToolchain:
|
||||||
if self.target.OUTPUT_NAMING == "8.3":
|
if self.target.OUTPUT_NAMING == "8.3":
|
||||||
name = name[0:8]
|
name = name[0:8]
|
||||||
ext = ext[0:3]
|
ext = ext[0:3]
|
||||||
|
|
||||||
# Create destination directory
|
# Create destination directory
|
||||||
head, tail = split(name)
|
head, tail = split(name)
|
||||||
new_path = join(tmp_path, head)
|
new_path = join(tmp_path, head)
|
||||||
mkdir(new_path)
|
mkdir(new_path)
|
||||||
|
|
||||||
filename = name+'.'+ext
|
filename = name+'.'+ext
|
||||||
elf = join(tmp_path, name + '.elf')
|
elf = join(tmp_path, name + '.elf')
|
||||||
bin = join(tmp_path, filename)
|
bin = join(tmp_path, filename)
|
||||||
|
@ -844,7 +845,7 @@ class mbedToolchain:
|
||||||
|
|
||||||
self.binary(r, elf, bin)
|
self.binary(r, elf, bin)
|
||||||
|
|
||||||
self.mem_stats(map)
|
self.map_outputs = self.mem_stats(map)
|
||||||
|
|
||||||
self.var("compile_succeded", True)
|
self.var("compile_succeded", True)
|
||||||
self.var("binary", filename)
|
self.var("binary", filename)
|
||||||
|
@ -900,7 +901,11 @@ class mbedToolchain:
|
||||||
self.notify({'type': 'var', 'key': key, 'val': value})
|
self.notify({'type': 'var', 'key': key, 'val': value})
|
||||||
|
|
||||||
def mem_stats(self, map):
|
def mem_stats(self, map):
|
||||||
# Creates parser object
|
"""! Creates parser object
|
||||||
|
@param map Path to linker map file to parse and decode
|
||||||
|
@return Memory summary structure with memory usage statistics
|
||||||
|
None if map file can't be opened and processed
|
||||||
|
"""
|
||||||
toolchain = self.__class__.__name__
|
toolchain = self.__class__.__name__
|
||||||
|
|
||||||
# Create memap object
|
# Create memap object
|
||||||
|
@ -909,7 +914,7 @@ class mbedToolchain:
|
||||||
# Parse and decode a map file
|
# Parse and decode a map file
|
||||||
if memap.parse(abspath(map), toolchain) is False:
|
if memap.parse(abspath(map), toolchain) is False:
|
||||||
self.info("Unknown toolchain for memory statistics %s" % toolchain)
|
self.info("Unknown toolchain for memory statistics %s" % toolchain)
|
||||||
return
|
return None
|
||||||
|
|
||||||
# Write output to stdout in text (pretty table) format
|
# Write output to stdout in text (pretty table) format
|
||||||
memap.generate_output('table')
|
memap.generate_output('table')
|
||||||
|
@ -917,11 +922,16 @@ class mbedToolchain:
|
||||||
# Write output to file in JSON format
|
# Write output to file in JSON format
|
||||||
map_out = splitext(map)[0] + "_map.json"
|
map_out = splitext(map)[0] + "_map.json"
|
||||||
memap.generate_output('json', map_out)
|
memap.generate_output('json', map_out)
|
||||||
|
|
||||||
# Write output to file in CSV format for the CI
|
# Write output to file in CSV format for the CI
|
||||||
map_csv = splitext(map)[0] + "_map.csv"
|
map_csv = splitext(map)[0] + "_map.csv"
|
||||||
memap.generate_output('csv-ci', map_csv)
|
memap.generate_output('csv-ci', map_csv)
|
||||||
|
|
||||||
|
# Here we return memory statistics structure (constructed after
|
||||||
|
# call to generate_output) which contains raw data in bytes
|
||||||
|
# about sections + summary
|
||||||
|
return memap.get_memory_summary()
|
||||||
|
|
||||||
# Set the configuration data
|
# Set the configuration data
|
||||||
def set_config_data(self, config_data):
|
def set_config_data(self, config_data):
|
||||||
self.config_data = config_data
|
self.config_data = config_data
|
||||||
|
|
Loading…
Reference in New Issue