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