xref: /aosp_15_r20/external/pigweed/pw_build/coverage_report.gni (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker# Copyright 2023 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker#
3*61c4878aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker# use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker# the License at
6*61c4878aSAndroid Build Coastguard Worker#
7*61c4878aSAndroid Build Coastguard Worker#     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker#
9*61c4878aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker# License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker# the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Workerimport("//build_overrides/pigweed.gni")
16*61c4878aSAndroid Build Coastguard Workerimport("//build_overrides/pigweed_environment.gni")
17*61c4878aSAndroid Build Coastguard Worker
18*61c4878aSAndroid Build Coastguard Workerimport("$dir_pw_build/python_action.gni")
19*61c4878aSAndroid Build Coastguard Workerimport("$dir_pw_toolchain/host_clang/toolchains.gni")
20*61c4878aSAndroid Build Coastguard Worker
21*61c4878aSAndroid Build Coastguard Worker# Expands to code coverage targets that can be used as dependencies to generate
22*61c4878aSAndroid Build Coastguard Worker# coverage reports at build time.
23*61c4878aSAndroid Build Coastguard Worker#
24*61c4878aSAndroid Build Coastguard Worker# Arguments:
25*61c4878aSAndroid Build Coastguard Worker# - enable_if (optional): Conditionally activates coverage report generation
26*61c4878aSAndroid Build Coastguard Worker#   when set to a boolean expression that evaluates to true.
27*61c4878aSAndroid Build Coastguard Worker# - failure_mode (optional/unstable): Specify the failure mode for llvm-profdata
28*61c4878aSAndroid Build Coastguard Worker#   (used to merge inidividual profraw files from pw_test runs). Available
29*61c4878aSAndroid Build Coastguard Worker#   options are "any" (default) or "all". This should be considered an
30*61c4878aSAndroid Build Coastguard Worker#   unstable/deprecated argument that should only be used as a last resort to
31*61c4878aSAndroid Build Coastguard Worker#   get a build working again. Using failure_mode = "all" usually indicates that
32*61c4878aSAndroid Build Coastguard Worker#   there are underlying problems in the build or test infrastructure that
33*61c4878aSAndroid Build Coastguard Worker#   should be independently resolved. Please reach out to the Pigweed team for
34*61c4878aSAndroid Build Coastguard Worker#   assistance.
35*61c4878aSAndroid Build Coastguard Worker# - Coverage Settings
36*61c4878aSAndroid Build Coastguard Worker#   - filter_paths (optional): List of file paths (using GN path helpers like
37*61c4878aSAndroid Build Coastguard Worker#     `//` is supported). These will be translated into absolute paths before
38*61c4878aSAndroid Build Coastguard Worker#     being used. These filter source files so that the coverage report *ONLY*
39*61c4878aSAndroid Build Coastguard Worker#     includes files that match one of these paths. These cannot be regular
40*61c4878aSAndroid Build Coastguard Worker#     expressions, but can be concrete file or folder paths. Folder paths will
41*61c4878aSAndroid Build Coastguard Worker#     allow all files in that directory or any recursive child directory.
42*61c4878aSAndroid Build Coastguard Worker#   - ignore_filename_patterns (optional): List of file path regular expressions
43*61c4878aSAndroid Build Coastguard Worker#     to ignore when generating the coverage report.
44*61c4878aSAndroid Build Coastguard Worker# - pw_test Depedencies (required): These control which test binaries are used
45*61c4878aSAndroid Build Coastguard Worker#   to collect usage data for the coverage report. The following can basically
46*61c4878aSAndroid Build Coastguard Worker#   be used interchangeably with no actual difference in the template expansion.
47*61c4878aSAndroid Build Coastguard Worker#   Only one of these is required to be provided.
48*61c4878aSAndroid Build Coastguard Worker#   - tests: A list of pw_test targets.
49*61c4878aSAndroid Build Coastguard Worker#   - group_deps: A list of pw_test_group targets.
50*61c4878aSAndroid Build Coastguard Worker#
51*61c4878aSAndroid Build Coastguard Worker# Expands To:
52*61c4878aSAndroid Build Coastguard Worker# pw_coverage_report follows the overall Pigweed pattern where targets exist
53*61c4878aSAndroid Build Coastguard Worker# for all build configurations, but are only configured to do meaningful work
54*61c4878aSAndroid Build Coastguard Worker# under the correct build configuration. In this vein, pw_coverage_report
55*61c4878aSAndroid Build Coastguard Worker# ensures that a coverage-enabled toolchain is being used and the provided
56*61c4878aSAndroid Build Coastguard Worker# enable_if evaluates to true (if provided).
57*61c4878aSAndroid Build Coastguard Worker#
58*61c4878aSAndroid Build Coastguard Worker# - If a coverage-enabled toolchain is being used and the provided enable_if
59*61c4878aSAndroid Build Coastguard Worker#   evaluates to true (if provided):
60*61c4878aSAndroid Build Coastguard Worker#   - <target_name>.text: Generates a text representation of the coverage
61*61c4878aSAndroid Build Coastguard Worker#                         report. This is the output of
62*61c4878aSAndroid Build Coastguard Worker#                         `llvm-cov show --format text`.
63*61c4878aSAndroid Build Coastguard Worker#   - <target_name>.html: Generates an HTML representation of the coverage
64*61c4878aSAndroid Build Coastguard Worker#                         report. This is the output of
65*61c4878aSAndroid Build Coastguard Worker#                         `llvm-cov show --format html`.
66*61c4878aSAndroid Build Coastguard Worker#   - <target_name>.lcov: Generates an LCOV representation of the coverage
67*61c4878aSAndroid Build Coastguard Worker#                         report. This is the output of
68*61c4878aSAndroid Build Coastguard Worker#                         `llvm-cov export --format lcov`.
69*61c4878aSAndroid Build Coastguard Worker#   - <target_name>.json: Generates a JSON representation of the coverage
70*61c4878aSAndroid Build Coastguard Worker#                         report. This is the output of
71*61c4878aSAndroid Build Coastguard Worker#                         `llvm-cov export --format text`.
72*61c4878aSAndroid Build Coastguard Worker#
73*61c4878aSAndroid Build Coastguard Worker#   - <target_name>: A group that takes dependencies on <target_name>.text,
74*61c4878aSAndroid Build Coastguard Worker#                    <target_name>.html, <target_name>.lcov, and
75*61c4878aSAndroid Build Coastguard Worker#                    <target_name>.json. This can be used to force generation of
76*61c4878aSAndroid Build Coastguard Worker#                    all coverage artifacts without manually depending on each
77*61c4878aSAndroid Build Coastguard Worker#                    target.
78*61c4878aSAndroid Build Coastguard Worker#
79*61c4878aSAndroid Build Coastguard Worker#   - The other targets this expands to should be considered private and not
80*61c4878aSAndroid Build Coastguard Worker#     used as dependencies.
81*61c4878aSAndroid Build Coastguard Worker# - If a coverage-enabled toolchain is not being used or the provided enable_if
82*61c4878aSAndroid Build Coastguard Worker#   evaluates to false (if provided).
83*61c4878aSAndroid Build Coastguard Worker#   - All of the above target names, but they are empty groups.
84*61c4878aSAndroid Build Coastguard Workertemplate("pw_coverage_report") {
85*61c4878aSAndroid Build Coastguard Worker  assert(defined(invoker.tests) || defined(invoker.group_deps),
86*61c4878aSAndroid Build Coastguard Worker         "One of `tests` or `group_deps` must be provided.")
87*61c4878aSAndroid Build Coastguard Worker  assert(!defined(invoker.failure_mode) ||
88*61c4878aSAndroid Build Coastguard Worker             (invoker.failure_mode == "any" || invoker.failure_mode == "all"),
89*61c4878aSAndroid Build Coastguard Worker         "failure_mode only supports \"any\" or \"all\".")
90*61c4878aSAndroid Build Coastguard Worker
91*61c4878aSAndroid Build Coastguard Worker  _report_name = target_name
92*61c4878aSAndroid Build Coastguard Worker  _format_types = [
93*61c4878aSAndroid Build Coastguard Worker    "text",
94*61c4878aSAndroid Build Coastguard Worker    "html",
95*61c4878aSAndroid Build Coastguard Worker    "lcov",
96*61c4878aSAndroid Build Coastguard Worker    "json",
97*61c4878aSAndroid Build Coastguard Worker  ]
98*61c4878aSAndroid Build Coastguard Worker  _should_enable = !defined(invoker.enable_if) || invoker.enable_if
99*61c4878aSAndroid Build Coastguard Worker
100*61c4878aSAndroid Build Coastguard Worker  # These two Pigweed build arguments are required to be in these states to
101*61c4878aSAndroid Build Coastguard Worker  # ensure binaries are instrumented for coverage and profraw files are
102*61c4878aSAndroid Build Coastguard Worker  # exported.
103*61c4878aSAndroid Build Coastguard Worker  if (_should_enable && pw_toolchain_COVERAGE_ENABLED) {
104*61c4878aSAndroid Build Coastguard Worker    _test_metadata = "$target_out_dir/$_report_name.test_metadata.json"
105*61c4878aSAndroid Build Coastguard Worker    _profdata_file = "$target_out_dir/merged.profdata"
106*61c4878aSAndroid Build Coastguard Worker    _arguments = {
107*61c4878aSAndroid Build Coastguard Worker      filter_paths = []
108*61c4878aSAndroid Build Coastguard Worker      if (defined(invoker.filter_paths)) {
109*61c4878aSAndroid Build Coastguard Worker        filter_paths += invoker.filter_paths
110*61c4878aSAndroid Build Coastguard Worker      }
111*61c4878aSAndroid Build Coastguard Worker
112*61c4878aSAndroid Build Coastguard Worker      ignore_filename_patterns = []
113*61c4878aSAndroid Build Coastguard Worker      if (defined(invoker.ignore_filename_patterns)) {
114*61c4878aSAndroid Build Coastguard Worker        ignore_filename_patterns += invoker.ignore_filename_patterns
115*61c4878aSAndroid Build Coastguard Worker      }
116*61c4878aSAndroid Build Coastguard Worker
117*61c4878aSAndroid Build Coastguard Worker      # Merge any provided `tests` or `group_deps` to `deps` and `run_deps`.
118*61c4878aSAndroid Build Coastguard Worker      #
119*61c4878aSAndroid Build Coastguard Worker      # `deps` are used to generate the .test_metadata.json file.
120*61c4878aSAndroid Build Coastguard Worker      # `run_deps` are used to block on the test execution to generate a profraw
121*61c4878aSAndroid Build Coastguard Worker      # file.
122*61c4878aSAndroid Build Coastguard Worker      deps = []
123*61c4878aSAndroid Build Coastguard Worker      run_deps = []
124*61c4878aSAndroid Build Coastguard Worker      test_or_group_deps = []
125*61c4878aSAndroid Build Coastguard Worker      if (defined(invoker.tests)) {
126*61c4878aSAndroid Build Coastguard Worker        test_or_group_deps += invoker.tests
127*61c4878aSAndroid Build Coastguard Worker      }
128*61c4878aSAndroid Build Coastguard Worker      if (defined(invoker.group_deps)) {
129*61c4878aSAndroid Build Coastguard Worker        test_or_group_deps += invoker.group_deps
130*61c4878aSAndroid Build Coastguard Worker      }
131*61c4878aSAndroid Build Coastguard Worker      foreach(dep, test_or_group_deps) {
132*61c4878aSAndroid Build Coastguard Worker        deps += [ dep ]
133*61c4878aSAndroid Build Coastguard Worker
134*61c4878aSAndroid Build Coastguard Worker        dep_target = get_label_info(dep, "label_no_toolchain")
135*61c4878aSAndroid Build Coastguard Worker        dep_toolchain = get_label_info(dep, "toolchain")
136*61c4878aSAndroid Build Coastguard Worker        run_deps += [ "$dep_target.run($dep_toolchain)" ]
137*61c4878aSAndroid Build Coastguard Worker      }
138*61c4878aSAndroid Build Coastguard Worker    }
139*61c4878aSAndroid Build Coastguard Worker
140*61c4878aSAndroid Build Coastguard Worker    # Generate a list of all test binaries and their associated profraw files
141*61c4878aSAndroid Build Coastguard Worker    # after executing we can use to generate the coverage report.
142*61c4878aSAndroid Build Coastguard Worker    generated_file("_$_report_name.test_metadata") {
143*61c4878aSAndroid Build Coastguard Worker      outputs = [ _test_metadata ]
144*61c4878aSAndroid Build Coastguard Worker      data_keys = [
145*61c4878aSAndroid Build Coastguard Worker        "unit_tests",
146*61c4878aSAndroid Build Coastguard Worker        "profraws",
147*61c4878aSAndroid Build Coastguard Worker      ]
148*61c4878aSAndroid Build Coastguard Worker      output_conversion = "json"
149*61c4878aSAndroid Build Coastguard Worker      deps = _arguments.deps
150*61c4878aSAndroid Build Coastguard Worker    }
151*61c4878aSAndroid Build Coastguard Worker
152*61c4878aSAndroid Build Coastguard Worker    # Merge the generated profraws from instrumented binaries into a single
153*61c4878aSAndroid Build Coastguard Worker    # profdata.
154*61c4878aSAndroid Build Coastguard Worker    pw_python_action("_$_report_name.merge_profraws") {
155*61c4878aSAndroid Build Coastguard Worker      _depfile_path = "$target_out_dir/$_report_name.merged_profraws.d"
156*61c4878aSAndroid Build Coastguard Worker
157*61c4878aSAndroid Build Coastguard Worker      module = "pw_build.merge_profraws"
158*61c4878aSAndroid Build Coastguard Worker      args = [
159*61c4878aSAndroid Build Coastguard Worker        "--llvm-profdata-path",
160*61c4878aSAndroid Build Coastguard Worker        pw_toolchain_clang_tools.llvm_profdata,
161*61c4878aSAndroid Build Coastguard Worker        "--test-metadata-path",
162*61c4878aSAndroid Build Coastguard Worker        rebase_path(_test_metadata, root_build_dir),
163*61c4878aSAndroid Build Coastguard Worker        "--profdata-path",
164*61c4878aSAndroid Build Coastguard Worker        rebase_path(_profdata_file, root_build_dir),
165*61c4878aSAndroid Build Coastguard Worker        "--depfile-path",
166*61c4878aSAndroid Build Coastguard Worker        rebase_path(_depfile_path, root_build_dir),
167*61c4878aSAndroid Build Coastguard Worker      ]
168*61c4878aSAndroid Build Coastguard Worker
169*61c4878aSAndroid Build Coastguard Worker      # TODO: b/256651964 - We really want `--failure-mode any` always to guarantee
170*61c4878aSAndroid Build Coastguard Worker      # we don't silently ignore any profraw report. However, there are downstream
171*61c4878aSAndroid Build Coastguard Worker      # projects that currently break when using `--failure-mode any`.
172*61c4878aSAndroid Build Coastguard Worker      #
173*61c4878aSAndroid Build Coastguard Worker      # See the task for examples of what is currently going wrong.
174*61c4878aSAndroid Build Coastguard Worker      #
175*61c4878aSAndroid Build Coastguard Worker      # Invalid profraw files will be ignored so coverage reports might have a
176*61c4878aSAndroid Build Coastguard Worker      # slight variance between runs depending on if something failed or not.
177*61c4878aSAndroid Build Coastguard Worker      if (defined(invoker.failure_mode)) {
178*61c4878aSAndroid Build Coastguard Worker        args += [
179*61c4878aSAndroid Build Coastguard Worker          "--failure-mode",
180*61c4878aSAndroid Build Coastguard Worker          invoker.failure_mode,
181*61c4878aSAndroid Build Coastguard Worker        ]
182*61c4878aSAndroid Build Coastguard Worker      }
183*61c4878aSAndroid Build Coastguard Worker
184*61c4878aSAndroid Build Coastguard Worker      inputs = [ _test_metadata ]
185*61c4878aSAndroid Build Coastguard Worker      sources = []
186*61c4878aSAndroid Build Coastguard Worker      depfile = _depfile_path
187*61c4878aSAndroid Build Coastguard Worker
188*61c4878aSAndroid Build Coastguard Worker      outputs = [ _profdata_file ]
189*61c4878aSAndroid Build Coastguard Worker
190*61c4878aSAndroid Build Coastguard Worker      python_deps = [ "$dir_pw_build/py" ]
191*61c4878aSAndroid Build Coastguard Worker      deps = _arguments.run_deps
192*61c4878aSAndroid Build Coastguard Worker      public_deps = [ ":_$_report_name.test_metadata" ]
193*61c4878aSAndroid Build Coastguard Worker    }
194*61c4878aSAndroid Build Coastguard Worker
195*61c4878aSAndroid Build Coastguard Worker    foreach(format, _format_types) {
196*61c4878aSAndroid Build Coastguard Worker      pw_python_action("$_report_name.$format") {
197*61c4878aSAndroid Build Coastguard Worker        _depfile_path = "$target_out_dir/$_report_name.$format.d"
198*61c4878aSAndroid Build Coastguard Worker        _output_dir = "$target_out_dir/$_report_name/$format/"
199*61c4878aSAndroid Build Coastguard Worker
200*61c4878aSAndroid Build Coastguard Worker        module = "pw_build.generate_report"
201*61c4878aSAndroid Build Coastguard Worker        args = [
202*61c4878aSAndroid Build Coastguard Worker          "--llvm-cov-path",
203*61c4878aSAndroid Build Coastguard Worker          pw_toolchain_clang_tools.llvm_cov,
204*61c4878aSAndroid Build Coastguard Worker          "--format",
205*61c4878aSAndroid Build Coastguard Worker          format,
206*61c4878aSAndroid Build Coastguard Worker          "--test-metadata-path",
207*61c4878aSAndroid Build Coastguard Worker          rebase_path(_test_metadata, root_build_dir),
208*61c4878aSAndroid Build Coastguard Worker          "--profdata-path",
209*61c4878aSAndroid Build Coastguard Worker          rebase_path(_profdata_file, root_build_dir),
210*61c4878aSAndroid Build Coastguard Worker          "--root-dir",
211*61c4878aSAndroid Build Coastguard Worker          rebase_path("//", root_build_dir),
212*61c4878aSAndroid Build Coastguard Worker          "--build-dir",
213*61c4878aSAndroid Build Coastguard Worker          ".",
214*61c4878aSAndroid Build Coastguard Worker          "--output-dir",
215*61c4878aSAndroid Build Coastguard Worker          rebase_path(_output_dir, root_build_dir),
216*61c4878aSAndroid Build Coastguard Worker          "--depfile-path",
217*61c4878aSAndroid Build Coastguard Worker          rebase_path(_depfile_path, root_build_dir),
218*61c4878aSAndroid Build Coastguard Worker        ]
219*61c4878aSAndroid Build Coastguard Worker        foreach(filter_path, _arguments.filter_paths) {
220*61c4878aSAndroid Build Coastguard Worker          args += [
221*61c4878aSAndroid Build Coastguard Worker            # We rebase to absolute paths here to resolve any "//" used in the
222*61c4878aSAndroid Build Coastguard Worker            # filter_paths.
223*61c4878aSAndroid Build Coastguard Worker            "--filter-path",
224*61c4878aSAndroid Build Coastguard Worker            rebase_path(filter_path),
225*61c4878aSAndroid Build Coastguard Worker          ]
226*61c4878aSAndroid Build Coastguard Worker        }
227*61c4878aSAndroid Build Coastguard Worker        foreach(ignore_filename_pattern, _arguments.ignore_filename_patterns) {
228*61c4878aSAndroid Build Coastguard Worker          args += [
229*61c4878aSAndroid Build Coastguard Worker            "--ignore-filename-pattern",
230*61c4878aSAndroid Build Coastguard Worker            ignore_filename_pattern,
231*61c4878aSAndroid Build Coastguard Worker          ]
232*61c4878aSAndroid Build Coastguard Worker        }
233*61c4878aSAndroid Build Coastguard Worker
234*61c4878aSAndroid Build Coastguard Worker        inputs = [
235*61c4878aSAndroid Build Coastguard Worker          _test_metadata,
236*61c4878aSAndroid Build Coastguard Worker          _profdata_file,
237*61c4878aSAndroid Build Coastguard Worker        ]
238*61c4878aSAndroid Build Coastguard Worker        sources = []
239*61c4878aSAndroid Build Coastguard Worker        depfile = _depfile_path
240*61c4878aSAndroid Build Coastguard Worker
241*61c4878aSAndroid Build Coastguard Worker        outputs = []
242*61c4878aSAndroid Build Coastguard Worker        if (format == "text") {
243*61c4878aSAndroid Build Coastguard Worker          outputs += [ "$_output_dir/index.txt" ]
244*61c4878aSAndroid Build Coastguard Worker        } else if (format == "html") {
245*61c4878aSAndroid Build Coastguard Worker          outputs += [ "$_output_dir/index.html" ]
246*61c4878aSAndroid Build Coastguard Worker        } else if (format == "lcov") {
247*61c4878aSAndroid Build Coastguard Worker          outputs += [ "$_output_dir/report.lcov" ]
248*61c4878aSAndroid Build Coastguard Worker        } else if (format == "json") {
249*61c4878aSAndroid Build Coastguard Worker          outputs += [ "$_output_dir/report.json" ]
250*61c4878aSAndroid Build Coastguard Worker        }
251*61c4878aSAndroid Build Coastguard Worker
252*61c4878aSAndroid Build Coastguard Worker        python_deps = [ "$dir_pw_build/py" ]
253*61c4878aSAndroid Build Coastguard Worker        deps = [ ":_$_report_name.merge_profraws" ]
254*61c4878aSAndroid Build Coastguard Worker      }
255*61c4878aSAndroid Build Coastguard Worker    }
256*61c4878aSAndroid Build Coastguard Worker  } else {
257*61c4878aSAndroid Build Coastguard Worker    not_needed(invoker, "*")
258*61c4878aSAndroid Build Coastguard Worker    foreach(format, _format_types) {
259*61c4878aSAndroid Build Coastguard Worker      group("$_report_name.$format") {
260*61c4878aSAndroid Build Coastguard Worker      }
261*61c4878aSAndroid Build Coastguard Worker    }
262*61c4878aSAndroid Build Coastguard Worker  }
263*61c4878aSAndroid Build Coastguard Worker
264*61c4878aSAndroid Build Coastguard Worker  group("$_report_name") {
265*61c4878aSAndroid Build Coastguard Worker    deps = []
266*61c4878aSAndroid Build Coastguard Worker    foreach(format, _format_types) {
267*61c4878aSAndroid Build Coastguard Worker      deps += [ ":$_report_name.$format" ]
268*61c4878aSAndroid Build Coastguard Worker    }
269*61c4878aSAndroid Build Coastguard Worker  }
270*61c4878aSAndroid Build Coastguard Worker}
271