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