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 file_names, 21 type_dictionary): 22 ret = {} 23 for typ in type_dictionary: 24 ret[typ] = [] 25 26 for name in file_names: 27 short_path = name.short_path 28 for typ, ext in type_dictionary.items(): 29 # prefix with label.name to prevent collisions between targets 30 # if proto compliation becomes an aspect, can prefix with output 31 # information instead to allow reuse, e.g. multiple cc `lite` 32 # libraries containing the same proto file 33 out_name = paths.join(ctx.label.name, paths.replace_extension(short_path, ext)) 34 declared = ctx.actions.declare_file(out_name) 35 ret[typ].append(declared) 36 37 return ret 38 39def _generate_jar_proto_action( 40 proto_infos, 41 protoc, 42 ctx, 43 out_flags = [], 44 plugin_executable = None, 45 out_arg = None, 46 mnemonic = "ProtoGen"): 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 ) 61 62 srcjar_name = jar_basename + ".srcjar" 63 srcjar_file = ctx.actions.declare_file(srcjar_name) 64 ctx.actions.symlink( 65 output = srcjar_file, 66 target_file = jar_file, 67 ) 68 69 return srcjar_file 70 71def _generate_proto_action( 72 proto_infos, 73 protoc, 74 ctx, 75 type_dictionary = None, 76 out_flags = [], 77 plugin_executable = None, 78 out_arg = None, 79 mnemonic = "ProtoGen", 80 output_file = None): 81 """ Utility function for creating proto_compiler action. 82 83 Args: 84 proto_infos: A list of ProtoInfo. 85 protoc: proto compiler executable. 86 ctx: context, used for declaring new files only. 87 type_dictionary: a dictionary of types to output extensions 88 out_flags: protoc output flags 89 plugin_executable: plugin executable file 90 out_arg: as appropriate, if plugin_executable and out_arg are both supplied, plugin_executable is preferred 91 mnemonic: (optional) a string to describe the proto compilation action 92 output_file: (optional) File, used to specify a specific file for protoc output (typically a JAR file) 93 94 Returns: 95 Dictionary with declared files grouped by type from the type_dictionary. 96 """ 97 98 # TODO(B/245629074): Don't build external/protobuf if it is provided in 99 # toolchain already. 100 proto_srcs = [] 101 proto_source_root_list = sets.make() 102 transitive_proto_srcs_list = [] 103 transitive_proto_path_list = sets.make() 104 105 for proto_info in proto_infos: 106 sets.insert(proto_source_root_list, proto_info.proto_source_root) 107 proto_srcs.extend(proto_info.direct_sources) 108 transitive_proto_srcs_list.append(proto_info.transitive_imports) 109 for p in proto_info.transitive_proto_path.to_list(): 110 sets.insert(transitive_proto_path_list, p) 111 112 protoc_out_name = paths.join(ctx.bin_dir.path, ctx.label.package) 113 if output_file: 114 protoc_out_name = paths.join(protoc_out_name, output_file.basename) 115 out_files = { 116 "out": [output_file], 117 } 118 else: 119 protoc_out_name = paths.join(protoc_out_name, ctx.label.name) 120 out_files = _generate_and_declare_output_files( 121 ctx, 122 proto_srcs, 123 type_dictionary, 124 ) 125 126 tools = [] 127 args = ctx.actions.args() 128 if plugin_executable: 129 tools.append(plugin_executable) 130 args.add("--plugin=protoc-gen-PLUGIN=" + plugin_executable.path) 131 args.add("--PLUGIN_out=" + ",".join(out_flags) + ":" + protoc_out_name) 132 else: 133 args.add("{}={}:{}".format(out_arg, ",".join(out_flags), protoc_out_name)) 134 135 # the order matters so we add the source roots first 136 args.add_all(["-I" + p for p in sets.to_list(proto_source_root_list)]) 137 138 args.add_all(["-I" + p for p in sets.to_list(transitive_proto_path_list)]) 139 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()]) 140 141 args.add_all([f.short_path for f in proto_srcs]) 142 143 inputs = depset( 144 direct = proto_srcs, 145 transitive = transitive_proto_srcs_list, 146 ) 147 148 outputs = [] 149 for outs in out_files.values(): 150 outputs.extend(outs) 151 152 ctx.actions.run( 153 inputs = inputs, 154 executable = protoc, 155 tools = tools, 156 outputs = outputs, 157 arguments = [args], 158 mnemonic = mnemonic, 159 ) 160 return out_files 161 162proto_file_utils = struct( 163 generate_proto_action = _generate_proto_action, 164 generate_jar_proto_action = _generate_jar_proto_action, 165) 166