]> git.proxmox.com Git - cargo.git/blame - tests/testsuite/weak_dep_features.rs
Stabilize namespaced and weak dependency features.
[cargo.git] / tests / testsuite / weak_dep_features.rs
CommitLineData
9ffcf690
EH
1//! Tests for weak-dep-features.
2
85854b18
EH
3use super::features2::switch_to_resolver_2;
4use cargo_test_support::paths::CargoPathExt;
9ffcf690 5use cargo_test_support::registry::{Dependency, Package};
be28b58b 6use cargo_test_support::{project, publish};
9ffcf690
EH
7use std::fmt::Write;
8
9// Helper to create lib.rs files that check features.
10fn require(enabled_features: &[&str], disabled_features: &[&str]) -> String {
11 let mut s = String::new();
12 for feature in enabled_features {
706c8242 13 writeln!(s, "#[cfg(not(feature=\"{feature}\"))] compile_error!(\"expected feature {feature} to be enabled\");",
9ffcf690
EH
14 feature=feature).unwrap();
15 }
16 for feature in disabled_features {
706c8242 17 writeln!(s, "#[cfg(feature=\"{feature}\")] compile_error!(\"did not expect feature {feature} to be enabled\");",
9ffcf690
EH
18 feature=feature).unwrap();
19 }
20 s
21}
22
9ffcf690
EH
23#[cargo_test]
24fn 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.
43a063c8 49 p.cargo("check --features f1")
9ffcf690
EH
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
43a063c8 61 p.cargo("check --features f1,bar")
9ffcf690
EH
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]
73fn 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
43a063c8 103 p.cargo("check")
9ffcf690
EH
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]
122fn 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
43a063c8 144 p.cargo("check")
9ffcf690
EH
145 .with_status(101)
146 .with_stderr("\
147error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
148
149Caused 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]
157fn 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
85854b18 179 // Does not build bar.
43a063c8 180 p.cargo("check --features bar?/feat")
9ffcf690
EH
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
85854b18 192 // Builds bar.
43a063c8 193 p.cargo("check --features bar?/feat,bar")
85854b18
EH
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.
43a063c8 207 p.cargo("check --features bar?/feat")
85854b18
EH
208 .with_stderr(
209 "\
210[CHECKING] foo v0.1.0 [..]
211[FINISHED] [..]
212",
213 )
214 .run();
215
216 // Builds bar.
43a063c8 217 p.cargo("check --features bar?/feat,bar")
9ffcf690
EH
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]
229fn 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
43a063c8 252 p.cargo("check")
9ffcf690
EH
253 .with_status(101)
254 .with_stderr(
255 "\
256[UPDATING] [..]
257[ERROR] invalid feature `bar?/feat` in required-features of target `foo`: \
258optional dependency with `?` is not allowed in required-features
259",
260 )
261 .run();
262}
263
264#[cargo_test]
265fn weak_with_host_decouple() {
43a063c8 266 // weak-dep-features with new resolver
9ffcf690
EH
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"
00615fc5 320 resolver = "2"
9ffcf690
EH
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
43a063c8 347 p.cargo("run")
9ffcf690
EH
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]
9034e488
EH
367fn weak_namespaced() {
368 // Behavior with a dep: dependency.
9ffcf690
EH
369 Package::new("bar", "1.0.0")
370 .feature("feat", &[])
371 .file("src/lib.rs", &require(&["feat"], &[]))
372 .publish();
9ffcf690
EH
373 let p = project()
374 .file(
375 "Cargo.toml",
376 r#"
377 [package]
378 name = "foo"
379 version = "0.1.0"
380
381 [dependencies]
9034e488
EH
382 bar = { version = "1.0", optional = true }
383
384 [features]
385 f1 = ["bar?/feat"]
386 f2 = ["dep:bar"]
9ffcf690
EH
387 "#,
388 )
9034e488 389 .file("src/lib.rs", &require(&["f1"], &["f2", "bar"]))
9ffcf690
EH
390 .build();
391
43a063c8 392 p.cargo("check --features f1")
9034e488 393 .with_stderr(
9ffcf690
EH
394 "\
395[UPDATING] [..]
396[DOWNLOADING] crates ...
9034e488
EH
397[DOWNLOADED] bar v1.0.0 [..]
398[CHECKING] foo v0.1.0 [..]
399[FINISHED] [..]
400",
401 )
402 .run();
403
43a063c8 404 p.cargo("tree -f")
9034e488 405 .arg("{p} feats:{f}")
9034e488
EH
406 .with_stdout("foo v0.1.0 ([ROOT]/foo) feats:")
407 .run();
408
43a063c8 409 p.cargo("tree --features f1 -f")
9034e488 410 .arg("{p} feats:{f}")
9034e488
EH
411 .with_stdout("foo v0.1.0 ([ROOT]/foo) feats:f1")
412 .run();
413
43a063c8 414 p.cargo("tree --features f1,f2 -f")
9034e488 415 .arg("{p} feats:{f}")
9034e488
EH
416 .with_stdout(
417 "\
418foo 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
43a063c8 427 p.cargo("check --features f1,f2")
9034e488
EH
428 .with_stderr(
429 "\
9ffcf690 430[CHECKING] bar v1.0.0
9ffcf690
EH
431[CHECKING] foo v0.1.0 [..]
432[FINISHED] [..]
433",
434 )
435 .run();
436}
437
438#[cargo_test]
439fn 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
43a063c8 462 p.cargo("tree --features f1")
9ffcf690
EH
463 .with_stdout("foo v0.1.0 ([ROOT]/foo)")
464 .run();
465
43a063c8 466 p.cargo("tree --features f1,bar")
9ffcf690
EH
467 .with_stdout(
468 "\
469foo v0.1.0 ([ROOT]/foo)
470└── bar v1.0.0
471",
472 )
473 .run();
474
43a063c8 475 p.cargo("tree --features f1,bar -e features")
9ffcf690
EH
476 .with_stdout(
477 "\
478foo v0.1.0 ([ROOT]/foo)
479└── bar feature \"default\"
480 └── bar v1.0.0
481",
482 )
483 .run();
484
43a063c8 485 p.cargo("tree --features f1,bar -e features -i bar")
9ffcf690
EH
486 .with_stdout(
487 "\
488bar v1.0.0
489├── bar feature \"default\"
490│ └── foo v0.1.0 ([ROOT]/foo)
491│ ├── foo feature \"bar\" (command-line)
9ffcf690
EH
492│ ├── foo feature \"default\" (command-line)
493│ └── foo feature \"f1\" (command-line)
494└── bar feature \"feat\"
495 └── foo feature \"f1\" (command-line)
85854b18
EH
496",
497 )
498 .run();
499
43a063c8 500 p.cargo("tree -e features --features bar?/feat")
85854b18
EH
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?
43a063c8 506 p.cargo("tree -e features -i bar --features bar?/feat")
85854b18
EH
507 .with_stdout("")
508 .run();
509
43a063c8 510 p.cargo("tree -e features -i bar --features bar?/feat,bar")
85854b18
EH
511 .with_stdout(
512 "\
513bar 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)
9ffcf690
EH
519",
520 )
521 .run();
522}
be28b58b
EH
523
524#[cargo_test]
525fn publish() {
526 // Publish behavior with /? syntax.
527 Package::new("bar", "1.0.0").feature("feat", &[]).publish();
528 let p = project()
529 .file(
530 "Cargo.toml",
531 r#"
532 [package]
533 name = "foo"
534 version = "0.1.0"
535 description = "foo"
536 license = "MIT"
537 homepage = "https://example.com/"
538
539 [dependencies]
540 bar = { version = "1.0", optional = true }
541
542 [features]
543 feat1 = []
544 feat2 = ["bar?/feat"]
545 "#,
546 )
547 .file("src/lib.rs", "")
548 .build();
549
43a063c8 550 p.cargo("publish --token sekrit")
be28b58b
EH
551 .with_stderr(
552 "\
553[UPDATING] [..]
554[PACKAGING] foo v0.1.0 [..]
555[VERIFYING] foo v0.1.0 [..]
556[COMPILING] foo v0.1.0 [..]
557[FINISHED] [..]
558[UPLOADING] foo v0.1.0 [..]
559",
560 )
561 .run();
562
563 publish::validate_upload_with_contents(
564 r#"
565 {
566 "authors": [],
567 "badges": {},
568 "categories": [],
569 "deps": [
570 {
571 "default_features": true,
572 "features": [],
573 "kind": "normal",
574 "name": "bar",
575 "optional": true,
576 "registry": "https://github.com/rust-lang/crates.io-index",
577 "target": null,
578 "version_req": "^1.0"
579 }
580 ],
581 "description": "foo",
582 "documentation": null,
583 "features": {
584 "feat1": [],
585 "feat2": ["bar?/feat"]
586 },
587 "homepage": "https://example.com/",
588 "keywords": [],
589 "license": "MIT",
590 "license_file": null,
591 "links": null,
592 "name": "foo",
593 "readme": null,
594 "readme_file": null,
595 "repository": null,
596 "vers": "0.1.0"
597 }
598 "#,
599 "foo-0.1.0.crate",
600 &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
601 &[(
602 "Cargo.toml",
b73e3d4f
EH
603 &format!(
604 r#"{}
be28b58b
EH
605[package]
606name = "foo"
607version = "0.1.0"
608description = "foo"
609homepage = "https://example.com/"
610license = "MIT"
611[dependencies.bar]
612version = "1.0"
613optional = true
614
615[features]
616feat1 = []
617feat2 = ["bar?/feat"]
618"#,
b73e3d4f
EH
619 cargo::core::package::MANIFEST_PREAMBLE
620 ),
be28b58b
EH
621 )],
622 );
623}