1 extern crate cargo_bazel;
2 extern crate serde_json;
3 extern crate tempfile;
4
5 use anyhow::{ensure, Context, Result};
6 use cargo_bazel::cli::{splice, SpliceOptions};
7 use serde_json::{json, Value};
8 use std::collections::HashMap;
9 use std::env;
10 use std::fs;
11 use std::path::PathBuf;
12
setup_cargo_env() -> Result<(PathBuf, PathBuf)>13 fn setup_cargo_env() -> Result<(PathBuf, PathBuf)> {
14 let cargo = std::fs::canonicalize(PathBuf::from(
15 env::var("CARGO").context("CARGO environment variable must be set.")?,
16 ))
17 .unwrap();
18 let rustc = std::fs::canonicalize(PathBuf::from(
19 env::var("RUSTC").context("RUSTC environment variable must be set.")?,
20 ))
21 .unwrap();
22 ensure!(cargo.exists());
23 ensure!(rustc.exists());
24 // If $RUSTC is a relative path it can cause issues with
25 // `cargo_metadata::MetadataCommand`. Just to be on the safe side, we make
26 // both of these env variables absolute paths.
27 if cargo != PathBuf::from(env::var("CARGO").unwrap()) {
28 env::set_var("CARGO", cargo.as_os_str());
29 }
30 if rustc != PathBuf::from(env::var("RUSTC").unwrap()) {
31 env::set_var("RUSTC", rustc.as_os_str());
32 }
33
34 let cargo_home = PathBuf::from(
35 env::var("TEST_TMPDIR").context("TEST_TMPDIR environment variable must be set.")?,
36 )
37 .join("cargo_home");
38 env::set_var("CARGO_HOME", cargo_home.as_os_str());
39 fs::create_dir_all(&cargo_home)?;
40
41 println!("$RUSTC={}", rustc.display());
42 println!("$CARGO={}", cargo.display());
43 println!("$CARGO_HOME={}", cargo_home.display());
44
45 Ok((cargo, rustc))
46 }
47
run(repository_name: &str, manifests: HashMap<String, String>, lockfile: &str) -> Value48 fn run(repository_name: &str, manifests: HashMap<String, String>, lockfile: &str) -> Value {
49 let (cargo, rustc) = setup_cargo_env().unwrap();
50
51 let scratch = tempfile::tempdir().unwrap();
52 let runfiles = runfiles::Runfiles::create().unwrap();
53
54 /*
55 let manifest_path = scratch.path().join("Cargo.toml");
56 fs::copy(
57 runfiles.rlocation(manifest),
58 manifest_path,
59 )
60 .unwrap();
61 */
62
63 let splicing_manifest = scratch.path().join("splicing_manifest.json");
64 fs::write(
65 &splicing_manifest,
66 serde_json::to_string(&json!({
67 "manifests": manifests,
68 "direct_packages": {},
69 "resolver_version": "2"
70 }))
71 .unwrap(),
72 )
73 .unwrap();
74
75 let config = scratch.path().join("config.json");
76 fs::write(
77 &config,
78 serde_json::to_string(&json!({
79 "generate_binaries": false,
80 "generate_build_scripts": false,
81 "rendering": {
82 "repository_name": repository_name,
83 "regen_command": "//crate_universe:cargo_integration_test"
84 },
85 "supported_platform_triples": [
86 "x86_64-apple-darwin",
87 "x86_64-pc-windows-msvc",
88 "x86_64-unknown-linux-gnu",
89 ]
90 }))
91 .unwrap(),
92 )
93 .unwrap();
94
95 splice(SpliceOptions {
96 splicing_manifest,
97 cargo_lockfile: Some(runfiles.rlocation(lockfile)),
98 repin: None,
99 workspace_dir: None,
100 output_dir: scratch.path().join("out"),
101 dry_run: false,
102 cargo_config: None,
103 config,
104 cargo,
105 rustc,
106 })
107 .unwrap();
108
109 let metadata = serde_json::from_str::<Value>(
110 &fs::read_to_string(scratch.path().join("out").join("metadata.json")).unwrap(),
111 )
112 .unwrap();
113
114 metadata
115 }
116
117 // See crate_universe/test_data/metadata/target_features/Cargo.toml for input.
118 #[test]
feature_generator()119 fn feature_generator() {
120 // This test case requires network access to build pull crate metadata
121 // so that we can actually run `cargo tree`. However, RBE (and perhaps
122 // other environments) disallow or don't support this. In those cases,
123 // we just skip this test case.
124 use std::net::ToSocketAddrs;
125 if "github.com:443".to_socket_addrs().is_err() {
126 eprintln!("This test case requires network access. Skipping!");
127 return;
128 }
129
130 let runfiles = runfiles::Runfiles::create().unwrap();
131 let metadata = run(
132 "target_feature_test",
133 HashMap::from([(
134 runfiles
135 .rlocation(
136 "rules_rust/crate_universe/test_data/metadata/target_features/Cargo.toml",
137 )
138 .to_string_lossy()
139 .to_string(),
140 "//:test_input".to_string(),
141 )]),
142 "rules_rust/crate_universe/test_data/metadata/target_features/Cargo.lock",
143 );
144
145 assert_eq!(
146 metadata["metadata"]["cargo-bazel"]["features"]["wgpu-hal 0.14.1"],
147 json!({
148 "selects": {
149 "x86_64-apple-darwin": [
150 "block", "foreign-types", "metal",
151 ],
152 "x86_64-pc-windows-msvc": [
153 "ash", "bit-set", "dx11", "dx12", "gpu-alloc",
154 "gpu-descriptor", "libloading", "native", "range-alloc",
155 "renderdoc", "renderdoc-sys", "smallvec", "vulkan",
156 ],
157 "x86_64-unknown-linux-gnu": [
158 "ash", "egl", "gles", "glow", "gpu-alloc",
159 "gpu-descriptor", "libloading", "renderdoc", "renderdoc-sys",
160 "smallvec", "vulkan",
161 ],
162 },
163 "common": [
164 "default",
165 ],
166 })
167 );
168 }
169
170 // See crate_universe/test_data/metadata/target_cfg_features/Cargo.toml for input.
171 #[test]
feature_generator_cfg_features()172 fn feature_generator_cfg_features() {
173 // This test case requires network access to build pull crate metadata
174 // so that we can actually run `cargo tree`. However, RBE (and perhaps
175 // other environments) disallow or don't support this. In those cases,
176 // we just skip this test case.
177 use std::net::ToSocketAddrs;
178 if "github.com:443".to_socket_addrs().is_err() {
179 eprintln!("This test case requires network access. Skipping!");
180 return;
181 }
182
183 let runfiles = runfiles::Runfiles::create().unwrap();
184 let metadata = run(
185 "target_cfg_features_test",
186 HashMap::from([(
187 runfiles
188 .rlocation(
189 "rules_rust/crate_universe/test_data/metadata/target_cfg_features/Cargo.toml",
190 )
191 .to_string_lossy()
192 .to_string(),
193 "//:test_input".to_string(),
194 )]),
195 "rules_rust/crate_universe/test_data/metadata/target_cfg_features/Cargo.lock",
196 );
197
198 assert_eq!(
199 metadata["metadata"]["cargo-bazel"]["features"],
200 json!({
201 "target_cfg_features 0.1.0": {
202 "common": [],
203 "selects": {}
204 },
205 "autocfg 1.1.0": {
206 "common": [],
207 "selects": {}
208 },
209 "pin-project-lite 0.2.9": {
210 "common": [],
211 "selects": {}
212 },
213 "tokio 1.25.0": {
214 "common": ["default"],
215 "selects": {
216 // Note: "x86_64-pc-windows-msvc" is *not* here, despite
217 // being included in `supported_platform_triples` above!
218 "x86_64-apple-darwin": ["fs"],
219 "x86_64-unknown-linux-gnu": ["fs"]
220 }
221 }
222 })
223 );
224 }
225
226 #[test]
feature_generator_workspace()227 fn feature_generator_workspace() {
228 // This test case requires network access to build pull crate metadata
229 // so that we can actually run `cargo tree`. However, RBE (and perhaps
230 // other environments) disallow or don't support this. In those cases,
231 // we just skip this test case.
232 use std::net::ToSocketAddrs;
233 if "github.com:443".to_socket_addrs().is_err() {
234 eprintln!("This test case requires network access. Skipping!");
235 return;
236 }
237
238 let runfiles = runfiles::Runfiles::create().unwrap();
239 let metadata = run(
240 "workspace_test",
241 HashMap::from([
242 (
243 runfiles
244 .rlocation("rules_rust/crate_universe/test_data/metadata/workspace/Cargo.toml")
245 .to_string_lossy()
246 .to_string(),
247 "//:test_input".to_string(),
248 ),
249 (
250 runfiles
251 .rlocation(
252 "rules_rust/crate_universe/test_data/metadata/workspace/child/Cargo.toml",
253 )
254 .to_string_lossy()
255 .to_string(),
256 "//crate_universe:test_data/metadata/workspace/child/Cargo.toml".to_string(),
257 ),
258 ]),
259 "rules_rust/crate_universe/test_data/metadata/workspace/Cargo.lock",
260 );
261
262 assert!(!metadata["metadata"]["cargo-bazel"]["features"]["wgpu 0.14.0"].is_null());
263 }
264
265 #[test]
feature_generator_crate_combined_features()266 fn feature_generator_crate_combined_features() {
267 // This test case requires network access to build pull crate metadata
268 // so that we can actually run `cargo tree`. However, RBE (and perhaps
269 // other environments) disallow or don't support this. In those cases,
270 // we just skip this test case.
271 use std::net::ToSocketAddrs;
272 if "github.com:443".to_socket_addrs().is_err() {
273 eprintln!("This test case requires network access. Skipping!");
274 return;
275 }
276
277 let runfiles = runfiles::Runfiles::create().unwrap();
278 let metadata = run(
279 "crate_combined_features",
280 HashMap::from([
281 (
282 runfiles
283 .rlocation("rules_rust/crate_universe/test_data/metadata/crate_combined_features/Cargo.toml")
284 .to_string_lossy()
285 .to_string(),
286 "//:test_input".to_string(),
287 )
288 ]),
289 "rules_rust/crate_universe/test_data/metadata/crate_combined_features/Cargo.lock",
290 );
291
292 // serde appears twice in the list of dependencies, with and without derive features
293 assert_eq!(
294 metadata["metadata"]["cargo-bazel"]["features"]["serde 1.0.158"]["common"],
295 json!(["default", "derive", "serde_derive", "std"])
296 );
297 }
298