New export-build tests.

Allows command line workflow of:
mbed import
mbed export

Also revises exporter supported target checks
pull/3172/head
Sarah Marsh 2016-10-31 16:00:15 -05:00
parent 7829b2f9bf
commit 986af0dd5e
10 changed files with 235 additions and 98 deletions

View File

@ -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")

View File

@ -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(

View File

@ -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

View File

@ -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"""

View File

@ -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

View File

@ -232,7 +232,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,

View File

@ -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

View File

@ -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"""

View File

@ -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

View File

@ -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':