Skip to content

Commit

Permalink
Test builds with minimal-printf and optional check for floats post-build
Browse files Browse the repository at this point in the history
Unless specified otherwise, run the example projects compilation test
using the minimal-printf extension profile. Also add an optional
argument to the `compile` subcommand to perform post-build checks.

The only post build check supported at the moment is checking for
floating point symbol in the object file. The floating point check
excludes example projects which currently still use floating points
  • Loading branch information
hugueskamba committed Sep 6, 2019
1 parent 9da5c22 commit 0944477
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 23 deletions.
54 changes: 42 additions & 12 deletions tools/test/examples/examples.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
"""
Copyright (c) 2017-2019 ARM Limited. All rights reserved.
Expand Down Expand Up @@ -27,17 +28,20 @@
""" import and bulid a bunch of example programs """

ROOT = abspath(dirname(dirname(dirname(dirname(__file__)))))
sys.path.insert(0, ROOT)
DEFAULT_BUILD_PROFILES = [
"develop",
"mbed-os/tools/profiles/extensions/minimal-printf.json",
]
sys.path.append(ROOT)

from tools.utils import argparse_force_uppercase_type
from tools.utils import argparse_many
from tools.build_api import get_mbed_official_release
import examples_lib as lib
from examples_lib import SUPPORTED_TOOLCHAINS, SUPPORTED_IDES

def main():
"""Entry point"""

def parse_args():
"""Parse the arguments passed to the script."""
official_targets = get_mbed_official_release("5")
official_target_names = [x[0] for x in official_targets]

Expand Down Expand Up @@ -72,10 +76,21 @@ def main():
argparse_force_uppercase_type(
official_target_names, "MCU")),
default=official_target_names)
compile_cmd.add_argument(
"--post-checks",
nargs='+',
choices=list(lib.SUPPORTED_POST_BUILD_CHECKS.keys()),
default=None,
help="specify check operation(s) to perform after compiling.",
)

compile_cmd.add_argument("--profile",
help=("build profile file"),
metavar="profile")
compile_cmd.add_argument(
"--profiles",
nargs='+',
default=DEFAULT_BUILD_PROFILES,
metavar="profile",
help=f"build profile file(s). default = {DEFAULT_BUILD_PROFILES}",
)

compile_cmd.add_argument("-v", "--verbose",
action="store_true",
Expand All @@ -84,7 +99,7 @@ def main():
help="Verbose diagnostic output")

export_cmd = subparsers.add_parser("export")
export_cmd.set_defaults(fn=do_export),
export_cmd.set_defaults(fn=do_export)
export_cmd.add_argument(
"ide", nargs="*", default=SUPPORTED_IDES,
type=argparse_force_uppercase_type(SUPPORTED_IDES,
Expand All @@ -97,7 +112,13 @@ def main():
argparse_force_uppercase_type(
official_target_names, "MCU")),
default=official_target_names)
args = parser.parse_args()
return parser.parse_args()


def main():
"""Entry point"""

args = parse_args()
config = json.load(open(os.path.join(os.path.dirname(__file__),
args.config)))

Expand Down Expand Up @@ -137,12 +158,21 @@ def do_deploy(_, config, examples):
def do_compile(args, config, examples):
"""Do the compile step"""
results = {}
results = lib.compile_repos(config, args.toolchains, args.mcu, args.profile, args.verbose, examples)
results = lib.compile_repos(
config,
args.toolchains,
args.mcu,
args.profiles,
args.verbose,
examples,
args.post_checks,
)
lib.print_summary(results)
failures = lib.get_num_failures(results)
print("Number of failures = %d" % failures)
return failures

return failures


def do_versionning(args, config, examples):
""" Test update the mbed-os to the version specified by the tag """
return lib.update_mbedos_version(config, args.tag, examples)
Expand Down
136 changes: 125 additions & 11 deletions tools/test/examples/examples_lib.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
"""
Copyright (c) 2017-2019 ARM Limited. All rights reserved.
Expand All @@ -15,6 +16,7 @@
See the License for the specific language governing permissions and
limitations
"""
import json
import os
from os.path import dirname, abspath, basename, join, normpath
import os.path
Expand All @@ -30,8 +32,16 @@
"""


EXECUTABLE_ANALYSIS_TOOLS_PATH = join(
os.path.dirname(__file__),
"../../executable_analysis_tools/",
)
sys.path.append(EXECUTABLE_ANALYSIS_TOOLS_PATH)
import elf_float_checker

ROOT = abspath(dirname(dirname(dirname(dirname(__file__)))))
sys.path.insert(0, ROOT)
sys.path.append(ROOT)

from tools.build_api import get_mbed_official_release
from tools.targets import TARGET_MAP
Expand Down Expand Up @@ -349,7 +359,7 @@ def status(message):
return results


def compile_repos(config, toolchains, targets, profile, verbose, examples):
def compile_repos(config, toolchains, targets, profiles, verbose, examples, post_checks):
"""Compiles combinations of example programs, targets and compile chains.
The results are returned in a [key: value] dictionary format:
Expand Down Expand Up @@ -388,6 +398,7 @@ def compile_repos(config, toolchains, targets, profile, verbose, examples):
test_example = True
else:
test_example = False

if example['compile']:
for repo_info in get_repo_list(example):
name = basename(repo_info['repo'])
Expand All @@ -399,28 +410,54 @@ def compile_repos(config, toolchains, targets, profile, verbose, examples):
example['features']):

build_command = ["mbed-cli", "compile", "-t", toolchain, "-m", target] + (['-vv'] if verbose else [])
if profile:
build_command.append("--profile")
build_command.append(profile)
if profiles:
for profile in profiles:
build_command.extend(["--profile", profile])

print("Compiling [%s] for [%s] with toolchain [%s]\n\n> %s" % (name, target, toolchain, " ".join(build_command)))

proc = subprocess.Popen(build_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if (
post_checks
and POST_BUILD_CHECK_FLOATS in post_checks
and name not in POST_BUILD_CHECK_FLOATS_EXCLUDED_EXAMPLES
):
# Disable floating point support for minimal-printf if
# a post build check for minimal-printf is specified.
has_float_support = "false"
else:
has_float_support = "true"

_set_minimal_printf_floats_status(has_float_support)

proc = subprocess.run(
build_command,
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)

std_out = proc.stdout.decode()
std_err = proc.stderr.decode()

std_out, std_err = proc.communicate()
std_out = std_out.decode('utf-8')
print ("\n#### STDOUT ####\n%s\n#### STDERR ####\n%s\n#### End of STDOUT/STDERR ####\n" % (std_out,std_err))

if test_example:
log = example['compare_log'].pop(0)
# example['compare_log'] is a list of log file/files, which matches each examples/sub-examples from same repo.
# pop the log file out of list regardless the compilation for each example pass of fail
image = fetch_output_image(std_out)
if image:
image_info = [{"binary_type": "bootable","path": normpath(join(name,image)),"compare_log":log}]
if post_checks:
try:
_perform_post_build_checks(
name, post_checks, dirname(image)
)
except PostBuildCheckFailureError as error:
failures.append(str(error))
else:
print ("Warning: could not find built image for example %s" % name)

example_summary = "{} {} {}".format(name, target, toolchain)
if proc.returncode:
failures.append(example_summary)
Expand Down Expand Up @@ -494,4 +531,81 @@ def fetch_output_image(output):
image = lines[index][7:]
if os.path.isfile(image):
return image
return False
return False


def _perform_post_build_checks(example_name, checks, elf_dir_path):
"""
Perform post build checks.
The function raises a PostBuildCheckFailureError exceptions if an error is
encountered.
"""
if not checks:
raise PostBuildCheckFailureError("No post build check specified")

# Find the elf file
elf_file = None
for dirpath, _, filenames in os.walk(elf_dir_path):
for filename in filenames:
if filename.endswith(".elf"):
elf_file = os.path.join(dirpath, filename)

if not elf_file:
raise PostBuildCheckFailureError(
"Cannot find ELF file in {}".format(elf_dir_path)
)

for check in set(checks):
SUPPORTED_POST_BUILD_CHECKS[check](example_name, elf_file)


def _set_minimal_printf_floats_status(status):
"""
Enable or disable floating point support in minimal-printf.
Pass the string `true` or `false` to enable or disable floating point.
"""
with open(
os.path.join(ROOT, "platform", "mbed_lib.json"), "r"
) as platform_lib_file:
data = json.load(platform_lib_file)

data["config"]["minimal-printf-enable-floating-point"]["value"] = status

with open(
os.path.join(ROOT, "platform", "mbed_lib.json"), "w"
) as platform_lib_file:
json.dump(data, platform_lib_file, indent=4)


# Post-build check functions should be listed below and must raise a
# a PostBuildCheckFailureError in case of failure

def _post_build_check_floating_point(example_name, elf_file):
"""Check if there are floating points in the executable."""

if example_name in POST_BUILD_CHECK_FLOATS_EXCLUDED_EXAMPLES:
return

float_symbols = elf_float_checker.check_float_symbols(elf_file)

if float_symbols:
raise PostBuildCheckFailureError(
"Floating point symbols found in executable: {}".format(
float_symbols
)
)

# Specify the example project that should not be checked for floating point because they will always fail
POST_BUILD_CHECK_FLOATS_EXCLUDED_EXAMPLES = [
"mbed-os-example-lorawan", # The LoRaWAN is not actively worked on and we are consideing dropping LoRaWAN example from Mbed OS examples.
"nanostack-border-router", # nanostack-border-router example depends on ST changes to stm-spirit1-rf-driver library.
]
POST_BUILD_CHECK_FLOATS = "FLOATS"
SUPPORTED_POST_BUILD_CHECKS = {
POST_BUILD_CHECK_FLOATS : _post_build_check_floating_point
}

class PostBuildCheckFailureError(Exception):
"""An exception to indicate that a post build check failed."""

0 comments on commit 0944477

Please sign in to comment.