|
21 | 21 | #![allow(clippy::disallowed_methods)]
|
22 | 22 |
|
23 | 23 | use cargo_test_support::*;
|
24 |
| -use std::env; |
25 | 24 | use std::path::Path;
|
| 25 | +use std::{env, fs}; |
26 | 26 |
|
27 | 27 | fn enable_build_std(e: &mut Execs, arg: Option<&str>) {
|
28 | 28 | e.env_remove("CARGO_HOME");
|
@@ -129,6 +129,154 @@ fn basic() {
|
129 | 129 | assert_eq!(p.glob(deps_dir.join("*.dylib")).count(), 0);
|
130 | 130 | }
|
131 | 131 |
|
| 132 | +#[cargo_test(build_std_real)] |
| 133 | +fn validate_std_crate_graph() { |
| 134 | + let p = project() |
| 135 | + .file( |
| 136 | + "src/main.rs", |
| 137 | + " |
| 138 | + fn main() { |
| 139 | + foo::f(); |
| 140 | + } |
| 141 | +
|
| 142 | + #[test] |
| 143 | + fn smoke_bin_unit() { |
| 144 | + foo::f(); |
| 145 | + } |
| 146 | + ", |
| 147 | + ) |
| 148 | + .file( |
| 149 | + "src/lib.rs", |
| 150 | + " |
| 151 | + extern crate alloc; |
| 152 | + extern crate proc_macro; |
| 153 | +
|
| 154 | + /// ``` |
| 155 | + /// foo::f(); |
| 156 | + /// ``` |
| 157 | + pub fn f() { |
| 158 | + } |
| 159 | +
|
| 160 | + #[test] |
| 161 | + fn smoke_lib_unit() { |
| 162 | + f(); |
| 163 | + } |
| 164 | + ", |
| 165 | + ) |
| 166 | + .file( |
| 167 | + "tests/smoke.rs", |
| 168 | + " |
| 169 | + #[test] |
| 170 | + fn smoke_integration() { |
| 171 | + foo::f(); |
| 172 | + } |
| 173 | + ", |
| 174 | + ) |
| 175 | + .build(); |
| 176 | + |
| 177 | + // Extract serialized crate graph from build. |
| 178 | + let serialized_graph = p |
| 179 | + .cargo("build -Zunstable-options --unit-graph") |
| 180 | + .build_std() |
| 181 | + .target_host() |
| 182 | + .exec_with_output() |
| 183 | + .unwrap(); |
| 184 | + let crate_graph: serde_json::Value = |
| 185 | + serde_json::from_slice(serialized_graph.stdout.as_slice()).unwrap(); |
| 186 | + |
| 187 | + // Find the stdlib sysroot. |
| 188 | + let sysroot = String::from_utf8( |
| 189 | + p.cargo("rustc -Zunstable-options --print sysroot") |
| 190 | + .masquerade_as_nightly_cargo(&["unstable-options"]) |
| 191 | + .exec_with_output() |
| 192 | + .unwrap() |
| 193 | + .stdout, |
| 194 | + ) |
| 195 | + .unwrap(); |
| 196 | + |
| 197 | + // Load stdlib lockfile. |
| 198 | + let s = fs::read_to_string(Path::new(sysroot.trim()).join("lib/rustlib/src/rust/Cargo.lock")) |
| 199 | + .unwrap(); |
| 200 | + let encoded_lockfile: cargo::core::resolver::EncodableResolve = toml::from_str(&s).unwrap(); |
| 201 | + |
| 202 | + // Extract the graph from both of the versions. |
| 203 | + let out_graph = crate_graph |
| 204 | + .as_object() |
| 205 | + .unwrap() |
| 206 | + .get("units") |
| 207 | + .unwrap() |
| 208 | + .as_array() |
| 209 | + .unwrap(); |
| 210 | + let lockfile_graph = encoded_lockfile.package().unwrap(); |
| 211 | + |
| 212 | + // Check that resolved graph is subgraph of lockfile. |
| 213 | + for out_unit in out_graph.iter() { |
| 214 | + // Extract package name, version from ID. |
| 215 | + let mut id_elems = out_unit.get("pkg_id").unwrap().as_str().unwrap().split(" "); |
| 216 | + let pkg_id = id_elems.next().unwrap(); |
| 217 | + let pkg_version = id_elems.next().unwrap(); |
| 218 | + |
| 219 | + // Ignore if this is our dummy package. |
| 220 | + if pkg_id == "foo" { |
| 221 | + continue; |
| 222 | + } |
| 223 | + |
| 224 | + // Extract package dependencies. |
| 225 | + let dependencies = out_unit.get("dependencies").unwrap().as_array().unwrap(); |
| 226 | + |
| 227 | + // Ensure this package exists in the lockfile & versions match. |
| 228 | + let lockfile_pkg = lockfile_graph.iter().find(|pkg| pkg.name() == pkg_id); |
| 229 | + if lockfile_pkg.is_none() { |
| 230 | + panic!( |
| 231 | + "Package '{}' ({}) was present in build-std unit graph, but not in lockfile.", |
| 232 | + pkg_id, pkg_version |
| 233 | + ); |
| 234 | + } |
| 235 | + let lockfile_pkg = lockfile_pkg.unwrap(); |
| 236 | + if lockfile_pkg.version() != pkg_version { |
| 237 | + panic!("Package '{}' ({}) from build-std unit graph does not match version in lockfile ({}).", pkg_id, pkg_version, lockfile_pkg.version()); |
| 238 | + } |
| 239 | + |
| 240 | + // Check dependencies match (resolved must be subset of lockfile). |
| 241 | + if lockfile_pkg.dependencies().is_some() && dependencies.len() > 0 { |
| 242 | + let lockfile_deps = lockfile_pkg |
| 243 | + .dependencies() |
| 244 | + .unwrap() |
| 245 | + .iter() |
| 246 | + .map(|x| x.name().replace("-", "_")) |
| 247 | + // Lockfile dependencies may have a prefix. |
| 248 | + .map(|x| { |
| 249 | + x.strip_prefix("rustc_std_workspace_") |
| 250 | + .unwrap_or(&x) |
| 251 | + .to_string() |
| 252 | + }) |
| 253 | + .collect::<Vec<String>>(); |
| 254 | + |
| 255 | + // When collecting resolved dependencies, we ignore the build script unit. |
| 256 | + let dep_names = dependencies |
| 257 | + .iter() |
| 258 | + .map(|x| x.get("extern_crate_name").unwrap().as_str().unwrap()) |
| 259 | + // Resolved dependencies may start with a prefix. |
| 260 | + .map(|x| { |
| 261 | + x.strip_prefix("rustc_std_workspace_") |
| 262 | + .unwrap_or(&x) |
| 263 | + .to_string() |
| 264 | + }) |
| 265 | + .filter(|x| *x != "build_script_build"); |
| 266 | + |
| 267 | + for dep in dep_names { |
| 268 | + if !lockfile_deps.contains(&dep.to_string()) { |
| 269 | + panic!("Package '{}' from build-std unit graph lists differing dependencies in unit graph from lockfile.", pkg_id); |
| 270 | + } |
| 271 | + } |
| 272 | + } else if lockfile_pkg.dependencies().is_some() |
| 273 | + && dependencies.len() > lockfile_pkg.dependencies().unwrap().len() |
| 274 | + { |
| 275 | + panic!("Package '{}' from build-std unit graph lists differing dependencies in unit graph from lockfile.", pkg_id); |
| 276 | + } |
| 277 | + } |
| 278 | +} |
| 279 | + |
132 | 280 | #[cargo_test(build_std_real)]
|
133 | 281 | fn cross_custom() {
|
134 | 282 | let p = project()
|
|
0 commit comments