Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-sdk-tools by
Diff: test/export/build_test.py
- Revision:
- 31:182518299918
- Parent:
- 25:aef6536015e3
diff -r f12ce67666d0 -r 182518299918 test/export/build_test.py --- a/test/export/build_test.py Mon Aug 29 11:56:59 2016 +0100 +++ b/test/export/build_test.py Wed Jan 04 11:58:24 2017 -0600 @@ -1,7 +1,7 @@ #!/usr/bin/env python """ mbed SDK -Copyright (c) 2011-2013 ARM Limited +Copyright (c) 2011-2016 ARM Limited Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,154 +16,283 @@ limitations under the License. """ - import sys +from os import remove, rename +from os.path import join, dirname, exists, abspath +ROOT = abspath(join(dirname(__file__), "..", "..", "..")) +sys.path.insert(0, ROOT) import argparse import os -import shutil -from os.path import join, abspath, dirname, exists, basename -r=dirname(__file__) -ROOT = abspath(join(r, "..","..","..")) -sys.path.insert(0, ROOT) - -from tools.export import EXPORTERS -from tools.targets import TARGET_NAMES, TARGET_MAP -from tools.project_api import setup_project, perform_export, print_results, get_test_from_name, get_lib_symbols -from project_generator_definitions.definitions import ProGenDef -from tools.utils import args_error +from argparse import ArgumentTypeError +import sys +from shutil import rmtree +from collections import namedtuple +from copy import copy -class ProgenBuildTest(): - def __init__(self, desired_ides, targets): - #map of targets and the ides that can build programs for them - self.target_ides = {} - for target in targets: - self.target_ides[target] =[] - for ide in desired_ides: - if target in EXPORTERS[ide].TARGETS: - #target is supported by ide - self.target_ides[target].append(ide) - if len(self.target_ides[target]) == 0: - del self.target_ides[target] +from tools.paths import EXPORT_DIR +from tools.tests import TESTS +from tools.build_api import get_mbed_official_release, RELEASE_VERSIONS +from tools.test_api import find_tests +from tools.project import export +from Queue import Queue +from threading import Thread, Lock +from tools.project_api import print_results, get_exporter_toolchain +from tools.tests import test_name_known, test_known +from tools.export import EXPORTERS +from tools.utils import argparse_force_lowercase_type, \ + argparse_many, columnate, args_error, \ + argparse_filestring_type +from tools.options import extract_profile + +print_lock = Lock() + +def do_queue(Class, function, interable) : + q = Queue() + threads = [Class(q, function) for each in range(20)] + for thing in interable : + q.put(thing) + for each in threads : + each.setDaemon(True) + each.start() + q.join() - @staticmethod - def get_pgen_targets(ides): - #targets supported by pgen and desired ides for tests - targs = [] - for ide in ides: - for target in TARGET_NAMES: - if target not in targs and hasattr(TARGET_MAP[target],'progen') \ - and ProGenDef(ide).is_supported(TARGET_MAP[target].progen['target']): - targs.append(target) - return targs +class Reader (Thread) : + def __init__(self, queue, func) : + Thread.__init__(self) + self.queue = queue + self.func = func + + def start(self) : + sys.stdout.flush() + while not self.queue.empty() : + test = self.queue.get() + self.func(test) + self.queue.task_done() + + +class ExportBuildTest(object): + """Object to encapsulate logic for progen build testing""" + def __init__(self, tests, parser, options): + """ + Initialize an instance of class ProgenBuildTest + Args: + tests: array of TestCase instances + """ + self.total = len(tests) + self.parser = parser + self.options = options + self.counter = 0 + self.successes = [] + self.failures = [] + self.skips = [] + self.tests = [ExportBuildTest.test_case(test) for test in tests] + self.build_queue = Queue() @staticmethod - def handle_project_files(project_dir, mcu, test, tool, clean=False): - log = '' - if tool == 'uvision' or tool == 'uvision5': - log = os.path.join(project_dir,"build","build_log.txt") - elif tool == 'iar': - log = os.path.join(project_dir, 'build_log.txt') + def test_case(case): + TestCase = namedtuple('TestCase', case.keys()) + return TestCase(**case) + + def handle_log(self,log): try: - with open(log, 'r') as f: - print f.read() - except: - return + with open(log, 'r') as in_log: + print in_log.read() + sys.stdout.flush() + log_name = join(EXPORT_DIR, dirname(log) + "_log.txt") + if exists(log_name): + # delete it if so + remove(log_name) + rename(log, log_name) + except IOError: + pass - prefix = "_".join([test, mcu, tool]) - log_name = os.path.join(os.path.dirname(project_dir), prefix+"_log.txt") - - #check if a log already exists for this platform+test+ide - if os.path.exists(log_name): - #delete it if so - os.remove(log_name) - os.rename(log, log_name) - - if clean: - shutil.rmtree(project_dir, ignore_errors=True) - return - - def generate_and_build(self, tests, clean=False): + def batch_tests(self, clean=False): + """Performs all exports of self.tests + Peroform_exports will fill self.build_queue. + This function will empty self.build_queue and call the test's + IDE's build function.""" + do_queue(Reader, self.perform_exports, self.tests) + self.counter = 0 + self.total = self.build_queue.qsize() + while not self.build_queue.empty(): + build = self.build_queue.get() + self.counter +=1 + exporter = build[0] + test_case = build[1] + self.display_counter("Building test case %s::%s\t%s" + % (test_case.mcu, + test_case.ide, + test_case.name)) - #build results - successes = [] - failures = [] - skips = [] - for mcu, ides in self.target_ides.items(): - for test in tests: - #resolve name alias - test = get_test_from_name(test) - for ide in ides: - lib_symbols = get_lib_symbols(None, None, test) - project_dir, project_name, project_temp = setup_project(mcu, ide, test) + cwd = os.getcwd() + os.chdir(exporter.export_dir) + res = EXPORTERS[exporter.NAME.lower()].build(exporter.project_name, cleanup=False) + os.chdir(cwd) + if res: + self.failures.append("%s::%s\t%s" % (test_case.mcu, + test_case.ide, + test_case.name)) + else: + self.successes.append("%s::%s\t%s" % (test_case.mcu, + test_case.ide, + test_case.name)) + self.handle_log(exporter.generated_files[-1]) + if clean: + rmtree(exporter.export_dir) + + def display_counter (self, message) : + with print_lock: + sys.stdout.write("{}/{} {}".format(self.counter, self.total, + message) +"\n") + sys.stdout.flush() - dest_dir = os.path.dirname(project_temp) - destination = os.path.join(dest_dir,"_".join([project_name, mcu, ide])) - - tmp_path, report = perform_export(project_dir, project_name, ide, mcu, destination, - lib_symbols=lib_symbols, progen_build = True) - - if report['success']: - successes.append("build for %s::%s\t%s" % (mcu, ide, project_name)) - elif report['skip']: - skips.append("%s::%s\t%s" % (mcu, ide, project_name)) - else: - failures.append("%s::%s\t%s for %s" % (mcu, ide, report['errormsg'], project_name)) - - ProgenBuildTest.handle_project_files(destination, mcu, project_name, ide, clean) - return successes, failures, skips + def perform_exports(self, test_case): + """ + Generate the project file for test_case and fill self.build_queue + Args: + test_case: object of type TestCase + """ + sys.stdout.flush() + self.counter += 1 + name_str = ('%s_%s_%s') % (test_case.mcu, test_case.ide, test_case.name) + self.display_counter("Exporting test case %s::%s\t%s" % (test_case.mcu, + test_case.ide, + test_case.name)) + exporter, toolchain = get_exporter_toolchain(test_case.ide) + if test_case.mcu not in exporter.TARGETS: + self.skips.append("%s::%s\t%s" % (test_case.mcu, test_case.ide, + test_case.name)) + return + profile = extract_profile(self.parser, self.options, toolchain) + exporter = export(test_case.mcu, test_case.ide, + project_id=test_case.id, zip_proj=None, + clean=True, src=test_case.src, + export_path=join(EXPORT_DIR,name_str), + silent=True, build_profile=profile) + exporter.generated_files.append(join(EXPORT_DIR,name_str,test_case.log)) + self.build_queue.put((exporter,test_case)) + # Check if the specified name is in all_os_tests -if __name__ == '__main__': - accepted_ides = ["iar", "uvision", "uvision5"] - accepted_targets = sorted(ProgenBuildTest.get_pgen_targets(accepted_ides)) - default_tests = ["MBED_BLINKY"] +def check_valid_mbed_os(test): + """Check if the specified name is in all_os_tests + args: + test: string name to index all_os_tests + returns: tuple of test_name and source location of test, + as given by find_tests""" + all_os_tests = find_tests(ROOT, "K64F", "ARM") + if test in all_os_tests.keys(): + return (test, all_os_tests[test]) + else: + supported = columnate([t for t in all_os_tests.keys()]) + raise ArgumentTypeError("Program with name '{0}' not found. " + "Supported tests are: \n{1}".format(test,supported)) + + +def check_version(version): + """Check if the specified version is valid + args: + version: integer versio of mbed + returns: + version if it is valid""" + if version not in RELEASE_VERSIONS: + raise ArgumentTypeError("Choose from versions : %s"%", ".join(RELEASE_VERSIONS)) + return version - parser = argparse.ArgumentParser(description = "Test progen builders. Leave any flag off to run with all possible options.") - parser.add_argument("-i", "--IDEs", - nargs = '+', - dest="ides", - help="tools you wish to perfrom build tests. (%s)" % ', '.join(accepted_ides), - default = accepted_ides) + +def main(): + """Entry point""" + + ide_list = ["iar", "uvision"] + + default_v2 = [test_name_known("MBED_BLINKY")] + default_v5 = [check_valid_mbed_os('tests-mbedmicro-rtos-mbed-basic')] + + parser = argparse.ArgumentParser(description= + "Test progen builders. Leave any flag off" + " to run with all possible options.") + parser.add_argument("-i", + dest="ides", + default=ide_list, + type=argparse_many(argparse_force_lowercase_type( + ide_list, "toolchain")), + help="The target IDE: %s"% str(ide_list)) + + parser.add_argument( "-p", + type=argparse_many(test_known), + dest="programs", + help="The index of the desired test program: [0-%d]" + % (len(TESTS) - 1)) parser.add_argument("-n", - nargs='+', - dest="tests", - help="names of desired test programs", - default = default_tests) + type=argparse_many(test_name_known), + dest="programs", + help="The name of the desired test program") - parser.add_argument("-m", "--mcus", - nargs='+', - dest ="targets", - help="generate project for the given MCUs (%s)" % '\n '.join(accepted_targets), - default = accepted_targets) + parser.add_argument("-m", "--mcu", + help=("Generate projects for the given MCUs"), + metavar="MCU", + type=argparse_many(str.upper)) + + parser.add_argument("-os-tests", + type=argparse_many(check_valid_mbed_os), + dest="os_tests", + help="Mbed-os tests") parser.add_argument("-c", "--clean", dest="clean", - action = "store_true", + action="store_true", help="clean up the exported project files", default=False) - options = parser.parse_args() + parser.add_argument("--release", + dest="release", + type=check_version, + help="Which version of mbed to test", + default=RELEASE_VERSIONS[-1]) - tests = options.tests - ides = [ide.lower() for ide in options.ides] - targets = [target.upper() for target in options.targets] + parser.add_argument("--profile", + dest="profile", + action="append", + type=argparse_filestring_type, + default=[]) - if any(get_test_from_name(test) is None for test in tests): - args_error(parser, "[ERROR] test name not recognized") + options = parser.parse_args() + # targets in chosen release + targetnames = [target[0] for target in + get_mbed_official_release(options.release)] + # all targets in release are default + test_targets = options.mcu or targetnames + if not all([t in targetnames for t in test_targets]): + args_error(parser, "Only specify targets in release %s:\n%s" + %(options.release, columnate(sorted(targetnames)))) - if any(target not in accepted_targets for target in targets): - args_error(parser, "[ERROR] mcu must be one of the following:\n %s" % '\n '.join(accepted_targets)) - - if any(ide not in accepted_ides for ide in ides): - args_error(parser, "[ERROR] ide must be in %s" % ', '.join(accepted_ides)) + v2_tests, v5_tests = [],[] + if options.release == '5': + v5_tests = options.os_tests or default_v5 + elif options.release == '2': + v2_tests = options.programs or default_v2 - build_test = ProgenBuildTest(ides, targets) - successes, failures, skips = build_test.generate_and_build(tests, options.clean) - print_results(successes, failures, skips) - sys.exit(len(failures)) + tests = [] + default_test = {key:None for key in ['ide', 'mcu', 'name', 'id', 'src', 'log']} + for mcu in test_targets: + for ide in options.ides: + log = "build_log.txt" if ide == 'iar' \ + else join('build', 'build_log.txt') + # add each test case to the tests array + default_test.update({'mcu': mcu, 'ide': ide, 'log':log}) + for test in v2_tests: + default_test.update({'name':TESTS[test]["id"], 'id':test}) + tests.append(copy(default_test)) + for test in v5_tests: + default_test.update({'name':test[0],'src':[test[1],ROOT]}) + tests.append(copy(default_test)) + test = ExportBuildTest(tests, parser, options) + test.batch_tests(clean=options.clean) + print_results(test.successes, test.failures, test.skips) + sys.exit(len(test.failures)) - - +if __name__ == "__main__": + main() \ No newline at end of file