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