Use a c++ wrapper to call rustc instead of shell (#354)
diff --git a/cargo/cargo_build_script_runner/lib.rs b/cargo/cargo_build_script_runner/lib.rs
index c203dae..c810c6d 100644
--- a/cargo/cargo_build_script_runner/lib.rs
+++ b/cargo/cargo_build_script_runner/lib.rs
@@ -129,7 +129,7 @@
}
})
.collect::<Vec<_>>()
- .join(" ")
+ .join("\n")
}
/// Convert a vector of [BuildScriptOutput] into a list of dependencies environment variables.
@@ -151,7 +151,7 @@
}
})
.collect::<Vec<_>>()
- .join(" ")
+ .join("\n")
}
/// Convert a vector of [BuildScriptOutput] into a flagfile.
@@ -169,8 +169,8 @@
}
}
CompileAndLinkFlags {
- compile_flags: compile_flags.join(" "),
- link_flags: link_flags.join(" ").replace(exec_root, "${EXEC_ROOT}"),
+ compile_flags: compile_flags.join("\n"),
+ link_flags: link_flags.join("\n").replace(exec_root, "<EXEC_ROOT>"),
}
}
}
@@ -212,19 +212,19 @@
assert_eq!(
BuildScriptOutput::to_dep_env(&result, "my-crate-sys"),
- "DEP_MY_CRATE_VERSION=123 DEP_MY_CRATE_VERSION_NUMBER=1010107f".to_owned()
+ "DEP_MY_CRATE_VERSION=123\nDEP_MY_CRATE_VERSION_NUMBER=1010107f".to_owned()
);
assert_eq!(
BuildScriptOutput::to_env(&result),
- "FOO=BAR BAR=FOO".to_owned()
+ "FOO=BAR\nBAR=FOO".to_owned()
);
assert_eq!(
BuildScriptOutput::to_flags(&result, "/some/absolute/path"),
CompileAndLinkFlags {
// -Lblah was output as a rustc-flags, so even though it probably _should_ be a link
// flag, we don't treat it like one.
- compile_flags: "-Lblah --cfg=feature=awesome".to_owned(),
- link_flags: "-lsdfsdf -L${EXEC_ROOT}/bleh".to_owned(),
+ compile_flags: "-Lblah\n--cfg=feature=awesome".to_owned(),
+ link_flags: "-lsdfsdf\n-L<EXEC_ROOT>/bleh".to_owned(),
}
);
}
diff --git a/proto/proto.bzl b/proto/proto.bzl
index 067df0e..d5aea2b 100644
--- a/proto/proto.bzl
+++ b/proto/proto.bzl
@@ -220,6 +220,12 @@
default = PROTO_COMPILE_DEPS,
),
"_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"),
+ "_rust_tool_wrapper": attr.label(
+ default = "@io_bazel_rules_rust//rust/private/rust_tool_wrapper:rust_tool_wrapper",
+ executable = True,
+ allow_single_file = True,
+ cfg = "exec",
+ ),
"_optional_output_wrapper": attr.label(
executable = True,
cfg = "host",
@@ -280,6 +286,12 @@
default = GRPC_COMPILE_DEPS,
),
"_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"),
+ "_rust_tool_wrapper": attr.label(
+ default = "@io_bazel_rules_rust//rust/private/rust_tool_wrapper:rust_tool_wrapper",
+ executable = True,
+ allow_single_file = True,
+ cfg = "exec",
+ ),
"_optional_output_wrapper": attr.label(
executable = True,
cfg = "host",
diff --git a/rust/platform/triple_mappings.bzl b/rust/platform/triple_mappings.bzl
index 82508a9..d6f7b3d 100644
--- a/rust/platform/triple_mappings.bzl
+++ b/rust/platform/triple_mappings.bzl
@@ -43,7 +43,10 @@
"darwin": "",
"windows": ".exe",
"emscripten": ".js",
- "unknown": "",
+ # This is currently a hack allowing us to have the proper
+ # generated extension for the wasm target, similarly to the
+ # windows target
+ "unknown": ".wasm",
}
_SYSTEM_TO_STATICLIB_EXT = {
diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl
index 83ad84f..b5162d6 100644
--- a/rust/private/clippy.bzl
+++ b/rust/private/clippy.bzl
@@ -18,7 +18,6 @@
"collect_deps",
"collect_inputs",
"construct_arguments",
- "construct_compile_command",
)
load(
"@io_bazel_rules_rust//rust:private/rust.bzl",
@@ -60,41 +59,34 @@
toolchain,
)
- compile_inputs, prep_commands, dynamic_env, dynamic_build_flags = collect_inputs(
+ compile_inputs, out_dir, build_env_file, build_flags_files = collect_inputs(
ctx,
ctx.rule.file,
ctx.rule.files,
toolchain,
crate_info,
dep_info,
- build_info
+ build_info,
)
- args, env, dynamic_env = construct_arguments(
- ctx,
- ctx.rule.file,
- toolchain,
- crate_info,
- dep_info,
- output_hash = repr(hash(root.path)),
- rust_flags = [],
- dynamic_env = dynamic_env)
-
# A marker file indicating clippy has executed successfully.
# This file is necessary because "ctx.actions.run" mandates an output.
clippy_marker = ctx.actions.declare_file(ctx.label.name + "_clippy.ok")
- command = construct_compile_command(
+ args, env = construct_arguments(
ctx,
- toolchain.clippy_driver.path,
+ ctx.file,
toolchain,
+ toolchain.clippy_driver.path,
crate_info,
- build_info,
dep_info,
- prep_commands,
- dynamic_env,
- dynamic_build_flags,
- ) + (" && touch %s" % clippy_marker.path)
+ output_hash = repr(hash(root.path)),
+ rust_flags = [],
+ out_dir = out_dir,
+ build_env_file = build_env_file,
+ build_flags_files = build_flags_files,
+ maker_path = clippy_marker.path,
+ )
# Deny the default-on clippy warning levels.
#
@@ -102,12 +94,12 @@
# result of the aspect to be "success", and Clippy won't be re-triggered
# unless the source file is modified.
args.add("-Dclippy::style")
- args.add("-Dclippy::correctness");
- args.add("-Dclippy::complexity");
- args.add("-Dclippy::perf");
+ args.add("-Dclippy::correctness")
+ args.add("-Dclippy::complexity")
+ args.add("-Dclippy::perf")
- ctx.actions.run_shell(
- command = command,
+ ctx.actions.run(
+ executable = ctx.executable._rust_tool_wrapper,
inputs = compile_inputs,
outputs = [clippy_marker],
env = env,
@@ -131,10 +123,16 @@
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
+ "_rust_tool_wrapper": attr.label(
+ default = "@io_bazel_rules_rust//rust/private/rust_tool_wrapper:rust_tool_wrapper",
+ executable = True,
+ allow_single_file = True,
+ cfg = "exec",
+ ),
},
toolchains = [
"@io_bazel_rules_rust//rust:toolchain",
- "@bazel_tools//tools/cpp:toolchain_type"
+ "@bazel_tools//tools/cpp:toolchain_type",
],
implementation = _clippy_aspect_impl,
doc = """
@@ -166,7 +164,6 @@
$ bazel build --aspects=@io_bazel_rules_rust//rust:rust.bzl%rust_clippy_aspect \
--output_groups=clippy_checks //hello_lib:all
""",
-
)
def _rust_clippy_rule_impl(ctx):
@@ -176,7 +173,7 @@
rust_clippy = rule(
implementation = _rust_clippy_rule_impl,
attrs = {
- 'deps': attr.label_list(aspects = [rust_clippy_aspect]),
+ "deps": attr.label_list(aspects = [rust_clippy_aspect]),
},
doc = """
Executes the clippy checker on a specific target.
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index b92cde5..1d26e51 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -155,10 +155,7 @@
toolchain = find_toolchain(ctx)
crate_name = ctx.label.name.replace("-", "_")
- if (toolchain.target_arch == "wasm32"):
- output = ctx.actions.declare_file(ctx.label.name + ".wasm")
- else:
- output = ctx.actions.declare_file(ctx.label.name)
+ output = ctx.actions.declare_file(ctx.label.name + toolchain.binary_ext)
crate_type = getattr(ctx.attr, "crate_type")
@@ -364,6 +361,12 @@
],
),
"_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"),
+ "_rust_tool_wrapper": attr.label(
+ default = "@io_bazel_rules_rust//rust/private/rust_tool_wrapper:rust_tool_wrapper",
+ executable = True,
+ allow_single_file = True,
+ cfg = "exec",
+ ),
}
_rust_library_attrs = {
diff --git a/rust/private/rust_tool_wrapper/.clang-format b/rust/private/rust_tool_wrapper/.clang-format
new file mode 100644
index 0000000..8a0f2ae
--- /dev/null
+++ b/rust/private/rust_tool_wrapper/.clang-format
@@ -0,0 +1,2 @@
+---
+BasedOnStyle: Google
diff --git a/rust/private/rust_tool_wrapper/BUILD b/rust/private/rust_tool_wrapper/BUILD
new file mode 100644
index 0000000..67fce4d
--- /dev/null
+++ b/rust/private/rust_tool_wrapper/BUILD
@@ -0,0 +1,63 @@
+config_setting(
+ name = "windows",
+ constraint_values = [
+ "@platforms//os:windows",
+ ],
+)
+
+config_setting(
+ name = "linux",
+ constraint_values = [
+ "@platforms//os:linux",
+ ],
+)
+
+config_setting(
+ name = "macos",
+ constraint_values = [
+ "@platforms//os:macos",
+ ],
+)
+
+config_setting(
+ name = "freebsd",
+ constraint_values = [
+ "@platforms//os:freebsd",
+ ],
+)
+
+config_setting(
+ name = "openbsd",
+ constraint_values = [
+ "@platforms//os:openbsd",
+ ],
+)
+
+POSIX_SRCS = [
+ "system_posix.cc",
+]
+
+cc_binary(
+ name = "rust_tool_wrapper",
+ srcs = [
+ "rust_tool_wrapper.cc",
+ "system.h",
+ "system.cc",
+ ] + select({
+ ":windows": [
+ "system_windows.cc",
+ ],
+ ":linux": POSIX_SRCS,
+ ":macos": POSIX_SRCS,
+ ":freebsd": POSIX_SRCS,
+ ":openbsd": POSIX_SRCS,
+ }),
+ defines = [] + select({
+ ":windows": [
+ "UNICODE",
+ "_UNICODE",
+ ],
+ "//conditions:default": [],
+ }),
+ visibility = ["//visibility:public"],
+)
diff --git a/rust/private/rust_tool_wrapper/rust_tool_wrapper.cc b/rust/private/rust_tool_wrapper/rust_tool_wrapper.cc
new file mode 100644
index 0000000..db4dc59
--- /dev/null
+++ b/rust/private/rust_tool_wrapper/rust_tool_wrapper.cc
@@ -0,0 +1,95 @@
+#include <fstream>
+#include <iostream>
+#include <streambuf>
+#include <string>
+
+#include "rust/private/rust_tool_wrapper/system.h"
+
+// Simple rust tools wrapper allowing us to prepare the context to call rustc or
+// clippy. Since it's only used as a private implementation detail of a rule and
+// not user invoked we don't bother with error checking.
+#if defined(RTW_WIN_UNICODE)
+int wmain(int argc, const wchar_t* argv[], const wchar_t* envp[]) {
+#else
+int main(int argc, const char* argv[], const char* envp[]) {
+#endif // defined(RTW_WIN_UNICODE)
+
+ using namespace rust_tool_wrapper;
+
+ // Parse args.
+ System::StrType tool_path;
+ System::StrType out_dir;
+ System::StrType rename_from;
+ System::StrType rename_to;
+ System::StrType maker_path;
+ System::Arguments arguments;
+ System::EnvironmentBlock environment_block;
+
+ for (int i = 1; i < argc; ++i) {
+ System::StrType arg = argv[i];
+ if (arg == RTW_SYS_STR_LITERAL("--tool-path")) {
+ tool_path = argv[++i];
+ } else if (arg == RTW_SYS_STR_LITERAL("--out-dir")) {
+ out_dir = System::JoinPaths(System::GetWorkingDirectory(), argv[++i]);
+ environment_block.push_back(System::ComposeEnvironmentVariable(
+ RTW_SYS_STR_LITERAL("OUT_DIR"), out_dir));
+ } else if (arg == RTW_SYS_STR_LITERAL("--build-env-file")) {
+ std::ifstream env_file(argv[++i]);
+ std::string line;
+ while (std::getline(env_file, line)) {
+ environment_block.push_back(System::ToStrType(line));
+ }
+ } else if (arg == RTW_SYS_STR_LITERAL("--build-flags-file")) {
+ std::ifstream env_file(argv[++i]);
+ std::string line;
+ while (std::getline(env_file, line)) {
+ // replace <EXEC_ROOT> by the exec root
+ const System::StrType token = RTW_SYS_STR_LITERAL("<EXEC_ROOT>");
+ System::StrType sys_line = System::ToStrType(line);
+ std::size_t pos = sys_line.find(token);
+ if (pos != std::string::npos) {
+ sys_line.replace(pos, token.size(), System::GetWorkingDirectory());
+ }
+ arguments.push_back(sys_line);
+ }
+ } else if (arg == RTW_SYS_STR_LITERAL("--package-dir")) {
+ environment_block.push_back(System::ComposeEnvironmentVariable(
+ RTW_SYS_STR_LITERAL("CARGO_MANIFEST_DIR"),
+ System::JoinPaths(System::GetWorkingDirectory(), argv[++i])));
+ } else if (arg == RTW_SYS_STR_LITERAL("--maker-path")) {
+ maker_path = argv[++i];
+ } else if (arg == RTW_SYS_STR_LITERAL("--rename")) {
+ rename_from = argv[++i];
+ rename_to = argv[++i];
+ } else if (arg == RTW_SYS_STR_LITERAL("--")) {
+ for (++i; i < argc; ++i) {
+ arguments.push_back(argv[i]);
+ }
+ }
+ }
+
+ for (int i = 0; envp[i] != nullptr; ++i) {
+ environment_block.push_back(envp[i]);
+ }
+
+ // This is to be able to benefit from caching when using RBE
+ arguments.push_back(RTW_SYS_STR_LITERAL("--remap-path-prefix=") +
+ System::GetWorkingDirectory() +
+ RTW_SYS_STR_LITERAL("=."));
+
+ int exit_code = System::Exec(tool_path, arguments, environment_block);
+ if (exit_code == 0) {
+ if (!maker_path.empty()) {
+ std::ofstream file(maker_path);
+ }
+
+ // we perform a rename if necessary
+ if (!rename_from.empty() && !rename_to.empty()) {
+ std::ifstream source(rename_from, std::ios::binary);
+ std::ofstream dest(rename_to, std::ios::binary);
+ dest << source.rdbuf();
+ }
+ }
+
+ return exit_code;
+}
diff --git a/rust/private/rust_tool_wrapper/system.cc b/rust/private/rust_tool_wrapper/system.cc
new file mode 100644
index 0000000..f520f51
--- /dev/null
+++ b/rust/private/rust_tool_wrapper/system.cc
@@ -0,0 +1,26 @@
+#include "rust/private/rust_tool_wrapper/system.h"
+
+#if defined(RTW_WIN_UNICODE)
+#include <codecvt>
+#include <locale>
+#endif // defined(RTW_WIN_UNICODE)
+
+namespace rust_tool_wrapper {
+
+System::StrType System::ToStrType(const std::string& string) {
+#if defined(RTW_WIN_UNICODE)
+ return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(string);
+#else
+ return string;
+#endif // defined(RTW_WIN_UNICODE)
+}
+
+System::StrType System::ComposeEnvironmentVariable(const StrType& key,
+ const StrType& value) {
+ StrType env = key;
+ env.push_back(RTW_SYS_STR_LITERAL('='));
+ env += value;
+ return env;
+}
+
+} // namespace rust_tool_wrapper
diff --git a/rust/private/rust_tool_wrapper/system.h b/rust/private/rust_tool_wrapper/system.h
new file mode 100644
index 0000000..fc87e68
--- /dev/null
+++ b/rust/private/rust_tool_wrapper/system.h
@@ -0,0 +1,54 @@
+#ifndef RUST_PRIVATE_RUSTC_WRAPPER_SYSTEM_H_
+#define RUST_PRIVATE_RUSTC_WRAPPER_SYSTEM_H_
+
+#include <string>
+#include <vector>
+
+#if defined(_WIN32) && defined(UNICODE)
+#define RTW_WIN_UNICODE
+#endif // defined(_WIN32) && defined(UNICODE)
+
+#if defined(RTW_WIN_UNICODE)
+#define RTW_SYS_STR_LITERAL(str) L##str
+#else
+#define RTW_SYS_STR_LITERAL(str) str
+#endif
+
+namespace rust_tool_wrapper {
+
+class System {
+ public:
+#if defined(RTW_WIN_UNICODE)
+ using StrType = std::wstring;
+#else
+ using StrType = std::string;
+#endif // defined(RTW_WIN_UNICODE)
+
+ using Arguments = std::vector<StrType>;
+ using EnvironmentBlock = std::vector<StrType>;
+
+ public:
+ // Converts to the system string format
+ static StrType ToStrType(const std::string& string);
+
+ // Joins an environment variable in a single string
+ static StrType ComposeEnvironmentVariable(const StrType& key,
+ const StrType& value);
+
+ // Gets the working directory of the current process
+ static StrType GetWorkingDirectory();
+
+ // Joins paths using the system convention
+ static StrType JoinPaths(const StrType& path1, const StrType& path2);
+
+ // Simple function to execute a process that inherits all the current
+ // process handles.
+ // Even if the function doesn't modify global state it is not reentrant
+ // It is meant to be called once during the lifetime of the parent process
+ static int Exec(const StrType& executable, const Arguments& arguments,
+ const EnvironmentBlock& environment_block);
+};
+
+} // namespace rust_tool_wrapper
+
+#endif // RUST_PRIVATE_RUSTC_WRAPPER_SYSTEM_H_
diff --git a/rust/private/rust_tool_wrapper/system_posix.cc b/rust/private/rust_tool_wrapper/system_posix.cc
new file mode 100644
index 0000000..eb12918
--- /dev/null
+++ b/rust/private/rust_tool_wrapper/system_posix.cc
@@ -0,0 +1,64 @@
+#include "rust/private/rust_tool_wrapper/system.h"
+
+// posix headers
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <vector>
+
+namespace rust_tool_wrapper {
+
+System::StrType System::GetWorkingDirectory() {
+ const size_t kMaxBufferLength = 4096;
+ char cwd[kMaxBufferLength];
+ if (getcwd(cwd, sizeof(cwd)) == NULL) {
+ return System::StrType{};
+ }
+ return System::StrType{cwd};
+}
+
+System::StrType System::JoinPaths(const StrType &path1, const StrType &path2) {
+ return path1 + "/" + path2;
+}
+
+int System::Exec(const System::StrType &executable,
+ const System::Arguments &arguments,
+ const System::EnvironmentBlock &environment_block) {
+ pid_t child_pid = fork();
+ if (child_pid < 0) {
+ std::cerr << "error: Failed to fork the current process." << std::endl;
+ return -1;
+ } else if (child_pid == 0) {
+ std::vector<char *> argv;
+ std::string argv0 = JoinPaths(GetWorkingDirectory(), executable);
+ argv.push_back(&argv0[0]);
+ for (const StrType &argument : arguments) {
+ argv.push_back(const_cast<char *>(argument.c_str()));
+ }
+ argv.push_back(nullptr);
+
+ std::vector<char *> envp;
+ for (const StrType &ev : environment_block) {
+ envp.push_back(const_cast<char *>(ev.c_str()));
+ }
+ envp.push_back(nullptr);
+
+ umask(022);
+
+ execve(executable.c_str(), argv.data(), envp.data());
+ std::cerr << "error: Failed to exec the new process." << std::endl;
+ return -1;
+ }
+
+ int err, exit_status = -1;
+ do {
+ err = waitpid(child_pid, &exit_status, 0);
+ } while (err == -1 && errno == EINTR);
+
+ return exit_status;
+}
+
+} // namespace rust_tool_wrapper
diff --git a/rust/private/rust_tool_wrapper/system_windows.cc b/rust/private/rust_tool_wrapper/system_windows.cc
new file mode 100644
index 0000000..b9cdd98
--- /dev/null
+++ b/rust/private/rust_tool_wrapper/system_windows.cc
@@ -0,0 +1,128 @@
+#include "rust/private/rust_tool_wrapper/system.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+
+#include <iostream>
+
+namespace rust_tool_wrapper {
+
+namespace {
+// Algorithm used:
+// https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
+
+void ArgumentQuote(const System::StrType& argument,
+ System::StrType& command_line) {
+ if (argument.empty() == false && argument.find_first_of(RTW_SYS_STR_LITERAL(
+ " \t\n\v\"")) == argument.npos) {
+ command_line.append(argument);
+ } else {
+ command_line.push_back(RTW_SYS_STR_LITERAL('"'));
+
+ for (auto it = argument.begin();; ++it) {
+ unsigned number_backslashes = 0;
+
+ while (it != argument.end() && *it == RTW_SYS_STR_LITERAL('\\')) {
+ ++it;
+ ++number_backslashes;
+ }
+
+ if (it == argument.end()) {
+ command_line.append(number_backslashes * 2, RTW_SYS_STR_LITERAL('\\'));
+ break;
+ } else if (*it == L'"') {
+ command_line.append(number_backslashes * 2 + 1,
+ RTW_SYS_STR_LITERAL('\\'));
+ command_line.push_back(*it);
+ } else {
+ command_line.append(number_backslashes, RTW_SYS_STR_LITERAL('\\'));
+ command_line.push_back(*it);
+ }
+ }
+ command_line.push_back(RTW_SYS_STR_LITERAL('"'));
+ }
+}
+
+void MakeCommandLine(const System::Arguments& arguments,
+ System::StrType& command_line) {
+ for (const System::StrType& argument : arguments) {
+ command_line.push_back(RTW_SYS_STR_LITERAL(' '));
+ ArgumentQuote(argument, command_line);
+ }
+}
+
+void MakeEnvironmentBlock(const System::EnvironmentBlock& environment_block,
+ System::StrType& environment_block_win) {
+ for (const System::StrType& ev : environment_block) {
+ environment_block_win += ev;
+ environment_block_win.push_back(RTW_SYS_STR_LITERAL('\0'));
+ }
+ environment_block_win.push_back(RTW_SYS_STR_LITERAL('\0'));
+}
+
+} // namespace
+
+System::StrType System::GetWorkingDirectory() {
+ const DWORD kMaxBufferLength = 4096;
+ TCHAR buffer[kMaxBufferLength];
+ if (::GetCurrentDirectory(kMaxBufferLength, buffer) == 0) {
+ return System::StrType{};
+ }
+ return System::StrType{buffer};
+}
+
+System::StrType System::JoinPaths(const StrType& path1, const StrType& path2) {
+ return path1 + RTW_SYS_STR_LITERAL("\\") + path2;
+}
+
+int System::Exec(const System::StrType& executable,
+ const System::Arguments& arguments,
+ const System::EnvironmentBlock& environment_block) {
+ PROCESS_INFORMATION process_info;
+ ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
+
+ STARTUPINFO startup_info;
+ ZeroMemory(&startup_info, sizeof(STARTUPINFO));
+ startup_info.cb = sizeof(STARTUPINFO);
+
+ System::StrType command_line;
+ ArgumentQuote(executable, command_line);
+ MakeCommandLine(arguments, command_line);
+
+ System::StrType environment_block_win;
+ MakeEnvironmentBlock(environment_block, environment_block_win);
+
+ BOOL success = ::CreateProcess(
+ /*lpApplicationName*/ nullptr,
+ /*lpCommandLine*/ command_line.empty() ? nullptr : &command_line[0],
+ /*lpProcessAttributes*/ nullptr,
+ /*lpThreadAttributes*/ nullptr, /*bInheritHandles*/ TRUE,
+ /*dwCreationFlags*/ 0
+#if defined(UNICODE)
+ | CREATE_UNICODE_ENVIRONMENT
+#endif // defined(UNICODE)
+ ,
+ /*lpEnvironment*/ environment_block_win.empty()
+ ? nullptr
+ : &environment_block_win[0],
+ /*lpCurrentDirectory*/ nullptr,
+ /*lpStartupInfo*/ &startup_info,
+ /*lpProcessInformation*/ &process_info);
+
+ if (success == FALSE) {
+ std::cerr << "error: Failed to launch a new process." << std::endl;
+ return -1;
+ }
+
+ DWORD exit_status;
+ WaitForSingleObject(process_info.hProcess, INFINITE);
+ if (GetExitCodeProcess(process_info.hProcess, &exit_status) == FALSE)
+ exit_status = -1;
+ CloseHandle(process_info.hProcess);
+ return exit_status;
+}
+
+} // namespace rust_tool_wrapper
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 303ecfd..9595a15 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -122,23 +122,22 @@
for dep in deps:
if CrateInfo in dep:
if dep[CrateInfo].type == "proc-macro":
- fail(
- "{} listed {} in its deps, but it is a proc-macro. It should instead be in the bazel property proc_macro_deps.".format(
- label,
- dep.label,
- )
- )
+ fail(
+ "{} listed {} in its deps, but it is a proc-macro. It should instead be in the bazel property proc_macro_deps.".format(
+ label,
+ dep.label,
+ ),
+ )
for dep in proc_macro_deps:
type = dep[CrateInfo].type
if type != "proc-macro":
- fail(
- "{} listed {} in its proc_macro_deps, but it is not proc-macro, it is a {}. It should probably instead be listed in deps.".format(
- label,
- dep.label,
- type,
- )
- )
-
+ fail(
+ "{} listed {} in its proc_macro_deps, but it is not proc-macro, it is a {}. It should probably instead be listed in deps.".format(
+ label,
+ dep.label,
+ type,
+ ),
+ )
# TODO: Fix depset union (https://docs.bazel.build/versions/master/skylark/depsets.html)
direct_crates = []
@@ -148,7 +147,7 @@
transitive_build_infos = depset()
build_info = None
- aliases = {k.label: v for k,v in aliases.items()}
+ aliases = {k.label: v for k, v in aliases.items()}
for dep in deps + proc_macro_deps:
if CrateInfo in dep:
# This dependency is a rust_library
@@ -249,10 +248,10 @@
build_info,
dep_info,
compile_inputs):
- extra_inputs, prep_commands, dynamic_env, dynamic_build_flags = _create_out_dir_action(ctx, file, build_info, dep_info)
+ extra_inputs, out_dir, build_env_file, build_flags_files = _create_extra_input_args(ctx, file, build_info, dep_info)
if extra_inputs:
compile_inputs = depset(extra_inputs, transitive = [compile_inputs])
- return compile_inputs, prep_commands, dynamic_env, dynamic_build_flags
+ return compile_inputs, out_dir, build_env_file, build_flags_files
def collect_inputs(
ctx,
@@ -290,18 +289,71 @@
ctx,
file,
toolchain,
+ tool_path,
crate_info,
dep_info,
output_hash,
rust_flags,
- dynamic_env):
+ out_dir,
+ build_env_file,
+ build_flags_files,
+ maker_path):
output_dir = getattr(crate_info.output, "dirname") if hasattr(crate_info.output, "dirname") else None
linker_script = getattr(file, "linker_script") if hasattr(file, "linker_script") else None
env = _get_rustc_env(ctx, toolchain)
+ # Wrapper args first
args = ctx.actions.args()
+ args.add("--tool-path", tool_path)
+ if out_dir != None:
+ args.add("--out-dir", out_dir)
+
+ if build_env_file != None:
+ args.add("--build-env-file", build_env_file)
+
+ for f in build_flags_files:
+ args.add("--build-flags-file", f)
+
+ # Certain rust build processes expect to find files from the environment variable
+ # `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera, asakuma.
+ #
+ # The compiler and by extension proc-macros see the current working directory as the Bazel exec
+ # root. Therefore, in order to fix this without an upstream code change, we have to set
+ # `$CARGO_MANIFEST_DIR`.
+ #
+ # As such we attempt to infer `$CARGO_MANIFEST_DIR`.
+ # Inference cannot be derived from `attr.crate_root`, as this points at a source file which may or
+ # may not follow the `src/lib.rs` convention. As such we use `ctx.build_file_path` mapped into the
+ # `exec_root`. Since we cannot (seemingly) get the `exec_root` from skylark, we cheat a little
+ # and use `$(pwd)` which resolves the `exec_root` at action execution time.
+ package_dir = ctx.build_file_path[:ctx.build_file_path.rfind("/")]
+ args.add("--package-dir", package_dir)
+
+ # Handle that the binary name and crate name may be different.
+ #
+ # If a target name contains a - then cargo (and rules_rust) will generate a
+ # crate name with _ instead. Accordingly, rustc will generate a output
+ # file (executable, or rlib, or whatever) with _ not -. But when cargo
+ # puts a binary in the target/${config} directory, and sets environment
+ # variables like `CARGO_BIN_EXE_${binary_name}` it will use the - version
+ # not the _ version. So we rename the rustc-generated file (with _s) to
+ # have -s if needed.
+ maybe_rename = ""
+ if crate_info.type == "bin" and crate_info.output != None:
+ generated_file = crate_info.name + toolchain.binary_ext
+ src = "/".join([crate_info.output.dirname, generated_file])
+ dst = crate_info.output.path
+ if src != dst:
+ args.add_all(["--rename", src, dst])
+
+ if maker_path != None:
+ args.add("--maker-path", maker_path)
+
+ # Rustc arguments
+ args.add("--")
+
args.add(crate_info.root)
args.add("--crate-name=" + crate_info.name)
args.add("--crate-type=" + crate_info.type)
@@ -362,61 +414,7 @@
# sysroot being undefined.
env["SYSROOT"] = ""
- # Certain rust build processes expect to find files from the environment variable
- # `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera, asakuma.
- #
- # The compiler and by extension proc-macros see the current working directory as the Bazel exec
- # root. Therefore, in order to fix this without an upstream code change, we have to set
- # `$CARGO_MANIFEST_DIR`.
- #
- # As such we attempt to infer `$CARGO_MANIFEST_DIR`.
- # Inference cannot be derived from `attr.crate_root`, as this points at a source file which may or
- # may not follow the `src/lib.rs` convention. As such we use `ctx.build_file_path` mapped into the
- # `exec_root`. Since we cannot (seemingly) get the `exec_root` from skylark, we cheat a little
- # and use `$(pwd)` which resolves the `exec_root` at action execution time.
- package_dir = ctx.build_file_path[:ctx.build_file_path.rfind("/")]
- dynamic_env["CARGO_MANIFEST_DIR"] = "${{EXEC_ROOT}}/{}".format(package_dir)
-
- return args, env, dynamic_env
-
-def construct_compile_command(
- ctx,
- command,
- toolchain,
- crate_info,
- build_info,
- dep_info,
- prep_commands,
- dynamic_env,
- dynamic_build_flags):
- # Handle that the binary name and crate name may be different.
- #
- # If a target name contains a - then cargo (and rules_rust) will generate a
- # crate name with _ instead. Accordingly, rustc will generate a output
- # file (executable, or rlib, or whatever) with _ not -. But when cargo
- # puts a binary in the target/${config} directory, and sets environment
- # variables like `CARGO_BIN_EXE_${binary_name}` it will use the - version
- # not the _ version. So we rename the rustc-generated file (with _s) to
- # have -s if needed.
- maybe_rename = ""
- if crate_info.type == "bin" and crate_info.output != None:
- generated_file = crate_info.name
- if toolchain.target_arch == "wasm32":
- generated_file = generated_file + ".wasm"
- src = "/".join([crate_info.output.dirname, generated_file])
- dst = crate_info.output.path
- if src != dst:
- maybe_rename = " && /bin/mv {src} {dst}".format(src=src, dst=dst)
-
- # Set ${EXEC_ROOT} so that actions which chdir still work.
- # See https://github.com/google/cargo-raze/issues/71#issuecomment-433225853 for the rationale as
- # to why.
- return 'export EXEC_ROOT=$(pwd) && {} && {} "$@" --remap-path-prefix="$(pwd)"=__bazel_redacted_pwd {}{}'.format(
- " && ".join(["export {}={}".format(key, value) for key, value in dynamic_env.items()] + prep_commands),
- command,
- " ".join(dynamic_build_flags),
- maybe_rename,
- )
+ return args, env
def rustc_compile_action(
ctx,
@@ -441,7 +439,7 @@
toolchain,
)
- compile_inputs, prep_commands, dynamic_env, dynamic_build_flags = collect_inputs(
+ compile_inputs, out_dir, build_env_file, build_flags_files = collect_inputs(
ctx,
ctx.file,
ctx.files,
@@ -451,27 +449,19 @@
build_info,
)
- args, env, dynamic_env = construct_arguments(
+ args, env = construct_arguments(
ctx,
ctx.file,
toolchain,
+ toolchain.rustc.path,
crate_info,
dep_info,
output_hash,
rust_flags,
- dynamic_env,
- )
-
- command = construct_compile_command(
- ctx,
- toolchain.rustc.path,
- toolchain,
- crate_info,
- build_info,
- dep_info,
- prep_commands,
- dynamic_env,
- dynamic_build_flags,
+ out_dir,
+ build_env_file,
+ build_flags_files,
+ maker_path = None,
)
if hasattr(ctx.attr, "version") and ctx.attr.version != "0.0.0":
@@ -479,8 +469,8 @@
else:
formatted_version = ""
- ctx.actions.run_shell(
- command = command,
+ ctx.actions.run(
+ executable = ctx.executable._rust_tool_wrapper,
inputs = compile_inputs,
outputs = [crate_info.output],
env = env,
@@ -493,6 +483,7 @@
len(crate_info.srcs),
),
)
+
runfiles = ctx.runfiles(
files = dep_info.transitive_dylibs.to_list() + getattr(ctx.files, "data", []),
collect_data = True,
@@ -509,7 +500,7 @@
# nb. This field is required for cc_library to depend on our output.
files = depset([crate_info.output]),
runfiles = runfiles,
- executable = crate_info.output if crate_info.type == "bin" or out_binary else None,
+ executable = crate_info.output if crate_info.type == "bin" or "--test" in rust_flags or out_binary else None,
),
]
@@ -517,27 +508,28 @@
if crate.edition != "2015":
args.add("--edition={}".format(crate.edition))
-def _create_out_dir_action(ctx, file, build_info, dep_info):
- prep_commands = []
+def _create_extra_input_args(ctx, file, build_info, dep_info):
input_files = []
- # Env vars and build flags which need to be set in the action's command line, rather than on the action's env,
- # because they rely on other env vars or commands.
- dynamic_env = {}
- dynamic_build_flags = []
+
+ # Arguments to the commandline line wrapper that are going to be used
+ # to create the final command line
+ out_dir = None
+ build_env_file = None
+ build_flags_files = []
if build_info:
- prep_commands.append("export $(cat %s)" % build_info.rustc_env.path)
+ out_dir = build_info.out_dir.path
+ build_env_file = build_info.rustc_env.path
# out_dir will be added as input by the transitive_build_infos loop below.
- dynamic_env["OUT_DIR"] = "${{EXEC_ROOT}}/{}".format(build_info.out_dir.path)
- dynamic_build_flags.append("$(cat '%s')" % build_info.flags.path)
+ build_flags_files.append(build_info.flags.path)
# This should probably only actually be exposed to actions which link.
for dep_build_info in dep_info.transitive_build_infos.to_list():
input_files.append(dep_build_info.out_dir)
- dynamic_build_flags.append("$(cat '{}' | sed -e \"s#\${{EXEC_ROOT}}#${{EXEC_ROOT}}#g\")".format(dep_build_info.link_flags.path))
+ build_flags_files.append(dep_build_info.link_flags.path)
input_files.append(dep_build_info.link_flags)
- return input_files, prep_commands, dynamic_env, dynamic_build_flags
+ return input_files, out_dir, build_env_file, build_flags_files
def _compute_rpaths(toolchain, output_dir, dep_info):
"""
diff --git a/rust/repositories.bzl b/rust/repositories.bzl
index aef35fa..1860a48 100644
--- a/rust/repositories.bzl
+++ b/rust/repositories.bzl
@@ -202,6 +202,7 @@
rustfmt = "@{workspace_name}//:rustfmt_bin",
clippy_driver = "@{workspace_name}//:clippy_driver_bin",
rustc_lib = "@{workspace_name}//:rustc_lib",
+ binary_ext = "{binary_ext}",
staticlib_ext = "{staticlib_ext}",
dylib_ext = "{dylib_ext}",
os = "{system}",
@@ -213,6 +214,7 @@
""".format(
toolchain_name = name,
workspace_name = workspace_name,
+ binary_ext = system_to_binary_ext(system),
staticlib_ext = system_to_staticlib_ext(system),
dylib_ext = system_to_dylib_ext(system),
system = system,
diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl
index bd5d278..cb9738b 100644
--- a/rust/toolchain.bzl
+++ b/rust/toolchain.bzl
@@ -19,6 +19,7 @@
clippy_driver = ctx.file.clippy_driver,
rustc_lib = ctx.attr.rustc_lib,
rust_lib = ctx.attr.rust_lib,
+ binary_ext = ctx.attr.binary_ext,
staticlib_ext = ctx.attr.staticlib_ext,
dylib_ext = ctx.attr.dylib_ext,
target_triple = ctx.attr.target_triple,
@@ -56,6 +57,7 @@
"rust_lib": attr.label(
doc = "The rust standard library.",
),
+ "binary_ext": attr.string(mandatory = True),
"staticlib_ext": attr.string(mandatory = True),
"dylib_ext": attr.string(mandatory = True),
"os": attr.string(mandatory = True),
@@ -98,6 +100,7 @@
rustc_lib = "@rust_cpuX//:rustc_lib",
rust_lib = "@rust_cpuX//:rust_lib",
rust_doc = "@rust_cpuX//:rustdoc",
+ binary_ext = "",
staticlib_ext = ".a",
dylib_ext = ".so",
os = "linux",