• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""A module defining a repository rule for vendoring the dependencies
2of a crate in the current workspace.
3"""
4
5load("@rules_rust//rust:repositories.bzl", "DEFAULT_RUST_VERSION", "load_arbitrary_tool")
6
7def _impl(repository_ctx):
8    # Link cxx repository into @third-party.
9    lockfile = repository_ctx.path(repository_ctx.attr.lockfile)
10    workspace = lockfile.dirname.dirname
11    repository_ctx.symlink(workspace, "workspace")
12
13    # Copy third-party/Cargo.lock since those are the crate versions that the
14    # BUILD file is written against.
15    vendor_lockfile = repository_ctx.path("workspace/third-party/Cargo.lock")
16    root_lockfile = repository_ctx.path("workspace/Cargo.lock")
17    _copy_file(repository_ctx, src = vendor_lockfile, dst = root_lockfile)
18
19    # Figure out which version of cargo to use.
20    if repository_ctx.attr.target_triple:
21        target_triple = repository_ctx.attr.target_triple
22    elif "mac" in repository_ctx.os.name:
23        target_triple = "x86_64-apple-darwin"
24    elif "windows" in repository_ctx.os.name:
25        target_triple = "x86_64-pc-windows-msvc"
26    else:
27        target_triple = "x86_64-unknown-linux-gnu"
28
29    # Download cargo.
30    load_arbitrary_tool(
31        ctx = repository_ctx,
32        tool_name = "cargo",
33        tool_subdirectories = ["cargo"],
34        version = repository_ctx.attr.cargo_version,
35        iso_date = repository_ctx.attr.cargo_iso_date,
36        target_triple = target_triple,
37    )
38
39    cmd = ["{}/bin/cargo".format(repository_ctx.path(".")), "vendor", "--versioned-dirs", "third-party/vendor"]
40    result = repository_ctx.execute(
41        cmd,
42        quiet = True,
43        working_directory = "workspace",
44    )
45    _log_cargo_vendor(repository_ctx, result)
46    if result.return_code != 0:
47        fail("failed to execute `{}`".format(" ".join(cmd)))
48
49    # Copy lockfile back to third-party/Cargo.lock to reflect any modification
50    # performed by Cargo.
51    _copy_file(repository_ctx, src = root_lockfile, dst = vendor_lockfile)
52
53    # Produce a token for third_party_glob to depend on so that the necessary
54    # sequencing is visible to Bazel.
55    repository_ctx.file("BUILD", executable = False)
56    repository_ctx.file("vendor.bzl", "vendored = True", executable = False)
57
58def _copy_file(repository_ctx, *, src, dst):
59    content = repository_ctx.read(src)
60    if not dst.exists or content != repository_ctx.read(dst):
61        repository_ctx.file(dst, content = content, executable = False)
62
63def _log_cargo_vendor(repository_ctx, result):
64    relevant = ""
65    for line in result.stderr.splitlines(True):
66        if line.strip() and not line.startswith("To use vendored sources,"):
67            relevant += line
68    if relevant:
69        # Render it as command output.
70        # If we just use print(), Bazel will cache and repeat the output even
71        # when not rerunning the command.
72        print = ["echo", relevant]
73        repository_ctx.execute(print, quiet = False)
74
75vendor = repository_rule(
76    doc = "A rule used to vendor the dependencies of a crate in the current workspace",
77    attrs = {
78        "cargo_version": attr.string(
79            doc = "The version of cargo to use",
80            default = DEFAULT_RUST_VERSION,
81        ),
82        "cargo_iso_date": attr.string(
83            doc = "The date of the tool (or None, if the version is a specific version)",
84        ),
85        "target_triple": attr.string(
86            doc = "The target triple of the cargo binary to download",
87        ),
88        "lockfile": attr.label(
89            doc = "A lockfile providing the set of crates to vendor",
90        ),
91    },
92    local = True,
93    implementation = _impl,
94)
95