• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("@bazel_skylib//lib:sets.bzl", "sets")
17
18def _generate_and_declare_output_files(
19        ctx,
20        file_names,
21        type_dictionary):
22    ret = {}
23    for typ in type_dictionary:
24        ret[typ] = []
25
26    for name in file_names:
27        short_path = name.short_path
28        for typ, ext in type_dictionary.items():
29            # prefix with label.name to prevent collisions between targets
30            # if proto compliation becomes an aspect, can prefix with output
31            # information instead to allow reuse, e.g. multiple cc `lite`
32            # libraries containing the same proto file
33            out_name = paths.join(ctx.label.name, paths.replace_extension(short_path, ext))
34            declared = ctx.actions.declare_file(out_name)
35            ret[typ].append(declared)
36
37    return ret
38
39def _generate_jar_proto_action(
40        proto_infos,
41        protoc,
42        ctx,
43        out_flags = [],
44        plugin_executable = None,
45        out_arg = None,
46        mnemonic = "ProtoGen"):
47    jar_basename = ctx.label.name + "-proto_gen"
48    jar_name = jar_basename + "-src.jar"
49    jar_file = ctx.actions.declare_file(jar_name)
50
51    _generate_proto_action(
52        proto_infos = proto_infos,
53        protoc = protoc,
54        ctx = ctx,
55        out_flags = out_flags,
56        plugin_executable = plugin_executable,
57        out_arg = out_arg,
58        mnemonic = mnemonic,
59        output_file = jar_file,
60    )
61
62    srcjar_name = jar_basename + ".srcjar"
63    srcjar_file = ctx.actions.declare_file(srcjar_name)
64    ctx.actions.symlink(
65        output = srcjar_file,
66        target_file = jar_file,
67    )
68
69    return srcjar_file
70
71def _generate_proto_action(
72        proto_infos,
73        protoc,
74        ctx,
75        type_dictionary = None,
76        out_flags = [],
77        plugin_executable = None,
78        out_arg = None,
79        mnemonic = "ProtoGen",
80        output_file = None):
81    """ Utility function for creating proto_compiler action.
82
83    Args:
84      proto_infos: A list of ProtoInfo.
85      protoc: proto compiler executable.
86      ctx: context, used for declaring new files only.
87      type_dictionary: a dictionary of types to output extensions
88      out_flags: protoc output flags
89      plugin_executable: plugin executable file
90      out_arg: as appropriate, if plugin_executable and out_arg are both supplied, plugin_executable is preferred
91      mnemonic: (optional) a string to describe the proto compilation action
92      output_file: (optional) File, used to specify a specific file for protoc output (typically a JAR file)
93
94    Returns:
95      Dictionary with declared files grouped by type from the type_dictionary.
96    """
97
98    # TODO(B/245629074): Don't build external/protobuf if it is provided in
99    #  toolchain already.
100    proto_srcs = []
101    proto_source_root_list = sets.make()
102    transitive_proto_srcs_list = []
103    transitive_proto_path_list = sets.make()
104
105    for proto_info in proto_infos:
106        sets.insert(proto_source_root_list, proto_info.proto_source_root)
107        proto_srcs.extend(proto_info.direct_sources)
108        transitive_proto_srcs_list.append(proto_info.transitive_imports)
109        for p in proto_info.transitive_proto_path.to_list():
110            sets.insert(transitive_proto_path_list, p)
111
112    protoc_out_name = paths.join(ctx.bin_dir.path, ctx.label.package)
113    if output_file:
114        protoc_out_name = paths.join(protoc_out_name, output_file.basename)
115        out_files = {
116            "out": [output_file],
117        }
118    else:
119        protoc_out_name = paths.join(protoc_out_name, ctx.label.name)
120        out_files = _generate_and_declare_output_files(
121            ctx,
122            proto_srcs,
123            type_dictionary,
124        )
125
126    tools = []
127    args = ctx.actions.args()
128    if plugin_executable:
129        tools.append(plugin_executable)
130        args.add("--plugin=protoc-gen-PLUGIN=" + plugin_executable.path)
131        args.add("--PLUGIN_out=" + ",".join(out_flags) + ":" + protoc_out_name)
132    else:
133        args.add("{}={}:{}".format(out_arg, ",".join(out_flags), protoc_out_name))
134
135    # the order matters so we add the source roots first
136    args.add_all(["-I" + p for p in sets.to_list(proto_source_root_list)])
137
138    args.add_all(["-I" + p for p in sets.to_list(transitive_proto_path_list)])
139    args.add_all(["-I{0}={1}".format(f.short_path, f.path) for t in transitive_proto_srcs_list for f in t.to_list()])
140
141    args.add_all([f.short_path for f in proto_srcs])
142
143    inputs = depset(
144        direct = proto_srcs,
145        transitive = transitive_proto_srcs_list,
146    )
147
148    outputs = []
149    for outs in out_files.values():
150        outputs.extend(outs)
151
152    ctx.actions.run(
153        inputs = inputs,
154        executable = protoc,
155        tools = tools,
156        outputs = outputs,
157        arguments = [args],
158        mnemonic = mnemonic,
159    )
160    return out_files
161
162proto_file_utils = struct(
163    generate_proto_action = _generate_proto_action,
164    generate_jar_proto_action = _generate_jar_proto_action,
165)
166