Skip to content

WIP Update from upstream #201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ readme = "README.md"

[lib]
name = "compiletest_rs"
edition = "2018"

[dependencies]
diff = "0.1.10"
@@ -26,6 +27,8 @@ serde_json = "1.0"
serde_derive = "1.0"
rustfix = "0.4.1"
tester = "0.7"
lazy_static = "1.0"
walkdir = "2"

[target."cfg(unix)".dependencies]
libc = "0.2"
245 changes: 195 additions & 50 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub use self::Mode::*;

use std::env;
use std::ffi::OsString;
use std::fmt;
use std::fs::{read_dir, remove_file};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::path::PathBuf;
#[cfg(feature = "rustc")]
use rustc;

use test::ColorConfig;
use runtest::dylib_env_var;
use crate::util::PathBufExt;
use crate::runtest::dylib_env_var;

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Mode {
CompileFail,
ParseFail,
RunFail,
RunPass,
RunPassValgrind,
Pretty,
DebugInfoCdb,
DebugInfoGdbLldb,
DebugInfoGdb,
DebugInfoLldb,
Codegen,
@@ -36,16 +27,19 @@ pub enum Mode {
Incremental,
RunMake,
Ui,
JsDocTest,
MirOpt,
Assembly,
}

impl Mode {
pub fn disambiguator(self) -> &'static str {
// Run-pass and pretty run-pass tests could run concurrently, and if they do,
// Pretty-printing tests could run concurrently, and if they do,
// they need to keep their output segregated. Same is true for debuginfo tests that
// can be run both on gdb and lldb.
// can be run on cdb, gdb, and lldb.
match self {
Pretty => ".pretty",
DebugInfoCdb => ".cdb",
DebugInfoGdb => ".gdb",
DebugInfoLldb => ".lldb",
_ => "",
@@ -58,11 +52,11 @@ impl FromStr for Mode {
fn from_str(s: &str) -> Result<Mode, ()> {
match s {
"compile-fail" => Ok(CompileFail),
"parse-fail" => Ok(ParseFail),
"run-fail" => Ok(RunFail),
"run-pass" => Ok(RunPass),
"run-pass-valgrind" => Ok(RunPassValgrind),
"pretty" => Ok(Pretty),
"debuginfo-cdb" => Ok(DebugInfoCdb),
"debuginfo-gdb+lldb" => Ok(DebugInfoGdbLldb),
"debuginfo-lldb" => Ok(DebugInfoLldb),
"debuginfo-gdb" => Ok(DebugInfoGdb),
"codegen" => Ok(Codegen),
@@ -71,65 +65,135 @@ impl FromStr for Mode {
"incremental" => Ok(Incremental),
"run-make" => Ok(RunMake),
"ui" => Ok(Ui),
"js-doc-test" => Ok(JsDocTest),
"mir-opt" => Ok(MirOpt),
"assembly" => Ok(Assembly),
_ => Err(()),
}
}
}

impl fmt::Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(match *self {
CompileFail => "compile-fail",
ParseFail => "parse-fail",
RunFail => "run-fail",
RunPass => "run-pass",
RunPassValgrind => "run-pass-valgrind",
Pretty => "pretty",
DebugInfoGdb => "debuginfo-gdb",
DebugInfoLldb => "debuginfo-lldb",
Codegen => "codegen",
Rustdoc => "rustdoc",
CodegenUnits => "codegen-units",
Incremental => "incremental",
RunMake => "run-make",
Ui => "ui",
MirOpt => "mir-opt",
},
f)
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match *self {
CompileFail => "compile-fail",
RunFail => "run-fail",
RunPassValgrind => "run-pass-valgrind",
Pretty => "pretty",
DebugInfoCdb => "debuginfo-cdb",
DebugInfoGdbLldb => "debuginfo-gdb+lldb",
DebugInfoGdb => "debuginfo-gdb",
DebugInfoLldb => "debuginfo-lldb",
Codegen => "codegen",
Rustdoc => "rustdoc",
CodegenUnits => "codegen-units",
Incremental => "incremental",
RunMake => "run-make",
Ui => "ui",
JsDocTest => "js-doc-test",
MirOpt => "mir-opt",
Assembly => "assembly",
};
fmt::Display::fmt(s, f)
}
}

#[derive(Clone, Copy, PartialEq, Debug, Hash)]
pub enum PassMode {
Check,
Build,
Run,
RunFail,
}

impl FromStr for PassMode {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
match s {
"check" => Ok(PassMode::Check),
"build" => Ok(PassMode::Build),
"run" => Ok(PassMode::Run),
_ => Err(()),
}
}
}

impl fmt::Display for PassMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match *self {
PassMode::Check => "check",
PassMode::Build => "build",
PassMode::Run => "run",
PassMode::RunFail => "run-fail",
};
fmt::Display::fmt(s, f)
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum CompareMode {
Nll,
Polonius,
}

impl CompareMode {
pub(crate) fn to_str(&self) -> &'static str {
match *self {
CompareMode::Nll => "nll",
CompareMode::Polonius => "polonius",
}
}

pub fn parse(s: String) -> CompareMode {
match s.as_str() {
"nll" => CompareMode::Nll,
"polonius" => CompareMode::Polonius,
x => panic!("unknown --compare-mode option: {}", x),
}
}
}

/// Configuration for compiletest
#[derive(Clone)]
pub struct Config {
/// The library paths required for running the compiler
/// `true` to to overwrite stderr/stdout files instead of complaining about changes in output.
pub bless: bool,

/// The library paths required for running the compiler.
pub compile_lib_path: PathBuf,

/// The library paths required for running compiled programs
/// The library paths required for running compiled programs.
pub run_lib_path: PathBuf,

/// The rustc executable
/// The rustc executable.
pub rustc_path: PathBuf,

/// The rustdoc executable
/// The rustdoc executable.
pub rustdoc_path: Option<PathBuf>,

/// The python executable to use for LLDB
/// The Python executable to use for LLDB.
pub lldb_python: String,

/// The python executable to use for htmldocck
/// The Python executable to use for htmldocck.
pub docck_python: String,

/// The llvm FileCheck binary path
/// The LLVM `FileCheck` binary path.
pub llvm_filecheck: Option<PathBuf>,

/// The valgrind path
/// Path to LLVM's bin directory.
pub llvm_bin_dir: Option<PathBuf>,

/// The valgrind path.
pub valgrind_path: Option<String>,

/// Whether to fail if we can't run run-pass-valgrind tests under valgrind
/// (or, alternatively, to silently run them like regular run-pass tests).
pub force_valgrind: bool,

/// The path to the Clang executable to run Clang-based tests with. If
/// `None` then these tests will be ignored.
pub run_clang_based_tests_with: Option<String>,

/// The directory containing the tests to run
pub src_base: PathBuf,

@@ -139,7 +203,7 @@ pub struct Config {
/// The name of the stage being built (stage1, etc)
pub stage_id: String,

/// The test mode, compile-fail, run-fail, run-pass
/// The test mode, compile-fail, run-fail, ui
pub mode: Mode,

/// Run ignored tests
@@ -151,6 +215,9 @@ pub struct Config {
/// Exactly match the filter, rather than a substring
pub filter_exact: bool,

/// Force the pass mode of a check/build/run-pass test to this mode.
pub force_pass_mode: Option<PassMode>,

/// Write out a parseable log of tests that were run
pub logfile: Option<PathBuf>,

@@ -170,6 +237,9 @@ pub struct Config {
/// Host triple for the compiler being invoked
pub host: String,

/// Path to / name of the Microsoft Console Debugger (CDB) executable
pub cdb: Option<OsString>,

/// Path to / name of the GDB executable
pub gdb: Option<String>,

@@ -182,6 +252,9 @@ pub struct Config {
/// Version of LLDB
pub lldb_version: Option<String>,

/// Whether LLDB has native rust support
pub lldb_native_rust: bool,

/// Version of LLVM
pub llvm_version: Option<String>,

@@ -215,6 +288,9 @@ pub struct Config {
/// where to find the remote test client process, if we're using it
pub remote_test_client: Option<PathBuf>,

/// mode describing what file the actual ui output will be compared to
pub compare_mode: Option<CompareMode>,

/// If true, this will generate a coverage file with UI test files that run `MachineApplicable`
/// diagnostics but are missing `run-rustfix` annotations. The generated coverage file is
/// created in `/<build_base>/rustfix_missing_coverage.txt`
@@ -229,19 +305,74 @@ pub struct Config {
pub linker: Option<String>,
pub llvm_components: String,
pub llvm_cxxflags: String,

/// Path to a NodeJS executable. Used for JS doctests, emscripten and WASM tests
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 {
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());
}
parts.push(kind);

let extension = parts.join(".");
testpaths.file.with_extension(extension)
}

pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT];
pub const UI_STDERR: &str = "stderr";
pub const UI_STDOUT: &str = "stdout";
pub const UI_FIXED: &str = "fixed";
pub const UI_RUN_STDERR: &str = "run.stderr";
pub const UI_RUN_STDOUT: &str = "run.stdout";

/// 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))
}


impl Config {
/// Add rustc flags to link with the crate's dependencies in addition to the crate itself
@@ -347,7 +478,7 @@ impl Default for Config {
src_base: PathBuf::from("tests/run-pass"),
build_base: env::temp_dir(),
stage_id: "stage-id".to_owned(),
mode: Mode::RunPass,
mode: Mode::Ui,
run_ignored: false,
filter: None,
filter_exact: false,
@@ -387,6 +518,20 @@ impl Default for Config {
llvm_components: "llvm-components".to_string(),
llvm_cxxflags: "llvm-cxxflags".to_string(),
nodejs: None,
bless: false,
cdb: None,
compare_mode: None,
force_pass_mode: None,
lldb_native_rust: false,
llvm_bin_dir: None,
run_clang_based_tests_with: None,
}
}
}

/// 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())
}
90 changes: 46 additions & 44 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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;

use log::*;

#[derive(Clone, Debug, PartialEq)]
pub enum ErrorKind {
Help,
@@ -35,15 +28,14 @@ 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(()),
}
}
}

impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ErrorKind::Help => write!(f, "help message"),
ErrorKind::Error => write!(f, "error"),
@@ -57,7 +49,7 @@ impl fmt::Display for ErrorKind {
#[derive(Debug)]
pub struct Error {
pub line_num: usize,
/// What kind of message we expect (e.g. warning, error, suggestion).
/// What kind of message we expect (e.g., warning, error, suggestion).
/// `None` if not specified or unknown message kind.
pub kind: Option<ErrorKind>,
pub msg: String,
@@ -101,61 +93,71 @@ 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)> {
let start = match line.find(tag) {
Some(i) => i,
None => return None,
};
fn parse_expected(
last_nonfollow_error: Option<usize>,
line_num: usize,
line: &str,
tag: &str,
) -> Option<(WhichLine, Error)> {
let start = line.find(tag)?;
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);
match line[kind_start..]
.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>();
}
Err(_) => {
// 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>();
}
}
let msg = msg.trim().to_owned();

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 +169,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,
},
))
}
555 changes: 440 additions & 115 deletions src/header.rs

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions src/header/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::*;

#[test]
fn test_parse_normalization_string() {
let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\".";
let first = parse_normalization_string(&mut s);
assert_eq!(first, Some("something (32 bits)".to_owned()));
assert_eq!(s, " -> \"something ($WORD bits)\".");

// Nothing to normalize (No quotes)
let mut s = "normalize-stderr-32bit: something (32 bits) -> something ($WORD bits).";
let first = parse_normalization_string(&mut s);
assert_eq!(first, None);
assert_eq!(s, r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."#);

// Nothing to normalize (Only a single quote)
let mut s = "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits).";
let first = parse_normalization_string(&mut s);
assert_eq!(first, None);
assert_eq!(s, "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits).");

// Nothing to normalize (Three quotes)
let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits).";
let first = parse_normalization_string(&mut s);
assert_eq!(first, Some("something (32 bits)".to_owned()));
assert_eq!(s, " -> \"something ($WORD bits).");
}
86 changes: 46 additions & 40 deletions src/json.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! These structs are a subset of the ones found in `rustc_errors::json`.
//! They are only used for deserialization of JSON output provided by libtest.
use errors::{Error, ErrorKind};
use crate::errors::{Error, ErrorKind};
use crate::runtest::ProcRes;
use serde::Deserialize;
use serde_json;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::path::Path;
use runtest::ProcRes;

// These structs are a subset of the ones found in
// `syntax::json`.

#[derive(Deserialize)]
struct Diagnostic {
@@ -27,6 +18,12 @@ struct Diagnostic {
rendered: Option<String>,
}

#[derive(Deserialize)]
struct ArtifactNotification {
#[allow(dead_code)]
artifact: PathBuf,
}

#[derive(Deserialize, Clone)]
struct DiagnosticSpan {
file_name: String,
@@ -72,20 +69,22 @@ struct DiagnosticCode {
explanation: Option<String>,
}

pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String {
pub fn extract_rendered(output: &str) -> 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: \
`{}`\nline: {}\noutput: {}",
error, line, output
)));
}
if let Ok(diagnostic) = serde_json::from_str::<Diagnostic>(line) {
diagnostic.rendered
} else if let Ok(_) = serde_json::from_str::<ArtifactNotification>(line) {
// Ignore the notification.
None
} else {
print!(
"failed to decode compiler output as json: line: {}\noutput: {}",
line, output
);
panic!()
}
} else {
// preserve non-JSON lines, such as ICEs
@@ -96,7 +95,8 @@ pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String {
}

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()
}
@@ -112,22 +112,24 @@ 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: \
`{}`\nline: {}\noutput: {}",
error, line, output
)));
}
}
} else {
vec![]
}
}

fn push_expected_errors(expected_errors: &mut Vec<Error>,
diagnostic: &Diagnostic,
default_spans: &[&DiagnosticSpan],
file_name: &str) {
fn push_expected_errors(
expected_errors: &mut Vec<Error>,
diagnostic: &Diagnostic,
default_spans: &[&DiagnosticSpan],
file_name: &str,
) {
// In case of macro expansions, we need to get the span of the callsite
let spans_info_in_this_file: Vec<_> = diagnostic
.spans
@@ -231,8 +233,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),
@@ -246,9 +250,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,
673 changes: 539 additions & 134 deletions src/lib.rs

Large diffs are not rendered by default.

42 changes: 18 additions & 24 deletions src/raise_fd_limit.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/// darwin_fd_limit exists to work around an issue where launchctl on macOS
/// defaults the rlimit maxfiles to 256/unlimited. The default soft limit of 256
/// ends up being far too low for our multithreaded scheduler testing, depending
@@ -17,7 +7,6 @@
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(non_camel_case_types)]
pub unsafe fn raise_fd_limit() {
use libc;
use std::cmp;
use std::io;
use std::mem::size_of_val;
@@ -34,12 +23,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);
}
@@ -54,14 +46,16 @@ pub unsafe fn raise_fd_limit() {
panic!("raise_fd_limit: error calling getrlimit: {}", err);
}

// Bump the soft limit to the smaller of kern.maxfilesperproc and the hard
// limit
rlim.rlim_cur = cmp::min(maxfiles as libc::rlim_t, rlim.rlim_max);
// Make sure we're only ever going to increase the rlimit.
if rlim.rlim_cur < maxfiles as libc::rlim_t {
// Bump the soft limit to the smaller of kern.maxfilesperproc and the hard limit.
rlim.rlim_cur = cmp::min(maxfiles as libc::rlim_t, rlim.rlim_max);

// Set our newly-increased resource limit
if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) != 0 {
let err = io::Error::last_os_error();
panic!("raise_fd_limit: error calling setrlimit: {}", err);
// Set our newly-increased resource limit.
if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) != 0 {
let err = io::Error::last_os_error();
panic!("raise_fd_limit: error calling setrlimit: {}", err);
}
}
}

105 changes: 51 additions & 54 deletions src/read2.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs`
// Consider unify the read2() in libstd, cargo and this to prevent further code duplication.

@@ -16,11 +6,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 dyn 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 +25,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 dyn FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
pub fn read2(
mut out_pipe: ChildStdout,
mut err_pipe: ChildStderr,
data: &mut dyn 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);
@@ -58,64 +52,63 @@ mod imp {
fds[0].events = libc::POLLIN;
fds[1].fd = err_pipe.as_raw_fd();
fds[1].events = libc::POLLIN;
loop {
let mut nfds = 2;
let mut errfd = 1;

while nfds > 0 {
// wait for either pipe to become readable using `select`
let r = unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) };
let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) };
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
// EAGAIN. If we hit EOF, then this will happen because the underlying
// 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)
}
}
};
if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
out_done = true;
}
data(true, &mut out, out_done);
if !err_done && fds[1].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
err_done = true;
nfds -= 1;
}
data(false, &mut err, err_done);

if out_done && err_done {
return Ok(())
if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
out_done = true;
fds[0].fd = err_pipe.as_raw_fd();
errfd = 0;
nfds -= 1;
}
data(true, &mut out, out_done);
}
Ok(())
}
}

#[cfg(windows)]
mod imp {
extern crate miow;
extern crate winapi;

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};
use self::miow::pipe::NamedPipe;
use self::miow::Overlapped;
use self::winapi::shared::winerror::ERROR_BROKEN_PIPE;
use miow::iocp::{CompletionPort, CompletionStatus};
use miow::pipe::NamedPipe;
use miow::Overlapped;
use winapi::shared::winerror::ERROR_BROKEN_PIPE;

struct Pipe<'a> {
dst: &'a mut Vec<u8>,
@@ -124,9 +117,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 dyn FnMut(bool, &mut Vec<u8>, bool),
) -> io::Result<()> {
let mut out = Vec::new();
let mut err = Vec::new();

@@ -202,7 +197,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(),
)
}
}
3,142 changes: 2,091 additions & 1,051 deletions src/runtest.rs

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions src/runtest/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use super::*;

#[test]
fn normalize_platform_differences() {
assert_eq!(
TestCx::normalize_platform_differences(r"$DIR\foo.rs"),
"$DIR/foo.rs"
);
assert_eq!(
TestCx::normalize_platform_differences(r"$BUILD_DIR\..\parser.rs"),
"$BUILD_DIR/../parser.rs"
);
assert_eq!(
TestCx::normalize_platform_differences(r"$DIR\bar.rs hello\nworld"),
r"$DIR/bar.rs hello\nworld"
);
assert_eq!(
TestCx::normalize_platform_differences(r"either bar\baz.rs or bar\baz\mod.rs"),
r"either bar/baz.rs or bar/baz/mod.rs",
);
assert_eq!(
TestCx::normalize_platform_differences(r"`.\some\path.rs`"),
r"`./some/path.rs`",
);
assert_eq!(
TestCx::normalize_platform_differences(r"`some\path.rs`"),
r"`some/path.rs`",
);
assert_eq!(
TestCx::normalize_platform_differences(r"$DIR\path-with-dashes.rs"),
r"$DIR/path-with-dashes.rs"
);
assert_eq!(
TestCx::normalize_platform_differences(r"$DIR\path_with_underscores.rs"),
r"$DIR/path_with_underscores.rs",
);
assert_eq!(
TestCx::normalize_platform_differences(r"$DIR\foo.rs:12:11"), "$DIR/foo.rs:12:11",
);
assert_eq!(
TestCx::normalize_platform_differences(r"$DIR\path with spaces 'n' quotes"),
"$DIR/path with spaces 'n' quotes",
);
assert_eq!(
TestCx::normalize_platform_differences(r"$DIR\file_with\no_extension"),
"$DIR/file_with/no_extension",
);

assert_eq!(TestCx::normalize_platform_differences(r"\n"), r"\n");
assert_eq!(TestCx::normalize_platform_differences(r"{ \n"), r"{ \n");
assert_eq!(TestCx::normalize_platform_differences(r"`\]`"), r"`\]`");
assert_eq!(TestCx::normalize_platform_differences(r#""\{""#), r#""\{""#);
assert_eq!(
TestCx::normalize_platform_differences(r#"write!(&mut v, "Hello\n")"#),
r#"write!(&mut v, "Hello\n")"#
);
assert_eq!(
TestCx::normalize_platform_differences(r#"println!("test\ntest")"#),
r#"println!("test\ntest")"#,
);
}
51 changes: 51 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use super::*;

#[test]
fn test_extract_gdb_version() {
macro_rules! test { ($($expectation:tt: $input:tt,)*) => {{$(
assert_eq!(extract_gdb_version($input), Some($expectation));
)*}}}

test! {
7000001: "GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)",

7002000: "GNU gdb (GDB) Red Hat Enterprise Linux (7.2-90.el6)",

7004000: "GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04",
7004001: "GNU gdb (GDB) 7.4.1-debian",

7006001: "GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7",

7007001: "GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1",
7007001: "GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1",
7007001: "GNU gdb (GDB) Fedora 7.7.1-21.fc20",

7008000: "GNU gdb (GDB; openSUSE 13.2) 7.8",
7009001: "GNU gdb (GDB) Fedora 7.9.1-20.fc22",
7010001: "GNU gdb (GDB) Fedora 7.10.1-31.fc23",

7011000: "GNU gdb (Ubuntu 7.11-0ubuntu1) 7.11",
7011001: "GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1",
7011001: "GNU gdb (Debian 7.11.1-2) 7.11.1",
7011001: "GNU gdb (GDB) Fedora 7.11.1-86.fc24",
7011001: "GNU gdb (GDB; openSUSE Leap 42.1) 7.11.1",
7011001: "GNU gdb (GDB; openSUSE Tumbleweed) 7.11.1",

7011090: "7.11.90",
7011090: "GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1) 7.11.90.20161005-git",

7012000: "7.12",
7012000: "GNU gdb (GDB) 7.12",
7012000: "GNU gdb (GDB) 7.12.20161027-git",
7012050: "GNU gdb (GDB) 7.12.50.20161027-git",
}
}

#[test]
fn is_test_test() {
assert_eq!(true, is_test(&OsString::from("a_test.rs")));
assert_eq!(false, is_test(&OsString::from(".a_test.rs")));
assert_eq!(false, is_test(&OsString::from("a_cat.gif")));
assert_eq!(false, is_test(&OsString::from("#a_dog_gif")));
assert_eq!(false, is_test(&OsString::from("~a_temp_file")));
}
106 changes: 85 additions & 21 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,80 +1,116 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::OsStr;
use std::env;
use common::Config;
use std::path::PathBuf;
use crate::common::Config;

use log::*;

#[cfg(test)]
mod tests;

/// Conversion table from triple OS name to Rust SYSNAME
const OS_TABLE: &'static [(&'static str, &'static str)] = &[
("android", "android"),
("bitrig", "bitrig"),
("androideabi", "android"),
("cloudabi", "cloudabi"),
("cuda", "cuda"),
("darwin", "macos"),
("dragonfly", "dragonfly"),
("emscripten", "emscripten"),
("freebsd", "freebsd"),
("fuchsia", "fuchsia"),
("haiku", "haiku"),
("hermit", "hermit"),
("ios", "ios"),
("l4re", "l4re"),
("linux", "linux"),
("mingw32", "windows"),
("none", "none"),
("netbsd", "netbsd"),
("openbsd", "openbsd"),
("redox", "redox"),
("sgx", "sgx"),
("solaris", "solaris"),
("win32", "windows"),
("windows", "windows"),
("solaris", "solaris"),
("emscripten", "emscripten"),
("vxworks", "vxworks"),
];

const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[
("aarch64", "aarch64"),
("amd64", "x86_64"),
("arm", "arm"),
("arm64", "aarch64"),
("armv4t", "arm"),
("armv5te", "arm"),
("armv7", "arm"),
("armv7s", "arm"),
("asmjs", "asmjs"),
("hexagon", "hexagon"),
("i386", "x86"),
("i586", "x86"),
("i686", "x86"),
("mips", "mips"),
("mips64", "mips64"),
("mips64el", "mips64"),
("mipsisa32r6", "mips"),
("mipsisa32r6el", "mips"),
("mipsisa64r6", "mips64"),
("mipsisa64r6el", "mips64"),
("mipsel", "mips"),
("mipsisa32r6", "mips"),
("mipsisa32r6el", "mips"),
("mipsisa64r6", "mips64"),
("mipsisa64r6el", "mips64"),
("msp430", "msp430"),
("nvptx64", "nvptx64"),
("powerpc", "powerpc"),
("powerpc64", "powerpc64"),
("powerpc64le", "powerpc64"),
("s390x", "s390x"),
("sparc", "sparc"),
("sparc64", "sparc64"),
("sparcv9", "sparc64"),
("thumbv6m", "thumb"),
("thumbv7em", "thumb"),
("thumbv7m", "thumb"),
("wasm32", "wasm32"),
("x86_64", "x86_64"),
("xcore", "xcore"),
("asmjs", "asmjs"),
("wasm32", "wasm32"),
];

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 {
if triple.contains(triple_os) {
if triple.contains(&triple_os) {
return os == name;
}
}
panic!("Cannot determine OS from triple");
}

/// Determine the architecture from `triple`
pub fn get_arch(triple: &str) -> &'static str {
let triple: Vec<_> = triple.split('-').collect();
for &(triple_arch, arch) in ARCH_TABLE {
if triple.contains(triple_arch) {
if triple.contains(&triple_arch) {
return arch;
}
}
panic!("Cannot determine Architecture from triple");
}

pub fn get_env(triple: &str) -> Option<&str> {
triple.split('-').nth(3)
pub fn matches_env(triple: &str, name: &str) -> bool {
if let Some(env) = triple.split('-').nth(3) {
env.starts_with(name)
} else {
false
}
}

pub fn get_pointer_width(triple: &str) -> &'static str {
@@ -108,3 +144,31 @@ 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)
}
}
}

pub fn remove_item<T: PartialEq>(v: &mut Vec<T>, item: &T) -> Option<T> {
let pos = match v.iter().position(|x| *x == *item) {
Some(x) => x,
None => return None,
};
Some(v.remove(pos))
}
32 changes: 32 additions & 0 deletions src/util/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use super::*;

#[test]
#[should_panic(expected = "Cannot determine Architecture from triple")]
fn test_get_arch_failure() {
get_arch("abc");
}

#[test]
fn test_get_arch() {
assert_eq!("x86_64", get_arch("x86_64-unknown-linux-gnu"));
assert_eq!("x86_64", get_arch("amd64"));
assert_eq!("nvptx64", get_arch("nvptx64-nvidia-cuda"));
}

#[test]
#[should_panic(expected = "Cannot determine OS from triple")]
fn test_matches_os_failure() {
matches_os("abc", "abc");
}

#[test]
fn test_matches_os() {
assert!(matches_os("x86_64-unknown-linux-gnu", "linux"));
assert!(matches_os("wasm32-unknown-unknown", "emscripten"));
assert!(matches_os("wasm32-unknown-unknown", "wasm32-bare"));
assert!(!matches_os("wasm32-unknown-unknown", "windows"));
assert!(matches_os("thumbv6m0-none-eabi", "none"));
assert!(matches_os("riscv32imc-unknown-none-elf", "none"));
assert!(matches_os("nvptx64-nvidia-cuda", "cuda"));
assert!(matches_os("x86_64-fortanix-unknown-sgx", "sgx"));
}