Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added render_config and splicing_config to create_universe bzlmod #3136

Merged
merged 3 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crate_universe/docs_bzlmod.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ crate = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")

crate.spec(package = "serde", features = ["derive"], version = "1.0")
crate.spec(package = "serde_json", version = "1.0")
crate.spec(package = "tokio", default_features=False, features = ["macros", "net", "rt-multi-thread"], version = "1.38")
crate.spec(package = "tokio", default_features = False, features = ["macros", "net", "rt-multi-thread"], version = "1.38")

crate.from_specs()
use_repo(crate, "crates")
Expand Down
197 changes: 187 additions & 10 deletions crate_universe/extensions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ 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: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", "GENERATOR_ENV_VARS", "render_config")
load("//crate_universe/private:generate_utils.bzl", "CARGO_BAZEL_GENERATOR_SHA256", "CARGO_BAZEL_GENERATOR_URL", "GENERATOR_ENV_VARS", generate_render_config = "render_config")
load("//crate_universe/private:local_crate_mirror.bzl", "local_crate_mirror")
load("//crate_universe/private:splicing_utils.bzl", 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")
Expand Down Expand Up @@ -61,24 +62,104 @@ def _annotations_for_repo(module_annotations, repo_specific_annotations):
_get_or_insert(annotations, crate, []).extend(values)
return annotations

def _generate_hub_and_spokes(*, module_ctx, cargo_bazel, cfg, annotations, cargo_lockfile = None, manifests = {}, packages = {}):
def _collect_render_config(module, repository):
"""Collect the render_config for the given crate_universe module.

Args:
module (StarlarkBazelModule): The current `crate` module.
repository (str): The name of the repository to collect the config for.

Returns:
dict: The rendering config to use.
"""

config = None
for raw_config in module.tags.render_config:
if not raw_config.repositories:
continue

if not repository 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")

# bzlmod doesn't allow passing `None` as a default parameter to indicate a value was
# not provided. So for backward compatibility, certain empty values are assumed to be
# not provided and thus are converted explicitly to `None`.
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):
"""Collect the splicing_config for the given crate_universe module.

Args:
module (StarlarkBazelModule): The current `crate` module.
repository (str): The name of the repository to collect the config for.

Returns:
dict: The splicing config to use.
"""
config = None
for raw_config in module.tags.splicing_config:
if not raw_config.repositories:
continue

if not repository 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, cfg, annotations, render_config, splicing_config, cargo_lockfile = None, manifests = {}, packages = {}):
"""Generates repositories for the transitive closure of crates defined by manifests and packages.

Args:
module_ctx (module_ctx): The module context object.
cargo_bazel (function): A function that can be called to execute cargo_bazel.
cfg (object): The module tag from `from_cargo` or `from_specs`
annotations (dict): The set of annotation tag classes that apply to this closure, keyed by crate name.
render_config (dict): The render config to use.
splicing_config (dict): The splicing config to use.
cargo_lockfile (path): Path to Cargo.lock, if we have one. This is optional for `from_specs` closures.
manifests (dict): The set of Cargo.toml manifests that apply to this closure, if any, keyed by path.
packages (dict): The set of extra cargo crate tags that apply to this closure, if any, keyed by package name.
"""

tag_path = module_ctx.path(cfg.name)

rendering_config = json.decode(render_config(
regen_command = "Run 'cargo update [--workspace]'",
))
config_file = tag_path.get_child("config.json")
module_ctx.file(
config_file,
Expand All @@ -94,7 +175,7 @@ def _generate_hub_and_spokes(*, module_ctx, cargo_bazel, cfg, annotations, cargo
output_pkg = cfg.name,
workspace_name = cfg.name,
generate_binaries = cfg.generate_binaries,
render_config = rendering_config,
render_config = render_config,
repository_ctx = module_ctx,
),
)
Expand All @@ -105,7 +186,7 @@ def _generate_hub_and_spokes(*, module_ctx, cargo_bazel, cfg, annotations, cargo
executable = False,
content = generate_splicing_manifest(
packages = packages,
splicing_config = cfg.splicing_config,
splicing_config = splicing_config,
cargo_config = cfg.cargo_config,
manifests = manifests,
manifest_to_path = module_ctx.path,
Expand Down Expand Up @@ -239,7 +320,7 @@ def _generate_hub_and_spokes(*, module_ctx, cargo_bazel, cfg, annotations, cargo
)
elif "Path" in repo:
options = {
"config": rendering_config,
"config": render_config,
"crate_context": crate,
"platform_conditions": contents["conditions"],
"supported_platform_triples": cfg.supported_platform_triples,
Expand Down Expand Up @@ -449,6 +530,9 @@ def _crate_impl(module_ctx):
local_repos.append(cfg.name)

for cfg in mod.tags.from_cargo:
render_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),
Expand All @@ -462,6 +546,8 @@ def _crate_impl(module_ctx):
cfg = cfg,
annotations = annotations,
cargo_lockfile = cargo_lockfile,
render_config = render_config,
splicing_config = splicing_config,
manifests = manifests,
)

Expand All @@ -476,12 +562,17 @@ def _crate_impl(module_ctx):
repo_specific_annotations.get(cfg.name),
)

render_config = _collect_render_config(mod, cfg.name)
splicing_config = _collect_splicing_config(mod, cfg.name)

packages = {p.package: _package_to_json(p) for p in mod.tags.spec}
_generate_hub_and_spokes(
module_ctx = module_ctx,
cargo_bazel = cargo_bazel,
cfg = cfg,
annotations = annotations,
render_config = render_config,
splicing_config = splicing_config,
packages = packages,
)

Expand Down Expand Up @@ -510,13 +601,13 @@ _from_cargo = tag_class(
"cargo_lockfile": CRATES_VENDOR_ATTRS["cargo_lockfile"],
"generate_binaries": CRATES_VENDOR_ATTRS["generate_binaries"],
"generate_build_scripts": CRATES_VENDOR_ATTRS["generate_build_scripts"],
"splicing_config": CRATES_VENDOR_ATTRS["splicing_config"],
"supported_platform_triples": CRATES_VENDOR_ATTRS["supported_platform_triples"],
},
)

# This should be kept in sync with crate_universe/private/crate.bzl.
_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.",
Expand Down Expand Up @@ -651,13 +742,13 @@ _from_specs = tag_class(
"cargo_config": CRATES_VENDOR_ATTRS["cargo_config"],
"generate_binaries": CRATES_VENDOR_ATTRS["generate_binaries"],
"generate_build_scripts": CRATES_VENDOR_ATTRS["generate_build_scripts"],
"splicing_config": CRATES_VENDOR_ATTRS["splicing_config"],
"supported_platform_triples": CRATES_VENDOR_ATTRS["supported_platform_triples"],
},
)

# This should be kept in sync with crate_universe/private/crate.bzl.
_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.",
Expand Down Expand Up @@ -694,6 +785,90 @@ _spec = tag_class(
},
)

_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_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": 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_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 = "",
),
},
)

_conditional_crate_args = {
"arch_dependent": True,
"os_dependent": True,
Expand All @@ -706,7 +881,9 @@ crate = module_extension(
"annotation": _annotation,
"from_cargo": _from_cargo,
"from_specs": _from_specs,
"render_config": _render_config,
"spec": _spec,
"splicing_config": _splicing_config,
},
**_conditional_crate_args
)
Loading
Loading