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("//build/bazel/rules:proto_file_utils.bzl", "proto_file_utils")
17load(":cc_library_common.bzl", "create_ccinfo_for_includes")
18load(":cc_library_static.bzl", "cc_library_static")
19
20_SOURCES_KEY = "sources"
21_HEADERS_KEY = "headers"
22PROTO_GEN_NAME_SUFFIX = "_proto_gen"
23
24def _cc_proto_sources_gen_rule_impl(ctx):
25    out_flags = []
26    plugin_executable = None
27    out_arg = None
28    if ctx.attr.plugin:
29        plugin_executable = ctx.executable.plugin
30    else:
31        out_arg = "--cpp_out"
32        if ctx.attr.out_format:
33            out_flags.append(ctx.attr.out_format)
34
35    srcs = []
36    hdrs = []
37    includes = []
38    proto_infos = []
39    transitive_proto_infos = []
40
41    for dep in ctx.attr.deps:
42        proto_info = dep[ProtoInfo]
43        proto_infos.append(proto_info)
44        if proto_info.proto_source_root == ".":
45            includes.append(paths.join(ctx.label.name, ctx.label.package))
46
47        includes.append(ctx.label.name)
48
49    for transitive_dep in ctx.attr.transitive_deps:
50        transitive_proto_infos.append(transitive_dep[ProtoInfo])
51
52    outs = _generate_cc_proto_action(
53        proto_infos = proto_infos,
54        protoc = ctx.executable._protoc,
55        ctx = ctx,
56        is_cc = True,
57        out_flags = out_flags,
58        plugin_executable = plugin_executable,
59        out_arg = out_arg,
60        transitive_proto_infos = transitive_proto_infos,
61    )
62    srcs.extend(outs[_SOURCES_KEY])
63    hdrs.extend(outs[_HEADERS_KEY])
64
65    return [
66        DefaultInfo(files = depset(direct = srcs + hdrs)),
67        create_ccinfo_for_includes(ctx, hdrs = hdrs, includes = includes),
68    ]
69
70_cc_proto_sources_gen = rule(
71    implementation = _cc_proto_sources_gen_rule_impl,
72    attrs = {
73        "deps": attr.label_list(
74            providers = [ProtoInfo],
75            doc = """
76proto_library or any other target exposing ProtoInfo provider with *.proto files
77""",
78            mandatory = True,
79        ),
80        "transitive_deps": attr.label_list(
81            providers = [ProtoInfo],
82            doc = """
83proto_library that will be added to aprotoc -I when compiling the direct .proto sources.
84WARNING: This is an experimental attribute and is expected to be deprecated in the future.
85""",
86        ),
87        "_protoc": attr.label(
88            default = Label("//external/protobuf:aprotoc"),
89            executable = True,
90            cfg = "exec",
91        ),
92        "plugin": attr.label(
93            executable = True,
94            cfg = "exec",
95        ),
96        "out_format": attr.string(
97            doc = """
98Optional argument specifying the out format, e.g. lite.
99If not provided, defaults to full protos.
100""",
101        ),
102    },
103    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
104    provides = [CcInfo],
105)
106
107def _src_extension(is_cc):
108    if is_cc:
109        return "cc"
110    return "c"
111
112def _generate_cc_proto_action(
113        proto_infos,
114        protoc,
115        ctx,
116        transitive_proto_infos,
117        plugin_executable,
118        out_arg,
119        out_flags,
120        is_cc):
121    type_dictionary = {
122        _SOURCES_KEY: ".pb." + _src_extension(is_cc),
123        _HEADERS_KEY: ".pb.h",
124    }
125    return proto_file_utils.generate_proto_action(
126        proto_infos,
127        protoc,
128        ctx,
129        type_dictionary,
130        out_flags,
131        plugin_executable = plugin_executable,
132        out_arg = out_arg,
133        mnemonic = "CcProtoGen",
134        transitive_proto_infos = transitive_proto_infos,
135    )
136
137def _cc_proto_library(
138        name,
139        deps = [],
140        transitive_deps = [],
141        cc_deps = [],
142        plugin = None,
143        tags = [],
144        target_compatible_with = [],
145        out_format = None,
146        **kwargs):
147    proto_lib_name = name + PROTO_GEN_NAME_SUFFIX
148
149    _cc_proto_sources_gen(
150        name = proto_lib_name,
151        deps = deps,
152        transitive_deps = transitive_deps,
153        plugin = plugin,
154        out_format = out_format,
155        tags = ["manual"],
156    )
157
158    cc_library_static(
159        name = name,
160        srcs = [":" + proto_lib_name],
161        deps = [proto_lib_name] + cc_deps,
162        local_includes = ["."],
163        tags = tags,
164        target_compatible_with = target_compatible_with,
165        **kwargs
166    )
167
168def cc_lite_proto_library(
169        name,
170        deps = [],
171        transitive_deps = [],
172        cc_deps = [],
173        plugin = None,
174        tags = [],
175        target_compatible_with = [],
176        **kwargs):
177    _cc_proto_library(
178        name,
179        deps = deps,
180        transitive_deps = transitive_deps,
181        cc_deps = cc_deps + ["//external/protobuf:libprotobuf-cpp-lite"],
182        plugin = plugin,
183        tags = tags,
184        target_compatible_with = target_compatible_with,
185        out_format = "lite",
186        **kwargs
187    )
188
189def cc_proto_library(
190        name,
191        deps = [],
192        transitive_deps = [],
193        cc_deps = [],
194        plugin = None,
195        tags = [],
196        target_compatible_with = [],
197        **kwargs):
198    _cc_proto_library(
199        name,
200        deps = deps,
201        transitive_deps = transitive_deps,
202        cc_deps = cc_deps + ["//external/protobuf:libprotobuf-cpp-full"],
203        plugin = plugin,
204        tags = tags,
205        target_compatible_with = target_compatible_with,
206        **kwargs
207    )
208