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