1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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.
11 use self::TargetLocation
::*;
14 use common
::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}
;
15 use common
::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc}
;
17 use header
::TestProps
;
24 use std
::fs
::{self, File}
;
25 use std
::io
::BufReader
;
26 use std
::io
::prelude
::*;
27 use std
::net
::TcpStream
;
28 use std
::path
::{Path, PathBuf}
;
29 use std
::process
::{Command, Output, ExitStatus}
;
31 pub fn run(config
: Config
, testfile
: &Path
) {
32 match &*config
.target
{
34 "arm-linux-androideabi" | "aarch64-linux-android" => {
35 if !config
.adb_device_status
{
36 panic
!("android device not available");
44 // We're going to be dumping a lot of info. Start on a new line.
47 debug
!("running {:?}", testfile
.display());
48 let props
= header
::load_props(&testfile
);
49 debug
!("loaded props");
51 CompileFail
=> run_cfail_test(&config
, &props
, &testfile
),
52 ParseFail
=> run_cfail_test(&config
, &props
, &testfile
),
53 RunFail
=> run_rfail_test(&config
, &props
, &testfile
),
54 RunPass
=> run_rpass_test(&config
, &props
, &testfile
),
55 RunPassValgrind
=> run_valgrind_test(&config
, &props
, &testfile
),
56 Pretty
=> run_pretty_test(&config
, &props
, &testfile
),
57 DebugInfoGdb
=> run_debuginfo_gdb_test(&config
, &props
, &testfile
),
58 DebugInfoLldb
=> run_debuginfo_lldb_test(&config
, &props
, &testfile
),
59 Codegen
=> run_codegen_test(&config
, &props
, &testfile
),
60 Rustdoc
=> run_rustdoc_test(&config
, &props
, &testfile
),
64 fn get_output(props
: &TestProps
, proc_res
: &ProcRes
) -> String
{
65 if props
.check_stdout
{
66 format
!("{}{}", proc_res
.stdout
, proc_res
.stderr
)
68 proc_res
.stderr
.clone()
72 fn run_cfail_test(config
: &Config
, props
: &TestProps
, testfile
: &Path
) {
73 let proc_res
= compile_test(config
, props
, testfile
);
75 if proc_res
.status
.success() {
76 fatal_proc_rec(&format
!("{} test compiled successfully!", config
.mode
)[..],
80 check_correct_failure_status(&proc_res
);
82 if proc_res
.status
.success() {
83 fatal("process did not return an error status");
86 let output_to_check
= get_output(props
, &proc_res
);
87 let expected_errors
= errors
::load_errors(testfile
);
88 if !expected_errors
.is_empty() {
89 if !props
.error_patterns
.is_empty() {
90 fatal("both error pattern and expected errors specified");
92 check_expected_errors(expected_errors
, testfile
, &proc_res
);
94 check_error_patterns(props
, testfile
, &output_to_check
, &proc_res
);
96 check_no_compiler_crash(&proc_res
);
97 check_forbid_output(props
, &output_to_check
, &proc_res
);
100 fn run_rfail_test(config
: &Config
, props
: &TestProps
, testfile
: &Path
) {
101 let proc_res
= compile_test(config
, props
, testfile
);
103 if !proc_res
.status
.success() {
104 fatal_proc_rec("compilation failed!", &proc_res
);
107 let proc_res
= exec_compiled_test(config
, props
, testfile
);
109 // The value our Makefile configures valgrind to return on failure
110 const VALGRIND_ERR
: i32 = 100;
111 if proc_res
.status
.code() == Some(VALGRIND_ERR
) {
112 fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res
);
115 let output_to_check
= get_output(props
, &proc_res
);
116 check_correct_failure_status(&proc_res
);
117 check_error_patterns(props
, testfile
, &output_to_check
, &proc_res
);
120 fn check_correct_failure_status(proc_res
: &ProcRes
) {
121 // The value the rust runtime returns on failure
122 const RUST_ERR
: i32 = 101;
123 if proc_res
.status
.code() != Some(RUST_ERR
) {
125 &format
!("failure produced the wrong error: {}",
131 fn run_rpass_test(config
: &Config
, props
: &TestProps
, testfile
: &Path
) {
132 let proc_res
= compile_test(config
, props
, testfile
);
134 if !proc_res
.status
.success() {
135 fatal_proc_rec("compilation failed!", &proc_res
);
138 let proc_res
= exec_compiled_test(config
, props
, testfile
);
140 if !proc_res
.status
.success() {
141 fatal_proc_rec("test run failed!", &proc_res
);
145 fn run_valgrind_test(config
: &Config
, props
: &TestProps
, testfile
: &Path
) {
146 if config
.valgrind_path
.is_none() {
147 assert
!(!config
.force_valgrind
);
148 return run_rpass_test(config
, props
, testfile
);
151 let mut proc_res
= compile_test(config
, props
, testfile
);
153 if !proc_res
.status
.success() {
154 fatal_proc_rec("compilation failed!", &proc_res
);
157 let mut new_config
= config
.clone();
158 new_config
.runtool
= new_config
.valgrind_path
.clone();
159 proc_res
= exec_compiled_test(&new_config
, props
, testfile
);
161 if !proc_res
.status
.success() {
162 fatal_proc_rec("test run failed!", &proc_res
);
166 fn run_pretty_test(config
: &Config
, props
: &TestProps
, testfile
: &Path
) {
167 if props
.pp_exact
.is_some() {
168 logv(config
, "testing for exact pretty-printing".to_string());
170 logv(config
, "testing for converging pretty-printing".to_string());
174 match props
.pp_exact { Some(_) => 1, None => 2 }
;
176 let mut src
= String
::new();
177 File
::open(testfile
).unwrap().read_to_string(&mut src
).unwrap();
178 let mut srcs
= vec
!(src
);
181 while round
< rounds
{
182 logv(config
, format
!("pretty-printing round {}", round
));
183 let proc_res
= print_source(config
,
186 srcs
[round
].to_string(),
189 if !proc_res
.status
.success() {
190 fatal_proc_rec(&format
!("pretty-printing failed in round {}", round
),
194 let ProcRes{ stdout, .. }
= proc_res
;
199 let mut expected
= match props
.pp_exact
{
201 let filepath
= testfile
.parent().unwrap().join(file
);
202 let mut s
= String
::new();
203 File
::open(&filepath
).unwrap().read_to_string(&mut s
).unwrap();
206 None
=> { srcs[srcs.len() - 2].clone() }
208 let mut actual
= srcs
[srcs
.len() - 1].clone();
210 if props
.pp_exact
.is_some() {
211 // Now we have to care about line endings
212 let cr
= "\r".to_string();
213 actual
= actual
.replace(&cr
, "").to_string();
214 expected
= expected
.replace(&cr
, "").to_string();
217 compare_source(&expected
, &actual
);
219 // If we're only making sure that the output matches then just stop here
220 if props
.pretty_compare_only { return; }
222 // Finally, let's make sure it actually appears to remain valid code
223 let proc_res
= typecheck_source(config
, props
, testfile
, actual
);
225 if !proc_res
.status
.success() {
226 fatal_proc_rec("pretty-printed source does not typecheck", &proc_res
);
228 if !props
.pretty_expanded { return }
230 // additionally, run `--pretty expanded` and try to build it.
231 let proc_res
= print_source(config
, props
, testfile
, srcs
[round
].clone(), "expanded");
232 if !proc_res
.status
.success() {
233 fatal_proc_rec("pretty-printing (expanded) failed", &proc_res
);
236 let ProcRes{ stdout: expanded_src, .. }
= proc_res
;
237 let proc_res
= typecheck_source(config
, props
, testfile
, expanded_src
);
238 if !proc_res
.status
.success() {
239 fatal_proc_rec("pretty-printed source (expanded) does not typecheck",
245 fn print_source(config
: &Config
,
249 pretty_type
: &str) -> ProcRes
{
250 let aux_dir
= aux_output_dir_name(config
, testfile
);
251 compose_and_run(config
,
256 pretty_type
.to_string()),
257 props
.exec_env
.clone(),
258 &config
.compile_lib_path
,
259 Some(aux_dir
.to_str().unwrap()),
263 fn make_pp_args(config
: &Config
,
266 pretty_type
: String
) -> ProcArgs
{
267 let aux_dir
= aux_output_dir_name(config
, testfile
);
268 // FIXME (#9639): This needs to handle non-utf8 paths
269 let mut args
= vec
!("-".to_string(),
270 "-Zunstable-options".to_string(),
271 "--pretty".to_string(),
273 format
!("--target={}", config
.target
),
275 aux_dir
.to_str().unwrap().to_string());
276 args
.extend(split_maybe_args(&config
.target_rustcflags
));
277 args
.extend(split_maybe_args(&props
.compile_flags
));
279 prog
: config
.rustc_path
.to_str().unwrap().to_string(),
284 fn compare_source(expected
: &str, actual
: &str) {
285 if expected
!= actual
{
286 error("pretty-printed source does not match expected source");
289 ------------------------------------------\n\
291 ------------------------------------------\n\
293 ------------------------------------------\n\
295 ------------------------------------------\n\
302 fn typecheck_source(config
: &Config
, props
: &TestProps
,
303 testfile
: &Path
, src
: String
) -> ProcRes
{
304 let args
= make_typecheck_args(config
, props
, testfile
);
305 compose_and_run_compiler(config
, props
, testfile
, args
, Some(src
))
308 fn make_typecheck_args(config
: &Config
, props
: &TestProps
, testfile
: &Path
) -> ProcArgs
{
309 let aux_dir
= aux_output_dir_name(config
, testfile
);
310 let target
= if props
.force_host
{
315 // FIXME (#9639): This needs to handle non-utf8 paths
316 let mut args
= vec
!("-".to_string(),
317 "-Zno-trans".to_string(),
318 "--crate-type=lib".to_string(),
319 format
!("--target={}", target
),
321 config
.build_base
.to_str().unwrap().to_string(),
323 aux_dir
.to_str().unwrap().to_string());
324 args
.extend(split_maybe_args(&config
.target_rustcflags
));
325 args
.extend(split_maybe_args(&props
.compile_flags
));
326 // FIXME (#9639): This needs to handle non-utf8 paths
328 prog
: config
.rustc_path
.to_str().unwrap().to_string(),
334 fn run_debuginfo_gdb_test(config
: &Config
, props
: &TestProps
, testfile
: &Path
) {
335 let mut config
= Config
{
336 target_rustcflags
: cleanup_debug_info_options(&config
.target_rustcflags
),
337 host_rustcflags
: cleanup_debug_info_options(&config
.host_rustcflags
),
341 let config
= &mut config
;
342 let DebuggerCommands
{
346 } = parse_debugger_commands(testfile
, "gdb");
347 let mut cmds
= commands
.connect("\n");
349 // compile test file (it should have 'compile-flags:-g' in the header)
350 let compiler_run_result
= compile_test(config
, props
, testfile
);
351 if !compiler_run_result
.status
.success() {
352 fatal_proc_rec("compilation failed!", &compiler_run_result
);
355 let exe_file
= make_exe_name(config
, testfile
);
357 let debugger_run_result
;
358 match &*config
.target
{
359 "arm-linux-androideabi" | "aarch64-linux-android" => {
361 cmds
= cmds
.replace("run", "continue");
363 // write debugger script
364 let mut script_str
= String
::with_capacity(2048);
365 script_str
.push_str(&format
!("set charset {}\n", charset()));
366 script_str
.push_str(&format
!("file {}\n", exe_file
.to_str().unwrap()));
367 script_str
.push_str("target remote :5039\n");
368 script_str
.push_str(&format
!("set solib-search-path \
369 ./{}/stage2/lib/rustlib/{}/lib/\n",
370 config
.host
, config
.target
));
371 for line
in &breakpoint_lines
{
372 script_str
.push_str(&format
!("break {:?}:{}\n",
373 testfile
.file_name().unwrap()
377 script_str
.push_str(&cmds
);
378 script_str
.push_str("\nquit\n");
380 debug
!("script_str = {}", script_str
);
381 dump_output_file(config
,
392 exe_file
.to_str().unwrap().to_string(),
393 config
.adb_test_dir
.clone()
395 vec
!(("".to_string(), "".to_string())),
396 Some("".to_string()))
397 .expect(&format
!("failed to exec `{:?}`", config
.adb_path
));
403 "forward".to_string(),
404 "tcp:5039".to_string(),
405 "tcp:5039".to_string()
407 vec
!(("".to_string(), "".to_string())),
408 Some("".to_string()))
409 .expect(&format
!("failed to exec `{:?}`", config
.adb_path
));
411 let adb_arg
= format
!("export LD_LIBRARY_PATH={}; \
412 gdbserver{} :5039 {}/{}",
413 config
.adb_test_dir
.clone(),
414 if config
.target
.contains("aarch64")
416 config
.adb_test_dir
.clone(),
417 exe_file
.file_name().unwrap().to_str()
420 let mut process
= procsrv
::run_background("",
428 vec
!(("".to_string(),
430 Some("".to_string()))
431 .expect(&format
!("failed to exec `{:?}`", config
.adb_path
));
433 //waiting 1 second for gdbserver start
434 ::std
::thread
::sleep_ms(1000);
435 if TcpStream
::connect("127.0.0.1:5039").is_ok() {
440 let tool_path
= match config
.android_cross_path
.to_str() {
441 Some(x
) => x
.to_string(),
442 None
=> fatal("cannot find android cross path")
445 let debugger_script
= make_out_name(config
, testfile
, "debugger.script");
446 // FIXME (#9639): This needs to handle non-utf8 paths
448 vec
!("-quiet".to_string(),
449 "-batch".to_string(),
451 format
!("-command={}", debugger_script
.to_str().unwrap()));
453 let mut gdb_path
= tool_path
;
454 gdb_path
.push_str(&format
!("/bin/{}-gdb", config
.target
));
455 let procsrv
::Result
{
463 vec
!(("".to_string(), "".to_string())),
465 .expect(&format
!("failed to exec `{:?}`", gdb_path
));
467 let cmdline
= make_cmdline("",
468 &format
!("{}-gdb", config
.target
),
470 logv(config
, format
!("executing {}", cmdline
));
474 debugger_run_result
= ProcRes
{
475 status
: Status
::Normal(status
),
480 if process
.kill().is_err() {
481 println
!("Adb process is already finished.");
486 let rust_src_root
= find_rust_src_root(config
)
487 .expect("Could not find Rust source root");
488 let rust_pp_module_rel_path
= Path
::new("./src/etc");
489 let rust_pp_module_abs_path
= rust_src_root
.join(rust_pp_module_rel_path
)
493 // write debugger script
494 let mut script_str
= String
::with_capacity(2048);
495 script_str
.push_str(&format
!("set charset {}\n", charset()));
496 script_str
.push_str("show version\n");
498 match config
.gdb_version
{
499 Some(ref version
) => {
500 println
!("NOTE: compiletest thinks it is using GDB version {}",
503 if header
::gdb_version_to_int(version
) >
504 header
::gdb_version_to_int("7.4") {
505 // Add the directory containing the pretty printers to
506 // GDB's script auto loading safe path
508 &format
!("add-auto-load-safe-path {}\n",
509 rust_pp_module_abs_path
.replace(r
"\", r
"\\"))
514 println
!("NOTE: compiletest does not know which version of \
519 // The following line actually doesn't have to do anything with
520 // pretty printing, it just tells GDB to print values on one line:
521 script_str
.push_str("set print pretty off\n");
523 // Add the pretty printer directory to GDB's source-file search path
524 script_str
.push_str(&format
!("directory {}\n",
525 rust_pp_module_abs_path
));
527 // Load the target executable
528 script_str
.push_str(&format
!("file {}\n",
529 exe_file
.to_str().unwrap()
530 .replace(r
"\", r
"\\")));
532 // Add line breakpoints
533 for line
in &breakpoint_lines
{
534 script_str
.push_str(&format
!("break '{}':{}\n",
535 testfile
.file_name().unwrap()
540 script_str
.push_str(&cmds
);
541 script_str
.push_str("\nquit\n");
543 debug
!("script_str = {}", script_str
);
544 dump_output_file(config
,
549 // run debugger script with gdb
550 fn debugger() -> &'
static str {
551 if cfg
!(windows
) {"gdb.exe"}
else {"gdb"}
554 let debugger_script
= make_out_name(config
, testfile
, "debugger.script");
556 // FIXME (#9639): This needs to handle non-utf8 paths
558 vec
!("-quiet".to_string(),
559 "-batch".to_string(),
561 format
!("-command={}", debugger_script
.to_str().unwrap()));
563 let proc_args
= ProcArgs
{
564 prog
: debugger().to_string(),
568 let environment
= vec
![("PYTHONPATH".to_string(), rust_pp_module_abs_path
)];
570 debugger_run_result
= compose_and_run(config
,
574 &config
.run_lib_path
,
580 if !debugger_run_result
.status
.success() {
581 fatal("gdb failed to execute");
584 check_debugger_output(&debugger_run_result
, &check_lines
);
587 fn find_rust_src_root(config
: &Config
) -> Option
<PathBuf
> {
588 let mut path
= config
.src_base
.clone();
589 let path_postfix
= Path
::new("src/etc/lldb_batchmode.py");
592 if path
.join(&path_postfix
).is_file() {
600 fn run_debuginfo_lldb_test(config
: &Config
, props
: &TestProps
, testfile
: &Path
) {
601 if config
.lldb_python_dir
.is_none() {
602 fatal("Can't run LLDB test because LLDB's python path is not set.");
605 let mut config
= Config
{
606 target_rustcflags
: cleanup_debug_info_options(&config
.target_rustcflags
),
607 host_rustcflags
: cleanup_debug_info_options(&config
.host_rustcflags
),
611 let config
= &mut config
;
613 // compile test file (it should have 'compile-flags:-g' in the header)
614 let compile_result
= compile_test(config
, props
, testfile
);
615 if !compile_result
.status
.success() {
616 fatal_proc_rec("compilation failed!", &compile_result
);
619 let exe_file
= make_exe_name(config
, testfile
);
621 match config
.lldb_version
{
622 Some(ref version
) => {
623 println
!("NOTE: compiletest thinks it is using LLDB version {}",
627 println
!("NOTE: compiletest does not know which version of \
632 // Parse debugger commands etc from test files
633 let DebuggerCommands
{
638 } = parse_debugger_commands(testfile
, "lldb");
640 // Write debugger script:
641 // We don't want to hang when calling `quit` while the process is still running
642 let mut script_str
= String
::from("settings set auto-confirm true\n");
644 // Make LLDB emit its version, so we have it documented in the test output
645 script_str
.push_str("version\n");
647 // Switch LLDB into "Rust mode"
648 let rust_src_root
= find_rust_src_root(config
)
649 .expect("Could not find Rust source root");
650 let rust_pp_module_rel_path
= Path
::new("./src/etc/lldb_rust_formatters.py");
651 let rust_pp_module_abs_path
= rust_src_root
.join(rust_pp_module_rel_path
)
656 script_str
.push_str(&format
!("command script import {}\n",
657 &rust_pp_module_abs_path
[..])[..]);
658 script_str
.push_str("type summary add --no-value ");
659 script_str
.push_str("--python-function lldb_rust_formatters.print_val ");
660 script_str
.push_str("-x \".*\" --category Rust\n");
661 script_str
.push_str("type category enable Rust\n");
663 // Set breakpoints on every line that contains the string "#break"
664 for line
in &breakpoint_lines
{
665 script_str
.push_str(&format
!("breakpoint set --line {}\n", line
));
668 // Append the other commands
669 for line
in &commands
{
670 script_str
.push_str(line
);
671 script_str
.push_str("\n");
674 // Finally, quit the debugger
675 script_str
.push_str("\nquit\n");
677 // Write the script into a file
678 debug
!("script_str = {}", script_str
);
679 dump_output_file(config
,
683 let debugger_script
= make_out_name(config
, testfile
, "debugger.script");
685 // Let LLDB execute the script via lldb_batchmode.py
686 let debugger_run_result
= run_lldb(config
,
691 if !debugger_run_result
.status
.success() {
692 fatal_proc_rec("Error while running LLDB", &debugger_run_result
);
695 check_debugger_output(&debugger_run_result
, &check_lines
);
697 fn run_lldb(config
: &Config
,
698 test_executable
: &Path
,
699 debugger_script
: &Path
,
700 rust_src_root
: &Path
)
702 // Prepare the lldb_batchmode which executes the debugger script
703 let lldb_script_path
= rust_src_root
.join("src/etc/lldb_batchmode.py");
706 Command
::new(&config
.python
)
707 .arg(&lldb_script_path
)
708 .arg(test_executable
)
709 .arg(debugger_script
)
711 config
.lldb_python_dir
.as_ref().unwrap()))
715 fn cmd2procres(config
: &Config
, test_executable
: &Path
, cmd
: &mut Command
)
717 let (status
, out
, err
) = match cmd
.output() {
718 Ok(Output { status, stdout, stderr }
) => {
720 String
::from_utf8(stdout
).unwrap(),
721 String
::from_utf8(stderr
).unwrap())
724 fatal(&format
!("Failed to setup Python process for \
725 LLDB script: {}", e
))
729 dump_output(config
, test_executable
, &out
, &err
);
731 status
: Status
::Normal(status
),
734 cmdline
: format
!("{:?}", cmd
)
738 struct DebuggerCommands
{
739 commands
: Vec
<String
>,
740 check_lines
: Vec
<String
>,
741 breakpoint_lines
: Vec
<usize>,
744 fn parse_debugger_commands(file_path
: &Path
, debugger_prefix
: &str)
745 -> DebuggerCommands
{
746 let command_directive
= format
!("{}-command", debugger_prefix
);
747 let check_directive
= format
!("{}-check", debugger_prefix
);
749 let mut breakpoint_lines
= vec
!();
750 let mut commands
= vec
!();
751 let mut check_lines
= vec
!();
753 let reader
= BufReader
::new(File
::open(file_path
).unwrap());
754 for line
in reader
.lines() {
757 if line
.contains("#break") {
758 breakpoint_lines
.push(counter
);
761 header
::parse_name_value_directive(
763 &command_directive
).map(|cmd
| {
767 header
::parse_name_value_directive(
769 &check_directive
).map(|cmd
| {
770 check_lines
.push(cmd
)
774 fatal(&format
!("Error while parsing debugger commands: {}", e
))
782 check_lines
: check_lines
,
783 breakpoint_lines
: breakpoint_lines
,
787 fn cleanup_debug_info_options(options
: &Option
<String
>) -> Option
<String
> {
788 if options
.is_none() {
792 // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
793 let options_to_remove
= [
796 "--debuginfo".to_string()
799 split_maybe_args(options
).into_iter()
800 .filter(|x
| !options_to_remove
.contains(x
))
801 .collect
::<Vec
<String
>>()
806 fn check_debugger_output(debugger_run_result
: &ProcRes
, check_lines
: &[String
]) {
807 let num_check_lines
= check_lines
.len();
808 if num_check_lines
> 0 {
809 // Allow check lines to leave parts unspecified (e.g., uninitialized
810 // bits in the wrong case of an enum) with the notation "[...]".
811 let check_fragments
: Vec
<Vec
<String
>> =
812 check_lines
.iter().map(|s
| {
816 .map(|x
| x
.to_string())
819 // check if each line in props.check_lines appears in the
822 for line
in debugger_run_result
.stdout
.lines() {
823 let mut rest
= line
.trim();
824 let mut first
= true;
825 let mut failed
= false;
826 for frag
in &check_fragments
[i
] {
827 let found
= if first
{
828 if rest
.starts_with(frag
) {
842 rest
= &rest
[(i
+ frag
.len())..];
847 if !failed
&& rest
.is_empty() {
850 if i
== num_check_lines
{
855 if i
!= num_check_lines
{
856 fatal_proc_rec(&format
!("line not found in debugger output: {}",
857 check_lines
.get(i
).unwrap()),
858 debugger_run_result
);
863 fn check_error_patterns(props
: &TestProps
,
865 output_to_check
: &str,
866 proc_res
: &ProcRes
) {
867 if props
.error_patterns
.is_empty() {
868 fatal(&format
!("no error pattern specified in {:?}", testfile
.display()));
870 let mut next_err_idx
= 0;
871 let mut next_err_pat
= &props
.error_patterns
[next_err_idx
];
872 let mut done
= false;
873 for line
in output_to_check
.lines() {
874 if line
.contains(next_err_pat
) {
875 debug
!("found error pattern {}", next_err_pat
);
877 if next_err_idx
== props
.error_patterns
.len() {
878 debug
!("found all error patterns");
882 next_err_pat
= &props
.error_patterns
[next_err_idx
];
887 let missing_patterns
= &props
.error_patterns
[next_err_idx
..];
888 if missing_patterns
.len() == 1 {
889 fatal_proc_rec(&format
!("error pattern '{}' not found!", missing_patterns
[0]),
892 for pattern
in missing_patterns
{
893 error(&format
!("error pattern '{}' not found!", *pattern
));
895 fatal_proc_rec("multiple error patterns not found", proc_res
);
899 fn check_no_compiler_crash(proc_res
: &ProcRes
) {
900 for line
in proc_res
.stderr
.lines() {
901 if line
.starts_with("error: internal compiler error:") {
902 fatal_proc_rec("compiler encountered internal error",
908 fn check_forbid_output(props
: &TestProps
,
909 output_to_check
: &str,
910 proc_res
: &ProcRes
) {
911 for pat
in &props
.forbid_output
{
912 if output_to_check
.contains(pat
) {
913 fatal_proc_rec("forbidden pattern found in compiler output", proc_res
);
918 fn check_expected_errors(expected_errors
: Vec
<errors
::ExpectedError
>,
920 proc_res
: &ProcRes
) {
922 // true if we found the error in question
923 let mut found_flags
= vec
![false; expected_errors
.len()];
925 if proc_res
.status
.success() {
926 fatal("process did not return an error status");
929 let prefixes
= expected_errors
.iter().map(|ee
| {
930 format
!("{}:{}:", testfile
.display(), ee
.line
)
931 }).collect
::<Vec
<String
>>();
933 fn prefix_matches(line
: &str, prefix
: &str) -> bool
{
934 use std
::ascii
::AsciiExt
;
935 // On windows just translate all '\' path separators to '/'
936 let line
= line
.replace(r
"\", "/");
938 line
.to_ascii_lowercase().starts_with(&prefix
.to_ascii_lowercase())
940 line
.starts_with(prefix
)
944 // A multi-line error will have followup lines which start with a space
946 fn continuation( line
: &str) -> bool
{
947 line
.starts_with(" ") || line
.starts_with("(")
950 // Scan and extract our error/warning messages,
952 // filename:line1:col1: line2:col2: *error:* msg
953 // filename:line1:col1: line2:col2: *warning:* msg
954 // where line1:col1: is the starting point, line2:col2:
955 // is the ending point, and * represents ANSI color codes.
956 for line
in proc_res
.stderr
.lines() {
957 let mut was_expected
= false;
959 for (i
, ee
) in expected_errors
.iter().enumerate() {
961 debug
!("prefix={} ee.kind={} ee.msg={} line={}",
966 // Suggestions have no line number in their output, so take on the line number of
967 // the previous expected error
968 if ee
.kind
== "suggestion" {
969 assert
!(expected_errors
[prev
].kind
== "help",
970 "SUGGESTIONs must be preceded by a HELP");
971 if line
.contains(&ee
.msg
) {
972 found_flags
[i
] = true;
977 if (prefix_matches(line
, &prefixes
[i
]) || continuation(line
)) &&
978 line
.contains(&ee
.kind
) &&
979 line
.contains(&ee
.msg
) {
980 found_flags
[i
] = true;
988 // ignore this msg which gets printed at the end
989 if line
.contains("aborting due to") {
993 if !was_expected
&& is_compiler_error_or_warning(line
) {
994 fatal_proc_rec(&format
!("unexpected compiler error or warning: '{}'",
1000 for (i
, &flag
) in found_flags
.iter().enumerate() {
1002 let ee
= &expected_errors
[i
];
1003 fatal_proc_rec(&format
!("expected {} on line {} not found: {}",
1004 ee
.kind
, ee
.line
, ee
.msg
),
1010 fn is_compiler_error_or_warning(line
: &str) -> bool
{
1013 scan_until_char(line
, '
:'
, &mut i
) &&
1014 scan_char(line
, '
:'
, &mut i
) &&
1015 scan_integer(line
, &mut i
) &&
1016 scan_char(line
, '
:'
, &mut i
) &&
1017 scan_integer(line
, &mut i
) &&
1018 scan_char(line
, '
:'
, &mut i
) &&
1019 scan_char(line
, ' '
, &mut i
) &&
1020 scan_integer(line
, &mut i
) &&
1021 scan_char(line
, '
:'
, &mut i
) &&
1022 scan_integer(line
, &mut i
) &&
1023 scan_char(line
, ' '
, &mut i
) &&
1024 (scan_string(line
, "error", &mut i
) ||
1025 scan_string(line
, "warning", &mut i
));
1028 fn scan_until_char(haystack
: &str, needle
: char, idx
: &mut usize) -> bool
{
1029 if *idx
>= haystack
.len() {
1032 let opt
= haystack
[(*idx
)..].find(needle
);
1036 *idx
= opt
.unwrap();
1040 fn scan_char(haystack
: &str, needle
: char, idx
: &mut usize) -> bool
{
1041 if *idx
>= haystack
.len() {
1044 let ch
= haystack
.char_at(*idx
);
1048 *idx
+= ch
.len_utf8();
1052 fn scan_integer(haystack
: &str, idx
: &mut usize) -> bool
{
1054 while i
< haystack
.len() {
1055 let ch
= haystack
.char_at(i
);
1056 if ch
< '
0'
|| '
9'
< ch
{
1068 fn scan_string(haystack
: &str, needle
: &str, idx
: &mut usize) -> bool
{
1069 let mut haystack_i
= *idx
;
1070 let mut needle_i
= 0;
1071 while needle_i
< needle
.len() {
1072 if haystack_i
>= haystack
.len() {
1075 let ch
= haystack
.char_at(haystack_i
);
1076 haystack_i
+= ch
.len_utf8();
1077 if !scan_char(needle
, ch
, &mut needle_i
) {
1103 fn code(&self) -> Option
<i32> {
1105 Status
::Parsed(i
) => Some(i
),
1106 Status
::Normal(ref e
) => e
.code(),
1110 fn success(&self) -> bool
{
1112 Status
::Parsed(i
) => i
== 0,
1113 Status
::Normal(ref e
) => e
.success(),
1118 impl fmt
::Display
for Status
{
1119 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1121 Status
::Parsed(i
) => write
!(f
, "exit code: {}", i
),
1122 Status
::Normal(ref e
) => e
.fmt(f
),
1127 fn compile_test(config
: &Config
, props
: &TestProps
,
1128 testfile
: &Path
) -> ProcRes
{
1129 compile_test_(config
, props
, testfile
, &[])
1132 fn compile_test_(config
: &Config
, props
: &TestProps
,
1133 testfile
: &Path
, extra_args
: &[String
]) -> ProcRes
{
1134 let aux_dir
= aux_output_dir_name(config
, testfile
);
1135 // FIXME (#9639): This needs to handle non-utf8 paths
1136 let mut link_args
= vec
!("-L".to_string(),
1137 aux_dir
.to_str().unwrap().to_string());
1138 link_args
.extend(extra_args
.iter().cloned());
1139 let args
= make_compile_args(config
,
1142 |a
, b
| TargetLocation
::ThisFile(make_exe_name(a
, b
)), testfile
);
1143 compose_and_run_compiler(config
, props
, testfile
, args
, None
)
1146 fn document(config
: &Config
, props
: &TestProps
,
1147 testfile
: &Path
, extra_args
: &[String
]) -> (ProcRes
, PathBuf
) {
1148 let aux_dir
= aux_output_dir_name(config
, testfile
);
1149 let out_dir
= output_base_name(config
, testfile
);
1150 let _
= fs
::remove_dir_all(&out_dir
);
1151 ensure_dir(&out_dir
);
1152 let mut args
= vec
!["-L".to_string(),
1153 aux_dir
.to_str().unwrap().to_string(),
1155 out_dir
.to_str().unwrap().to_string(),
1156 testfile
.to_str().unwrap().to_string()];
1157 args
.extend(extra_args
.iter().cloned());
1158 args
.extend(split_maybe_args(&props
.compile_flags
));
1159 let args
= ProcArgs
{
1160 prog
: config
.rustdoc_path
.to_str().unwrap().to_string(),
1163 (compose_and_run_compiler(config
, props
, testfile
, args
, None
), out_dir
)
1166 fn exec_compiled_test(config
: &Config
, props
: &TestProps
,
1167 testfile
: &Path
) -> ProcRes
{
1169 let env
= props
.exec_env
.clone();
1171 match &*config
.target
{
1173 "arm-linux-androideabi" | "aarch64-linux-android" => {
1174 _arm_exec_compiled_test(config
, props
, testfile
, env
)
1178 let aux_dir
= aux_output_dir_name(config
, testfile
);
1179 compose_and_run(config
,
1181 make_run_args(config
, props
, testfile
),
1183 &config
.run_lib_path
,
1184 Some(aux_dir
.to_str().unwrap()),
1190 fn compose_and_run_compiler(config
: &Config
, props
: &TestProps
,
1191 testfile
: &Path
, args
: ProcArgs
,
1192 input
: Option
<String
>) -> ProcRes
{
1193 if !props
.aux_builds
.is_empty() {
1194 ensure_dir(&aux_output_dir_name(config
, testfile
));
1197 let aux_dir
= aux_output_dir_name(config
, testfile
);
1198 // FIXME (#9639): This needs to handle non-utf8 paths
1199 let extra_link_args
= vec
!["-L".to_string(),
1200 aux_dir
.to_str().unwrap().to_string()];
1202 for rel_ab
in &props
.aux_builds
{
1203 let abs_ab
= config
.aux_base
.join(rel_ab
);
1204 let aux_props
= header
::load_props(&abs_ab
);
1205 let mut crate_type
= if aux_props
.no_prefer_dynamic
{
1208 // We primarily compile all auxiliary libraries as dynamic libraries
1209 // to avoid code size bloat and large binaries as much as possible
1210 // for the test suite (otherwise including libstd statically in all
1211 // executables takes up quite a bit of space).
1213 // For targets like MUSL, however, there is no support for dynamic
1214 // libraries so we just go back to building a normal library. Note,
1215 // however, that if the library is built with `force_host` then it's
1216 // ok to be a dylib as the host should always support dylibs.
1217 if config
.target
.contains("musl") && !aux_props
.force_host
{
1218 vec
!("--crate-type=lib".to_string())
1220 vec
!("--crate-type=dylib".to_string())
1223 crate_type
.extend(extra_link_args
.clone());
1225 make_compile_args(config
,
1229 let f
= make_lib_name(a
, b
, testfile
);
1230 let parent
= f
.parent().unwrap();
1231 TargetLocation
::ThisDirectory(parent
.to_path_buf())
1234 let auxres
= compose_and_run(config
,
1238 &config
.compile_lib_path
,
1239 Some(aux_dir
.to_str().unwrap()),
1241 if !auxres
.status
.success() {
1243 &format
!("auxiliary build of {:?} failed to compile: ",
1248 match &*config
.target
{
1249 "arm-linux-androideabi" | "aarch64-linux-android" => {
1250 _arm_push_aux_shared_library(config
, testfile
);
1256 compose_and_run(config
,
1260 &config
.compile_lib_path
,
1261 Some(aux_dir
.to_str().unwrap()),
1265 fn ensure_dir(path
: &Path
) {
1266 if path
.is_dir() { return; }
1267 fs
::create_dir(path
).unwrap();
1270 fn compose_and_run(config
: &Config
, testfile
: &Path
,
1271 ProcArgs{ args, prog }
: ProcArgs
,
1272 procenv
: Vec
<(String
, String
)> ,
1274 aux_path
: Option
<&str>,
1275 input
: Option
<String
>) -> ProcRes
{
1276 return program_output(config
, testfile
, lib_path
,
1277 prog
, aux_path
, args
, procenv
, input
);
1280 enum TargetLocation
{
1282 ThisDirectory(PathBuf
),
1285 fn make_compile_args
<F
>(config
: &Config
,
1287 extras
: Vec
<String
> ,
1291 F
: FnOnce(&Config
, &Path
) -> TargetLocation
,
1293 let xform_file
= xform(config
, testfile
);
1294 let target
= if props
.force_host
{
1299 // FIXME (#9639): This needs to handle non-utf8 paths
1300 let mut args
= vec
!(testfile
.to_str().unwrap().to_string(),
1302 config
.build_base
.to_str().unwrap().to_string(),
1303 format
!("--target={}", target
));
1304 args
.push_all(&extras
);
1305 if !props
.no_prefer_dynamic
{
1306 args
.push("-C".to_string());
1307 args
.push("prefer-dynamic".to_string());
1309 let path
= match xform_file
{
1310 TargetLocation
::ThisFile(path
) => {
1311 args
.push("-o".to_string());
1314 TargetLocation
::ThisDirectory(path
) => {
1315 args
.push("--out-dir".to_string());
1319 args
.push(path
.to_str().unwrap().to_string());
1320 if props
.force_host
{
1321 args
.extend(split_maybe_args(&config
.host_rustcflags
));
1323 args
.extend(split_maybe_args(&config
.target_rustcflags
));
1325 args
.extend(split_maybe_args(&props
.compile_flags
));
1327 prog
: config
.rustc_path
.to_str().unwrap().to_string(),
1332 fn make_lib_name(config
: &Config
, auxfile
: &Path
, testfile
: &Path
) -> PathBuf
{
1333 // what we return here is not particularly important, as it
1334 // happens; rustc ignores everything except for the directory.
1335 let auxname
= output_testname(auxfile
);
1336 aux_output_dir_name(config
, testfile
).join(&auxname
)
1339 fn make_exe_name(config
: &Config
, testfile
: &Path
) -> PathBuf
{
1340 let mut f
= output_base_name(config
, testfile
);
1341 if !env
::consts
::EXE_SUFFIX
.is_empty() {
1342 let mut fname
= f
.file_name().unwrap().to_os_string();
1343 fname
.push(env
::consts
::EXE_SUFFIX
);
1344 f
.set_file_name(&fname
);
1349 fn make_run_args(config
: &Config
, props
: &TestProps
, testfile
: &Path
)
1351 // If we've got another tool to run under (valgrind),
1352 // then split apart its command
1353 let mut args
= split_maybe_args(&config
.runtool
);
1354 let exe_file
= make_exe_name(config
, testfile
);
1356 // FIXME (#9639): This needs to handle non-utf8 paths
1357 args
.push(exe_file
.to_str().unwrap().to_string());
1359 // Add the arguments in the run_flags directive
1360 args
.extend(split_maybe_args(&props
.run_flags
));
1362 let prog
= args
.remove(0);
1369 fn split_maybe_args(argstr
: &Option
<String
>) -> Vec
<String
> {
1375 if s
.chars().all(|c
| c
.is_whitespace()) {
1386 fn program_output(config
: &Config
, testfile
: &Path
, lib_path
: &str, prog
: String
,
1387 aux_path
: Option
<&str>, args
: Vec
<String
>,
1388 env
: Vec
<(String
, String
)>,
1389 input
: Option
<String
>) -> ProcRes
{
1392 let cmdline
= make_cmdline(lib_path
,
1395 logv(config
, format
!("executing {}", cmdline
));
1398 let procsrv
::Result
{
1402 } = procsrv
::run(lib_path
,
1407 input
).expect(&format
!("failed to exec `{}`", prog
));
1408 dump_output(config
, testfile
, &out
, &err
);
1410 status
: Status
::Normal(status
),
1417 fn make_cmdline(libpath
: &str, prog
: &str, args
: &[String
]) -> String
{
1420 // Linux and mac don't require adjusting the library search path
1422 format
!("{} {}", prog
, args
.connect(" "))
1424 // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
1425 // for diagnostic purposes
1426 fn lib_path_cmd_prefix(path
: &str) -> String
{
1427 format
!("{}=\"{}\"", util
::lib_path_env_var(), util
::make_new_path(path
))
1430 format
!("{} {} {}", lib_path_cmd_prefix(libpath
), prog
, args
.connect(" "))
1434 fn dump_output(config
: &Config
, testfile
: &Path
, out
: &str, err
: &str) {
1435 dump_output_file(config
, testfile
, out
, "out");
1436 dump_output_file(config
, testfile
, err
, "err");
1437 maybe_dump_to_stdout(config
, out
, err
);
1440 fn dump_output_file(config
: &Config
, testfile
: &Path
,
1441 out
: &str, extension
: &str) {
1442 let outfile
= make_out_name(config
, testfile
, extension
);
1443 File
::create(&outfile
).unwrap().write_all(out
.as_bytes()).unwrap();
1446 fn make_out_name(config
: &Config
, testfile
: &Path
, extension
: &str) -> PathBuf
{
1447 output_base_name(config
, testfile
).with_extension(extension
)
1450 fn aux_output_dir_name(config
: &Config
, testfile
: &Path
) -> PathBuf
{
1451 let f
= output_base_name(config
, testfile
);
1452 let mut fname
= f
.file_name().unwrap().to_os_string();
1453 fname
.push(&format
!(".{}.libaux", config
.mode
));
1454 f
.with_file_name(&fname
)
1457 fn output_testname(testfile
: &Path
) -> PathBuf
{
1458 PathBuf
::from(testfile
.file_stem().unwrap())
1461 fn output_base_name(config
: &Config
, testfile
: &Path
) -> PathBuf
{
1463 .join(&output_testname(testfile
))
1464 .with_extension(&config
.stage_id
)
1467 fn maybe_dump_to_stdout(config
: &Config
, out
: &str, err
: &str) {
1469 println
!("------{}------------------------------", "stdout");
1470 println
!("{}", out
);
1471 println
!("------{}------------------------------", "stderr");
1472 println
!("{}", err
);
1473 println
!("------------------------------------------");
1477 fn error(err
: &str) { println!("\nerror: {}
", err); }
1479 fn fatal(err: &str) -> ! { error(err); panic!(); }
1481 fn fatal_proc_rec(err: &str, proc_res: &ProcRes) -> ! {
1487 ------------------------------------------\n\
1489 ------------------------------------------\n\
1491 ------------------------------------------\n\
1493 ------------------------------------------\n\
1495 err, proc_res.status, proc_res.cmdline, proc_res.stdout,
1500 fn _arm_exec_compiled_test(config: &Config,
1503 env: Vec<(String, String)>)
1505 let args = make_run_args(config, props, testfile);
1506 let cmdline = make_cmdline("",
1510 // get bare program string
1511 let mut tvec: Vec<String> = args.prog
1513 .map(|ts| ts.to_string())
1515 let prog_short = tvec.pop().unwrap();
1518 let copy_result = procsrv::run("",
1524 config.adb_test_dir.clone()
1526 vec!(("".to_string(), "".to_string())),
1527 Some("".to_string()))
1528 .expect(&format!("failed to exec `{}`
", config.adb_path));
1531 println!("push ({}
) {} {} {}
",
1538 logv(config, format!("executing ({}
) {}
", config.target, cmdline));
1540 let mut runargs = Vec::new();
1542 // run test via adb_run_wrapper
1543 runargs.push("shell
".to_string());
1544 for (key, val) in env {
1545 runargs.push(format!("{}
={}
", key, val));
1547 runargs.push(format!("{}
/../adb_run_wrapper
.sh
", config.adb_test_dir));
1548 runargs.push(format!("{}
", config.adb_test_dir));
1549 runargs.push(format!("{}
", prog_short));
1551 for tv in &args.args {
1552 runargs.push(tv.to_string());
1558 vec!(("".to_string(), "".to_string())), Some("".to_string()))
1559 .expect(&format!("failed to exec `{}`
", config.adb_path));
1561 // get exitcode of result
1562 runargs = Vec::new();
1563 runargs.push("shell
".to_string());
1564 runargs.push("cat
".to_string());
1565 runargs.push(format!("{}
/{}
.exitcode
", config.adb_test_dir, prog_short));
1567 let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
1572 vec!(("".to_string(), "".to_string())),
1573 Some("".to_string()))
1574 .expect(&format!("failed to exec `{}`
", config.adb_path));
1576 let mut exitcode: i32 = 0;
1577 for c in exitcode_out.chars() {
1578 if !c.is_numeric() { break; }
1579 exitcode = exitcode * 10 + match c {
1580 '0' ... '9' => c as i32 - ('0' as i32),
1585 // get stdout of result
1586 runargs = Vec::new();
1587 runargs.push("shell
".to_string());
1588 runargs.push("cat
".to_string());
1589 runargs.push(format!("{}
/{}
.stdout
", config.adb_test_dir, prog_short));
1591 let procsrv::Result{ out: stdout_out, err: _, status: _ } =
1596 vec!(("".to_string(), "".to_string())),
1597 Some("".to_string()))
1598 .expect(&format!("failed to exec `{}`
", config.adb_path));
1600 // get stderr of result
1601 runargs = Vec::new();
1602 runargs.push("shell
".to_string());
1603 runargs.push("cat
".to_string());
1604 runargs.push(format!("{}
/{}
.stderr
", config.adb_test_dir, prog_short));
1606 let procsrv::Result{ out: stderr_out, err: _, status: _ } =
1611 vec!(("".to_string(), "".to_string())),
1612 Some("".to_string()))
1613 .expect(&format!("failed to exec `{}`
", config.adb_path));
1621 status: Status::Parsed(exitcode),
1628 fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) {
1629 let tdir = aux_output_dir_name(config, testfile);
1631 let dirs = fs::read_dir(&tdir).unwrap();
1633 let file = file.unwrap().path();
1634 if file.extension().and_then(|s| s.to_str()) == Some("so
") {
1635 // FIXME (#9639): This needs to handle non-utf8 paths
1636 let copy_result = procsrv::run("",
1644 config.adb_test_dir.to_string(),
1646 vec!(("".to_string(),
1648 Some("".to_string()))
1649 .expect(&format!("failed to exec `{}`
", config.adb_path));
1652 println!("push ({}
) {:?} {} {}
",
1653 config.target, file.display(),
1654 copy_result.out, copy_result.err);
1660 // codegen tests (using FileCheck)
1662 fn compile_test_and_save_ir(config: &Config, props: &TestProps,
1663 testfile: &Path) -> ProcRes {
1664 let aux_dir = aux_output_dir_name(config, testfile);
1665 // FIXME (#9639): This needs to handle non-utf8 paths
1666 let mut link_args = vec!("-L
".to_string(),
1667 aux_dir.to_str().unwrap().to_string());
1668 let llvm_args = vec!("--emit
=llvm
-ir
".to_string(),
1669 "--crate-type=lib
".to_string());
1670 link_args.extend(llvm_args);
1671 let args = make_compile_args(config,
1674 |a, b| TargetLocation::ThisDirectory(
1675 output_base_name(a, b).parent()
1676 .unwrap().to_path_buf()),
1678 compose_and_run_compiler(config, props, testfile, args, None)
1681 fn check_ir_with_filecheck(config: &Config, testfile: &Path) -> ProcRes {
1682 let irfile = output_base_name(config, testfile).with_extension("ll
");
1683 let prog = config.llvm_bin_path.as_ref().unwrap().join("FileCheck
");
1684 let proc_args = ProcArgs {
1685 // FIXME (#9639): This needs to handle non-utf8 paths
1686 prog: prog.to_str().unwrap().to_string(),
1687 args: vec!(format!("-input
-file
={}
", irfile.to_str().unwrap()),
1688 testfile.to_str().unwrap().to_string())
1690 compose_and_run(config, testfile, proc_args, Vec::new(), "", None, None)
1693 fn run_codegen_test(config: &Config, props: &TestProps, testfile: &Path) {
1695 if config.llvm_bin_path.is_none() {
1696 fatal("missing
--llvm
-bin
-path
");
1699 let mut proc_res = compile_test_and_save_ir(config, props, testfile);
1700 if !proc_res.status.success() {
1701 fatal_proc_rec("compilation failed
!", &proc_res);
1704 proc_res = check_ir_with_filecheck(config, testfile);
1705 if !proc_res.status.success() {
1706 fatal_proc_rec("verification with 'FileCheck' failed
",
1711 fn charset() -> &'static str {
1712 if cfg!(any(target_os = "bitrig
", target_os = "freebsd
")) {
1719 fn run_rustdoc_test(config: &Config, props: &TestProps, testfile: &Path) {
1720 let (proc_res, out_dir) = document(config, props, testfile, &[]);
1721 if !proc_res.status.success() {
1722 fatal_proc_rec("rustdoc failed
!", &proc_res);
1724 let root = find_rust_src_root(config).unwrap();
1726 let res = cmd2procres(config,
1728 Command::new(&config.python)
1729 .arg(root.join("src
/etc
/htmldocck
.py
"))
1732 if !res.status.success() {
1733 fatal_proc_rec("htmldocck failed
!", &res);