1# Copyright (C) 2021 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://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, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15load("@bazel_skylib//lib:paths.bzl", "paths") 16load("@bazel_skylib//lib:sets.bzl", "sets") 17 18def _generate_and_declare_output_files( 19 ctx, 20 proto_rel_paths, 21 type_dictionary): 22 ret = {} 23 for typ in type_dictionary: 24 ret[typ] = [] 25 26 for proto_rel_path in proto_rel_paths: 27 for typ, ext in type_dictionary.items(): 28 # prefix with label.name to prevent collisions between targets 29 # if proto compliation becomes an aspect, can prefix with output 30 # information instead to allow reuse, e.g. multiple cc `lite` 31 # libraries containing the same proto file 32 out_name = paths.join(ctx.label.name, paths.replace_extension(proto_rel_path, ext)) 33 declared = ctx.actions.declare_file(out_name) 34 ret[typ].append(declared) 35 36 return ret 37 38def _generate_jar_proto_action( 39 proto_infos, 40 protoc, 41 ctx, 42 out_flags = [], 43 plugin_executable = None, 44 out_arg = None, 45 mnemonic = "ProtoGen", 46 transitive_proto_infos = []): 47 jar_basename = ctx.label.name + "-proto_gen" 48 jar_name = jar_basename + "-src.jar" 49 jar_file = ctx.actions.declare_file(jar_name) 50 51 _generate_proto_action( 52 proto_infos = proto_infos, 53 protoc = protoc, 54 ctx = ctx, 55 out_flags = out_flags, 56 plugin_executable = plugin_executable, 57 out_arg = out_arg, 58 mnemonic = mnemonic, 59 output_file = jar_file, 60 transitive_proto_infos = transitive_proto_infos, 61 ) 62 63 srcjar_name = jar_basename + ".srcjar" 64 srcjar_file = ctx.actions.declare_file(srcjar_name) 65 ctx.actions.symlink( 66 output = srcjar_file, 67 target_file = jar_file, 68 ) 69 70 return srcjar_file 71 72def _generate_proto_action( 73 proto_infos, 74 protoc, 75 ctx, 76 type_dictionary = None, 77 out_flags = [], 78 plugin_executable = None, 79 out_arg = None, 80 mnemonic = "ProtoGen", 81 output_file = None, 82 transitive_proto_infos = []): 83 """ Utility function for creating proto_compiler action. 84 85 Args: 86 proto_infos: A list of ProtoInfo. 87 protoc: proto compiler executable. 88 ctx: context, used for declaring new files only. 89 type_dictionary: a dictionary of types to output extensions 90 out_flags: protoc output flags 91 plugin_executable: plugin executable file 92 out_arg: as appropriate, if plugin_executable and out_arg are both supplied, plugin_executable is preferred 93 mnemonic: (optional) a string to describe the proto compilation action 94 output_file: (optional) File, used to specify a specific file for protoc output (typically a JAR file) 95 96 Returns: 97 Dictionary with declared files grouped by type from the type_dictionary. 98 """ 99 100 # TODO(B/245629074): Don't build external/protobuf if it is provided in 101 # toolchain already. 102 proto_srcs = [] 103 proto_rel_srcs = [] 104 proto_source_root_list = sets.make() 105 transitive_proto_srcs_list = [] 106 transitive_proto_path_list = sets.make() 107 108 for proto_info in proto_infos: 109 sets.insert(proto_source_root_list, proto_info.proto_source_root) 110 proto_srcs.extend(proto_info.direct_sources) 111 proto_rel_srcs.extend([paths.relativize(p.path, proto_info.proto_source_root) for p in proto_info.direct_sources]) 112 transitive_proto_srcs_list.append(proto_info.transitive_imports) 113 for p in proto_info.transitive_proto_path.to_list(): 114 sets.insert(transitive_proto_path_list, p) 115 116 for transitive_proto_info in transitive_proto_infos: 117 sets.insert(transitive_proto_path_list, transitive_proto_info.proto_source_root) 118 transitive_proto_srcs_list.append(depset(transitive_proto_info.direct_sources)) 119 120 protoc_out_name = paths.join(ctx.bin_dir.path, ctx.label.package) 121 122 if output_file: 123 protoc_out_name = paths.join(protoc_out_name, output_file.basename) 124 out_files = { 125 "out": [output_file], 126 } 127 else: 128 protoc_out_name = paths.join(protoc_out_name, ctx.label.name) 129 out_files = _generate_and_declare_output_files( 130 ctx, 131 proto_rel_srcs, 132 type_dictionary, 133 ) 134 135 tools = [] 136 args = ctx.actions.args() 137 if plugin_executable: 138 tools.append(plugin_executable) 139 args.add("--plugin=protoc-gen-PLUGIN=" + plugin_executable.path) 140 args.add("--PLUGIN_out=" + ",".join(out_flags) + ":" + protoc_out_name) 141 else: 142 args.add("{}={}:{}".format(out_arg, ",".join(out_flags), protoc_out_name)) 143 144 # the order matters so we add the source roots first 145 args.add_all(["-I" + p for p in sets.to_list(proto_source_root_list)]) 146 args.add_all(["-I" + p for p in sets.to_list(transitive_proto_path_list)]) 147 args.add_all(["-I{0}={1}".format(f.short_path, f.path) for t in transitive_proto_srcs_list for f in t.to_list()]) 148 149 args.add_all([f.path for f in proto_srcs]) 150 151 inputs = depset( 152 direct = proto_srcs, 153 transitive = transitive_proto_srcs_list, 154 ) 155 156 outputs = [] 157 for outs in out_files.values(): 158 outputs.extend(outs) 159 160 ctx.actions.run( 161 inputs = inputs, 162 executable = protoc, 163 tools = tools, 164 outputs = outputs, 165 arguments = [args], 166 mnemonic = mnemonic, 167 ) 168 return out_files 169 170proto_file_utils = struct( 171 generate_proto_action = _generate_proto_action, 172 generate_jar_proto_action = _generate_jar_proto_action, 173) 174