Skip to content

Commit 0944477

Browse files
committed
Test builds with minimal-printf and optional check for floats post-build
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
1 parent 9da5c22 commit 0944477

File tree

3 files changed

+167
-23
lines changed

3 files changed

+167
-23
lines changed

tools/test/examples/examples.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#!/usr/bin/env python3
12
"""
23
Copyright (c) 2017-2019 ARM Limited. All rights reserved.
34
@@ -27,17 +28,20 @@
2728
""" import and bulid a bunch of example programs """
2829

2930
ROOT = abspath(dirname(dirname(dirname(dirname(__file__)))))
30-
sys.path.insert(0, ROOT)
31+
DEFAULT_BUILD_PROFILES = [
32+
"develop",
33+
"mbed-os/tools/profiles/extensions/minimal-printf.json",
34+
]
35+
sys.path.append(ROOT)
3136

3237
from tools.utils import argparse_force_uppercase_type
3338
from tools.utils import argparse_many
3439
from tools.build_api import get_mbed_official_release
3540
import examples_lib as lib
3641
from examples_lib import SUPPORTED_TOOLCHAINS, SUPPORTED_IDES
3742

38-
def main():
39-
"""Entry point"""
40-
43+
def parse_args():
44+
"""Parse the arguments passed to the script."""
4145
official_targets = get_mbed_official_release("5")
4246
official_target_names = [x[0] for x in official_targets]
4347

@@ -72,10 +76,21 @@ def main():
7276
argparse_force_uppercase_type(
7377
official_target_names, "MCU")),
7478
default=official_target_names)
79+
compile_cmd.add_argument(
80+
"--post-checks",
81+
nargs='+',
82+
choices=list(lib.SUPPORTED_POST_BUILD_CHECKS.keys()),
83+
default=None,
84+
help="specify check operation(s) to perform after compiling.",
85+
)
7586

76-
compile_cmd.add_argument("--profile",
77-
help=("build profile file"),
78-
metavar="profile")
87+
compile_cmd.add_argument(
88+
"--profiles",
89+
nargs='+',
90+
default=DEFAULT_BUILD_PROFILES,
91+
metavar="profile",
92+
help=f"build profile file(s). default = {DEFAULT_BUILD_PROFILES}",
93+
)
7994

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

86101
export_cmd = subparsers.add_parser("export")
87-
export_cmd.set_defaults(fn=do_export),
102+
export_cmd.set_defaults(fn=do_export)
88103
export_cmd.add_argument(
89104
"ide", nargs="*", default=SUPPORTED_IDES,
90105
type=argparse_force_uppercase_type(SUPPORTED_IDES,
@@ -97,7 +112,13 @@ def main():
97112
argparse_force_uppercase_type(
98113
official_target_names, "MCU")),
99114
default=official_target_names)
100-
args = parser.parse_args()
115+
return parser.parse_args()
116+
117+
118+
def main():
119+
"""Entry point"""
120+
121+
args = parse_args()
101122
config = json.load(open(os.path.join(os.path.dirname(__file__),
102123
args.config)))
103124

@@ -137,12 +158,21 @@ def do_deploy(_, config, examples):
137158
def do_compile(args, config, examples):
138159
"""Do the compile step"""
139160
results = {}
140-
results = lib.compile_repos(config, args.toolchains, args.mcu, args.profile, args.verbose, examples)
161+
results = lib.compile_repos(
162+
config,
163+
args.toolchains,
164+
args.mcu,
165+
args.profiles,
166+
args.verbose,
167+
examples,
168+
args.post_checks,
169+
)
141170
lib.print_summary(results)
142171
failures = lib.get_num_failures(results)
143172
print("Number of failures = %d" % failures)
144-
return failures
145-
173+
return failures
174+
175+
146176
def do_versionning(args, config, examples):
147177
""" Test update the mbed-os to the version specified by the tag """
148178
return lib.update_mbedos_version(config, args.tag, examples)

tools/test/examples/examples_lib.py

Lines changed: 125 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#!/usr/bin/env python3
12
"""
23
Copyright (c) 2017-2019 ARM Limited. All rights reserved.
34
@@ -15,6 +16,7 @@
1516
See the License for the specific language governing permissions and
1617
limitations
1718
"""
19+
import json
1820
import os
1921
from os.path import dirname, abspath, basename, join, normpath
2022
import os.path
@@ -30,8 +32,16 @@
3032
3133
"""
3234

35+
36+
EXECUTABLE_ANALYSIS_TOOLS_PATH = join(
37+
os.path.dirname(__file__),
38+
"../../executable_analysis_tools/",
39+
)
40+
sys.path.append(EXECUTABLE_ANALYSIS_TOOLS_PATH)
41+
import elf_float_checker
42+
3343
ROOT = abspath(dirname(dirname(dirname(dirname(__file__)))))
34-
sys.path.insert(0, ROOT)
44+
sys.path.append(ROOT)
3545

3646
from tools.build_api import get_mbed_official_release
3747
from tools.targets import TARGET_MAP
@@ -349,7 +359,7 @@ def status(message):
349359
return results
350360

351361

352-
def compile_repos(config, toolchains, targets, profile, verbose, examples):
362+
def compile_repos(config, toolchains, targets, profiles, verbose, examples, post_checks):
353363
"""Compiles combinations of example programs, targets and compile chains.
354364
355365
The results are returned in a [key: value] dictionary format:
@@ -388,6 +398,7 @@ def compile_repos(config, toolchains, targets, profile, verbose, examples):
388398
test_example = True
389399
else:
390400
test_example = False
401+
391402
if example['compile']:
392403
for repo_info in get_repo_list(example):
393404
name = basename(repo_info['repo'])
@@ -399,28 +410,54 @@ def compile_repos(config, toolchains, targets, profile, verbose, examples):
399410
example['features']):
400411

401412
build_command = ["mbed-cli", "compile", "-t", toolchain, "-m", target] + (['-vv'] if verbose else [])
402-
if profile:
403-
build_command.append("--profile")
404-
build_command.append(profile)
413+
if profiles:
414+
for profile in profiles:
415+
build_command.extend(["--profile", profile])
405416

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

408-
proc = subprocess.Popen(build_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
419+
if (
420+
post_checks
421+
and POST_BUILD_CHECK_FLOATS in post_checks
422+
and name not in POST_BUILD_CHECK_FLOATS_EXCLUDED_EXAMPLES
423+
):
424+
# Disable floating point support for minimal-printf if
425+
# a post build check for minimal-printf is specified.
426+
has_float_support = "false"
427+
else:
428+
has_float_support = "true"
429+
430+
_set_minimal_printf_floats_status(has_float_support)
431+
432+
proc = subprocess.run(
433+
build_command,
434+
stdin=None,
435+
stdout=subprocess.PIPE,
436+
stderr=subprocess.PIPE,
437+
)
438+
439+
std_out = proc.stdout.decode()
440+
std_err = proc.stderr.decode()
409441

410-
std_out, std_err = proc.communicate()
411-
std_out = std_out.decode('utf-8')
412442
print ("\n#### STDOUT ####\n%s\n#### STDERR ####\n%s\n#### End of STDOUT/STDERR ####\n" % (std_out,std_err))
413-
443+
414444
if test_example:
415445
log = example['compare_log'].pop(0)
416446
# example['compare_log'] is a list of log file/files, which matches each examples/sub-examples from same repo.
417447
# pop the log file out of list regardless the compilation for each example pass of fail
418448
image = fetch_output_image(std_out)
419449
if image:
420450
image_info = [{"binary_type": "bootable","path": normpath(join(name,image)),"compare_log":log}]
451+
if post_checks:
452+
try:
453+
_perform_post_build_checks(
454+
name, post_checks, dirname(image)
455+
)
456+
except PostBuildCheckFailureError as error:
457+
failures.append(str(error))
421458
else:
422459
print ("Warning: could not find built image for example %s" % name)
423-
460+
424461
example_summary = "{} {} {}".format(name, target, toolchain)
425462
if proc.returncode:
426463
failures.append(example_summary)
@@ -494,4 +531,81 @@ def fetch_output_image(output):
494531
image = lines[index][7:]
495532
if os.path.isfile(image):
496533
return image
497-
return False
534+
return False
535+
536+
537+
def _perform_post_build_checks(example_name, checks, elf_dir_path):
538+
"""
539+
Perform post build checks.
540+
541+
The function raises a PostBuildCheckFailureError exceptions if an error is
542+
encountered.
543+
"""
544+
if not checks:
545+
raise PostBuildCheckFailureError("No post build check specified")
546+
547+
# Find the elf file
548+
elf_file = None
549+
for dirpath, _, filenames in os.walk(elf_dir_path):
550+
for filename in filenames:
551+
if filename.endswith(".elf"):
552+
elf_file = os.path.join(dirpath, filename)
553+
554+
if not elf_file:
555+
raise PostBuildCheckFailureError(
556+
"Cannot find ELF file in {}".format(elf_dir_path)
557+
)
558+
559+
for check in set(checks):
560+
SUPPORTED_POST_BUILD_CHECKS[check](example_name, elf_file)
561+
562+
563+
def _set_minimal_printf_floats_status(status):
564+
"""
565+
Enable or disable floating point support in minimal-printf.
566+
567+
Pass the string `true` or `false` to enable or disable floating point.
568+
"""
569+
with open(
570+
os.path.join(ROOT, "platform", "mbed_lib.json"), "r"
571+
) as platform_lib_file:
572+
data = json.load(platform_lib_file)
573+
574+
data["config"]["minimal-printf-enable-floating-point"]["value"] = status
575+
576+
with open(
577+
os.path.join(ROOT, "platform", "mbed_lib.json"), "w"
578+
) as platform_lib_file:
579+
json.dump(data, platform_lib_file, indent=4)
580+
581+
582+
# Post-build check functions should be listed below and must raise a
583+
# a PostBuildCheckFailureError in case of failure
584+
585+
def _post_build_check_floating_point(example_name, elf_file):
586+
"""Check if there are floating points in the executable."""
587+
588+
if example_name in POST_BUILD_CHECK_FLOATS_EXCLUDED_EXAMPLES:
589+
return
590+
591+
float_symbols = elf_float_checker.check_float_symbols(elf_file)
592+
593+
if float_symbols:
594+
raise PostBuildCheckFailureError(
595+
"Floating point symbols found in executable: {}".format(
596+
float_symbols
597+
)
598+
)
599+
600+
# Specify the example project that should not be checked for floating point because they will always fail
601+
POST_BUILD_CHECK_FLOATS_EXCLUDED_EXAMPLES = [
602+
"mbed-os-example-lorawan", # The LoRaWAN is not actively worked on and we are consideing dropping LoRaWAN example from Mbed OS examples.
603+
"nanostack-border-router", # nanostack-border-router example depends on ST changes to stm-spirit1-rf-driver library.
604+
]
605+
POST_BUILD_CHECK_FLOATS = "FLOATS"
606+
SUPPORTED_POST_BUILD_CHECKS = {
607+
POST_BUILD_CHECK_FLOATS : _post_build_check_floating_point
608+
}
609+
610+
class PostBuildCheckFailureError(Exception):
611+
"""An exception to indicate that a post build check failed."""

0 commit comments

Comments
 (0)