blob: 0235851e64259e46418401b89ffef9ebab860a90 [file] [log] [blame] [edit]
use crate::directory::Directory;
use crate::error::{Error, Result};
use crate::manifest::Name;
use crate::run::Project;
use crate::rustflags;
use serde::Deserialize;
use std::process::{Command, Output, Stdio};
use std::{env, fs};
#[derive(Deserialize)]
pub struct Metadata {
pub target_directory: Directory,
pub workspace_root: Directory,
pub packages: Vec<Package>,
}
#[derive(Deserialize)]
pub struct Package {
pub name: String,
}
fn raw_cargo() -> Command {
match env::var_os("CARGO") {
Some(cargo) => Command::new(cargo),
None => Command::new("cargo"),
}
}
fn cargo(project: &Project) -> Command {
let mut cmd = raw_cargo();
cmd.current_dir(&project.dir);
cmd.env(
"CARGO_TARGET_DIR",
path!(project.target_dir / "tests" / "target"),
);
cmd.arg("--offline");
rustflags::set_env(&mut cmd);
cmd
}
pub fn build_dependencies(project: &Project) -> Result<()> {
let workspace_cargo_lock = path!(project.workspace / "Cargo.lock");
if workspace_cargo_lock.exists() {
let _ = fs::copy(workspace_cargo_lock, path!(project.dir / "Cargo.lock"));
} else {
let _ = cargo(project).arg("generate-lockfile").status();
}
let status = cargo(project)
.arg(if project.has_pass { "build" } else { "check" })
.args(target())
.arg("--bin")
.arg(&project.name)
.args(features(project))
.status()
.map_err(Error::Cargo)?;
if status.success() {
Ok(())
} else {
Err(Error::CargoFail)
}
}
pub fn build_test(project: &Project, name: &Name) -> Result<Output> {
let _ = cargo(project)
.arg("clean")
.arg("--package")
.arg(&project.name)
.arg("--color=never")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
cargo(project)
.arg(if project.has_pass { "build" } else { "check" })
.args(target())
.arg("--bin")
.arg(name)
.args(features(project))
.arg("--quiet")
.arg("--color=never")
.output()
.map_err(Error::Cargo)
}
pub fn run_test(project: &Project, name: &Name) -> Result<Output> {
cargo(project)
.arg("run")
.args(target())
.arg("--bin")
.arg(name)
.args(features(project))
.arg("--quiet")
.arg("--color=never")
.output()
.map_err(Error::Cargo)
}
pub fn metadata() -> Result<Metadata> {
let output = raw_cargo()
.arg("metadata")
.arg("--no-deps")
.arg("--format-version=1")
.output()
.map_err(Error::Cargo)?;
serde_json::from_slice(&output.stdout).map_err(|err| {
print!("{}", String::from_utf8_lossy(&output.stderr));
Error::Metadata(err)
})
}
fn features(project: &Project) -> Vec<String> {
match &project.features {
Some(features) => vec![
"--no-default-features".to_owned(),
"--features".to_owned(),
features.join(","),
],
None => vec![],
}
}
fn target() -> Vec<&'static str> {
const TARGET: Option<&str> = include!(concat!(env!("OUT_DIR"), "/target"));
// When --target flag is passed, cargo does not pass RUSTFLAGS to rustc when
// building proc-macro and build script even if the host and target triples
// are the same. Therefore, if we always pass --target to cargo, tools such
// as coverage that require RUSTFLAGS do not work for tests run by trybuild.
//
// To avoid that problem, do not pass --target to cargo if we know that it
// has not been passed.
//
// Currently, cargo does not have a way to tell the build script whether
// --target has been passed or not, and there is no heuristic that can
// handle this well.
//
// Therefore, expose a cfg to always treat the target as host.
if cfg!(trybuild_no_target) {
vec![]
} else if let Some(target) = TARGET {
vec!["--target", target]
} else {
vec![]
}
}