1# Copyright (c) 2009-2021, Google LLC
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are met:
6#     * Redistributions of source code must retain the above copyright
7#       notice, this list of conditions and the following disclaimer.
8#     * Redistributions in binary form must reproduce the above copyright
9#       notice, this list of conditions and the following disclaimer in the
10#       documentation and/or other materials provided with the distribution.
11#     * Neither the name of Google LLC nor the
12#       names of its contributors may be used to endorse or promote products
13#       derived from this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
19# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26"""lua_proto_library(): a rule for building Lua protos."""
27
28load("@bazel_skylib//lib:paths.bzl", "paths")
29
30# Generic support code #########################################################
31
32# begin:github_only
33_is_google3 = False
34# end:github_only
35
36# begin:google_only
37# _is_google3 = True
38# end:google_only
39
40def _get_real_short_path(file):
41    # For some reason, files from other archives have short paths that look like:
42    #   ../com_google_protobuf/google/protobuf/descriptor.proto
43    short_path = file.short_path
44    if short_path.startswith("../"):
45        second_slash = short_path.index("/", 3)
46        short_path = short_path[second_slash + 1:]
47
48    # Sometimes it has another few prefixes like:
49    #   _virtual_imports/any_proto/google/protobuf/any.proto
50    #   benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto
51    # We want just google/protobuf/any.proto.
52    virtual_imports = "_virtual_imports/"
53    if virtual_imports in short_path:
54        short_path = short_path.split(virtual_imports)[1].split("/", 1)[1]
55    return short_path
56
57def _get_real_root(ctx, file):
58    real_short_path = _get_real_short_path(file)
59    root = file.path[:-len(real_short_path) - 1]
60    if not _is_google3 and ctx.rule.attr.strip_import_prefix:
61        root = paths.join(root, ctx.rule.attr.strip_import_prefix[1:])
62    return root
63
64def _generate_output_file(ctx, src, extension):
65    package = ctx.label.package
66    if not _is_google3 and ctx.rule.attr.strip_import_prefix and ctx.rule.attr.strip_import_prefix != "/":
67        package = package[len(ctx.rule.attr.strip_import_prefix):]
68    real_short_path = _get_real_short_path(src)
69    real_short_path = paths.relativize(real_short_path, package)
70    output_filename = paths.replace_extension(real_short_path, extension)
71    ret = ctx.actions.declare_file(output_filename)
72    return ret
73
74# upb_proto_library / upb_proto_reflection_library shared code #################
75
76_LuaFilesInfo = provider(
77    "A set of lua files generated from .proto files",
78    fields = ["files"],
79)
80
81def _compile_upb_protos(ctx, proto_info, proto_sources):
82    files = [_generate_output_file(ctx, name, "_pb.lua") for name in proto_sources]
83    transitive_sets = proto_info.transitive_descriptor_sets.to_list()
84    ctx.actions.run(
85        inputs = depset(
86            direct = [proto_info.direct_descriptor_set],
87            transitive = [proto_info.transitive_descriptor_sets],
88        ),
89        tools = [ctx.executable._upbc],
90        outputs = files,
91        executable = ctx.executable._protoc,
92        arguments = [
93                        "--lua_out=" + _get_real_root(ctx, files[0]),
94                        "--plugin=protoc-gen-lua=" + ctx.executable._upbc.path,
95                        "--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]),
96                    ] +
97                    [_get_real_short_path(file) for file in proto_sources],
98        progress_message = "Generating Lua protos for :" + ctx.label.name,
99    )
100    return files
101
102def _lua_proto_rule_impl(ctx):
103    if len(ctx.attr.deps) != 1:
104        fail("only one deps dependency allowed.")
105    dep = ctx.attr.deps[0]
106    if _LuaFilesInfo not in dep:
107        fail("proto_library rule must generate _LuaFilesInfo (aspect should have handled this).")
108    files = dep[_LuaFilesInfo].files
109    return [
110        DefaultInfo(
111            files = files,
112            data_runfiles = ctx.runfiles(files = files.to_list()),
113        ),
114    ]
115
116def _lua_proto_library_aspect_impl(target, ctx):
117    proto_info = target[ProtoInfo]
118    files = _compile_upb_protos(ctx, proto_info, proto_info.direct_sources)
119    deps = ctx.rule.attr.deps
120    transitive = [dep[_LuaFilesInfo].files for dep in deps if _LuaFilesInfo in dep]
121    return [_LuaFilesInfo(files = depset(direct = files, transitive = transitive))]
122
123# lua_proto_library() ##########################################################
124
125_lua_proto_library_aspect = aspect(
126    attrs = {
127        "_upbc": attr.label(
128            executable = True,
129            cfg = "exec",
130            default = "//lua:protoc-gen-lua",
131        ),
132        "_protoc": attr.label(
133            executable = True,
134            cfg = "exec",
135            default = "@com_google_protobuf//:protoc",
136        ),
137    },
138    implementation = _lua_proto_library_aspect_impl,
139    provides = [_LuaFilesInfo],
140    attr_aspects = ["deps"],
141    fragments = ["cpp"],
142)
143
144lua_proto_library = rule(
145    output_to_genfiles = True,
146    implementation = _lua_proto_rule_impl,
147    attrs = {
148        "deps": attr.label_list(
149            aspects = [_lua_proto_library_aspect],
150            allow_rules = ["proto_library"],
151            providers = [ProtoInfo],
152        ),
153    },
154)
155