Import Mbed OS hard-float snapshot

This commit is contained in:
Beslan
2026-06-01 20:15:04 +03:00
commit d3738e2f89
16278 changed files with 10628036 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
"""
Copyright (c) 2018, Arm Limited
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

View File

@@ -0,0 +1,151 @@
"""
Copyright (c) 2018, Arm Limited
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
GENERATE TEST CODE COVERAGE
"""
import os
import logging
import posixpath
import re
from .utils import execute_program
from .get_tools import get_gcov_program, \
get_gcovr_program
from .settings import COVERAGE_OUTPUT_TYPES
class CoverageAPI(object):
"""
Generate code coverage reports for unit tests.
"""
def __init__(self, mbed_os_root=None, build_dir=None):
self.root = mbed_os_root
if not self.root:
self.root = os.path.normpath(os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"../.."))
self.build_dir = build_dir
if not self.build_dir:
logging.error("No build directory given for CoverageAPI.")
def _gen_cmd(self, coverage_type, excludes, filter_regex):
# Generate coverage generation command:
args = [get_gcovr_program(),
"--gcov-executable",
get_gcov_program(),
"-r",
os.path.relpath(self.root, self.build_dir),
"."]
if coverage_type == "html":
args.extend(["--html",
"--html-detail",
"-o",
"./coverage/index.html"])
elif coverage_type == "xml":
args.extend(["-x",
"-o",
"./coverage.xml"])
else:
logging.error("Invalid coverage output type: %s" % coverage_type)
# Add exclude filters:
for path in excludes:
# Use posix separators if path is string
if isinstance(path, ("".__class__, u"".__class__)):
path = path.replace("\\", "/")
args.extend(["-e", path])
# Use regular expressions as is
elif isinstance(path, type(re.compile(""))):
args.extend(["-e", path.pattern])
# Add include filters:
if filter_regex:
filters = filter_regex.split(",")
for filt in filters:
regex = "(.+/)?.*%s" % filt.replace("-", "/")
args.extend(["-f", regex])
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
args.append("-v")
return args
def generate_reports(self,
outputs,
excludes=None,
filter_regex=None,
build_path=None):
"""
Run tests to generate coverage data, and generate coverage reports.
Positional arguments:
outputs - list of types to generate
Keyword arguments:
excludes - list of paths to exclude from the report
filter_regex - comma-separated string to use for test filtering
build_path - build path
"""
# Check for the tool
if get_gcovr_program() is None:
logging.error(
"No gcovr tool found in path. " +
"Cannot generate coverage reports.")
return
if build_path is None:
build_path = os.getcwd()
if outputs is None:
outputs = []
if excludes is None:
excludes = []
for output in outputs:
# Skip if invalid/unsupported output type
if output not in COVERAGE_OUTPUT_TYPES:
logging.warning(
"Invalid output type. " +
"Skip coverage report for type: %s." % output.upper())
continue
if output == "html":
# Create a build directory if not exist
coverage_path = os.path.join(build_path, "coverage")
if not os.path.exists(coverage_path):
os.mkdir(coverage_path)
coverage_output = os.path.join(coverage_path, "index.html")
else:
coverage_output = os.path.join(build_path, "coverage.xml")
# Generate the command
args = self._gen_cmd(output, excludes, filter_regex)
# Run the coverage tool
execute_program(
args,
"%s code coverage report generation failed." % output.upper(),
"%s code coverage report created in %s" % (output.upper(), coverage_output))

View File

@@ -0,0 +1,89 @@
"""
Copyright (c) 2018, Arm Limited
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
GET UNIT TEST TOOLS
"""
import platform
from .utils import is_tool
from .settings import MAKE_PROGRAMS, \
CXX_COMPILERS, \
C_COMPILERS, \
GCOV_PROGRAMS
def _get_program(programs, default=None):
if programs is None:
programs = []
for program in programs:
if is_tool(program):
return program
return default
def get_make_tool():
"""
Get make program
"""
default_make = "mingw32-make" if (platform.system() == "Windows") \
else "make"
return _get_program(MAKE_PROGRAMS, default=default_make)
def get_cmake_tool():
"""
Get cmake program
"""
return _get_program(["cmake"])
def get_valgrind_tool():
"""
Get Valgrind program
"""
return _get_program(["valgrind"])
def get_cxx_tool():
"""
Get C++ compiler
"""
return _get_program(CXX_COMPILERS, "g++")
def get_c_tool():
"""
Get C compiler
"""
return _get_program(C_COMPILERS, "gcc")
def get_gcov_program():
"""
Get gcov program
"""
return _get_program(GCOV_PROGRAMS, "gcov")
def get_gcovr_program():
"""
Get covr program
"""
return _get_program(["gcovr"])

142
UNITTESTS/unit_test/new.py Normal file
View File

@@ -0,0 +1,142 @@
"""
Copyright (c) 2018, Arm Limited
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
UNIT TEST GENERATE NEW UNIT TESTS
"""
from __future__ import print_function
import os
import re
import logging
class UnitTestGeneratorTool(object):
"""
Generator tool to create new unit tests from template
"""
def _replace_content(self, template_content, dirname, classname, extension):
if extension == "h":
content = re.sub(r"cppfile",
"",
template_content)
elif extension == "cpp":
content = re.sub(r"cppfile",
"../dirname/template.cpp",
template_content)
content = re.sub(r"headerfile", "../dirname/template.h", content)
content = re.sub(r"dirname", dirname, content)
content = re.sub(r"template", classname, content)
return content
def create(self, mbed_os_root=None, filepath=None):
"""
Generate a unit test for a file to be tested from templates
Keyword arguments:
mbed_os_root - Mbed OS root directory
filepath - Path to the file to be tested
"""
if filepath is None:
logging.error("No filepath given. Cannot create a new unit test.")
return
if mbed_os_root is None:
mbed_os_root = \
os.path.normpath(os.path.join(os.path.dirname(__file__),
"../.."))
if os.path.isabs(filepath):
filepath = os.path.join(
os.path.relpath(os.path.dirname(filepath), mbed_os_root),
os.path.basename(filepath))
if not re.match(r"((\w+\/)+)(\w+)\.(c|cpp|h|hpp)$", filepath):
logging.error(
"No proper path to source file given.")
return
if not os.path.isfile(os.path.join(mbed_os_root, filepath)):
logging.warning("Cannot find source file: %s from Mbed OS.",
filepath)
dir_name = re.sub(r"(\\|\/)(\w+)\.(cpp|h)$", "", filepath)
match = re.search(r"(\\|\/)(\w+)\.(cpp|h)$", filepath)
if match:
class_name = match.group(2)
file_extension = match.group(3)
# Create directory tree if not exist.
test_dir = os.path.join(mbed_os_root, "UNITTESTS", dir_name, class_name)
if not os.path.exists(test_dir):
os.makedirs(test_dir)
else:
logging.error("Unit tests exist already for this file.")
return
try:
suite_name = ("%s/%s" % (dir_name, class_name)).replace("/", "_")
# Test source file template
template_source_file = \
os.path.join(mbed_os_root,
"UNITTESTS/template/test_template.cpp")
with open(template_source_file, "r") as input_file:
template_content = input_file.read()
output_source_file = os.path.join(test_dir,
"test_%s.cpp" % class_name)
with open(output_source_file, "w") as output_file:
content = self._replace_content(template_content,
dir_name,
class_name,
file_extension)
output_file.writelines(content)
# Definition file template
template_source_file = \
os.path.join(mbed_os_root,
"UNITTESTS/template/unittest.cmake.template")
with open(template_source_file, "r") as input_file:
template_content = input_file.read()
output_source_file = os.path.join(test_dir, "unittest.cmake")
with open(output_source_file, "w") as output_file:
content = self._replace_content(template_content,
dir_name,
class_name,
file_extension)
output_file.writelines(content)
logging.info("""
Unit test %s created.
Generated files into %s directory.
""", suite_name, test_dir)
except IOError:
logging.error("Cannot create files for a unit test.")

View File

@@ -0,0 +1,153 @@
"""
Copyright (c) 2018, Arm Limited
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
UNIT TEST OPTIONS
"""
import argparse
import logging
from .settings import CMAKE_GENERATORS, MAKE_PROGRAMS, COVERAGE_ARGS
from .get_tools import get_make_tool
def get_options_parser():
"""
Create a unit test options parser.
Returns a parser object
"""
parser = argparse.ArgumentParser()
log_group = parser.add_mutually_exclusive_group()
log_group.add_argument("-v",
"--verbose",
help="Verbose output",
action="store_const",
const=logging.getLevelName(logging.DEBUG),
dest="log_level")
log_group.add_argument("--quiet",
help="Quiet output",
action="store_const",
const=logging.getLevelName(logging.ERROR),
dest="log_level")
parser.add_argument("--compile",
action="store_true",
help="Only compile unit tests",
dest="compile_only")
parser.add_argument("--run",
action="store_true",
help="Only run unit tests",
dest="run_only")
parser.add_argument("-c",
"--clean",
action="store_true",
help="Clean the build directory",
dest="clean")
parser.add_argument("-d",
"--debug",
action="store_true",
help="Enable debug build",
dest="debug_build")
parser.add_argument("--coverage",
choices=COVERAGE_ARGS,
help="Generate code coverage report",
dest="coverage")
parser.add_argument("--include-headers",
action="store_true",
help="Include headers to coverage reports, defaults to false.",
dest="include_headers")
parser.add_argument("-m",
"--make-program",
default=get_make_tool(),
choices=MAKE_PROGRAMS,
help="Which make program to use",
dest="make_program")
parser.add_argument("-g",
"--generator",
choices=CMAKE_GENERATORS,
help="Which CMake generator to use",
dest="cmake_generator")
parser.add_argument("-r",
"--regex",
help="Run tests matching a regular expression",
dest="test_regex")
parser.add_argument("--build",
default="build",
help="Build directory. Default: UNITTESTS/build/",
dest="build")
parser.add_argument("--valgrind",
help="Use Valgrind when running executables",
action="store_true",
dest="valgrind")
parser.add_argument("--new",
action="append",
help="Source file from which to generate test files. E.g. rtos/Semaphore.cpp",
metavar="FILEPATH",
dest="new_files")
return parser
def pretty_print_test_options(options=None):
"""
Pretty print test options
Keyword arguments:
options - options
"""
if options is None:
return
# Mode
logging.info("""
#####################
Mbed OS unit testing:
#####################
""")
logging.info("Steps:\n")
logging.info(" [%s] \tPrepare build directory", "AUTO")
logging.info(" \t\t - Clean: %s", options.clean)
logging.info(" \t\t - Directory: %s\n", options.build)
status = "SET" if options.compile_only else "UNSET"
logging.info(" [%s] \tBuild unit tests", status)
logging.info(" \t\t - Debug: %s", options.debug_build)
logging.info(" \t\t - CMake generator: %s", options.cmake_generator)
logging.info(" \t\t - Make program: %s\n", options.make_program)
status = "SET" if options.run_only else "UNSET"
logging.info(" [%s] \tRun unit tests", status)
logging.info(" \t\t - Filter: %s\n", options.test_regex)
if options.coverage:
logging.info(" [%s] \tGenerate coverage reports", "SET")
logging.info(" \t\t - Output: %s", options.coverage)
logging.info(" \t\t - Include headers: %s", options.include_headers)

View File

@@ -0,0 +1,42 @@
"""
Copyright (c) 2018, Arm Limited
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
UNIT TEST SETTINGS
"""
CMAKE_GENERATORS = ["Unix Makefiles", "MinGW Makefiles", "Ninja"]
MAKE_PROGRAMS = ["gmake", "make", "mingw32-make", "ninja"]
DEFAULT_CMAKE_GENERATORS = {
"gmake": "Unix Makefiles",
"make": "Unix Makefiles",
"mingw32-make": "MinGW Makefiles",
"ninja": "Ninja"
}
COVERAGE_ARGS = ["html",
"xml",
"both"]
COVERAGE_OUTPUT_TYPES = ["html", "xml"]
CXX_COMPILERS = ["g++-6", "g++-8", "g++-7", "g++-5", "g++-4.9", "g++"]
C_COMPILERS = ["gcc-6", "gcc-8", "gcc-7", "gcc-5", "gcc-4.9", "gcc"]
GCOV_PROGRAMS = ["gcov-6", "gcov-8", "gcov-7", "gcov-5", "gcov-4.9", "gcov"]

220
UNITTESTS/unit_test/test.py Normal file
View File

@@ -0,0 +1,220 @@
"""
Copyright (c) 2018, Arm Limited
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
UNIT TEST BUILD & RUN
"""
import os
import logging
import sys
import psutil
from shutil import copy
from .utils import execute_program
from .get_tools import get_make_tool, \
get_cmake_tool, \
get_cxx_tool, \
get_c_tool, \
get_valgrind_tool
from .settings import DEFAULT_CMAKE_GENERATORS
class UnitTestTool(object):
"""
Unit test tool to:
- prepare build directory
- create makefiles
- build unit tests
- run unit tests
- generate code coverage reports
"""
def __init__(self,
make_program=None):
"""
Constructor
Keyword arguments:
make_program - Make tool to use
"""
self.make_program = make_program
if self.make_program is None:
self.make_program = get_make_tool()
def create_makefiles(self,
path_to_src=None,
generator=None,
coverage_output_type=None,
debug=False,
valgrind=False):
"""
Create Makefiles and prepare targets with CMake.
Keyword arguments:
path_to_src - Path to source directory
generator - Type of Makefiles to generate
coverage_output_type - Generate HTML, XML or both reports
debug - Target debug or release build
"""
if generator is None:
generator = DEFAULT_CMAKE_GENERATORS.get(self.make_program,
"Unix Makefiles")
cmake = get_cmake_tool()
if cmake is None:
logging.error(
"No CMake found in Path. Install all the required tools.")
sys.exit(1)
args = [cmake,
"-G",
generator,
"-DCMAKE_MAKE_PROGRAM=%s" % self.make_program,
"-DCMAKE_CXX_COMPILER=%s" % get_cxx_tool(),
"-DCMAKE_C_COMPILER=%s" % get_c_tool()]
if debug:
args.append("-DCMAKE_BUILD_TYPE=Debug")
if coverage_output_type:
args.append("-DCOVERAGE:STRING=%s" % coverage_output_type)
if valgrind:
valgrind = get_valgrind_tool()
if valgrind is None:
logging.error(
"No Valgrind found in Path. Install all the required tools.\n")
sys.exit(1)
args.append("-DVALGRIND=1")
args.append("-DMEMORYCHECK_COMMAND_OPTIONS=\"--track-origins=yes\" \"--leak-check=full\" \"--show-reachable=yes\" \"--error-exitcode=1\"")
else:
args.append("-DVALGRIND=0")
if path_to_src is not None:
args.append(path_to_src)
execute_program(args,
"CMake failed to run successfully. See error message.")
def build_tests(self):
"""
Build unit tests and libraries to be tested.
"""
args = [self.make_program]
# Speed up compilation by running on more than one core
count = psutil.cpu_count()
args.append("-j{}".format(count+1))
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
args.append("VERBOSE=1")
execute_program(args,
"Building unit tests failed.",
"Unit tests built successfully.")
def run_tests(self, filter_regex=None, valgrind=False):
"""
Run unit tests.
Keyword arguments:
filter_regex - Regular expression to select which tests to run
"""
args = [self.make_program, "test"]
if valgrind:
if filter_regex:
args.append("ARGS=-R %s -V -D ExperimentalMemCheck" % filter_regex)
else:
args.append("ARGS=-V -D ExperimentalMemCheck")
else:
if filter_regex:
args.append("ARGS=-R %s -V -D ExperimentalTest" % filter_regex)
else:
args.append("ARGS=-V -D ExperimentalTest")
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
args.append("VERBOSE=1")
execute_program(args, "Unit test run failed.")
def prepare_build_directory(self,
path_to_src=None,
build_path=None,
clean=False):
"""
Create build directory if not exist and
change current working directory to it
Keyword arguments:
path_to_src - Path to source directory
build_path - Path to build directory
clean - Clean build directory
"""
if build_path is None:
build_path = os.getcwd()
# Clean CMake data if exists.
if clean:
self._clean_build(build_path)
# Create build directory if not exist.
if not os.path.exists(build_path):
os.makedirs(build_path)
filename = ".mbedignore"
inputfile = os.path.join(path_to_src, filename)
outputfile = os.path.join(build_path, filename)
copy(inputfile, outputfile)
# Change current working directory to build directory.
os.chdir(build_path)
def _clean_build(self, build_path=None):
"""
Try clean build directory
Keyword arguments:
build_path - Path to build directory
"""
logging.info("Cleaning build directory...")
if os.path.exists(os.path.join(build_path, "CMakeCache.txt")):
args = [self.make_program,
"-C",
build_path,
"--no-print-directory",
"clean"]
# Remove coverage files
for root, _, files in os.walk(build_path):
for current_file in files:
if current_file.endswith((".gcno", ".gcda")):
os.remove(os.path.join(root, current_file))
execute_program(args,
"Clean step failed.",
"Clean done.")
else:
logging.warning("%s does not exist or \
does not contain previous build data.", build_path)

View File

@@ -0,0 +1,71 @@
"""
Copyright (c) 2018, Arm Limited
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
UNIT TEST UTILITIES
"""
import platform
import subprocess
import logging
import sys
def is_tool(name):
"""
Test if tool is found in PATH
@param name: executable name
@return: true if tool found, false otherwise
"""
cmd = "where" if platform.system() == "Windows" else "which"
try:
subprocess.check_output([cmd, name], stderr=subprocess.STDOUT)
return True
except subprocess.CalledProcessError:
return False
def execute_program(args, error_msg="An error occurred!", success_msg=None):
"""
Execute program in a subprocess with given arguments
@param args: program and its arguments in a list
@param success_msg: message to show in case of success
@param error_msg: message to show in case of failure
"""
try:
process = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
# Output is stripped to remove newline character. logging adds its own
# so we avoid double newlines.
# Because the process can terminate before the loop has read all lines,
# we read the output remnant just in case. Otherwise we lose it.
while process.poll() is None:
logging.info(process.stdout.readline().decode('utf8').rstrip('\n'))
logging.info(process.stdout.read().decode('utf8').rstrip('\n'))
retcode = process.wait()
if retcode:
raise subprocess.CalledProcessError(retcode, args)
elif success_msg:
logging.info(success_msg)
except subprocess.CalledProcessError as error:
logging.error(error_msg)
sys.exit(error.returncode)