blob: eb493bfa43d08ce91afd8c65834916716462ffd7 [file] [log] [blame]
Yuke Liaobb571bd62018-10-31 21:51:521#!/usr/bin/env python
2# Copyright 2018 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""Adds code coverage flags to the invocations of the Clang C/C++ compiler.
6
7This script is used to instrument a subset of the source files, and the list of
8files to instrument is specified by an input file that is passed to this script
9as a command-line argument.
10
11The path to the coverage instrumentation input file should be relative to the
12root build directory, and the file consists of multiple lines where each line
13represents a path to a source file, and the specified paths must be relative to
14the root build directory. e.g. ../../base/task/post_task.cc for build
15directory 'out/Release'.
16
17One caveat with this compiler wrapper is that it may introduce unexpected
18behaviors in incremental builds when the file path to the coverage
19instrumentation input file changes between consecutive runs, so callers of this
20script are strongly advised to always use the same path such as
21"${root_build_dir}/coverage_instrumentation_input.txt".
22
23It's worth noting on try job builders, if the contents of the instrumentation
24file changes so that a file doesn't need to be instrumented any longer, it will
25be recompiled automatically because if try job B runs after try job A, the files
26that were instrumented in A will be updated (i.e., reverted to the checked in
27version) in B, and so they'll be considered out of date by ninja and recompiled.
28
29Example usage:
30 clang_code_coverage_wrapper.py \\
31 --files-to-instrument=coverage_instrumentation_input.txt
32"""
33
34import argparse
35import os
36import subprocess
37import sys
38
39# Flags used to enable coverage instrumentation.
Yuke Liao5eff7822019-02-28 03:56:2440_COVERAGE_FLAGS = [
Sajjad Mirza49c00e32019-03-01 22:46:5241 '-fprofile-instr-generate', '-fcoverage-mapping',
42 # Following experimental flags remove unused header functions from the
43 # coverage mapping data embedded in the test binaries, and the reduction
44 # of binary size enables building Chrome's large unit test targets on
45 # MacOS. Please refer to crbug.com/796290 for more details.
46 '-mllvm', '-limited-coverage-experimental=true'
Yuke Liao5eff7822019-02-28 03:56:2447]
Yuke Liaobb571bd62018-10-31 21:51:5248
49
50def main():
51 # TODO(crbug.com/898695): Make this wrapper work on Windows platform.
52 arg_parser = argparse.ArgumentParser()
53 arg_parser.usage = __doc__
54 arg_parser.add_argument(
55 '--files-to-instrument',
56 type=str,
57 required=True,
58 help='Path to a file that contains a list of file names to instrument.')
59 arg_parser.add_argument('args', nargs=argparse.REMAINDER)
60 parsed_args = arg_parser.parse_args()
61
62 if not os.path.isfile(parsed_args.files_to_instrument):
63 raise Exception('Path to the coverage instrumentation file: "%s" doesn\'t '
64 'exist.' % parsed_args.files_to_instrument)
65
66 compile_command = parsed_args.args
67 try:
68 # The command is assumed to use Clang as the compiler, and the path to the
69 # source file is behind the -c argument, and the path to the source path is
70 # relative to the root build directory. For example:
71 # clang++ -fvisibility=hidden -c ../../base/files/file_path.cc -o \
72 # obj/base/base/file_path.o
73 index_dash_c = compile_command.index('-c')
74 except ValueError:
75 print '-c argument is not found in the compile command.'
76 raise
77
78 if index_dash_c + 1 >= len(compile_command):
79 raise Exception('Source file to be compiled is missing from the command.')
80
81 compile_source_file = compile_command[index_dash_c + 1]
82 with open(parsed_args.files_to_instrument) as f:
83 if compile_source_file + '\n' in f.read():
84 compile_command.extend(_COVERAGE_FLAGS)
85
86 return subprocess.call(compile_command)
87
88
89if __name__ == '__main__':
90 sys.exit(main())