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}}""" 50 51LIBRARY_IGNORE_LIST = set([ 52 'grpcpp_channelz', 53 'grpc++_reflection', 54 'benchmark_helpers', 55 'boringssl_test_util', 56]) 57 58TARGET_ALLOW_LIST = set([ 59 'grpc_cpp_plugin', 60]) 61 62STATIC_LIBRARY_TARGETS = set([ 63 'upb', 64 're2', 65 'boringssl', 66 'grpc++', 67]) 68 69 70def grpc_relpath(*segments: str) -> str: 71 '''From path segments to GRPC root, returns the absolute path.''' 72 return os.path.join(ROOT_DIR, 'buildtools', 'grpc', 'src', *segments) 73 74 75GRPC_BUILD_YAML = grpc_relpath('build_autogenerated.yaml') 76ABSL_GEN_BUILD_YAML = grpc_relpath('src', 'abseil-cpp', 'gen_build_yaml.py') 77UPB_GEN_BUILD_YAML = grpc_relpath('src', 'upb', 'gen_build_yaml.py') 78RE2_GEN_BUILD_YAML = grpc_relpath('src', 're2', 'gen_build_yaml.py') 79BSSL_GEN_BUILD_YAML = grpc_relpath('src', 'boringssl', 'gen_build_yaml.py') 80 81 82def gen_grpc_dep_yaml(gen_path: str) -> Dict[str, Any]: 83 '''Invokes a gen_build_yaml.py file for creating YAML for gRPC deps.''' 84 return yaml.safe_load(subprocess.check_output(['python3', gen_path])) 85 86 87def bazel_label_to_gn_target(dep: str) -> str: 88 '''Converts a Bazel label name into a gn target name.''' 89 if dep == 'libssl': 90 return 'boringssl' 91 return dep.replace('/', '_').replace(':', '_') 92 93 94def get_deps_for_target(target: str) -> List[str]: 95 if target == 'grpc_plugin_support': 96 return ['..:protoc_lib'] 97 if target == 'grpc': 98 return [':re2', '../../gn:zlib'] 99 if target == 'grpc_authorization_provider': 100 return [':re2'] 101 return [] 102 103 104def get_library_target_type(target: str) -> str: 105 if target in STATIC_LIBRARY_TARGETS: 106 return 'static_library' 107 return 'source_set' 108 109 110def yaml_to_gn_targets(desc: Dict[str, Any], build_types: list[str], 111 config_name: str) -> List[str]: 112 '''Given a gRPC YAML description of the build graph, generates GN targets.''' 113 out = [] 114 for lib in desc['libs']: 115 if lib['build'] not in build_types: 116 continue 117 if lib['name'] in LIBRARY_IGNORE_LIST: 118 continue 119 srcs = [f'src/{file}' for file in lib['src'] + lib['headers']] 120 if 'asm_src' in lib: 121 srcs += [f'src/{file}' for file in lib['asm_src']['crypto_linux_x86_64']] 122 deps = [f':{bazel_label_to_gn_target(dep)}' for dep in lib.get('deps', []) 123 ] + get_deps_for_target(lib['name']) 124 library_target = TARGET_TEMPLATE.format( 125 name=bazel_label_to_gn_target(lib['name']), 126 config_name=config_name, 127 srcs=json.dumps(srcs), 128 deps=json.dumps(deps), 129 target_type=get_library_target_type(lib['name'])) 130 out.append(library_target) 131 132 for bin in desc.get('targets', []): 133 if bin['build'] not in build_types: 134 continue 135 if bin['name'] not in TARGET_ALLOW_LIST: 136 continue 137 srcs = json.dumps([f'src/{file}' for file in bin['src'] + bin['headers']]) 138 deps = [f':{bazel_label_to_gn_target(dep)}' for dep in bin.get('deps', []) 139 ] + get_deps_for_target(bin['name']) 140 binary_target = TARGET_TEMPLATE.format( 141 name=bazel_label_to_gn_target(bin['name']), 142 config_name=config_name, 143 srcs=srcs, 144 deps=json.dumps(deps), 145 target_type='executable') 146 out.append(binary_target) 147 return out 148 149 150def main(): 151 out: List[str] = [] 152 153 # Generate absl rules 154 absl_yaml = gen_grpc_dep_yaml(ABSL_GEN_BUILD_YAML) 155 out.extend(yaml_to_gn_targets(absl_yaml, ['private'], 'grpc_absl_config')) 156 157 # Generate upb rules 158 upb_yaml = gen_grpc_dep_yaml(UPB_GEN_BUILD_YAML) 159 out.extend(yaml_to_gn_targets(upb_yaml, ['all'], 'grpc_upb_config')) 160 161 # Generate re2 rules 162 re2_yaml = gen_grpc_dep_yaml(RE2_GEN_BUILD_YAML) 163 out.extend(yaml_to_gn_targets(re2_yaml, ['private'], 'grpc_re2_config')) 164 165 # Generate boringssl rules 166 boringssl_yaml = gen_grpc_dep_yaml(BSSL_GEN_BUILD_YAML) 167 out.extend( 168 yaml_to_gn_targets(boringssl_yaml, ['private'], 'grpc_boringssl_config')) 169 170 # Generate grpc rules 171 with open(GRPC_BUILD_YAML, 'r', encoding='utf-8') as f: 172 grpc_yaml = yaml.safe_load(f.read()) 173 out.extend( 174 yaml_to_gn_targets(grpc_yaml, ['all', 'protoc'], 'grpc_internal_config')) 175 176 print(GRPC_GN_HEADER) 177 print('\n'.join(out)) 178 return 0 179 180 181if __name__ == '__main__': 182 sys.exit(main()) 183