use std::{
env,
ffi::{OsStr, OsString},
process::Command,
};
use crate::{
binary_file, collect_audit_data,
platform_detection::{is_32bit_x86, is_apple, is_msvc, is_wasm},
rustc_arguments::{self, should_embed_audit_data},
target_info,
};
use std::io::BufRead;
pub fn main(rustc_path: &OsStr) {
let mut command = match rustc_command_with_audit_data(rustc_path) {
Some(cmd) => cmd,
None => rustc_command(rustc_path), };
let results = command.status().unwrap_or_else(|err| {
let mut command_with_args: Vec<&OsStr> = vec![command.get_program()];
command_with_args.extend(command.get_args());
eprintln!(
"Failed to invoke rustc! Make sure it's in your $PATH\n\
The error was: {err}\n\
The attempted call was: {command_with_args:?}",
);
std::process::exit(1);
});
let code = results
.code()
.expect("rustc was terminated by a deadly signal");
std::process::exit(code);
}
fn rustc_command(rustc_path: &OsStr) -> Command {
let mut command = Command::new(rustc_path);
command.args(env::args_os().skip(2));
command
}
fn rustc_host_target_triple(rustc_path: &OsStr) -> String {
Command::new(rustc_path)
.arg("-vV")
.output()
.expect("Failed to invoke rustc! Is it in your $PATH?")
.stdout
.lines()
.map(|l| l.unwrap())
.find(|l| l.starts_with("host: "))
.map(|l| l[6..].to_string())
.expect("Failed to parse rustc output to determine the current platform. Please report this bug!")
}
fn rustc_command_with_audit_data(rustc_path: &OsStr) -> Option<Command> {
let mut command = rustc_command(rustc_path);
#[allow(clippy::question_mark)]
if env::var_os("CARGO_PRIMARY_PACKAGE").is_none() {
return None;
}
let args = rustc_arguments::parse_args().unwrap(); if !should_embed_audit_data(&args) {
return None;
}
let target_triple = args
.target
.clone()
.unwrap_or_else(|| rustc_host_target_triple(rustc_path));
let contents: Vec<u8> = collect_audit_data::compressed_dependency_list(&args, &target_triple);
let target_info = target_info::rustc_target_info(rustc_path, &target_triple);
let binfile = binary_file::create_binary_file(
&target_info,
&target_triple,
&contents,
"AUDITABLE_VERSION_INFO",
);
if let Some(file) = binfile {
let crate_name = match args.crate_name.as_deref() {
Some(name) => name,
None => {
eprintln!(
"WARNING: cargo-auditable: rustc command is missing --crate-name\n\
Please double-check that the audit data was injected into the binary.\n\
If it wasn't, please report a bug."
);
return None;
}
};
let out_dir = match args.out_dir.as_deref() {
Some(name) => name,
None => {
eprintln!(
"WARNING: cargo-auditable: rustc command is missing --out-dir\n\
Please double-check that the audit data was injected into the binary.\n\
If it wasn't, please report a bug."
);
return None;
}
};
let filename = format!("{crate_name}_audit_data.o");
let path = out_dir.join(filename);
std::fs::write(&path, file).expect("Unable to write output file");
let mut linker_command = OsString::from("-Clink-arg=");
linker_command.push(&path);
command.arg(linker_command);
if is_apple(&target_info) {
if args.bare_linker() {
command.arg("-Clink-arg=-u");
command.arg("-Clink-arg=_AUDITABLE_VERSION_INFO");
} else {
command.arg("-Clink-arg=-Wl,-u,_AUDITABLE_VERSION_INFO");
}
} else if is_msvc(&target_info) {
if is_32bit_x86(&target_info) {
command.arg("-Clink-arg=/INCLUDE:_AUDITABLE_VERSION_INFO");
} else {
command.arg("-Clink-arg=/INCLUDE:AUDITABLE_VERSION_INFO");
}
} else if is_wasm(&target_info) {
} else {
if args.bare_linker() {
command.arg("-Clink-arg=-u");
command.arg("-Clink-arg=AUDITABLE_VERSION_INFO");
} else {
command.arg("-Clink-arg=-Wl,-u,AUDITABLE_VERSION_INFO");
}
}
Some(command)
} else {
eprintln!(
"WARNING: cargo-auditable: target '{target_triple}' is not supported!\n\
The build will continue, but no audit data will be injected into the binary."
);
None
}
}