]> git.proxmox.com Git - rustc.git/blob - src/tools/cargo/tests/testsuite/weak_dep_features.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / src / tools / cargo / tests / testsuite / weak_dep_features.rs
1 //! Tests for weak-dep-features.
2
3 use super::features2::switch_to_resolver_2;
4 use cargo_test_support::paths::CargoPathExt;
5 use cargo_test_support::registry::{Dependency, Package, RegistryBuilder};
6 use cargo_test_support::{project, publish};
7 use std::fmt::Write;
8
9 // Helper to create lib.rs files that check features.
10 fn require(enabled_features: &[&str], disabled_features: &[&str]) -> String {
11 let mut s = String::new();
12 for feature in enabled_features {
13 writeln!(s, "#[cfg(not(feature=\"{feature}\"))] compile_error!(\"expected feature {feature} to be enabled\");",
14 feature=feature).unwrap();
15 }
16 for feature in disabled_features {
17 writeln!(s, "#[cfg(feature=\"{feature}\")] compile_error!(\"did not expect feature {feature} to be enabled\");",
18 feature=feature).unwrap();
19 }
20 s
21 }
22
23 #[cargo_test]
24 fn simple() {
25 Package::new("bar", "1.0.0")
26 .feature("feat", &[])
27 .file("src/lib.rs", &require(&["feat"], &[]))
28 .publish();
29 let p = project()
30 .file(
31 "Cargo.toml",
32 r#"
33 [package]
34 name = "foo"
35 version = "0.1.0"
36
37 [dependencies]
38 bar = { version = "1.0", optional = true }
39
40 [features]
41 f1 = ["bar?/feat"]
42 "#,
43 )
44 .file("src/lib.rs", &require(&["f1"], &[]))
45 .build();
46
47 // It's a bit unfortunate that this has to download `bar`, but avoiding
48 // that is extremely difficult.
49 p.cargo("check --features f1")
50 .with_stderr(
51 "\
52 [UPDATING] [..]
53 [DOWNLOADING] crates ...
54 [DOWNLOADED] bar v1.0.0 [..]
55 [CHECKING] foo v0.1.0 [..]
56 [FINISHED] [..]
57 ",
58 )
59 .run();
60
61 p.cargo("check --features f1,bar")
62 .with_stderr(
63 "\
64 [CHECKING] bar v1.0.0
65 [CHECKING] foo v0.1.0 [..]
66 [FINISHED] [..]
67 ",
68 )
69 .run();
70 }
71
72 #[cargo_test]
73 fn deferred() {
74 // A complex chain that requires deferring enabling the feature due to
75 // another dependency getting enabled.
76 Package::new("bar", "1.0.0")
77 .feature("feat", &[])
78 .file("src/lib.rs", &require(&["feat"], &[]))
79 .publish();
80 Package::new("dep", "1.0.0")
81 .add_dep(Dependency::new("bar", "1.0").optional(true))
82 .feature("feat", &["bar?/feat"])
83 .publish();
84 Package::new("bar_activator", "1.0.0")
85 .feature_dep("dep", "1.0", &["bar"])
86 .publish();
87 let p = project()
88 .file(
89 "Cargo.toml",
90 r#"
91 [package]
92 name = "foo"
93 version = "0.1.0"
94
95 [dependencies]
96 dep = { version = "1.0", features = ["feat"] }
97 bar_activator = "1.0"
98 "#,
99 )
100 .file("src/lib.rs", "")
101 .build();
102
103 p.cargo("check")
104 .with_stderr(
105 "\
106 [UPDATING] [..]
107 [DOWNLOADING] crates ...
108 [DOWNLOADED] dep v1.0.0 [..]
109 [DOWNLOADED] bar_activator v1.0.0 [..]
110 [DOWNLOADED] bar v1.0.0 [..]
111 [CHECKING] bar v1.0.0
112 [CHECKING] dep v1.0.0
113 [CHECKING] bar_activator v1.0.0
114 [CHECKING] foo v0.1.0 [..]
115 [FINISHED] [..]
116 ",
117 )
118 .run();
119 }
120
121 #[cargo_test]
122 fn not_optional_dep() {
123 // Attempt to use dep_name?/feat where dep_name is not optional.
124 Package::new("dep", "1.0.0").feature("feat", &[]).publish();
125
126 let p = project()
127 .file(
128 "Cargo.toml",
129 r#"
130 [package]
131 name = "foo"
132 version = "0.1.0"
133
134 [dependencies]
135 dep = "1.0"
136
137 [features]
138 feat = ["dep?/feat"]
139 "#,
140 )
141 .file("src/lib.rs", "")
142 .build();
143
144 p.cargo("check")
145 .with_status(101)
146 .with_stderr("\
147 error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
148
149 Caused by:
150 feature `feat` includes `dep?/feat` with a `?`, but `dep` is not an optional dependency
151 A non-optional dependency of the same name is defined; consider removing the `?` or changing the dependency to be optional
152 ")
153 .run();
154 }
155
156 #[cargo_test]
157 fn optional_cli_syntax() {
158 // --features bar?/feat
159 Package::new("bar", "1.0.0")
160 .feature("feat", &[])
161 .file("src/lib.rs", &require(&["feat"], &[]))
162 .publish();
163
164 let p = project()
165 .file(
166 "Cargo.toml",
167 r#"
168 [package]
169 name = "foo"
170 version = "0.1.0"
171
172 [dependencies]
173 bar = { version = "1.0", optional = true }
174 "#,
175 )
176 .file("src/lib.rs", "")
177 .build();
178
179 // Does not build bar.
180 p.cargo("check --features bar?/feat")
181 .with_stderr(
182 "\
183 [UPDATING] [..]
184 [DOWNLOADING] crates ...
185 [DOWNLOADED] bar v1.0.0 [..]
186 [CHECKING] foo v0.1.0 [..]
187 [FINISHED] [..]
188 ",
189 )
190 .run();
191
192 // Builds bar.
193 p.cargo("check --features bar?/feat,bar")
194 .with_stderr(
195 "\
196 [CHECKING] bar v1.0.0
197 [CHECKING] foo v0.1.0 [..]
198 [FINISHED] [..]
199 ",
200 )
201 .run();
202
203 eprintln!("check V2 resolver");
204 switch_to_resolver_2(&p);
205 p.build_dir().rm_rf();
206 // Does not build bar.
207 p.cargo("check --features bar?/feat")
208 .with_stderr(
209 "\
210 [CHECKING] foo v0.1.0 [..]
211 [FINISHED] [..]
212 ",
213 )
214 .run();
215
216 // Builds bar.
217 p.cargo("check --features bar?/feat,bar")
218 .with_stderr(
219 "\
220 [CHECKING] bar v1.0.0
221 [CHECKING] foo v0.1.0 [..]
222 [FINISHED] [..]
223 ",
224 )
225 .run();
226 }
227
228 #[cargo_test]
229 fn required_features() {
230 // required-features doesn't allow ?
231 Package::new("bar", "1.0.0").feature("feat", &[]).publish();
232
233 let p = project()
234 .file(
235 "Cargo.toml",
236 r#"
237 [package]
238 name = "foo"
239 version = "0.1.0"
240
241 [dependencies]
242 bar = { version = "1.0", optional = true }
243
244 [[bin]]
245 name = "foo"
246 required-features = ["bar?/feat"]
247 "#,
248 )
249 .file("src/main.rs", "fn main() {}")
250 .build();
251
252 p.cargo("check")
253 .with_status(101)
254 .with_stderr(
255 "\
256 [UPDATING] [..]
257 [ERROR] invalid feature `bar?/feat` in required-features of target `foo`: \
258 optional dependency with `?` is not allowed in required-features
259 ",
260 )
261 .run();
262 }
263
264 #[cargo_test]
265 fn weak_with_host_decouple() {
266 // weak-dep-features with new resolver
267 //
268 // foo v0.1.0
269 // └── common v1.0.0
270 // └── bar v1.0.0 <-- does not have `feat` enabled
271 // [build-dependencies]
272 // └── bar_activator v1.0.0
273 // └── common v1.0.0
274 // └── bar v1.0.0 <-- does have `feat` enabled
275 Package::new("bar", "1.0.0")
276 .feature("feat", &[])
277 .file(
278 "src/lib.rs",
279 r#"
280 pub fn feat() -> bool {
281 cfg!(feature = "feat")
282 }
283 "#,
284 )
285 .publish();
286
287 Package::new("common", "1.0.0")
288 .add_dep(Dependency::new("bar", "1.0").optional(true))
289 .feature("feat", &["bar?/feat"])
290 .file(
291 "src/lib.rs",
292 r#"
293 #[cfg(feature = "bar")]
294 pub fn feat() -> bool { bar::feat() }
295 #[cfg(not(feature = "bar"))]
296 pub fn feat() -> bool { false }
297 "#,
298 )
299 .publish();
300
301 Package::new("bar_activator", "1.0.0")
302 .feature_dep("common", "1.0", &["bar", "feat"])
303 .file(
304 "src/lib.rs",
305 r#"
306 pub fn feat() -> bool {
307 common::feat()
308 }
309 "#,
310 )
311 .publish();
312
313 let p = project()
314 .file(
315 "Cargo.toml",
316 r#"
317 [package]
318 name = "foo"
319 version = "0.1.0"
320 resolver = "2"
321
322 [dependencies]
323 common = { version = "1.0", features = ["feat"] }
324
325 [build-dependencies]
326 bar_activator = "1.0"
327 "#,
328 )
329 .file(
330 "src/main.rs",
331 r#"
332 fn main() {
333 assert!(!common::feat());
334 }
335 "#,
336 )
337 .file(
338 "build.rs",
339 r#"
340 fn main() {
341 assert!(bar_activator::feat());
342 }
343 "#,
344 )
345 .build();
346
347 p.cargo("run")
348 .with_stderr(
349 "\
350 [UPDATING] [..]
351 [DOWNLOADING] crates ...
352 [DOWNLOADED] [..]
353 [DOWNLOADED] [..]
354 [DOWNLOADED] [..]
355 [COMPILING] bar v1.0.0
356 [COMPILING] common v1.0.0
357 [COMPILING] bar_activator v1.0.0
358 [COMPILING] foo v0.1.0 [..]
359 [FINISHED] [..]
360 [RUNNING] `target/debug/foo[EXE]`
361 ",
362 )
363 .run();
364 }
365
366 #[cargo_test]
367 fn weak_namespaced() {
368 // Behavior with a dep: dependency.
369 Package::new("bar", "1.0.0")
370 .feature("feat", &[])
371 .file("src/lib.rs", &require(&["feat"], &[]))
372 .publish();
373 let p = project()
374 .file(
375 "Cargo.toml",
376 r#"
377 [package]
378 name = "foo"
379 version = "0.1.0"
380
381 [dependencies]
382 bar = { version = "1.0", optional = true }
383
384 [features]
385 f1 = ["bar?/feat"]
386 f2 = ["dep:bar"]
387 "#,
388 )
389 .file("src/lib.rs", &require(&["f1"], &["f2", "bar"]))
390 .build();
391
392 p.cargo("check --features f1")
393 .with_stderr(
394 "\
395 [UPDATING] [..]
396 [DOWNLOADING] crates ...
397 [DOWNLOADED] bar v1.0.0 [..]
398 [CHECKING] foo v0.1.0 [..]
399 [FINISHED] [..]
400 ",
401 )
402 .run();
403
404 p.cargo("tree -f")
405 .arg("{p} feats:{f}")
406 .with_stdout("foo v0.1.0 ([ROOT]/foo) feats:")
407 .run();
408
409 p.cargo("tree --features f1 -f")
410 .arg("{p} feats:{f}")
411 .with_stdout("foo v0.1.0 ([ROOT]/foo) feats:f1")
412 .run();
413
414 p.cargo("tree --features f1,f2 -f")
415 .arg("{p} feats:{f}")
416 .with_stdout(
417 "\
418 foo v0.1.0 ([ROOT]/foo) feats:f1,f2
419 └── bar v1.0.0 feats:feat
420 ",
421 )
422 .run();
423
424 // "bar" remains not-a-feature
425 p.change_file("src/lib.rs", &require(&["f1", "f2"], &["bar"]));
426
427 p.cargo("check --features f1,f2")
428 .with_stderr(
429 "\
430 [CHECKING] bar v1.0.0
431 [CHECKING] foo v0.1.0 [..]
432 [FINISHED] [..]
433 ",
434 )
435 .run();
436 }
437
438 #[cargo_test]
439 fn tree() {
440 Package::new("bar", "1.0.0")
441 .feature("feat", &[])
442 .file("src/lib.rs", &require(&["feat"], &[]))
443 .publish();
444 let p = project()
445 .file(
446 "Cargo.toml",
447 r#"
448 [package]
449 name = "foo"
450 version = "0.1.0"
451
452 [dependencies]
453 bar = { version = "1.0", optional = true }
454
455 [features]
456 f1 = ["bar?/feat"]
457 "#,
458 )
459 .file("src/lib.rs", &require(&["f1"], &[]))
460 .build();
461
462 p.cargo("tree --features f1")
463 .with_stdout("foo v0.1.0 ([ROOT]/foo)")
464 .run();
465
466 p.cargo("tree --features f1,bar")
467 .with_stdout(
468 "\
469 foo v0.1.0 ([ROOT]/foo)
470 └── bar v1.0.0
471 ",
472 )
473 .run();
474
475 p.cargo("tree --features f1,bar -e features")
476 .with_stdout(
477 "\
478 foo v0.1.0 ([ROOT]/foo)
479 └── bar feature \"default\"
480 └── bar v1.0.0
481 ",
482 )
483 .run();
484
485 p.cargo("tree --features f1,bar -e features -i bar")
486 .with_stdout(
487 "\
488 bar v1.0.0
489 ├── bar feature \"default\"
490 │ └── foo v0.1.0 ([ROOT]/foo)
491 │ ├── foo feature \"bar\" (command-line)
492 │ ├── foo feature \"default\" (command-line)
493 │ └── foo feature \"f1\" (command-line)
494 └── bar feature \"feat\"
495 └── foo feature \"f1\" (command-line)
496 ",
497 )
498 .run();
499
500 p.cargo("tree -e features --features bar?/feat")
501 .with_stdout("foo v0.1.0 ([ROOT]/foo)")
502 .run();
503
504 // This is a little strange in that it produces no output.
505 // Maybe `cargo tree` should print a note about why?
506 p.cargo("tree -e features -i bar --features bar?/feat")
507 .with_stdout("")
508 .run();
509
510 p.cargo("tree -e features -i bar --features bar?/feat,bar")
511 .with_stdout(
512 "\
513 bar v1.0.0
514 ├── bar feature \"default\"
515 │ └── foo v0.1.0 ([ROOT]/foo)
516 │ ├── foo feature \"bar\" (command-line)
517 │ └── foo feature \"default\" (command-line)
518 └── bar feature \"feat\" (command-line)
519 ",
520 )
521 .run();
522 }
523
524 #[cargo_test]
525 fn publish() {
526 let registry = RegistryBuilder::new().http_api().http_index().build();
527
528 // Publish behavior with /? syntax.
529 Package::new("bar", "1.0.0").feature("feat", &[]).publish();
530 let p = project()
531 .file(
532 "Cargo.toml",
533 r#"
534 [package]
535 name = "foo"
536 version = "0.1.0"
537 description = "foo"
538 license = "MIT"
539 homepage = "https://example.com/"
540
541 [dependencies]
542 bar = { version = "1.0", optional = true }
543
544 [features]
545 feat1 = []
546 feat2 = ["bar?/feat"]
547 "#,
548 )
549 .file("src/lib.rs", "")
550 .build();
551
552 p.cargo("publish")
553 .replace_crates_io(registry.index_url())
554 .with_stderr(
555 "\
556 [UPDATING] [..]
557 [PACKAGING] foo v0.1.0 [..]
558 [VERIFYING] foo v0.1.0 [..]
559 [UPDATING] [..]
560 [COMPILING] foo v0.1.0 [..]
561 [FINISHED] [..]
562 [PACKAGED] [..]
563 [UPLOADING] foo v0.1.0 [..]
564 [UPLOADED] foo v0.1.0 to registry `crates-io`
565 note: Waiting for `foo v0.1.0` to be available at registry `crates-io`.
566 You may press ctrl-c to skip waiting; the crate should be available shortly.
567 [PUBLISHED] foo v0.1.0 at registry `crates-io`
568 ",
569 )
570 .run();
571
572 publish::validate_upload_with_contents(
573 r#"
574 {
575 "authors": [],
576 "badges": {},
577 "categories": [],
578 "deps": [
579 {
580 "default_features": true,
581 "features": [],
582 "kind": "normal",
583 "name": "bar",
584 "optional": true,
585 "target": null,
586 "version_req": "^1.0"
587 }
588 ],
589 "description": "foo",
590 "documentation": null,
591 "features": {
592 "feat1": [],
593 "feat2": ["bar?/feat"]
594 },
595 "homepage": "https://example.com/",
596 "keywords": [],
597 "license": "MIT",
598 "license_file": null,
599 "links": null,
600 "name": "foo",
601 "readme": null,
602 "readme_file": null,
603 "repository": null,
604 "rust_version": null,
605 "vers": "0.1.0"
606 }
607 "#,
608 "foo-0.1.0.crate",
609 &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
610 &[(
611 "Cargo.toml",
612 &format!(
613 r#"{}
614 [package]
615 name = "foo"
616 version = "0.1.0"
617 description = "foo"
618 homepage = "https://example.com/"
619 license = "MIT"
620
621 [dependencies.bar]
622 version = "1.0"
623 optional = true
624
625 [features]
626 feat1 = []
627 feat2 = ["bar?/feat"]
628 "#,
629 cargo::core::package::MANIFEST_PREAMBLE
630 ),
631 )],
632 );
633 }