diff --git a/crate_universe/module_extensions/BUILD.bazel b/crate_universe/module_extensions/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crate_universe/module_extensions/crate.bzl b/crate_universe/module_extensions/crate.bzl new file mode 100644 index 0000000000..63aace1a18 --- /dev/null +++ b/crate_universe/module_extensions/crate.bzl @@ -0,0 +1,226 @@ +"""Module extension for generating third-party crates for use in bazel.""" + +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:defs.bzl", _crate_universe_crate = "crate") +load("//crate_universe/private:crates_vendor.bzl", "CRATES_VENDOR_ATTRS", "write_config_file", "write_splicing_manifest") +load("//crate_universe/private:generate_utils.bzl", "render_config") +load("//crate_universe/private/module_extensions:cargo_bazel_bootstrap.bzl", "get_cargo_bazel_runner") + +def _generate_repo_impl(repo_ctx): + for path, contents in repo_ctx.attr.contents.items(): + repo_ctx.file(path, contents) + +_generate_repo = repository_rule( + implementation = _generate_repo_impl, + attrs = dict( + contents = attr.string_dict(mandatory = True), + ), +) + +def _generate_annotations(module_ctx, annotation_files): + annotations = {} + + def add_annotation(k, v): + if k not in annotations: + annotations[k] = [] + annotations[k].append(v) + + for file in annotation_files: + for name, annotations_for_crate in json.decode(module_ctx.read(file)).items(): + for annotation_for_crate in annotations_for_crate: + add_annotation(name, _crate_universe_crate.annotation(**annotation_for_crate)) + + return annotations + +def _crate_impl(module_ctx): + cargo_bazel = get_cargo_bazel_runner(module_ctx) + + for mod in module_ctx.modules: + mod_path = module_ctx.path(mod.name) + + # At the moment, we namespace each different instance of the module + # extension. This ensures that if I do the following: + # crate.from_cargo(manifests=["a/Cargo.toml"], suffix="a") + # crate.from_cargo(manifests=["b/Cargo.toml"], suffix="b") + # + # If a/Cargo.toml declares a dep on anyhow with no features, and + # b/Cargo.toml declares a dep on anyhow with the "backtrace" feature, + # then "crates_a//:anyhow" won't be able to use backtrace. + # + # However, it also means that if they use the exact same config, then + # we'll have to build it twice. + # + # This is a non-issue if using cargo workspaces, which I'd generally + # recommend anyway, but in the long term we may want to consider sharing + # repos if they use the same configuration. + for cfg in mod.tags.from_cargo: + annotations = _generate_annotations(module_ctx, cfg.annotation_files) + tag_path = mod_path + repo_name = mod.name + "_crates" + if cfg.suffix: + tag_path = mod_path.get_child(cfg.suffix) + repo_name += "_" + cfg.suffix + + cargo_lockfile = module_ctx.path(cfg.cargo_lockfile) + + def write_data_file( + ctx, # @unused + name, + data): + path = tag_path.get_child(name) # buildifier: disable=uninitialized + module_ctx.file(path, content = data, executable = False) + return path + + rendering_config = json.decode(render_config( + regen_command = "Run 'cargo update [--workspace]'", + )) + config_file = write_config_file( + module_ctx, + mode = "remote", + annotations = annotations, + generate_build_scripts = cfg.generate_build_scripts, + supported_platform_triples = cfg.supported_platform_triples, + repository_name = repo_name, + output_pkg = repo_name, + repo_name = repo_name, + write_data_file = write_data_file, + generate_binaries = cfg.generate_binaries, + rendering_config = rendering_config, + ) + + manifests = {module_ctx.path(m): m for m in cfg.manifests} + splicing_manifest = write_splicing_manifest( + module_ctx, + packages = {}, + splicing_config = "", + cargo_config = cfg.cargo_config, + manifests = {str(k): str(v) for k, v in manifests.items()}, + write_data_file = write_data_file, + manifest_to_path = module_ctx.path, + ) + + splicing_output_dir = tag_path.get_child("splicing-output") + cargo_bazel([ + "splice", + "--output-dir", + splicing_output_dir, + "--config", + config_file, + "--splicing-manifest", + splicing_manifest, + "--cargo-lockfile", + cargo_lockfile, + ]) + + # Create a lockfile, since we need to parse it to generate spoke + # repos. + lockfile_path = tag_path.get_child("lockfile.json") + module_ctx.file(lockfile_path, "") + + # cargo-bazel generate takes the lockfile as input, but also writes + # to the lockfile. This means that even though nothing changes, the + # modified timestamp of the file is updated. Since the lock file is + # an input to the rule, this would invalidate the repo rule, + # requiring it to be rerun on every invocation. + # To solve this, we allow it to touch a copy of the lock file, + # rather than the original. + cargo_lockfile_copy = tag_path.get_child("copy/Cargo.lock") + module_ctx.file( + cargo_lockfile_copy, + module_ctx.read(cargo_lockfile), + ) + cargo_bazel([ + "generate", + "--cargo-lockfile", + cargo_lockfile_copy, + "--config", + config_file, + "--splicing-manifest", + splicing_manifest, + "--repository-dir", + tag_path, + "--metadata", + splicing_output_dir.get_child("metadata.json"), + "--repin", + "--lockfile", + lockfile_path, + ]) + + crates_dir = tag_path.get_child(repo_name) + _generate_repo( + name = repo_name, + contents = { + "BUILD.bazel": module_ctx.read(crates_dir.get_child("BUILD.bazel")), + }, + ) + + contents = json.decode(module_ctx.read(lockfile_path)) + + for crate in contents["crates"].values(): + repo = crate["repository"] + if repo == None: + continue + name = crate["name"] + version = crate["version"] + + # "+" isn't valid in a repo name. + crate_repo_name = "%s__%s-%s" % (repo_name, name, version.replace("+", "-")) + + build_file_content = module_ctx.read(crates_dir.get_child("BUILD.%s-%s.bazel" % (name, version))) + if "Http" in repo: + # Replicates functionality in repo_http.j2. + 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["sha256"], + type = "tar.gz", + urls = [repo["url"]], + strip_prefix = "%s-%s" % (crate["name"], crate["version"]), + build_file_content = build_file_content, + ) + elif "Git" in repo: + # Replicates functionality in repo_git.j2 + repo = repo["Git"] + 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), + **repo["commitish"] + ) + else: + fail("Invalid repo: expected Http or Git to exist for crate %s-%s, got %s" % (name, version, repo)) + +_from_cargo = tag_class( + doc = "Generates a repo _crates", + attrs = dict( + suffix = attr.string( + doc = "If provided, instead generates a repo _crates_. " + + "This can help avoid conflicts if you declare multiple from_cargo in a single module.", + ), + cargo_lockfile = CRATES_VENDOR_ATTRS["cargo_lockfile"], + manifests = CRATES_VENDOR_ATTRS["manifests"], + cargo_config = CRATES_VENDOR_ATTRS["cargo_config"], + generate_binaries = CRATES_VENDOR_ATTRS["generate_binaries"], + generate_build_scripts = CRATES_VENDOR_ATTRS["generate_build_scripts"], + supported_platform_triples = CRATES_VENDOR_ATTRS["supported_platform_triples"], + annotation_files = attr.label_list(allow_files = [".json"]), + ), +) + +crate = module_extension( + implementation = _crate_impl, + tag_classes = dict( + from_cargo = _from_cargo, + ), +) diff --git a/crate_universe/private/crates_vendor.bzl b/crate_universe/private/crates_vendor.bzl index 3cd6ed533b..5247a4649d 100644 --- a/crate_universe/private/crates_vendor.bzl +++ b/crate_universe/private/crates_vendor.bzl @@ -1,7 +1,7 @@ """Rules for vendoring Bazel targets into existing workspaces""" 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:splicing_utils.bzl", "kebab_case_keys", generate_splicing_config = "splicing_config") load("//crate_universe/private:urls.bzl", "CARGO_BAZEL_LABEL") load("//rust/platform:triple_mappings.bzl", "SUPPORTED_PLATFORM_TRIPLES") @@ -93,24 +93,41 @@ def _prepare_manifest_path(target): return "${build_workspace_directory}/" + manifest.short_path def _write_splicing_manifest(ctx): + # Manifests are required to be single files + manifests = {_prepare_manifest_path(m): str(m.label) for m in ctx.attr.manifests} + + manifest = write_splicing_manifest( + ctx, + write_data_file = _write_data_file, + packages = ctx.attr.packages, + splicing_config = ctx.attr.splicing_config, + cargo_config = ctx.attr.cargo_config, + manifests = manifests, + manifest_to_path = _prepare_manifest_path, + ) + + is_windows = _is_windows(ctx) + + args = ["--splicing-manifest", _runfiles_path(manifest, is_windows)] + runfiles = [manifest] + ctx.files.manifests + ([ctx.file.cargo_config] if ctx.attr.cargo_config else []) + return args, runfiles + +def write_splicing_manifest(ctx, write_data_file, packages, splicing_config, cargo_config, manifests, manifest_to_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 ctx.attr.packages.items() + for (pkg, data) in packages.items() } - # Manifests are required to be single files - manifests = {_prepare_manifest_path(m): str(m.label) for m in ctx.attr.manifests} - - config = json.decode(ctx.attr.splicing_config or splicing_config()) + config = json.decode(splicing_config or generate_splicing_config()) splicing_manifest_content = { - "cargo_config": _prepare_manifest_path(ctx.attr.cargo_config) if ctx.attr.cargo_config else None, + "cargo_config": manifest_to_path(cargo_config) if cargo_config else None, "direct_packages": direct_packages_info, "manifests": manifests, } - manifest = _write_data_file( + return write_data_file( ctx = ctx, name = "cargo-bazel-splicing-manifest.json", data = json.encode_indent( @@ -119,27 +136,67 @@ def _write_splicing_manifest(ctx): ), ) - is_windows = _is_windows(ctx) +def _write_config_file(ctx): + workspace_name = ctx.workspace_name + if workspace_name == "__main__": + workspace_name = "" - args = ["--splicing-manifest", _runfiles_path(manifest, is_windows)] - runfiles = [manifest] + ctx.files.manifests + ([ctx.file.cargo_config] if ctx.attr.cargo_config else []) - return args, runfiles + config = write_config_file( + ctx, + mode = ctx.attr.mode, + annotations = ctx.attr.annotations, + generate_binaries = ctx.attr.generate_binaries, + generate_build_scripts = ctx.attr.generate_build_scripts, + supported_platform_triples = ctx.attr.supported_platform_triples, + repository_name = ctx.attr.repository_name, + output_pkg = _get_output_package(ctx), + repo_name = workspace_name, + write_data_file = _write_data_file, + rendering_config = dict(json.decode(ctx.attr.render_config)) if ctx.attr.render_config else None, + ) -def _write_config_file(ctx): - default_render_config = dict(json.decode(render_config())) + is_windows = _is_windows(ctx) + args = ["--config", _runfiles_path(config, is_windows)] + runfiles = [config] + ctx.files.manifests + return args, runfiles - if ctx.attr.render_config: - rendering_config = dict(json.decode(ctx.attr.render_config)) - else: - rendering_config = default_render_config +def write_config_file( + ctx, + mode, + annotations, + generate_binaries, + generate_build_scripts, + supported_platform_triples, + repository_name, + output_pkg, + repo_name, + write_data_file, + rendering_config): + """Writes the rendering config to cargo-bazel-config.json. - output_pkg = _get_output_package(ctx) + Args: + ctx: The rule's context. + mode (str): The vendoring mode. + annotations: Any annotations provided. + generate_binaries (bool): Whether to generate binaries for the crates. + generate_build_scripts (bool): Whether to generate BUILD.bazel files. + supported_platform_triples (str): The platform triples to support in + the generated BUILD.bazel files. + repository_name (str): The name of the repository to generate. + output_pkg: The path to the package containing the build files. + repo_name (str): The name of the repo being generated. + write_data_file: A function that takes in ctx, the filename, and the + data to write, and writes the specified file to the generated repo. + rendering_config: The rendering config to use. - workspace_name = ctx.workspace_name - if ctx.workspace_name == "__main__": - workspace_name = "" + Returns: + file: The cargo-bazel-config.json written. + """ + default_render_config = json.decode(render_config()) + if rendering_config == None: + rendering_config = default_render_config - if ctx.attr.mode == "local": + if mode == "local": build_file_base_template = "@{}//{}/{{name}}-{{version}}:BUILD.bazel" crate_label_template = "//{}/{{name}}-{{version}}:{{target}}".format( output_pkg, @@ -150,15 +207,15 @@ def _write_config_file(ctx): updates = { "build_file_template": build_file_base_template.format( - workspace_name, + repo_name, output_pkg, ), "crate_label_template": crate_label_template, "crates_module_template": "@{}//{}:{{file}}".format( - workspace_name, + repo_name, output_pkg, ), - "vendor_mode": ctx.attr.mode, + "vendor_mode": mode, } for key in updates: @@ -175,16 +232,16 @@ def _write_config_file(ctx): rendering_config.update({"regen_command": "bazel run {}".format(ctx.label)}) config_data = compile_config( - crate_annotations = ctx.attr.annotations, - generate_binaries = ctx.attr.generate_binaries, - generate_build_scripts = ctx.attr.generate_build_scripts, + crate_annotations = annotations, + generate_binaries = generate_binaries, + generate_build_scripts = 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, + supported_platform_triples = supported_platform_triples, + repository_name = repository_name or ctx.label.name, ) - config = _write_data_file( + return write_data_file( ctx = ctx, name = "cargo-bazel-config.json", data = json.encode_indent( @@ -193,11 +250,6 @@ def _write_config_file(ctx): ), ) - is_windows = _is_windows(ctx) - args = ["--config", _runfiles_path(config, is_windows)] - runfiles = [config] + ctx.files.manifests - return args, runfiles - def _crates_vendor_impl(ctx): toolchain = ctx.toolchains[Label("@rules_rust//rust:toolchain_type")] is_windows = _is_windows(ctx) @@ -282,6 +334,99 @@ def _crates_vendor_impl(ctx): executable = runner, ) +CRATES_VENDOR_ATTRS = { + "annotations": attr.string_list_dict( + doc = "Extra settings to apply to crates. See [crate.annotation](#crateannotation).", + ), + "bazel": attr.label( + doc = "The path to a bazel binary used to locate the output_base for the current workspace.", + cfg = "exec", + executable = True, + allow_files = True, + ), + "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, + allow_files = 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, + ), + "cargo_config": attr.label( + doc = "A [Cargo configuration](https://doc.rust-lang.org/cargo/reference/config.html) file.", + allow_single_file = True, + ), + "cargo_lockfile": attr.label( + doc = "The path to an existing `Cargo.lock` file", + allow_single_file = True, + ), + "generate_binaries": attr.bool( + doc = ( + "Whether to generate `rust_binary` targets for all the binary crates in every package. " + + "By default only the `rust_library` targets are generated." + ), + default = False, + ), + "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).", + ), + "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." + ), + ), + "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", + ), +} + crates_vendor = rule( implementation = _crates_vendor_impl, doc = """\ @@ -356,98 +501,7 @@ call against the generated workspace. The following table describes how to contr | `package_name@1.2.3` | `cargo upgrade --package package_name --precise 1.2.3` | """, - attrs = { - "annotations": attr.string_list_dict( - doc = "Extra settings to apply to crates. See [crate.annotation](#crateannotation).", - ), - "bazel": attr.label( - doc = "The path to a bazel binary used to locate the output_base for the current workspace.", - cfg = "exec", - executable = True, - allow_files = True, - ), - "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, - allow_files = 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, - ), - "cargo_config": attr.label( - doc = "A [Cargo configuration](https://doc.rust-lang.org/cargo/reference/config.html) file.", - allow_single_file = True, - ), - "cargo_lockfile": attr.label( - doc = "The path to an existing `Cargo.lock` file", - allow_single_file = True, - ), - "generate_binaries": attr.bool( - doc = ( - "Whether to generate `rust_binary` targets for all the binary crates in every package. " + - "By default only the `rust_library` targets are generated." - ), - default = False, - ), - "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).", - ), - "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." - ), - ), - "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", - ), - }, + attrs = CRATES_VENDOR_ATTRS, executable = True, toolchains = ["@rules_rust//rust:toolchain_type"], ) diff --git a/examples/bzlmod/crate_universe/.bazelrc b/examples/bzlmod/crate_universe/.bazelrc new file mode 100644 index 0000000000..e2ece0c386 --- /dev/null +++ b/examples/bzlmod/crate_universe/.bazelrc @@ -0,0 +1 @@ +build --experimental_enable_bzlmod diff --git a/examples/bzlmod/crate_universe/.gitignore b/examples/bzlmod/crate_universe/.gitignore new file mode 100644 index 0000000000..a6ef824c1f --- /dev/null +++ b/examples/bzlmod/crate_universe/.gitignore @@ -0,0 +1 @@ +/bazel-* diff --git a/examples/bzlmod/crate_universe/BUILD.bazel b/examples/bzlmod/crate_universe/BUILD.bazel new file mode 100644 index 0000000000..2c1e258c5a --- /dev/null +++ b/examples/bzlmod/crate_universe/BUILD.bazel @@ -0,0 +1,15 @@ +load("@rules_rust//rust:defs.bzl", "rust_binary") + +rust_binary( + name = "hello_log", + srcs = ["src/main.rs"], + # At the moment, the only easy-to-use use case for bzlmod crate_universe is + # to have a dummy crate which contains all your dependencies for *all* your + # rust code, and then each rust target will declare what it depends on. + # This is the equivalent of having a cargo workspace, and declaring in this + # crates' cargo.toml "env_logger.workspace = true". + deps = [ + "@crates//:env_logger", + "@crates//:log", + ], +) diff --git a/examples/bzlmod/crate_universe/Cargo.lock b/examples/bzlmod/crate_universe/Cargo.lock new file mode 100644 index 0000000000..efb8305ad2 --- /dev/null +++ b/examples/bzlmod/crate_universe/Cargo.lock @@ -0,0 +1,143 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "demo" +version = "0.1.0" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/examples/bzlmod/crate_universe/Cargo.toml b/examples/bzlmod/crate_universe/Cargo.toml new file mode 100644 index 0000000000..116f603c90 --- /dev/null +++ b/examples/bzlmod/crate_universe/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "demo" +version = "0.1.0" +edition = "2021" + +[dependencies] +env_logger = "0.9.2" +log = "0.4.17" \ No newline at end of file diff --git a/examples/bzlmod/crate_universe/MODULE.bazel b/examples/bzlmod/crate_universe/MODULE.bazel new file mode 100644 index 0000000000..1b2b99a0a2 --- /dev/null +++ b/examples/bzlmod/crate_universe/MODULE.bazel @@ -0,0 +1,34 @@ +module( + name = "crates_repository", + version = "1.0", +) + +bazel_dep(name = "rules_rust", version = "0.20.0") +local_path_override( + module_name = "rules_rust", + path = "../../..", +) + +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain(edition = "2021") +use_repo( + rust, + "rust_toolchains", +) + +register_toolchains("@rust_toolchains//:all") + +# Use a toml file to generate your dependencies like so: +crate = use_extension("@rules_rust//rust:extensions.bzl", "crate") +crate.from_cargo( + # This is optional, but if you need crate.annotation, then this is the + # bzlmod equivalent. + annotation_files = ["//:annotations.json"], + cargo_lockfile = "//:Cargo.lock", + manifests = ["//:Cargo.toml"], +) +use_repo( + crate, + # Rename it from _crates to crates + crates = "crates_repository_crates", +) diff --git a/examples/bzlmod/crate_universe/WORKSPACE b/examples/bzlmod/crate_universe/WORKSPACE new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/bzlmod/crate_universe/annotations.json b/examples/bzlmod/crate_universe/annotations.json new file mode 100644 index 0000000000..3d5102e9fa --- /dev/null +++ b/examples/bzlmod/crate_universe/annotations.json @@ -0,0 +1,13 @@ +{ + "env_logger": [ + { + "version": "0.9.2", + "additive_build_file_content": "\n# Extra stuff here" + } + ], + "log": [ + { + "additive_build_file_content": "\n# Extra stuff here" + } + ] +} \ No newline at end of file diff --git a/examples/bzlmod/crate_universe/src/main.rs b/examples/bzlmod/crate_universe/src/main.rs new file mode 100644 index 0000000000..14e1f53e94 --- /dev/null +++ b/examples/bzlmod/crate_universe/src/main.rs @@ -0,0 +1,22 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +fn main() { + env_logger::init(); + if log::log_enabled!(log::Level::Info) { + println!("Info enabled"); + } else { + println!("Info disabled"); + } +} diff --git a/rust/extensions.bzl b/rust/extensions.bzl index 5f39d768f7..316bfc31d4 100644 --- a/rust/extensions.bzl +++ b/rust/extensions.bzl @@ -1,5 +1,7 @@ "Module extensions for using rules_rust with bzlmod" +load("//crate_universe/module_extensions:crate.bzl", _crate = "crate") load("//rust/private/module_extensions:toolchain.bzl", _rust = "rust") +crate = _crate rust = _rust