xref: /aosp_15_r20/external/pigweed/pw_build/py/pw_build/merge_profraws.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2023 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Merge multiple profraws together using llvm-profdata."""
15
16import argparse
17import enum
18import json
19import logging
20import subprocess
21import sys
22from pathlib import Path
23from typing import Any
24
25_LOG = logging.getLogger(__name__)
26
27
28class FailureMode(enum.Enum):
29    ANY = 'any'
30    ALL = 'all'
31
32    def __str__(self):
33        return self.value
34
35
36def _parse_args() -> dict[str, Any]:
37    parser = argparse.ArgumentParser(description=__doc__)
38    parser.add_argument(
39        '--llvm-profdata-path',
40        type=Path,
41        required=True,
42        help='Path to the llvm-profdata binary to use for merging.',
43    )
44    parser.add_argument(
45        '--test-metadata-path',
46        type=Path,
47        required=True,
48        help='Path to the *.test_metadata.json file that describes all of the '
49        'tests being used to generate a coverage report.',
50    )
51    parser.add_argument(
52        '--profdata-path',
53        type=Path,
54        required=True,
55        help='Path for the output merged profdata file to use with generating a'
56        ' coverage report for the tests described in --test-metadata.',
57    )
58    parser.add_argument(
59        '--depfile-path',
60        type=Path,
61        required=True,
62        help='Path for the output depfile to convey the extra input '
63        'requirements from parsing --test-metadata.',
64    )
65    parser.add_argument(
66        '--failure-mode',
67        type=FailureMode,
68        choices=list(FailureMode),
69        required=False,
70        default=FailureMode.ANY,
71        help='Sets the llvm-profdata --failure-mode option.',
72    )
73    return vars(parser.parse_args())
74
75
76def merge_profraws(
77    llvm_profdata_path: Path,
78    test_metadata_path: Path,
79    profdata_path: Path,
80    depfile_path: Path,
81    failure_mode: FailureMode,
82) -> int:
83    """Merge multiple profraws together using llvm-profdata."""
84
85    # Open the test_metadata_path, parse it to JSON, and extract out the
86    # profraws.
87    test_metadata = json.loads(test_metadata_path.read_text())
88    profraw_paths = [
89        str(obj['path'])
90        for obj in test_metadata
91        if 'type' in obj and obj['type'] == 'profraw'
92    ]
93
94    # Generate merged profdata.
95    command = [
96        str(llvm_profdata_path),
97        'merge',
98        '--sparse',
99        '--failure-mode',
100        str(failure_mode),
101        '-o',
102        str(profdata_path),
103    ] + profraw_paths
104
105    _LOG.info('')
106    _LOG.info(' '.join(command))
107    _LOG.info('')
108
109    output = subprocess.run(command)
110    if output.returncode != 0:
111        return output.returncode
112
113    # Generate the depfile that describes the dependency on the profraws used to
114    # create profdata_path.
115    depfile_path.write_text(
116        ''.join(
117            [
118                str(profdata_path),
119                ': \\\n',
120                *[str(path) + ' \\\n' for path in profraw_paths],
121            ]
122        )
123    )
124
125    return 0
126
127
128def main() -> int:
129    return merge_profraws(**_parse_args())
130
131
132if __name__ == "__main__":
133    sys.exit(main())
134