Skip to content

Commit 48f90c0

Browse files
authored
Merge pull request ARMmbed#11904 from jamesbeyond/HW_gcov
TOOLS: enable build greentea test coverage for HW
2 parents a81f016 + d047b28 commit 48f90c0

File tree

7 files changed

+68
-17
lines changed

7 files changed

+68
-17
lines changed

tools/build_api.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ def target_supports_toolchain(target, toolchain_name):
443443
def prepare_toolchain(src_paths, build_dir, target, toolchain_name,
444444
macros=None, clean=False, jobs=1,
445445
notify=None, config=None, app_config=None,
446-
build_profile=None, ignore=None):
446+
build_profile=None, ignore=None, coverage_patterns=None):
447447
""" Prepares resource related objects - toolchain, target, config
448448
449449
Positional arguments:
@@ -460,6 +460,7 @@ def prepare_toolchain(src_paths, build_dir, target, toolchain_name,
460460
app_config - location of a chosen mbed_app.json file
461461
build_profile - a list of mergeable build profiles
462462
ignore - list of paths to add to mbedignore
463+
coverage_patterns - list of patterns for code coverage
463464
"""
464465

465466
# We need to remove all paths which are repeated to avoid
@@ -483,6 +484,9 @@ def prepare_toolchain(src_paths, build_dir, target, toolchain_name,
483484
target.default_toolchain = "uARM"
484485
toolchain_name = selected_toolchain_name
485486

487+
if coverage_patterns:
488+
target.extra_labels.append(u'COVERAGE')
489+
486490
try:
487491
cur_tc = TOOLCHAIN_CLASSES[toolchain_name]
488492
except KeyError:
@@ -494,7 +498,7 @@ def prepare_toolchain(src_paths, build_dir, target, toolchain_name,
494498
profile[key].extend(contents[toolchain_name].get(key, []))
495499

496500
toolchain = cur_tc(
497-
target, notify, macros, build_dir=build_dir, build_profile=profile)
501+
target, notify, macros, build_dir=build_dir, build_profile=profile, coverage_patterns=coverage_patterns)
498502

499503
toolchain.config = config
500504
toolchain.jobs = jobs
@@ -517,7 +521,7 @@ def build_project(src_paths, build_path, target, toolchain_name,
517521
report=None, properties=None, project_id=None,
518522
project_description=None, config=None,
519523
app_config=None, build_profile=None, stats_depth=None,
520-
ignore=None, resource_filter=None):
524+
ignore=None, resource_filter=None, coverage_patterns=None):
521525
""" Build a project. A project may be a test or a user program.
522526
523527
Positional arguments:
@@ -562,7 +566,7 @@ def build_project(src_paths, build_path, target, toolchain_name,
562566
toolchain = prepare_toolchain(
563567
src_paths, build_path, target, toolchain_name, macros=macros,
564568
clean=clean, jobs=jobs, notify=notify, config=config,
565-
app_config=app_config, build_profile=build_profile, ignore=ignore)
569+
app_config=app_config, build_profile=build_profile, ignore=ignore, coverage_patterns=coverage_patterns)
566570
toolchain.version_check()
567571

568572
# The first path will give the name to the library
@@ -668,7 +672,7 @@ def build_library(src_paths, build_path, target, toolchain_name,
668672
archive=True, notify=None, macros=None, inc_dirs=None, jobs=1,
669673
report=None, properties=None, project_id=None,
670674
remove_config_header_file=False, app_config=None,
671-
build_profile=None, ignore=None, resource_filter=None):
675+
build_profile=None, ignore=None, resource_filter=None, coverage_patterns=None):
672676
""" Build a library
673677
674678
Positional arguments:
@@ -719,7 +723,7 @@ def build_library(src_paths, build_path, target, toolchain_name,
719723
toolchain = prepare_toolchain(
720724
src_paths, build_path, target, toolchain_name, macros=macros,
721725
clean=clean, jobs=jobs, notify=notify, app_config=app_config,
722-
build_profile=build_profile, ignore=ignore)
726+
build_profile=build_profile, ignore=ignore, coverage_patterns=coverage_patterns)
723727
toolchain.version_check()
724728

725729
# The first path will give the name to the library

tools/test.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from tools.config import ConfigException, Config
3030
from tools.test_configs import get_default_config
3131
from tools.test_api import find_tests, get_test_config, print_tests, build_tests, test_spec_from_test_builds
32-
from tools.options import get_default_options_parser, extract_profile, extract_mcus
32+
from tools.options import get_default_options_parser, extract_profile, extract_mcus, argparse_profile_filestring_type
3333
from tools.build_api import build_library
3434
from tools.build_api import print_build_memory_usage
3535
from tools.build_api import merge_build_data
@@ -125,6 +125,9 @@ def main():
125125
parser.add_argument("--ignore", dest="ignore", type=argparse_many(str),
126126
default=None, help="Comma separated list of patterns to add to mbedignore (eg. ./main.cpp)")
127127

128+
parser.add_argument("--coverage-filters", dest="coverage_patterns", nargs='+',
129+
default=[], help="match patterns to build with debug compile and coverage linker options")
130+
128131
parser.add_argument("--icetea",
129132
action="store_true",
130133
dest="icetea",
@@ -230,6 +233,13 @@ def main():
230233
if not base_source_paths:
231234
base_source_paths = ['.']
232235

236+
# Coverage requires debug profile
237+
if options.coverage_patterns:
238+
if toolchain != u'GCC_ARM':
239+
raise ToolException('Coverage supports only GCC_ARM toolchain')
240+
options.profile.append(argparse_profile_filestring_type('debug'))
241+
print("[Warning] Test building with coverage filters %s" % options.coverage_patterns)
242+
233243
build_report = {}
234244
build_properties = {}
235245

@@ -255,6 +265,7 @@ def main():
255265
app_config=config,
256266
build_profile=profile,
257267
ignore=options.ignore,
268+
coverage_patterns=options.coverage_patterns,
258269
resource_filter=resource_filter
259270
)
260271

@@ -301,6 +312,7 @@ def main():
301312
build_profile=profile,
302313
stats_depth=options.stats_depth,
303314
ignore=options.ignore,
315+
coverage_patterns=options.coverage_patterns,
304316
resource_filter=resource_filter)
305317

306318
# If a path to a test spec is provided, write it to a file

tools/test_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2112,7 +2112,7 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
21122112
silent=False, report=None, properties=None,
21132113
continue_on_build_fail=False, app_config=None,
21142114
build_profile=None, stats_depth=None, ignore=None,
2115-
resource_filter=None):
2115+
resource_filter=None, coverage_patterns=None):
21162116
"""Given the data structure from 'find_tests' and the typical build parameters,
21172117
build all the tests
21182118
@@ -2168,6 +2168,7 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
21682168
'toolchain_paths': TOOLCHAIN_PATHS,
21692169
'stats_depth': stats_depth,
21702170
'notify': MockNotifier(),
2171+
'coverage_patterns': coverage_patterns,
21712172
'resource_filter': resource_filter
21722173
}
21732174

tools/toolchains/arm.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def check_executable():
6969
return mbedToolchain.generic_check_executable("ARM", 'armcc', 2, 'bin')
7070

7171
def __init__(self, target, notify=None, macros=None,
72-
build_profile=None, build_dir=None):
72+
build_profile=None, build_dir=None, coverage_patterns=None):
7373
mbedToolchain.__init__(
7474
self, target, notify, macros, build_dir=build_dir,
7575
build_profile=build_profile)
@@ -453,15 +453,17 @@ def __init__(
453453
notify=None,
454454
macros=None,
455455
build_profile=None,
456-
build_dir=None
456+
build_dir=None,
457+
coverage_patterns=None
457458
):
458459
ARM.__init__(
459460
self,
460461
target,
461462
notify,
462463
macros,
463464
build_dir=build_dir,
464-
build_profile=build_profile
465+
build_profile=build_profile,
466+
coverage_patterns=None
465467
)
466468
if int(target.build_tools_metadata["version"]) > 0:
467469
# check only for ARMC5 because ARM_STD means using ARMC5, and thus

tools/toolchains/gcc.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
limitations under the License.
1616
"""
1717
import re
18+
import fnmatch
1819
from os.path import join, basename, splitext, dirname, exists
1920
from os import getenv
2021
from distutils.spawn import find_executable
@@ -36,14 +37,15 @@ class GCC(mbedToolchain):
3637
GCC_VERSION_RE = re.compile(b"\d+\.\d+\.\d+")
3738

3839
def __init__(self, target, notify=None, macros=None, build_profile=None,
39-
build_dir=None):
40+
build_dir=None, coverage_patterns=None):
4041
mbedToolchain.__init__(
4142
self,
4243
target,
4344
notify,
4445
macros,
4546
build_profile=build_profile,
46-
build_dir=build_dir
47+
build_dir=build_dir,
48+
coverage_patterns=coverage_patterns
4749
)
4850

4951
tool_path = TOOLCHAIN_PATHS['GCC_ARM']
@@ -137,7 +139,7 @@ def __init__(self, target, notify=None, macros=None, build_profile=None,
137139
self.cpu.append("-mno-unaligned-access")
138140

139141
self.flags["common"] += self.cpu
140-
142+
self.coverage_supported = True
141143
main_cc = join(tool_path, "arm-none-eabi-gcc")
142144
main_cppc = join(tool_path, "arm-none-eabi-g++")
143145
self.asm = [main_cc] + self.flags['asm'] + self.flags["common"]
@@ -157,6 +159,22 @@ def __init__(self, target, notify=None, macros=None, build_profile=None,
157159
self.use_distcc = (bool(getenv("DISTCC_POTENTIAL_HOSTS", False))
158160
and not getenv("MBED_DISABLE_DISTCC", False))
159161

162+
# create copies of gcc/ld options as coverage build options, and injects extra coverage options
163+
self.coverage_cc = self.cc + ["--coverage", "-DENABLE_LIBGCOV_PORT"]
164+
self.coverage_cppc = self.cppc + ["--coverage", "-DENABLE_LIBGCOV_PORT"]
165+
self.coverage_ld = self.ld + ['--coverage', '-Wl,--wrap,GREENTEA_SETUP', '-Wl,--wrap,_Z25GREENTEA_TESTSUITE_RESULTi']
166+
167+
# for gcc coverage options remove MBED_DEBUG macro (this is required by code coverage function)
168+
for flag in ["-DMBED_DEBUG"]:
169+
if flag in self.coverage_cc:
170+
self.coverage_cc.remove(flag)
171+
if flag in self.coverage_cppc:
172+
self.coverage_cppc.remove(flag)
173+
# for lg coverage options remove exit wrapper (this is required by code coverage function)
174+
for flag in ['-Wl,--wrap,exit', '-Wl,--wrap,atexit']:
175+
if flag in self.coverage_ld:
176+
self.coverage_ld.remove(flag)
177+
160178
def version_check(self):
161179
stdout, _, retcode = run_cmd([self.cc[0], "--version"], redirect=True)
162180
msg = None
@@ -230,6 +248,13 @@ def get_compile_options(self, defines, includes, for_asm=False):
230248
opts = opts + self.get_config_option(config_header)
231249
return opts
232250

251+
def match_coverage_patterns(self, source):
252+
"""Check whether the give source file match with coverage patterns, if so return True. """
253+
for pattern in self.coverage_patterns:
254+
if fnmatch.fnmatch(source, pattern):
255+
return True
256+
return False
257+
233258
def assemble(self, source, object, includes):
234259
# Build assemble command
235260
cmd = self.asm + self.get_compile_options(
@@ -253,9 +278,13 @@ def compile(self, cc, source, object, includes):
253278
return [cmd]
254279

255280
def compile_c(self, source, object, includes):
281+
if self.coverage_patterns and self.match_coverage_patterns(source):
282+
return self.compile(self.coverage_cc, source, object, includes)
256283
return self.compile(self.cc, source, object, includes)
257284

258285
def compile_cpp(self, source, object, includes):
286+
if self.coverage_patterns and self.match_coverage_patterns(source):
287+
return self.compile(self.coverage_cppc, source, object, includes)
259288
return self.compile(self.cppc, source, object, includes)
260289

261290
def link(self, output, objects, libraries, lib_dirs, mem_map):
@@ -279,7 +308,7 @@ def link(self, output, objects, libraries, lib_dirs, mem_map):
279308
# Build linker command
280309
map_file = splitext(output)[0] + ".map"
281310
cmd = (
282-
self.ld +
311+
(self.coverage_ld if self.coverage_patterns else self.ld) +
283312
["-o", output, "-Wl,-Map=%s" % map_file] +
284313
objects +
285314
["-Wl,--start-group"] +

tools/toolchains/iar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def check_executable():
4343
)
4444

4545
def __init__(self, target, notify=None, macros=None, build_profile=None,
46-
build_dir=None):
46+
build_dir=None, coverage_patterns=None):
4747
mbedToolchain.__init__(
4848
self,
4949
target,

tools/toolchains/mbed_toolchain.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class mbedToolchain(with_metaclass(ABCMeta, object)):
132132
profile_template = {'common': [], 'c': [], 'cxx': [], 'asm': [], 'ld': []}
133133

134134
def __init__(self, target, notify=None, macros=None, build_profile=None,
135-
build_dir=None):
135+
build_dir=None, coverage_patterns=None):
136136
self.target = target
137137
self.name = self.__class__.__name__
138138

@@ -190,6 +190,9 @@ def __init__(self, target, notify=None, macros=None, build_profile=None,
190190
# Used by the mbed Online Build System to build in chrooted environment
191191
self.CHROOT = None
192192

193+
self.coverage_supported = False
194+
self.coverage_patterns = coverage_patterns
195+
193196
# post-init hook used by the online compiler TODO: remove this.
194197
self.init()
195198

0 commit comments

Comments
 (0)