Squashed 'third_party/rules_rust/' content from commit bf59038ca

git-subtree-dir: third_party/rules_rust
git-subtree-split: bf59038cac11798cbaef9f3bf965bad8182b97fa
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I5a20e403203d670df467ea97dde9a4ac40339a8d
diff --git a/proto/proto.bzl b/proto/proto.bzl
new file mode 100644
index 0000000..ea239b0
--- /dev/null
+++ b/proto/proto.bzl
@@ -0,0 +1,432 @@
+# Copyright 2018 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Rust Protobuf Rules
+
+These build rules are used for building [protobufs][protobuf]/[gRPC][grpc] in [Rust][rust] with Bazel.
+
+[rust]: http://www.rust-lang.org/
+[protobuf]: https://developers.google.com/protocol-buffers/
+[grpc]: https://grpc.io
+
+### Setup
+
+To use the Rust proto rules, add the following to your `WORKSPACE` file to add the
+external repositories for the Rust proto toolchain (in addition to the [rust rules setup](..)):
+
+```python
+load("//proto:repositories.bzl", "rust_proto_repositories")
+
+rust_proto_repositories()
+```
+"""
+
+load("@rules_proto//proto:defs.bzl", "ProtoInfo")
+load(
+    "//proto:toolchain.bzl",
+    _generate_proto = "rust_generate_proto",
+    _generated_file_stem = "generated_file_stem",
+)
+load("//rust:defs.bzl", "rust_common")
+
+# buildifier: disable=bzl-visibility
+load("//rust/private:rustc.bzl", "rustc_compile_action")
+
+# buildifier: disable=bzl-visibility
+load("//rust/private:utils.bzl", "compute_crate_name", "determine_output_hash", "find_toolchain", "transform_deps")
+
+RustProtoInfo = provider(
+    doc = "Rust protobuf provider info",
+    fields = {
+        "proto_sources": "List[string]: list of source paths of protos",
+        "transitive_proto_sources": "depset[string]",
+    },
+)
+
+def _compute_proto_source_path(file, source_root_attr):
+    """Take the short path of file and make it suitable for protoc.
+
+    Args:
+        file (File): The target source file.
+        source_root_attr (str): The directory relative to which the `.proto` \
+            files defined in the proto_library are defined.
+
+    Returns:
+        str: The protoc suitible path of `file`
+    """
+
+    # Bazel creates symlinks to the .proto files under a directory called
+    # "_virtual_imports/<rule name>" if we do any sort of munging of import
+    # paths (e.g. using strip_import_prefix / import_prefix attributes)
+    virtual_imports = "/_virtual_imports/"
+    if virtual_imports in file.path:
+        return file.path.split(virtual_imports)[1].split("/", 1)[1]
+
+    # For proto, they need to be requested with their absolute name to be
+    # compatible with the descriptor_set passed by proto_library.
+    # I.e. if you compile a protobuf at @repo1//package:file.proto, the proto
+    # compiler would generate a file descriptor with the path
+    # `package/file.proto`. Since we compile from the proto descriptor, we need
+    # to pass the list of descriptors and the list of path to compile.
+    # For the precedent example, the file (noted `f`) would have
+    # `f.short_path` returns `external/repo1/package/file.proto`.
+    # In addition, proto_library can provide a proto_source_path to change the base
+    # path, which should a be a prefix.
+    path = file.short_path
+
+    # Strip external prefix.
+    path = path.split("/", 2)[2] if path.startswith("../") else path
+
+    # Strip source_root.
+    if path.startswith(source_root_attr):
+        return path[len(source_root_attr):]
+    else:
+        return path
+
+def _rust_proto_aspect_impl(target, ctx):
+    """The implementation of the `rust_proto_aspect` aspect
+
+    Args:
+        target (Target): The target to which the aspect is applied
+        ctx (ctx): The rule context which the targetis created from
+
+    Returns:
+        list: A list containg a `RustProtoInfo` provider
+    """
+    if ProtoInfo not in target:
+        return None
+
+    if hasattr(ctx.rule.attr, "proto_source_root"):
+        source_root = ctx.rule.attr.proto_source_root
+    else:
+        source_root = ""
+
+    if source_root and source_root[-1] != "/":
+        source_root += "/"
+
+    sources = [
+        _compute_proto_source_path(f, source_root)
+        for f in target[ProtoInfo].direct_sources
+    ]
+    transitive_sources = [
+        f[RustProtoInfo].transitive_proto_sources
+        for f in ctx.rule.attr.deps
+        if RustProtoInfo in f
+    ]
+    return [RustProtoInfo(
+        proto_sources = sources,
+        transitive_proto_sources = depset(transitive = transitive_sources, direct = sources),
+    )]
+
+_rust_proto_aspect = aspect(
+    doc = "An aspect that gathers rust proto direct and transitive sources",
+    implementation = _rust_proto_aspect_impl,
+    attr_aspects = ["deps"],
+)
+
+def _gen_lib(ctx, grpc, srcs, lib):
+    """Generate a lib.rs file for the crates.
+
+    Args:
+        ctx (ctx): The current rule's context object
+        grpc (bool): True if the current rule is a `gRPC` rule.
+        srcs (list): A list of protoc suitible file paths (str).
+        lib (File): The File object where the rust source file should be written
+    """
+    content = ["extern crate protobuf;"]
+    if grpc:
+        content.append("extern crate grpc;")
+        content.append("extern crate tls_api;")
+    for f in srcs.to_list():
+        content.append("pub mod %s;" % _generated_file_stem(f))
+        content.append("pub use %s::*;" % _generated_file_stem(f))
+        if grpc:
+            content.append("pub mod %s_grpc;" % _generated_file_stem(f))
+            content.append("pub use %s_grpc::*;" % _generated_file_stem(f))
+    ctx.actions.write(lib, "\n".join(content))
+
+def _expand_provider(lst, provider):
+    """Gathers a list of a specific provider from a list of targets.
+
+    Args:
+        lst (list): A list of Targets
+        provider (Provider): The target provider type to extract `lst`
+
+    Returns:
+        list: A list of providers of the type from `provider`.
+    """
+    return [el[provider] for el in lst if provider in el]
+
+def _rust_proto_compile(protos, descriptor_sets, imports, crate_name, ctx, is_grpc, compile_deps, toolchain):
+    """Create and run a rustc compile action based on the current rule's attributes
+
+    Args:
+        protos (depset): Paths of protos to compile.
+        descriptor_sets (depset): A set of transitive protobuf `FileDescriptorSet`s
+        imports (depset): A set of transitive protobuf Imports.
+        crate_name (str): The name of the Crate for the current target
+        ctx (ctx): The current rule's context object
+        is_grpc (bool): True if the current rule is a `gRPC` rule.
+        compile_deps (list): A list of Rust dependencies (`List[Target]`)
+        toolchain (rust_toolchain): the current `rust_toolchain`.
+
+    Returns:
+        list: A list of providers, see `rustc_compile_action`
+    """
+
+    # Create all the source in a specific folder
+    proto_toolchain = ctx.toolchains[Label("//proto:toolchain")]
+    output_dir = "%s.%s.rust" % (crate_name, "grpc" if is_grpc else "proto")
+
+    # Generate the proto stubs
+    srcs = _generate_proto(
+        ctx,
+        descriptor_sets,
+        protos = protos,
+        imports = imports,
+        output_dir = output_dir,
+        proto_toolchain = proto_toolchain,
+        is_grpc = is_grpc,
+    )
+
+    # and lib.rs
+    lib_rs = ctx.actions.declare_file("%s/lib.rs" % output_dir)
+    _gen_lib(ctx, is_grpc, protos, lib_rs)
+    srcs.append(lib_rs)
+
+    # And simulate rust_library behavior
+    output_hash = determine_output_hash(lib_rs, ctx.label)
+    rust_lib = ctx.actions.declare_file("%s/lib%s-%s.rlib" % (
+        output_dir,
+        crate_name,
+        output_hash,
+    ))
+
+    # Gather all dependencies for compilation
+    compile_action_deps = depset(
+        transform_deps(
+            compile_deps +
+            proto_toolchain.grpc_compile_deps if is_grpc else proto_toolchain.proto_compile_deps,
+        ),
+    )
+
+    return rustc_compile_action(
+        ctx = ctx,
+        attr = ctx.attr,
+        toolchain = toolchain,
+        crate_info = rust_common.create_crate_info(
+            name = crate_name,
+            type = "rlib",
+            root = lib_rs,
+            srcs = depset(srcs),
+            deps = compile_action_deps,
+            proc_macro_deps = depset([]),
+            aliases = {},
+            output = rust_lib,
+            edition = proto_toolchain.edition,
+            rustc_env = {},
+            is_test = False,
+            compile_data = depset([target.files for target in getattr(ctx.attr, "compile_data", [])]),
+            wrapped_crate_type = None,
+            owner = ctx.label,
+        ),
+        output_hash = output_hash,
+    )
+
+def _rust_protogrpc_library_impl(ctx, is_grpc):
+    """Implementation of the rust_(proto|grpc)_library.
+
+    Args:
+        ctx (ctx): The current rule's context object
+        is_grpc (bool): True if the current rule is a `gRPC` rule.
+
+    Returns:
+        list: A list of providers, see `_rust_proto_compile`
+    """
+    proto = _expand_provider(ctx.attr.deps, ProtoInfo)
+    transitive_sources = [
+        f[RustProtoInfo].transitive_proto_sources
+        for f in ctx.attr.deps
+        if RustProtoInfo in f
+    ]
+
+    toolchain = find_toolchain(ctx)
+    crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain)
+
+    return _rust_proto_compile(
+        protos = depset(transitive = transitive_sources),
+        descriptor_sets = depset(transitive = [p.transitive_descriptor_sets for p in proto]),
+        imports = depset(transitive = [p.transitive_imports for p in proto]),
+        crate_name = crate_name,
+        ctx = ctx,
+        is_grpc = is_grpc,
+        compile_deps = ctx.attr.rust_deps,
+        toolchain = toolchain,
+    )
+
+def _rust_proto_library_impl(ctx):
+    """The implementation of the `rust_proto_library` rule
+
+    Args:
+        ctx (ctx): The rule's context object.
+
+    Returns:
+        list: A list of providers, see `_rust_protogrpc_library_impl`
+    """
+    return _rust_protogrpc_library_impl(ctx, False)
+
+rust_proto_library = rule(
+    implementation = _rust_proto_library_impl,
+    attrs = {
+        "deps": attr.label_list(
+            doc = (
+                "List of proto_library dependencies that will be built. " +
+                "One crate for each proto_library will be created with the corresponding stubs."
+            ),
+            mandatory = True,
+            providers = [ProtoInfo],
+            aspects = [_rust_proto_aspect],
+        ),
+        "rust_deps": attr.label_list(
+            doc = "The crates the generated library depends on.",
+        ),
+        "_cc_toolchain": attr.label(
+            default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
+        ),
+        "_optional_output_wrapper": attr.label(
+            executable = True,
+            cfg = "exec",
+            default = Label("//proto:optional_output_wrapper"),
+        ),
+        "_process_wrapper": attr.label(
+            default = Label("//util/process_wrapper"),
+            executable = True,
+            allow_single_file = True,
+            cfg = "exec",
+        ),
+    },
+    fragments = ["cpp"],
+    host_fragments = ["cpp"],
+    toolchains = [
+        str(Label("//proto:toolchain")),
+        str(Label("//rust:toolchain")),
+        "@bazel_tools//tools/cpp:toolchain_type",
+    ],
+    # TODO: Remove once (bazelbuild/bazel#11584) is closed and the rules use
+    # the version of Bazel that issue was closed on as the min supported version
+    incompatible_use_toolchain_transition = True,
+    doc = """\
+Builds a Rust library crate from a set of `proto_library`s.
+
+Example:
+
+```python
+load("@rules_rust//proto:proto.bzl", "rust_proto_library")
+
+proto_library(
+    name = "my_proto",
+    srcs = ["my.proto"]
+)
+
+rust_proto_library(
+    name = "rust",
+    deps = [":my_proto"],
+)
+
+rust_binary(
+    name = "my_proto_binary",
+    srcs = ["my_proto_binary.rs"],
+    deps = [":rust"],
+)
+```
+""",
+)
+
+def _rust_grpc_library_impl(ctx):
+    """The implementation of the `rust_grpc_library` rule
+
+    Args:
+        ctx (ctx): The rule's context object
+
+    Returns:
+        list: A list of providers. See `_rust_protogrpc_library_impl`
+    """
+    return _rust_protogrpc_library_impl(ctx, True)
+
+rust_grpc_library = rule(
+    implementation = _rust_grpc_library_impl,
+    attrs = {
+        "deps": attr.label_list(
+            doc = (
+                "List of proto_library dependencies that will be built. " +
+                "One crate for each proto_library will be created with the corresponding gRPC stubs."
+            ),
+            mandatory = True,
+            providers = [ProtoInfo],
+            aspects = [_rust_proto_aspect],
+        ),
+        "rust_deps": attr.label_list(
+            doc = "The crates the generated library depends on.",
+        ),
+        "_cc_toolchain": attr.label(
+            default = "@bazel_tools//tools/cpp:current_cc_toolchain",
+        ),
+        "_optional_output_wrapper": attr.label(
+            executable = True,
+            cfg = "exec",
+            default = Label("//proto:optional_output_wrapper"),
+        ),
+        "_process_wrapper": attr.label(
+            default = Label("//util/process_wrapper"),
+            executable = True,
+            allow_single_file = True,
+            cfg = "exec",
+        ),
+    },
+    fragments = ["cpp"],
+    host_fragments = ["cpp"],
+    toolchains = [
+        str(Label("//proto:toolchain")),
+        str(Label("//rust:toolchain")),
+        "@bazel_tools//tools/cpp:toolchain_type",
+    ],
+    # TODO: Remove once (bazelbuild/bazel#11584) is closed and the rules use
+    # the version of Bazel that issue was closed on as the min supported version
+    incompatible_use_toolchain_transition = True,
+    doc = """\
+Builds a Rust library crate from a set of `proto_library`s suitable for gRPC.
+
+Example:
+
+```python
+load("//proto:proto.bzl", "rust_grpc_library")
+
+proto_library(
+    name = "my_proto",
+    srcs = ["my.proto"]
+)
+
+rust_grpc_library(
+    name = "rust",
+    deps = [":my_proto"],
+)
+
+rust_binary(
+    name = "my_service",
+    srcs = ["my_service.rs"],
+    deps = [":rust"],
+)
+```
+""",
+)