]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
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, | |
49aad941 | 604 | "rust_version": null, |
0a29b90c FG |
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 | } |