# This rule was inspired by rules_closure`s implementation of # |closure_proto_library|, licensed under Apache 2. # https://github.com/bazelbuild/rules_closure/blob/3555e5ba61fdcc17157dd833eaf7d19b313b1bca/closure/protobuf/closure_proto_library.bzl load( "@io_bazel_rules_closure//closure/compiler:closure_js_library.bzl", "create_closure_js_library", ) load( "@io_bazel_rules_closure//closure/private:defs.bzl", "CLOSURE_JS_TOOLCHAIN_ATTRS", "unfurl", ) load( "@io_bazel_rules_closure//closure/protobuf:closure_proto_library.bzl", "closure_proto_aspect", ) # This was borrowed from Rules Go, licensed under Apache 2. # https://github.com/bazelbuild/rules_go/blob/67f44035d84a352cffb9465159e199066ecb814c/proto/compiler.bzl#L72 def _proto_path(proto): path = proto.path root = proto.root.path ws = proto.owner.workspace_root if path.startswith(root): path = path[len(root):] if path.startswith("/"): path = path[1:] if path.startswith(ws): path = path[len(ws):] if path.startswith("/"): path = path[1:] return path def _proto_include_path(proto): path = proto.path[:-len(_proto_path(proto))] if not path: return "." if path.endswith("/"): path = path[:-1] return path def _proto_include_paths(protos): return depset([_proto_include_path(proto) for proto in protos]) def _generate_closure_grpc_web_src_progress_message(name): # TODO(yannic): Add a better message? return "Generating GRPC Web %s" % name def _generate_closure_grpc_web_srcs( actions, protoc, protoc_gen_grpc_web, import_style, mode, sources, transitive_sources): all_sources = [src for src in sources] + [src for src in transitive_sources] proto_include_paths = [ "-I%s" % p for p in _proto_include_paths( [f for f in all_sources], ) ] grpc_web_out_common_options = ",".join([ "import_style={}".format(import_style), "mode={}".format(mode), ]) files = [] for src in sources: name = "{}.grpc.js".format( ".".join(src.path.split("/")[-1].split(".")[:-1]), ) js = actions.declare_file(name) files.append(js) args = proto_include_paths + [ "--plugin=protoc-gen-grpc-web={}".format(protoc_gen_grpc_web.path), "--grpc-web_out={options},out={out_file}:{path}".format( options = grpc_web_out_common_options, out_file = name, path = js.path[:js.path.rfind("/")], ), src.path, ] actions.run( tools = [protoc_gen_grpc_web], inputs = all_sources, outputs = [js], executable = protoc, arguments = args, progress_message = _generate_closure_grpc_web_src_progress_message(name), ) return files _error_multiple_deps = "".join([ "'deps' attribute must contain exactly one label ", "(we didn't name it 'dep' for consistency). ", "We may revisit this restriction later.", ]) def _closure_grpc_web_library_impl(ctx): if len(ctx.attr.deps) > 1: # TODO(yannic): Revisit this restriction. fail(_error_multiple_deps, "deps") dep = ctx.attr.deps[0] srcs = _generate_closure_grpc_web_srcs( actions = ctx.actions, protoc = ctx.executable._protoc, protoc_gen_grpc_web = ctx.executable._protoc_gen_grpc_web, import_style = ctx.attr.import_style, mode = ctx.attr.mode, sources = dep[ProtoInfo].direct_sources, transitive_sources = dep[ProtoInfo].transitive_imports, ) deps = unfurl(ctx.attr.deps, provider = "closure_js_library") deps += [ ctx.attr._grpc_web_abstractclientbase, ctx.attr._grpc_web_clientreadablestream, ctx.attr._grpc_web_error, ctx.attr._grpc_web_grpcwebclientbase, ] suppress = [ "misplacedTypeAnnotation", "unusedPrivateMembers", "reportUnknownTypes", "strictDependencies", "extraRequire", ] library = create_closure_js_library( ctx = ctx, srcs = srcs, deps = deps, suppress = suppress, lenient = False, ) return struct( exports = library.exports, closure_js_library = library.closure_js_library, # The usual suspects are exported as runfiles, in addition to raw source. runfiles = ctx.runfiles(files = srcs), ) closure_grpc_web_library = rule( implementation = _closure_grpc_web_library_impl, attrs = dict({ "deps": attr.label_list( mandatory = True, providers = [ProtoInfo, "closure_js_library"], # The files generated by this aspect are required dependencies. aspects = [closure_proto_aspect], ), "import_style": attr.string( default = "closure", values = ["closure"], ), "mode": attr.string( default = "grpcwebtext", values = ["grpcwebtext", "grpcweb"], ), # internal only # TODO(yannic): Convert to toolchain. "_protoc": attr.label( default = Label("@com_google_protobuf//:protoc"), executable = True, cfg = "host", ), "_protoc_gen_grpc_web": attr.label( default = Label("//javascript/net/grpc/web:protoc-gen-grpc-web"), executable = True, cfg = "host", ), "_grpc_web_abstractclientbase": attr.label( default = Label("//javascript/net/grpc/web:abstractclientbase"), ), "_grpc_web_clientreadablestream": attr.label( default = Label("//javascript/net/grpc/web:clientreadablestream"), ), "_grpc_web_error": attr.label( default = Label("//javascript/net/grpc/web:error"), ), "_grpc_web_grpcwebclientbase": attr.label( default = Label("//javascript/net/grpc/web:grpcwebclientbase"), ), }, **CLOSURE_JS_TOOLCHAIN_ATTRS), )