Set up to build Rust code with dependencies

cargo-raze is like gazelle, but for Rust code. It generates BUILD files
for external dependencies.

I'm putting this in a separate change from running it and setting up CI
to do that for ease of telling what's generated vs not.

Change-Id: I87a07255dca475514cae7bdbf9b0b62ce46e2512
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..4d31a6d
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,120 @@
+# A fake Cargo.toml for cargo-raze. This will not work well if you try actually
+# running cargo.
+#
+# cargo-raze generates BUILD files etc for all dependencies listed here and in the workspace
+# members. Hand-written BUILD files can then depend on the corresponding targets that they need.
+#
+# To generate BUILD files from this:
+# ```console
+# $ bazel run @cargo_raze//:raze -- --manifest-path=$(realpath Cargo.toml) --generate-lockfile
+# ```
+# To remove unnecessary redundant BUILD files afterwards:
+# ```console
+# $ bazel run //tools/rust:tweak_cargo_raze_output -- $(readlink -f .)
+# ```
+#
+# # Generated targets
+# //third_party/cargo has forwarding targets for the individual crates. Don't
+# reference the repositories by name, because those change when they're
+# upgraded. If you need to access another target, set `extra_aliased_targets`
+# for the crate.
+#
+# See here for what you can set in the package.metadata.raze sections:
+#   https://github.com/google/cargo-raze/blob/main/impl/src/settings.rs
+[package]
+name = "compile_with_bazel"
+version = "0.0.0"
+
+# Mandatory (or Cargo tooling is unhappy)
+[lib]
+path = "fake_lib.rs"
+
+[workspace]
+members = [
+	"third_party/autocxx",
+	"third_party/autocxx/engine",
+	"third_party/autocxx/parser",
+	"third_party/autocxx/gen/cmd",
+	"third_party/autocxx/macro",
+	"third_party/autocxx/integration-tests",
+	"third_party/flatbuffers/rust/flatbuffers",
+]
+
+[dependencies]
+cxx = "1.0"
+cxxbridge-macro = "1.0"
+cxxbridge-cmd = "1.0"
+uuid = "1.0"
+toml = "0.5"
+anyhow = "1.0"
+
+# For bindgen.
+bindgen = "0.58.1"
+libloading = "=0.6.3"
+
+# Bazel toolchains take care of linking the C++ standard library, so don't add
+# an extra flag via Rust by enabling the `nothing` feature. I'm not even sure
+# it would end up on the link command line, but this crate's build.rs attempts
+# to find a C++ compiler itself otherwise which definitely doesn't work.
+link-cplusplus = { version = "1.0", features = ["nothing"] }
+
+[package.metadata.raze.binary_deps]
+# Needed if we want to generate BUILD files for autocxx-gen.
+#autocxx-gen = "0.16.0"
+
+[workspace.metadata.raze]
+# `cargo raze` will generate Bazel-compatible BUILD files into this path.
+workspace_path = "//third_party/cargo"
+
+# Put the aliases in the same package as the BUILD files.
+package_aliases_dir = "third_party/cargo"
+
+# The set of targets to generate BUILD rules for.
+targets = [
+	"x86_64-unknown-linux-gnu",
+	"arm-unknown-linux-gnueabi",
+	"armv7-unknown-linux-gnueabihf",
+	"aarch64-unknown-linux-gnueabi",
+]
+
+# Reference crates by URL, instead of vendoring them all.
+genmode = "Remote"
+
+# TODO(Brian): This isn't great for being hermetic or repeatable, but it is the
+# default. A lot of the problematic crates probably have pre-written versions
+# which avoid build.rs in cargo-raze itself or elsewhere. rules_rust is another
+# place to look.
+# https://github.com/bazelbuild/rules_rust/blob/main/bindgen/raze/Cargo.toml
+default_gen_buildrs = true
+
+[package.metadata.raze.crates.cxx.'*']
+gen_buildrs = false
+additional_build_file = "third_party/cargo/cxx/include.BUILD.bazel"
+extra_aliased_targets = ["cxx_cc"]
+
+[package.metadata.raze.crates.cxxbridge-cmd.'*']
+compile_data_attr = "[\"src/gen/include/cxx.h\"]"
+extra_aliased_targets = ["cargo_bin_cxxbridge"]
+
+[package.metadata.raze.crates.cxx-gen.'*']
+compile_data_attr = "[\"src/gen/include/cxx.h\"]"
+
+[package.metadata.raze.crates.supports-hyperlinks.'*']
+compile_data_attr = "[\"README.md\"]"
+
+[package.metadata.raze.crates.supports-unicode.'*']
+compile_data_attr = "[\"README.md\"]"
+
+[package.metadata.raze.crates.autocxx.'*']
+compile_data_attr = "[\"README.md\"]"
+
+[package.metadata.raze.crates.autocxx-gen.'*']
+extra_aliased_targets = ["cargo_bin_autocxx_gen"]
+
+[package.metadata.raze.crates.bindgen.'*']
+extra_aliased_targets = ["cargo_bin_bindgen"]
+
+[package.metadata.raze.crates.log.'*']
+additional_flags = [
+	"--cfg=atomic_cas",
+]
diff --git a/WORKSPACE b/WORKSPACE
index df7ed47..50aad8c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1182,3 +1182,31 @@
     sha256 = "cf4dfcf71e20a60406aaded03a165312c1ca535b509ead90eb1846fc598137d2",
     urls = ["https://github.com/foxglove/mcap/releases/download/releases%2Fmcap-cli%2Fv0.0.5/mcap-linux-amd64"],
 )
+
+http_archive(
+    name = "cargo_raze",
+    patches = ["@//third_party/cargo_raze:cargo_raze.patch"],
+    sha256 = "08bfc8859ff686ecb55005a3c4a9cf790115de0abdbcc69cf57b15be0745a859",
+    strip_prefix = "cargo-raze-0.14.2",
+    url = "https://github.com/google/cargo-raze/archive/v0.14.2.tar.gz",
+)
+
+http_archive(
+    name = "rules_foreign_cc",
+    patch_args = ["-p1"],
+    patches = ["@//third_party/rules_foreign_cc:backport_d93bd96dc719760c968b54730258ad0a5b10f8fb.patch"],
+    sha256 = "69023642d5781c68911beda769f91fcbc8ca48711db935a75da7f6536b65047f",
+    strip_prefix = "rules_foreign_cc-0.6.0",
+    url = "https://github.com/bazelbuild/rules_foreign_cc/archive/0.6.0.tar.gz",
+)
+
+load("@cargo_raze//:repositories.bzl", "cargo_raze_repositories")
+
+cargo_raze_repositories()
+
+# cargo_raze_transitive_deps wants to do `rust_repositories`, which we do
+# manually to get the right platforms. rules_foreign_cc is currently the only
+# other thing it has, so just do that manually.
+load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies")
+
+rules_foreign_cc_dependencies()
diff --git a/third_party/autocxx/Cargo.toml b/third_party/autocxx/Cargo.toml
index 5c24af8..7d34f78 100644
--- a/third_party/autocxx/Cargo.toml
+++ b/third_party/autocxx/Cargo.toml
@@ -30,10 +30,6 @@
 aquamarine = "0.1" # docs
 moveit = { version = "0.5", features = [ "cxx" ] }
 
-[workspace]
-members = ["parser", "engine", "gen/cmd", "gen/build", "macro", "demo", "tools/reduce", "tools/mdbook-preprocessor", "integration-tests"]
-exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack", "examples/llvm", "tools/stress-test"]
-
 #[patch.crates-io]
 #cxx = { path="../cxx" }
 #cxx-gen = { path="../cxx/gen/lib" }
diff --git a/third_party/bazel-toolchain/toolchain/BUILD.llvm_repo b/third_party/bazel-toolchain/toolchain/BUILD.llvm_repo
index 9920705..c0dbadf 100644
--- a/third_party/bazel-toolchain/toolchain/BUILD.llvm_repo
+++ b/third_party/bazel-toolchain/toolchain/BUILD.llvm_repo
@@ -124,3 +124,8 @@
     name = "strip",
     srcs = ["bin/llvm-strip"],
 )
+
+filegroup(
+    name = "libclang",
+    srcs = ["lib/libclang.so"],
+)
diff --git a/third_party/cargo/BUILD.bazel b/third_party/cargo/BUILD.bazel
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/cargo/BUILD.bazel
diff --git a/third_party/cargo/README.md b/third_party/cargo/README.md
new file mode 100644
index 0000000..b6e78f3
--- /dev/null
+++ b/third_party/cargo/README.md
@@ -0,0 +1,5 @@
+This folder is largely generated by cargo-raze, based on configuration in
+`//:Cargo.toml`. See there for details.
+
+Hand-written files referenced by `//:Cargo.toml` are stored in subfolders for
+each crate they are used with.
diff --git a/third_party/cargo/cxx/include.BUILD.bazel b/third_party/cargo/cxx/include.BUILD.bazel
new file mode 100644
index 0000000..f6b9f5a
--- /dev/null
+++ b/third_party/cargo/cxx/include.BUILD.bazel
@@ -0,0 +1,9 @@
+# This file is included in the BUILD for the cxx crate, to export its header
+# file for C++ code to depend on.
+cc_library(
+    name = "cxx_cc",
+    visibility = ["//visibility:public"],
+    hdrs = ["include/cxx.h"],
+    srcs = ["src/cxx.cc"],
+    includes = ["include"],
+)
diff --git a/third_party/cargo_raze/BUILD b/third_party/cargo_raze/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/cargo_raze/BUILD
diff --git a/third_party/cargo_raze/cargo_raze.patch b/third_party/cargo_raze/cargo_raze.patch
new file mode 100644
index 0000000..54cd9d5
--- /dev/null
+++ b/third_party/cargo_raze/cargo_raze.patch
@@ -0,0 +1,184 @@
+--- tools/BUILD.bazel	2022-02-03 01:20:57.060271436 -0800
++++ tools/BUILD.bazel	2022-02-03 01:21:14.321056806 -0800
+@@ -43,24 +43,12 @@ sh_binary(
+ 
+ alias(
+     name = "cargo",
+-    actual = select({
+-        "@rules_rust//rust/platform:aarch64-apple-darwin": "@rust_darwin_aarch64//:cargo",
+-        "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": "@rust_linux_aarch64//:cargo",
+-        "@rules_rust//rust/platform:x86_64-apple-darwin": "@rust_darwin_x86_64//:cargo",
+-        "@rules_rust//rust/platform:x86_64-pc-windows-msvc": "@rust_windows_x86_64//:cargo",
+-        "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": "@rust_linux_x86_64//:cargo",
+-    }),
++    actual = "@rust//:cargo",
+ )
+ 
+ alias(
+     name = "rustc",
+-    actual = select({
+-        "@rules_rust//rust/platform:aarch64-apple-darwin": "@rust_darwin_aarch64//:rustc",
+-        "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": "@rust_linux_aarch64//:rustc",
+-        "@rules_rust//rust/platform:x86_64-apple-darwin": "@rust_darwin_x86_64//:rustc",
+-        "@rules_rust//rust/platform:x86_64-pc-windows-msvc": "@rust_windows_x86_64//:rustc",
+-        "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": "@rust_linux_x86_64//:rustc",
+-    }),
++    actual = "@rust//:rustc",
+ )
+ 
+ sh_binary(
+
+--- third_party/zlib/BUILD.zlib.bazel	2022-02-03 01:26:46.756187851 -0800
++++ third_party/zlib/BUILD.zlib.bazel	2022-02-03 01:26:58.048702010 -0800
+@@ -58,6 +58,8 @@ cc_library(
+         "//conditions:default": [
+             "-Wno-unused-variable",
+             "-Wno-implicit-function-declaration",
++            "-Wno-cast-qual",
++            "-Wno-format-nonliteral",
+         ],
+     }),
+     includes = ["zlib/include/"],
+@@ -28,6 +28,7 @@ genrule(
+     srcs = _ZLIB_HEADERS,
+     outs = _ZLIB_PREFIXED_HEADERS,
+     cmd = "cp $(SRCS) $(@D)/zlib/include/",
++    visibility = ["//visibility:public"],
+ )
+ 
+ cc_library(
+
+--- third_party/pcre/BUILD.pcre.bazel	2022-02-03 01:29:12.830839568 -0800
++++ third_party/pcre/BUILD.pcre.bazel	2022-02-03 01:29:25.187402321 -0800
+@@ -10,7 +10,14 @@ filegroup(
+ cmake(
+     name = "pcre",
+     cache_entries = {
+-        "CMAKE_C_FLAGS": "-fPIC",
++        "CMAKE_C_FLAGS": "-fPIC -DHAVE_STRERROR=1 -Wno-unused -Wno-unused-parameter -Wunused-command-line-argument -Wno-cast-qual -Wno-self-assign -Wno-cast-align -Wno-incompatible-pointer-types-discards-qualifiers",
++        "CMAKE_CXX_FLAGS": "-fPIC -DHAVE_STRERROR=1 -Wno-unused -Wno-unused-parameter -Wno-unused-command-line-argument",
++        "CMAKE_EXE_LINKER_FLAGS": "-Wno-unused-command-line-argument",
++        "PCRE_SUPPORT_LIBREADLINE": "off",
++        "PCRE_BUILD_TESTS": "off",
+     },
++    deps = [
++        "@cargo_raze__zlib//:zlib",
++    ],
+     lib_source = ":all",
+     out_static_libs = ["libpcre.a"],
+
+--- third_party/openssl/BUILD.openssl.bazel	2022-02-03 23:31:09.601309219 -0800
++++ third_party/openssl/BUILD.openssl.bazel	2022-02-03 23:33:16.123104901 -0800
+@@ -41,6 +41,14 @@ configure_make(
+         "libssl.a",
+         "libcrypto.a",
+     ],
++    copts = [
++        "-Wno-unused-parameter",
++        "-Wno-format-nonliteral",
++        "-Wno-incompatible-pointer-types-discards-qualifiers",
++        "-Wno-missing-field-initializers",
++        "-Wno-cast-qual",
++        "-Wno-cast-align",
++    ],
+     targets = [
+         "build_libs",
+         "install_dev",
+
+--- third_party/libssh2/BUILD.libssh2.bazel	2022-02-04 00:03:43.831120614 -0800
++++ third_party/libssh2/BUILD.libssh2.bazel	2022-02-04 00:04:19.100745883 -0800
+@@ -29,6 +29,14 @@ cmake(
+         "@rules_rust//rust/platform:windows": ["ssh2.lib"],
+         "//conditions:default": ["libssh2.a"],
+     }),
++    copts = [
++        "-Wno-cast-qual",
++        "-Wno-sizeof-array-div",
++        "-Wno-unused-parameter",
++        "-DHAVE_SNPRINTF=1",
++        "-DHAVE_SYS_UIO_H=1",
++        "-DHAVE_SYS_SOCKET_H=1",
++    ],
+     visibility = ["//visibility:public"],
+     deps = ["@cargo_raze__openssl//:openssl"],
+ )
+
+--- third_party/libgit2/BUILD.libgit2.bazel	2022-02-04 00:13:21.681725240 -0800
++++ third_party/libgit2/BUILD.libgit2.bazel	2022-02-04 00:13:58.599423312 -0800
+@@ -39,6 +39,13 @@ cmake(
+         "@rules_rust//rust/platform:windows": ["git2.lib"],
+         "//conditions:default": ["libgit2.a"],
+     }),
++    copts = [
++        "-Wno-unused-command-line-argument",
++        "-Wno-cast-qual",
++        "-Wno-cast-align",
++        "-Wno-incompatible-pointer-types-discards-qualifiers",
++        "-Wno-format-nonliteral",
++    ],
+     visibility = ["//visibility:public"],
+     deps = [
+         "@cargo_raze__libssh2//:libssh2",
+
+--- impl/Cargo.toml	2022-02-04 00:44:15.679153957 -0800
++++ impl/Cargo.toml	2022-02-04 00:44:17.799251382 -0800
+@@ -113,6 +113,7 @@ additional_deps = ["@cargo_raze__libssh2
+ # build.rs file: https://github.com/rust-lang/libz-sys/blob/main/build.rs
+ gen_buildrs = false
+ additional_flags = ["--cfg=static"]
++additional_deps = ["@cargo_raze__zlib//:zlib"]
+ 
+ [package.metadata.raze.crates.openssl.'*']
+ additional_deps = ["@cargo_raze__openssl//:openssl"]
+@@ -106,6 +106,7 @@ additional_deps = ["@cargo_raze__libgit2
+ build_data_dependencies = [
+     "@cargo_raze__libssh2//:libssh2",
+     "@cargo_raze__openssl//:openssl",
++    "@cargo_raze__zlib//:copy_public_headers",
+ ]
+ additional_deps = ["@cargo_raze__libssh2//:libssh2"]
+ 
+
+--- third_party/cargo/remote/BUILD.libz-sys-1.1.2.bazel	2022-02-04 00:45:43.779201978 -0800
++++ third_party/cargo/remote/BUILD.libz-sys-1.1.2.bazel	2022-02-04 00:45:57.151816346 -0800
+@@ -57,6 +57,7 @@ rust_library(
+     # buildifier: leave-alone
+     deps = [
+         "@cargo_raze__libc__0_2_92//:libc",
++        "@cargo_raze__zlib//:zlib",
+     ] + selects.with_or({
+         # cfg(target_env = "msvc")
+         (
+
+--- third_party/cargo/remote/BUILD.libssh2-sys-0.2.21.bazel	2022-02-04 00:54:43.031966734 -0800
++++ third_party/cargo/remote/BUILD.libssh2-sys-0.2.21.bazel	2022-02-04 00:54:44.272023742 -0800
+@@ -41,6 +41,8 @@ cargo_build_script(
+     name = "libssh2_sys_build_script",
+     srcs = glob(["**/*.rs"]),
+     build_script_env = {
++        # TODO: Custom rule that does the copy and exports a Make variable with the folder?
++        "DEP_Z_INCLUDE": "${pwd}/bazel-out/k8-fastbuild/bin/external/cargo_raze__zlib/zlib/include",
+     },
+     crate_features = [
+     ],
+@@ -48,6 +48,7 @@ cargo_build_script(
+     data = glob(["**"]) + [
+         "@cargo_raze__libssh2//:libssh2",
+         "@cargo_raze__openssl//:openssl",
++        "@cargo_raze__zlib//:copy_public_headers",
+     ],
+     edition = "2015",
+     links = "ssh2",
+
+--- impl/src/util.rs	2022-02-06 17:53:29.535707368 -0800
++++ impl/src/util.rs	2022-02-06 17:55:22.513000536 -0800
+@@ -40,6 +40,8 @@ static SUPPORTED_PLATFORM_TRIPLES: &[&st
+   "aarch64-linux-android",
+   "aarch64-unknown-linux-gnu",
+   "arm-unknown-linux-gnueabi",
++  "armv7-unknown-linux-gnueabi",
++  "armv7-unknown-linux-gnueabihf",
+   "i686-linux-android",
+   "i686-unknown-freebsd",
+   "powerpc-unknown-linux-gnu",
+
diff --git a/third_party/rules_foreign_cc/BUILD b/third_party/rules_foreign_cc/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/rules_foreign_cc/BUILD
diff --git a/third_party/rules_foreign_cc/backport_d93bd96dc719760c968b54730258ad0a5b10f8fb.patch b/third_party/rules_foreign_cc/backport_d93bd96dc719760c968b54730258ad0a5b10f8fb.patch
new file mode 100644
index 0000000..7659456
--- /dev/null
+++ b/third_party/rules_foreign_cc/backport_d93bd96dc719760c968b54730258ad0a5b10f8fb.patch
@@ -0,0 +1,82 @@
+From d93bd96dc719760c968b54730258ad0a5b10f8fb Mon Sep 17 00:00:00 2001
+From: Fabian Meumertzheim <meumertzheim@code-intelligence.com>
+Date: Tue, 5 Oct 2021 16:07:10 +0200
+Subject: [PATCH] Add flags from copts and linkopts attributes (#796)
+
+---
+ foreign_cc/private/cc_toolchain_util.bzl |  6 +++---
+ test/standard_cxx_flags_test/BUILD.bazel |  6 +++++-
+ test/standard_cxx_flags_test/tests.bzl   | 16 ++++++++++++++++
+ 3 files changed, 24 insertions(+), 4 deletions(-)
+
+diff --git a/foreign_cc/private/cc_toolchain_util.bzl b/foreign_cc/private/cc_toolchain_util.bzl
+index 9842d8c2c..6843750d7 100644
+--- a/foreign_cc/private/cc_toolchain_util.bzl
++++ b/foreign_cc/private/cc_toolchain_util.bzl
+@@ -301,9 +301,9 @@ def get_flags_info(ctx, link_output_file = None):
+         cc_toolchain = cc_toolchain_,
+     )
+ 
+-    copts = (ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts) or []
+-    cxxopts = (ctx.fragments.cpp.copts + ctx.fragments.cpp.cxxopts) or []
+-    linkopts = ctx.fragments.cpp.linkopts or []
++    copts = (ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts + ctx.attr.copts) or []
++    cxxopts = (ctx.fragments.cpp.copts + ctx.fragments.cpp.cxxopts + ctx.attr.copts) or []
++    linkopts = (ctx.fragments.cpp.linkopts + ctx.attr.linkopts) or []
+     defines = _defines_from_deps(ctx)
+ 
+     flags = CxxFlagsInfo(
+diff --git a/test/standard_cxx_flags_test/BUILD.bazel b/test/standard_cxx_flags_test/BUILD.bazel
+index fa41a521f..799e73c5a 100644
+--- a/test/standard_cxx_flags_test/BUILD.bazel
++++ b/test/standard_cxx_flags_test/BUILD.bazel
+@@ -1,3 +1,7 @@
+ load(":tests.bzl", "flags_test")
+ 
+-flags_test(name = "flags_test")
++flags_test(
++    name = "flags_test",
++    copts = ["-fblah4"],
++    linkopts = ["-fblah5"],
++)
+diff --git a/test/standard_cxx_flags_test/tests.bzl b/test/standard_cxx_flags_test/tests.bzl
+index 7da3aedc3..11c2d67a9 100644
+--- a/test/standard_cxx_flags_test/tests.bzl
++++ b/test/standard_cxx_flags_test/tests.bzl
+@@ -10,12 +10,26 @@ def _impl(ctx):
+ 
+     assert_contains_once(flags.cc, "-fblah0")
+     assert_contains_once(flags.cc, "-fblah2")
++    assert_contains_once(flags.cc, "-fblah4")
++    if "-fblah5" in flags.cc:
++        fail("C flags should not contain '-fblah5'")
+ 
+     assert_contains_once(flags.cxx, "-fblah0")
+     assert_contains_once(flags.cxx, "-fblah1")
++    assert_contains_once(flags.cxx, "-fblah4")
++    if "-fblah5" in flags.cxx:
++        fail("C++ flags should not contain '-fblah5'")
+ 
+     assert_contains_once(flags.cxx_linker_executable, "-fblah3")
++    assert_contains_once(flags.cxx_linker_executable, "-fblah5")
++    if "-fblah4" in flags.cxx_linker_executable:
++        fail("Executable linker flags should not contain '-fblah4'")
++
+     assert_contains_once(flags.cxx_linker_shared, "-fblah3")
++    assert_contains_once(flags.cxx_linker_shared, "-fblah5")
++    if "-fblah4" in flags.cxx_linker_shared:
++        fail("Shared linker flags should not contain '-fblah4'")
++
+     if "-fblah3" in flags.cxx_linker_static:
+         fail("Static linker flags should not contain '-fblah3'")
+ 
+@@ -44,7 +58,9 @@ def assert_contains_once(arr, value):
+ _flags_test = rule(
+     implementation = _impl,
+     attrs = {
++        "copts": attr.string_list(),
+         "deps": attr.label_list(),
++        "linkopts": attr.string_list(),
+         "out": attr.output(),
+         "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
+     },
diff --git a/tools/rust/BUILD b/tools/rust/BUILD
index e58bb19..fccc3e4 100644
--- a/tools/rust/BUILD
+++ b/tools/rust/BUILD
@@ -119,3 +119,16 @@
     toolchain = ":noop_rust_toolchain_impl",
     toolchain_type = "@rules_rust//rust:toolchain",
 )
+
+'''
+# TODO(Brian): Uncomment in the next change once dependencies exist.
+rust_binary(
+    name = "tweak_cargo_raze_output",
+    srcs = ["tweak_cargo_raze_output.rs"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//third_party/cargo:anyhow",
+        "//third_party/cargo:toml",
+    ],
+)
+'''
diff --git a/tools/rust/tweak_cargo_raze_output.rs b/tools/rust/tweak_cargo_raze_output.rs
new file mode 100644
index 0000000..88a9d0a
--- /dev/null
+++ b/tools/rust/tweak_cargo_raze_output.rs
@@ -0,0 +1,102 @@
+//! A tool that postprocesses cargo-raze output to do what we want.
+//!
+//! Currently this is limited to removing extraneous BUILD files which cargo-raze places in all the
+//! third_party packages we feed to it, which we don't want to use. We're hand-writing BUILD files
+//! for these dependencies, without intending them to be used as separate Bazel workspaces, so it's
+//! easiest for them to all reference the top-level //third_party/cargo package.
+use std::{
+    env, fs,
+    io::{self, ErrorKind},
+    path::Path,
+};
+
+use anyhow::Context;
+use toml::Value;
+
+fn filter_not_found(result: io::Result<()>) -> io::Result<()> {
+    match result {
+        Err(e) if matches!(e.kind(), ErrorKind::NotFound) => Ok(()),
+        r => r,
+    }
+}
+
+fn main() -> anyhow::Result<()> {
+    let argv: Vec<_> = env::args().collect();
+    let workspace_path = Path::new(&argv[1]);
+    let cargo_toml_path = workspace_path.join("Cargo.toml");
+    eprintln!("Loading Cargo.toml from {:?}", cargo_toml_path);
+    let cargo_toml_contents = fs::read(&cargo_toml_path)
+        .with_context(|| format!("Failed to read Cargo.toml: {:?}", cargo_toml_path))?;
+    let cargo_toml_contents = std::str::from_utf8(&cargo_toml_contents).with_context(|| {
+        format!(
+            "Failed to interpret Cargo.toml contents as UTF-8: {:?}",
+            cargo_toml_path
+        )
+    })?;
+    let cargo_toml: Value = cargo_toml_contents
+        .parse()
+        .with_context(|| format!("Failed to parse Cargo.toml contents: {:?}", cargo_toml_path))?;
+
+    let package_aliases_dir = cargo_toml["workspace"]["metadata"]["raze"]["package_aliases_dir"]
+        .as_str()
+        .with_context(|| {
+            format!(
+                "Found non-string package_aliases_dir in Cargo.toml: {:?}",
+                cargo_toml_path
+            )
+        })?;
+
+    let workspace_members = cargo_toml["workspace"]["members"]
+        .as_array()
+        .with_context(|| {
+            format!(
+                "Did not find workspace members in Cargo.toml: {:?}",
+                cargo_toml_path
+            )
+        })?;
+    for member in workspace_members.iter() {
+        let member = member.as_str().with_context(|| {
+            format!(
+                "Found non-string workspace member in Cargo.toml: {:?}",
+                cargo_toml_path
+            )
+        })?;
+
+        // First delete the BUILD file.
+        let member_build = workspace_path
+            .join(member)
+            .join(package_aliases_dir)
+            .join("BUILD.bazel");
+        filter_not_found(fs::remove_file(&member_build)).with_context(|| {
+            format!(
+                "Failed to remove workspace member BUILD.bazel: {:?}",
+                member_build
+            )
+        })?;
+
+        // Then go and delete each folder in reverse order, but only if it's now empty to avoid
+        // overeager deletion. The file we're deleting should be the only thing in these folders,
+        // but if somebody wrote something else for some reason then we don't want to silently
+        // delete it.
+        let mut folder_path = workspace_path.join(member);
+        for d in Path::new(package_aliases_dir)
+            .components()
+            .scan(&mut folder_path, |a, b| {
+                **a = a.join(b);
+                Some(a.clone())
+            })
+            .collect::<Vec<_>>()
+            .iter()
+            .rev()
+        {
+            filter_not_found(fs::remove_dir(d)).with_context(|| {
+                format!(
+                    "Failed to remove workspace member package_aliases directory: {:?}",
+                    d
+                )
+            })?;
+        }
+    }
+    eprintln!("All done");
+    Ok(())
+}