load("@bazel_features//:features.bzl", "bazel_features")
load("@bazel_skylib//lib:structs.bzl", "structs")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load(
"//crate_universe/private:common_utils.bzl",
"new_cargo_bazel_fn",
)
load("//crate_universe/private:crates_repository.bzl", "SUPPORTED_PLATFORM_TRIPLES")
load(
"//crate_universe/private:crates_vendor.bzl",
"CRATES_VENDOR_ATTRS",
"generate_config_file",
"generate_splicing_manifest",
)
load(
"//crate_universe/private:generate_utils.bzl",
"CARGO_BAZEL_GENERATOR_SHA256",
"CARGO_BAZEL_GENERATOR_URL",
"CRATES_REPOSITORY_ENVIRON",
"GENERATOR_ENV_VARS",
"determine_repin",
"execute_generator",
generate_render_config = "render_config",
)
load("//crate_universe/private:local_crate_mirror.bzl", "local_crate_mirror")
load(
"//crate_universe/private:splicing_utils.bzl",
"splice_workspace_manifest",
generate_splicing_config = "splicing_config",
)
load("//crate_universe/private:urls.bzl", "CARGO_BAZEL_SHA256S", "CARGO_BAZEL_URLS")
load("//rust/platform:triple.bzl", "get_host_triple")
load("//rust/platform:triple_mappings.bzl", "system_to_binary_ext")
load(":defs.bzl", _crate_universe_crate = "crate")
_relative_label_list = attr.string_list
_OPT_BOOL_VALUES = {
"auto": None,
"off": False,
"on": True,
}
def _get_or_insert(d, key, value):
if key not in d:
d[key] = value
return d[key]
def _generate_repo_impl(repository_ctx):
for path, contents in repository_ctx.attr.contents.items():
repository_ctx.file(path, contents)
repository_ctx.file("WORKSPACE.bazel", """workspace(name = "{}")""".format(
repository_ctx.name,
))
_generate_repo = repository_rule(
doc = "A utility for generating a hub repo.",
implementation = _generate_repo_impl,
attrs = {
"contents": attr.string_dict(
doc = "A mapping of file names to text they should contain.",
mandatory = True,
),
},
)
def _annotations_for_repo(module_annotations, repo_specific_annotations):
if not repo_specific_annotations:
return module_annotations
annotations = dict(module_annotations)
for crate, values in repo_specific_annotations.items():
_get_or_insert(annotations, crate, []).extend(values)
return annotations
def _collect_render_config(module, repository):
config = None
for raw_config in module.tags.render_config:
if raw_config.repositories and repository not in raw_config.repositories:
continue
if config:
fail("Multiple render configs provided for module `{}`. Only 1 is allowed.".format(
module.name,
))
config_kwargs = {attr: getattr(raw_config, attr) for attr in dir(raw_config)}
if "repositories" in config_kwargs:
config_kwargs.pop("repositories")
if "default_alias_rule_bzl" in config_kwargs:
default_alias_rule_bzl = config_kwargs.pop("default_alias_rule_bzl")
if default_alias_rule_bzl:
config_kwargs["default_alias_rule"] = str(default_alias_rule_bzl)
if "default_alias_rule_name" in config_kwargs:
config_kwargs["default_alias_rule"] = "{}:{}".format(
config_kwargs.get("default_alias_rule", ""),
config_kwargs.pop("default_alias_rule_name"),
).lstrip(":")
for null_defaults in ["vendor_mode", "regen_command", "default_package_name"]:
if config_kwargs[null_defaults] == "":
config_kwargs[null_defaults] = None
config = json.decode(generate_render_config(**config_kwargs))
if not config:
config = json.decode(generate_render_config())
if not config["regen_command"]:
config["regen_command"] = "bazel mod show_repo '{}'".format(module.name)
return config
def _collect_splicing_config(module, repository):
config = None
for raw_config in module.tags.splicing_config:
if raw_config.repositories and repository not in raw_config.repositories:
continue
if config:
fail("Multiple render configs provided for module `{}`. Only 1 is allowed.".format(
module.name,
))
config_kwargs = {attr: getattr(raw_config, attr) for attr in dir(raw_config)}
if "repositories" in config_kwargs:
config_kwargs.pop("repositories")
config = json.decode(generate_splicing_config(**config_kwargs))
if not config:
config = json.decode(generate_splicing_config())
return config
def _generate_hub_and_spokes(
*,
module_ctx,
cargo_bazel_fn,
cfg,
annotations,
render_config,
splicing_config,
lockfile,
cargo_lockfile = None,
manifests = {},
packages = {}):
tag_path = module_ctx.path(cfg.name)
config_file = tag_path.get_child("config.json")
module_ctx.file(
config_file,
executable = False,
content = generate_config_file(
module_ctx,
mode = "remote",
annotations = annotations,
generate_build_scripts = cfg.generate_build_scripts,
supported_platform_triples = cfg.supported_platform_triples,
generate_target_compatible_with = True,
repository_name = cfg.name,
output_pkg = cfg.name,
workspace_name = cfg.name,
generate_binaries = cfg.generate_binaries,
render_config = render_config,
repository_ctx = module_ctx,
),
)
splicing_manifest = tag_path.get_child("splicing_manifest.json")
module_ctx.file(
splicing_manifest,
executable = False,
content = generate_splicing_manifest(
packages = packages,
splicing_config = splicing_config,
cargo_config = cfg.cargo_config,
manifests = manifests,
manifest_to_path = module_ctx.path,
),
)
repin = not lockfile or determine_repin(
repository_ctx = module_ctx,
repository_name = cfg.name,
cargo_bazel_fn = cargo_bazel_fn,
lockfile_path = lockfile,
config = config_file,
splicing_manifest = splicing_manifest,
)
kwargs = dict()
if repin:
module_ctx.report_progress("Splicing Cargo workspace for `{}`".format(cfg.name))
splice_outputs = splice_workspace_manifest(
repository_ctx = module_ctx,
cargo_bazel_fn = cargo_bazel_fn,
cargo_lockfile = cargo_lockfile,
splicing_manifest = splicing_manifest,
config_path = config_file,
output_dir = tag_path.get_child("splicing-output"),
debug_workspace_dir = tag_path.get_child("splicing-workspace"),
)
if cargo_lockfile == None:
cargo_lockfile = splice_outputs.cargo_lock
if lockfile == None:
lockfile = tag_path.get_child("cargo-bazel-lock.json")
module_ctx.file(lockfile, "")
kwargs.update({
"metadata": splice_outputs.metadata,
})
nonhermetic_root_bazel_workspace_dir = module_ctx.path(Label("@@//:MODULE.bazel")).dirname
paths_to_track_file = tag_path.get_child("paths_to_track.json")
warnings_output_file = tag_path.get_child("warnings_output.json")
module_ctx.report_progress("Generating crate BUILD files for `{}`".format(cfg.name))
execute_generator(
cargo_bazel_fn = cargo_bazel_fn,
config = config_file,
splicing_manifest = splicing_manifest,
lockfile_path = lockfile,
cargo_lockfile_path = cargo_lockfile,
repository_dir = tag_path,
nonhermetic_root_bazel_workspace_dir = nonhermetic_root_bazel_workspace_dir,
paths_to_track_file = paths_to_track_file,
warnings_output_file = warnings_output_file,
**kwargs
)
module_ctx.report_progress("Generating hub and spokes")
paths_to_track = json.decode(module_ctx.read(paths_to_track_file))
for path in paths_to_track:
module_ctx.read(path)
warnings_output_file = json.decode(module_ctx.read(warnings_output_file))
for warning in warnings_output_file:
print("WARN: {}".format(warning))
crates_dir = tag_path.get_child(cfg.name)
_generate_repo(
name = cfg.name,
contents = {
"BUILD.bazel": module_ctx.read(crates_dir.get_child("BUILD.bazel")),
"alias_rules.bzl": module_ctx.read(crates_dir.get_child("alias_rules.bzl")),
"defs.bzl": module_ctx.read(crates_dir.get_child("defs.bzl")),
},
)
contents = json.decode(module_ctx.read(lockfile))
for crate in contents["crates"].values():
repo = crate["repository"]
if repo == None:
continue
name = crate["name"]
version = crate["version"]
crate_repo_name = "{repo_name}__{name}-{version}".format(
repo_name = cfg.name,
name = name,
version = version.replace("+", "-"),
)
if "Http" in repo:
build_file_content = module_ctx.read(crates_dir.get_child("BUILD.%s-%s.bazel" % (name, version)))
repo = repo["Http"]
http_archive(
name = crate_repo_name,
patch_args = repo.get("patch_args", None),
patch_tool = repo.get("patch_tool", None),
patches = repo.get("patches", None),
remote_patch_strip = 1,
sha256 = repo.get("sha256", None),
type = "tar.gz",
urls = [repo["url"]],
strip_prefix = "%s-%s" % (crate["name"], crate["version"]),
build_file_content = build_file_content,
)
elif "Git" in repo:
build_file_content = module_ctx.read(crates_dir.get_child("BUILD.%s-%s.bazel" % (name, version)))
repo = repo["Git"]
kwargs = {}
for k, v in repo["commitish"].items():
if k == "Rev":
kwargs["commit"] = v
else:
kwargs[k.lower()] = v
new_git_repository(
name = crate_repo_name,
init_submodules = True,
patch_args = repo.get("patch_args", None),
patch_tool = repo.get("patch_tool", None),
patches = repo.get("patches", None),
shallow_since = repo.get("shallow_since", None),
remote = repo["remote"],
build_file_content = build_file_content,
strip_prefix = repo.get("strip_prefix", None),
**kwargs
)
elif "Path" in repo:
options = {
"config": render_config,
"crate_context": crate,
"platform_conditions": contents["conditions"],
"supported_platform_triples": cfg.supported_platform_triples,
}
kwargs = {}
if len(CARGO_BAZEL_URLS) == 0:
kwargs["generator"] = "@cargo_bazel_bootstrap//:cargo-bazel"
local_crate_mirror(
name = crate_repo_name,
options_json = json.encode(options),
path = repo["Path"]["path"],
**kwargs
)
else:
fail("Invalid repo: expected Http or Git to exist for crate %s-%s, got %s" % (name, version, repo))
def _package_to_json(p):
return json.encode({
k: v
for k, v in structs.to_dict(p).items()
if v or k == "default_features"
})
def _get_generator(module_ctx, host_triple):
use_environ = False
for var in GENERATOR_ENV_VARS:
if var in module_ctx.os.environ:
use_environ = True
if use_environ:
generator_sha256 = module_ctx.os.environ.get(CARGO_BAZEL_GENERATOR_SHA256)
generator_url = module_ctx.os.environ.get(CARGO_BAZEL_GENERATOR_URL)
elif len(CARGO_BAZEL_URLS) == 0:
return module_ctx.path(Label("@cargo_bazel_bootstrap//:BUILD.bazel")).dirname.get_child("cargo-bazel")
else:
generator_sha256 = CARGO_BAZEL_SHA256S.get(host_triple.str)
generator_url = CARGO_BAZEL_URLS.get(host_triple.str)
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.str))
output = module_ctx.path("cargo-bazel.exe" if "win" in module_ctx.os.name else "cargo-bazel")
download_kwargs = {
"executable": True,
"output": output,
"url": generator_url,
}
if generator_sha256:
download_kwargs.update({"sha256": generator_sha256})
module_ctx.download(**download_kwargs)
return output
def _get_host_cargo_rustc(module_ctx, host_triple, host_tools_repo):
binary_ext = system_to_binary_ext(host_triple.system)
root = module_ctx.path(host_tools_repo)
cargo_path = root.dirname.get_child("bin/cargo{}".format(binary_ext))
rustc_path = root.dirname.get_child("bin/rustc{}".format(binary_ext))
return cargo_path, rustc_path
def _crate_impl(module_ctx):
reproducible = True
host_triple = get_host_triple(module_ctx, abi = {
"aarch64-unknown-linux": "musl",
"x86_64-unknown-linux": "musl",
})
generator = _get_generator(module_ctx, host_triple)
all_repos = []
for mod in module_ctx.modules:
if not mod.tags.from_cargo and not mod.tags.from_specs:
fail("`.from_cargo` or `.from_specs` are required. Please update {}".format(mod.name))
local_repos = []
module_annotations = {}
repo_specific_annotations = {}
for annotation_tag in mod.tags.annotation:
annotation_dict = structs.to_dict(annotation_tag)
repositories = annotation_dict.pop("repositories")
crate = annotation_dict.pop("crate")
if annotation_dict.pop("gen_all_binaries"):
annotation_dict["gen_binaries"] = True
annotation_dict["gen_build_script"] = _OPT_BOOL_VALUES[annotation_dict["gen_build_script"]]
annotation_dict["override_targets"] = dict()
replacement = annotation_dict.pop("override_target_lib")
if replacement:
annotation_dict["override_targets"]["lib"] = str(replacement)
replacement = annotation_dict.pop("override_target_proc_macro")
if replacement:
annotation_dict["override_targets"]["proc_macro"] = str(replacement)
replacement = annotation_dict.pop("override_target_build_script")
if replacement:
annotation_dict["override_targets"]["build_script"] = str(replacement)
replacement = annotation_dict.pop("override_target_bin")
if replacement:
annotation_dict["override_targets"]["bin"] = str(replacement)
annotation = _crate_universe_crate.annotation(**{
k: v
for k, v in annotation_dict.items()
if v != "" and v != [] and v != {}
})
if not repositories:
_get_or_insert(module_annotations, crate, []).append(annotation)
for repo in repositories:
_get_or_insert(
_get_or_insert(repo_specific_annotations, repo, {}),
crate,
[],
).append(annotation)
common_specs = []
repo_specific_specs = {}
for spec in mod.tags.spec:
if not spec.repositories:
common_specs.append(spec)
else:
for name in spec.repositories:
if name not in repo_specific_specs:
repo_specific_specs[name] = []
repo_specific_specs[name].append(spec)
for cfg in mod.tags.from_cargo + mod.tags.from_specs:
if cfg.name in local_repos:
fail("Defined two crate universes with the same name in the same MODULE.bazel file (`{}`). Use the `name` tag to give them different names.".format(
cfg.name,
))
if cfg.name in all_repos:
fail("Defined two crate universes with the same name in different MODULE.bazel files (`{}`). Either give one a different name, or use `use_extension(isolate=True)`".format(
cfg.name,
))
all_repos.append(cfg.name)
local_repos.append(cfg.name)
for repo in repo_specific_annotations:
if repo not in local_repos:
fail("Annotation specified for repo {}, but the module defined repositories {}".format(repo, local_repos))
for repo in repo_specific_specs:
if repo not in local_repos:
fail("Spec specified for repo {}, but the module defined repositories {}".format(repo, local_repos))
for cfg in mod.tags.from_cargo + mod.tags.from_specs:
if cfg.cargo_lockfile:
module_ctx.watch(cfg.cargo_lockfile)
if cfg.lockfile:
module_ctx.watch(cfg.lockfile)
if cfg.cargo_config:
module_ctx.watch(cfg.cargo_config)
if hasattr(cfg, "manifests"):
for m in cfg.manifests:
module_ctx.watch(m)
cargo_path, rustc_path = _get_host_cargo_rustc(module_ctx, host_triple, cfg.host_tools)
cargo_bazel_fn = new_cargo_bazel_fn(
repository_ctx = module_ctx,
cargo_bazel_path = generator,
cargo_path = cargo_path,
rustc_path = rustc_path,
isolated = cfg.isolated,
)
rendering_config = _collect_render_config(mod, cfg.name)
splicing_config = _collect_splicing_config(mod, cfg.name)
annotations = _annotations_for_repo(
module_annotations,
repo_specific_annotations.get(cfg.name),
)
lockfile_path = None
if cfg.lockfile:
lockfile_path = module_ctx.path(cfg.lockfile)
else:
reproducible = False
cargo_lockfile = None
if cfg.cargo_lockfile:
cargo_lockfile = module_ctx.path(cfg.cargo_lockfile)
else:
reproducible = False
manifests = {}
packages = {}
if hasattr(cfg, "manifests"):
manifests = {str(module_ctx.path(m)): str(m) for m in cfg.manifests}
packages = {
p.package: _package_to_json(p)
for p in common_specs + repo_specific_specs.get(cfg.name, [])
}
_generate_hub_and_spokes(
module_ctx = module_ctx,
cargo_bazel_fn = cargo_bazel_fn,
cfg = cfg,
annotations = annotations,
lockfile = lockfile_path,
cargo_lockfile = cargo_lockfile,
render_config = rendering_config,
splicing_config = splicing_config,
manifests = manifests,
packages = packages,
)
metadata_kwargs = {}
if bazel_features.external_deps.extension_metadata_has_reproducible:
metadata_kwargs["reproducible"] = reproducible
return module_ctx.extension_metadata(**metadata_kwargs)
_FROM_COMMON_ATTRS = {
"cargo_config": CRATES_VENDOR_ATTRS["cargo_config"],
"cargo_lockfile": CRATES_VENDOR_ATTRS["cargo_lockfile"],
"generate_binaries": CRATES_VENDOR_ATTRS["generate_binaries"],
"generate_build_scripts": CRATES_VENDOR_ATTRS["generate_build_scripts"],
"host_tools": attr.label(
doc = "The `rust_host_tools` repository to use.",
default = "@rust_host_tools",
),
"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. " +
"If set, this file must exist within the workspace (but can be empty) before this rule will work."
),
),
"supported_platform_triples": attr.string_list(
doc = "A set of all platform triples to consider when generating dependencies.",
default = SUPPORTED_PLATFORM_TRIPLES,
),
}
_from_cargo = tag_class(
doc = "Generates a repo @crates from a Cargo.toml / Cargo.lock pair.",
attrs = {
"name": attr.string(
doc = "The name of the repo to generate",
default = "crates",
),
} | {
"manifests": CRATES_VENDOR_ATTRS["manifests"],
} | _FROM_COMMON_ATTRS,
)
_annotation = tag_class(
doc = "A collection of extra attributes and settings for a particular crate.",
attrs = {
"additive_build_file": attr.label(
doc = "A file containing extra contents to write to the bottom of generated BUILD files.",
),
"additive_build_file_content": attr.string(
doc = "Extra contents to write to the bottom of generated BUILD files.",
),
"alias_rule": attr.string(
doc = "Alias rule to use instead of `native.alias()`. Overrides [render_config](#render_config)'s 'default_alias_rule'.",
),
"build_script_data": _relative_label_list(
doc = "A list of labels to add to a crate's `cargo_build_script::data` attribute.",
),
"build_script_data_glob": attr.string_list(
doc = "A list of glob patterns to add to a crate's `cargo_build_script::data` attribute",
),
"build_script_deps": _relative_label_list(
doc = "A list of labels to add to a crate's `cargo_build_script::deps` attribute.",
),
"build_script_env": attr.string_dict(
doc = "Additional environment variables to set on a crate's `cargo_build_script::env` attribute.",
),
"build_script_link_deps": _relative_label_list(
doc = "A list of labels to add to a crate's `cargo_build_script::link_deps` attribute.",
),
"build_script_proc_macro_deps": _relative_label_list(
doc = "A list of labels to add to a crate's `cargo_build_script::proc_macro_deps` attribute.",
),
"build_script_rundir": attr.string(
doc = "An override for the build script's rundir attribute.",
),
"build_script_rustc_env": attr.string_dict(
doc = "Additional environment variables to set on a crate's `cargo_build_script::env` attribute.",
),
"build_script_toolchains": attr.label_list(
doc = "A list of labels to set on a crates's `cargo_build_script::toolchains` attribute.",
),
"build_script_tools": _relative_label_list(
doc = "A list of labels to add to a crate's `cargo_build_script::tools` attribute.",
),
"compile_data": _relative_label_list(
doc = "A list of labels to add to a crate's `rust_library::compile_data` attribute.",
),
"compile_data_glob": attr.string_list(
doc = "A list of glob patterns to add to a crate's `rust_library::compile_data` attribute.",
),
"compile_data_glob_excludes": attr.string_list(
doc = "A list of glob patterns to be excllued from a crate's `rust_library::compile_data` attribute.",
),
"crate": attr.string(
doc = "The name of the crate the annotation is applied to",
mandatory = True,
),
"crate_features": attr.string_list(
doc = "A list of strings to add to a crate's `rust_library::crate_features` attribute.",
),
"data": _relative_label_list(
doc = "A list of labels to add to a crate's `rust_library::data` attribute.",
),
"data_glob": attr.string_list(
doc = "A list of glob patterns to add to a crate's `rust_library::data` attribute.",
),
"deps": _relative_label_list(
doc = "A list of labels to add to a crate's `rust_library::deps` attribute.",
),
"disable_pipelining": attr.bool(
doc = "If True, disables pipelining for library targets for this crate.",
),
"extra_aliased_targets": attr.string_dict(
doc = "A list of targets to add to the generated aliases in the root crate_universe repository.",
),
"gen_all_binaries": attr.bool(
doc = "If true, generates `rust_binary` targets for all of the crates bins",
),
"gen_binaries": attr.string_list(
doc = "As a list, the subset of the crate's bins that should get `rust_binary` targets produced.",
),
"gen_build_script": attr.string(
doc = "An authoritative flag to determine whether or not to produce `cargo_build_script` targets for the current crate. Supported values are 'on', 'off', and 'auto'.",
values = _OPT_BOOL_VALUES.keys(),
default = "auto",
),
"override_target_bin": attr.label(
doc = "An optional alternate target to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.",
),
"override_target_build_script": attr.label(
doc = "An optional alternate target to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.",
),
"override_target_lib": attr.label(
doc = "An optional alternate target to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.",
),
"override_target_proc_macro": attr.label(
doc = "An optional alternate target to use when something depends on this crate to allow the parent repo to provide its own version of this dependency.",
),
"patch_args": attr.string_list(
doc = "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": attr.string(
doc = "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": attr.label_list(
doc = "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": _relative_label_list(
doc = "A list of labels to add to a crate's `rust_library::proc_macro_deps` attribute.",
),
"repositories": attr.string_list(
doc = "A list of repository names specified from `crate.from_cargo(name=...)` that this annotation is applied to. Defaults to all repositories.",
default = [],
),
"rustc_env": attr.string_dict(
doc = "Additional variables to set on a crate's `rust_library::rustc_env` attribute.",
),
"rustc_env_files": _relative_label_list(
doc = "A list of labels to set on a crate's `rust_library::rustc_env_files` attribute.",
),
"rustc_flags": attr.string_list(
doc = "A list of strings to set on a crate's `rust_library::rustc_flags` attribute.",
),
"shallow_since": attr.string(
doc = "An optional timestamp used for crates originating from a git repository instead of a crate registry. This flag optimizes fetching the source code.",
),
"version": attr.string(
doc = "The versions of the crate the annotation is applied to. Defaults to all versions.",
default = "*",
),
},
)
_from_specs = tag_class(
doc = "Generates a repo @crates from the defined `spec` tags.",
attrs = {
"name": attr.string(
doc = "The name of the repo to generate.",
default = "crates",
),
} | _FROM_COMMON_ATTRS,
)
_spec = tag_class(
doc = "A constructor for a crate dependency.",
attrs = {
"artifact": attr.string(
doc = "Set to 'bin' to pull in a binary crate as an artifact dependency. Requires a nightly Cargo.",
),
"branch": attr.string(
doc = "The git branch of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified. Specifying `rev` is recommended for fully-reproducible builds.",
),
"default_features": attr.bool(
doc = "Maps to the `default-features` flag.",
default = True,
),
"features": attr.string_list(
doc = "A list of features to use for the crate.",
),
"git": attr.string(
doc = "The Git url to use for the crate. Cannot be used with `version`.",
),
"lib": attr.bool(
doc = "If using `artifact = 'bin'`, additionally setting `lib = True` declares a dependency on both the package's library and binary, as opposed to just the binary.",
),
"package": attr.string(
doc = "The explicit name of the package.",
mandatory = True,
),
"repositories": attr.string_list(
doc = "A list of repository names specified from `crate.from_cargo(name=...)` that this spec is applied to. Defaults to all repositories.",
default = [],
),
"rev": attr.string(
doc = "The git revision of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified.",
),
"tag": attr.string(
doc = "The git tag of the remote crate. Tied with the `git` param. Only one of branch, tag or rev may be specified. Specifying `rev` is recommended for fully-reproducible builds.",
),
"version": attr.string(
doc = "The exact version of the crate. Cannot be used with `git`.",
),
},
)
_splicing_config = tag_class(
doc = "Various settings used to configure Cargo manifest splicing behavior.",
attrs = {
"repositories": attr.string_list(
doc = "A list of repository names specified from `crate.from_cargo(name=...)` that this annotation is applied to. Defaults to all repositories.",
default = [],
),
} | {
"resolver_version": attr.string(
doc = "The [resolver version](https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions) to use in generated Cargo manifests. This flag is **only** used when splicing a manifest from direct package definitions. See `crates_repository::packages`",
default = "2",
),
},
)
_render_config = tag_class(
doc = """\
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 |
""",
attrs = {
"repositories": attr.string_list(
doc = "A list of repository names specified from `crate.from_cargo(name=...)` that this annotation is applied to. Defaults to all repositories.",
default = [],
),
} | {
"build_file_template": attr.string(
doc = "The base template to use for BUILD file names. The available format keys are [`{name}`, {version}`].",
default = "//:BUILD.{name}-{version}.bazel",
),
"crate_alias_template": attr.string(
doc = "The base template to use for crate labels. The available format keys are [`{repository}`, `{name}`, `{version}`, `{target}`].",
default = "@{repository}//:{name}-{version}-{target}",
),
"crate_label_template": attr.string(
doc = "The base template to use for crate labels. The available format keys are [`{repository}`, `{name}`, `{version}`, `{target}`].",
default = "@{repository}__{name}-{version}//:{target}",
),
"crate_repository_template": attr.string(
doc = "The base template to use for Crate label repository names. The available format keys are [`{repository}`, `{name}`, `{version}`].",
default = "{repository}__{name}-{version}",
),
"crates_module_template": attr.string(
doc = "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 = "//:{file}",
),
"default_alias_rule_bzl": attr.label(
doc = "Alias rule to use when generating aliases for all crates. Acceptable values are 'alias', 'dbg'/'fastbuild'/'opt' (transitions each crate's `compilation_mode`) or a string representing a rule in the form '<label to .bzl>:<rule>' that takes a single label parameter 'actual'. See '@crate_index//:alias_rules.bzl' for an example.",
),
"default_alias_rule_name": attr.string(
doc = "Alias rule to use when generating aliases for all crates. Acceptable values are 'alias', 'dbg'/'fastbuild'/'opt' (transitions each crate's `compilation_mode`) or a string representing a rule in the form '<label to .bzl>:<rule>' that takes a single label parameter 'actual'. See '@crate_index//:alias_rules.bzl' for an example.",
default = "alias",
),
"default_package_name": attr.string(
doc = "The default package name to use in the rendered macros. This affects the auto package detection of things like `all_crate_deps`.",
default = "",
),
"generate_cargo_toml_env_vars": attr.bool(
doc = "Whether to generate cargo_toml_env_vars targets.",
default = True,
),
"generate_rules_license_metadata": attr.bool(
doc = "Whether to generate rules license metedata.",
default = False,
),
"generate_target_compatible_with": attr.bool(
doc = "Whether to generate `target_compatible_with` annotations on the generated BUILD files. This catches a `target_triple` being targeted that isn't declared in `supported_platform_triples`.",
default = True,
),
"platforms_template": attr.string(
doc = "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}`].",
default = "@rules_rust//rust/platform:{triple}",
),
"regen_command": attr.string(
doc = "An optional command to demonstrate how generated files should be regenerated.",
default = "",
),
"vendor_mode": attr.string(
doc = "An optional configuration for rendering content to be rendered into repositories.",
default = "",
),
},
)
crate = module_extension(
doc = """\
Crate universe module extensions.
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 authoritative 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. See [Repinning / Updating Dependencies](#repinning--updating-dependencies) for more details. |
| `CARGO_BAZEL_REPIN_ONLY` | A comma-delimited allowlist for rules to execute repinning. Can be useful if multiple instances of the repository rule are used in a Bazel workspace, but repinning should be limited to one of them. |
""",
implementation = _crate_impl,
tag_classes = {
"annotation": _annotation,
"from_cargo": _from_cargo,
"from_specs": _from_specs,
"render_config": _render_config,
"spec": _spec,
"splicing_config": _splicing_config,
},
environ = CRATES_REPOSITORY_ENVIRON,
)