crate_universe: Added tests for serialized config files. (#1291)
diff --git a/crate_universe/BUILD.bazel b/crate_universe/BUILD.bazel
index 47f3bc8..523e325 100644
--- a/crate_universe/BUILD.bazel
+++ b/crate_universe/BUILD.bazel
@@ -95,6 +95,7 @@
data = glob(["test_data/**"]) + [
"@rules_rust//rust/toolchain:current_exec_cargo_files",
"@rules_rust//rust/toolchain:current_exec_rustc_files",
+ "//crate_universe/test_data/serialized_configs",
],
proc_macro_deps = all_crate_deps(
proc_macro_dev = True,
diff --git a/crate_universe/private/crates_repository.bzl b/crate_universe/private/crates_repository.bzl
index 41ace04..a25e3a0 100644
--- a/crate_universe/private/crates_repository.bzl
+++ b/crate_universe/private/crates_repository.bzl
@@ -27,7 +27,7 @@
generator, generator_sha256 = get_generator(repository_ctx, host_triple.triple)
# Generate a config file for all settings
- config = generate_config(repository_ctx)
+ config_path = generate_config(repository_ctx)
# Locate the lockfile
lockfile = get_lockfile(repository_ctx)
@@ -46,7 +46,7 @@
generator = generator,
lockfile_path = lockfile.path,
lockfile_kind = lockfile.kind,
- config = config.path,
+ config = config_path,
splicing_manifest = splicing_manifest,
cargo = cargo_path,
rustc = rustc_path,
@@ -74,7 +74,7 @@
execute_generator(
repository_ctx = repository_ctx,
generator = generator,
- config = config.path,
+ config = config_path,
splicing_manifest = splicing_manifest,
lockfile_path = lockfile.path,
lockfile_kind = lockfile.kind,
diff --git a/crate_universe/private/crates_vendor.bzl b/crate_universe/private/crates_vendor.bzl
index ceaba69..6846af4 100644
--- a/crate_universe/private/crates_vendor.bzl
+++ b/crate_universe/private/crates_vendor.bzl
@@ -1,6 +1,6 @@
"""Rules for vendoring Bazel targets into existing workspaces"""
-load("//crate_universe/private:generate_utils.bzl", "collect_crate_annotations", "render_config")
+load("//crate_universe/private:generate_utils.bzl", "compile_config", "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")
@@ -104,14 +104,6 @@
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)
@@ -135,16 +127,16 @@
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,
+ config_data = compile_config(
+ crate_annotations = ctx.attr.annotations,
generate_build_scripts = ctx.attr.generate_build_scripts,
cargo_config = None,
+ render_config = rendering_config,
supported_platform_triples = ctx.attr.supported_platform_triples,
+ repository_name = ctx.attr.repository_name or ctx.label.name,
)
config = _write_data_file(
diff --git a/crate_universe/private/generate_utils.bzl b/crate_universe/private/generate_utils.bzl
index 2329b55..95ce36f 100644
--- a/crate_universe/private/generate_utils.bzl
+++ b/crate_universe/private/generate_utils.bzl
@@ -182,16 +182,80 @@
return repository_ctx.read(config)
return None
+def _update_render_config(config, repository_name):
+ """Add the repository name to the render config
+
+ Args:
+ config (dict): A `render_config` struct
+ repository_name (str): The name of the repository that owns the config
+
+ Returns:
+ struct: An updated `render_config`.
+ """
+
+ # Add the repository name as it's very relevant to rendering.
+ config.update({"repository_name": repository_name})
+
+ return struct(**config)
+
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 config
- return struct(**config)
+def compile_config(crate_annotations, generate_build_scripts, cargo_config, render_config, supported_platform_triples, repository_name, repository_ctx = None):
+ """Create a config file for generating crate targets
+
+ [cargo_config]: https://doc.rust-lang.org/cargo/reference/config.html
+
+ Args:
+ crate_annotations (dict): Extra settings to apply to crates. See
+ `crates_repository.annotations` or `crates_vendor.annotations`.
+ generate_build_scripts (bool): Whether or not to globally disable build scripts.
+ cargo_config (str): The optional contents of a [Cargo config][cargo_config].
+ render_config (dict): The deserialized dict of the `render_config` function.
+ supported_platform_triples (list): A list of platform triples
+ repository_name (str): The name of the repository being generated
+ repository_ctx (repository_ctx, optional): A repository context object used for enabling
+ certain functionality.
+
+ Returns:
+ struct: A struct matching a `cargo_bazel::config::Config`.
+ """
+ annotations = collect_crate_annotations(crate_annotations, repository_name)
+
+ # Load additive build files if any have been provided.
+ unexpected = []
+ for name, data in annotations.items():
+ f = data.pop("additive_build_file", None)
+ if f and not repository_ctx:
+ unexpected.append(name)
+ f = None
+ content = [x for x in [
+ data.pop("additive_build_file_content", None),
+ repository_ctx.read(Label(f)) if f else None,
+ ] if x]
+ if content:
+ data.update({"additive_build_file_content": "\n".join(content)})
+
+ if unexpected:
+ fail("The following annotations use `additive_build_file` which is not supported for {}: {}".format(repository_name, unexpected))
+
+ config = struct(
+ generate_build_scripts = generate_build_scripts,
+ annotations = annotations,
+ cargo_config = cargo_config,
+ rendering = _update_render_config(
+ config = render_config,
+ repository_name = repository_name,
+ ),
+ supported_platform_triples = supported_platform_triples,
+ )
+
+ return config
def generate_config(repository_ctx):
"""Generate a config file from various attributes passed to the rule.
@@ -202,24 +266,15 @@
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.
- for data in annotations.values():
- f = data.pop("additive_build_file", None)
- content = [x for x in [
- data.pop("additive_build_file_content", None),
- repository_ctx.read(Label(f)) if f else None,
- ] if x]
- if content:
- data.update({"additive_build_file_content": "\n".join(content)})
-
- config = struct(
+ config = compile_config(
+ crate_annotations = repository_ctx.attr.annotations,
generate_build_scripts = repository_ctx.attr.generate_build_scripts,
- annotations = annotations,
cargo_config = _read_cargo_config(repository_ctx),
- rendering = _get_render_config(repository_ctx),
+ render_config = _get_render_config(repository_ctx),
supported_platform_triples = repository_ctx.attr.supported_platform_triples,
+ repository_name = repository_ctx.name,
+ repository_ctx = repository_ctx,
)
config_path = repository_ctx.path("cargo-bazel.json")
@@ -228,13 +283,7 @@
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,
- )
+ return config_path
def get_lockfile(repository_ctx):
"""Locate the lockfile and identify the it's type (Cargo or Bazel).
diff --git a/crate_universe/private/splicing_utils.bzl b/crate_universe/private/splicing_utils.bzl
index 249e7a4..44ca076 100644
--- a/crate_universe/private/splicing_utils.bzl
+++ b/crate_universe/private/splicing_utils.bzl
@@ -92,6 +92,38 @@
for (key, val) in data.items()
}
+def compile_splicing_manifest(splicing_config, manifests, cargo_config_path, packages):
+ """Produce a manifest containing required components for splciing a new Cargo workspace
+
+ [cargo_config]: https://doc.rust-lang.org/cargo/reference/config.html
+ [cargo_toml]: https://doc.rust-lang.org/cargo/reference/manifest.html
+
+ Args:
+ splicing_config (dict): A deserialized `splicing_config`
+ manifests (dict): A mapping of paths to Bazel labels which represent [Cargo manifests][cargo_toml].
+ cargo_config_path (str): The absolute path to a [Cargo config][cargo_config].
+ packages (dict): A set of crates (packages) specifications to depend on
+
+ Returns:
+ dict: A dictionary representation of a `cargo_bazel::splicing::SplicingManifest`
+ """
+
+ # 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 packages.items()
+ }
+
+ # Auto-generated splicier manifest values
+ splicing_manifest_content = {
+ "cargo_config": cargo_config_path,
+ "direct_packages": direct_packages_info,
+ "manifests": manifests,
+ }
+
+ return dict(splicing_config.items() + splicing_manifest_content.items())
+
def create_splicing_manifest(repository_ctx):
"""Produce a manifest containing required components for splciing a new Cargo workspace
@@ -101,14 +133,6 @@
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}
@@ -120,19 +144,22 @@
# 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,
- }
+ repo_dir = repository_ctx.path(".")
+
+ splicing_manifest = repository_ctx.path("{}/splicing_manifest.json".format(repo_dir))
+
+ data = compile_splicing_manifest(
+ splicing_config = config,
+ manifests = manifests,
+ cargo_config_path = cargo_config,
+ packages = repository_ctx.attr.packages,
+ )
# 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()),
+ data,
indent = " " * 4,
),
)
diff --git a/crate_universe/src/config.rs b/crate_universe/src/config.rs
index 66e3a7e..ec3a0f7 100644
--- a/crate_universe/src/config.rs
+++ b/crate_universe/src/config.rs
@@ -492,4 +492,36 @@
id.version = "<1".to_owned();
assert!(!id.matches(&package));
}
+
+ #[test]
+ fn deserialize_config() {
+ let runfiles = runfiles::Runfiles::create().unwrap();
+ let path = runfiles
+ .rlocation("rules_rust/crate_universe/test_data/serialized_configs/config.json");
+
+ let content = std::fs::read_to_string(path).unwrap();
+
+ let config: Config = serde_json::from_str(&content).unwrap();
+
+ println!("{:#?}", config);
+ // Annotations
+ let annotation = config
+ .annotations
+ .get(&CrateId::new("rand".to_owned(), "0.8.5".to_owned()))
+ .unwrap();
+ assert_eq!(
+ annotation.crate_features,
+ Some(BTreeSet::from(["small_rng".to_owned()]))
+ );
+
+ // Global settings
+ assert!(config.cargo_config.is_none());
+ assert!(!config.generate_build_scripts);
+
+ // Render Config
+ assert_eq!(
+ config.rendering.platforms_template,
+ "//custom/platform:{triple}"
+ );
+ }
}
diff --git a/crate_universe/src/splicing.rs b/crate_universe/src/splicing.rs
index 0de1daa..74e012e 100644
--- a/crate_universe/src/splicing.rs
+++ b/crate_universe/src/splicing.rs
@@ -493,3 +493,52 @@
Ok(lockfile)
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ use std::path::PathBuf;
+
+ #[test]
+ fn deserialize_splicing_manifest() {
+ let runfiles = runfiles::Runfiles::create().unwrap();
+ let path = runfiles.rlocation(
+ "rules_rust/crate_universe/test_data/serialized_configs/splicing_manifest.json",
+ );
+
+ let content = std::fs::read_to_string(path).unwrap();
+
+ let manifest: SplicingManifest = serde_json::from_str(&content).unwrap();
+
+ // Check splicing configs
+ assert_eq!(manifest.resolver_version, cargo_toml::Resolver::V2);
+
+ // Check manifests
+ assert_eq!(manifest.manifests.len(), 1);
+ let maniefst_label = manifest
+ .manifests
+ .get(&PathBuf::from("/tmp/abs/path/workspace/Cargo.toml"))
+ .unwrap();
+ assert_eq!(maniefst_label, &Label::from_str("//:Cargo.toml").unwrap());
+
+ // Check packages
+ assert_eq!(manifest.direct_packages.len(), 1);
+ let package = manifest.direct_packages.get("rand").unwrap();
+ assert_eq!(
+ package,
+ &cargo_toml::DependencyDetail {
+ default_features: Some(false),
+ features: vec!["small_rng".to_owned()],
+ version: Some("0.8.5".to_owned()),
+ ..Default::default()
+ }
+ );
+
+ // Check cargo config
+ assert_eq!(
+ manifest.cargo_config,
+ Some(PathBuf::from("/tmp/abs/path/workspace/.cargo/config.toml"))
+ );
+ }
+}
diff --git a/crate_universe/test_data/serialized_configs/BUILD.bazel b/crate_universe/test_data/serialized_configs/BUILD.bazel
new file mode 100644
index 0000000..81d8bc4
--- /dev/null
+++ b/crate_universe/test_data/serialized_configs/BUILD.bazel
@@ -0,0 +1,64 @@
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+load("//crate_universe:defs.bzl", "crate", "render_config", "splicing_config")
+
+# buildifier: disable=bzl-visibility
+load("//crate_universe/private:generate_utils.bzl", "compile_config")
+
+# buildifier: disable=bzl-visibility
+load("//crate_universe/private:splicing_utils.bzl", "compile_splicing_manifest")
+
+write_file(
+ name = "config",
+ out = "config.json",
+ content = [json.encode(
+ compile_config(
+ cargo_config = None,
+ crate_annotations = {
+ "rand": [crate.annotation(
+ crate_features = ["small_rng"],
+ version = "0.8.5",
+ )],
+ },
+ generate_build_scripts = False,
+ render_config = json.decode(render_config(
+ platforms_template = "//custom/platform:{triple}",
+ )),
+ repository_name = "mock_config",
+ supported_platform_triples = [
+ "x86_64-unknown-linux-gnu",
+ "x86_64-pc-windows-msvc",
+ "x86_64-apple-darwin",
+ ],
+ ),
+ ).strip()],
+ newline = "unix",
+)
+
+write_file(
+ name = "splicing_manifest",
+ out = "splicing_manifest.json",
+ content = [json.encode(compile_splicing_manifest(
+ cargo_config_path = "/tmp/abs/path/workspace/.cargo/config.toml",
+ manifests = {"/tmp/abs/path/workspace/Cargo.toml": "//:Cargo.toml"},
+ packages = {
+ "rand": crate.spec(
+ default_features = False,
+ features = ["small_rng"],
+ version = "0.8.5",
+ ),
+ },
+ splicing_config = dict(json.decode(splicing_config(
+ resolver_version = "2",
+ ))),
+ )).strip()],
+ newline = "unix",
+)
+
+filegroup(
+ name = "serialized_configs",
+ srcs = [
+ "config.json",
+ "splicing_manifest.json",
+ ],
+ visibility = ["//crate_universe:__pkg__"],
+)