# 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 = "exec", ), "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 = "exec", ), }, 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, )