trybuild_internals_api/
cargo.rs1use crate::directory::Directory;
2use crate::error::{Error, Result};
3use crate::manifest::Name;
4use crate::run::Project;
5use crate::rustflags;
6use serde_derive::Deserialize;
7use std::path::PathBuf;
8use std::process::{Command, Output, Stdio};
9use std::{env, fs, iter};
10
11#[derive(Deserialize)]
12pub struct Metadata {
13 pub target_directory: Directory,
14 pub workspace_root: Directory,
15 pub packages: Vec<PackageMetadata>,
16}
17
18#[derive(Deserialize)]
19pub struct PackageMetadata {
20 pub name: String,
21 pub targets: Vec<BuildTarget>,
22 pub manifest_path: PathBuf,
23}
24
25#[derive(Deserialize)]
26pub struct BuildTarget {
27 pub crate_types: Vec<String>,
28}
29
30fn raw_cargo() -> Command {
31 match env::var_os("CARGO") {
32 Some(cargo) => Command::new(cargo),
33 None => Command::new("cargo"),
34 }
35}
36
37pub fn cargo(project: &Project) -> Command {
38 let mut cmd = raw_cargo();
39 cmd.current_dir(&project.dir);
40 cmd.envs(cargo_target_dir(project));
41 cmd.env_remove("RUSTFLAGS");
42 cmd.env("CARGO_INCREMENTAL", "0");
43 cmd.arg("--offline");
44 cmd.arg(format!("--config=build.rustflags={}", rustflags::toml()));
45 cmd
46}
47
48fn cargo_target_dir(project: &Project) -> impl Iterator<Item = (&'static str, PathBuf)> {
49 iter::once((
50 "CARGO_TARGET_DIR",
51 path!(project.target_dir / "tests" / "trybuild"),
52 ))
53}
54
55pub fn manifest_dir() -> Result<Directory> {
56 if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") {
57 return Ok(Directory::from(manifest_dir));
58 }
59 let mut dir = Directory::current()?;
60 loop {
61 if dir.join("Cargo.toml").exists() {
62 return Ok(dir);
63 }
64 dir = dir.parent().ok_or(Error::ProjectDir)?;
65 }
66}
67
68pub fn build_dependencies(project: &mut Project) -> Result<()> {
69 let workspace_cargo_lock = path!(project.workspace / "Cargo.lock");
70 if workspace_cargo_lock.exists() {
71 let _ = fs::copy(workspace_cargo_lock, path!(project.dir / "Cargo.lock"));
72 } else {
73 let _ = cargo(project).arg("generate-lockfile").status();
74 }
75
76 let mut command = cargo(project);
77 command
78 .arg(if project.has_pass { "build" } else { "check" })
79 .args(target())
80 .arg("--bin")
81 .arg(&project.name)
82 .args(features(project));
83
84 let status = command.status().map_err(Error::Cargo)?;
85 if !status.success() {
86 return Err(Error::CargoFail);
87 }
88
89 project.keep_going = command
91 .arg("--keep-going")
92 .stdout(Stdio::null())
93 .stderr(Stdio::null())
94 .status()
95 .map(|status| status.success())
96 .unwrap_or(false);
97
98 Ok(())
99}
100
101pub fn build_test(project: &Project, name: &Name) -> Result<Output> {
102 let _ = cargo(project)
103 .arg("clean")
104 .arg("--package")
105 .arg(&project.name)
106 .arg("--color=never")
107 .stdout(Stdio::null())
108 .stderr(Stdio::null())
109 .status();
110
111 cargo(project)
112 .arg(if project.has_pass { "build" } else { "check" })
113 .args(target())
114 .arg("--bin")
115 .arg(name)
116 .args(features(project))
117 .arg("--quiet")
118 .arg("--color=never")
119 .arg("--message-format=json")
120 .output()
121 .map_err(Error::Cargo)
122}
123
124pub fn build_all_tests(project: &Project) -> Result<Output> {
125 let _ = cargo(project)
126 .arg("clean")
127 .arg("--package")
128 .arg(&project.name)
129 .arg("--color=never")
130 .stdout(Stdio::null())
131 .stderr(Stdio::null())
132 .status();
133
134 cargo(project)
135 .arg(if project.has_pass { "build" } else { "check" })
136 .args(target())
137 .arg("--bins")
138 .args(features(project))
139 .arg("--quiet")
140 .arg("--color=never")
141 .arg("--message-format=json")
142 .arg("--keep-going")
143 .output()
144 .map_err(Error::Cargo)
145}
146
147pub fn run_test(project: &Project, name: &Name) -> Result<Output> {
148 cargo(project)
149 .arg("run")
150 .args(target())
151 .arg("--bin")
152 .arg(name)
153 .args(features(project))
154 .arg("--quiet")
155 .arg("--color=never")
156 .output()
157 .map_err(Error::Cargo)
158}
159
160pub fn metadata() -> Result<Metadata> {
161 let output = raw_cargo()
162 .arg("metadata")
163 .arg("--no-deps")
164 .arg("--format-version=1")
165 .output()
166 .map_err(Error::Cargo)?;
167
168 serde_json::from_slice(&output.stdout).map_err(|err| {
169 print!("{}", String::from_utf8_lossy(&output.stderr));
170 Error::Metadata(err)
171 })
172}
173
174fn features(project: &Project) -> Vec<String> {
175 match &project.features {
176 Some(features) => vec![
177 "--no-default-features".to_owned(),
178 "--features".to_owned(),
179 features.join(","),
180 ],
181 None => vec![],
182 }
183}
184
185fn target() -> Vec<&'static str> {
186 #[cfg(not(host_os = "windows"))]
187 const TARGET: Option<&str> = include!(concat!(env!("OUT_DIR"), "/target"));
188
189 #[cfg(host_os = "windows")]
190 const TARGET: Option<&str> = include!(concat!(env!("OUT_DIR"), "\\target"));
191
192 if cfg!(trybuild_no_target) {
206 vec![]
207 } else if let Some(target) = TARGET {
208 vec!["--target", target]
209 } else {
210 vec![]
211 }
212}