rustc: add arbitrary environment variables (#314)

Rules that call `rustc` setup environment variables in line with `cargo`'s behaviour (see the [documentation here](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates)). However they don't allow passing in additional environment variables that may be required at compile time, such as the [`PROTOC` variable in `prost-build`](https://github.com/danburkert/prost/blob/2de785aca480779ab0d4442d4bc91a696e3e6c89/prost-build/src/lib.rs#L689).

This PR adds the `rustc_env` attribute to the common attribute set, allowing it to be set in the following rules:
- `rust_benchmark`
- `rust_binary`
- `rust_library`
- `rust_test`

Variables are provided in a string dictionary, and will be merged into the generated environment overriding existing values.
diff --git a/proto/proto.bzl b/proto/proto.bzl
index f4174e2..680d9af 100644
--- a/proto/proto.bzl
+++ b/proto/proto.bzl
@@ -171,6 +171,7 @@
             aliases = {},
             output = rust_lib,
             edition = proto_toolchain.edition,
+            rustc_env = {},
         ),
         output_hash = output_hash,
     )
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index bf871f6..7d01d1a 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -134,6 +134,7 @@
             aliases = ctx.attr.aliases,
             output = rust_lib,
             edition = _get_edition(ctx, toolchain),
+            rustc_env = ctx.attr.rustc_env,
         ),
         output_hash = output_hash,
     )
@@ -159,6 +160,7 @@
             aliases = ctx.attr.aliases,
             output = output,
             edition = _get_edition(ctx, toolchain),
+            rustc_env = ctx.attr.rustc_env,
         ),
     )
 
@@ -185,6 +187,7 @@
             aliases = ctx.attr.aliases,
             output = test_binary,
             edition = crate.edition,
+            rustc_env = ctx.attr.rustc_env,
         )
     elif len(ctx.attr.deps) == 1 and len(ctx.files.srcs) == 0:
         dep = ctx.attr.deps[0].label
@@ -204,6 +207,7 @@
             aliases = ctx.attr.aliases,
             output = test_binary,
             edition = _get_edition(ctx, toolchain),
+            rustc_env = ctx.attr.rustc_env,
         )
 
     return rustc_compile_action(
@@ -298,6 +302,11 @@
             These are other `rust_library` targets and will be presented as the new name given.
         """),
     ),
+    "rustc_env": attr.string_dict(
+        doc = _tidy("""
+            Dictionary of additional `"key": "value"` environment variables to set for rustc.
+        """),
+    ),
     "crate_features": attr.string_list(
         doc = _tidy("""
             List of features to enable for this crate.
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 3c3a1e4..543a92a 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -35,6 +35,7 @@
         "aliases": "Dict[Label, String]: Renamed and aliased crates",
         "output": "File: The output File that will be produced, depends on crate type.",
         "edition": "str: The edition of this crate.",
+        "rustc_env": """Dict[String, String]: Additional `"key": "value"` environment variables to set for rustc.""",
     },
 )
 
@@ -330,6 +331,9 @@
     else:
         formatted_version = ""
 
+    # Update environment with user provided variables.
+    env.update(crate_info.rustc_env)
+
     ctx.actions.run_shell(
         command = command,
         inputs = compile_inputs,
diff --git a/test/build_env/BUILD b/test/build_env/BUILD
index 803787e..55496c9 100644
--- a/test/build_env/BUILD
+++ b/test/build_env/BUILD
@@ -10,3 +10,11 @@
     srcs = ["tests/manifest_dir.rs"],
     data = ["src/manifest_dir_file.txt"],
 )
+
+rust_test(
+    name = "arbitrary_env_test",
+    srcs = ["tests/arbitrary_env.rs"],
+    rustc_env = {
+        "USER_DEFINED_KEY": "USER_DEFINED_VALUE",
+    },
+)
diff --git a/test/build_env/tests/arbitrary_env.rs b/test/build_env/tests/arbitrary_env.rs
new file mode 100644
index 0000000..1b9ab23
--- /dev/null
+++ b/test/build_env/tests/arbitrary_env.rs
@@ -0,0 +1,6 @@
+#[test]
+pub fn test_arbitrary_env() {
+    let actual = env!("USER_DEFINED_KEY");
+    let expected = "USER_DEFINED_VALUE".to_owned();
+    assert_eq!(actual, expected);
+}