xref: /aosp_15_r20/external/perfetto/tools/gen_grpc_build_gn.py (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1#!/usr/bin/env python3
2# Copyright (C) 2023 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import json
17import os
18import subprocess
19import sys
20from typing import Any, Dict, List
21import yaml
22
23ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
24
25GRPC_GN_HEADER = '''
26#
27# DO NOT EDIT. AUTOGENERATED file
28#
29# This file is generated with the command:
30# tools/gen_grpc_build_gn.py > buildtools/grpc/BUILD.gn
31#
32
33import("../../gn/perfetto.gni")
34
35# Prevent the gRPC from being depended upon without explicitly being opted in.
36assert(enable_perfetto_grpc)
37
38# BoringSSL has assembly code which is tied to platform-specific. For now, we
39# only care about Linux x64 so assert this as the case.
40assert(is_linux && current_cpu == "x64")
41'''
42
43TARGET_TEMPLATE = """
44{target_type}("{name}") {{
45  sources = {srcs}
46  public_deps = {deps}
47  public_configs = ["..:{config_name}"]
48  configs -= [ "//gn/standalone:extra_warnings" ]
49  check_includes = {check_includes}
50}}"""
51
52LIBRARY_IGNORE_LIST = set([
53    'grpcpp_channelz',
54    'grpc++_reflection',
55    'benchmark_helpers',
56    'boringssl_test_util',
57    'grpcpp_otel_plugin',
58    'otel_plugin_test',
59])
60
61TARGET_ALLOW_LIST = set([
62    'grpc_cpp_plugin',
63])
64
65STATIC_LIBRARY_TARGETS = set([
66    'upb',
67    're2',
68    'boringssl',
69    'grpc++',
70    'upb_json_lib',
71    'upb_textformat_lib',
72])
73
74DEP_DENYLIST = set([
75    'cares',
76])
77
78
79def grpc_relpath(*segments: str) -> str:
80  '''From path segments to GRPC root, returns the absolute path.'''
81  return os.path.join(ROOT_DIR, 'buildtools', 'grpc', 'src', *segments)
82
83
84GRPC_BUILD_YAML = grpc_relpath('build_autogenerated.yaml')
85ABSL_GEN_BUILD_YAML = grpc_relpath('src', 'abseil-cpp', 'gen_build_yaml.py')
86BSSL_GEN_BUILD_YAML = grpc_relpath('src', 'boringssl', 'gen_build_yaml.py')
87
88
89def gen_grpc_dep_yaml(gen_path: str) -> Dict[str, Any]:
90  '''Invokes a gen_build_yaml.py file for creating YAML for gRPC deps.'''
91  return yaml.safe_load(subprocess.check_output(['python3', gen_path]))
92
93
94def bazel_label_to_gn_target(dep: str) -> str:
95  '''Converts a Bazel label name into a gn target name.'''
96  if dep == 'libssl':
97    return 'boringssl'
98  return dep.replace('/', '_').replace(':', '_')
99
100
101def bazel_label_to_gn_dep(dep: str) -> str:
102  if dep == 'protobuf':
103    return '..:protobuf_full'
104  if dep == 'protoc':
105    return '..:protoc_lib'
106  if dep == 'z':
107    return '..:zlib'
108  return ':' + bazel_label_to_gn_target(dep)
109
110
111def get_library_target_type(target: str) -> str:
112  if target in STATIC_LIBRARY_TARGETS:
113    return 'static_library'
114  return 'source_set'
115
116
117def yaml_to_gn_targets(desc: Dict[str, Any], build_types: list[str],
118                       config_name: str) -> List[str]:
119  '''Given a gRPC YAML description of the build graph, generates GN targets.'''
120  out = []
121  for lib in desc['libs']:
122    if lib['build'] not in build_types:
123      continue
124    if lib['name'] in LIBRARY_IGNORE_LIST:
125      continue
126    srcs = [f'src/{file}' for file in lib['src'] + lib['headers']]
127    if 'asm_src' in lib:
128      srcs += [f'src/{file}' for file in lib['asm_src']['crypto_asm']]
129    deps = [
130        bazel_label_to_gn_dep(dep)
131        for dep in lib.get('deps', [])
132        if dep not in DEP_DENYLIST
133    ]
134    library_target = TARGET_TEMPLATE.format(
135        name=bazel_label_to_gn_target(lib['name']),
136        config_name=config_name,
137        srcs=json.dumps(srcs),
138        deps=json.dumps(deps),
139        target_type=get_library_target_type(lib['name']),
140        check_includes='false' if lib['name'] == 'upb_json_lib' or
141        lib['name'] == 'upb_textformat_lib' else 'true')
142    out.append(library_target)
143
144  for bin in desc.get('targets', []):
145    if bin['build'] not in build_types:
146      continue
147    if bin['name'] not in TARGET_ALLOW_LIST:
148      continue
149    srcs = json.dumps([f'src/{file}' for file in bin['src'] + bin['headers']])
150    deps = [
151        bazel_label_to_gn_dep(dep)
152        for dep in bin.get('deps', [])
153        if dep not in DEP_DENYLIST
154    ]
155    binary_target = TARGET_TEMPLATE.format(
156        name=bazel_label_to_gn_target(bin['name']),
157        config_name=config_name,
158        srcs=srcs,
159        deps=json.dumps(deps),
160        target_type='executable',
161        check_includes='true')
162    out.append(binary_target)
163  return out
164
165
166def main():
167  out: List[str] = []
168
169  # Generate absl rules
170  absl_yaml = gen_grpc_dep_yaml(ABSL_GEN_BUILD_YAML)
171  out.extend(yaml_to_gn_targets(absl_yaml, ['private'], 'grpc_absl_config'))
172
173  # Generate boringssl rules
174  boringssl_yaml = gen_grpc_dep_yaml(BSSL_GEN_BUILD_YAML)
175  out.extend(
176      yaml_to_gn_targets(boringssl_yaml, ['private'], 'grpc_boringssl_config'))
177
178  # Generate grpc rules
179  with open(GRPC_BUILD_YAML, 'r', encoding='utf-8') as f:
180    grpc_yaml = yaml.safe_load(f.read())
181  out.extend(
182      yaml_to_gn_targets(grpc_yaml, ['all', 'protoc'], 'grpc_internal_config'))
183
184  print(GRPC_GN_HEADER)
185  print('\n'.join(out))
186  return 0
187
188
189if __name__ == '__main__':
190  sys.exit(main())
191