blob: 1c83b630861cdef27f546013bf784d1d3f1b2df7 [file] [log] [blame]
use std::env;
use std::ffi::{OsStr, OsString};
use std::io::Write;
use std::path::Path;
use std::process::{Command, Output, Stdio};
use crate::{handle_failed_output, set_host_rpath, tmp_dir};
/// Construct a new `rustc` invocation.
pub fn rustc() -> Rustc {
Rustc::new()
}
/// Construct a new `rustc` aux-build invocation.
pub fn aux_build() -> Rustc {
Rustc::new_aux_build()
}
/// A `rustc` invocation builder.
#[derive(Debug)]
pub struct Rustc {
cmd: Command,
stdin: Option<Box<[u8]>>,
}
crate::impl_common_helpers!(Rustc);
fn setup_common() -> Command {
let rustc = env::var("RUSTC").unwrap();
let mut cmd = Command::new(rustc);
set_host_rpath(&mut cmd);
cmd.arg("--out-dir").arg(tmp_dir()).arg("-L").arg(tmp_dir());
cmd
}
impl Rustc {
// `rustc` invocation constructor methods
/// Construct a new `rustc` invocation.
pub fn new() -> Self {
let cmd = setup_common();
Self { cmd, stdin: None }
}
/// Construct a new `rustc` invocation with `aux_build` preset (setting `--crate-type=lib`).
pub fn new_aux_build() -> Self {
let mut cmd = setup_common();
cmd.arg("--crate-type=lib");
Self { cmd, stdin: None }
}
// Argument provider methods
/// Configure the compilation environment.
pub fn cfg(&mut self, s: &str) -> &mut Self {
self.cmd.arg("--cfg");
self.cmd.arg(s);
self
}
/// Specify default optimization level `-O` (alias for `-C opt-level=2`).
pub fn opt(&mut self) -> &mut Self {
self.cmd.arg("-O");
self
}
/// Specify a specific optimization level.
pub fn opt_level(&mut self, option: &str) -> &mut Self {
self.cmd.arg(format!("-Copt-level={option}"));
self
}
/// Specify type(s) of output files to generate.
pub fn emit(&mut self, kinds: &str) -> &mut Self {
self.cmd.arg(format!("--emit={kinds}"));
self
}
/// Specify where an external library is located.
pub fn extern_<P: AsRef<Path>>(&mut self, crate_name: &str, path: P) -> &mut Self {
assert!(
!crate_name.contains(|c: char| c.is_whitespace() || c == '\\' || c == '/'),
"crate name cannot contain whitespace or path separators"
);
let path = path.as_ref().to_string_lossy();
self.cmd.arg("--extern");
self.cmd.arg(format!("{crate_name}={path}"));
self
}
/// Specify path to the input file.
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg(path.as_ref());
self
}
/// Specify path to the output file. Equivalent to `-o`` in rustc.
pub fn output<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg("-o");
self.cmd.arg(path.as_ref());
self
}
/// This flag defers LTO optimizations to the linker.
pub fn linker_plugin_lto(&mut self, option: &str) -> &mut Self {
self.cmd.arg(format!("-Clinker-plugin-lto={option}"));
self
}
/// Specify what happens when the code panics.
pub fn panic(&mut self, option: &str) -> &mut Self {
self.cmd.arg(format!("-Cpanic={option}"));
self
}
/// Specify number of codegen units
pub fn codegen_units(&mut self, units: usize) -> &mut Self {
self.cmd.arg(format!("-Ccodegen-units={units}"));
self
}
/// Specify directory path used for incremental cache
pub fn incremental<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
let mut arg = OsString::new();
arg.push("-Cincremental=");
arg.push(path.as_ref());
self.cmd.arg(&arg);
self
}
/// Specify error format to use
pub fn error_format(&mut self, format: &str) -> &mut Self {
self.cmd.arg(format!("--error-format={format}"));
self
}
/// Specify json messages printed by the compiler
pub fn json(&mut self, items: &str) -> &mut Self {
self.cmd.arg(format!("--json={items}"));
self
}
/// Specify the target triple, or a path to a custom target json spec file.
pub fn target(&mut self, target: &str) -> &mut Self {
self.cmd.arg(format!("--target={target}"));
self
}
/// Specify the crate type.
pub fn crate_type(&mut self, crate_type: &str) -> &mut Self {
self.cmd.arg("--crate-type");
self.cmd.arg(crate_type);
self
}
/// Add a directory to the library search path. Equivalent to `-L` in rustc.
pub fn library_search_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg("-L");
self.cmd.arg(path.as_ref());
self
}
/// Override the system root. Equivalent to `--sysroot` in rustc.
pub fn sysroot<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg("--sysroot");
self.cmd.arg(path.as_ref());
self
}
/// Specify the edition year.
pub fn edition(&mut self, edition: &str) -> &mut Self {
self.cmd.arg("--edition");
self.cmd.arg(edition);
self
}
/// Specify the print request.
pub fn print(&mut self, request: &str) -> &mut Self {
self.cmd.arg("--print");
self.cmd.arg(request);
self
}
/// Add an extra argument to the linker invocation, via `-Clink-arg`.
pub fn link_arg(&mut self, link_arg: &str) -> &mut Self {
self.cmd.arg(format!("-Clink-arg={link_arg}"));
self
}
/// Specify a stdin input
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.stdin = Some(input.as_ref().to_vec().into_boxed_slice());
self
}
/// Specify the crate name.
pub fn crate_name<S: AsRef<OsStr>>(&mut self, name: S) -> &mut Self {
self.cmd.arg("--crate-name");
self.cmd.arg(name.as_ref());
self
}
/// Get the [`Output`][::std::process::Output] of the finished process.
#[track_caller]
pub fn command_output(&mut self) -> ::std::process::Output {
// let's make sure we piped all the input and outputs
self.cmd.stdin(Stdio::piped());
self.cmd.stdout(Stdio::piped());
self.cmd.stderr(Stdio::piped());
if let Some(input) = &self.stdin {
let mut child = self.cmd.spawn().unwrap();
{
let mut stdin = child.stdin.take().unwrap();
stdin.write_all(input.as_ref()).unwrap();
}
child.wait_with_output().expect("failed to get output of finished process")
} else {
self.cmd.output().expect("failed to get output of finished process")
}
}
#[track_caller]
pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output {
let caller_location = std::panic::Location::caller();
let caller_line_number = caller_location.line();
let output = self.command_output();
if output.status.code().unwrap() != code {
handle_failed_output(&self.cmd, output, caller_line_number);
}
output
}
}