Clone of official tools

Revision:
31:8ea194f6145b
Parent:
25:aef6536015e3
Child:
35:da9c89f8be7d
--- 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