1 //! Tests for cfg() expressions.
2 
3 use cargo_test_support::registry::Package;
4 use cargo_test_support::rustc_host;
5 use cargo_test_support::{basic_manifest, project};
6 
7 #[cargo_test]
cfg_easy()8 fn cfg_easy() {
9     let p = project()
10         .file(
11             "Cargo.toml",
12             r#"
13                 [package]
14                 name = "a"
15                 version = "0.0.1"
16                 authors = []
17 
18                 [target.'cfg(unix)'.dependencies]
19                 b = { path = 'b' }
20                 [target."cfg(windows)".dependencies]
21                 b = { path = 'b' }
22             "#,
23         )
24         .file("src/lib.rs", "extern crate b;")
25         .file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
26         .file("b/src/lib.rs", "")
27         .build();
28     p.cargo("build -v").run();
29 }
30 
31 #[cargo_test]
dont_include()32 fn dont_include() {
33     let other_family = if cfg!(unix) { "windows" } else { "unix" };
34     let p = project()
35         .file(
36             "Cargo.toml",
37             &format!(
38                 r#"
39                     [package]
40                     name = "a"
41                     version = "0.0.1"
42                     authors = []
43 
44                     [target.'cfg({})'.dependencies]
45                     b = {{ path = 'b' }}
46                 "#,
47                 other_family
48             ),
49         )
50         .file("src/lib.rs", "")
51         .file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
52         .file("b/src/lib.rs", "")
53         .build();
54     p.cargo("build")
55         .with_stderr(
56             "\
57 [COMPILING] a v0.0.1 ([..])
58 [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
59 ",
60         )
61         .run();
62 }
63 
64 #[cargo_test]
works_through_the_registry()65 fn works_through_the_registry() {
66     Package::new("baz", "0.1.0").publish();
67     Package::new("bar", "0.1.0")
68         .target_dep("baz", "0.1.0", "cfg(unix)")
69         .target_dep("baz", "0.1.0", "cfg(windows)")
70         .publish();
71 
72     let p = project()
73         .file(
74             "Cargo.toml",
75             r#"
76                 [package]
77                 name = "foo"
78                 version = "0.0.1"
79                 authors = []
80 
81                 [dependencies]
82                 bar = "0.1.0"
83             "#,
84         )
85         .file(
86             "src/lib.rs",
87             "#[allow(unused_extern_crates)] extern crate bar;",
88         )
89         .build();
90 
91     p.cargo("build")
92         .with_stderr(
93             "\
94 [UPDATING] [..] index
95 [DOWNLOADING] crates ...
96 [DOWNLOADED] [..]
97 [DOWNLOADED] [..]
98 [COMPILING] baz v0.1.0
99 [COMPILING] bar v0.1.0
100 [COMPILING] foo v0.0.1 ([..])
101 [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
102 ",
103         )
104         .run();
105 }
106 
107 #[cargo_test]
ignore_version_from_other_platform()108 fn ignore_version_from_other_platform() {
109     let this_family = if cfg!(unix) { "unix" } else { "windows" };
110     let other_family = if cfg!(unix) { "windows" } else { "unix" };
111     Package::new("bar", "0.1.0").publish();
112     Package::new("bar", "0.2.0").publish();
113 
114     let p = project()
115         .file(
116             "Cargo.toml",
117             &format!(
118                 r#"
119                     [package]
120                     name = "foo"
121                     version = "0.0.1"
122                     authors = []
123 
124                     [target.'cfg({})'.dependencies]
125                     bar = "0.1.0"
126 
127                     [target.'cfg({})'.dependencies]
128                     bar = "0.2.0"
129                 "#,
130                 this_family, other_family
131             ),
132         )
133         .file(
134             "src/lib.rs",
135             "#[allow(unused_extern_crates)] extern crate bar;",
136         )
137         .build();
138 
139     p.cargo("build")
140         .with_stderr(
141             "\
142 [UPDATING] [..] index
143 [DOWNLOADING] crates ...
144 [DOWNLOADED] [..]
145 [COMPILING] bar v0.1.0
146 [COMPILING] foo v0.0.1 ([..])
147 [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
148 ",
149         )
150         .run();
151 }
152 
153 #[cargo_test]
bad_target_spec()154 fn bad_target_spec() {
155     let p = project()
156         .file(
157             "Cargo.toml",
158             r#"
159                 [package]
160                 name = "foo"
161                 version = "0.0.1"
162                 authors = []
163 
164                 [target.'cfg(4)'.dependencies]
165                 bar = "0.1.0"
166             "#,
167         )
168         .file("src/lib.rs", "")
169         .build();
170 
171     p.cargo("build")
172         .with_status(101)
173         .with_stderr(
174             "\
175 [ERROR] failed to parse manifest at `[..]`
176 
177 Caused by:
178   failed to parse `4` as a cfg expression: unexpected character `4` in cfg, [..]
179 ",
180         )
181         .run();
182 }
183 
184 #[cargo_test]
bad_target_spec2()185 fn bad_target_spec2() {
186     let p = project()
187         .file(
188             "Cargo.toml",
189             r#"
190                 [package]
191                 name = "foo"
192                 version = "0.0.1"
193                 authors = []
194 
195                 [target.'cfg(bar =)'.dependencies]
196                 baz = "0.1.0"
197             "#,
198         )
199         .file("src/lib.rs", "")
200         .build();
201 
202     p.cargo("build")
203         .with_status(101)
204         .with_stderr(
205             "\
206 [ERROR] failed to parse manifest at `[..]`
207 
208 Caused by:
209   failed to parse `bar =` as a cfg expression: expected a string, but cfg expression ended
210 ",
211         )
212         .run();
213 }
214 
215 #[cargo_test]
multiple_match_ok()216 fn multiple_match_ok() {
217     let p = project()
218         .file(
219             "Cargo.toml",
220             &format!(
221                 r#"
222                     [package]
223                     name = "a"
224                     version = "0.0.1"
225                     authors = []
226 
227                     [target.'cfg(unix)'.dependencies]
228                     b = {{ path = 'b' }}
229                     [target.'cfg(target_family = "unix")'.dependencies]
230                     b = {{ path = 'b' }}
231                     [target."cfg(windows)".dependencies]
232                     b = {{ path = 'b' }}
233                     [target.'cfg(target_family = "windows")'.dependencies]
234                     b = {{ path = 'b' }}
235                     [target."cfg(any(windows, unix))".dependencies]
236                     b = {{ path = 'b' }}
237 
238                     [target.{}.dependencies]
239                     b = {{ path = 'b' }}
240                 "#,
241                 rustc_host()
242             ),
243         )
244         .file("src/lib.rs", "extern crate b;")
245         .file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
246         .file("b/src/lib.rs", "")
247         .build();
248     p.cargo("build -v").run();
249 }
250 
251 #[cargo_test]
any_ok()252 fn any_ok() {
253     let p = project()
254         .file(
255             "Cargo.toml",
256             r#"
257                 [package]
258                 name = "a"
259                 version = "0.0.1"
260                 authors = []
261 
262                 [target."cfg(any(windows, unix))".dependencies]
263                 b = { path = 'b' }
264             "#,
265         )
266         .file("src/lib.rs", "extern crate b;")
267         .file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
268         .file("b/src/lib.rs", "")
269         .build();
270     p.cargo("build -v").run();
271 }
272 
273 // https://github.com/rust-lang/cargo/issues/5313
274 #[cargo_test]
275 #[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
cfg_looks_at_rustflags_for_target()276 fn cfg_looks_at_rustflags_for_target() {
277     let p = project()
278         .file(
279             "Cargo.toml",
280             r#"
281                 [package]
282                 name = "a"
283                 version = "0.0.1"
284                 authors = []
285 
286                 [target.'cfg(with_b)'.dependencies]
287                 b = { path = 'b' }
288             "#,
289         )
290         .file(
291             "src/main.rs",
292             r#"
293                 #[cfg(with_b)]
294                 extern crate b;
295 
296                 fn main() { b::foo(); }
297             "#,
298         )
299         .file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
300         .file("b/src/lib.rs", "pub fn foo() {}")
301         .build();
302 
303     p.cargo("build --target x86_64-unknown-linux-gnu")
304         .env("RUSTFLAGS", "--cfg with_b")
305         .run();
306 }
307 
308 #[cargo_test]
bad_cfg_discovery()309 fn bad_cfg_discovery() {
310     // Check error messages when `rustc -v` and `rustc --print=*` parsing fails.
311     //
312     // This is a `rustc` replacement which behaves differently based on an
313     // environment variable.
314     let p = project()
315         .at("compiler")
316         .file("Cargo.toml", &basic_manifest("compiler", "0.1.0"))
317         .file(
318             "src/main.rs",
319             r#"
320             fn run_rustc() -> String {
321                 let mut cmd = std::process::Command::new("rustc");
322                 for arg in std::env::args_os().skip(1) {
323                     cmd.arg(arg);
324                 }
325                 String::from_utf8(cmd.output().unwrap().stdout).unwrap()
326             }
327 
328             fn main() {
329                 let mode = std::env::var("FUNKY_MODE").unwrap();
330                 if mode == "bad-version" {
331                     println!("foo");
332                     return;
333                 }
334                 if std::env::args_os().any(|a| a == "-vV") {
335                     print!("{}", run_rustc());
336                     return;
337                 }
338                 if mode == "no-crate-types" {
339                     return;
340                 }
341                 if mode == "bad-crate-type" {
342                     println!("foo");
343                     return;
344                 }
345                 let output = run_rustc();
346                 let mut lines = output.lines();
347                 let sysroot = loop {
348                     let line = lines.next().unwrap();
349                     if line.contains("___") {
350                         println!("{}", line);
351                     } else {
352                         break line;
353                     }
354                 };
355                 if mode == "no-sysroot" {
356                     return;
357                 }
358                 println!("{}", sysroot);
359                 if mode != "bad-cfg" {
360                     panic!("unexpected");
361                 }
362                 println!("123");
363             }
364             "#,
365         )
366         .build();
367     p.cargo("build").run();
368     let funky_rustc = p.bin("compiler");
369 
370     let p = project().file("src/lib.rs", "").build();
371 
372     p.cargo("build")
373         .env("RUSTC", &funky_rustc)
374         .env("FUNKY_MODE", "bad-version")
375         .with_status(101)
376         .with_stderr(
377             "\
378 [ERROR] `rustc -vV` didn't have a line for `host:`, got:
379 foo
380 
381 ",
382         )
383         .run();
384 
385     p.cargo("build")
386         .env("RUSTC", &funky_rustc)
387         .env("FUNKY_MODE", "no-crate-types")
388         .with_status(101)
389         .with_stderr(
390             "\
391 [ERROR] malformed output when learning about crate-type bin information
392 command was: `[..]compiler[..] --crate-name ___ [..]`
393 (no output received)
394 ",
395         )
396         .run();
397 
398     p.cargo("build")
399         .env("RUSTC", &funky_rustc)
400         .env("FUNKY_MODE", "no-sysroot")
401         .with_status(101)
402         .with_stderr(
403             "\
404 [ERROR] output of --print=sysroot missing when learning about target-specific information from rustc
405 command was: `[..]compiler[..]--crate-type [..]`
406 
407 --- stdout
408 [..]___[..]
409 [..]___[..]
410 [..]___[..]
411 [..]___[..]
412 [..]___[..]
413 [..]___[..]
414 
415 ",
416         )
417         .run();
418 
419     p.cargo("build")
420         .env("RUSTC", &funky_rustc)
421         .env("FUNKY_MODE", "bad-cfg")
422         .with_status(101)
423         .with_stderr(
424             "\
425 [ERROR] failed to parse the cfg from `rustc --print=cfg`, got:
426 [..]___[..]
427 [..]___[..]
428 [..]___[..]
429 [..]___[..]
430 [..]___[..]
431 [..]___[..]
432 [..]
433 123
434 
435 
436 Caused by:
437   failed to parse `123` as a cfg expression: unexpected character `1` in cfg, \
438   expected parens, a comma, an identifier, or a string
439 ",
440         )
441         .run();
442 }
443 
444 #[cargo_test]
exclusive_dep_kinds()445 fn exclusive_dep_kinds() {
446     // Checks for a bug where the same package with different cfg expressions
447     // was not being filtered correctly.
448     Package::new("bar", "1.0.0").publish();
449     let p = project()
450         .file(
451             "Cargo.toml",
452             r#"
453                 [package]
454                 name = "foo"
455                 version = "0.1.0"
456 
457                 [target.'cfg(abc)'.dependencies]
458                 bar = "1.0"
459 
460                 [target.'cfg(not(abc))'.build-dependencies]
461                 bar = "1.0"
462             "#,
463         )
464         .file("src/lib.rs", "")
465         .file("build.rs", "extern crate bar; fn main() {}")
466         .build();
467 
468     p.cargo("check").run();
469     p.change_file("src/lib.rs", "extern crate bar;");
470     p.cargo("check")
471         .with_status(101)
472         // can't find crate for `bar`
473         .with_stderr_contains("[..]E0463[..]")
474         .run();
475 }
476