1# Copyright (c) 2023, Google LLC 2# All rights reserved. 3# 4# Use of this source code is governed by a BSD-style 5# license that can be found in the LICENSE file or at 6# https://developers.google.com/open-source/licenses/bsd 7"""lua_proto_library(): a rule for building Lua protos.""" 8 9load("@bazel_skylib//lib:paths.bzl", "paths") 10load("//bazel/common:proto_info.bzl", "ProtoInfo") 11 12# Generic support code ######################################################### 13 14def _get_real_short_path(file): 15 # For some reason, files from other archives have short paths that look like: 16 # ../com_google_protobuf/google/protobuf/descriptor.proto 17 short_path = file.short_path 18 if short_path.startswith("../"): 19 second_slash = short_path.index("/", 3) 20 short_path = short_path[second_slash + 1:] 21 22 # Sometimes it has another few prefixes like: 23 # _virtual_imports/any_proto/google/protobuf/any.proto 24 # benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto 25 # We want just google/protobuf/any.proto. 26 virtual_imports = "_virtual_imports/" 27 if virtual_imports in short_path: 28 short_path = short_path.split(virtual_imports)[1].split("/", 1)[1] 29 return short_path 30 31def _get_real_root(ctx, file): 32 real_short_path = _get_real_short_path(file) 33 root = file.path[:-len(real_short_path) - 1] 34 35 if ctx.rule.attr.strip_import_prefix: 36 root = paths.join(root, ctx.rule.attr.strip_import_prefix[1:]) 37 38 return root 39 40def _generate_output_file(ctx, src, extension): 41 package = ctx.label.package 42 43 if ctx.rule.attr.strip_import_prefix and ctx.rule.attr.strip_import_prefix != "/": 44 package = package[len(ctx.rule.attr.strip_import_prefix):] 45 46 real_short_path = _get_real_short_path(src) 47 real_short_path = paths.relativize(real_short_path, package) 48 output_filename = paths.replace_extension(real_short_path, extension) 49 ret = ctx.actions.declare_file(output_filename) 50 return ret 51 52# upb_proto_library / upb_proto_reflection_library shared code ################# 53 54_LuaFilesInfo = provider( 55 "A set of lua files generated from .proto files", 56 fields = ["files"], 57) 58 59def _compile_upb_protos(ctx, proto_info, proto_sources): 60 files = [_generate_output_file(ctx, name, "_pb.lua") for name in proto_sources] 61 transitive_sets = proto_info.transitive_descriptor_sets.to_list() 62 ctx.actions.run( 63 inputs = depset( 64 direct = [proto_info.direct_descriptor_set], 65 transitive = [proto_info.transitive_descriptor_sets], 66 ), 67 tools = [ctx.executable._upbc], 68 outputs = files, 69 executable = ctx.executable._protoc, 70 arguments = [ 71 "--lua_out=" + _get_real_root(ctx, files[0]), 72 "--plugin=protoc-gen-lua=" + ctx.executable._upbc.path, 73 "--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]), 74 ] + 75 [_get_real_short_path(file) for file in proto_sources], 76 progress_message = "Generating Lua protos for :" + ctx.label.name, 77 ) 78 return files 79 80def _lua_proto_rule_impl(ctx): 81 if len(ctx.attr.deps) != 1: 82 fail("only one deps dependency allowed.") 83 dep = ctx.attr.deps[0] 84 if _LuaFilesInfo not in dep: 85 fail("proto_library rule must generate _LuaFilesInfo (aspect should have handled this).") 86 files = dep[_LuaFilesInfo].files 87 return [ 88 DefaultInfo( 89 files = files, 90 data_runfiles = ctx.runfiles(files = files.to_list()), 91 ), 92 ] 93 94def _lua_proto_library_aspect_impl(target, ctx): 95 proto_info = target[ProtoInfo] 96 files = _compile_upb_protos(ctx, proto_info, proto_info.direct_sources) 97 deps = ctx.rule.attr.deps 98 transitive = [dep[_LuaFilesInfo].files for dep in deps if _LuaFilesInfo in dep] 99 return [_LuaFilesInfo(files = depset(direct = files, transitive = transitive))] 100 101# lua_proto_library() ########################################################## 102 103_lua_proto_library_aspect = aspect( 104 attrs = { 105 "_upbc": attr.label( 106 executable = True, 107 cfg = "exec", 108 default = "//lua:protoc-gen-lua", 109 ), 110 "_protoc": attr.label( 111 executable = True, 112 cfg = "exec", 113 default = "//:protoc", 114 ), 115 }, 116 implementation = _lua_proto_library_aspect_impl, 117 provides = [_LuaFilesInfo], 118 attr_aspects = ["deps"], 119 fragments = ["cpp"], 120) 121 122lua_proto_library = rule( 123 implementation = _lua_proto_rule_impl, 124 attrs = { 125 "deps": attr.label_list( 126 aspects = [_lua_proto_library_aspect], 127 allow_rules = ["proto_library"], 128 providers = [ProtoInfo], 129 ), 130 }, 131) 132