diff --git a/docs/flatten.md b/docs/flatten.md index 20509d9722..872eb04022 100644 --- a/docs/flatten.md +++ b/docs/flatten.md @@ -54,7 +54,7 @@ Produces a rust-project.json for the given targets. Configure rust-analyzer to l
 rust_benchmark(name, aliases, compile_data, crate_features, crate_root, data, deps, edition,
-               out_dir_tar, proc_macro_deps, rustc_env, rustc_flags, srcs, version)
+               out_dir_tar, proc_macro_deps, rustc_env, rustc_env_files, rustc_flags, srcs, version)
 
Builds a Rust benchmark test. @@ -149,6 +149,7 @@ Run the benchmark test using: `bazel run //fibonacci:fibonacci_bench`. | out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None | | proc_macro_deps | List of rust_library targets with kind proc-macro used to help build this library target. | List of labels | optional | [] | | rustc_env | Dictionary of additional "key": "value" environment variables to set for rustc.

rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} | +| rustc_env_files | Files containing additional environment variables to set for rustc.

These files should contain a single variable per line, of format NAME=value, and newlines may be included in a value by ending a line with a trailing back-slash (\).

The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] | | rustc_flags | List of compiler flags passed to rustc. | List of strings | optional | [] | | srcs | List of Rust .rs source files used to build the library.

If srcs contains more than one file, then there must be a file either named lib.rs. Otherwise, crate_root must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] | | version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" | @@ -160,8 +161,8 @@ Run the benchmark test using: `bazel run //fibonacci:fibonacci_bench`.
 rust_binary(name, aliases, compile_data, crate_features, crate_root, crate_type, data, deps,
-            edition, linker_script, out_binary, out_dir_tar, proc_macro_deps, rustc_env, rustc_flags,
-            srcs, version)
+            edition, linker_script, out_binary, out_dir_tar, proc_macro_deps, rustc_env,
+            rustc_env_files, rustc_flags, srcs, version)
 
Builds a Rust binary crate. @@ -267,6 +268,7 @@ Hello world | out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None | | proc_macro_deps | List of rust_library targets with kind proc-macro used to help build this library target. | List of labels | optional | [] | | rustc_env | Dictionary of additional "key": "value" environment variables to set for rustc.

rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} | +| rustc_env_files | Files containing additional environment variables to set for rustc.

These files should contain a single variable per line, of format NAME=value, and newlines may be included in a value by ending a line with a trailing back-slash (\).

The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] | | rustc_flags | List of compiler flags passed to rustc. | List of strings | optional | [] | | srcs | List of Rust .rs source files used to build the library.

If srcs contains more than one file, then there must be a file either named lib.rs. Otherwise, crate_root must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] | | version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" | @@ -534,7 +536,8 @@ rust_binary(
 rust_library(name, aliases, compile_data, crate_features, crate_root, crate_type, data, deps,
-             edition, out_dir_tar, proc_macro_deps, rustc_env, rustc_flags, srcs, version)
+             edition, out_dir_tar, proc_macro_deps, rustc_env, rustc_env_files, rustc_flags, srcs,
+             version)
 
Builds a Rust library crate. @@ -618,6 +621,7 @@ INFO: Elapsed time: 1.245s, Critical Path: 1.01s | out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None | | proc_macro_deps | List of rust_library targets with kind proc-macro used to help build this library target. | List of labels | optional | [] | | rustc_env | Dictionary of additional "key": "value" environment variables to set for rustc.

rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} | +| rustc_env_files | Files containing additional environment variables to set for rustc.

These files should contain a single variable per line, of format NAME=value, and newlines may be included in a value by ending a line with a trailing back-slash (\).

The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] | | rustc_flags | List of compiler flags passed to rustc. | List of strings | optional | [] | | srcs | List of Rust .rs source files used to build the library.

If srcs contains more than one file, then there must be a file either named lib.rs. Otherwise, crate_root must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] | | version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" | @@ -727,7 +731,7 @@ See @rules_rust//proto:BUILD for examples of defining the toolchain.
 rust_test(name, aliases, compile_data, crate, crate_features, crate_root, data, deps, edition, env,
-          out_dir_tar, proc_macro_deps, rustc_env, rustc_flags, srcs, version)
+          out_dir_tar, proc_macro_deps, rustc_env, rustc_env_files, rustc_flags, srcs, version)
 
Builds a Rust test crate. @@ -877,6 +881,7 @@ Run the test with `bazel build //hello_lib:hello_lib_test`. | out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None | | proc_macro_deps | List of rust_library targets with kind proc-macro used to help build this library target. | List of labels | optional | [] | | rustc_env | Dictionary of additional "key": "value" environment variables to set for rustc.

rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} | +| rustc_env_files | Files containing additional environment variables to set for rustc.

These files should contain a single variable per line, of format NAME=value, and newlines may be included in a value by ending a line with a trailing back-slash (\).

The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] | | rustc_flags | List of compiler flags passed to rustc. | List of strings | optional | [] | | srcs | List of Rust .rs source files used to build the library.

If srcs contains more than one file, then there must be a file either named lib.rs. Otherwise, crate_root must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] | | version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" | diff --git a/docs/rust.md b/docs/rust.md index c5374ac3da..929934477d 100644 --- a/docs/rust.md +++ b/docs/rust.md @@ -10,7 +10,7 @@
 rust_benchmark(name, aliases, compile_data, crate_features, crate_root, data, deps, edition,
-               out_dir_tar, proc_macro_deps, rustc_env, rustc_flags, srcs, version)
+               out_dir_tar, proc_macro_deps, rustc_env, rustc_env_files, rustc_flags, srcs, version)
 
Builds a Rust benchmark test. @@ -105,6 +105,7 @@ Run the benchmark test using: `bazel run //fibonacci:fibonacci_bench`. | out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None | | proc_macro_deps | List of rust_library targets with kind proc-macro used to help build this library target. | List of labels | optional | [] | | rustc_env | Dictionary of additional "key": "value" environment variables to set for rustc.

rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} | +| rustc_env_files | Files containing additional environment variables to set for rustc.

These files should contain a single variable per line, of format NAME=value, and newlines may be included in a value by ending a line with a trailing back-slash (\).

The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] | | rustc_flags | List of compiler flags passed to rustc. | List of strings | optional | [] | | srcs | List of Rust .rs source files used to build the library.

If srcs contains more than one file, then there must be a file either named lib.rs. Otherwise, crate_root must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] | | version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" | @@ -116,8 +117,8 @@ Run the benchmark test using: `bazel run //fibonacci:fibonacci_bench`.
 rust_binary(name, aliases, compile_data, crate_features, crate_root, crate_type, data, deps,
-            edition, linker_script, out_binary, out_dir_tar, proc_macro_deps, rustc_env, rustc_flags,
-            srcs, version)
+            edition, linker_script, out_binary, out_dir_tar, proc_macro_deps, rustc_env,
+            rustc_env_files, rustc_flags, srcs, version)
 
Builds a Rust binary crate. @@ -223,6 +224,7 @@ Hello world | out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None | | proc_macro_deps | List of rust_library targets with kind proc-macro used to help build this library target. | List of labels | optional | [] | | rustc_env | Dictionary of additional "key": "value" environment variables to set for rustc.

rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} | +| rustc_env_files | Files containing additional environment variables to set for rustc.

These files should contain a single variable per line, of format NAME=value, and newlines may be included in a value by ending a line with a trailing back-slash (\).

The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] | | rustc_flags | List of compiler flags passed to rustc. | List of strings | optional | [] | | srcs | List of Rust .rs source files used to build the library.

If srcs contains more than one file, then there must be a file either named lib.rs. Otherwise, crate_root must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] | | version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" | @@ -234,7 +236,8 @@ Hello world
 rust_library(name, aliases, compile_data, crate_features, crate_root, crate_type, data, deps,
-             edition, out_dir_tar, proc_macro_deps, rustc_env, rustc_flags, srcs, version)
+             edition, out_dir_tar, proc_macro_deps, rustc_env, rustc_env_files, rustc_flags, srcs,
+             version)
 
Builds a Rust library crate. @@ -318,6 +321,7 @@ INFO: Elapsed time: 1.245s, Critical Path: 1.01s | out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None | | proc_macro_deps | List of rust_library targets with kind proc-macro used to help build this library target. | List of labels | optional | [] | | rustc_env | Dictionary of additional "key": "value" environment variables to set for rustc.

rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} | +| rustc_env_files | Files containing additional environment variables to set for rustc.

These files should contain a single variable per line, of format NAME=value, and newlines may be included in a value by ending a line with a trailing back-slash (\).

The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] | | rustc_flags | List of compiler flags passed to rustc. | List of strings | optional | [] | | srcs | List of Rust .rs source files used to build the library.

If srcs contains more than one file, then there must be a file either named lib.rs. Otherwise, crate_root must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] | | version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" | @@ -329,7 +333,7 @@ INFO: Elapsed time: 1.245s, Critical Path: 1.01s
 rust_test(name, aliases, compile_data, crate, crate_features, crate_root, data, deps, edition, env,
-          out_dir_tar, proc_macro_deps, rustc_env, rustc_flags, srcs, version)
+          out_dir_tar, proc_macro_deps, rustc_env, rustc_env_files, rustc_flags, srcs, version)
 
Builds a Rust test crate. @@ -479,6 +483,7 @@ Run the test with `bazel build //hello_lib:hello_lib_test`. | out_dir_tar | __Deprecated__, do not use, see [#cargo_build_script] instead. | Label | optional | None | | proc_macro_deps | List of rust_library targets with kind proc-macro used to help build this library target. | List of labels | optional | [] | | rustc_env | Dictionary of additional "key": "value" environment variables to set for rustc.

rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the location of a generated file or external tool. Cargo build scripts that wish to expand locations should use cargo_build_script()'s build_script_env argument instead, as build scripts are run in a different environment - see cargo_build_script()'s documentation for more. | Dictionary: String -> String | optional | {} | +| rustc_env_files | Files containing additional environment variables to set for rustc.

These files should contain a single variable per line, of format NAME=value, and newlines may be included in a value by ending a line with a trailing back-slash (\).

The order that these files will be processed is unspecified, so multiple definitions of a particular variable are discouraged. | List of labels | optional | [] | | rustc_flags | List of compiler flags passed to rustc. | List of strings | optional | [] | | srcs | List of Rust .rs source files used to build the library.

If srcs contains more than one file, then there must be a file either named lib.rs. Otherwise, crate_root must be set to the source file that is the root of the crate to be passed to rustc to build this crate. | List of labels | optional | [] | | version | A version to inject in the cargo environment variable. | String | optional | "0.0.0" | diff --git a/examples/version_stamping/BUILD b/examples/version_stamping/BUILD new file mode 100644 index 0000000000..7d671c8918 --- /dev/null +++ b/examples/version_stamping/BUILD @@ -0,0 +1,20 @@ +load( + "@rules_rust//rust:rust.bzl", + "rust_binary", +) + +package(default_visibility = ["//visibility:public"]) + +rust_binary( + name = "version_reporter", + srcs = ["src/main.rs"], + rustc_env_files = [":generate_rustc_env_file"], +) + +# See https://docs.bazel.build/versions/master/user-manual.html#workspace_status for more information. +genrule( + name = "generate_rustc_env_file", + outs = ["rustc_env_file"], + cmd = "echo \"CARGO_PKG_VERSION=0.1.0-$$(awk '$$1 == \"BUILD_TIMESTAMP\" {print $$2}' bazel-out/volatile-status.txt)\" > $@", + stamp = True, +) diff --git a/examples/version_stamping/src/main.rs b/examples/version_stamping/src/main.rs new file mode 100644 index 0000000000..695c2377a0 --- /dev/null +++ b/examples/version_stamping/src/main.rs @@ -0,0 +1,17 @@ +// Copyright 2021 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +fn main() { + println!("Version: {}", env!("CARGO_PKG_VERSION")); +} diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl index 35b92f195a..f495a84076 100644 --- a/rust/private/clippy.bzl +++ b/rust/private/clippy.bzl @@ -67,7 +67,7 @@ def _clippy_aspect_impl(target, ctx): toolchain, ) - compile_inputs, out_dir, build_env_file, build_flags_files = collect_inputs( + compile_inputs, out_dir, build_env_files, build_flags_files = collect_inputs( ctx, ctx.rule.file, ctx.rule.files, @@ -95,7 +95,7 @@ def _clippy_aspect_impl(target, ctx): output_hash = determine_output_hash(root), rust_flags = [], out_dir = out_dir, - build_env_file = build_env_file, + build_env_files = build_env_files, build_flags_files = build_flags_files, maker_path = clippy_marker.path, aspect = True, diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index 56c7476faa..c9ebba6a82 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -542,6 +542,18 @@ _common_attrs = { documentation for more. """), ), + "rustc_env_files": attr.label_list( + doc = _tidy(""" + Files containing additional environment variables to set for rustc. + + These files should contain a single variable per line, of format + `NAME=value`, and newlines may be included in a value by ending a + line with a trailing back-slash (`\\`). + + The order that these files will be processed is unspecified, so + multiple definitions of a particular variable are discouraged. + """), + ), "rustc_flags": attr.string_list( doc = "List of compiler flags passed to `rustc`.", ), diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 8eee5d51b3..aa2df0569f 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -287,7 +287,7 @@ def _process_build_scripts( tuple: A tuple: A tuple of the following items: - (list): A list of all build info `OUT_DIR` File objects - (str): The `OUT_DIR` of the current build info - - (str): An optional path to a generated environment file from a `cargo_build_script` target + - (File): An optional path to a generated environment file from a `cargo_build_script` target - (list): All direct and transitive build flags from the current build info. """ extra_inputs, out_dir, build_env_file, build_flags_files = _create_extra_input_args(ctx, file, build_info, dep_info) @@ -338,7 +338,12 @@ def collect_inputs( linker_depset, ], ) - return _process_build_scripts(ctx, file, crate_info, build_info, dep_info, compile_inputs) + build_env_files = getattr(files, "rustc_env_files", []) + compile_inputs, out_dir, build_env_file, build_flags_files = _process_build_scripts(ctx, file, crate_info, build_info, dep_info, compile_inputs) + if build_env_file: + build_env_files = [f for f in build_env_files] + [build_env_file] + compile_inputs = depset(build_env_files, transitive = [compile_inputs]) + return compile_inputs, out_dir, build_env_files, build_flags_files def construct_arguments( ctx, @@ -353,7 +358,7 @@ def construct_arguments( output_hash, rust_flags, out_dir, - build_env_file, + build_env_files, build_flags_files, maker_path = None, aspect = False, @@ -373,7 +378,7 @@ def construct_arguments( output_hash (str): The hashed path of the crate root rust_flags (list): Additional flags to pass to rustc out_dir (str): The path to the output directory for the target Crate. - build_env_file (str): The output file of a `cargo_build_script` action containing rustc environment variables + build_env_files (list): Files containing rustc environment variables, for instance from `cargo_build_script` actions. build_flags_files (list): The output files of a `cargo_build_script` actions containing rustc build flags maker_path (File): An optional clippy marker file aspect (bool): True if called in an aspect context. @@ -393,7 +398,7 @@ def construct_arguments( # Wrapper args first args = ctx.actions.args() - if build_env_file != None: + for build_env_file in build_env_files: args.add("--env-file", build_env_file) args.add_all(build_flags_files, before_each = "--arg-file") @@ -558,7 +563,7 @@ def rustc_compile_action( toolchain, ) - compile_inputs, out_dir, build_env_file, build_flags_files = collect_inputs( + compile_inputs, out_dir, build_env_files, build_flags_files = collect_inputs( ctx, ctx.file, ctx.files, @@ -582,7 +587,7 @@ def rustc_compile_action( output_hash, rust_flags, out_dir, - build_env_file, + build_env_files, build_flags_files, ) @@ -699,7 +704,7 @@ def _create_extra_input_args(ctx, file, build_info, dep_info): tuple: A tuple of the following items: - (list): A list of all build info `OUT_DIR` File objects - (str): The `OUT_DIR` of the current build info - - (str): An optional path to a generated environment file from a `cargo_build_script` target + - (File): An optional generated environment file from a `cargo_build_script` target - (list): All direct and transitive build flags from the current build info. """ input_files = [] @@ -712,7 +717,7 @@ def _create_extra_input_args(ctx, file, build_info, dep_info): if build_info: out_dir = build_info.out_dir.path - build_env_file = build_info.rustc_env.path + build_env_file = build_info.rustc_env build_flags_files.append(build_info.flags.path) build_flags_files.append(build_info.link_flags.path) input_files.append(build_info.out_dir) diff --git a/test/rustc_env_files/BUILD b/test/rustc_env_files/BUILD new file mode 100644 index 0000000000..0528e7f630 --- /dev/null +++ b/test/rustc_env_files/BUILD @@ -0,0 +1,26 @@ +load( + "@rules_rust//rust:rust.bzl", + "rust_binary", +) + +package(default_visibility = ["//visibility:public"]) + +rust_binary( + name = "hello_env", + srcs = ["src/main.rs"], + deps = ["@examples//hello_lib"], + rustc_env_files = [":generate_rustc_env_file"], +) + +genrule( + name = "generate_rustc_env_file", + outs = ["rustc_env_file"], + cmd = "echo CARGO_PKG_VERSION=1.2.3 >> $@ && echo GREETING=Howdy >> $@", +) + +sh_test( + name = "output_test", + srcs = ["output_test.sh"], + args = ["$(location :hello_env)"], + data = [":hello_env"], +) diff --git a/test/rustc_env_files/output_test.sh b/test/rustc_env_files/output_test.sh new file mode 100755 index 0000000000..9bb91fe474 --- /dev/null +++ b/test/rustc_env_files/output_test.sh @@ -0,0 +1,6 @@ +#!/bin/bash -eu + +set -o pipefail + +output="$($1)" +[[ "${output}" == "Howdy from version 1.2.3" ]] || { echo >&2 "Unexpected output: ${output}"; exit 1;} diff --git a/test/rustc_env_files/src/main.rs b/test/rustc_env_files/src/main.rs new file mode 100644 index 0000000000..81702c5119 --- /dev/null +++ b/test/rustc_env_files/src/main.rs @@ -0,0 +1,17 @@ +// Copyright 2021 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +fn main() { + println!("{} from version {}", env!("GREETING"), env!("CARGO_PKG_VERSION")); +} diff --git a/util/process_wrapper/process_wrapper.cc b/util/process_wrapper/process_wrapper.cc index 2100ac640d..e9d7ee7c86 100644 --- a/util/process_wrapper/process_wrapper.cc +++ b/util/process_wrapper/process_wrapper.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include #include @@ -155,6 +156,10 @@ int PW_MAIN(int argc, const CharType* argv[], const CharType* envp[]) { } } + // Have the last values added take precedence over the first. + // This is simpler than needing to track duplicates and explicitly override them. + std::reverse(environment_block.begin(), environment_block.end()); + int exit_code = System::Exec(exec_path, arguments, environment_block, stdout_file, stderr_file); if (exit_code == 0) {