src/lib.rs
src/lib.rs, line 306-348
fn probe_fmt<'a>(&self, source: Arguments<'a>) -> Result<(), Error> {
let crate_name = self.new_crate_name();
let mut command = self.rustc.command();
command
.arg("--crate-name")
.arg(&crate_name)
.arg("--crate-type=lib")
.arg("--out-dir")
.arg(&self.out_dir)
.arg("--emit=llvm-ir");
if let Some(edition) = self.edition.as_ref() {
command.arg("--edition").arg(edition);
}
if let Some(target) = self.target.as_ref() {
command.arg("--target").arg(target);
}
command.args(&self.rustflags);
command.arg("-").stdin(Stdio::piped());
let mut child = try!(command.spawn().map_err(error::from_io));
let mut stdin = child.stdin.take().expect("rustc stdin");
try!(stdin.write_fmt(source).map_err(error::from_io));
drop(stdin);
match child.wait() {
Ok(status) if status.success() => {
// Try to remove the output file so it doesn't look like a build product for
// systems like bazel -- but this is best-effort, so we can ignore failure.
// The probe itself is already considered successful at this point.
let mut file = self.out_dir.join(crate_name);
file.set_extension("ll");
let _ = fs::remove_file(file);
Ok(())
}
Ok(status) => Err(error::from_exit(status)),
Err(error) => Err(error::from_io(error)),
}
}
probe_fmt builds a Command in argv form, appending --crate-name, --crate-type=lib, --out-dir, --emit=llvm-ir, optional --edition and --target, and the caller's rustflags. The probe source code is written to rustc's stdin, not passed as a command-line argument. The rustc binary path comes from the RUSTC environment variable (set by Cargo) or defaults to "rustc" from PATH. No shell is involved. Justifies uses-exec and exec-safe.
src/lib.rs, line 579-618
fn rustflags(target: &Option<OsString>, dir: &Path) -> Vec<String> {
// Starting with rust-lang/cargo#9601, shipped in Rust 1.55, Cargo always sets
// CARGO_ENCODED_RUSTFLAGS for any host/target build script invocation. This
// includes any source of flags, whether from the environment, toml config, or
// whatever may come in the future. The value is either an empty string, or a
// list of arguments separated by the ASCII unit separator (US), 0x1f.
if let Ok(a) = env::var("CARGO_ENCODED_RUSTFLAGS") {
return if a.is_empty() {
Vec::new()
} else {
a.split('\x1f').map(str::to_string).collect()
};
}
// Otherwise, we have to take a more heuristic approach, and we don't
// support values from toml config at all.
//
// Cargo only applies RUSTFLAGS for building TARGET artifact in
// cross-compilation environment. Sadly, we don't have a way to detect
// when we're building HOST artifact in a cross-compilation environment,
// so for now we only apply RUSTFLAGS when cross-compiling an artifact.
//
// See https://github.com/cuviper/autocfg/pull/10#issuecomment-527575030.
if *target != env::var_os("HOST")
|| dir_contains_target(target, dir, env::var_os("CARGO_TARGET_DIR"))
{
if let Ok(rustflags) = env::var("RUSTFLAGS") {
// This is meant to match how cargo handles the RUSTFLAGS environment variable.
// See https://github.com/rust-lang/cargo/blob/69aea5b6f69add7c51cca939a79644080c0b0ba0/src/cargo/core/compiler/build_context/target_info.rs#L434-L441
return rustflags
.split(' ')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_string)
.collect();
}
}
Vec::new()
}
Reads the following environment variables: OUT_DIR (required, set by Cargo), TARGET and HOST (set by Cargo), RUSTC (set by Cargo), RUSTFLAGS and CARGO_ENCODED_RUSTFLAGS (set by Cargo or user), RUSTC_WRAPPER and RUSTC_WORKSPACE_WRAPPER (set by Cargo), CARGO_TARGET_DIR (set by Cargo). All are standard Cargo build-script variables; none enumerate or exfiltrate the environment. Justifies uses-environment and environment-safe.
src/lib.rs, line 194-198
let dir = dir.into();
let meta = try!(fs::metadata(&dir).map_err(error::from_io));
if !meta.is_dir() || meta.permissions().readonly() {
return Err(error::from_str("output path is not a writable directory"));
}
fs::metadata checks that OUT_DIR is a writable directory before use. fs::remove_file is called on the probe output file (an .ll file within OUT_DIR) after a successful probe; failures are silently ignored. All filesystem access is confined to the OUT_DIR path provided by Cargo. Justifies uses-filesystem and filesystem-safe.
src/lib.rs, line 298-303
fn new_crate_name(&self) -> String {
#[allow(deprecated)]
static ID: AtomicUsize = ATOMIC_USIZE_INIT;
let id = ID.fetch_add(1, Ordering::Relaxed);
format!("autocfg_{:016x}_{}", self.uuid, id)
Uses AtomicUsize with Ordering::Relaxed to generate monotonically increasing probe IDs. Initialised with the deprecated ATOMIC_USIZE_INIT constant (suppressed with #[allow(deprecated)]) to maintain Rust 1.0 compatibility. The relaxed ordering is correct here: the counter just needs to be unique within a process, not to synchronise other memory accesses. No threads are spawned. Justifies uses-concurrency and concurrency-safe. Thread-safety of AutoCfg itself is not documented in its public API; justifies concurrency-documented = false.
src/lib.rs, line 621-636
///
/// This attempts to be random, within the constraints of Rust 1.0 and no dependencies.
fn new_uuid() -> u64 {
const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x100_0000_01b3;
// This set should have an actual random hasher.
let set: std::collections::HashSet<u64> = (0..256).collect();
// Feed the `HashSet`-shuffled order into FNV-1a.
let mut hash: u64 = FNV_OFFSET_BASIS;
for x in set {
hash = (hash ^ x).wrapping_mul(FNV_PRIME);
}
hash
}
Generates a pseudo-random u64 by inserting 0..256 into a HashSet<u64> and feeding the iteration order (which is randomised by Rust's default HashMap/HashSet randomisation since 1.7) through FNV-1a. The comment acknowledges this is not a cryptographic RNG. The purpose is only to make probe crate names unique across parallel invocations of different build scripts, not for any security-sensitive purpose. No use of cryptographic operations.