erkin yucel / mbed-os

Dependents:   BLE_file_test BLE_Blink ExternalEncoder

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers build_test.py Source File

build_test.py

00001 #!/usr/bin/env python
00002 """
00003 mbed SDK
00004 Copyright (c) 2011-2016 ARM Limited
00005 
00006 Licensed under the Apache License, Version 2.0 (the "License");
00007 you may not use this file except in compliance with the License.
00008 You may obtain a copy of the License at
00009 
00010     http://www.apache.org/licenses/LICENSE-2.0
00011 
00012 Unless required by applicable law or agreed to in writing, software
00013 distributed under the License is distributed on an "AS IS" BASIS,
00014 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 See the License for the specific language governing permissions and
00016 limitations under the License.
00017 """
00018 
00019 import sys
00020 from os import remove, rename
00021 from os.path import join, dirname, exists, abspath
00022 ROOT = abspath(join(dirname(__file__), "..", "..", ".."))
00023 sys.path.insert(0, ROOT)
00024 import argparse
00025 from argparse import ArgumentTypeError
00026 import sys
00027 from shutil import rmtree
00028 from collections import namedtuple
00029 from copy import copy
00030 
00031 
00032 from tools.paths import EXPORT_DIR
00033 from tools.tests import TESTS
00034 from tools.build_api import get_mbed_official_release, RELEASE_VERSIONS
00035 from tools.test_api import find_tests
00036 from tools.project import export
00037 from Queue import Queue
00038 from threading import Thread, Lock
00039 from tools.project_api import print_results, get_exporter_toolchain
00040 from tools.tests import test_name_known, test_known, Test
00041 from tools.export.exporters import FailedBuildException, \
00042                                    TargetNotSupportedException
00043 from tools.utils import argparse_force_lowercase_type, \
00044                         argparse_many, columnate, args_error, \
00045                         argparse_filestring_type
00046 from tools.options import extract_profile
00047 
00048 print_lock = Lock()
00049 
00050 def do_queue(Class, function, interable) :
00051     q = Queue()
00052     threads = [Class(q, function) for each in range(20)]
00053     for thing in interable :
00054         q.put(thing)
00055     for each in threads :
00056         each.setDaemon(True)
00057         each.start()
00058     q.join()
00059 
00060 
00061 class Reader (Thread) :
00062     def __init__(self, queue, func) :
00063         Thread.__init__(self)
00064         self.queue = queue
00065         self.func = func
00066 
00067     def start(self) :
00068         sys.stdout.flush()
00069         while not self.queue.empty() :
00070             test = self.queue.get()
00071             self.func(test)
00072             self.queue.task_done()
00073 
00074 
00075 class ExportBuildTest (object):
00076     """Object to encapsulate logic for progen build testing"""
00077     def __init__ (self, tests, parser, options):
00078         """
00079         Initialize an instance of class ProgenBuildTest
00080         Args:
00081             tests: array of TestCase instances
00082         """
00083         self.total  = len(tests)
00084         self.parser  = parser
00085         self.options  = options
00086         self.counter  = 0
00087         self.successes  = []
00088         self.failures  = []
00089         self.skips  = []
00090         self.tests  = [ExportBuildTest.test_case(test) for test in tests]
00091         self.build_queue  = Queue()
00092 
00093     @staticmethod
00094     def test_case(case):
00095         TestCase = namedtuple('TestCase', case.keys())
00096         return TestCase(**case)
00097 
00098     def handle_log(self,log):
00099         try:
00100             with open(log, 'r') as in_log:
00101                 print in_log.read()
00102                 sys.stdout.flush()
00103             log_name = join(EXPORT_DIR, dirname(log) + "_log.txt")
00104             if exists(log_name):
00105                 # delete it if so
00106                 remove(log_name)
00107             rename(log, log_name)
00108         except IOError:
00109             pass
00110 
00111     def batch_tests (self, clean=False):
00112         """Performs all exports of self.tests
00113         Peroform_exports will fill self.build_queue.
00114         This function will empty self.build_queue and call the test's
00115         IDE's build function."""
00116         do_queue(Reader, self.perform_exports , self.tests )
00117         self.counter  = 0
00118         self.total  = self.build_queue .qsize()
00119         while not self.build_queue .empty():
00120             build = self.build_queue .get()
00121             self.counter  +=1
00122             exporter = build[0]
00123             test_case = build[1]
00124             self.display_counter ("Building test case  %s::%s\t%s"
00125                                  % (test_case.mcu,
00126                                     test_case.ide,
00127                                     test_case.name))
00128             try:
00129                 exporter.build()
00130             except FailedBuildException:
00131                 self.failures .append("%s::%s\t%s" % (test_case.mcu,
00132                                                      test_case.ide,
00133                                                      test_case.name))
00134             else:
00135                 self.successes .append("%s::%s\t%s" % (test_case.mcu,
00136                                                       test_case.ide,
00137                                                       test_case.name))
00138             self.handle_log (exporter.generated_files[-1])
00139             if clean:
00140                 rmtree(exporter.export_dir)
00141 
00142     def display_counter (self, message) :
00143         with print_lock:
00144             sys.stdout.write("{}/{} {}".format(self.counter , self.total ,
00145                                                message) +"\n")
00146             sys.stdout.flush()
00147 
00148     def perform_exports (self, test_case):
00149         """
00150         Generate the project file for test_case and fill self.build_queue
00151         Args:
00152             test_case: object of type TestCase
00153         """
00154         sys.stdout.flush()
00155         self.counter  += 1
00156         name_str = ('%s_%s_%s') % (test_case.mcu, test_case.ide, test_case.name)
00157         self.display_counter ("Exporting test case  %s::%s\t%s" % (test_case.mcu,
00158                                                                   test_case.ide,
00159                                                                   test_case.name))
00160 
00161         try:
00162             _, toolchain = get_exporter_toolchain(test_case.ide)
00163             profile = extract_profile(self.parser , self.options , toolchain)
00164             exporter = export(test_case.mcu, test_case.ide,
00165                               project_id=test_case.id, zip_proj=None,
00166                               clean=True, src=test_case.src,
00167                               export_path=join(EXPORT_DIR,name_str),
00168                               silent=True, build_profile=profile)
00169             exporter.generated_files.append(join(EXPORT_DIR,name_str,test_case.log))
00170             self.build_queue .put((exporter,test_case))
00171         except TargetNotSupportedException:
00172             self.skips .append("%s::%s\t%s" % (test_case.mcu, test_case.ide,
00173                                               test_case.name))
00174             # Check if the specified name is in all_os_tests
00175 
00176 
00177 def check_valid_mbed_os(test):
00178     """Check if the specified name is in all_os_tests
00179     args:
00180         test: string name to index all_os_tests
00181     returns: tuple of test_name and source location of test,
00182         as given by find_tests"""
00183     all_os_tests = find_tests(ROOT, "K64F", "ARM")
00184     if test in all_os_tests.keys():
00185         return (test, all_os_tests[test])
00186     else:
00187         supported = columnate([t for t in all_os_tests.keys()])
00188         raise ArgumentTypeError("Program with name '{0}' not found. "
00189                                 "Supported tests are: \n{1}".format(test,supported))
00190 
00191 
00192 def check_version(version):
00193     """Check if the specified version is valid
00194     args:
00195         version: integer versio of mbed
00196     returns:
00197         version if it is valid"""
00198     if version not in RELEASE_VERSIONS:
00199         raise ArgumentTypeError("Choose from versions : %s"%", ".join(RELEASE_VERSIONS))
00200     return version
00201 
00202 
00203 def main():
00204     """Entry point"""
00205 
00206     ide_list = ["iar", "uvision"]
00207 
00208     default_v2 = [test_name_known("MBED_BLINKY")]
00209     default_v5 = [check_valid_mbed_os('tests-mbedmicro-rtos-mbed-basic')]
00210 
00211     parser = argparse.ArgumentParser(description=
00212                                      "Test progen builders. Leave any flag off"
00213                                      " to run with all possible options.")
00214     parser.add_argument("-i",
00215                         dest="ides",
00216                         default=ide_list,
00217                         type=argparse_many(argparse_force_lowercase_type(
00218                             ide_list, "toolchain")),
00219                         help="The target IDE: %s"% str(ide_list))
00220 
00221     parser.add_argument( "-p",
00222                         type=argparse_many(test_known),
00223                         dest="programs",
00224                         help="The index of the desired test program: [0-%d]"
00225                              % (len(TESTS) - 1))
00226 
00227     parser.add_argument("-n",
00228                         type=argparse_many(test_name_known),
00229                         dest="programs",
00230                         help="The name of the desired test program")
00231 
00232     parser.add_argument("-m", "--mcu",
00233                         help=("Generate projects for the given MCUs"),
00234                         metavar="MCU",
00235                         type=argparse_many(str.upper))
00236 
00237     parser.add_argument("-os-tests",
00238                         type=argparse_many(check_valid_mbed_os),
00239                         dest="os_tests",
00240                         help="Mbed-os tests")
00241 
00242     parser.add_argument("-c", "--clean",
00243                         dest="clean",
00244                         action="store_true",
00245                         help="clean up the exported project files",
00246                         default=False)
00247 
00248     parser.add_argument("--release",
00249                         dest="release",
00250                         type=check_version,
00251                         help="Which version of mbed to test",
00252                         default=RELEASE_VERSIONS[-1])
00253 
00254     parser.add_argument("--profile",
00255                         dest="profile",
00256                         action="append",
00257                         type=argparse_filestring_type,
00258                         default=[])
00259 
00260     options = parser.parse_args()
00261     # targets in chosen release
00262     targetnames = [target[0] for target in
00263                    get_mbed_official_release(options.release)]
00264     # all targets in release are default
00265     test_targets = options.mcu or targetnames
00266     if not all([t in targetnames for t in test_targets]):
00267         args_error(parser, "Only specify targets in release %s:\n%s"
00268                    %(options.release, columnate(targetnames)))
00269 
00270     v2_tests, v5_tests = [],[]
00271     if options.release == '5':
00272         v5_tests = options.os_tests or default_v5
00273     elif options.release == '2':
00274         v2_tests = options.programs or default_v2
00275 
00276     tests = []
00277     default_test = {key:None for key in ['ide', 'mcu', 'name', 'id', 'src', 'log']}
00278     for mcu in test_targets:
00279         for ide in options.ides:
00280             log = "build_log.txt" if ide == 'iar' \
00281                 else join('build', 'build_log.txt')
00282             # add each test case to the tests array
00283             default_test.update({'mcu': mcu, 'ide': ide, 'log':log})
00284             for test in v2_tests:
00285                 default_test.update({'name':TESTS[test]["id"], 'id':test})
00286                 tests.append(copy(default_test))
00287             for test in v5_tests:
00288                 default_test.update({'name':test[0],'src':[test[1],ROOT]})
00289                 tests.append(copy(default_test))
00290     test = ExportBuildTest(tests, parser, options)
00291     test.batch_tests(clean=options.clean)
00292     print_results(test.successes, test.failures, test.skips)
00293     sys.exit(len(test.failures))
00294 
00295 if __name__ == "__main__":
00296     main()