| # Copyright 2021 The gRPC Authors | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #     http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 |  | 
 | """ | 
 | This module contains build rules relating to gRPC Objective-C. | 
 | """ | 
 |  | 
 | load("@rules_proto//proto:defs.bzl", "ProtoInfo") | 
 | load( | 
 |     "//bazel:protobuf.bzl", | 
 |     "get_include_directory", | 
 |     "get_plugin_args", | 
 |     "proto_path_to_generated_filename", | 
 | ) | 
 | load(":grpc_util.bzl", "to_upper_camel_with_extension") | 
 |  | 
 | _GRPC_PROTO_HEADER_FMT = "{}.pbrpc.h" | 
 | _GRPC_PROTO_SRC_FMT = "{}.pbrpc.m" | 
 | _PROTO_HEADER_FMT = "{}.pbobjc.h" | 
 | _PROTO_SRC_FMT = "{}.pbobjc.m" | 
 | _GENERATED_PROTOS_DIR = "_generated_protos" | 
 |  | 
 | _GENERATE_HDRS = 1 | 
 | _GENERATE_SRCS = 2 | 
 | _GENERATE_NON_ARC_SRCS = 3 | 
 |  | 
 | def _generate_objc_impl(ctx): | 
 |     """Implementation of the generate_objc rule.""" | 
 |     protos = [ | 
 |         f | 
 |         for src in ctx.attr.deps | 
 |         for f in src[ProtoInfo].transitive_imports.to_list() | 
 |     ] | 
 |  | 
 |     target_package = _join_directories([ctx.label.workspace_root, ctx.label.package]) | 
 |  | 
 |     files_with_rpc = [_label_to_full_file_path(f, target_package) for f in ctx.attr.srcs] | 
 |  | 
 |     outs = [] | 
 |     for proto in protos: | 
 |         outs.append(_get_output_file_name_from_proto(proto, _PROTO_HEADER_FMT)) | 
 |         outs.append(_get_output_file_name_from_proto(proto, _PROTO_SRC_FMT)) | 
 |  | 
 |         file_path = _get_full_path_from_file(proto) | 
 |         if file_path in files_with_rpc: | 
 |             outs.append(_get_output_file_name_from_proto(proto, _GRPC_PROTO_HEADER_FMT)) | 
 |             outs.append(_get_output_file_name_from_proto(proto, _GRPC_PROTO_SRC_FMT)) | 
 |  | 
 |     out_files = [ctx.actions.declare_file(out) for out in outs] | 
 |     dir_out = _join_directories([ | 
 |         str(ctx.genfiles_dir.path), | 
 |         target_package, | 
 |         _GENERATED_PROTOS_DIR, | 
 |     ]) | 
 |  | 
 |     arguments = [] | 
 |     tools = [] | 
 |     if ctx.executable.plugin: | 
 |         arguments += get_plugin_args( | 
 |             ctx.executable.plugin, | 
 |             [], | 
 |             dir_out, | 
 |             False, | 
 |         ) | 
 |         tools = [ctx.executable.plugin] | 
 |     arguments.append("--objc_out=" + dir_out) | 
 |  | 
 |     arguments.append("--proto_path=.") | 
 |     arguments += [ | 
 |         "--proto_path={}".format(get_include_directory(i)) | 
 |         for i in protos | 
 |     ] | 
 |  | 
 |     # Include the output directory so that protoc puts the generated code in the | 
 |     # right directory. | 
 |     arguments.append("--proto_path={}".format(dir_out)) | 
 |     arguments += ["--proto_path={}".format(_get_directory_from_proto(proto)) for proto in protos] | 
 |     arguments += [_get_full_path_from_file(proto) for proto in protos] | 
 |  | 
 |     # create a list of well known proto files if the argument is non-None | 
 |     well_known_proto_files = [] | 
 |     if ctx.attr.use_well_known_protos: | 
 |         f = ctx.attr.well_known_protos.files.to_list()[0].dirname | 
 |  | 
 |         # go two levels up so that #import "google/protobuf/..." is correct | 
 |         arguments.append("-I{0}".format(f + "/../..")) | 
 |         well_known_proto_files = ctx.attr.well_known_protos.files.to_list() | 
 |     ctx.actions.run( | 
 |         inputs = protos + well_known_proto_files, | 
 |         tools = tools, | 
 |         outputs = out_files, | 
 |         executable = ctx.executable._protoc, | 
 |         arguments = arguments, | 
 |     ) | 
 |  | 
 |     return struct(files = depset(out_files))  # buildifier: disable=rule-impl-return | 
 |  | 
 | def _label_to_full_file_path(src, package): | 
 |     if not src.startswith("//"): | 
 |         # Relative from current package | 
 |         if not src.startswith(":"): | 
 |             # "a.proto" -> ":a.proto" | 
 |             src = ":" + src | 
 |         src = "//" + package + src | 
 |  | 
 |     # Converts //path/to/package:File.ext to path/to/package/File.ext. | 
 |     src = src.replace("//", "") | 
 |     src = src.replace(":", "/") | 
 |     if src.startswith("/"): | 
 |         # "//:a.proto" -> "/a.proto" so remove the initial slash | 
 |         return src[1:] | 
 |     else: | 
 |         return src | 
 |  | 
 | def _get_output_file_name_from_proto(proto, fmt): | 
 |     return proto_path_to_generated_filename( | 
 |         _GENERATED_PROTOS_DIR + "/" + | 
 |         _get_directory_from_proto(proto) + _get_slash_or_null_from_proto(proto) + | 
 |         to_upper_camel_with_extension(_get_file_name_from_proto(proto), "proto"), | 
 |         fmt, | 
 |     ) | 
 |  | 
 | def _get_file_name_from_proto(proto): | 
 |     return proto.path.rpartition("/")[2] | 
 |  | 
 | def _get_slash_or_null_from_proto(proto): | 
 |     """Potentially returns empty (if the file is in the root directory)""" | 
 |     return proto.path.rpartition("/")[1] | 
 |  | 
 | def _get_directory_from_proto(proto): | 
 |     return proto.path.rpartition("/")[0] | 
 |  | 
 | def _get_full_path_from_file(file): | 
 |     gen_dir_length = 0 | 
 |  | 
 |     # if file is generated, then prepare to remote its root | 
 |     # (including CPU architecture...) | 
 |     if not file.is_source: | 
 |         gen_dir_length = len(file.root.path) + 1 | 
 |  | 
 |     return file.path[gen_dir_length:] | 
 |  | 
 | def _join_directories(directories): | 
 |     massaged_directories = [directory for directory in directories if len(directory) != 0] | 
 |     return "/".join(massaged_directories) | 
 |  | 
 | generate_objc = rule( | 
 |     attrs = { | 
 |         "deps": attr.label_list( | 
 |             mandatory = True, | 
 |             allow_empty = False, | 
 |             providers = [ProtoInfo], | 
 |         ), | 
 |         "plugin": attr.label( | 
 |             default = "@com_github_grpc_grpc//src/compiler:grpc_objective_c_plugin", | 
 |             executable = True, | 
 |             providers = ["files_to_run"], | 
 |             cfg = "host", | 
 |         ), | 
 |         "srcs": attr.string_list( | 
 |             mandatory = False, | 
 |             allow_empty = True, | 
 |         ), | 
 |         "use_well_known_protos": attr.bool( | 
 |             mandatory = False, | 
 |             default = False, | 
 |         ), | 
 |         "well_known_protos": attr.label( | 
 |             default = "@com_google_protobuf//:well_known_type_protos", | 
 |         ), | 
 |         "_protoc": attr.label( | 
 |             default = Label("//external:protocol_compiler"), | 
 |             executable = True, | 
 |             cfg = "host", | 
 |         ), | 
 |     }, | 
 |     output_to_genfiles = True, | 
 |     implementation = _generate_objc_impl, | 
 | ) | 
 |  | 
 | def _group_objc_files_impl(ctx): | 
 |     suffix = "" | 
 |     if ctx.attr.gen_mode == _GENERATE_HDRS: | 
 |         suffix = "h" | 
 |     elif ctx.attr.gen_mode == _GENERATE_SRCS: | 
 |         suffix = "pbrpc.m" | 
 |     elif ctx.attr.gen_mode == _GENERATE_NON_ARC_SRCS: | 
 |         suffix = "pbobjc.m" | 
 |     else: | 
 |         fail("Undefined gen_mode") | 
 |     out_files = [ | 
 |         file | 
 |         for file in ctx.attr.src.files.to_list() | 
 |         if file.basename.endswith(suffix) | 
 |     ] | 
 |     return struct(files = depset(out_files))  # buildifier: disable=rule-impl-return | 
 |  | 
 | generate_objc_hdrs = rule( | 
 |     attrs = { | 
 |         "src": attr.label( | 
 |             mandatory = True, | 
 |         ), | 
 |         "gen_mode": attr.int( | 
 |             default = _GENERATE_HDRS, | 
 |         ), | 
 |     }, | 
 |     implementation = _group_objc_files_impl, | 
 | ) | 
 |  | 
 | generate_objc_srcs = rule( | 
 |     attrs = { | 
 |         "src": attr.label( | 
 |             mandatory = True, | 
 |         ), | 
 |         "gen_mode": attr.int( | 
 |             default = _GENERATE_SRCS, | 
 |         ), | 
 |     }, | 
 |     implementation = _group_objc_files_impl, | 
 | ) | 
 |  | 
 | generate_objc_non_arc_srcs = rule( | 
 |     attrs = { | 
 |         "src": attr.label( | 
 |             mandatory = True, | 
 |         ), | 
 |         "gen_mode": attr.int( | 
 |             default = _GENERATE_NON_ARC_SRCS, | 
 |         ), | 
 |     }, | 
 |     implementation = _group_objc_files_impl, | 
 | ) |