Skip to content

Commit

Permalink
Merge pull request #210 from oli-obk/librarification
Browse files Browse the repository at this point in the history
Lots of cleanups
  • Loading branch information
oli-obk authored Mar 28, 2024
2 parents df8246c + 245b589 commit 2288aed
Show file tree
Hide file tree
Showing 11 changed files with 1,042 additions and 1,111 deletions.
64 changes: 60 additions & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use regex::bytes::Regex;
use spanned::{Span, Spanned};

use crate::{
dependencies::build_dependencies, per_test_config::Comments, CommandBuilder, Match, Mode,
RustfixMode,
dependencies::build_dependencies,
filter::Match,
per_test_config::{Comments, Condition},
CommandBuilder, Mode, RustfixMode,
};
pub use color_eyre;
use color_eyre::eyre::Result;
Expand Down Expand Up @@ -259,8 +261,12 @@ impl Config {
}

/// Check whether the host is the specified string
pub fn host_matches(&self, target: &str) -> bool {
self.host.as_ref().expect("host should have been filled in") == target
pub fn host_matches_target(&self) -> bool {
self.host.as_ref().expect("host should have been filled in")
== self
.target
.as_ref()
.expect("target should have been filled in")
}

pub(crate) fn has_asm_support(&self) -> bool {
Expand All @@ -274,6 +280,56 @@ impl Config {
.iter()
.any(|arch| self.target.as_ref().unwrap().contains(arch))
}

pub(crate) fn get_pointer_width(&self) -> u8 {
// Taken 1:1 from compiletest-rs
fn get_pointer_width(triple: &str) -> u8 {
if (triple.contains("64")
&& !triple.ends_with("gnux32")
&& !triple.ends_with("gnu_ilp32"))
|| triple.starts_with("s390x")
{
64
} else if triple.starts_with("avr") {
16
} else {
32
}
}
get_pointer_width(self.target.as_ref().unwrap())
}

pub(crate) fn test_condition(&self, condition: &Condition) -> bool {
let target = self.target.as_ref().unwrap();
match condition {
Condition::Bitwidth(bits) => self.get_pointer_width() == *bits,
Condition::Target(t) => target.contains(t),
Condition::Host(t) => self.host.as_ref().unwrap().contains(t),
Condition::OnHost => self.host_matches_target(),
}
}

/// Returns whether according to the in-file conditions, this file should be run.
pub fn test_file_conditions(&self, comments: &Comments, revision: &str) -> bool {
if comments
.for_revision(revision)
.flat_map(|r| r.ignore.iter())
.any(|c| self.test_condition(c))
{
return self.run_only_ignored;
}
if comments
.for_revision(revision)
.any(|r| r.needs_asm_support && !self.has_asm_support())
{
return self.run_only_ignored;
}
comments
.for_revision(revision)
.flat_map(|r| r.only.iter())
.all(|c| self.test_condition(c))
^ self.run_only_ignored
}
}

#[derive(Debug, Clone)]
Expand Down
114 changes: 110 additions & 4 deletions src/dependencies.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
use bstr::ByteSlice;
use cargo_metadata::{camino::Utf8PathBuf, DependencyKind};
use cargo_platform::Cfg;
use color_eyre::eyre::{bail, eyre, Result};
use std::{
collections::{hash_map::Entry, HashMap, HashSet},
ffi::OsString,
path::PathBuf,
path::{Path, PathBuf},
process::Command,
str::FromStr,
sync::{Arc, OnceLock, RwLock},
};

use crate::{
build_aux, status_emitter::StatusEmitter, Config, Errored, Mode, OutputConflictHandling,
crate_type, default_per_file_config,
per_test_config::{Comments, TestConfig},
rustc_stderr,
status_emitter::StatusEmitter,
test_result::Errored,
Config, CrateType, Error, Mode, OutputConflictHandling,
};

#[derive(Default, Debug)]
Expand Down Expand Up @@ -290,7 +296,7 @@ impl<'a> BuildManager<'a> {
Err(())
}
},
Build::Aux { aux_file } => match build_aux(aux_file, config, self) {
Build::Aux { aux_file } => match self.build_aux(aux_file, config) {
Ok(args) => Ok(args.iter().map(Into::into).collect()),
Err(e) => {
err = Some(e);
Expand All @@ -300,7 +306,7 @@ impl<'a> BuildManager<'a> {
};
build.done(
&res.as_ref()
.map(|_| crate::TestOk::Ok)
.map(|_| crate::test_result::TestOk::Ok)
.map_err(|()| Errored {
command: Command::new(what.description()),
errors: vec![],
Expand All @@ -320,4 +326,104 @@ impl<'a> BuildManager<'a> {
})
})
}

fn build_aux(
&self,
aux_file: &Path,
config: &Config,
) -> std::result::Result<Vec<OsString>, Errored> {
let file_contents = std::fs::read(aux_file).map_err(|err| Errored {
command: Command::new(format!("reading aux file `{}`", aux_file.display())),
errors: vec![],
stderr: err.to_string().into_bytes(),
stdout: vec![],
})?;
let comments = Comments::parse(&file_contents, config.comment_defaults.clone(), aux_file)
.map_err(|errors| Errored::new(errors, "parse aux comments"))?;
assert_eq!(
comments.revisions, None,
"aux builds cannot specify revisions"
);

let mut config = config.clone();

// Strip any `crate-type` flags from the args, as we need to set our own,
// and they may conflict (e.g. `lib` vs `proc-macro`);
let mut prev_was_crate_type = false;
config.program.args.retain(|arg| {
if prev_was_crate_type {
prev_was_crate_type = false;
return false;
}
if arg == "--test" {
false
} else if arg == "--crate-type" {
prev_was_crate_type = true;
false
} else if let Some(arg) = arg.to_str() {
!arg.starts_with("--crate-type=")
} else {
true
}
});

default_per_file_config(&mut config, aux_file, &file_contents);

match crate_type(&file_contents) {
// Proc macros must be run on the host
CrateType::ProcMacro => config.target = config.host.clone(),
CrateType::Test | CrateType::Bin | CrateType::Lib => {}
}

let mut config = TestConfig {
config,
revision: "",
comments: &comments,
path: aux_file,
};

config.patch_out_dir();

let mut aux_cmd = config.build_command()?;

let mut extra_args = config.build_aux_files(aux_file.parent().unwrap(), self)?;
// Make sure we see our dependencies
aux_cmd.args(extra_args.iter());

aux_cmd.arg("--emit=link");
let filename = aux_file.file_stem().unwrap().to_str().unwrap();
let output = aux_cmd.output().unwrap();
if !output.status.success() {
let error = Error::Command {
kind: "compilation of aux build failed".to_string(),
status: output.status,
};
return Err(Errored {
command: aux_cmd,
errors: vec![error],
stderr: rustc_stderr::process(aux_file, &output.stderr).rendered,
stdout: output.stdout,
});
}

// Now run the command again to fetch the output filenames
aux_cmd.arg("--print").arg("file-names");
let output = aux_cmd.output().unwrap();
assert!(output.status.success());

for file in output.stdout.lines() {
let file = std::str::from_utf8(file).unwrap();
let crate_name = filename.replace('-', "_");
let path = config.config.out_dir.join(file);
extra_args.push("--extern".into());
let mut cname = OsString::from(&crate_name);
cname.push("=");
cname.push(path);
extra_args.push(cname);
// Help cargo find the crates added with `--extern`.
extra_args.push("-L".into());
extra_args.push(config.config.out_dir.as_os_str().to_os_string());
}
Ok(extra_args)
}
}
71 changes: 71 additions & 0 deletions src/filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//! Datastructures and operations used for normalizing test output.
use bstr::ByteSlice;
use lazy_static::lazy_static;
use regex::bytes::{Captures, Regex};
use std::borrow::Cow;
use std::path::Path;

/// A filter's match rule.
#[derive(Clone, Debug)]
pub enum Match {
/// If the regex matches, the filter applies
Regex(Regex),
/// If the exact byte sequence is found, the filter applies
Exact(Vec<u8>),
/// Uses a heuristic to find backslashes in windows style paths
PathBackslash,
}

impl Match {
pub(crate) fn replace_all<'a>(&self, text: &'a [u8], replacement: &[u8]) -> Cow<'a, [u8]> {
match self {
Match::Regex(regex) => regex.replace_all(text, replacement),
Match::Exact(needle) => text.replace(needle, replacement).into(),
Match::PathBackslash => {
lazy_static! {
static ref PATH_RE: Regex = Regex::new(
r"(?x)
(?:
# Match paths to files with extensions that don't include spaces
\\(?:[\pL\pN.\-_']+[/\\])*[\pL\pN.\-_']+\.\pL+
|
# Allow spaces in absolute paths
[A-Z]:\\(?:[\pL\pN.\-_'\ ]+[/\\])+
)",
)
.unwrap();
}

PATH_RE.replace_all(text, |caps: &Captures<'_>| {
caps[0].replace(r"\", replacement)
})
}
}
}
}

impl From<&'_ Path> for Match {
fn from(v: &Path) -> Self {
let mut v = v.display().to_string();
// Normalize away windows canonicalized paths.
if v.starts_with(r"\\?\") {
v.drain(0..4);
}
let mut v = v.into_bytes();
// Normalize paths on windows to use slashes instead of backslashes,
// So that paths are rendered the same on all systems.
for c in &mut v {
if *c == b'\\' {
*c = b'/';
}
}
Self::Exact(v)
}
}

impl From<Regex> for Match {
fn from(v: Regex) -> Self {
Self::Regex(v)
}
}
Loading

0 comments on commit 2288aed

Please sign in to comment.