Skip to content

Is there a way to have a rust_library where the cargo root is generated with cargo_build_script? #3757

@mrkkrp

Description

@mrkkrp

The title states most of my question. For example, we can imagine something like this:

cargo_build_script(
    name = "build_script",
    srcs = ["build.rs"],
    compile_data = [":Cargo.toml"],
    crate_root = "build.rs",
    visibility = ["//visibility:public"],
)

rust_library(
    name = "example",
    deps = [":build_script"],
    compile_data = ["Cargo.toml"],
    visibility = ["//visibility:public"],
)

Here, build_script produces lib.rs that then should become the crate root of example.

This simply fails the check in rust/private/utils.bzl, line 786:

Error in fail: Couldn't find lib.rs or example.rs among `srcs`, please use `crate_root` to specify the root file.

AFAUI the build script should go into deps, not srcs; build_script produces a directory which is kind of opaque to Bazel (the below error is with srcs = [":build_script"]:

Error in add: Cannot add directories to Args#add since they may expand to multiple values. Either use Args#add_all (if you want expansion) or args.add(directory.path) (if you do not).

I thought generated crate roots are not at all supported. If so, I'd like your thoughts on how to best add this feature, should cargo_build_script declare the files it generates and then pass this information around in the BuildInfo provider? Because crate_root inside rust_library_common is a File, not a string, so it has to be declared as an individual file. Alternatively, if crate_root can become a string, then maybe cargo_build_script could pre-declare its generated cargo root while still declaring a directory

out_dir = ctx.actions.declare_directory(ctx.label.name + ".out_dir")

and then this could be taken into account in rust_library_common.

Then, while reading the source code I noticed that some support for generated source files is definitely there, e.g.:

def transform_sources(ctx, srcs, compile_data, crate_root):
"""Creates symlinks of the source files if needed.
Rustc assumes that the source files are located next to the crate root.
In case of a mix between generated and non-generated source files, this
we violate this assumption, as part of the sources will be located under
bazel-out/... . In order to allow for targets that contain both generated
and non-generated source files, we generate symlinks for all non-generated
files.
Args:
ctx (struct): The current rule's context.
srcs (List[File]): The sources listed in the `srcs` attribute
compile_data (List[File]): The sources listed in the `compile_data`
attribute
crate_root (File): The file specified in the `crate_root` attribute,
if it exists, otherwise None
Returns:
Tuple(List[File], List[File], File): The transformed srcs, compile_data
and crate_root
"""
has_generated_sources = (
len([src for src in srcs if not src.is_source]) +
len([src for src in compile_data if not src.is_source]) >
0
)
if not has_generated_sources:
return srcs, compile_data, crate_root
package_root = paths.join(ctx.label.workspace_root, ctx.label.package)
generated_sources = [_symlink_for_non_generated_source(ctx, src, package_root) for src in srcs if src != crate_root]
generated_compile_data = [_symlink_for_non_generated_source(ctx, src, package_root) for src in compile_data]
generated_root = crate_root
if crate_root:
generated_root = _symlink_for_non_generated_source(ctx, crate_root, package_root)
generated_sources.append(generated_root)
return generated_sources, generated_compile_data, generated_root

But it does not seem to support generated crate roots. The word "generated" seems to have two different meanings in this function:

  • the first is whether it is a generated file as opposed to a source that returns True for src.is_source, the variable name has_generated_sources employs this meaning;
  • the second is whether it is a symlink that is generated in the transform_sources function, the variable names generated_sources, generated_compile_data, and generated_root employ the latter meaning.

It would appear from reading the source code that :build_script should be added to compile_data then at least symlink creation will be initiated. But rules_rust still complains about the missing crate root in that case. If crate root is specified as crate_root = "build_script.out_dir/lib.rs" then a new errors results:

ERROR: One of the output paths 'bazel-out/k8-fastbuild/bin/build_script.out_dir' (belonging to //:build_script) and 'bazel-out/k8-fastbuild/bin/build_script.out_dir/lib.rs' (belonging to //:example) is a prefix of the other. These actions cannot be simultaneously present; please rename one of the output files or build just one of them

Specifying crate_root = "lib.rs" simply complains about non-existing //:lib.rs file, indeed the crate_root attribute is a Label.

So, I'd like to know how this is supposed to work. I am willing to implement support for this as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions