Skip to content
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

Upstream merge #87

Merged
merged 1 commit into from
Nov 23, 2017
Merged
Show file tree
Hide file tree
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
18 changes: 18 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ pub enum Mode {
MirOpt,
}

impl Mode {
pub fn disambiguator(self) -> &'static str {
// Run-pass and pretty run-pass 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.
match self {
Pretty => ".pretty",
DebugInfoGdb => ".gdb",
DebugInfoLldb => ".lldb",
_ => "",
}
}
}

impl FromStr for Mode {
type Err = ();
fn from_str(s: &str) -> Result<Mode, ()> {
Expand Down Expand Up @@ -204,6 +218,8 @@ pub struct Config {
pub cc: String,
pub cxx: String,
pub cflags: String,
pub ar: String,
pub linker: Option<String>,
pub llvm_components: String,
pub llvm_cxxflags: String,
pub nodejs: Option<String>,
Expand Down Expand Up @@ -314,6 +330,8 @@ impl Default for Config {
cc: "cc".to_string(),
cxx: "cxx".to_string(),
cflags: "cflags".to_string(),
ar: "ar".to_string(),
linker: None,
llvm_components: "llvm-components".to_string(),
llvm_cxxflags: "llvm-cxxflags".to_string(),
nodejs: None,
Expand Down
2 changes: 1 addition & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl FromStr for ErrorKind {
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ErrorKind::Help => write!(f, "help"),
ErrorKind::Help => write!(f, "help message"),
ErrorKind::Error => write!(f, "error"),
ErrorKind::Note => write!(f, "note"),
ErrorKind::Suggestion => write!(f, "suggestion"),
Expand Down
35 changes: 28 additions & 7 deletions src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ impl EarlyProps {
// Ignore if actual version is smaller the minimum required
// version
&actual_version[..] < min_version
} else if line.starts_with("min-system-llvm-version") {
let min_version = line.trim_right()
.rsplit(' ')
.next()
.expect("Malformed llvm version directive");
// Ignore if using system LLVM and actual version
// is smaller the minimum required version
!(config.system_llvm && &actual_version[..] < min_version)
} else {
false
}
Expand Down Expand Up @@ -259,20 +267,20 @@ impl TestProps {
props
}

pub fn from_file(testfile: &Path, config: &Config) -> Self {
pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
let mut props = TestProps::new();
props.load_from(testfile, None, config);
props.load_from(testfile, cfg, config);
props
}

/// Load properties from `testfile` into `props`. If a property is
/// tied to a particular revision `foo` (indicated by writing
/// `//[foo]`), then the property is ignored unless `cfg` is
/// `Some("foo")`.
pub fn load_from(&mut self,
testfile: &Path,
cfg: Option<&str>,
config: &Config) {
fn load_from(&mut self,
testfile: &Path,
cfg: Option<&str>,
config: &Config) {
iter_header(testfile,
cfg,
&mut |ln| {
Expand Down Expand Up @@ -531,7 +539,7 @@ impl Config {
let name = line[prefix.len()+1 ..].split(&[':', ' '][..]).next().unwrap();

name == "test" ||
name == util::get_os(&self.target) || // target
util::matches_os(&self.target, name) || // target
name == util::get_arch(&self.target) || // architecture
name == util::get_pointer_width(&self.target) || // pointer width
name == self.stage_id.split('-').next().unwrap() || // stage
Expand Down Expand Up @@ -567,6 +575,19 @@ impl Config {
None
}
}

pub fn find_rust_src_root(&self) -> Option<PathBuf> {
let mut path = self.src_base.clone();
let path_postfix = Path::new("src/etc/lldb_batchmode.py");

while path.pop() {
if path.join(&path_postfix).is_file() {
return Some(path);
}
}

None
}
}

pub fn lldb_version_to_int(version_string: &str) -> isize {
Expand Down
19 changes: 10 additions & 9 deletions src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct DiagnosticSpan {
column_end: usize,
is_primary: bool,
label: Option<String>,
suggested_replacement: Option<String>,
expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
}

Expand Down Expand Up @@ -164,15 +165,15 @@ fn push_expected_errors(expected_errors: &mut Vec<Error>,
}

// If the message has a suggestion, register that.
if let Some(ref rendered) = diagnostic.rendered {
let start_line = primary_spans.iter().map(|s| s.line_start).min().expect("\
every suggestion should have at least one span");
for (index, line) in rendered.lines().enumerate() {
expected_errors.push(Error {
line_num: start_line + index,
kind: Some(ErrorKind::Suggestion),
msg: line.to_string(),
});
for span in primary_spans {
if let Some(ref suggested_replacement) = span.suggested_replacement {
for (index, line) in suggested_replacement.lines().enumerate() {
expected_errors.push(Error {
line_num: span.line_start + index,
kind: Some(ErrorKind::Suggestion),
msg: line.to_string(),
});
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@

#![feature(rustc_private)]
#![feature(test)]
#![feature(slice_rotate)]

#![deny(unused_imports)]

#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(unix)]
extern crate libc;
extern crate test;
extern crate rustc;
Expand All @@ -40,12 +41,13 @@ use test::TestPaths;
use self::header::EarlyProps;

pub mod uidiff;
pub mod json;
pub mod util;
mod json;
pub mod header;
pub mod runtest;
pub mod common;
pub mod errors;
mod read2;

pub use common::Config;

Expand Down
208 changes: 208 additions & 0 deletions src/read2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// 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.

pub use self::imp::read2;

#[cfg(not(any(unix, windows)))]
mod imp {
use std::io::{self, Read};
use std::process::{ChildStdout, ChildStderr};

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);
buffer.clear();
err_pipe.read_to_end(&mut buffer)?;
data(false, &mut buffer, true);
Ok(())
}
}

#[cfg(unix)]
mod imp {
use std::io::prelude::*;
use std::io;
use std::mem;
use std::os::unix::prelude::*;
use std::process::{ChildStdout, ChildStderr};
use libc;

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

let mut out_done = false;
let mut err_done = false;
let mut out = Vec::new();
let mut err = Vec::new();

let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
fds[0].fd = out_pipe.as_raw_fd();
fds[0].events = libc::POLLIN;
fds[1].fd = err_pipe.as_raw_fd();
fds[1].events = libc::POLLIN;
loop {
// wait for either pipe to become readable using `select`
let r = unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) };
if r == -1 {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::Interrupted {
continue
}
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)
}
}
}
};
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))? {
err_done = true;
}
data(false, &mut err, err_done);

if out_done && err_done {
return 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::slice;

use self::miow::iocp::{CompletionPort, CompletionStatus};
use self::miow::pipe::NamedPipe;
use self::miow::Overlapped;
use self::winapi::ERROR_BROKEN_PIPE;

struct Pipe<'a> {
dst: &'a mut Vec<u8>,
overlapped: Overlapped,
pipe: NamedPipe,
done: bool,
}

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();

let port = CompletionPort::new(1)?;
port.add_handle(0, &out_pipe)?;
port.add_handle(1, &err_pipe)?;

unsafe {
let mut out_pipe = Pipe::new(out_pipe, &mut out);
let mut err_pipe = Pipe::new(err_pipe, &mut err);

out_pipe.read()?;
err_pipe.read()?;

let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];

while !out_pipe.done || !err_pipe.done {
for status in port.get_many(&mut status, None)? {
if status.token() == 0 {
out_pipe.complete(status);
data(true, out_pipe.dst, out_pipe.done);
out_pipe.read()?;
} else {
err_pipe.complete(status);
data(false, err_pipe.dst, err_pipe.done);
err_pipe.read()?;
}
}
}

Ok(())
}
}

impl<'a> Pipe<'a> {
unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
Pipe {
dst: dst,
pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
overlapped: Overlapped::zero(),
done: false,
}
}

unsafe fn read(&mut self) -> io::Result<()> {
let dst = slice_to_end(self.dst);
match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
Ok(_) => Ok(()),
Err(e) => {
if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
self.done = true;
Ok(())
} else {
Err(e)
}
}
}
}

unsafe fn complete(&mut self, status: &CompletionStatus) {
let prev = self.dst.len();
self.dst.set_len(prev + status.bytes_transferred() as usize);
if status.bytes_transferred() == 0 {
self.done = true;
}
}
}

unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
if v.capacity() == 0 {
v.reserve(16);
}
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())
}
}
Loading