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/crate_universe/private/BUILD.bazel b/crate_universe/private/BUILD.bazel
new file mode 100644
index 0000000..39f0969
--- /dev/null
+++ b/crate_universe/private/BUILD.bazel
@@ -0,0 +1,24 @@
+load(":bootstrap_utils.bzl", "srcs_module")
+
+package(default_visibility = ["//:__subpackages__"])
+
+exports_files(glob(["**"]))
+
+srcs_module(
+ name = "srcs_module",
+ srcs = "//crate_universe:rust_srcs",
+ dest = ":srcs.bzl",
+)
+
+filegroup(
+ name = "bzl_srcs",
+ srcs = glob(["*.bzl"]),
+)
+
+filegroup(
+ name = "distro",
+ srcs = glob(["*.bzl"]) + [
+ "BUILD.bazel",
+ "//crate_universe/private/vendor:distro",
+ ],
+)
diff --git a/crate_universe/private/bootstrap_utils.bzl b/crate_universe/private/bootstrap_utils.bzl
new file mode 100644
index 0000000..9df387e
--- /dev/null
+++ b/crate_universe/private/bootstrap_utils.bzl
@@ -0,0 +1,106 @@
+"""Utilities directly related to bootstrapping `cargo-bazel`"""
+
+_SRCS_TEMPLATE = """\
+\"\"\"A generate file containing all source files used to produce `cargo-bazel`\"\"\"
+
+# Each source file is tracked as a target so the `cargo_bootstrap_repository`
+# rule will know to automatically rebuild if any of the sources changed.
+CARGO_BAZEL_SRCS = [
+ {srcs}
+]
+"""
+
+def _srcs_module_impl(ctx):
+ srcs = ["@rules_rust{}".format(src.owner) for src in ctx.files.srcs]
+ if not srcs:
+ fail("`srcs` cannot be empty")
+ output = ctx.actions.declare_file(ctx.label.name)
+
+ ctx.actions.write(
+ output = output,
+ content = _SRCS_TEMPLATE.format(
+ srcs = "\n ".join(["\"{}\",".format(src) for src in srcs]),
+ ),
+ )
+
+ return DefaultInfo(
+ files = depset([output]),
+ )
+
+_srcs_module = rule(
+ doc = "A rule for writing a list of sources to a templated file",
+ implementation = _srcs_module_impl,
+ attrs = {
+ "srcs": attr.label(
+ doc = "A filegroup of source files",
+ allow_files = True,
+ ),
+ },
+)
+
+_INSTALLER_TEMPLATE = """\
+#!/bin/bash
+set -euo pipefail
+cp -f "{path}" "${{BUILD_WORKSPACE_DIRECTORY}}/{dest}"
+"""
+
+def _srcs_installer_impl(ctx):
+ output = ctx.actions.declare_file(ctx.label.name + ".sh")
+ target_file = ctx.file.input
+ dest = ctx.file.dest.short_path
+
+ ctx.actions.write(
+ output = output,
+ content = _INSTALLER_TEMPLATE.format(
+ path = target_file.short_path,
+ dest = dest,
+ ),
+ is_executable = True,
+ )
+
+ return DefaultInfo(
+ files = depset([output]),
+ runfiles = ctx.runfiles(files = [target_file]),
+ executable = output,
+ )
+
+_srcs_installer = rule(
+ doc = "A rule for writing a file back to the repository",
+ implementation = _srcs_installer_impl,
+ attrs = {
+ "dest": attr.label(
+ doc = "the file name to use for installation",
+ allow_single_file = True,
+ mandatory = True,
+ ),
+ "input": attr.label(
+ doc = "The file to write back to the repository",
+ allow_single_file = True,
+ mandatory = True,
+ ),
+ },
+ executable = True,
+)
+
+def srcs_module(name, dest, **kwargs):
+ """A helper rule to ensure the bootstrapping functionality of `cargo-bazel` is always up to date
+
+ Args:
+ name (str): The name of the sources module
+ dest (str): The filename the module should be written as in the current package.
+ **kwargs (dict): Additional keyword arguments
+ """
+ tags = kwargs.pop("tags", [])
+
+ _srcs_module(
+ name = name,
+ tags = tags,
+ **kwargs
+ )
+
+ _srcs_installer(
+ name = name + ".install",
+ input = name,
+ dest = dest,
+ tags = tags,
+ )
diff --git a/crate_universe/private/common_utils.bzl b/crate_universe/private/common_utils.bzl
new file mode 100644
index 0000000..ae65b63
--- /dev/null
+++ b/crate_universe/private/common_utils.bzl
@@ -0,0 +1,109 @@
+"""Common utilities useful for unifying the behavior of different parts of `cargo-bazel`."""
+
+# buildifier: disable=bzl-visibility
+load(
+ "//cargo/private:cargo_utils.bzl",
+ _get_host_triple = "get_host_triple",
+ _rust_get_rust_tools = "get_rust_tools",
+)
+
+get_host_triple = _get_host_triple
+
+CARGO_BAZEL_ISOLATED = "CARGO_BAZEL_ISOLATED"
+
+_EXECUTE_ERROR_MESSAGE = """\
+Command {args} failed with exit code {exit_code}.
+STDOUT ------------------------------------------------------------------------
+{stdout}
+STDERR ------------------------------------------------------------------------
+{stderr}
+"""
+
+def execute(repository_ctx, args, env = {}):
+ """A heler macro for executing some arguments and displaying nicely formatted errors
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+ args (list): A list of strings which act as `argv` for execution.
+ env (dict, optional): Environment variables to set in the execution environment.
+
+ Returns:
+ struct: The results of `repository_ctx.execute`
+ """
+ result = repository_ctx.execute(
+ args,
+ environment = env,
+ quiet = repository_ctx.attr.quiet,
+ )
+
+ if result.return_code:
+ fail(_EXECUTE_ERROR_MESSAGE.format(
+ args = args,
+ exit_code = result.return_code,
+ stdout = result.stdout,
+ stderr = result.stderr,
+ ))
+
+ return result
+
+def get_rust_tools(repository_ctx, host_triple):
+ """Retrieve a cargo and rustc binary based on the host triple.
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+ host_triple (struct): A `@rules_rust//rust:triple.bzl%triple` object.
+
+ Returns:
+ struct: A struct containing the expected rust tools
+ """
+
+ # This is a bit hidden but to ensure Cargo behaves consistently based
+ # on the user provided config file, the config file is installed into
+ # the `CARGO_HOME` path. This is done so here since fetching tools
+ # is expected to always occur before any subcommands are run.
+ if repository_ctx.attr.isolated and repository_ctx.attr.cargo_config:
+ cargo_home = _cargo_home_path(repository_ctx)
+ cargo_home_config = repository_ctx.path("{}/config.toml".format(cargo_home))
+ cargo_config = repository_ctx.path(repository_ctx.attr.cargo_config)
+ repository_ctx.symlink(cargo_config, cargo_home_config)
+
+ return _rust_get_rust_tools(
+ cargo_template = repository_ctx.attr.rust_toolchain_cargo_template,
+ rustc_template = repository_ctx.attr.rust_toolchain_rustc_template,
+ host_triple = host_triple,
+ version = repository_ctx.attr.rust_version,
+ )
+
+def _cargo_home_path(repository_ctx):
+ """Define a path within the repository to use in place of `CARGO_HOME`
+
+ Args:
+ repository_ctx (repository_ctx): The rules context object
+
+ Returns:
+ path: The path to a directory to use as `CARGO_HOME`
+ """
+ return repository_ctx.path(".cargo_home")
+
+def cargo_environ(repository_ctx):
+ """Define Cargo environment varables for use with `cargo-bazel`
+
+ Args:
+ repository_ctx (repository_ctx): The rules context object
+
+ Returns:
+ dict: A set of environment variables for `cargo-bazel` executions
+ """
+ env = dict()
+
+ if CARGO_BAZEL_ISOLATED in repository_ctx.os.environ:
+ if repository_ctx.os.environ[CARGO_BAZEL_ISOLATED].lower() in ["true", "1", "yes", "on"]:
+ env.update({
+ "CARGO_HOME": str(_cargo_home_path(repository_ctx)),
+ })
+ elif repository_ctx.attr.isolated:
+ env.update({
+ "CARGO_HOME": str(_cargo_home_path(repository_ctx)),
+ })
+
+ return env
diff --git a/crate_universe/private/crate.bzl b/crate_universe/private/crate.bzl
new file mode 100644
index 0000000..611bf62
--- /dev/null
+++ b/crate_universe/private/crate.bzl
@@ -0,0 +1,172 @@
+"""Macros used for represeting crates or annotations for existing crates"""
+
+def _workspace_member(version, sha256 = None):
+ """Define information for extra workspace members
+
+ Args:
+ version (str): The semver of the crate to download. Must be an exact version.
+ sha256 (str, optional): The sha256 checksum of the `.crate` file.
+
+ Returns:
+ string: A json encoded string of all inputs
+ """
+ return json.encode(struct(
+ version = version,
+ sha256 = sha256,
+ ))
+
+def _spec(
+ package = None,
+ version = None,
+ default_features = True,
+ features = [],
+ git = None,
+ rev = None):
+ """A constructor for a crate dependency.
+
+ See [specifying dependencies][sd] in the Cargo book for more details.
+
+ [sd]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
+
+ Args:
+ package (str, optional): The explicit name of the package (used when attempting to alias a crate).
+ version (str, optional): The exact version of the crate. Cannot be used with `git`.
+ default_features (bool, optional): Maps to the `default-features` flag.
+ features (list, optional): A list of features to use for the crate
+ git (str, optional): The Git url to use for the crate. Cannot be used with `version`.
+ rev (str, optional): The git revision of the remote crate. Tied with the `git` param.
+
+ Returns:
+ string: A json encoded string of all inputs
+ """
+ return json.encode(struct(
+ package = package,
+ default_features = default_features,
+ features = features,
+ version = version,
+ git = git,
+ rev = rev,
+ ))
+
+def _assert_absolute(label):
+ """Ensure a given label is an absolute label
+
+ Args:
+ label (Label): The label to check
+ """
+ label_str = str(label)
+ if not label.startswith("@"):
+ fail("The labels must be absolute. Please update '{}'".format(
+ label_str,
+ ))
+
+def _annotation(
+ version = "*",
+ additive_build_file = None,
+ additive_build_file_content = None,
+ build_script_data = None,
+ build_script_data_glob = None,
+ build_script_deps = None,
+ build_script_env = None,
+ build_script_proc_macro_deps = None,
+ build_script_rustc_env = None,
+ compile_data = None,
+ compile_data_glob = None,
+ crate_features = None,
+ data = None,
+ data_glob = None,
+ deps = None,
+ gen_build_script = None,
+ patch_args = None,
+ patch_tool = None,
+ patches = None,
+ proc_macro_deps = None,
+ rustc_env = None,
+ rustc_env_files = None,
+ rustc_flags = None,
+ shallow_since = None):
+ """A collection of extra attributes and settings for a particular crate
+
+ Args:
+ version (str, optional): The version or semver-conditions to match with a crate.
+ additive_build_file_content (str, optional): Extra contents to write to the bottom of generated BUILD files.
+ additive_build_file (str, optional): A file containing extra contents to write to the bottom of
+ generated BUILD files.
+ build_script_data (list, optional): A list of labels to add to a crate's `cargo_build_script::data` attribute.
+ build_script_data_glob (list, optional): A list of glob patterns to add to a crate's `cargo_build_script::data`
+ attribute.
+ build_script_deps (list, optional): A list of labels to add to a crate's `cargo_build_script::deps` attribute.
+ build_script_env (dict, optional): Additional environment variables to set on a crate's
+ `cargo_build_script::env` attribute.
+ build_script_proc_macro_deps (list, optional): A list of labels to add to a crate's
+ `cargo_build_script::proc_macro_deps` attribute.
+ build_script_rustc_env (dict, optional): Additional environment variables to set on a crate's
+ `cargo_build_script::env` attribute.
+ compile_data (list, optional): A list of labels to add to a crate's `rust_library::compile_data` attribute.
+ compile_data_glob (list, optional): A list of glob patterns to add to a crate's `rust_library::compile_data`
+ attribute.
+ crate_features (list, optional): A list of strings to add to a crate's `rust_library::crate_features`
+ attribute.
+ data (list, optional): A list of labels to add to a crate's `rust_library::data` attribute.
+ data_glob (list, optional): A list of glob patterns to add to a crate's `rust_library::data` attribute.
+ deps (list, optional): A list of labels to add to a crate's `rust_library::deps` attribute.
+ gen_build_script (bool, optional): An authorative flag to determine whether or not to produce
+ `cargo_build_script` targets for the current crate.
+ patch_args (list, optional): The `patch_args` attribute of a Bazel repository rule. See
+ [http_archive.patch_args](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_args)
+ patch_tool (list, optional): The `patch_tool` attribute of a Bazel repository rule. See
+ [http_archive.patch_tool](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_tool)
+ patches (list, optional): The `patches` attribute of a Bazel repository rule. See
+ [http_archive.patches](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patches)
+ proc_macro_deps (list, optional): A list of labels to add to a crate's `rust_library::proc_macro_deps`
+ attribute.
+ rustc_env (dict, optional): Additional variables to set on a crate's `rust_library::rustc_env` attribute.
+ rustc_env_files (list, optional): A list of labels to set on a crate's `rust_library::rustc_env_files`
+ attribute.
+ rustc_flags (list, optional): A list of strings to set on a crate's `rust_library::rustc_flags` attribute.
+ shallow_since (str, optional): An optional timestamp used for crates originating from a git repository
+ instead of a crate registry. This flag optimizes fetching the source code.
+
+ Returns:
+ string: A json encoded string containing the specified version and separately all other inputs.
+ """
+ if additive_build_file:
+ _assert_absolute(additive_build_file)
+ if patches:
+ for patch in patches:
+ _assert_absolute(patch)
+
+ return json.encode((
+ version,
+ struct(
+ additive_build_file = additive_build_file,
+ additive_build_file_content = additive_build_file_content,
+ build_script_data = build_script_data,
+ build_script_data_glob = build_script_data_glob,
+ build_script_deps = build_script_deps,
+ build_script_env = build_script_env,
+ build_script_proc_macro_deps = build_script_proc_macro_deps,
+ build_script_rustc_env = build_script_rustc_env,
+ compile_data = compile_data,
+ compile_data_glob = compile_data_glob,
+ crate_features = crate_features,
+ data = data,
+ data_glob = data_glob,
+ deps = deps,
+ gen_build_script = gen_build_script,
+ patch_args = patch_args,
+ patch_tool = patch_tool,
+ patches = patches,
+ proc_macro_deps = proc_macro_deps,
+ rustc_env = rustc_env,
+ rustc_env_files = rustc_env_files,
+ rustc_flags = rustc_flags,
+ shallow_since = shallow_since,
+ ),
+ ))
+
+crate = struct(
+ spec = _spec,
+ annotation = _annotation,
+ workspace_member = _workspace_member,
+)
diff --git a/crate_universe/private/crates_repository.bzl b/crate_universe/private/crates_repository.bzl
new file mode 100644
index 0000000..92def09
--- /dev/null
+++ b/crate_universe/private/crates_repository.bzl
@@ -0,0 +1,244 @@
+"""`crates_repository` rule implementation"""
+
+load("//crate_universe/private:common_utils.bzl", "get_host_triple", "get_rust_tools")
+load(
+ "//crate_universe/private:generate_utils.bzl",
+ "CRATES_REPOSITORY_ENVIRON",
+ "determine_repin",
+ "execute_generator",
+ "generate_config",
+ "get_generator",
+ "get_lockfile",
+)
+load(
+ "//crate_universe/private:splicing_utils.bzl",
+ "create_splicing_manifest",
+ "splice_workspace_manifest",
+)
+load("//crate_universe/private:urls.bzl", "CARGO_BAZEL_SHA256S", "CARGO_BAZEL_URLS")
+load("//rust:defs.bzl", "rust_common")
+load("//rust/platform:triple_mappings.bzl", "SUPPORTED_PLATFORM_TRIPLES")
+
+def _crates_repository_impl(repository_ctx):
+ # Determine the current host's platform triple
+ host_triple = get_host_triple(repository_ctx)
+
+ # Locate the generator to use
+ generator, generator_sha256 = get_generator(repository_ctx, host_triple.triple)
+
+ # Generate a config file for all settings
+ config = generate_config(repository_ctx)
+
+ # Locate the lockfile
+ lockfile = get_lockfile(repository_ctx)
+
+ # Locate Rust tools (cargo, rustc)
+ tools = get_rust_tools(repository_ctx, host_triple)
+ cargo_path = repository_ctx.path(tools.cargo)
+ rustc_path = repository_ctx.path(tools.rustc)
+
+ # Create a manifest of all dependency inputs
+ splicing_manifest = create_splicing_manifest(repository_ctx)
+
+ # Determine whether or not to repin depednencies
+ repin = determine_repin(
+ repository_ctx = repository_ctx,
+ generator = generator,
+ lockfile_path = lockfile.path,
+ lockfile_kind = lockfile.kind,
+ config = config.path,
+ splicing_manifest = splicing_manifest,
+ cargo = cargo_path,
+ rustc = rustc_path,
+ )
+
+ # If re-pinning is enabled, gather additional inputs for the generator
+ kwargs = dict()
+ if repin or lockfile.kind == "cargo":
+ # Generate a top level Cargo workspace and manifest for use in generation
+ metadata_path = splice_workspace_manifest(
+ repository_ctx = repository_ctx,
+ generator = generator,
+ lockfile = lockfile,
+ splicing_manifest = splicing_manifest,
+ cargo = cargo_path,
+ rustc = rustc_path,
+ )
+
+ kwargs.update({
+ "metadata": metadata_path,
+ "repin": True,
+ })
+
+ # Run the generator
+ execute_generator(
+ repository_ctx = repository_ctx,
+ generator = generator,
+ config = config.path,
+ splicing_manifest = splicing_manifest,
+ lockfile_path = lockfile.path,
+ lockfile_kind = lockfile.kind,
+ repository_dir = repository_ctx.path("."),
+ cargo = cargo_path,
+ rustc = rustc_path,
+ # sysroot = tools.sysroot,
+ **kwargs
+ )
+
+ # Determine the set of reproducible values
+ attrs = {attr: getattr(repository_ctx.attr, attr) for attr in dir(repository_ctx.attr)}
+ exclude = ["to_json", "to_proto"]
+ for attr in exclude:
+ attrs.pop(attr, None)
+
+ # Note that this is only scoped to the current host platform. Users should
+ # ensure they provide all the values necessary for the host environments
+ # they support
+ if generator_sha256:
+ attrs.update({"generator_sha256s": generator_sha256})
+
+ return attrs
+
+crates_repository = repository_rule(
+ doc = """\
+A rule for defining and downloading Rust dependencies (crates).
+
+Environment Variables:
+
+| variable | usage |
+| --- | --- |
+| `CARGO_BAZEL_GENERATOR_SHA256` | The sha256 checksum of the file located at `CARGO_BAZEL_GENERATOR_URL` |
+| `CARGO_BAZEL_GENERATOR_URL` | The URL of a cargo-bazel binary. This variable takes precedence over attributes and can use `file://` for local paths |
+| `CARGO_BAZEL_ISOLATED` | An authorative flag as to whether or not the `CARGO_HOME` environment variable should be isolated from the host configuration |
+| `CARGO_BAZEL_REPIN` | An indicator that the dependencies represented by the rule should be regenerated. `REPIN` may also be used. |
+
+""",
+ implementation = _crates_repository_impl,
+ attrs = {
+ "annotations": attr.string_list_dict(
+ doc = "Extra settings to apply to crates. See [crate.annotations](#crateannotations).",
+ ),
+ "cargo_config": attr.label(
+ doc = "A [Cargo configuration](https://doc.rust-lang.org/cargo/reference/config.html) file",
+ ),
+ "extra_workspace_member_url_template": attr.string(
+ doc = "The registry url to use when fetching extra workspace members",
+ default = "https://crates.io/api/v1/crates/{name}/{version}/download",
+ ),
+ "extra_workspace_members": attr.string_dict(
+ doc = (
+ "Additional crates to download and include as a workspace member. This is unfortunately required in " +
+ "order to add information about \"binary-only\" crates so that a `rust_binary` may be generated for " +
+ "it. [rust-lang/cargo#9096](https://github.com/rust-lang/cargo/issues/9096) tracks an RFC which may " +
+ "solve for this."
+ ),
+ ),
+ "generate_build_scripts": attr.bool(
+ doc = (
+ "Whether or not to generate " +
+ "[cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) by default."
+ ),
+ default = True,
+ ),
+ "generator": attr.string(
+ doc = (
+ "The absolute label of a generator. Eg. `@cargo_bazel_bootstrap//:cargo-bazel`. " +
+ "This is typically used when bootstrapping"
+ ),
+ ),
+ "generator_sha256s": attr.string_dict(
+ doc = "Dictionary of `host_triple` -> `sha256` for a `cargo-bazel` binary.",
+ default = CARGO_BAZEL_SHA256S,
+ ),
+ "generator_urls": attr.string_dict(
+ doc = (
+ "URL template from which to download the `cargo-bazel` binary. `{host_triple}` and will be " +
+ "filled in according to the host platform."
+ ),
+ default = CARGO_BAZEL_URLS,
+ ),
+ "isolated": attr.bool(
+ doc = (
+ "If true, `CARGO_HOME` will be overwritten to a directory within the generated repository in " +
+ "order to prevent other uses of Cargo from impacting having any effect on the generated targets " +
+ "produced by this rule. For users who either have multiple `crate_repository` definitions in a " +
+ "WORKSPACE or rapidly re-pin dependencies, setting this to false may improve build times. This " +
+ "variable is also controled by `CARGO_BAZEL_ISOLATED` environment variable."
+ ),
+ default = True,
+ ),
+ "lockfile": attr.label(
+ doc = (
+ "The path to a file to use for reproducible renderings. Two kinds of lock files are supported, " +
+ "Cargo (`Cargo.lock` files) and Bazel (custom files generated by this rule, naming is irrelevant). " +
+ "Bazel lockfiles should be the prefered kind as they're desigend with Bazel's notions of " +
+ "reporducibility in mind. Cargo lockfiles can be used in cases where it's intended to be the " +
+ "source of truth, but more work will need to be done to generate BUILD files which are not " +
+ "guaranteed to be determinsitic."
+ ),
+ mandatory = True,
+ ),
+ "lockfile_kind": attr.string(
+ doc = (
+ "Two different kinds of lockfiles are supported, the custom \"Bazel\" lockfile, which is generated " +
+ "by this rule, and Cargo lockfiles (`Cargo.lock`). This attribute allows for explicitly defining " +
+ "the type in cases where it may not be auto-detectable."
+ ),
+ values = [
+ "auto",
+ "bazel",
+ "cargo",
+ ],
+ default = "auto",
+ ),
+ "manifests": attr.label_list(
+ doc = "A list of Cargo manifests (`Cargo.toml` files).",
+ ),
+ "packages": attr.string_dict(
+ doc = "A set of crates (packages) specifications to depend on. See [crate.spec](#crate.spec).",
+ ),
+ "quiet": attr.bool(
+ doc = "If stdout and stderr should not be printed to the terminal.",
+ default = True,
+ ),
+ "render_config": attr.string(
+ doc = (
+ "The configuration flags to use for rendering. Use `//crate_universe:defs.bzl\\%render_config` to " +
+ "generate the value for this field. If unset, the defaults defined there will be used."
+ ),
+ ),
+ "rust_toolchain_cargo_template": attr.string(
+ doc = (
+ "The template to use for finding the host `cargo` binary. `{version}` (eg. '1.53.0'), " +
+ "`{triple}` (eg. 'x86_64-unknown-linux-gnu'), `{arch}` (eg. 'aarch64'), `{vendor}` (eg. 'unknown'), " +
+ "`{system}` (eg. 'darwin'), `{cfg}` (eg. 'exec'), and `{tool}` (eg. 'rustc.exe') will be replaced in " +
+ "the string if present."
+ ),
+ default = "@rust_{system}_{arch}//:bin/{tool}",
+ ),
+ "rust_toolchain_rustc_template": attr.string(
+ doc = (
+ "The template to use for finding the host `rustc` binary. `{version}` (eg. '1.53.0'), " +
+ "`{triple}` (eg. 'x86_64-unknown-linux-gnu'), `{arch}` (eg. 'aarch64'), `{vendor}` (eg. 'unknown'), " +
+ "`{system}` (eg. 'darwin'), `{cfg}` (eg. 'exec'), and `{tool}` (eg. 'cargo.exe') will be replaced in " +
+ "the string if present."
+ ),
+ default = "@rust_{system}_{arch}//:bin/{tool}",
+ ),
+ "rust_version": attr.string(
+ doc = "The version of Rust the currently registered toolchain is using. Eg. `1.56.0`, or `nightly-2021-09-08`",
+ default = rust_common.default_version,
+ ),
+ "splicing_config": attr.string(
+ doc = (
+ "The configuration flags to use for splicing Cargo maniests. Use `//crate_universe:defs.bzl\\%rsplicing_config` to " +
+ "generate the value for this field. If unset, the defaults defined there will be used."
+ ),
+ ),
+ "supported_platform_triples": attr.string_list(
+ doc = "A set of all platform triples to consider when generating dependencies.",
+ default = SUPPORTED_PLATFORM_TRIPLES,
+ ),
+ },
+ environ = CRATES_REPOSITORY_ENVIRON,
+)
diff --git a/crate_universe/private/crates_vendor.bzl b/crate_universe/private/crates_vendor.bzl
new file mode 100644
index 0000000..11cfaa1
--- /dev/null
+++ b/crate_universe/private/crates_vendor.bzl
@@ -0,0 +1,335 @@
+"""Rules for vendoring Bazel targets into existing workspaces"""
+
+load("//crate_universe/private:generate_utils.bzl", "collect_crate_annotations", "render_config")
+load("//crate_universe/private:splicing_utils.bzl", "kebab_case_keys", "splicing_config")
+load("//crate_universe/private:urls.bzl", "CARGO_BAZEL_LABEL")
+load("//rust/platform:triple_mappings.bzl", "SUPPORTED_PLATFORM_TRIPLES")
+
+_UNIX_WRAPPER = """\
+#!/usr/bin/env bash
+set -euo pipefail
+export RUNTIME_PWD="$(pwd)"
+eval exec env - BUILD_WORKSPACE_DIRECTORY="${{BUILD_WORKSPACE_DIRECTORY}}" {env} \\
+"{bin}" {args} "$@"
+"""
+
+_WINDOWS_WRAPPER = """\
+@ECHO OFF
+set RUNTIME_PWD=%CD%
+{env}
+
+call {bin} {args} %@%
+"""
+
+CARGO_BAZEL_GENERATOR_PATH = "CARGO_BAZEL_GENERATOR_PATH"
+
+def _runfiles_path(path, is_windows):
+ if is_windows:
+ runtime_pwd_var = "%RUNTIME_PWD%"
+ else:
+ runtime_pwd_var = "${RUNTIME_PWD}"
+ if path.startswith("../"):
+ return "{}/external/{}".format(runtime_pwd_var, path[len("../"):])
+ return "{}/{}".format(runtime_pwd_var, path)
+
+def _is_windows(ctx):
+ toolchain = ctx.toolchains[Label("@rules_rust//rust:toolchain")]
+ return "windows" in toolchain.target_triple
+
+def _get_output_package(ctx):
+ # Determine output directory
+ if ctx.attr.vendor_path.startswith("/"):
+ output = ctx.attr.vendor_path
+ else:
+ output = "{}/{}".format(
+ ctx.label.package,
+ ctx.attr.vendor_path,
+ )
+ return output
+
+def _write_data_file(ctx, name, data):
+ file = ctx.actions.declare_file("{}.{}".format(ctx.label.name, name))
+ ctx.actions.write(
+ output = file,
+ content = data,
+ )
+ return file
+
+def _write_splicing_manifest(ctx):
+ # Deserialize information about direct packges
+ direct_packages_info = {
+ # Ensure the data is using kebab-case as that's what `cargo_toml::DependencyDetail` expects.
+ pkg: kebab_case_keys(dict(json.decode(data)))
+ for (pkg, data) in ctx.attr.packages.items()
+ }
+
+ # Manifests are required to be single files
+ manifests = {m[DefaultInfo].files.to_list()[0].short_path: str(m.label) for m in ctx.attr.manifests}
+
+ config = json.decode(ctx.attr.splicing_config or splicing_config())
+ splicing_manifest_content = {
+ # TODO: How do cargo config files get factored into vendored builds
+ "cargo_config": None,
+ "direct_packages": direct_packages_info,
+ "manifests": manifests,
+ }
+
+ manifest = _write_data_file(
+ ctx = ctx,
+ name = "cargo-bazel-splicing-manifest.json",
+ data = json.encode_indent(
+ dict(dict(config).items() + splicing_manifest_content.items()),
+ indent = " " * 4,
+ ),
+ )
+
+ is_windows = _is_windows(ctx)
+
+ args = ["--splicing-manifest", _runfiles_path(manifest.short_path, is_windows)]
+ runfiles = [manifest]
+ return args, runfiles
+
+def _write_extra_manifests_manifest(ctx):
+ manifest = _write_data_file(
+ ctx = ctx,
+ name = "cargo-bazel-extra-manifests-manifest.json",
+ data = json.encode(struct(
+ # TODO: This is for extra workspace members
+ manifests = [],
+ )),
+ )
+ is_windows = _is_windows(ctx)
+ args = ["--extra-manifests-manifest", _runfiles_path(manifest.short_path, is_windows)]
+ runfiles = [manifest]
+ return args, runfiles
+
+def _write_config_file(ctx):
+ annotations = collect_crate_annotations(ctx.attr.annotations, str(ctx.label))
+ unexpected = []
+ for id, annotation in annotations.items():
+ if annotation.get("additive_build_file", None):
+ unexpected.append(id)
+ if unexpected:
+ fail("The following annotations use `additive_build_file` which is not supported for `crates_vendor`: {}".format(unexpected))
+
+ rendering_config = dict(json.decode(render_config()))
+
+ output_pkg = _get_output_package(ctx)
+
+ if ctx.attr.mode == "local":
+ build_file_base_template = "@{}//{}/{{name}}-{{version}}:BUILD.bazel"
+ crate_label_template = "//{}/{{name}}-{{version}}:{{target}}".format(
+ output_pkg,
+ )
+ else:
+ build_file_base_template = "@{}//{}:BUILD.{{name}}-{{version}}.bazel"
+ crate_label_template = rendering_config["crate_label_template"]
+
+ rendering_config.update({
+ "build_file_template": build_file_base_template.format(
+ ctx.workspace_name,
+ output_pkg,
+ ),
+ "crate_label_template": crate_label_template,
+ "crates_module_template": "@{}//{}:{{file}}".format(
+ ctx.workspace_name,
+ output_pkg,
+ ),
+ "repository_name": ctx.attr.repository_name or ctx.label.name,
+ "vendor_mode": ctx.attr.mode,
+ })
+
+ config_data = struct(
+ annotations = annotations,
+ rendering = rendering_config,
+ generate_build_scripts = ctx.attr.generate_build_scripts,
+ cargo_config = None,
+ supported_platform_triples = ctx.attr.supported_platform_triples,
+ )
+
+ config = _write_data_file(
+ ctx = ctx,
+ name = "cargo-bazel-config.json",
+ data = json.encode_indent(
+ config_data,
+ indent = " " * 4,
+ ),
+ )
+
+ is_windows = _is_windows(ctx)
+ args = ["--config", _runfiles_path(config.short_path, is_windows)]
+ runfiles = [config] + ctx.files.manifests
+ return args, runfiles
+
+def _crates_vendor_impl(ctx):
+ toolchain = ctx.toolchains[Label("@rules_rust//rust:toolchain")]
+ is_windows = _is_windows(ctx)
+
+ environ = {
+ "CARGO": _runfiles_path(toolchain.cargo.short_path, is_windows),
+ "RUSTC": _runfiles_path(toolchain.rustc.short_path, is_windows),
+ }
+
+ args = ["vendor"]
+
+ cargo_bazel_runfiles = []
+
+ # Allow action envs to override the use of the cargo-bazel target.
+ if CARGO_BAZEL_GENERATOR_PATH in ctx.var:
+ bin_path = ctx.var[CARGO_BAZEL_GENERATOR_PATH]
+ elif ctx.executable.cargo_bazel:
+ bin_path = _runfiles_path(ctx.executable.cargo_bazel.short_path, is_windows)
+ cargo_bazel_runfiles.append(ctx.executable.cargo_bazel)
+ else:
+ fail("{} is missing either the `cargo_bazel` attribute or the '{}' action env".format(
+ ctx.label,
+ CARGO_BAZEL_GENERATOR_PATH,
+ ))
+
+ # Generate config file
+ config_args, config_runfiles = _write_config_file(ctx)
+ args.extend(config_args)
+ cargo_bazel_runfiles.extend(config_runfiles)
+
+ # Generate splicing manifest
+ splicing_manifest_args, splicing_manifest_runfiles = _write_splicing_manifest(ctx)
+ args.extend(splicing_manifest_args)
+ cargo_bazel_runfiles.extend(splicing_manifest_runfiles)
+
+ # Generate extra-manifests manifest
+ extra_manifests_manifest_args, extra_manifests_manifest_runfiles = _write_extra_manifests_manifest(ctx)
+ args.extend(extra_manifests_manifest_args)
+ cargo_bazel_runfiles.extend(extra_manifests_manifest_runfiles)
+
+ # Optionally include buildifier
+ if ctx.attr.buildifier:
+ args.extend(["--buildifier", _runfiles_path(ctx.executable.buildifier.short_path, is_windows)])
+ cargo_bazel_runfiles.append(ctx.executable.buildifier)
+
+ # Dtermine platform specific settings
+ if is_windows:
+ extension = ".bat"
+ template = _WINDOWS_WRAPPER
+ env_template = "\nset {}={}"
+ else:
+ extension = ".sh"
+ template = _UNIX_WRAPPER
+ env_template = "{}={}"
+
+ # Write the wrapper script
+ runner = ctx.actions.declare_file(ctx.label.name + extension)
+ ctx.actions.write(
+ output = runner,
+ content = template.format(
+ env = " ".join([env_template.format(key, val) for key, val in environ.items()]),
+ bin = bin_path,
+ args = " ".join(args),
+ ),
+ is_executable = True,
+ )
+
+ return DefaultInfo(
+ files = depset([runner]),
+ runfiles = ctx.runfiles(
+ files = cargo_bazel_runfiles,
+ transitive_files = toolchain.all_files,
+ ),
+ executable = runner,
+ )
+
+crates_vendor = rule(
+ implementation = _crates_vendor_impl,
+ doc = "A rule for defining Rust dependencies (crates) and writing targets for them to the current workspace",
+ attrs = {
+ "annotations": attr.string_list_dict(
+ doc = "Extra settings to apply to crates. See [crate.annotations](#crateannotations).",
+ ),
+ "buildifier": attr.label(
+ doc = "The path to a [buildifier](https://github.com/bazelbuild/buildtools/blob/5.0.1/buildifier/README.md) binary used to format generated BUILD files.",
+ cfg = "exec",
+ executable = True,
+ default = Label("//crate_universe/private/vendor:buildifier"),
+ ),
+ "cargo_bazel": attr.label(
+ doc = (
+ "The cargo-bazel binary to use for vendoring. If this attribute is not set, then a " +
+ "`{}` action env will be used.".format(CARGO_BAZEL_GENERATOR_PATH)
+ ),
+ cfg = "exec",
+ executable = True,
+ allow_files = True,
+ default = CARGO_BAZEL_LABEL,
+ ),
+ "generate_build_scripts": attr.bool(
+ doc = (
+ "Whether or not to generate " +
+ "[cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) by default."
+ ),
+ default = True,
+ ),
+ "manifests": attr.label_list(
+ doc = "A list of Cargo manifests (`Cargo.toml` files).",
+ allow_files = ["Cargo.toml"],
+ ),
+ "mode": attr.string(
+ doc = (
+ "Flags determining how crates should be vendored. `local` is where crate source and BUILD files are " +
+ "written to the repository. `remote` is where only BUILD files are written and repository rules " +
+ "used to fetch source code."
+ ),
+ values = [
+ "local",
+ "remote",
+ ],
+ default = "remote",
+ ),
+ "packages": attr.string_dict(
+ doc = "A set of crates (packages) specifications to depend on. See [crate.spec](#crate.spec).",
+ ),
+ "repository_name": attr.string(
+ doc = "The name of the repository to generate for `remote` vendor modes. If unset, the label name will be used",
+ ),
+ "splicing_config": attr.string(
+ doc = (
+ "The configuration flags to use for splicing Cargo maniests. Use `//crate_universe:defs.bzl\\%rsplicing_config` to " +
+ "generate the value for this field. If unset, the defaults defined there will be used."
+ ),
+ ),
+ "supported_platform_triples": attr.string_list(
+ doc = "A set of all platform triples to consider when generating dependencies.",
+ default = SUPPORTED_PLATFORM_TRIPLES,
+ ),
+ "vendor_path": attr.string(
+ doc = "The path to a directory to write files into. Absolute paths will be treated as relative to the workspace root",
+ default = "crates",
+ ),
+ },
+ executable = True,
+ toolchains = ["@rules_rust//rust:toolchain"],
+)
+
+def _crates_vendor_remote_repository_impl(repository_ctx):
+ build_file = repository_ctx.path(repository_ctx.attr.build_file)
+ defs_module = repository_ctx.path(repository_ctx.attr.defs_module)
+
+ repository_ctx.file("BUILD.bazel", repository_ctx.read(build_file))
+ repository_ctx.file("defs.bzl", repository_ctx.read(defs_module))
+ repository_ctx.file("crates.bzl", "")
+ repository_ctx.file("WORKSPACE.bazel", """workspace(name = "{}")""".format(
+ repository_ctx.name,
+ ))
+
+crates_vendor_remote_repository = repository_rule(
+ doc = "Creates a repository paired with `crates_vendor` targets using the `remote` vendor mode.",
+ implementation = _crates_vendor_remote_repository_impl,
+ attrs = {
+ "build_file": attr.label(
+ doc = "The BUILD file to use for the root package",
+ mandatory = True,
+ ),
+ "defs_module": attr.label(
+ doc = "The `defs.bzl` file to use in the repository",
+ mandatory = True,
+ ),
+ },
+)
diff --git a/crate_universe/private/generate_utils.bzl b/crate_universe/private/generate_utils.bzl
new file mode 100644
index 0000000..91e8c6e
--- /dev/null
+++ b/crate_universe/private/generate_utils.bzl
@@ -0,0 +1,410 @@
+"""Utilities directly related to the `generate` step of `cargo-bazel`."""
+
+load(":common_utils.bzl", "CARGO_BAZEL_ISOLATED", "cargo_environ", "execute")
+
+CARGO_BAZEL_GENERATOR_SHA256 = "CARGO_BAZEL_GENERATOR_SHA256"
+CARGO_BAZEL_GENERATOR_URL = "CARGO_BAZEL_GENERATOR_URL"
+CARGO_BAZEL_REPIN = "CARGO_BAZEL_REPIN"
+REPIN = "REPIN"
+
+GENERATOR_ENV_VARS = [
+ CARGO_BAZEL_GENERATOR_URL,
+ CARGO_BAZEL_GENERATOR_SHA256,
+]
+
+REPIN_ENV_VARS = [
+ REPIN,
+ CARGO_BAZEL_REPIN,
+]
+
+CRATES_REPOSITORY_ENVIRON = GENERATOR_ENV_VARS + REPIN_ENV_VARS + [
+ CARGO_BAZEL_ISOLATED,
+]
+
+def get_generator(repository_ctx, host_triple):
+ """Query network resources to locate a `cargo-bazel` binary
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+ host_triple (string): A string representing the host triple
+
+ Returns:
+ tuple(path, dict): The path to a `cargo-bazel` binary and the host sha256 pairing.
+ The pairing (dict) may be `None` if there is no need to update the attribute
+ """
+ use_environ = False
+ for var in GENERATOR_ENV_VARS:
+ if var in repository_ctx.os.environ:
+ use_environ = True
+
+ output = repository_ctx.path("cargo-bazel.exe" if "win" in repository_ctx.os.name else "cargo-bazel")
+
+ # The `generator` attribute is the next highest priority behind
+ # environment variables. We check those first before deciding to
+ # use an explicitly provided variable.
+ if not use_environ and repository_ctx.attr.generator:
+ generator = repository_ctx.path(Label(repository_ctx.attr.generator))
+
+ # Resolve a few levels of symlinks to ensure we're accessing the direct binary
+ for _ in range(1, 100):
+ real_generator = generator.realpath
+ if real_generator == generator:
+ break
+ generator = real_generator
+ return generator, None
+
+ # The environment variable will take precedence if set
+ if use_environ:
+ generator_sha256 = repository_ctx.os.environ.get(CARGO_BAZEL_GENERATOR_SHA256)
+ generator_url = repository_ctx.os.environ.get(CARGO_BAZEL_GENERATOR_URL)
+ else:
+ generator_sha256 = repository_ctx.attr.generator_sha256s.get(host_triple)
+ generator_url = repository_ctx.attr.generator_urls.get(host_triple)
+
+ if not generator_url:
+ fail((
+ "No generator URL was found either in the `CARGO_BAZEL_GENERATOR_URL` " +
+ "environment variable or for the `{}` triple in the `generator_urls` attribute"
+ ).format(host_triple))
+
+ # Download the file into place
+ if generator_sha256:
+ repository_ctx.download(
+ output = output,
+ url = generator_url,
+ sha256 = generator_sha256,
+ executable = True,
+ )
+ return output, None
+
+ result = repository_ctx.download(
+ output = output,
+ url = generator_url,
+ executable = True,
+ )
+
+ return output, {host_triple: result.sha256}
+
+def render_config(
+ build_file_template = "//:BUILD.{name}-{version}.bazel",
+ crate_label_template = "@{repository}__{name}-{version}//:{target}",
+ crate_repository_template = "{repository}__{name}-{version}",
+ crates_module_template = "//:{file}",
+ default_package_name = None,
+ platforms_template = "@rules_rust//rust/platform:{triple}",
+ vendor_mode = None):
+ """Various settings used to configure rendered outputs
+
+ The template parameters each support a select number of format keys. A description of each key
+ can be found below where the supported keys for each template can be found in the parameter docs
+
+ | key | definition |
+ | --- | --- |
+ | `name` | The name of the crate. Eg `tokio` |
+ | `repository` | The rendered repository name for the crate. Directly relates to `crate_repository_template`. |
+ | `triple` | A platform triple. Eg `x86_64-unknown-linux-gnu` |
+ | `version` | The crate version. Eg `1.2.3` |
+ | `target` | The library or binary target of the crate |
+ | `file` | The basename of a file |
+
+ Args:
+ build_file_template (str, optional): The base template to use for BUILD file names. The available format keys
+ are [`{name}`, {version}`].
+ crate_label_template (str, optional): The base template to use for crate labels. The available format keys
+ are [`{repository}`, `{name}`, `{version}`, `{target}`].
+ crate_repository_template (str, optional): The base template to use for Crate label repository names. The
+ available format keys are [`{repository}`, `{name}`, `{version}`].
+ crates_module_template (str, optional): The pattern to use for the `defs.bzl` and `BUILD.bazel`
+ file names used for the crates module. The available format keys are [`{file}`].
+ default_package_name (str, optional): The default package name to in the rendered macros. This affects the
+ auto package detection of things like `all_crate_deps`.
+ platforms_template (str, optional): The base template to use for platform names.
+ See [platforms documentation](https://docs.bazel.build/versions/main/platforms.html). The available format
+ keys are [`{triple}`].
+ vendor_mode (str, optional): An optional configuration for rendirng content to be rendered into repositories.
+
+ Returns:
+ string: A json encoded struct to match the Rust `config::RenderConfig` struct
+ """
+ return json.encode(struct(
+ build_file_template = build_file_template,
+ crate_label_template = crate_label_template,
+ crate_repository_template = crate_repository_template,
+ crates_module_template = crates_module_template,
+ default_package_name = default_package_name,
+ platforms_template = platforms_template,
+ vendor_mode = vendor_mode,
+ ))
+
+def _crate_id(name, version):
+ """Creates a `cargo_bazel::config::CrateId`.
+
+ Args:
+ name (str): The name of the crate
+ version (str): The crate's version
+
+ Returns:
+ str: A serialized representation of a CrateId
+ """
+ return "{} {}".format(name, version)
+
+def collect_crate_annotations(annotations, repository_name):
+ """Deserialize and sanitize crate annotations.
+
+ Args:
+ annotations (dict): A mapping of crate names to lists of serialized annotations
+ repository_name (str): The name of the repository that owns the annotations
+
+ Returns:
+ dict: A mapping of `cargo_bazel::config::CrateId` to sets of annotations
+ """
+ annotations = {name: [json.decode(a) for a in annotation] for name, annotation in annotations.items()}
+ crate_annotations = {}
+ for name, annotation in annotations.items():
+ for (version, data) in annotation:
+ if name == "*" and version != "*":
+ fail(
+ "Wildcard crate names must have wildcard crate versions. " +
+ "Please update the `annotations` attribute of the {} crates_repository".format(
+ repository_name,
+ ),
+ )
+ id = _crate_id(name, version)
+ if id in crate_annotations:
+ fail("Found duplicate entries for {}".format(id))
+
+ crate_annotations.update({id: data})
+ return crate_annotations
+
+def _read_cargo_config(repository_ctx):
+ if repository_ctx.attr.cargo_config:
+ config = repository_ctx.path(repository_ctx.attr.cargo_config)
+ return repository_ctx.read(config)
+ return None
+
+def _get_render_config(repository_ctx):
+ if repository_ctx.attr.render_config:
+ config = dict(json.decode(repository_ctx.attr.render_config))
+ else:
+ config = dict(json.decode(render_config()))
+
+ # Add the repository name as it's very relevant to rendering.
+ config.update({"repository_name": repository_ctx.name})
+
+ return struct(**config)
+
+def generate_config(repository_ctx):
+ """Generate a config file from various attributes passed to the rule.
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+
+ Returns:
+ struct: A struct containing the path to a config and it's contents
+ """
+ annotations = collect_crate_annotations(repository_ctx.attr.annotations, repository_ctx.name)
+
+ # Load additive build files if any have been provided.
+ content = list()
+ for data in annotations.values():
+ additive_build_file_content = data.pop("additive_build_file_content", None)
+ if additive_build_file_content:
+ content.append(additive_build_file_content)
+ additive_build_file = data.pop("additive_build_file", None)
+ if additive_build_file:
+ file_path = repository_ctx.path(Label(additive_build_file))
+ content.append(repository_ctx.read(file_path))
+ data.update({"additive_build_file_content": "\n".join(content) if content else None})
+
+ config = struct(
+ generate_build_scripts = repository_ctx.attr.generate_build_scripts,
+ annotations = annotations,
+ cargo_config = _read_cargo_config(repository_ctx),
+ rendering = _get_render_config(repository_ctx),
+ supported_platform_triples = repository_ctx.attr.supported_platform_triples,
+ )
+
+ config_path = repository_ctx.path("cargo-bazel.json")
+ repository_ctx.file(
+ config_path,
+ json.encode_indent(config, indent = " " * 4),
+ )
+
+ # This was originally written to return a struct and not just the config path
+ # so splicing can have access to some rendering information embedded in the config
+ # If splicing should no longer need that info, it'd be simpler to just return a `path`.
+ return struct(
+ path = config_path,
+ info = config,
+ )
+
+def get_lockfile(repository_ctx):
+ """Locate the lockfile and identify the it's type (Cargo or Bazel).
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+
+ Returns:
+ struct: The path to the lockfile as well as it's type
+ """
+ if repository_ctx.attr.lockfile_kind == "auto":
+ if str(repository_ctx.attr.lockfile).endswith("Cargo.lock"):
+ kind = "cargo"
+ else:
+ kind = "bazel"
+ else:
+ kind = repository_ctx.attr.lockfile_kind
+
+ return struct(
+ path = repository_ctx.path(repository_ctx.attr.lockfile),
+ kind = kind,
+ )
+
+def determine_repin(repository_ctx, generator, lockfile_path, lockfile_kind, config, splicing_manifest, cargo, rustc):
+ """Use the `cargo-bazel` binary to determine whether or not dpeendencies need to be re-pinned
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+ generator (path): The path to a `cargo-bazel` binary.
+ config (path): The path to a `cargo-bazel` config file. See `generate_config`.
+ splicing_manifest (path): The path to a `cargo-bazel` splicing manifest. See `create_splicing_manifest`
+ lockfile_path (path): The path to a "lock" file for reproducible outputs.
+ lockfile_kind (str): The type of lock file represented by `lockfile_path`
+ cargo (path): The path to a Cargo binary.
+ rustc (path): The path to a Rustc binary.
+
+ Returns:
+ bool: True if dependencies need to be re-pinned
+ """
+
+ # If a repin environment variable is set, always repin
+ for var in REPIN_ENV_VARS:
+ if repository_ctx.os.environ.get(var, "").lower() in ["true", "yes", "1", "on"]:
+ return True
+
+ # Cargo lockfiles should always be repinned.
+ if lockfile_kind == "cargo":
+ return True
+
+ # Run the binary to check if a repin is needed
+ args = [
+ generator,
+ "query",
+ "--lockfile",
+ lockfile_path,
+ "--config",
+ config,
+ "--splicing-manifest",
+ splicing_manifest,
+ "--cargo",
+ cargo,
+ "--rustc",
+ rustc,
+ ]
+
+ env = {
+ "CARGO": str(cargo),
+ "RUSTC": str(rustc),
+ "RUST_BACKTRACE": "full",
+ }
+
+ # Add any Cargo environment variables to the `cargo-bazel` execution
+ env.update(cargo_environ(repository_ctx))
+
+ result = execute(
+ repository_ctx = repository_ctx,
+ args = args,
+ env = env,
+ )
+
+ # If it was determined repinning should occur but there was no
+ # flag indicating repinning was requested, an error is raised
+ # since repinning should be an explicit action
+ if result.stdout.strip().lower() == "repin":
+ # buildifier: disable=print
+ print(result.stderr)
+ fail((
+ "The current `lockfile` is out of date for '{}'. Please re-run " +
+ "bazel using `CARGO_BAZEL_REPIN=true` if this is expected " +
+ "and the lockfile should be updated."
+ ).format(repository_ctx.name))
+
+ return False
+
+def execute_generator(
+ repository_ctx,
+ lockfile_path,
+ lockfile_kind,
+ generator,
+ config,
+ splicing_manifest,
+ repository_dir,
+ cargo,
+ rustc,
+ repin = False,
+ metadata = None):
+ """Execute the `cargo-bazel` binary to produce `BUILD` and `.bzl` files.
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+ lockfile_path (path): The path to a "lock" file (file used for reproducible renderings).
+ lockfile_kind (str): The type of lockfile given (Cargo or Bazel).
+ generator (path): The path to a `cargo-bazel` binary.
+ config (path): The path to a `cargo-bazel` config file.
+ splicing_manifest (path): The path to a `cargo-bazel` splicing manifest. See `create_splicing_manifest`
+ repository_dir (path): The output path for the Bazel module and BUILD files.
+ cargo (path): The path of a Cargo binary.
+ rustc (path): The path of a Rustc binary.
+ repin (bool, optional): Whether or not to repin dependencies
+ metadata (path, optional): The path to a Cargo metadata json file.
+
+ Returns:
+ struct: The results of `repository_ctx.execute`.
+ """
+ repository_ctx.report_progress("Generating crate BUILD files.")
+
+ args = [
+ generator,
+ "generate",
+ "--lockfile",
+ lockfile_path,
+ "--lockfile-kind",
+ lockfile_kind,
+ "--config",
+ config,
+ "--splicing-manifest",
+ splicing_manifest,
+ "--repository-dir",
+ repository_dir,
+ "--cargo",
+ cargo,
+ "--rustc",
+ rustc,
+ ]
+
+ env = {
+ "RUST_BACKTRACE": "full",
+ }
+
+ # Some components are not required unless re-pinning is enabled
+ if repin:
+ args.extend([
+ "--repin",
+ "--metadata",
+ metadata,
+ ])
+ env.update({
+ "CARGO": str(cargo),
+ "RUSTC": str(rustc),
+ })
+
+ # Add any Cargo environment variables to the `cargo-bazel` execution
+ env.update(cargo_environ(repository_ctx))
+
+ result = execute(
+ repository_ctx = repository_ctx,
+ args = args,
+ env = env,
+ )
+
+ return result
diff --git a/crate_universe/private/selects.bzl b/crate_universe/private/selects.bzl
new file mode 100644
index 0000000..bcbad19
--- /dev/null
+++ b/crate_universe/private/selects.bzl
@@ -0,0 +1,56 @@
+"""A helper module solving for complex select statements in rendered cargo-bazel modules"""
+
+def select_with_or(input_dict, no_match_error = ""):
+ """Drop-in replacement for `select()` that supports ORed keys.
+
+ This is notably different from [@bazel_skylib//lib:selects.bzl%selects.with_or][swo] in that
+ the input dict must have a list as it's values and they keys will continue to expand for each
+ entry instead of failing on duplicates.
+
+ Example:
+ ```starlark
+ deps = selects.with_or({
+ "//configs:one": [":dep1"],
+ ("//configs:two", "//configs:three"): [":dep2or3"],
+ "//configs:four": [":dep4"],
+ "//conditions:default": [":default"]
+ })
+ ```
+ Key labels may appear at most once anywhere in the input.
+
+ This macro eturns a native `select` that expands `("//configs:two", "//configs:three"): [":dep2or3"]` to
+ ```starlark
+ "//configs:two": [":dep2or3"],
+ "//configs:three": [":dep2or3"],
+ ```
+
+ [swo]: https://github.com/bazelbuild/bazel-skylib/blob/1.1.1/docs/selects_doc.md#selectswith_or
+
+ Args:
+ input_dict: The same dictionary `select()` takes, except keys may take
+ either the usual form `"//foo:config1"` or
+ `("//foo:config1", "//foo:config2", ...)` to signify
+ `//foo:config1` OR `//foo:config2` OR `...`.
+ no_match_error: Optional custom error to report if no condition matches.
+
+ Returns:
+ A native `select()`
+ """
+ output_dict = {}
+ for (key, value) in input_dict.items():
+ if type(key) == type(()):
+ for config_setting in key:
+ if config_setting in output_dict:
+ output_dict[config_setting].extend(value)
+ else:
+ output_dict[config_setting] = list(value)
+ elif key in output_dict:
+ output_dict[key].extend(value)
+ else:
+ output_dict[key] = list(value)
+
+ # return a dict with deduped lists
+ return select(
+ {key: depset(value).to_list() for key, value in output_dict.items()},
+ no_match_error = no_match_error,
+ )
diff --git a/crate_universe/private/splicing_utils.bzl b/crate_universe/private/splicing_utils.bzl
new file mode 100644
index 0000000..a419c75
--- /dev/null
+++ b/crate_universe/private/splicing_utils.bzl
@@ -0,0 +1,223 @@
+"""Utilities directly related to the `splicing` step of `cargo-bazel`."""
+
+load(":common_utils.bzl", "cargo_environ", "execute")
+
+def splicing_config(resolver_version = "1"):
+ """arious settings used to configure Cargo manifest splicing behavior.
+
+ [rv]: https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions
+
+ Args:
+ resolver_version (str, optional): The [resolver version][rv] to use in generated Cargo
+ manifests. This flag is **only** used when splicing a manifest from direct package
+ definitions. See `crates_repository::packages`.
+
+ Returns:
+ str: A json encoded string of the parameters provided
+ """
+ return json.encode(struct(
+ resolver_version = resolver_version,
+ ))
+
+def download_extra_workspace_members(repository_ctx, cache_dir, render_template_registry_url):
+ """Download additional workspace members for use in splicing.
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+ cache_dir (path): A directory in which to download and extract extra workspace members
+ render_template_registry_url (str): The base template to use for determining the crate's registry URL.
+
+ Returns:
+ list: A list of information related to the downloaded crates
+ - manifest: The path of the manifest.
+ - url: The url the manifest came from.
+ - sha256: The sha256 checksum of the new manifest.
+ """
+ manifests = []
+ extra_workspace_members = repository_ctx.attr.extra_workspace_members
+ if extra_workspace_members:
+ repository_ctx.report_progress("Downloading extra workspace members.")
+
+ for name, spec in repository_ctx.attr.extra_workspace_members.items():
+ spec = struct(**json.decode(spec))
+
+ url = render_template_registry_url
+ url = url.replace("{name}", name)
+ url = url.replace("{version}", spec.version)
+
+ if spec.sha256:
+ result = repository_ctx.download_and_extract(
+ output = cache_dir,
+ url = url,
+ sha256 = spec.sha256,
+ type = "tar.gz",
+ )
+ else:
+ result = repository_ctx.download_and_extract(
+ output = cache_dir,
+ url = url,
+ type = "tar.gz",
+ )
+
+ manifest = repository_ctx.path("{}/{}-{}/Cargo.toml".format(
+ cache_dir,
+ name,
+ spec.version,
+ ))
+
+ if not manifest.exists:
+ fail("Extra workspace member '{}' has no root Cargo.toml file".format(name))
+
+ manifests.append(struct(
+ manifest = str(manifest),
+ url = url,
+ sha256 = result.sha256,
+ ))
+
+ return manifests
+
+def kebab_case_keys(data):
+ """Ensure the key value of the data given are kebab-case
+
+ Args:
+ data (dict): A deserialized json blob
+
+ Returns:
+ dict: The same `data` but with kebab-case keys
+ """
+ return {
+ key.lower().replace("_", "-"): val
+ for (key, val) in data.items()
+ }
+
+def create_splicing_manifest(repository_ctx):
+ """Produce a manifest containing required components for splciing a new Cargo workspace
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+
+ Returns:
+ path: The path to a json encoded manifest
+ """
+ repo_dir = repository_ctx.path(".")
+
+ # Deserialize information about direct packges
+ direct_packages_info = {
+ # Ensure the data is using kebab-case as that's what `cargo_toml::DependencyDetail` expects.
+ pkg: kebab_case_keys(dict(json.decode(data)))
+ for (pkg, data) in repository_ctx.attr.packages.items()
+ }
+
+ manifests = {str(repository_ctx.path(m)): str(m) for m in repository_ctx.attr.manifests}
+
+ if repository_ctx.attr.cargo_config:
+ cargo_config = str(repository_ctx.path(repository_ctx.attr.cargo_config))
+ else:
+ cargo_config = None
+
+ # Load user configurable splicing settings
+ config = json.decode(repository_ctx.attr.splicing_config or splicing_config())
+
+ # Auto-generated splicier manifest values
+ splicing_manifest_content = {
+ "cargo_config": cargo_config,
+ "direct_packages": direct_packages_info,
+ "manifests": manifests,
+ }
+
+ # Serialize information required for splicing
+ splicing_manifest = repository_ctx.path("{}/splicing_manifest.json".format(repo_dir))
+ repository_ctx.file(
+ splicing_manifest,
+ json.encode_indent(
+ dict(dict(config).items() + splicing_manifest_content.items()),
+ indent = " " * 4,
+ ),
+ )
+
+ return splicing_manifest
+
+def splice_workspace_manifest(repository_ctx, generator, lockfile, splicing_manifest, cargo, rustc):
+ """Splice together a Cargo workspace from various other manifests and package definitions
+
+ Args:
+ repository_ctx (repository_ctx): The rule's context object.
+ generator (path): The `cargo-bazel` binary.
+ lockfile (path): The path to a "lock" file for reproducible `cargo-bazel` renderings.
+ splicing_manifest (path): The path to a splicing manifest.
+ cargo (path): The path to a Cargo binary.
+ rustc (path): The Path to a Rustc binary.
+
+ Returns:
+ path: The path to a Cargo metadata json file found in the spliced workspace root.
+ """
+ repository_ctx.report_progress("Splicing Cargo workspace.")
+ repo_dir = repository_ctx.path(".")
+
+ # Download extra workspace members
+ crates_cache_dir = repository_ctx.path("{}/.crates_cache".format(repo_dir))
+ extra_manifest_info = download_extra_workspace_members(
+ repository_ctx = repository_ctx,
+ cache_dir = crates_cache_dir,
+ render_template_registry_url = repository_ctx.attr.extra_workspace_member_url_template,
+ )
+
+ extra_manifests_manifest = repository_ctx.path("{}/extra_manifests_manifest.json".format(repo_dir))
+ repository_ctx.file(
+ extra_manifests_manifest,
+ json.encode_indent(struct(
+ manifests = extra_manifest_info,
+ ), indent = " " * 4),
+ )
+
+ cargo_workspace = repository_ctx.path("{}/cargo-bazel-splicing".format(repo_dir))
+
+ # Generate a workspace root which contains all workspace members
+ arguments = [
+ generator,
+ "splice",
+ "--workspace-dir",
+ cargo_workspace,
+ "--splicing-manifest",
+ splicing_manifest,
+ "--extra-manifests-manifest",
+ extra_manifests_manifest,
+ "--cargo",
+ cargo,
+ "--rustc",
+ rustc,
+ ]
+
+ # Splicing accepts a Cargo.lock file in some scenarios. Ensure it's passed
+ # if the lockfile is a actually a Cargo lockfile.
+ if lockfile.kind == "cargo":
+ arguments.extend([
+ "--cargo-lockfile",
+ lockfile.path,
+ ])
+
+ env = {
+ "CARGO": str(cargo),
+ "RUSTC": str(rustc),
+ "RUST_BACKTRACE": "full",
+ }
+
+ # Add any Cargo environment variables to the `cargo-bazel` execution
+ env.update(cargo_environ(repository_ctx))
+
+ execute(
+ repository_ctx = repository_ctx,
+ args = arguments,
+ env = env,
+ )
+
+ root_manifest = repository_ctx.path("{}/Cargo.toml".format(cargo_workspace))
+ if not root_manifest.exists:
+ fail("Root manifest does not exist: {}".format(root_manifest))
+
+ # This file must match the one generated in splicing
+ metadata_path = repository_ctx.path("{}/cargo-bazel-spliced-metadata.json".format(cargo_workspace))
+ if not metadata_path.exists:
+ fail("Root metadata file does not exist: {}".format(metadata_path))
+
+ return metadata_path
diff --git a/crate_universe/private/srcs.bzl b/crate_universe/private/srcs.bzl
new file mode 100644
index 0000000..acdfa61
--- /dev/null
+++ b/crate_universe/private/srcs.bzl
@@ -0,0 +1,51 @@
+"""A generate file containing all source files used to produce `cargo-bazel`"""
+
+# Each source file is tracked as a target so the `cargo_bootstrap_repository`
+# rule will know to automatically rebuild if any of the sources changed.
+CARGO_BAZEL_SRCS = [
+ "@rules_rust//crate_universe:src/cli.rs",
+ "@rules_rust//crate_universe:src/cli/generate.rs",
+ "@rules_rust//crate_universe:src/cli/query.rs",
+ "@rules_rust//crate_universe:src/cli/splice.rs",
+ "@rules_rust//crate_universe:src/cli/vendor.rs",
+ "@rules_rust//crate_universe:src/config.rs",
+ "@rules_rust//crate_universe:src/context.rs",
+ "@rules_rust//crate_universe:src/context/crate_context.rs",
+ "@rules_rust//crate_universe:src/context/platforms.rs",
+ "@rules_rust//crate_universe:src/lib.rs",
+ "@rules_rust//crate_universe:src/lockfile.rs",
+ "@rules_rust//crate_universe:src/main.rs",
+ "@rules_rust//crate_universe:src/metadata.rs",
+ "@rules_rust//crate_universe:src/metadata/dependency.rs",
+ "@rules_rust//crate_universe:src/metadata/metadata_annotation.rs",
+ "@rules_rust//crate_universe:src/rendering.rs",
+ "@rules_rust//crate_universe:src/rendering/template_engine.rs",
+ "@rules_rust//crate_universe:src/rendering/templates/crate_build_file.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/module_build_file.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/module_bzl.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/crate/aliases.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/crate/binary.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/crate/build_script.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/crate/common_attrs.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/crate/deps.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/crate/library.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/crate/proc_macro.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/header.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/module/aliases_map.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/module/deps_map.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/module/repo_git.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/module/repo_http.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/starlark/glob.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/starlark/selectable_dict.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/partials/starlark/selectable_list.j2",
+ "@rules_rust//crate_universe:src/rendering/templates/vendor_module.j2",
+ "@rules_rust//crate_universe:src/splicing.rs",
+ "@rules_rust//crate_universe:src/splicing/cargo_config.rs",
+ "@rules_rust//crate_universe:src/splicing/splicer.rs",
+ "@rules_rust//crate_universe:src/test.rs",
+ "@rules_rust//crate_universe:src/utils.rs",
+ "@rules_rust//crate_universe:src/utils/starlark.rs",
+ "@rules_rust//crate_universe:src/utils/starlark/glob.rs",
+ "@rules_rust//crate_universe:src/utils/starlark/label.rs",
+ "@rules_rust//crate_universe:src/utils/starlark/select.rs",
+]
diff --git a/crate_universe/private/test/BUILD.bazel b/crate_universe/private/test/BUILD.bazel
new file mode 100644
index 0000000..c138c17
--- /dev/null
+++ b/crate_universe/private/test/BUILD.bazel
@@ -0,0 +1,15 @@
+load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
+
+diff_test(
+ name = "srcs_diff_test",
+ failure_message = "Please run 'bazel run //crate_universe/private:srcs_module.install'",
+ file1 = "//crate_universe/private:srcs_module",
+ file2 = "//crate_universe/private:srcs.bzl",
+ # TODO: The diff_test here fails on Windows. As does the
+ # install script. This should be fixed.
+ target_compatible_with = select({
+ "@platforms//os:linux": [],
+ "@platforms//os:macos": [],
+ "//conditions:default": ["@platforms//:incompatible"],
+ }),
+)
diff --git a/crate_universe/private/urls.bzl b/crate_universe/private/urls.bzl
new file mode 100644
index 0000000..e4d615c
--- /dev/null
+++ b/crate_universe/private/urls.bzl
@@ -0,0 +1,25 @@
+"""A file containing urls and associated sha256 values for cargo-bazel binaries
+
+This file is auto-generated for each release to match the urls and sha256s of
+the binaries produced for it.
+"""
+
+# Example:
+# {
+# "x86_64-unknown-linux-gnu": "https://domain.com/downloads/cargo-bazel-x86_64-unknown-linux-gnu",
+# "x86_64-apple-darwin": "https://domain.com/downloads/cargo-bazel-x86_64-apple-darwin",
+# "x86_64-pc-windows-msvc": "https://domain.com/downloads/cargo-bazel-x86_64-pc-windows-msvc",
+# }
+CARGO_BAZEL_URLS = {}
+
+# Example:
+# {
+# "x86_64-unknown-linux-gnu": "1d687fcc860dc8a1aa6198e531f0aee0637ed506d6a412fe2b9884ff5b2b17c0",
+# "x86_64-apple-darwin": "0363e450125002f581d29cf632cc876225d738cfa433afa85ca557afb671eafa",
+# "x86_64-pc-windows-msvc": "f5647261d989f63dafb2c3cb8e131b225338a790386c06cf7112e43dd9805882",
+# }
+CARGO_BAZEL_SHA256S = {}
+
+# Example:
+# Label("//crate_universe:cargo_bazel_bin")
+CARGO_BAZEL_LABEL = Label("@cargo_bazel_bootstrap//:binary")
diff --git a/crate_universe/private/vendor/BUILD.bazel b/crate_universe/private/vendor/BUILD.bazel
new file mode 100644
index 0000000..3c02387
--- /dev/null
+++ b/crate_universe/private/vendor/BUILD.bazel
@@ -0,0 +1,11 @@
+load("//crate_universe/private:vendor_utils.bzl", "crates_vendor_deps_targets")
+
+crates_vendor_deps_targets()
+
+filegroup(
+ name = "distro",
+ srcs = glob(["*.bzl"]) + [
+ "BUILD.bazel",
+ ],
+ visibility = ["//crate_universe/private:__subpackages__"],
+)
diff --git a/crate_universe/private/vendor_utils.bzl b/crate_universe/private/vendor_utils.bzl
new file mode 100644
index 0000000..a866edd
--- /dev/null
+++ b/crate_universe/private/vendor_utils.bzl
@@ -0,0 +1,74 @@
+"""Utility functions for use with the `crates_vendor` rule"""
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+
+_BUILDIFIER_VERSION = "5.0.1"
+_BUILDIFIER_URL_TEMPLATE = "https://github.com/bazelbuild/buildtools/releases/download/{version}/{bin}"
+_BUILDIFIER_SHA256S = {
+ "buildifier-darwin-amd64": "2cb0a54683633ef6de4e0491072e22e66ac9c6389051432b76200deeeeaf93fb",
+ "buildifier-darwin-arm64": "4da23315f0dccabf878c8227fddbccf35545b23b3cb6225bfcf3107689cc4364",
+ "buildifier-linux-amd64": "3ed7358c7c6a1ca216dc566e9054fd0b97a1482cb0b7e61092be887d42615c5d",
+ "buildifier-linux-arm64": "c657c628fca72b7e0446f1a542231722a10ba4321597bd6f6249a5da6060b6ff",
+ "buildifier-windows-amd64.exe": "45e13b2951e4c611d346dacdaf0aafaa484045a3e7300fbc5dd01a896a688177",
+}
+
+def crates_vendor_deps():
+ for bin, sha256 in _BUILDIFIER_SHA256S.items():
+ maybe(
+ http_file,
+ name = "cargo_bazel.{}".format(bin),
+ urls = [_BUILDIFIER_URL_TEMPLATE.format(
+ bin = bin,
+ version = _BUILDIFIER_VERSION,
+ )],
+ sha256 = sha256,
+ downloaded_file_path = "buildifier.exe",
+ executable = True,
+ )
+
+# buildifier: disable=unnamed-macro
+def crates_vendor_deps_targets():
+ """Define dependencies of the `crates_vendor` rule"""
+
+ native.config_setting(
+ name = "linux_amd64",
+ constraint_values = ["@platforms//os:linux", "@platforms//cpu:x86_64"],
+ visibility = ["//visibility:public"],
+ )
+
+ native.config_setting(
+ name = "linux_arm64",
+ constraint_values = ["@platforms//os:linux", "@platforms//cpu:arm64"],
+ visibility = ["//visibility:public"],
+ )
+
+ native.config_setting(
+ name = "macos_amd64",
+ constraint_values = ["@platforms//os:macos", "@platforms//cpu:x86_64"],
+ visibility = ["//visibility:public"],
+ )
+
+ native.config_setting(
+ name = "macos_arm64",
+ constraint_values = ["@platforms//os:macos", "@platforms//cpu:arm64"],
+ visibility = ["//visibility:public"],
+ )
+
+ native.config_setting(
+ name = "windows",
+ constraint_values = ["@platforms//os:windows"],
+ visibility = ["//visibility:public"],
+ )
+
+ native.alias(
+ name = "buildifier",
+ actual = select({
+ ":linux_amd64": "@cargo_bazel.buildifier-linux-amd64//file",
+ ":linux_arm64": "@cargo_bazel.buildifier-linux-arm64//file",
+ ":macos_amd64": "@cargo_bazel.buildifier-darwin-amd64//file",
+ ":macos_arm64": "@cargo_bazel.buildifier-darwin-arm64//file",
+ ":windows": "@cargo_bazel.buildifier-windows-amd64.exe//file",
+ }),
+ visibility = ["//visibility:public"],
+ )