1load("@rules_proto//proto:defs.bzl", "ProtoInfo") 2load( 3 "//bazel:protobuf.bzl", 4 "get_include_directory", 5 "get_plugin_args", 6 "proto_path_to_generated_filename", 7) 8load(":grpc_util.bzl", "to_upper_camel_with_extension") 9 10_GRPC_PROTO_HEADER_FMT = "{}.pbrpc.h" 11_GRPC_PROTO_SRC_FMT = "{}.pbrpc.m" 12_PROTO_HEADER_FMT = "{}.pbobjc.h" 13_PROTO_SRC_FMT = "{}.pbobjc.m" 14_GENERATED_PROTOS_DIR = "_generated_protos" 15 16_GENERATE_HDRS = 1 17_GENERATE_SRCS = 2 18_GENERATE_NON_ARC_SRCS = 3 19 20def _generate_objc_impl(ctx): 21 """Implementation of the generate_objc rule.""" 22 protos = [ 23 f 24 for src in ctx.attr.deps 25 for f in src[ProtoInfo].transitive_imports.to_list() 26 ] 27 28 target_package = _join_directories([ctx.label.workspace_root, ctx.label.package]) 29 30 files_with_rpc = [_label_to_full_file_path(f, target_package) for f in ctx.attr.srcs] 31 32 outs = [] 33 for proto in protos: 34 outs += [_get_output_file_name_from_proto(proto, _PROTO_HEADER_FMT)] 35 outs += [_get_output_file_name_from_proto(proto, _PROTO_SRC_FMT)] 36 37 file_path = _get_full_path_from_file(proto) 38 if file_path in files_with_rpc: 39 outs += [_get_output_file_name_from_proto(proto, _GRPC_PROTO_HEADER_FMT)] 40 outs += [_get_output_file_name_from_proto(proto, _GRPC_PROTO_SRC_FMT)] 41 42 out_files = [ctx.actions.declare_file(out) for out in outs] 43 dir_out = _join_directories([ 44 str(ctx.genfiles_dir.path), 45 target_package, 46 _GENERATED_PROTOS_DIR, 47 ]) 48 49 arguments = [] 50 if ctx.executable.plugin: 51 arguments += get_plugin_args( 52 ctx.executable.plugin, 53 [], 54 dir_out, 55 False, 56 ) 57 tools = [ctx.executable.plugin] 58 arguments += ["--objc_out=" + dir_out] 59 60 arguments += ["--proto_path=."] 61 arguments += [ 62 "--proto_path={}".format(get_include_directory(i)) 63 for i in protos 64 ] 65 66 # Include the output directory so that protoc puts the generated code in the 67 # right directory. 68 arguments += ["--proto_path={}".format(dir_out)] 69 arguments += ["--proto_path={}".format(_get_directory_from_proto(proto)) for proto in protos] 70 arguments += [_get_full_path_from_file(proto) for proto in protos] 71 72 # create a list of well known proto files if the argument is non-None 73 well_known_proto_files = [] 74 if ctx.attr.use_well_known_protos: 75 f = ctx.attr.well_known_protos.files.to_list()[0].dirname 76 77 # go two levels up so that #import "google/protobuf/..." is correct 78 arguments += ["-I{0}".format(f + "/../..")] 79 well_known_proto_files = ctx.attr.well_known_protos.files.to_list() 80 ctx.actions.run( 81 inputs = protos + well_known_proto_files, 82 tools = tools, 83 outputs = out_files, 84 executable = ctx.executable._protoc, 85 arguments = arguments, 86 ) 87 88 return struct(files = depset(out_files)) 89 90def _label_to_full_file_path(src, package): 91 if not src.startswith("//"): 92 # Relative from current package 93 if not src.startswith(":"): 94 # "a.proto" -> ":a.proto" 95 src = ":" + src 96 src = "//" + package + src 97 98 # Converts //path/to/package:File.ext to path/to/package/File.ext. 99 src = src.replace("//", "") 100 src = src.replace(":", "/") 101 if src.startswith("/"): 102 # "//:a.proto" -> "/a.proto" so remove the initial slash 103 return src[1:] 104 else: 105 return src 106 107def _get_output_file_name_from_proto(proto, fmt): 108 return proto_path_to_generated_filename( 109 _GENERATED_PROTOS_DIR + "/" + 110 _get_directory_from_proto(proto) + _get_slash_or_null_from_proto(proto) + 111 to_upper_camel_with_extension(_get_file_name_from_proto(proto), "proto"), 112 fmt, 113 ) 114 115def _get_file_name_from_proto(proto): 116 return proto.path.rpartition("/")[2] 117 118def _get_slash_or_null_from_proto(proto): 119 """Potentially returns empty (if the file is in the root directory)""" 120 return proto.path.rpartition("/")[1] 121 122def _get_directory_from_proto(proto): 123 return proto.path.rpartition("/")[0] 124 125def _get_full_path_from_file(file): 126 gen_dir_length = 0 127 128 # if file is generated, then prepare to remote its root 129 # (including CPU architecture...) 130 if not file.is_source: 131 gen_dir_length = len(file.root.path) + 1 132 133 return file.path[gen_dir_length:] 134 135def _join_directories(directories): 136 massaged_directories = [directory for directory in directories if len(directory) != 0] 137 return "/".join(massaged_directories) 138 139generate_objc = rule( 140 attrs = { 141 "deps": attr.label_list( 142 mandatory = True, 143 allow_empty = False, 144 providers = [ProtoInfo], 145 ), 146 "plugin": attr.label( 147 default = "@com_github_grpc_grpc//src/compiler:grpc_objective_c_plugin", 148 executable = True, 149 providers = ["files_to_run"], 150 cfg = "host", 151 ), 152 "srcs": attr.string_list( 153 mandatory = False, 154 allow_empty = True, 155 ), 156 "use_well_known_protos": attr.bool( 157 mandatory = False, 158 default = False, 159 ), 160 "well_known_protos": attr.label( 161 default = "@com_google_protobuf//:well_known_protos", 162 ), 163 "_protoc": attr.label( 164 default = Label("//external:protocol_compiler"), 165 executable = True, 166 cfg = "host", 167 ), 168 }, 169 output_to_genfiles = True, 170 implementation = _generate_objc_impl, 171) 172 173def _group_objc_files_impl(ctx): 174 suffix = "" 175 if ctx.attr.gen_mode == _GENERATE_HDRS: 176 suffix = "h" 177 elif ctx.attr.gen_mode == _GENERATE_SRCS: 178 suffix = "pbrpc.m" 179 elif ctx.attr.gen_mode == _GENERATE_NON_ARC_SRCS: 180 suffix = "pbobjc.m" 181 else: 182 fail("Undefined gen_mode") 183 out_files = [ 184 file 185 for file in ctx.attr.src.files.to_list() 186 if file.basename.endswith(suffix) 187 ] 188 return struct(files = depset(out_files)) 189 190generate_objc_hdrs = rule( 191 attrs = { 192 "src": attr.label( 193 mandatory = True, 194 ), 195 "gen_mode": attr.int( 196 default = _GENERATE_HDRS, 197 ), 198 }, 199 implementation = _group_objc_files_impl, 200) 201 202generate_objc_srcs = rule( 203 attrs = { 204 "src": attr.label( 205 mandatory = True, 206 ), 207 "gen_mode": attr.int( 208 default = _GENERATE_SRCS, 209 ), 210 }, 211 implementation = _group_objc_files_impl, 212) 213 214generate_objc_non_arc_srcs = rule( 215 attrs = { 216 "src": attr.label( 217 mandatory = True, 218 ), 219 "gen_mode": attr.int( 220 default = _GENERATE_NON_ARC_SRCS, 221 ), 222 }, 223 implementation = _group_objc_files_impl, 224) 225