Auto merge of #50400 - ehuss:compiletest-revisions, r=alexcrichton
compiletest: Run revisions as independent tests. Fixes #47604. - The output of each test is now in its own directory. - "auxiliary" output is now under the respective test directory. - `stage_id` removed from filenames, and instead placed in the stamp file as a hash. This helps keep path lengths down for Windows. In brief, the new layout looks like this: ``` <build_base>/<relative_dir>/<testname>.<revision>.<mode>/ stamp <testname>.err <testname>.out a (binary) auxiliary/lib<auxname>.dylib auxiliary/<auxname>/<auxname>.err auxiliary/<auxname>/<auxname>.out ``` (revision and mode are optional)
This commit is contained in:
commit
e3150564f8
11 changed files with 479 additions and 396 deletions
|
@ -31,18 +31,18 @@ MYDIR=$(dirname $0)
|
|||
BUILD_DIR="$1"
|
||||
shift
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
while [[ "$1" != "" ]]; do
|
||||
STDERR_NAME="${1/%.rs/.stderr}"
|
||||
STDOUT_NAME="${1/%.rs/.stdout}"
|
||||
for EXT in "stderr" "stdout"; do
|
||||
for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do
|
||||
OUT_DIR=`dirname "$1"`
|
||||
OUT_BASE=`basename "$OUT_NAME"`
|
||||
if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then
|
||||
echo updating $MYDIR/$OUT_DIR/$OUT_BASE
|
||||
cp $OUT_NAME $MYDIR/$OUT_DIR
|
||||
fi
|
||||
done
|
||||
done
|
||||
shift
|
||||
if [ -f $BUILD_DIR/$STDOUT_NAME ] && \
|
||||
! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME >& /dev/null); then
|
||||
echo updating $MYDIR/$STDOUT_NAME
|
||||
cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME
|
||||
fi
|
||||
if [ -f $BUILD_DIR/$STDERR_NAME ] && \
|
||||
! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME >& /dev/null); then
|
||||
echo updating $MYDIR/$STDERR_NAME
|
||||
cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -35,7 +35,7 @@ shopt -s nullglob
|
|||
|
||||
while [[ "$1" != "" ]]; do
|
||||
for EXT in "stderr" "stdout" "fixed"; do
|
||||
for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do
|
||||
for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do
|
||||
OUT_DIR=`dirname "$1"`
|
||||
OUT_BASE=`basename "$OUT_NAME"`
|
||||
if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
pub use self::Mode::*;
|
||||
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use test::ColorConfig;
|
||||
use util::PathBufExt;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum Mode {
|
||||
|
@ -103,7 +104,7 @@ pub enum CompareMode {
|
|||
impl CompareMode {
|
||||
pub(crate) fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
CompareMode::Nll => "nll"
|
||||
CompareMode::Nll => "nll",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,24 +246,28 @@ pub struct Config {
|
|||
pub nodejs: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestPaths {
|
||||
pub file: PathBuf, // e.g., compile-test/foo/bar/baz.rs
|
||||
pub base: PathBuf, // e.g., compile-test, auxiliary
|
||||
pub relative_dir: PathBuf, // e.g., foo/bar
|
||||
}
|
||||
|
||||
/// Used by `ui` tests to generate things like `foo.stderr` from `foo.rs`.
|
||||
pub fn expected_output_path(testpaths: &TestPaths,
|
||||
revision: Option<&str>,
|
||||
compare_mode: &Option<CompareMode>,
|
||||
kind: &str) -> PathBuf {
|
||||
|
||||
pub fn expected_output_path(
|
||||
testpaths: &TestPaths,
|
||||
revision: Option<&str>,
|
||||
compare_mode: &Option<CompareMode>,
|
||||
kind: &str,
|
||||
) -> PathBuf {
|
||||
assert!(UI_EXTENSIONS.contains(&kind));
|
||||
let mut parts = Vec::new();
|
||||
|
||||
if let Some(x) = revision { parts.push(x); }
|
||||
if let Some(ref x) = *compare_mode { parts.push(x.to_str()); }
|
||||
if let Some(x) = revision {
|
||||
parts.push(x);
|
||||
}
|
||||
if let Some(ref x) = *compare_mode {
|
||||
parts.push(x.to_str());
|
||||
}
|
||||
parts.push(kind);
|
||||
|
||||
let extension = parts.join(".");
|
||||
|
@ -273,3 +278,38 @@ pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED];
|
|||
pub const UI_STDERR: &str = "stderr";
|
||||
pub const UI_STDOUT: &str = "stdout";
|
||||
pub const UI_FIXED: &str = "fixed";
|
||||
|
||||
/// Absolute path to the directory where all output for all tests in the given
|
||||
/// `relative_dir` group should reside. Example:
|
||||
/// /path/to/build/host-triple/test/ui/relative/
|
||||
/// This is created early when tests are collected to avoid race conditions.
|
||||
pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf {
|
||||
config.build_base.join(relative_dir)
|
||||
}
|
||||
|
||||
/// Generates a unique name for the test, such as `testname.revision.mode`.
|
||||
pub fn output_testname_unique(
|
||||
config: &Config,
|
||||
testpaths: &TestPaths,
|
||||
revision: Option<&str>,
|
||||
) -> PathBuf {
|
||||
let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str());
|
||||
PathBuf::from(&testpaths.file.file_stem().unwrap())
|
||||
.with_extra_extension(revision.unwrap_or(""))
|
||||
.with_extra_extension(mode)
|
||||
}
|
||||
|
||||
/// Absolute path to the directory where all output for the given
|
||||
/// test/revision should reside. Example:
|
||||
/// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/
|
||||
pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
|
||||
output_relative_path(config, &testpaths.relative_dir)
|
||||
.join(output_testname_unique(config, testpaths, revision))
|
||||
}
|
||||
|
||||
/// Absolute path to the base filename used as output for the given
|
||||
/// test/revision. Example:
|
||||
/// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/testname
|
||||
pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
|
||||
output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap())
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ use self::WhichLine::*;
|
|||
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -35,8 +35,7 @@ impl FromStr for ErrorKind {
|
|||
"ERROR" => Ok(ErrorKind::Error),
|
||||
"NOTE" => Ok(ErrorKind::Note),
|
||||
"SUGGESTION" => Ok(ErrorKind::Suggestion),
|
||||
"WARN" |
|
||||
"WARNING" => Ok(ErrorKind::Warning),
|
||||
"WARN" | "WARNING" => Ok(ErrorKind::Warning),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
@ -101,23 +100,25 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
|
|||
rdr.lines()
|
||||
.enumerate()
|
||||
.filter_map(|(line_num, line)| {
|
||||
parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag)
|
||||
.map(|(which, error)| {
|
||||
parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag).map(
|
||||
|(which, error)| {
|
||||
match which {
|
||||
FollowPrevious(_) => {}
|
||||
_ => last_nonfollow_error = Some(error.line_num),
|
||||
}
|
||||
error
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_expected(last_nonfollow_error: Option<usize>,
|
||||
line_num: usize,
|
||||
line: &str,
|
||||
tag: &str)
|
||||
-> Option<(WhichLine, Error)> {
|
||||
fn parse_expected(
|
||||
last_nonfollow_error: Option<usize>,
|
||||
line_num: usize,
|
||||
line: &str,
|
||||
tag: &str,
|
||||
) -> Option<(WhichLine, Error)> {
|
||||
let start = match line.find(tag) {
|
||||
Some(i) => i,
|
||||
None => return None,
|
||||
|
@ -125,7 +126,13 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
|
|||
let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' {
|
||||
(true, 0)
|
||||
} else {
|
||||
(false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count())
|
||||
(
|
||||
false,
|
||||
line[start + tag.len()..]
|
||||
.chars()
|
||||
.take_while(|c| *c == '^')
|
||||
.count(),
|
||||
)
|
||||
};
|
||||
let kind_start = start + tag.len() + adjusts + (follow as usize);
|
||||
let (kind, msg);
|
||||
|
@ -133,12 +140,14 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
|
|||
.split_whitespace()
|
||||
.next()
|
||||
.expect("Encountered unexpected empty comment")
|
||||
.parse::<ErrorKind>() {
|
||||
.parse::<ErrorKind>()
|
||||
{
|
||||
Ok(k) => {
|
||||
// If we find `//~ ERROR foo` or something like that:
|
||||
kind = Some(k);
|
||||
let letters = line[kind_start..].chars();
|
||||
msg = letters.skip_while(|c| c.is_whitespace())
|
||||
msg = letters
|
||||
.skip_while(|c| c.is_whitespace())
|
||||
.skip_while(|c| !c.is_whitespace())
|
||||
.collect::<String>();
|
||||
}
|
||||
|
@ -146,7 +155,8 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
|
|||
// Otherwise we found `//~ foo`:
|
||||
kind = None;
|
||||
let letters = line[kind_start..].chars();
|
||||
msg = letters.skip_while(|c| c.is_whitespace())
|
||||
msg = letters
|
||||
.skip_while(|c| c.is_whitespace())
|
||||
.collect::<String>();
|
||||
}
|
||||
}
|
||||
|
@ -154,8 +164,10 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
|
|||
|
||||
let (which, line_num) = if follow {
|
||||
assert_eq!(adjusts, 0, "use either //~| or //~^, not both.");
|
||||
let line_num = last_nonfollow_error.expect("encountered //~| without \
|
||||
preceding //~^ line.");
|
||||
let line_num = last_nonfollow_error.expect(
|
||||
"encountered //~| without \
|
||||
preceding //~^ line.",
|
||||
);
|
||||
(FollowPrevious(line_num), line_num)
|
||||
} else {
|
||||
let which = if adjusts > 0 {
|
||||
|
@ -167,16 +179,16 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
|
|||
(which, line_num)
|
||||
};
|
||||
|
||||
debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}",
|
||||
line_num,
|
||||
tag,
|
||||
which,
|
||||
kind,
|
||||
msg);
|
||||
Some((which,
|
||||
Error {
|
||||
line_num,
|
||||
kind,
|
||||
msg,
|
||||
}))
|
||||
debug!(
|
||||
"line={} tag={:?} which={:?} kind={:?} msg={:?}",
|
||||
line_num, tag, which, kind, msg
|
||||
);
|
||||
Some((
|
||||
which,
|
||||
Error {
|
||||
line_num,
|
||||
kind,
|
||||
msg,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use common::Config;
|
||||
use common;
|
||||
use common::Config;
|
||||
use util;
|
||||
|
||||
use extract_gdb_version;
|
||||
|
@ -38,19 +38,14 @@ impl EarlyProps {
|
|||
revisions: vec![],
|
||||
};
|
||||
|
||||
iter_header(testfile,
|
||||
None,
|
||||
&mut |ln| {
|
||||
iter_header(testfile, None, &mut |ln| {
|
||||
// we should check if any only-<platform> exists and if it exists
|
||||
// and does not matches the current platform, skip the test
|
||||
props.ignore =
|
||||
props.ignore ||
|
||||
config.parse_cfg_name_directive(ln, "ignore") ||
|
||||
(config.has_cfg_prefix(ln, "only") &&
|
||||
!config.parse_cfg_name_directive(ln, "only")) ||
|
||||
ignore_gdb(config, ln) ||
|
||||
ignore_lldb(config, ln) ||
|
||||
ignore_llvm(config, ln);
|
||||
props.ignore = props.ignore || config.parse_cfg_name_directive(ln, "ignore")
|
||||
|| (config.has_cfg_prefix(ln, "only")
|
||||
&& !config.parse_cfg_name_directive(ln, "only"))
|
||||
|| ignore_gdb(config, ln) || ignore_lldb(config, ln)
|
||||
|| ignore_llvm(config, ln);
|
||||
|
||||
if let Some(s) = config.parse_aux_build(ln) {
|
||||
props.aux.push(s);
|
||||
|
@ -149,7 +144,7 @@ impl EarlyProps {
|
|||
|
||||
fn ignore_llvm(config: &Config, line: &str) -> bool {
|
||||
if config.system_llvm && line.starts_with("no-system-llvm") {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
if let Some(ref actual_version) = config.llvm_version {
|
||||
if line.starts_with("min-llvm-version") {
|
||||
|
@ -272,11 +267,7 @@ impl TestProps {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_aux_file(&self,
|
||||
testfile: &Path,
|
||||
cfg: Option<&str>,
|
||||
config: &Config)
|
||||
-> Self {
|
||||
pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
|
||||
let mut props = TestProps::new();
|
||||
|
||||
// copy over select properties to the aux build:
|
||||
|
@ -296,20 +287,15 @@ impl TestProps {
|
|||
/// tied to a particular revision `foo` (indicated by writing
|
||||
/// `//[foo]`), then the property is ignored unless `cfg` is
|
||||
/// `Some("foo")`.
|
||||
fn load_from(&mut self,
|
||||
testfile: &Path,
|
||||
cfg: Option<&str>,
|
||||
config: &Config) {
|
||||
iter_header(testfile,
|
||||
cfg,
|
||||
&mut |ln| {
|
||||
fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
|
||||
iter_header(testfile, cfg, &mut |ln| {
|
||||
if let Some(ep) = config.parse_error_pattern(ln) {
|
||||
self.error_patterns.push(ep);
|
||||
}
|
||||
|
||||
if let Some(flags) = config.parse_compile_flags(ln) {
|
||||
self.compile_flags.extend(flags.split_whitespace()
|
||||
.map(|s| s.to_owned()));
|
||||
self.compile_flags
|
||||
.extend(flags.split_whitespace().map(|s| s.to_owned()));
|
||||
}
|
||||
|
||||
if let Some(r) = config.parse_revisions(ln) {
|
||||
|
@ -382,8 +368,7 @@ impl TestProps {
|
|||
|
||||
if !self.compile_pass {
|
||||
// run-pass implies must_compile_sucessfully
|
||||
self.compile_pass =
|
||||
config.parse_compile_pass(ln) || self.run_pass;
|
||||
self.compile_pass = config.parse_compile_pass(ln) || self.run_pass;
|
||||
}
|
||||
|
||||
if !self.skip_trans {
|
||||
|
@ -453,7 +438,7 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
|
|||
None => false,
|
||||
};
|
||||
if matches {
|
||||
it(ln[(close_brace + 1) ..].trim_left());
|
||||
it(ln[(close_brace + 1)..].trim_left());
|
||||
}
|
||||
} else {
|
||||
panic!("malformed condition directive: expected `{}foo]`, found `{}`",
|
||||
|
@ -554,9 +539,7 @@ impl Config {
|
|||
fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
|
||||
self.parse_name_value_directive(line, name).map(|nv| {
|
||||
// nv is either FOO or FOO=BAR
|
||||
let mut strs: Vec<String> = nv.splitn(2, '=')
|
||||
.map(str::to_owned)
|
||||
.collect();
|
||||
let mut strs: Vec<String> = nv.splitn(2, '=').map(str::to_owned).collect();
|
||||
|
||||
match strs.len() {
|
||||
1 => (strs.pop().unwrap(), "".to_owned()),
|
||||
|
@ -599,7 +582,10 @@ impl Config {
|
|||
/// or `normalize-stderr-32bit`. Returns `true` if the line matches it.
|
||||
fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> bool {
|
||||
if line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') {
|
||||
let name = line[prefix.len()+1 ..].split(&[':', ' '][..]).next().unwrap();
|
||||
let name = line[prefix.len() + 1..]
|
||||
.split(&[':', ' '][..])
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
name == "test" ||
|
||||
util::matches_os(&self.target, name) || // target
|
||||
|
@ -612,8 +598,7 @@ impl Config {
|
|||
common::DebugInfoLldb => name == "lldb",
|
||||
common::Pretty => name == "pretty",
|
||||
_ => false,
|
||||
} ||
|
||||
(self.target != self.host && name == "cross-compile")
|
||||
} || (self.target != self.host && name == "cross-compile")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -631,14 +616,14 @@ impl Config {
|
|||
// the line says "ignore-x86_64".
|
||||
line.starts_with(directive) && match line.as_bytes().get(directive.len()) {
|
||||
None | Some(&b' ') | Some(&b':') => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
|
||||
let colon = directive.len();
|
||||
if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
|
||||
let value = line[(colon + 1) ..].to_owned();
|
||||
let value = line[(colon + 1)..].to_owned();
|
||||
debug!("{}: {}", directive, value);
|
||||
Some(expand_variables(value, self))
|
||||
} else {
|
||||
|
@ -665,8 +650,10 @@ impl Config {
|
|||
}
|
||||
|
||||
pub fn lldb_version_to_int(version_string: &str) -> isize {
|
||||
let error_string = format!("Encountered LLDB version string with unexpected format: {}",
|
||||
version_string);
|
||||
let error_string = format!(
|
||||
"Encountered LLDB version string with unexpected format: {}",
|
||||
version_string
|
||||
);
|
||||
version_string.parse().expect(&error_string)
|
||||
}
|
||||
|
||||
|
@ -713,6 +700,6 @@ fn parse_normalization_string(line: &mut &str) -> Option<String> {
|
|||
None => return None,
|
||||
};
|
||||
let result = line[begin..end].to_owned();
|
||||
*line = &line[end+1..];
|
||||
*line = &line[end + 1..];
|
||||
Some(result)
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
// except according to those terms.
|
||||
|
||||
use errors::{Error, ErrorKind};
|
||||
use serde_json;
|
||||
use std::str::FromStr;
|
||||
use std::path::Path;
|
||||
use runtest::ProcRes;
|
||||
use serde_json;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
// These structs are a subset of the ones found in
|
||||
// `syntax::json`.
|
||||
|
@ -58,26 +58,30 @@ struct DiagnosticCode {
|
|||
}
|
||||
|
||||
pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String {
|
||||
output.lines()
|
||||
.filter_map(|line| if line.starts_with('{') {
|
||||
match serde_json::from_str::<Diagnostic>(line) {
|
||||
Ok(diagnostic) => diagnostic.rendered,
|
||||
Err(error) => {
|
||||
proc_res.fatal(Some(&format!("failed to decode compiler output as json: \
|
||||
`{}`\noutput: {}\nline: {}",
|
||||
error,
|
||||
line,
|
||||
output)));
|
||||
output
|
||||
.lines()
|
||||
.filter_map(|line| {
|
||||
if line.starts_with('{') {
|
||||
match serde_json::from_str::<Diagnostic>(line) {
|
||||
Ok(diagnostic) => diagnostic.rendered,
|
||||
Err(error) => {
|
||||
proc_res.fatal(Some(&format!(
|
||||
"failed to decode compiler output as json: \
|
||||
`{}`\noutput: {}\nline: {}",
|
||||
error, line, output
|
||||
)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec<Error> {
|
||||
output.lines()
|
||||
output
|
||||
.lines()
|
||||
.flat_map(|line| parse_line(file_name, line, output, proc_res))
|
||||
.collect()
|
||||
}
|
||||
|
@ -93,11 +97,11 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) ->
|
|||
expected_errors
|
||||
}
|
||||
Err(error) => {
|
||||
proc_res.fatal(Some(&format!("failed to decode compiler output as json: \
|
||||
`{}`\noutput: {}\nline: {}",
|
||||
error,
|
||||
line,
|
||||
output)));
|
||||
proc_res.fatal(Some(&format!(
|
||||
"failed to decode compiler output as json: \
|
||||
`{}`\noutput: {}\nline: {}",
|
||||
error, line, output
|
||||
)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -105,11 +109,14 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) ->
|
|||
}
|
||||
}
|
||||
|
||||
fn push_expected_errors(expected_errors: &mut Vec<Error>,
|
||||
diagnostic: &Diagnostic,
|
||||
default_spans: &[&DiagnosticSpan],
|
||||
file_name: &str) {
|
||||
let spans_in_this_file: Vec<_> = diagnostic.spans
|
||||
fn push_expected_errors(
|
||||
expected_errors: &mut Vec<Error>,
|
||||
diagnostic: &Diagnostic,
|
||||
default_spans: &[&DiagnosticSpan],
|
||||
file_name: &str,
|
||||
) {
|
||||
let spans_in_this_file: Vec<_> = diagnostic
|
||||
.spans
|
||||
.iter()
|
||||
.filter(|span| Path::new(&span.file_name) == Path::new(&file_name))
|
||||
.collect();
|
||||
|
@ -204,8 +211,10 @@ fn push_expected_errors(expected_errors: &mut Vec<Error>,
|
|||
}
|
||||
|
||||
// Add notes for any labels that appear in the message.
|
||||
for span in spans_in_this_file.iter()
|
||||
.filter(|span| span.label.is_some()) {
|
||||
for span in spans_in_this_file
|
||||
.iter()
|
||||
.filter(|span| span.label.is_some())
|
||||
{
|
||||
expected_errors.push(Error {
|
||||
line_num: span.line_start,
|
||||
kind: Some(ErrorKind::Note),
|
||||
|
@ -219,9 +228,11 @@ fn push_expected_errors(expected_errors: &mut Vec<Error>,
|
|||
}
|
||||
}
|
||||
|
||||
fn push_backtrace(expected_errors: &mut Vec<Error>,
|
||||
expansion: &DiagnosticSpanMacroExpansion,
|
||||
file_name: &str) {
|
||||
fn push_backtrace(
|
||||
expected_errors: &mut Vec<Error>,
|
||||
expansion: &DiagnosticSpanMacroExpansion,
|
||||
file_name: &str,
|
||||
) {
|
||||
if Path::new(&expansion.span.file_name) == Path::new(&file_name) {
|
||||
expected_errors.push(Error {
|
||||
line_num: expansion.span.line_start,
|
||||
|
|
|
@ -31,31 +31,31 @@ extern crate serde_json;
|
|||
extern crate test;
|
||||
extern crate rustfix;
|
||||
|
||||
use common::CompareMode;
|
||||
use common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
|
||||
use common::{Config, TestPaths};
|
||||
use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
|
||||
use filetime::FileTime;
|
||||
use getopts::Options;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::{self, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use filetime::FileTime;
|
||||
use getopts::Options;
|
||||
use common::{Config, TestPaths};
|
||||
use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
|
||||
use common::{expected_output_path, UI_EXTENSIONS};
|
||||
use common::CompareMode;
|
||||
use test::ColorConfig;
|
||||
use util::logv;
|
||||
|
||||
use self::header::EarlyProps;
|
||||
|
||||
pub mod util;
|
||||
mod json;
|
||||
pub mod header;
|
||||
pub mod runtest;
|
||||
pub mod common;
|
||||
pub mod errors;
|
||||
pub mod header;
|
||||
mod json;
|
||||
mod raise_fd_limit;
|
||||
mod read2;
|
||||
pub mod runtest;
|
||||
pub mod util;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
@ -236,7 +236,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||
"",
|
||||
"compare-mode",
|
||||
"mode describing what file the actual ui output will be compared to",
|
||||
"COMPARE MODE"
|
||||
"COMPARE MODE",
|
||||
)
|
||||
.optflag("h", "help", "show this message");
|
||||
|
||||
|
@ -501,7 +501,11 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
|
|||
filter: config.filter.clone(),
|
||||
filter_exact: config.filter_exact,
|
||||
run_ignored: config.run_ignored,
|
||||
format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
|
||||
format: if config.quiet {
|
||||
test::OutputFormat::Terse
|
||||
} else {
|
||||
test::OutputFormat::Pretty
|
||||
},
|
||||
logfile: config.logfile.clone(),
|
||||
run_tests: true,
|
||||
bench_benchmarks: true,
|
||||
|
@ -548,10 +552,9 @@ fn collect_tests_from_dir(
|
|||
if name == *"Makefile" && config.mode == Mode::RunMake {
|
||||
let paths = TestPaths {
|
||||
file: dir.to_path_buf(),
|
||||
base: base.to_path_buf(),
|
||||
relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
|
||||
};
|
||||
tests.push(make_test(config, &paths));
|
||||
tests.extend(make_test(config, &paths));
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
@ -562,7 +565,7 @@ fn collect_tests_from_dir(
|
|||
// sequential loop because otherwise, if we do it in the
|
||||
// tests themselves, they race for the privilege of
|
||||
// creating the directories and sometimes fail randomly.
|
||||
let build_dir = config.build_base.join(&relative_dir_path);
|
||||
let build_dir = output_relative_path(config, relative_dir_path);
|
||||
fs::create_dir_all(&build_dir).unwrap();
|
||||
|
||||
// Add each `.rs` file as a test, and recurse further on any
|
||||
|
@ -576,21 +579,12 @@ fn collect_tests_from_dir(
|
|||
debug!("found test file: {:?}", file_path.display());
|
||||
let paths = TestPaths {
|
||||
file: file_path,
|
||||
base: base.to_path_buf(),
|
||||
relative_dir: relative_dir_path.to_path_buf(),
|
||||
};
|
||||
tests.push(make_test(config, &paths))
|
||||
tests.extend(make_test(config, &paths))
|
||||
} else if file_path.is_dir() {
|
||||
let relative_file_path = relative_dir_path.join(file.file_name());
|
||||
if &file_name == "auxiliary" {
|
||||
// `aux` directories contain other crates used for
|
||||
// cross-crate tests. Don't search them for tests, but
|
||||
// do create a directory in the build dir for them,
|
||||
// since we will dump intermediate output in there
|
||||
// sometimes.
|
||||
let build_dir = config.build_base.join(&relative_file_path);
|
||||
fs::create_dir_all(&build_dir).unwrap();
|
||||
} else {
|
||||
if &file_name != "auxiliary" {
|
||||
debug!("found directory: {:?}", file_path.display());
|
||||
collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?;
|
||||
}
|
||||
|
@ -613,7 +607,7 @@ pub fn is_test(file_name: &OsString) -> bool {
|
|||
!invalid_prefixes.iter().any(|p| file_name.starts_with(p))
|
||||
}
|
||||
|
||||
pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
|
||||
pub fn make_test(config: &Config, testpaths: &TestPaths) -> Vec<test::TestDescAndFn> {
|
||||
let early_props = if config.mode == Mode::RunMake {
|
||||
// Allow `ignore` directives to be in the Makefile.
|
||||
EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
|
||||
|
@ -633,47 +627,68 @@ pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn
|
|||
},
|
||||
};
|
||||
|
||||
// Debugging emscripten code doesn't make sense today
|
||||
let ignore = early_props.ignore
|
||||
|| !up_to_date(config, testpaths, &early_props)
|
||||
|| (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
|
||||
&& config.target.contains("emscripten");
|
||||
|
||||
test::TestDescAndFn {
|
||||
desc: test::TestDesc {
|
||||
name: make_test_name(config, testpaths),
|
||||
ignore,
|
||||
should_panic,
|
||||
allow_fail: false,
|
||||
},
|
||||
testfn: make_test_closure(config, testpaths),
|
||||
}
|
||||
}
|
||||
|
||||
fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf {
|
||||
let mode_suffix = match config.compare_mode {
|
||||
Some(ref mode) => format!("-{}", mode.to_str()),
|
||||
None => format!(""),
|
||||
// Incremental tests are special, they inherently cannot be run in parallel.
|
||||
// `runtest::run` will be responsible for iterating over revisions.
|
||||
let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
|
||||
vec![None]
|
||||
} else {
|
||||
early_props.revisions.iter().map(|r| Some(r)).collect()
|
||||
};
|
||||
let stamp_name = format!(
|
||||
"{}-{}{}.stamp",
|
||||
testpaths.file.file_name().unwrap().to_str().unwrap(),
|
||||
config.stage_id,
|
||||
mode_suffix
|
||||
);
|
||||
config
|
||||
.build_base
|
||||
.canonicalize()
|
||||
.unwrap_or_else(|_| config.build_base.clone())
|
||||
.join(&testpaths.relative_dir)
|
||||
.join(stamp_name)
|
||||
revisions
|
||||
.into_iter()
|
||||
.map(|revision| {
|
||||
// Debugging emscripten code doesn't make sense today
|
||||
let ignore = early_props.ignore
|
||||
|| !up_to_date(
|
||||
config,
|
||||
testpaths,
|
||||
&early_props,
|
||||
revision.map(|s| s.as_str()),
|
||||
)
|
||||
|| (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
|
||||
&& config.target.contains("emscripten");
|
||||
test::TestDescAndFn {
|
||||
desc: test::TestDesc {
|
||||
name: make_test_name(config, testpaths, revision),
|
||||
ignore,
|
||||
should_panic,
|
||||
allow_fail: false,
|
||||
},
|
||||
testfn: make_test_closure(config, testpaths, revision),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> bool {
|
||||
fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
|
||||
output_base_dir(config, testpaths, revision).join("stamp")
|
||||
}
|
||||
|
||||
fn up_to_date(
|
||||
config: &Config,
|
||||
testpaths: &TestPaths,
|
||||
props: &EarlyProps,
|
||||
revision: Option<&str>,
|
||||
) -> bool {
|
||||
let stamp_name = stamp(config, testpaths, revision);
|
||||
// Check hash.
|
||||
let mut f = match fs::File::open(&stamp_name) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return true,
|
||||
};
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)
|
||||
.expect("Can't read stamp contents");
|
||||
let expected_hash = runtest::compute_stamp_hash(config);
|
||||
if contents != expected_hash {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check timestamps.
|
||||
let rust_src_dir = config
|
||||
.find_rust_src_root()
|
||||
.expect("Could not find Rust source root");
|
||||
let stamp = mtime(&stamp(config, testpaths));
|
||||
let stamp = mtime(&stamp_name);
|
||||
let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)];
|
||||
for aux in props.aux.iter() {
|
||||
inputs.push(mtime(&testpaths
|
||||
|
@ -694,8 +709,7 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo
|
|||
for pretty_printer_file in &pretty_printer_files {
|
||||
inputs.push(mtime(&rust_src_dir.join(pretty_printer_file)));
|
||||
}
|
||||
let mut entries = config.run_lib_path.read_dir().unwrap()
|
||||
.collect::<Vec<_>>();
|
||||
let mut entries = config.run_lib_path.read_dir().unwrap().collect::<Vec<_>>();
|
||||
while let Some(entry) = entries.pop() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
|
@ -712,18 +726,8 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo
|
|||
|
||||
// UI test files.
|
||||
for extension in UI_EXTENSIONS {
|
||||
for revision in &props.revisions {
|
||||
let path = &expected_output_path(testpaths,
|
||||
Some(revision),
|
||||
&config.compare_mode,
|
||||
extension);
|
||||
inputs.push(mtime(path));
|
||||
}
|
||||
|
||||
if props.revisions.is_empty() {
|
||||
let path = &expected_output_path(testpaths, None, &config.compare_mode, extension);
|
||||
inputs.push(mtime(path));
|
||||
}
|
||||
let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
|
||||
inputs.push(mtime(path));
|
||||
}
|
||||
|
||||
inputs.iter().any(|input| *input > stamp)
|
||||
|
@ -735,7 +739,11 @@ fn mtime(path: &Path) -> FileTime {
|
|||
.unwrap_or_else(|_| FileTime::zero())
|
||||
}
|
||||
|
||||
pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
|
||||
fn make_test_name(
|
||||
config: &Config,
|
||||
testpaths: &TestPaths,
|
||||
revision: Option<&String>,
|
||||
) -> test::TestName {
|
||||
// Convert a complete path to something like
|
||||
//
|
||||
// run-pass/foo/bar/baz.rs
|
||||
|
@ -746,13 +754,26 @@ pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName
|
|||
Some(ref mode) => format!(" ({})", mode.to_str()),
|
||||
None => format!(""),
|
||||
};
|
||||
test::DynTestName(format!("[{}{}] {}", config.mode, mode_suffix, path.display()))
|
||||
test::DynTestName(format!(
|
||||
"[{}{}] {}{}",
|
||||
config.mode,
|
||||
mode_suffix,
|
||||
path.display(),
|
||||
revision.map_or("".to_string(), |rev| format!("#{}", rev))
|
||||
))
|
||||
}
|
||||
|
||||
pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
|
||||
fn make_test_closure(
|
||||
config: &Config,
|
||||
testpaths: &TestPaths,
|
||||
revision: Option<&String>,
|
||||
) -> test::TestFn {
|
||||
let config = config.clone();
|
||||
let testpaths = testpaths.clone();
|
||||
test::DynTestFn(Box::new(move || runtest::run(config, &testpaths)))
|
||||
let revision = revision.cloned();
|
||||
test::DynTestFn(Box::new(move || {
|
||||
runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str()))
|
||||
}))
|
||||
}
|
||||
|
||||
/// Returns (Path to GDB, GDB Version, GDB has Rust Support)
|
||||
|
|
|
@ -34,12 +34,15 @@ pub unsafe fn raise_fd_limit() {
|
|||
let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC];
|
||||
let mut maxfiles: libc::c_int = 0;
|
||||
let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t;
|
||||
if libc::sysctl(&mut mib[0],
|
||||
2,
|
||||
&mut maxfiles as *mut _ as *mut _,
|
||||
&mut size,
|
||||
null_mut(),
|
||||
0) != 0 {
|
||||
if libc::sysctl(
|
||||
&mut mib[0],
|
||||
2,
|
||||
&mut maxfiles as *mut _ as *mut _,
|
||||
&mut size,
|
||||
null_mut(),
|
||||
0,
|
||||
) != 0
|
||||
{
|
||||
let err = io::Error::last_os_error();
|
||||
panic!("raise_fd_limit: error calling sysctl: {}", err);
|
||||
}
|
||||
|
|
|
@ -16,11 +16,13 @@ pub use self::imp::read2;
|
|||
#[cfg(not(any(unix, windows)))]
|
||||
mod imp {
|
||||
use std::io::{self, Read};
|
||||
use std::process::{ChildStdout, ChildStderr};
|
||||
use std::process::{ChildStderr, ChildStdout};
|
||||
|
||||
pub fn read2(out_pipe: ChildStdout,
|
||||
err_pipe: ChildStderr,
|
||||
data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
|
||||
pub fn read2(
|
||||
out_pipe: ChildStdout,
|
||||
err_pipe: ChildStderr,
|
||||
data: &mut FnMut(bool, &mut Vec<u8>, bool),
|
||||
) -> io::Result<()> {
|
||||
let mut buffer = Vec::new();
|
||||
out_pipe.read_to_end(&mut buffer)?;
|
||||
data(true, &mut buffer, true);
|
||||
|
@ -33,16 +35,18 @@ mod imp {
|
|||
|
||||
#[cfg(unix)]
|
||||
mod imp {
|
||||
use std::io::prelude::*;
|
||||
use libc;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::mem;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::process::{ChildStdout, ChildStderr};
|
||||
use libc;
|
||||
use std::process::{ChildStderr, ChildStdout};
|
||||
|
||||
pub fn read2(mut out_pipe: ChildStdout,
|
||||
mut err_pipe: ChildStderr,
|
||||
data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
|
||||
pub fn read2(
|
||||
mut out_pipe: ChildStdout,
|
||||
mut err_pipe: ChildStderr,
|
||||
data: &mut FnMut(bool, &mut Vec<u8>, bool),
|
||||
) -> io::Result<()> {
|
||||
unsafe {
|
||||
libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
|
||||
libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
|
||||
|
@ -67,9 +71,9 @@ mod imp {
|
|||
if r == -1 {
|
||||
let err = io::Error::last_os_error();
|
||||
if err.kind() == io::ErrorKind::Interrupted {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
return Err(err)
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
// Read as much as we can from each pipe, ignoring EWOULDBLOCK or
|
||||
|
@ -77,15 +81,13 @@ mod imp {
|
|||
// reader will return Ok(0), in which case we'll see `Ok` ourselves. In
|
||||
// this case we flip the other fd back into blocking mode and read
|
||||
// whatever's leftover on that file descriptor.
|
||||
let handle = |res: io::Result<_>| {
|
||||
match res {
|
||||
Ok(_) => Ok(true),
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
Ok(false)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
let handle = |res: io::Result<_>| match res {
|
||||
Ok(_) => Ok(true),
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
Ok(false)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -113,7 +115,7 @@ mod imp {
|
|||
|
||||
use std::io;
|
||||
use std::os::windows::prelude::*;
|
||||
use std::process::{ChildStdout, ChildStderr};
|
||||
use std::process::{ChildStderr, ChildStdout};
|
||||
use std::slice;
|
||||
|
||||
use self::miow::iocp::{CompletionPort, CompletionStatus};
|
||||
|
@ -128,9 +130,11 @@ mod imp {
|
|||
done: bool,
|
||||
}
|
||||
|
||||
pub fn read2(out_pipe: ChildStdout,
|
||||
err_pipe: ChildStderr,
|
||||
data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
|
||||
pub fn read2(
|
||||
out_pipe: ChildStdout,
|
||||
err_pipe: ChildStderr,
|
||||
data: &mut FnMut(bool, &mut Vec<u8>, bool),
|
||||
) -> io::Result<()> {
|
||||
let mut out = Vec::new();
|
||||
let mut err = Vec::new();
|
||||
|
||||
|
@ -206,7 +210,9 @@ mod imp {
|
|||
if v.capacity() == v.len() {
|
||||
v.reserve(1);
|
||||
}
|
||||
slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize),
|
||||
v.capacity() - v.len())
|
||||
slice::from_raw_parts_mut(
|
||||
v.as_mut_ptr().offset(v.len() as isize),
|
||||
v.capacity() - v.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,28 +8,29 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use common::{Config, TestPaths};
|
||||
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
|
||||
use common::{Codegen, CodegenUnits, DebugInfoGdb, DebugInfoLldb, Rustdoc};
|
||||
use common::{Incremental, MirOpt, RunMake, Ui};
|
||||
use common::{expected_output_path, UI_STDERR, UI_STDOUT, UI_FIXED};
|
||||
use common::CompareMode;
|
||||
use common::{expected_output_path, UI_STDERR, UI_STDOUT, UI_FIXED};
|
||||
use common::{output_base_dir, output_base_name, output_testname_unique};
|
||||
use common::{Codegen, CodegenUnits, DebugInfoGdb, DebugInfoLldb, Rustdoc};
|
||||
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
|
||||
use common::{Config, TestPaths};
|
||||
use common::{Incremental, MirOpt, RunMake, Ui};
|
||||
use diff;
|
||||
use errors::{self, Error, ErrorKind};
|
||||
use filetime::FileTime;
|
||||
use json;
|
||||
use header::TestProps;
|
||||
use util::logv;
|
||||
use json;
|
||||
use regex::Regex;
|
||||
use rustfix::{apply_suggestions, get_suggestions_from_json};
|
||||
use util::{logv, PathBufExt};
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::{self, create_dir_all, File};
|
||||
use std::ffi::OsString;
|
||||
use std::fmt;
|
||||
use std::fs::{self, create_dir_all, File};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::prelude::*;
|
||||
use std::io::{self, BufReader};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -106,26 +107,6 @@ impl Mismatch {
|
|||
}
|
||||
}
|
||||
|
||||
trait PathBufExt {
|
||||
/// Append an extension to the path, even if it already has one.
|
||||
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf;
|
||||
}
|
||||
|
||||
impl PathBufExt for PathBuf {
|
||||
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
|
||||
if extension.as_ref().len() == 0 {
|
||||
self.clone()
|
||||
} else {
|
||||
let mut fname = self.file_name().unwrap().to_os_string();
|
||||
if !extension.as_ref().to_str().unwrap().starts_with(".") {
|
||||
fname.push(".");
|
||||
}
|
||||
fname.push(extension);
|
||||
self.with_file_name(fname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Produces a diff between the expected output and actual output.
|
||||
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
|
||||
let mut line_number = 1;
|
||||
|
@ -186,7 +167,7 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma
|
|||
results
|
||||
}
|
||||
|
||||
pub fn run(config: Config, testpaths: &TestPaths) {
|
||||
pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
|
||||
match &*config.target {
|
||||
"arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
|
||||
if !config.adb_device_status {
|
||||
|
@ -207,20 +188,25 @@ pub fn run(config: Config, testpaths: &TestPaths) {
|
|||
print!("\n\n");
|
||||
}
|
||||
debug!("running {:?}", testpaths.file.display());
|
||||
let base_props = TestProps::from_file(&testpaths.file, None, &config);
|
||||
let props = TestProps::from_file(&testpaths.file, revision, &config);
|
||||
|
||||
let base_cx = TestCx {
|
||||
let cx = TestCx {
|
||||
config: &config,
|
||||
props: &base_props,
|
||||
props: &props,
|
||||
testpaths,
|
||||
revision: None,
|
||||
revision: revision,
|
||||
};
|
||||
base_cx.init_all();
|
||||
create_dir_all(&cx.output_base_dir()).unwrap();
|
||||
|
||||
if base_props.revisions.is_empty() {
|
||||
base_cx.run_revision()
|
||||
} else {
|
||||
for revision in &base_props.revisions {
|
||||
if config.mode == Incremental {
|
||||
// Incremental tests are special because they cannot be run in
|
||||
// parallel.
|
||||
assert!(
|
||||
!props.revisions.is_empty(),
|
||||
"Incremental tests require revisions."
|
||||
);
|
||||
cx.init_incremental_test();
|
||||
for revision in &props.revisions {
|
||||
let revision_props = TestProps::from_file(&testpaths.file, Some(revision), &config);
|
||||
let rev_cx = TestCx {
|
||||
config: &config,
|
||||
|
@ -230,11 +216,17 @@ pub fn run(config: Config, testpaths: &TestPaths) {
|
|||
};
|
||||
rev_cx.run_revision();
|
||||
}
|
||||
} else {
|
||||
cx.run_revision();
|
||||
}
|
||||
|
||||
base_cx.complete_all();
|
||||
cx.create_stamp();
|
||||
}
|
||||
|
||||
File::create(::stamp(&config, testpaths)).unwrap();
|
||||
pub fn compute_stamp_hash(config: &Config) -> String {
|
||||
let mut hash = DefaultHasher::new();
|
||||
config.stage_id.hash(&mut hash);
|
||||
format!("{:x}", hash.finish())
|
||||
}
|
||||
|
||||
struct TestCx<'test> {
|
||||
|
@ -251,14 +243,6 @@ struct DebuggerCommands {
|
|||
}
|
||||
|
||||
impl<'test> TestCx<'test> {
|
||||
/// invoked once before any revisions have been processed
|
||||
fn init_all(&self) {
|
||||
assert!(self.revision.is_none(), "init_all invoked for a revision");
|
||||
if let Incremental = self.config.mode {
|
||||
self.init_incremental_test()
|
||||
}
|
||||
}
|
||||
|
||||
/// Code executed for each revision in turn (or, if there are no
|
||||
/// revisions, exactly once, with revision == None).
|
||||
fn run_revision(&self) {
|
||||
|
@ -280,11 +264,6 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Invoked after all revisions have executed.
|
||||
fn complete_all(&self) {
|
||||
assert!(self.revision.is_none(), "init_all invoked for a revision");
|
||||
}
|
||||
|
||||
fn check_if_test_should_compile(&self, proc_res: &ProcRes) {
|
||||
if self.props.compile_pass {
|
||||
if !proc_res.status.success() {
|
||||
|
@ -355,9 +334,10 @@ impl<'test> TestCx<'test> {
|
|||
|
||||
if expected_status != received_status {
|
||||
self.fatal_proc_rec(
|
||||
&format!("Error: expected failure status ({:?}) but received status {:?}.",
|
||||
expected_status,
|
||||
received_status),
|
||||
&format!(
|
||||
"Error: expected failure status ({:?}) but received status {:?}.",
|
||||
expected_status, received_status
|
||||
),
|
||||
proc_res,
|
||||
);
|
||||
}
|
||||
|
@ -440,8 +420,7 @@ impl<'test> TestCx<'test> {
|
|||
self.config,
|
||||
format!(
|
||||
"pretty-printing round {} revision {:?}",
|
||||
round,
|
||||
self.revision
|
||||
round, self.revision
|
||||
),
|
||||
);
|
||||
let proc_res = self.print_source(srcs[round].to_owned(), &self.props.pretty_mode);
|
||||
|
@ -450,8 +429,7 @@ impl<'test> TestCx<'test> {
|
|||
self.fatal_proc_rec(
|
||||
&format!(
|
||||
"pretty-printing failed in round {} revision {:?}",
|
||||
round,
|
||||
self.revision
|
||||
round, self.revision
|
||||
),
|
||||
&proc_res,
|
||||
);
|
||||
|
@ -555,8 +533,7 @@ impl<'test> TestCx<'test> {
|
|||
{}\n\
|
||||
------------------------------------------\n\
|
||||
\n",
|
||||
expected,
|
||||
actual
|
||||
expected, actual
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -661,8 +638,7 @@ impl<'test> TestCx<'test> {
|
|||
script_str.push_str(&format!(
|
||||
"set solib-search-path \
|
||||
./{}/stage2/lib/rustlib/{}/lib/\n",
|
||||
self.config.host,
|
||||
self.config.target
|
||||
self.config.host, self.config.target
|
||||
));
|
||||
for line in &breakpoint_lines {
|
||||
script_str.push_str(
|
||||
|
@ -881,7 +857,6 @@ impl<'test> TestCx<'test> {
|
|||
..self.config.clone()
|
||||
};
|
||||
|
||||
|
||||
let test_cx = TestCx {
|
||||
config: &config,
|
||||
..*self
|
||||
|
@ -952,8 +927,7 @@ impl<'test> TestCx<'test> {
|
|||
for line in &breakpoint_lines {
|
||||
script_str.push_str(&format!(
|
||||
"breakpoint set --file '{}' --line {}\n",
|
||||
source_file_name,
|
||||
line
|
||||
source_file_name, line
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -1028,9 +1002,7 @@ impl<'test> TestCx<'test> {
|
|||
fn parse_debugger_commands(&self, debugger_prefixes: &[&str]) -> DebuggerCommands {
|
||||
let directives = debugger_prefixes
|
||||
.iter()
|
||||
.map(|prefix| {
|
||||
(format!("{}-command", prefix), format!("{}-check", prefix))
|
||||
})
|
||||
.map(|prefix| (format!("{}-command", prefix), format!("{}-check", prefix)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut breakpoint_lines = vec![];
|
||||
|
@ -1041,12 +1013,11 @@ impl<'test> TestCx<'test> {
|
|||
for line in reader.lines() {
|
||||
match line {
|
||||
Ok(line) => {
|
||||
let line =
|
||||
if line.starts_with("//") {
|
||||
line[2..].trim_left()
|
||||
} else {
|
||||
line.as_str()
|
||||
};
|
||||
let line = if line.starts_with("//") {
|
||||
line[2..].trim_left()
|
||||
} else {
|
||||
line.as_str()
|
||||
};
|
||||
|
||||
if line.contains("#break") {
|
||||
breakpoint_lines.push(counter);
|
||||
|
@ -1369,6 +1340,8 @@ impl<'test> TestCx<'test> {
|
|||
testpaths: &aux_testpaths,
|
||||
revision: self.revision,
|
||||
};
|
||||
// Create the directory for the stdout/stderr files.
|
||||
create_dir_all(aux_cx.output_base_dir()).unwrap();
|
||||
let auxres = aux_cx.document(out_dir);
|
||||
if !auxres.status.success() {
|
||||
return auxres;
|
||||
|
@ -1453,7 +1426,7 @@ impl<'test> TestCx<'test> {
|
|||
let mut program = Command::new(&prog);
|
||||
program
|
||||
.args(args)
|
||||
.current_dir(&self.output_base_name().parent().unwrap())
|
||||
.current_dir(&self.output_base_dir())
|
||||
.envs(env.clone());
|
||||
self.compose_and_run(
|
||||
program,
|
||||
|
@ -1491,9 +1464,9 @@ impl<'test> TestCx<'test> {
|
|||
|
||||
TestPaths {
|
||||
file: test_ab,
|
||||
base: self.testpaths.base.clone(),
|
||||
relative_dir: self.testpaths
|
||||
.relative_dir
|
||||
.join(self.output_testname_unique())
|
||||
.join("auxiliary")
|
||||
.join(rel_ab)
|
||||
.parent()
|
||||
|
@ -1503,28 +1476,27 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
|
||||
fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
|
||||
if !self.props.aux_builds.is_empty() {
|
||||
create_dir_all(&self.aux_output_dir_name()).unwrap();
|
||||
}
|
||||
|
||||
let aux_dir = self.aux_output_dir_name();
|
||||
|
||||
if !self.props.aux_builds.is_empty() {
|
||||
let _ = fs::remove_dir_all(&aux_dir);
|
||||
create_dir_all(&aux_dir).unwrap();
|
||||
}
|
||||
|
||||
for rel_ab in &self.props.aux_builds {
|
||||
let aux_testpaths = self.compute_aux_test_paths(rel_ab);
|
||||
let aux_props =
|
||||
self.props
|
||||
.from_aux_file(&aux_testpaths.file, self.revision, self.config);
|
||||
let aux_output = {
|
||||
let f = self.make_lib_name(&self.testpaths.file);
|
||||
let parent = f.parent().unwrap();
|
||||
TargetLocation::ThisDirectory(parent.to_path_buf())
|
||||
};
|
||||
let aux_output = TargetLocation::ThisDirectory(self.aux_output_dir_name());
|
||||
let aux_cx = TestCx {
|
||||
config: self.config,
|
||||
props: &aux_props,
|
||||
testpaths: &aux_testpaths,
|
||||
revision: self.revision,
|
||||
};
|
||||
// Create the directory for the stdout/stderr files.
|
||||
create_dir_all(aux_cx.output_base_dir()).unwrap();
|
||||
let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output);
|
||||
|
||||
let crate_type = if aux_props.no_prefer_dynamic {
|
||||
|
@ -1645,9 +1617,13 @@ impl<'test> TestCx<'test> {
|
|||
let mut rustc = if !is_rustdoc {
|
||||
Command::new(&self.config.rustc_path)
|
||||
} else {
|
||||
Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
|
||||
Command::new(&self.config
|
||||
.rustdoc_path
|
||||
.clone()
|
||||
.expect("no rustdoc built yet"))
|
||||
};
|
||||
rustc.arg(input_file).arg("-L").arg(&self.config.build_base);
|
||||
// FIXME Why is -L here?
|
||||
rustc.arg(input_file);//.arg("-L").arg(&self.config.build_base);
|
||||
|
||||
// Optionally prevent default --target if specified in test compile-flags.
|
||||
let custom_target = self.props
|
||||
|
@ -1671,10 +1647,7 @@ impl<'test> TestCx<'test> {
|
|||
|
||||
if !is_rustdoc {
|
||||
if let Some(ref incremental_dir) = self.props.incremental_dir {
|
||||
rustc.args(&[
|
||||
"-C",
|
||||
&format!("incremental={}", incremental_dir.display()),
|
||||
]);
|
||||
rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]);
|
||||
rustc.args(&["-Z", "incremental-verify-ich"]);
|
||||
rustc.args(&["-Z", "incremental-queries"]);
|
||||
}
|
||||
|
@ -1697,7 +1670,11 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
}
|
||||
Ui => {
|
||||
if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
|
||||
if !self.props
|
||||
.compile_flags
|
||||
.iter()
|
||||
.any(|s| s.starts_with("--error-format"))
|
||||
{
|
||||
rustc.args(&["--error-format", "json"]);
|
||||
}
|
||||
if !self.props.disable_ui_testing_normalization {
|
||||
|
@ -1720,16 +1697,8 @@ impl<'test> TestCx<'test> {
|
|||
|
||||
rustc.arg(dir_opt);
|
||||
}
|
||||
RunPass |
|
||||
RunFail |
|
||||
RunPassValgrind |
|
||||
Pretty |
|
||||
DebugInfoGdb |
|
||||
DebugInfoLldb |
|
||||
Codegen |
|
||||
Rustdoc |
|
||||
RunMake |
|
||||
CodegenUnits => {
|
||||
RunPass | RunFail | RunPassValgrind | Pretty | DebugInfoGdb | DebugInfoLldb
|
||||
| Codegen | Rustdoc | RunMake | CodegenUnits => {
|
||||
// do not use JSON output
|
||||
}
|
||||
}
|
||||
|
@ -1759,8 +1728,8 @@ impl<'test> TestCx<'test> {
|
|||
match self.config.compare_mode {
|
||||
Some(CompareMode::Nll) => {
|
||||
rustc.args(&["-Zborrowck=mir", "-Ztwo-phase-borrows"]);
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
if self.props.force_host {
|
||||
|
@ -1779,15 +1748,12 @@ impl<'test> TestCx<'test> {
|
|||
rustc
|
||||
}
|
||||
|
||||
fn make_lib_name(&self, auxfile: &Path) -> PathBuf {
|
||||
// what we return here is not particularly important, as it
|
||||
// happens; rustc ignores everything except for the directory.
|
||||
let auxname = self.output_testname(auxfile);
|
||||
self.aux_output_dir_name().join(&auxname)
|
||||
}
|
||||
|
||||
fn make_exe_name(&self) -> PathBuf {
|
||||
let mut f = self.output_base_name_stage();
|
||||
// Using a single letter here to keep the path length down for
|
||||
// Windows. Some test names get very long. rustc creates `rcgu`
|
||||
// files with the module name appended to it which can more than
|
||||
// double the length.
|
||||
let mut f = self.output_base_dir().join("a");
|
||||
// FIXME: This is using the host architecture exe suffix, not target!
|
||||
if self.config.target.contains("emscripten") {
|
||||
f = f.with_extra_extension("js");
|
||||
|
@ -1897,33 +1863,47 @@ impl<'test> TestCx<'test> {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
/// Create a filename for output with the given extension. Example:
|
||||
/// /.../testname.revision.mode/testname.extension
|
||||
fn make_out_name(&self, extension: &str) -> PathBuf {
|
||||
self.output_base_name().with_extension(extension)
|
||||
}
|
||||
|
||||
/// Directory where auxiliary files are written. Example:
|
||||
/// /.../testname.revision.mode/auxiliary/
|
||||
fn aux_output_dir_name(&self) -> PathBuf {
|
||||
self.output_base_name_stage()
|
||||
self.output_base_dir()
|
||||
.join("auxiliary")
|
||||
.with_extra_extension(self.config.mode.disambiguator())
|
||||
.with_extra_extension(".aux")
|
||||
}
|
||||
|
||||
fn output_testname(&self, filepath: &Path) -> PathBuf {
|
||||
PathBuf::from(filepath.file_stem().unwrap())
|
||||
/// Generates a unique name for the test, such as `testname.revision.mode`.
|
||||
fn output_testname_unique(&self) -> PathBuf {
|
||||
output_testname_unique(self.config, self.testpaths, self.safe_revision())
|
||||
}
|
||||
|
||||
/// Given a test path like `compile-fail/foo/bar.rs` returns a name like
|
||||
/// `/path/to/build/<triple>/test/compile-fail/foo/bar`.
|
||||
/// The revision, ignored for Incremental since it wants all revisions in
|
||||
/// the same directory.
|
||||
fn safe_revision(&self) -> Option<&str> {
|
||||
if self.config.mode == Incremental {
|
||||
None
|
||||
} else {
|
||||
self.revision
|
||||
}
|
||||
}
|
||||
|
||||
/// Absolute path to the directory where all output for the given
|
||||
/// test/revision should reside. Example:
|
||||
/// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/
|
||||
fn output_base_dir(&self) -> PathBuf {
|
||||
output_base_dir(self.config, self.testpaths, self.safe_revision())
|
||||
}
|
||||
|
||||
/// Absolute path to the base filename used as output for the given
|
||||
/// test/revision. Example:
|
||||
/// /.../relative/testname.revision.mode/testname
|
||||
fn output_base_name(&self) -> PathBuf {
|
||||
let dir = self.config.build_base.join(&self.testpaths.relative_dir);
|
||||
|
||||
// Note: The directory `dir` is created during `collect_tests_from_dir`
|
||||
dir.join(&self.output_testname(&self.testpaths.file))
|
||||
}
|
||||
|
||||
/// Same as `output_base_name`, but includes the stage ID as an extension,
|
||||
/// such as: `.../compile-fail/foo/bar.stage1-<triple>`
|
||||
fn output_base_name_stage(&self) -> PathBuf {
|
||||
self.output_base_name().with_extension(&self.config.stage_id)
|
||||
output_base_name(self.config, self.testpaths, self.safe_revision())
|
||||
}
|
||||
|
||||
fn maybe_dump_to_stdout(&self, out: &str, err: &str) {
|
||||
|
@ -1998,8 +1978,7 @@ impl<'test> TestCx<'test> {
|
|||
fn compile_test_and_save_ir(&self) -> ProcRes {
|
||||
let aux_dir = self.aux_output_dir_name();
|
||||
|
||||
let output_file =
|
||||
TargetLocation::ThisDirectory(self.output_base_name().parent().unwrap().to_path_buf());
|
||||
let output_file = TargetLocation::ThisDirectory(self.output_base_dir());
|
||||
let mut rustc = self.make_compile_args(&self.testpaths.file, output_file);
|
||||
rustc.arg("-L").arg(aux_dir).arg("--emit=llvm-ir");
|
||||
|
||||
|
@ -2048,7 +2027,7 @@ impl<'test> TestCx<'test> {
|
|||
fn run_rustdoc_test(&self) {
|
||||
assert!(self.revision.is_none(), "revisions not relevant here");
|
||||
|
||||
let out_dir = self.output_base_name_stage();
|
||||
let out_dir = self.output_base_dir();
|
||||
let _ = fs::remove_dir_all(&out_dir);
|
||||
create_dir_all(&out_dir).unwrap();
|
||||
|
||||
|
@ -2376,7 +2355,7 @@ impl<'test> TestCx<'test> {
|
|||
fn run_incremental_test(&self) {
|
||||
// Basic plan for a test incremental/foo/bar.rs:
|
||||
// - load list of revisions rpass1, cfail2, rpass3
|
||||
// - each should begin with `rpass`, `cfail`, or `cfail`
|
||||
// - each should begin with `rpass`, `cfail`, or `rfail`
|
||||
// - if `rpass`, expect compile and execution to succeed
|
||||
// - if `cfail`, expect compilation to fail
|
||||
// - if `rfail`, expect execution to fail
|
||||
|
@ -2417,8 +2396,7 @@ impl<'test> TestCx<'test> {
|
|||
if self.config.verbose {
|
||||
print!(
|
||||
"revision={:?} revision_props={:#?}",
|
||||
revision,
|
||||
revision_props
|
||||
revision, revision_props
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2450,7 +2428,7 @@ impl<'test> TestCx<'test> {
|
|||
.unwrap();
|
||||
let src_root = cwd.join(&src_root);
|
||||
|
||||
let tmpdir = cwd.join(self.output_base_name_stage());
|
||||
let tmpdir = cwd.join(self.output_base_name());
|
||||
if tmpdir.exists() {
|
||||
self.aggressive_rm_rf(&tmpdir).unwrap();
|
||||
}
|
||||
|
@ -2731,8 +2709,7 @@ impl<'test> TestCx<'test> {
|
|||
if source_time > output_time {
|
||||
debug!(
|
||||
"source file time: {:?} output file time: {:?}",
|
||||
source_time,
|
||||
output_time
|
||||
source_time, output_time
|
||||
);
|
||||
panic!(
|
||||
"test source file `{}` is newer than potentially stale output file `{}`.",
|
||||
|
@ -2906,10 +2883,12 @@ impl<'test> TestCx<'test> {
|
|||
}
|
||||
|
||||
fn expected_output_path(&self, kind: &str) -> PathBuf {
|
||||
let mut path = expected_output_path(&self.testpaths,
|
||||
self.revision,
|
||||
&self.config.compare_mode,
|
||||
kind);
|
||||
let mut path = expected_output_path(
|
||||
&self.testpaths,
|
||||
self.revision,
|
||||
&self.config.compare_mode,
|
||||
kind,
|
||||
);
|
||||
if !path.exists() && self.config.compare_mode.is_some() {
|
||||
// fallback!
|
||||
path = expected_output_path(&self.testpaths, self.revision, &None, kind);
|
||||
|
@ -2959,14 +2938,14 @@ impl<'test> TestCx<'test> {
|
|||
DiffLine::Expected(e) => {
|
||||
println!("-\t{}", e);
|
||||
line_number += 1;
|
||||
},
|
||||
}
|
||||
DiffLine::Context(c) => {
|
||||
println!("{}\t{}", line_number, c);
|
||||
line_number += 1;
|
||||
},
|
||||
}
|
||||
DiffLine::Resulting(r) => {
|
||||
println!("+\t{}", r);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("");
|
||||
|
@ -2993,6 +2972,11 @@ impl<'test> TestCx<'test> {
|
|||
println!("Actual {} saved to {}", kind, output_file.display());
|
||||
1
|
||||
}
|
||||
|
||||
fn create_stamp(&self) {
|
||||
let mut f = File::create(::stamp(&self.config, self.testpaths, self.revision)).unwrap();
|
||||
f.write_all(compute_stamp_hash(&self.config).as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcArgs {
|
||||
|
@ -3025,10 +3009,7 @@ impl ProcRes {
|
|||
{}\n\
|
||||
------------------------------------------\n\
|
||||
\n",
|
||||
self.status,
|
||||
self.cmdline,
|
||||
self.stdout,
|
||||
self.stderr
|
||||
self.status, self.cmdline, self.stdout, self.stderr
|
||||
);
|
||||
panic!();
|
||||
}
|
||||
|
@ -3072,8 +3053,8 @@ fn nocomment_mir_line(line: &str) -> &str {
|
|||
}
|
||||
|
||||
fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
|
||||
use std::mem::replace;
|
||||
use read2::read2;
|
||||
use std::mem::replace;
|
||||
|
||||
const HEAD_LEN: usize = 160 * 1024;
|
||||
const TAIL_LEN: usize = 256 * 1024;
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use common::Config;
|
||||
|
||||
/// Conversion table from triple OS name to Rust SYSNAME
|
||||
|
@ -73,7 +75,7 @@ pub fn matches_os(triple: &str, name: &str) -> bool {
|
|||
// For the wasm32 bare target we ignore anything also ignored on emscripten
|
||||
// and then we also recognize `wasm32-bare` as the os for the target
|
||||
if triple == "wasm32-unknown-unknown" {
|
||||
return name == "emscripten" || name == "wasm32-bare"
|
||||
return name == "emscripten" || name == "wasm32-bare";
|
||||
}
|
||||
let triple: Vec<_> = triple.split('-').collect();
|
||||
for &(triple_os, os) in OS_TABLE {
|
||||
|
@ -128,3 +130,23 @@ pub fn logv(config: &Config, s: String) {
|
|||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PathBufExt {
|
||||
/// Append an extension to the path, even if it already has one.
|
||||
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf;
|
||||
}
|
||||
|
||||
impl PathBufExt for PathBuf {
|
||||
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
|
||||
if extension.as_ref().len() == 0 {
|
||||
self.clone()
|
||||
} else {
|
||||
let mut fname = self.file_name().unwrap().to_os_string();
|
||||
if !extension.as_ref().to_str().unwrap().starts_with(".") {
|
||||
fname.push(".");
|
||||
}
|
||||
fname.push(extension);
|
||||
self.with_file_name(fname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue