diff --git a/tools/export/cmsis/__init__.py b/tools/export/cmsis/__init__.py index 313c5e52c6..68aff56a66 100644 --- a/tools/export/cmsis/__init__.py +++ b/tools/export/cmsis/__init__.py @@ -31,10 +31,25 @@ class DeviceCMSIS(): Encapsulates target information retrieved by arm-pack-manager""" def __init__(self, target): - cache = Cache(True, False) + target_info = self.check_supported(target) + if not target_info: + raise TargetNotSupportedException("Target not supported in CMSIS pack") + self.url = target_info['pdsc_file'] + self.pack_url, self.pack_id = ntpath.split(self.url) + self.dname = target_info["_cpu_name"] + self.core = target_info["_core"] + self.dfpu = target_info['processor']['fpu'] + self.debug, self.dvendor = self.vendor_debug(target_info['vendor']) + self.dendian = target_info['processor'].get('endianness','Little-endian') + self.debug_svd = target_info.get('debug', '') + self.compile_header = target_info['compile']['header'] + self.target_info = target_info + + @staticmethod + def check_supported(target): + cache = Cache(True, False) t = TARGET_MAP[target] - self.core = t.core try: cpu_name = t.device_name target_info = cache.index[cpu_name] @@ -42,26 +57,13 @@ class DeviceCMSIS(): except: try: # Try to find the core as a generic CMSIS target - cpu_name = self.cpu_cmsis() + cpu_name = DeviceCMSIS.cpu_cmsis(t.core) target_info = cache.index[cpu_name] except: - raise TargetNotSupportedException("Target not in CMSIS packs") - - self.target_info = target_info - - self.url = target_info['pdsc_file'] - self.pack_url, self.pack_id = ntpath.split(self.url) - self.dname = cpu_name - self.dfpu = target_info['processor']['fpu'] - self.debug, self.dvendor = self.vendor_debug(target_info['vendor']) - self.dendian = target_info['processor'].get('endianness','Little-endian') - self.debug_svd = target_info.get('debug', '') - self.compile_header = target_info['compile']['header'] - - def check_version(self, filename): - with open(filename) as data_file: - data = json.load(data_file) - return data.get("version", "0") == "0.1.0" + return False + target_info["_cpu_name"] = cpu_name + target_info["_core"] = t.core + return target_info def vendor_debug(self, vendor): reg = "([\w\s]+):?\d*?" @@ -74,9 +76,9 @@ class DeviceCMSIS(): } return debug_map.get(vendor_match, "CMSIS-DAP"), vendor_match - def cpu_cmsis(self): + @staticmethod + def cpu_cmsis(cpu): #Cortex-M4F => ARMCM4_FP, Cortex-M0+ => ARMCM0P - cpu = self.core cpu = cpu.replace("Cortex-","ARMC") cpu = cpu.replace("+","P") cpu = cpu.replace("F","_FP") diff --git a/tools/export/exporters.py b/tools/export/exporters.py index ba31064e58..ae7ad692eb 100644 --- a/tools/export/exporters.py +++ b/tools/export/exporters.py @@ -119,13 +119,6 @@ class Exporter(object): source_files.extend(getattr(self.resources, key)) return list(set([os.path.dirname(src) for src in source_files])) - def check_supported(self): - """Indicated if this combination of IDE and MCU is supported""" - if self.target not in self.TARGETS or \ - self.TOOLCHAIN not in TARGET_MAP[self.target].supported_toolchains: - raise TargetNotSupportedException() - return True - def gen_file(self, template_file, data, target_file): """Generates a project file from a template using jinja""" jinja_loader = FileSystemLoader( diff --git a/tools/export/iar/__init__.py b/tools/export/iar/__init__.py index f595a3606f..4b065ba874 100644 --- a/tools/export/iar/__init__.py +++ b/tools/export/iar/__init__.py @@ -2,7 +2,7 @@ import os from os.path import sep, join, exists from collections import namedtuple from subprocess import Popen, PIPE -from distutils.spawn import find_executable +import shutil import re import sys @@ -29,7 +29,8 @@ class IAR(Exporter): #iar_definitions.json TARGETS = [target for target, obj in TARGET_MAP.iteritems() if hasattr(obj, 'device_name') and - obj.device_name in IAR_DEFS.keys()] + obj.device_name in IAR_DEFS.keys() and "IAR" in obj.supported_toolchains + and DeviceCMSIS.check_supported(target)] SPECIAL_TEMPLATES = { 'rz_a1h' : 'iar/iar_rz_a1h.ewp.tmpl', @@ -120,22 +121,13 @@ class IAR(Exporter): self.gen_file('iar/ewd.tmpl', ctx, self.project_name + ".ewd") self.gen_file(self.get_ewp_template(), ctx, self.project_name + ".ewp") - def build(self): + @staticmethod + def build(project_name, clean=True): """ Build IAR project """ # > IarBuild [project_path] -build [project_name] - proj_file = join(self.export_dir, self.project_name + ".ewp") - if find_executable("IarBuild"): - iar_exe = "IarBuild.exe" - else: - iar_exe = join('C:', sep, - 'Program Files (x86)', 'IAR Systems', - 'Embedded Workbench 7.5', 'common', 'bin', - 'IarBuild.exe') - if not exists(iar_exe): - raise Exception("IarBuild.exe not found. Add to path.") - - cmd = [iar_exe, proj_file, '-build', self.project_name] + proj_file = project_name + ".ewp" + cmd = ["IarBuild.exe", proj_file, '-build', project_name] # IAR does not support a '0' option to automatically use all # available CPUs, so we use Python's multiprocessing library @@ -156,7 +148,14 @@ class IAR(Exporter): m = re.match(error_re, line) if m is not None: num_errors = int(m.group(1)) + + if clean: + os.remove(project_name + ".ewp") + os.remove(project_name + ".ewd") + os.remove(project_name + ".eww") + shutil.rmtree('.build') + if num_errors !=0: # Seems like something went wrong. - raise FailedBuildException("Project: %s build failed with %s erros" % ( - proj_file, num_errors)) + return -1 + return 0 diff --git a/tools/export/makefile/__init__.py b/tools/export/makefile/__init__.py index 41030f54c2..46a4d626d3 100644 --- a/tools/export/makefile/__init__.py +++ b/tools/export/makefile/__init__.py @@ -16,7 +16,10 @@ limitations under the License. """ from os.path import splitext, basename, relpath, join, abspath, dirname,\ exists -from os import curdir, getcwd +from os import remove +import sys +from subprocess import check_output, CalledProcessError, Popen, PIPE +import shutil from jinja2.exceptions import TemplateNotFound from tools.export.exporters import Exporter from tools.utils import NotSupportedException @@ -102,6 +105,38 @@ class Makefile(Exporter): else: raise NotSupportedException("This make tool is in development") + @staticmethod + def build(project_name, build_log="build_log.txt", project_loc=None, clean=True): + """ Build Make project """ + # > Make -C [project directory] -j + cmd = ["make", "-j"] + p = Popen(cmd, stdout=PIPE, stderr=PIPE) + ret = p.communicate() + out, err = ret[0], ret[1] + ret_code = p.returncode + with open(build_log, 'w+') as f: + f.write("=" * 10 + "OUT" + "=" * 10 + "\n") + f.write(out) + f.write("=" * 10 + "ERR" + "=" * 10 + "\n") + f.write(err) + if ret_code == 0: + f.write("SUCCESS") + else: + f.write("FAILURE") + with open(build_log, 'r') as f: + print "\n".join(f.readlines()) + sys.stdout.flush() + + if clean: + remove("Makefile") + remove(build_log) + if exists('.build'): + shutil.rmtree('.build') + if ret_code != 0: + # Seems like something went wrong. + return -1 + return 0 + class GccArm(Makefile): """GCC ARM specific makefile target""" diff --git a/tools/export/uvision/__init__.py b/tools/export/uvision/__init__.py index a50a5665eb..2de2f33326 100644 --- a/tools/export/uvision/__init__.py +++ b/tools/export/uvision/__init__.py @@ -3,7 +3,7 @@ from os.path import sep, normpath, join, exists import ntpath import copy from collections import namedtuple -from distutils.spawn import find_executable +import shutil import subprocess import re @@ -117,10 +117,15 @@ class Uvision(Exporter): project file (.uvprojx). The needed information can be viewed in uvision.tmpl """ - NAME = 'cmsis' + NAME = 'uvision5' TOOLCHAIN = 'ARM' - TARGETS = [target for target, obj in TARGET_MAP.iteritems() - if "ARM" in obj.supported_toolchains] + TARGETS = [] + for target, obj in TARGET_MAP.iteritems(): + if not ("ARM" in obj.supported_toolchains and hasattr(obj, "device_name")): + continue + if not DeviceCMSIS.check_supported(target): + continue + TARGETS.append(target) #File associations within .uvprojx file file_types = {'.cpp': 8, '.c': 1, '.s': 2, '.obj': 3, '.o': 3, '.lib': 4, @@ -200,35 +205,22 @@ class Uvision(Exporter): self.gen_file('uvision/uvision.tmpl', ctx, self.project_name+".uvprojx") self.gen_file('uvision/uvision_debug.tmpl', ctx, self.project_name + ".uvoptx") - def build(self): - ERRORLEVEL = { - 0: 'success (0 warnings, 0 errors)', - 1: 'warnings', - 2: 'errors', - 3: 'fatal errors', - 11: 'cant write to project file', - 12: 'device error', - 13: 'error writing', - 15: 'error reading xml file', - } + @staticmethod + def build(project_name, log_name='build_log.txt', clean=True): success = 0 warn = 1 - if find_executable("UV4"): - uv_exe = "UV4.exe" - else: - uv_exe = join('C:', sep, - 'Keil_v5', 'UV4', 'UV4.exe') - if not exists(uv_exe): - raise Exception("UV4.exe not found. Add to path.") - cmd = [uv_exe, '-r', '-j0', '-o', join(self.export_dir,'build_log.txt'), join(self.export_dir,self.project_name+".uvprojx")] + cmd = ["UV4.exe", '-r', '-j0', '-o', log_name, project_name+".uvprojx"] ret_code = subprocess.call(cmd) - with open(join(self.export_dir, 'build_log.txt'), 'r') as build_log: + with open(log_name, 'r') as build_log: print build_log.read() + if clean: + os.remove(log_name) + os.remove(project_name+".uvprojx") + os.remove(project_name+".uvoptx") + shutil.rmtree(".build") + if ret_code != success and ret_code != warn: # Seems like something went wrong. - raise FailedBuildException("Project: %s build failed with the status: %s" % ( - self.project_name, ERRORLEVEL.get(ret_code, "Unknown"))) - else: - return "Project: %s build succeeded with the status: %s" % ( - self.project_name, ERRORLEVEL.get(ret_code, "Unknown")) + return -1 + return 0 diff --git a/tools/project.py b/tools/project.py index ad9e5ed209..1e51ceccfd 100644 --- a/tools/project.py +++ b/tools/project.py @@ -233,7 +233,9 @@ def main(): if (options.program is None) and (not options.source_dir): args_error(parser, "one of -p, -n, or --source is required") # Export to selected toolchain - _, toolchain_name = get_exporter_toolchain(options.ide) + exporter, toolchain_name = get_exporter_toolchain(options.ide) + if options.mcu not in exporter.TARGETS: + args_error(parser, "%s not supported by %s") profile = extract_profile(parser, options, toolchain_name) export(options.mcu, options.ide, build=options.build, src=options.source_dir, macros=options.macros, diff --git a/tools/project_api.py b/tools/project_api.py index d735b36ec9..dc5871f217 100644 --- a/tools/project_api.py +++ b/tools/project_api.py @@ -86,7 +86,6 @@ def generate_project_files(resources, export_path, target, name, toolchain, ide, exporter_cls, _ = get_exporter_toolchain(ide) exporter = exporter_cls(target, export_path, name, toolchain, extra_symbols=macros, resources=resources) - exporter.check_supported() exporter.generate() files = exporter.generated_files return files, exporter diff --git a/tools/test/examples/examples.py b/tools/test/examples/examples.py index fecadef456..a69f4a746d 100644 --- a/tools/test/examples/examples.py +++ b/tools/test/examples/examples.py @@ -14,6 +14,62 @@ sys.path.insert(0, ROOT) from tools.utils import argparse_force_uppercase_type import examples_lib as lib from examples_lib import SUPPORTED_TOOLCHAINS +from tools.export import EXPORTERS +from tools.build_api import get_mbed_official_release +from tools.targets import TARGET_MAP + +EXAMPLES = json.load(open(os.path.join(os.path.dirname(__file__), + "examples.json"))) + +def print_stuff(name, lst): + if lst: + print("#"*80) + print("# {} example combinations".format(name)) + print("#") + for thing in lst: + print(thing) + + +SUPPORTED_TOOLCHAINS = ["ARM", "IAR", "GCC_ARM"] +SUPPORTED_IDES = ["iar", "uvision", "make_gcc_arm", "make_iar", "make_armc5"] + + +def target_cross_toolchain(allowed_toolchains, + features=[], targets=TARGET_MAP.keys(), + toolchains=SUPPORTED_TOOLCHAINS): + """Generate pairs of target and toolchains + + Args: + allowed_toolchains - a list of all possible toolchains + + Kwargs: + features - the features that must be in the features array of a + target + targets - a list of available targets + toolchains - a list of available toolchains + """ + for release_target, release_toolchains in get_mbed_official_release("5"): + for toolchain in release_toolchains: + if (toolchain in allowed_toolchains and + toolchain in toolchains and + release_target in targets and + all(feature in TARGET_MAP[release_target].features + for feature in features)): + yield release_target, toolchain + + +def target_cross_ide(allowed_ides, + targets=TARGET_MAP.keys()): + """Generate pairs of target and ides + + Args: + allowed_ides - a list of all possible IDEs + + """ + for release_target, release_toolchains in get_mbed_official_release("5"): + for ide in allowed_ides: + if release_target in EXPORTERS[ide].TARGETS: + yield release_target, ide def main(): @@ -27,17 +83,61 @@ def main(): version_cmd.add_argument("tag") version_cmd.set_defaults(fn=do_versionning) compile_cmd = subparsers.add_parser("compile") - compile_cmd.set_defaults(fn=do_compile) + compile_cmd.set_defaults(fn=do_compile), compile_cmd.add_argument( "toolchains", nargs="*", default=SUPPORTED_TOOLCHAINS, type=argparse_force_uppercase_type(SUPPORTED_TOOLCHAINS, - "toolchain")) + "toolchain")), + export_cmd = subparsers.add_parser("export") + export_cmd.set_defaults(fn=do_export), + export_cmd.add_argument( + "ide", nargs="*", default=SUPPORTED_IDES, + type=argparse_force_uppercase_type(SUPPORTED_IDES, + "ide")) args = parser.parse_args() config = json.load(open(os.path.join(os.path.dirname(__file__), args.config))) - return args.fn(args, config) + +def do_export(args): + + def print_message(message, name): + print(message+ " %s"%name) + sys.stdout.flush() + + export_failures = [] + build_failures = [] + sucesses = [] + for example, requirements in EXAMPLES.iteritems(): + ex_name = basename(example) + if ex_name != "mbed-os-example-blinky": + continue + os.chdir(ex_name) + for target, ide in target_cross_ide(args.ide): + example_name = "{} {} {}".format(ex_name, target, + ide) + print_message("Export:",example_name) + proc = subprocess.Popen(["mbed-cli", "export", "-i", ide, + "-m", target]) + proc.wait() + if proc.returncode: + export_failures.append(example_name) + print_message("FAILURE Export:", example_name) + else: + print_message("SUCCESS Export:", example_name) + print_message("Build:", example_name) + if EXPORTERS[ide].build(ex_name): + print_message("FAILURE Build:", example_name) + build_failures.append(example_name) + else: + print_message("SUCCESS Build:", example_name) + sucesses.append(example_name) + print_stuff("Passed", sucesses) + print_stuff("Failed Export", export_failures) + print_stuff("Failed Building", build_failures) + return len(export_failures+build_failures) + def do_import(_, config): """Do the import step of this process""" diff --git a/tools/test/examples/examples_lib.py b/tools/test/examples/examples_lib.py index ec34c416df..110216e580 100644 --- a/tools/test/examples/examples_lib.py +++ b/tools/test/examples/examples_lib.py @@ -81,6 +81,19 @@ def target_cross_toolchain(allowed_toolchains, for feature in features)): yield target, toolchain +def target_cross_ide(allowed_ides, + targets=TARGET_MAP.keys()): + """Generate pairs of target and ides + + Args: + allowed_ides - a list of all possible IDEs + + """ + for release_target, release_toolchains in get_mbed_official_release("5"): + for ide in allowed_ides: + if release_target in EXPORTERS[ide].TARGETS: + yield release_target, ide + def get_repo_list(example): """ Returns a list of all the repos associated with the specific example in the json diff --git a/tools/test/export/build_test.py b/tools/test/export/build_test.py index f03f360d6b..d467018a43 100644 --- a/tools/test/export/build_test.py +++ b/tools/test/export/build_test.py @@ -22,6 +22,7 @@ from os.path import join, dirname, exists, abspath ROOT = abspath(join(dirname(__file__), "..", "..", "..")) sys.path.insert(0, ROOT) import argparse +import os from argparse import ArgumentTypeError import sys from shutil import rmtree @@ -37,9 +38,8 @@ 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, Test -from tools.export.exporters import FailedBuildException, \ - TargetNotSupportedException +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 @@ -125,9 +125,12 @@ class ExportBuildTest(object): % (test_case.mcu, test_case.ide, test_case.name)) - try: - exporter.build() - except FailedBuildException: + + cwd = os.getcwd() + os.chdir(exporter.export_dir) + res = EXPORTERS[exporter.NAME.lower()].build(exporter.project_name, clean=False) + os.chdir(cwd) + if res: self.failures.append("%s::%s\t%s" % (test_case.mcu, test_case.ide, test_case.name)) @@ -157,20 +160,19 @@ class ExportBuildTest(object): self.display_counter("Exporting test case %s::%s\t%s" % (test_case.mcu, test_case.ide, test_case.name)) - - try: - _, toolchain = get_exporter_toolchain(test_case.ide) - profile = extract_profile(self.parser, self.options, toolchain) - exporter = export(test_case.mcu, test_case.ide, + 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)) - except TargetNotSupportedException: - self.skips.append("%s::%s\t%s" % (test_case.mcu, test_case.ide, - test_case.name)) + 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 @@ -265,7 +267,7 @@ def main(): 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(targetnames))) + %(options.release, columnate(sorted(targetnames)))) v2_tests, v5_tests = [],[] if options.release == '5':