]> git.proxmox.com Git - rustc.git/blame - src/compiletest/header.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / compiletest / header.rs
CommitLineData
1a4d82fc
JJ
1// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
223e47cc
LB
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
c34b1796
AL
11use std::env;
12use std::fs::File;
13use std::io::BufReader;
14use std::io::prelude::*;
15use std::path::{Path, PathBuf};
16
1a4d82fc 17use common::Config;
970d7e83 18use common;
1a4d82fc 19use util;
223e47cc 20
54a0048b 21#[derive(Clone, Debug)]
223e47cc
LB
22pub struct TestProps {
23 // Lines that should be expected, in order, on standard out
1a4d82fc 24 pub error_patterns: Vec<String> ,
223e47cc 25 // Extra flags to pass to the compiler
54a0048b 26 pub compile_flags: Vec<String>,
1a4d82fc
JJ
27 // Extra flags to pass when the compiled code is run (such as --bench)
28 pub run_flags: Option<String>,
223e47cc
LB
29 // If present, the name of a file that this test should match when
30 // pretty-printed
c34b1796 31 pub pp_exact: Option<PathBuf>,
223e47cc 32 // Modules from aux directory that should be compiled
1a4d82fc 33 pub aux_builds: Vec<String> ,
54a0048b
SL
34 // Environment settings to use for compiling
35 pub rustc_env: Vec<(String,String)> ,
223e47cc 36 // Environment settings to use during execution
1a4d82fc 37 pub exec_env: Vec<(String,String)> ,
223e47cc 38 // Lines to check if they appear in the expected debugger output
1a4d82fc 39 pub check_lines: Vec<String> ,
92a42be0
SL
40 // Build documentation for all specified aux-builds as well
41 pub build_aux_docs: bool,
1a4d82fc
JJ
42 // Flag to force a crate to be built with the host architecture
43 pub force_host: bool,
44 // Check stdout for error-pattern output as well as stderr
45 pub check_stdout: bool,
46 // Don't force a --crate-type=dylib flag on the command line
47 pub no_prefer_dynamic: bool,
c34b1796
AL
48 // Run --pretty expanded when running pretty printing tests
49 pub pretty_expanded: bool,
1a4d82fc
JJ
50 // Which pretty mode are we testing with, default to 'normal'
51 pub pretty_mode: String,
52 // Only compare pretty output and don't try compiling
53 pub pretty_compare_only: bool,
54 // Patterns which must not appear in the output of a cfail test.
55 pub forbid_output: Vec<String>,
54a0048b
SL
56 // Revisions to test for incremental compilation.
57 pub revisions: Vec<String>,
223e47cc
LB
58}
59
60// Load any test directives embedded in the file
61pub fn load_props(testfile: &Path) -> TestProps {
54a0048b
SL
62 let error_patterns = Vec::new();
63 let aux_builds = Vec::new();
64 let exec_env = Vec::new();
65 let run_flags = None;
66 let pp_exact = None;
67 let check_lines = Vec::new();
68 let build_aux_docs = false;
69 let force_host = false;
70 let check_stdout = false;
71 let no_prefer_dynamic = false;
72 let pretty_expanded = false;
73 let pretty_compare_only = false;
74 let forbid_output = Vec::new();
75 let mut props = TestProps {
76 error_patterns: error_patterns,
77 compile_flags: vec![],
78 run_flags: run_flags,
79 pp_exact: pp_exact,
80 aux_builds: aux_builds,
81 revisions: vec![],
82 rustc_env: vec![],
83 exec_env: exec_env,
84 check_lines: check_lines,
85 build_aux_docs: build_aux_docs,
86 force_host: force_host,
87 check_stdout: check_stdout,
88 no_prefer_dynamic: no_prefer_dynamic,
89 pretty_expanded: pretty_expanded,
90 pretty_mode: format!("normal"),
91 pretty_compare_only: pretty_compare_only,
92 forbid_output: forbid_output,
93 };
94 load_props_into(&mut props, testfile, None);
95 props
96}
97
98/// Load properties from `testfile` into `props`. If a property is
99/// tied to a particular revision `foo` (indicated by writing
100/// `//[foo]`), then the property is ignored unless `cfg` is
101/// `Some("foo")`.
102pub fn load_props_into(props: &mut TestProps, testfile: &Path, cfg: Option<&str>) {
103 iter_header(testfile, cfg, &mut |ln| {
e9174d1e 104 if let Some(ep) = parse_error_pattern(ln) {
54a0048b 105 props.error_patterns.push(ep);
e9174d1e 106 }
223e47cc 107
54a0048b
SL
108 if let Some(flags) = parse_compile_flags(ln) {
109 props.compile_flags.extend(
110 flags
111 .split_whitespace()
112 .map(|s| s.to_owned()));
223e47cc
LB
113 }
114
54a0048b
SL
115 if let Some(r) = parse_revisions(ln) {
116 props.revisions.extend(r);
1a4d82fc
JJ
117 }
118
54a0048b
SL
119 if props.run_flags.is_none() {
120 props.run_flags = parse_run_flags(ln);
223e47cc
LB
121 }
122
54a0048b
SL
123 if props.pp_exact.is_none() {
124 props.pp_exact = parse_pp_exact(ln, testfile);
92a42be0
SL
125 }
126
54a0048b
SL
127 if !props.build_aux_docs {
128 props.build_aux_docs = parse_build_aux_docs(ln);
1a4d82fc
JJ
129 }
130
54a0048b
SL
131 if !props.force_host {
132 props.force_host = parse_force_host(ln);
1a4d82fc
JJ
133 }
134
54a0048b
SL
135 if !props.check_stdout {
136 props.check_stdout = parse_check_stdout(ln);
1a4d82fc
JJ
137 }
138
54a0048b
SL
139 if !props.no_prefer_dynamic {
140 props.no_prefer_dynamic = parse_no_prefer_dynamic(ln);
1a4d82fc
JJ
141 }
142
54a0048b
SL
143 if !props.pretty_expanded {
144 props.pretty_expanded = parse_pretty_expanded(ln);
1a4d82fc
JJ
145 }
146
54a0048b
SL
147 if let Some(m) = parse_pretty_mode(ln) {
148 props.pretty_mode = m;
149 }
150
151 if !props.pretty_compare_only {
152 props.pretty_compare_only = parse_pretty_compare_only(ln);
1a4d82fc
JJ
153 }
154
e9174d1e 155 if let Some(ab) = parse_aux_build(ln) {
54a0048b 156 props.aux_builds.push(ab);
223e47cc
LB
157 }
158
54a0048b
SL
159 if let Some(ee) = parse_env(ln, "exec-env") {
160 props.exec_env.push(ee);
161 }
162
163 if let Some(ee) = parse_env(ln, "rustc-env") {
164 props.rustc_env.push(ee);
223e47cc
LB
165 }
166
e9174d1e 167 if let Some(cl) = parse_check_line(ln) {
54a0048b 168 props.check_lines.push(cl);
e9174d1e 169 }
1a4d82fc 170
e9174d1e 171 if let Some(of) = parse_forbid_output(ln) {
54a0048b 172 props.forbid_output.push(of);
1a4d82fc 173 }
1a4d82fc
JJ
174 });
175
c34b1796
AL
176 for key in vec!["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] {
177 match env::var(key) {
178 Ok(val) =>
54a0048b
SL
179 if props.exec_env.iter().find(|&&(ref x, _)| *x == key).is_none() {
180 props.exec_env.push((key.to_owned(), val))
c34b1796
AL
181 },
182 Err(..) => {}
183 }
184 }
54a0048b 185}
c34b1796 186
54a0048b
SL
187pub struct EarlyProps {
188 pub ignore: bool,
189 pub should_fail: bool,
1a4d82fc
JJ
190}
191
54a0048b
SL
192// scan the file to detect whether the test should be ignored and
193// whether it should panic; these are two things the test runner needs
194// to know early, before actually running the test
195pub fn early_props(config: &Config, testfile: &Path) -> EarlyProps {
196 let mut props = EarlyProps {
197 ignore: false,
198 should_fail: false,
199 };
200
201 iter_header(testfile, None, &mut |ln| {
202 props.ignore =
203 props.ignore ||
204 parse_name_directive(ln, "ignore-test") ||
205 parse_name_directive(ln, &ignore_target(config)) ||
206 parse_name_directive(ln, &ignore_architecture(config)) ||
207 parse_name_directive(ln, &ignore_stage(config)) ||
208 parse_name_directive(ln, &ignore_env(config)) ||
209 (config.mode == common::Pretty &&
210 parse_name_directive(ln, "ignore-pretty")) ||
211 (config.target != config.host &&
212 parse_name_directive(ln, "ignore-cross-compile")) ||
213 ignore_gdb(config, ln) ||
214 ignore_lldb(config, ln);
215
216 props.should_fail =
217 props.should_fail ||
218 parse_name_directive(ln, "should-fail");
219 });
220
221 return props;
222
1a4d82fc 223 fn ignore_target(config: &Config) -> String {
85aaf69f 224 format!("ignore-{}", util::get_os(&config.target))
1a4d82fc 225 }
c34b1796
AL
226 fn ignore_architecture(config: &Config) -> String {
227 format!("ignore-{}", util::get_arch(&config.target))
228 }
1a4d82fc
JJ
229 fn ignore_stage(config: &Config) -> String {
230 format!("ignore-{}",
85aaf69f 231 config.stage_id.split('-').next().unwrap())
1a4d82fc 232 }
d9579d0f
AL
233 fn ignore_env(config: &Config) -> String {
234 format!("ignore-{}", util::get_env(&config.target).unwrap_or("<unknown>"))
235 }
1a4d82fc
JJ
236 fn ignore_gdb(config: &Config, line: &str) -> bool {
237 if config.mode != common::DebugInfoGdb {
238 return false;
239 }
240
241 if parse_name_directive(line, "ignore-gdb") {
242 return true;
243 }
244
e9174d1e
SL
245 if let Some(ref actual_version) = config.gdb_version {
246 if line.contains("min-gdb-version") {
247 let min_version = line.trim()
248 .split(' ')
249 .last()
250 .expect("Malformed GDB version directive");
251 // Ignore if actual version is smaller the minimum required
252 // version
253 gdb_version_to_int(actual_version) <
254 gdb_version_to_int(min_version)
255 } else {
256 false
1a4d82fc 257 }
e9174d1e
SL
258 } else {
259 false
1a4d82fc
JJ
260 }
261 }
262
263 fn ignore_lldb(config: &Config, line: &str) -> bool {
264 if config.mode != common::DebugInfoLldb {
265 return false;
266 }
267
268 if parse_name_directive(line, "ignore-lldb") {
269 return true;
270 }
271
e9174d1e
SL
272 if let Some(ref actual_version) = config.lldb_version {
273 if line.contains("min-lldb-version") {
274 let min_version = line.trim()
275 .split(' ')
276 .last()
277 .expect("Malformed lldb version directive");
278 // Ignore if actual version is smaller the minimum required
279 // version
280 lldb_version_to_int(actual_version) <
281 lldb_version_to_int(min_version)
282 } else {
283 false
1a4d82fc 284 }
e9174d1e
SL
285 } else {
286 false
1a4d82fc 287 }
223e47cc
LB
288 }
289}
290
54a0048b
SL
291fn iter_header(testfile: &Path,
292 cfg: Option<&str>,
293 it: &mut FnMut(&str)) {
c34b1796 294 let rdr = BufReader::new(File::open(testfile).unwrap());
1a4d82fc 295 for ln in rdr.lines() {
223e47cc
LB
296 // Assume that any directives will be found before the first
297 // module or function. This doesn't seem to be an optimization
298 // with a warm page cache. Maybe with a cold one.
1a4d82fc 299 let ln = ln.unwrap();
54a0048b
SL
300 let ln = ln.trim();
301 if ln.starts_with("fn") || ln.starts_with("mod") {
302 return;
303 } else if ln.starts_with("//[") {
304 // A comment like `//[foo]` is specific to revision `foo`
305 if let Some(close_brace) = ln.find("]") {
306 let lncfg = &ln[3..close_brace];
307 let matches = match cfg {
308 Some(s) => s == &lncfg[..],
309 None => false,
310 };
311 if matches {
312 it(&ln[close_brace+1..]);
313 }
314 } else {
315 panic!("malformed condition directive: expected `//[foo]`, found `{}`",
316 ln)
1a4d82fc 317 }
54a0048b
SL
318 } else if ln.starts_with("//") {
319 it(&ln[2..]);
1a4d82fc 320 }
223e47cc 321 }
54a0048b 322 return;
223e47cc
LB
323}
324
1a4d82fc
JJ
325fn parse_error_pattern(line: &str) -> Option<String> {
326 parse_name_value_directive(line, "error-pattern")
327}
328
329fn parse_forbid_output(line: &str) -> Option<String> {
330 parse_name_value_directive(line, "forbid-output")
331}
332
333fn parse_aux_build(line: &str) -> Option<String> {
334 parse_name_value_directive(line, "aux-build")
335}
336
337fn parse_compile_flags(line: &str) -> Option<String> {
338 parse_name_value_directive(line, "compile-flags")
339}
340
54a0048b
SL
341fn parse_revisions(line: &str) -> Option<Vec<String>> {
342 parse_name_value_directive(line, "revisions")
343 .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
344}
345
1a4d82fc
JJ
346fn parse_run_flags(line: &str) -> Option<String> {
347 parse_name_value_directive(line, "run-flags")
348}
349
350fn parse_check_line(line: &str) -> Option<String> {
351 parse_name_value_directive(line, "check")
352}
353
354fn parse_force_host(line: &str) -> bool {
355 parse_name_directive(line, "force-host")
223e47cc
LB
356}
357
92a42be0
SL
358fn parse_build_aux_docs(line: &str) -> bool {
359 parse_name_directive(line, "build-aux-docs")
360}
361
1a4d82fc
JJ
362fn parse_check_stdout(line: &str) -> bool {
363 parse_name_directive(line, "check-stdout")
223e47cc
LB
364}
365
1a4d82fc
JJ
366fn parse_no_prefer_dynamic(line: &str) -> bool {
367 parse_name_directive(line, "no-prefer-dynamic")
223e47cc
LB
368}
369
c34b1796
AL
370fn parse_pretty_expanded(line: &str) -> bool {
371 parse_name_directive(line, "pretty-expanded")
223e47cc
LB
372}
373
1a4d82fc
JJ
374fn parse_pretty_mode(line: &str) -> Option<String> {
375 parse_name_value_directive(line, "pretty-mode")
223e47cc
LB
376}
377
1a4d82fc
JJ
378fn parse_pretty_compare_only(line: &str) -> bool {
379 parse_name_directive(line, "pretty-compare-only")
380}
381
54a0048b
SL
382fn parse_env(line: &str, name: &str) -> Option<(String, String)> {
383 parse_name_value_directive(line, name).map(|nv| {
223e47cc 384 // nv is either FOO or FOO=BAR
85aaf69f 385 let mut strs: Vec<String> = nv
c34b1796 386 .splitn(2, '=')
e9174d1e 387 .map(str::to_owned)
1a4d82fc 388 .collect();
970d7e83 389
223e47cc 390 match strs.len() {
e9174d1e 391 1 => (strs.pop().unwrap(), "".to_owned()),
85aaf69f 392 2 => {
1a4d82fc
JJ
393 let end = strs.pop().unwrap();
394 (strs.pop().unwrap(), end)
970d7e83 395 }
1a4d82fc 396 n => panic!("Expected 1 or 2 strings, not {}", n)
223e47cc 397 }
1a4d82fc 398 })
223e47cc
LB
399}
400
c34b1796 401fn parse_pp_exact(line: &str, testfile: &Path) -> Option<PathBuf> {
e9174d1e
SL
402 if let Some(s) = parse_name_value_directive(line, "pp-exact") {
403 Some(PathBuf::from(&s))
404 } else {
970d7e83 405 if parse_name_directive(line, "pp-exact") {
e9174d1e 406 testfile.file_name().map(PathBuf::from)
223e47cc
LB
407 } else {
408 None
409 }
223e47cc
LB
410 }
411}
412
970d7e83 413fn parse_name_directive(line: &str, directive: &str) -> bool {
c34b1796 414 // This 'no-' rule is a quick hack to allow pretty-expanded and no-pretty-expanded to coexist
e9174d1e 415 line.contains(directive) && !line.contains(&("no-".to_owned() + directive))
223e47cc
LB
416}
417
1a4d82fc
JJ
418pub fn parse_name_value_directive(line: &str, directive: &str)
419 -> Option<String> {
420 let keycolon = format!("{}:", directive);
e9174d1e
SL
421 if let Some(colon) = line.find(&keycolon) {
422 let value = line[(colon + keycolon.len()) .. line.len()].to_owned();
423 debug!("{}: {}", directive, value);
424 Some(value)
425 } else {
426 None
223e47cc
LB
427 }
428}
1a4d82fc 429
c34b1796 430pub fn gdb_version_to_int(version_string: &str) -> isize {
1a4d82fc
JJ
431 let error_string = format!(
432 "Encountered GDB version string with unexpected format: {}",
433 version_string);
85aaf69f 434 let error_string = error_string;
1a4d82fc
JJ
435
436 let components: Vec<&str> = version_string.trim().split('.').collect();
437
438 if components.len() != 2 {
439 panic!("{}", error_string);
440 }
441
c34b1796
AL
442 let major: isize = components[0].parse().ok().expect(&error_string);
443 let minor: isize = components[1].parse().ok().expect(&error_string);
1a4d82fc
JJ
444
445 return major * 1000 + minor;
446}
447
c34b1796 448pub fn lldb_version_to_int(version_string: &str) -> isize {
1a4d82fc
JJ
449 let error_string = format!(
450 "Encountered LLDB version string with unexpected format: {}",
451 version_string);
85aaf69f 452 let error_string = error_string;
c34b1796 453 let major: isize = version_string.parse().ok().expect(&error_string);
1a4d82fc
JJ
454 return major;
455}