mirror of https://github.com/ARMmbed/mbed-os.git
New export-build tests.
Allows command line workflow of: mbed import mbed export Also revises exporter supported target checkspull/3172/head
parent
7829b2f9bf
commit
986af0dd5e
|
@ -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")
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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':
|
||||
|
|
Loading…
Reference in New Issue