Merge commit '98ed962c7d' into master

This commit is contained in:
Guillaume Gomez 2024-07-10 12:44:23 +02:00
commit 7cbe50e209
68 changed files with 2682 additions and 1135 deletions

View file

@ -1,5 +1,7 @@
use crate::config::{Channel, ConfigInfo};
use crate::utils::{run_command, run_command_with_output_and_env, walk_dir};
use crate::utils::{
copy_file, create_dir, get_sysroot_dir, run_command, run_command_with_output_and_env, walk_dir,
};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
@ -9,12 +11,14 @@ use std::path::Path;
struct BuildArg {
flags: Vec<String>,
config_info: ConfigInfo,
build_sysroot: bool,
}
impl BuildArg {
/// Creates a new `BuildArg` instance by parsing command-line arguments.
fn new() -> Result<Option<Self>, String> {
let mut build_arg = Self::default();
// We skip binary name and the `build` command.
// Skip binary name and the `build` command.
let mut args = std::env::args().skip(2);
while let Some(arg) = args.next() {
@ -29,6 +33,9 @@ impl BuildArg {
);
}
}
"--sysroot" => {
build_arg.build_sysroot = true;
}
"--help" => {
Self::usage();
return Ok(None);
@ -48,20 +55,20 @@ impl BuildArg {
r#"
`build` command help:
--features [arg] : Add a new feature [arg]"#
--features [arg] : Add a new feature [arg]
--sysroot : Build with sysroot"#
);
ConfigInfo::show_usage();
println!(" --help : Show this help");
}
}
pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Result<(), String> {
let start_dir = Path::new("build_sysroot");
fn cleanup_sysroot_previous_build(start_dir: &Path) {
// Cleanup for previous run
// Clean target dir except for build scripts and incremental cache
let _ = walk_dir(
start_dir.join("target"),
|dir: &Path| {
&mut |dir: &Path| {
for top in &["debug", "release"] {
let _ = fs::remove_dir_all(dir.join(top).join("build"));
let _ = fs::remove_dir_all(dir.join(top).join("deps"));
@ -70,7 +77,7 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
let _ = walk_dir(
dir.join(top),
|sub_dir: &Path| {
&mut |sub_dir: &Path| {
if sub_dir
.file_name()
.map(|filename| filename.to_str().unwrap().starts_with("libsysroot"))
@ -80,7 +87,7 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
}
Ok(())
},
|file: &Path| {
&mut |file: &Path| {
if file
.file_name()
.map(|filename| filename.to_str().unwrap().starts_with("libsysroot"))
@ -90,16 +97,39 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
}
Ok(())
},
false,
);
}
Ok(())
},
|_| Ok(()),
&mut |_| Ok(()),
false,
);
let _ = fs::remove_file(start_dir.join("Cargo.lock"));
let _ = fs::remove_file(start_dir.join("test_target/Cargo.lock"));
let _ = fs::remove_dir_all(start_dir.join("sysroot"));
}
pub fn create_build_sysroot_content(start_dir: &Path) -> Result<(), String> {
if !start_dir.is_dir() {
create_dir(start_dir)?;
}
copy_file("build_system/build_sysroot/Cargo.toml", &start_dir.join("Cargo.toml"))?;
copy_file("build_system/build_sysroot/Cargo.lock", &start_dir.join("Cargo.lock"))?;
let src_dir = start_dir.join("src");
if !src_dir.is_dir() {
create_dir(&src_dir)?;
}
copy_file("build_system/build_sysroot/lib.rs", &start_dir.join("src/lib.rs"))
}
pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Result<(), String> {
let start_dir = get_sysroot_dir();
cleanup_sysroot_previous_build(&start_dir);
create_build_sysroot_content(&start_dir)?;
// Builds libs
let mut rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default();
@ -110,7 +140,6 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
if config.no_default_features {
rustflags.push_str(" -Csymbol-mangling-version=v0");
}
let mut env = env.clone();
let mut args: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"build", &"--target", &config.target];
@ -127,46 +156,33 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
"debug"
};
if let Ok(cg_rustflags) = std::env::var("CG_RUSTFLAGS") {
rustflags.push(' ');
rustflags.push_str(&cg_rustflags);
}
let mut env = env.clone();
env.insert("RUSTFLAGS".to_string(), rustflags);
run_command_with_output_and_env(&args, Some(start_dir), Some(&env))?;
run_command_with_output_and_env(&args, Some(&start_dir), Some(&env))?;
// Copy files to sysroot
let sysroot_path = start_dir.join(format!("sysroot/lib/rustlib/{}/lib/", config.target_triple));
fs::create_dir_all(&sysroot_path).map_err(|error| {
format!(
"Failed to create directory `{}`: {:?}",
sysroot_path.display(),
error
)
})?;
let copier = |dir_to_copy: &Path| {
create_dir(&sysroot_path)?;
let mut copier = |dir_to_copy: &Path| {
// FIXME: should not use shell command!
run_command(&[&"cp", &"-r", &dir_to_copy, &sysroot_path], None).map(|_| ())
};
walk_dir(
start_dir.join(&format!("target/{}/{}/deps", config.target_triple, channel)),
copier,
copier,
&mut copier.clone(),
&mut copier,
false,
)?;
// Copy the source files to the sysroot (Rust for Linux needs this).
let sysroot_src_path = start_dir.join("sysroot/lib/rustlib/src/rust");
fs::create_dir_all(&sysroot_src_path).map_err(|error| {
format!(
"Failed to create directory `{}`: {:?}",
sysroot_src_path.display(),
error
)
})?;
run_command(
&[
&"cp",
&"-r",
&start_dir.join("sysroot_src/library/"),
&sysroot_src_path,
],
None,
)?;
create_dir(&sysroot_src_path)?;
run_command(&[&"cp", &"-r", &start_dir.join("sysroot_src/library/"), &sysroot_src_path], None)?;
Ok(())
}
@ -174,20 +190,11 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
fn build_codegen(args: &mut BuildArg) -> Result<(), String> {
let mut env = HashMap::new();
env.insert(
"LD_LIBRARY_PATH".to_string(),
args.config_info.gcc_path.clone(),
);
env.insert(
"LIBRARY_PATH".to_string(),
args.config_info.gcc_path.clone(),
);
env.insert("LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone());
env.insert("LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone());
if args.config_info.no_default_features {
env.insert(
"RUSTFLAGS".to_string(),
"-Csymbol-mangling-version=v0".to_string(),
);
env.insert("RUSTFLAGS".to_string(), "-Csymbol-mangling-version=v0".to_string());
}
let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"rustc"];
@ -212,18 +219,15 @@ fn build_codegen(args: &mut BuildArg) -> Result<(), String> {
// We voluntarily ignore the error.
let _ = fs::remove_dir_all("target/out");
let gccjit_target = "target/out/gccjit";
fs::create_dir_all(gccjit_target).map_err(|error| {
format!(
"Failed to create directory `{}`: {:?}",
gccjit_target, error
)
})?;
println!("[BUILD] sysroot");
build_sysroot(&env, &args.config_info)?;
create_dir(gccjit_target)?;
if args.build_sysroot {
println!("[BUILD] sysroot");
build_sysroot(&env, &args.config_info)?;
}
Ok(())
}
/// Executes the build process.
pub fn run() -> Result<(), String> {
let mut args = match BuildArg::new()? {
Some(args) => args,

View file

@ -1,114 +0,0 @@
use crate::config::ConfigInfo;
use crate::utils::{
get_toolchain, run_command_with_output_and_env_no_err, rustc_toolchain_version_info,
rustc_version_info,
};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::path::PathBuf;
fn args() -> Result<Option<Vec<String>>, String> {
// We skip the binary and the "cargo" option.
if let Some("--help") = std::env::args().skip(2).next().as_deref() {
usage();
return Ok(None);
}
let args = std::env::args().skip(2).collect::<Vec<_>>();
if args.is_empty() {
return Err(
"Expected at least one argument for `cargo` subcommand, found none".to_string(),
);
}
Ok(Some(args))
}
fn usage() {
println!(
r#"
`cargo` command help:
[args] : Arguments to be passed to the cargo command
--help : Show this help
"#
)
}
pub fn run() -> Result<(), String> {
let args = match args()? {
Some(a) => a,
None => return Ok(()),
};
// We first need to go to the original location to ensure that the config setup will go as
// expected.
let current_dir = std::env::current_dir()
.and_then(|path| path.canonicalize())
.map_err(|error| format!("Failed to get current directory path: {:?}", error))?;
let current_exe = std::env::current_exe()
.and_then(|path| path.canonicalize())
.map_err(|error| format!("Failed to get current exe path: {:?}", error))?;
let mut parent_dir = current_exe
.components()
.map(|comp| comp.as_os_str())
.collect::<Vec<_>>();
// We run this script from "build_system/target/release/y", so we need to remove these elements.
for to_remove in &["y", "release", "target", "build_system"] {
if parent_dir
.last()
.map(|part| part == to_remove)
.unwrap_or(false)
{
parent_dir.pop();
} else {
return Err(format!(
"Build script not executed from `build_system/target/release/y` (in path {})",
current_exe.display(),
));
}
}
let parent_dir = PathBuf::from(parent_dir.join(&OsStr::new("/")));
std::env::set_current_dir(&parent_dir).map_err(|error| {
format!(
"Failed to go to `{}` folder: {:?}",
parent_dir.display(),
error
)
})?;
let mut env: HashMap<String, String> = std::env::vars().collect();
ConfigInfo::default().setup(&mut env, false)?;
let toolchain = get_toolchain()?;
let toolchain_version = rustc_toolchain_version_info(&toolchain)?;
let default_version = rustc_version_info(None)?;
if toolchain_version != default_version {
println!(
"rustc_codegen_gcc is built for {} but the default rustc version is {}.",
toolchain_version.short, default_version.short,
);
println!("Using {}.", toolchain_version.short);
}
// We go back to the original folder since we now have set up everything we needed.
std::env::set_current_dir(&current_dir).map_err(|error| {
format!(
"Failed to go back to `{}` folder: {:?}",
current_dir.display(),
error
)
})?;
let rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default();
env.insert("RUSTDOCFLAGS".to_string(), rustflags);
let toolchain = format!("+{}", toolchain);
let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &toolchain];
for arg in &args {
command.push(arg);
}
if run_command_with_output_and_env_no_err(&command, None, Some(&env)).is_err() {
std::process::exit(1);
}
Ok(())
}

View file

@ -1,4 +1,4 @@
use crate::utils::{remove_file, run_command};
use crate::utils::{get_sysroot_dir, remove_file, run_command};
use std::fs::remove_dir_all;
use std::path::Path;
@ -42,11 +42,12 @@ fn usage() {
}
fn clean_all() -> Result<(), String> {
let build_sysroot = get_sysroot_dir();
let dirs_to_remove = [
"target",
"build_sysroot/sysroot",
"build_sysroot/sysroot_src",
"build_sysroot/target",
"target".into(),
build_sysroot.join("sysroot"),
build_sysroot.join("sysroot_src"),
build_sysroot.join("target"),
];
for dir in dirs_to_remove {
let _ = remove_dir_all(dir);
@ -56,10 +57,11 @@ fn clean_all() -> Result<(), String> {
let _ = remove_dir_all(Path::new(crate::BUILD_DIR).join(dir));
}
let files_to_remove = ["build_sysroot/Cargo.lock", "perf.data", "perf.data.old"];
let files_to_remove =
[build_sysroot.join("Cargo.lock"), "perf.data".into(), "perf.data.old".into()];
for file in files_to_remove {
let _ = remove_file(file);
let _ = remove_file(&file);
}
println!("Successfully ran `clean all`");

View file

@ -1,5 +1,6 @@
use crate::utils::{
create_symlink, get_os_name, run_command_with_output, rustc_version_info, split_args,
create_dir, create_symlink, get_os_name, get_sysroot_dir, run_command_with_output,
rustc_version_info, split_args,
};
use std::collections::HashMap;
use std::env as std_env;
@ -26,11 +27,7 @@ impl Channel {
}
fn failed_config_parsing(config_file: &Path, err: &str) -> Result<ConfigFile, String> {
Err(format!(
"Failed to parse `{}`: {}",
config_file.display(),
err
))
Err(format!("Failed to parse `{}`: {}", config_file.display(), err))
}
#[derive(Default)]
@ -48,11 +45,7 @@ impl ConfigFile {
)
})?;
let toml = Toml::parse(&content).map_err(|err| {
format!(
"Error occurred around `{}`: {:?}",
&content[err.start..=err.end],
err.kind
)
format!("Error occurred around `{}`: {:?}", &content[err.start..=err.end], err.kind)
})?;
let mut config = Self::default();
for (key, value) in toml.iter() {
@ -181,11 +174,7 @@ impl ConfigInfo {
},
"--use-backend" => match args.next() {
Some(backend) if !backend.is_empty() => self.backend = Some(backend),
_ => {
return Err(
"Expected an argument after `--use-backend`, found nothing".into()
)
}
_ => return Err("Expected an argument after `--use-backend`, found nothing".into()),
},
"--no-default-features" => self.no_default_features = true,
_ => return Ok(false),
@ -228,20 +217,10 @@ impl ConfigInfo {
let output_dir = output_dir.join(&commit);
if !output_dir.is_dir() {
std::fs::create_dir_all(&output_dir).map_err(|err| {
format!(
"failed to create folder `{}`: {:?}",
output_dir.display(),
err,
)
})?;
create_dir(&output_dir)?;
}
let output_dir = output_dir.canonicalize().map_err(|err| {
format!(
"Failed to get absolute path of `{}`: {:?}",
output_dir.display(),
err
)
format!("Failed to get absolute path of `{}`: {:?}", output_dir.display(), err)
})?;
let libgccjit_so_name = "libgccjit.so";
@ -252,13 +231,7 @@ impl ConfigInfo {
let tempfile = output_dir.join(&tempfile_name);
let is_in_ci = std::env::var("GITHUB_ACTIONS").is_ok();
let url = format!(
"https://github.com/antoyo/gcc/releases/download/master-{}/libgccjit.so",
commit,
);
println!("Downloading `{}`...", url);
download_gccjit(url, &output_dir, tempfile_name, !is_in_ci)?;
download_gccjit(&commit, &output_dir, tempfile_name, !is_in_ci)?;
let libgccjit_so = output_dir.join(libgccjit_so_name);
// If we reach this point, it means the file was correctly downloaded, so let's
@ -275,10 +248,7 @@ impl ConfigInfo {
println!("Downloaded libgccjit.so version {} successfully!", commit);
// We need to create a link named `libgccjit.so.0` because that's what the linker is
// looking for.
create_symlink(
&libgccjit_so,
output_dir.join(&format!("{}.0", libgccjit_so_name)),
)?;
create_symlink(&libgccjit_so, output_dir.join(&format!("{}.0", libgccjit_so_name)))?;
}
self.gcc_path = output_dir.display().to_string();
@ -298,10 +268,7 @@ impl ConfigInfo {
Some(config_file) => config_file.into(),
None => self.compute_path("config.toml"),
};
let ConfigFile {
gcc_path,
download_gccjit,
} = ConfigFile::new(&config_file)?;
let ConfigFile { gcc_path, download_gccjit } = ConfigFile::new(&config_file)?;
if let Some(true) = download_gccjit {
self.download_gccjit_if_needed()?;
@ -310,10 +277,7 @@ impl ConfigInfo {
self.gcc_path = match gcc_path {
Some(path) => path,
None => {
return Err(format!(
"missing `gcc-path` value from `{}`",
config_file.display(),
))
return Err(format!("missing `gcc-path` value from `{}`", config_file.display(),))
}
};
Ok(())
@ -393,15 +357,16 @@ impl ConfigInfo {
.join(&format!("librustc_codegen_gcc.{}", self.dylib_ext))
.display()
.to_string();
self.sysroot_path = current_dir
.join("build_sysroot/sysroot")
.display()
.to_string();
self.sysroot_path =
current_dir.join(&get_sysroot_dir()).join("sysroot").display().to_string();
if let Some(backend) = &self.backend {
// This option is only used in the rust compiler testsuite. The sysroot is handled
// by its build system directly so no need to set it ourselves.
rustflags.push(format!("-Zcodegen-backend={}", backend));
} else {
rustflags.extend_from_slice(&[
"--sysroot".to_string(), self.sysroot_path.clone(),
"--sysroot".to_string(),
self.sysroot_path.clone(),
format!("-Zcodegen-backend={}", self.cg_backend_path),
]);
}
@ -422,13 +387,6 @@ impl ConfigInfo {
rustflags.push("-Csymbol-mangling-version=v0".to_string());
}
rustflags.push("-Cdebuginfo=2".to_string());
// Since we don't support ThinLTO, disable LTO completely when not trying to do LTO.
// TODO(antoyo): remove when we can handle ThinLTO.
if !env.contains_key(&"FAT_LTO".to_string()) {
rustflags.push("-Clto=off".to_string());
}
// FIXME(antoyo): remove once the atomic shim is gone
if os_name == "Darwin" {
rustflags.extend_from_slice(&[
@ -440,10 +398,9 @@ impl ConfigInfo {
// display metadata load errors
env.insert("RUSTC_LOG".to_string(), "warn".to_string());
let sysroot = current_dir.join(&format!(
"build_sysroot/sysroot/lib/rustlib/{}/lib",
self.target_triple,
));
let sysroot = current_dir
.join(&get_sysroot_dir())
.join(&format!("sysroot/lib/rustlib/{}/lib", self.target_triple));
let ld_library_path = format!(
"{target}:{sysroot}:{gcc_path}",
target = self.cargo_target_dir,
@ -501,11 +458,27 @@ impl ConfigInfo {
}
fn download_gccjit(
url: String,
commit: &str,
output_dir: &Path,
tempfile_name: String,
with_progress_bar: bool,
) -> Result<(), String> {
let url = if std::env::consts::OS == "linux" && std::env::consts::ARCH == "x86_64" {
format!("https://github.com/rust-lang/gcc/releases/download/master-{}/libgccjit.so", commit)
} else {
eprintln!(
"\
Pre-compiled libgccjit.so not available for this os or architecture.
Please compile it yourself and update the `config.toml` file
to `download-gccjit = false` and set `gcc-path` to the appropriate directory."
);
return Err(String::from(
"no appropriate pre-compiled libgccjit.so available for download",
));
};
println!("Downloading `{}`...", url);
// Try curl. If that fails and we are on windows, fallback to PowerShell.
let mut ret = run_command_with_output(
&[
@ -521,11 +494,7 @@ fn download_gccjit(
&"--retry",
&"3",
&"-SRfL",
if with_progress_bar {
&"--progress-bar"
} else {
&"-s"
},
if with_progress_bar { &"--progress-bar" } else { &"-s" },
&url.as_str(),
],
Some(&output_dir),

View file

@ -0,0 +1,35 @@
use crate::utils::run_command_with_output;
use std::ffi::OsStr;
use std::path::Path;
fn show_usage() {
println!(
r#"
`fmt` command help:
--check : Pass `--check` argument to `cargo fmt` commands
--help : Show this help"#
);
}
pub fn run() -> Result<(), String> {
let mut check = false;
// We skip binary name and the `info` command.
let mut args = std::env::args().skip(2);
while let Some(arg) = args.next() {
match arg.as_str() {
"--help" => {
show_usage();
return Ok(());
}
"--check" => check = true,
_ => return Err(format!("Unknown option {}", arg)),
}
}
let cmd: &[&dyn AsRef<OsStr>] =
if check { &[&"cargo", &"fmt", &"--check"] } else { &[&"cargo", &"fmt"] };
run_command_with_output(cmd, Some(&Path::new(".")))?;
run_command_with_output(cmd, Some(&Path::new("build_system")))
}

View file

@ -2,12 +2,13 @@ use std::env;
use std::process;
mod build;
mod cargo;
mod clean;
mod clone_gcc;
mod config;
mod fmt;
mod info;
mod prepare;
mod rust_tools;
mod rustc_info;
mod test;
mod utils;
@ -26,16 +27,23 @@ macro_rules! arg_error {
fn usage() {
println!(
"\
Available commands for build_system:
rustc_codegen_gcc build system
cargo : Run cargo command
clean : Run clean command
prepare : Run prepare command
build : Run build command
test : Run test command
info : Run info command
clone-gcc : Run clone-gcc command
--help : Show this message"
Usage: build_system [command] [options]
Options:
--help : Displays this help message.
Commands:
cargo : Executes a cargo command.
rustc : Compiles the program using the GCC compiler.
clean : Cleans the build directory, removing all compiled files and artifacts.
prepare : Prepares the environment for building, including fetching dependencies and setting up configurations.
build : Compiles the project.
test : Runs tests for the project.
info : Displays information about the build environment and project configuration.
clone-gcc : Clones the GCC compiler from a specified source.
fmt : Runs rustfmt"
);
}
@ -45,8 +53,10 @@ pub enum Command {
CloneGcc,
Prepare,
Build,
Rustc,
Test,
Info,
Fmt,
}
fn main() {
@ -56,12 +66,14 @@ fn main() {
let command = match env::args().nth(1).as_deref() {
Some("cargo") => Command::Cargo,
Some("rustc") => Command::Rustc,
Some("clean") => Command::Clean,
Some("prepare") => Command::Prepare,
Some("build") => Command::Build,
Some("test") => Command::Test,
Some("info") => Command::Info,
Some("clone-gcc") => Command::CloneGcc,
Some("fmt") => Command::Fmt,
Some("--help") => {
usage();
process::exit(0);
@ -75,13 +87,15 @@ fn main() {
};
if let Err(e) = match command {
Command::Cargo => cargo::run(),
Command::Cargo => rust_tools::run_cargo(),
Command::Rustc => rust_tools::run_rustc(),
Command::Clean => clean::run(),
Command::Prepare => prepare::run(),
Command::Build => build::run(),
Command::Test => test::run(),
Command::Info => info::run(),
Command::CloneGcc => clone_gcc::run(),
Command::Fmt => fmt::run(),
} {
eprintln!("Command failed to run: {e}");
process::exit(1);

View file

@ -1,58 +1,58 @@
use crate::rustc_info::get_rustc_path;
use crate::utils::{
cargo_install, git_clone_root_dir, remove_file, run_command, run_command_with_output, walk_dir,
cargo_install, create_dir, get_sysroot_dir, git_clone_root_dir, remove_file, run_command,
run_command_with_output, walk_dir,
};
use std::fs;
use std::path::Path;
use std::path::{Path, PathBuf};
fn prepare_libcore(
sysroot_path: &Path,
libgccjit12_patches: bool,
cross_compile: bool,
sysroot_source: Option<String>,
) -> Result<(), String> {
let rustc_path = match get_rustc_path() {
Some(path) => path,
None => return Err("`rustc` path not found".to_string()),
};
let rustlib_dir: PathBuf;
let parent = match rustc_path.parent() {
Some(path) => path,
None => return Err(format!("No parent for `{}`", rustc_path.display())),
};
if let Some(path) = sysroot_source {
rustlib_dir = Path::new(&path)
.canonicalize()
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
if !rustlib_dir.is_dir() {
return Err(format!("Custom sysroot path {:?} not found", rustlib_dir));
}
} else {
let rustc_path = match get_rustc_path() {
Some(path) => path,
None => return Err("`rustc` path not found".to_string()),
};
let rustlib_dir = parent
.join("../lib/rustlib/src/rust")
.canonicalize()
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
if !rustlib_dir.is_dir() {
return Err("Please install `rust-src` component".to_string());
let parent = match rustc_path.parent() {
Some(path) => path,
None => return Err(format!("No parent for `{}`", rustc_path.display())),
};
rustlib_dir = parent
.join("../lib/rustlib/src/rust")
.canonicalize()
.map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
if !rustlib_dir.is_dir() {
return Err("Please install `rust-src` component".to_string());
}
}
let sysroot_dir = sysroot_path.join("sysroot_src");
if sysroot_dir.is_dir() {
if let Err(error) = fs::remove_dir_all(&sysroot_dir) {
return Err(format!(
"Failed to remove `{}`: {:?}",
sysroot_dir.display(),
error,
));
return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,));
}
}
let sysroot_library_dir = sysroot_dir.join("library");
fs::create_dir_all(&sysroot_library_dir).map_err(|error| {
format!(
"Failed to create folder `{}`: {:?}",
sysroot_library_dir.display(),
error,
)
})?;
create_dir(&sysroot_library_dir)?;
run_command(
&[&"cp", &"-r", &rustlib_dir.join("library"), &sysroot_dir],
None,
)?;
run_command(&[&"cp", &"-r", &rustlib_dir.join("library"), &sysroot_dir], None)?;
println!("[GIT] init (cwd): `{}`", sysroot_dir.display());
run_command(&[&"git", &"init"], Some(&sysroot_dir))?;
@ -63,70 +63,52 @@ fn prepare_libcore(
// This is needed on systems where nothing is configured.
// git really needs something here, or it will fail.
// Even using --author is not enough.
run_command(
&[&"git", &"config", &"user.email", &"none@example.com"],
Some(&sysroot_dir),
)?;
run_command(
&[&"git", &"config", &"user.name", &"None"],
Some(&sysroot_dir),
)?;
run_command(
&[&"git", &"config", &"core.autocrlf", &"false"],
Some(&sysroot_dir),
)?;
run_command(
&[&"git", &"config", &"commit.gpgSign", &"false"],
Some(&sysroot_dir),
)?;
run_command(
&[&"git", &"commit", &"-m", &"Initial commit", &"-q"],
Some(&sysroot_dir),
)?;
run_command(&[&"git", &"config", &"user.email", &"none@example.com"], Some(&sysroot_dir))?;
run_command(&[&"git", &"config", &"user.name", &"None"], Some(&sysroot_dir))?;
run_command(&[&"git", &"config", &"core.autocrlf", &"false"], Some(&sysroot_dir))?;
run_command(&[&"git", &"config", &"commit.gpgSign", &"false"], Some(&sysroot_dir))?;
run_command(&[&"git", &"commit", &"-m", &"Initial commit", &"-q"], Some(&sysroot_dir))?;
let mut patches = Vec::new();
walk_dir(
"patches",
|_| Ok(()),
|file_path: &Path| {
&mut |_| Ok(()),
&mut |file_path: &Path| {
patches.push(file_path.to_path_buf());
Ok(())
},
false,
)?;
if cross_compile {
walk_dir(
"patches/cross_patches",
|_| Ok(()),
|file_path: &Path| {
&mut |_| Ok(()),
&mut |file_path: &Path| {
patches.push(file_path.to_path_buf());
Ok(())
},
false,
)?;
}
if libgccjit12_patches {
walk_dir(
"patches/libgccjit12",
|_| Ok(()),
|file_path: &Path| {
&mut |_| Ok(()),
&mut |file_path: &Path| {
patches.push(file_path.to_path_buf());
Ok(())
},
false,
)?;
}
patches.sort();
for file_path in patches {
println!("[GIT] apply `{}`", file_path.display());
let path = Path::new("../..").join(file_path);
let path = Path::new("../../..").join(file_path);
run_command_with_output(&[&"git", &"apply", &path], Some(&sysroot_dir))?;
run_command_with_output(&[&"git", &"add", &"-A"], Some(&sysroot_dir))?;
run_command_with_output(
&[
&"git",
&"commit",
&"--no-gpg-sign",
&"-m",
&format!("Patch {}", path.display()),
],
&[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())],
Some(&sysroot_dir),
)?;
}
@ -145,13 +127,7 @@ fn prepare_rand() -> Result<(), String> {
run_command_with_output(&[&"git", &"apply", &path], Some(rand_dir))?;
run_command_with_output(&[&"git", &"add", &"-A"], Some(rand_dir))?;
run_command_with_output(
&[
&"git",
&"commit",
&"--no-gpg-sign",
&"-m",
&format!("Patch {}", path.display()),
],
&[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())],
Some(rand_dir),
)?;
@ -165,10 +141,7 @@ fn build_raytracer(repo_dir: &Path) -> Result<(), String> {
if mv_target.is_file() {
remove_file(&mv_target)?;
}
run_command(
&[&"mv", &"target/debug/main", &"raytracer_cg_llvm"],
Some(repo_dir),
)?;
run_command(&[&"mv", &"target/debug/main", &"raytracer_cg_llvm"], Some(repo_dir))?;
Ok(())
}
@ -193,6 +166,7 @@ struct PrepareArg {
cross_compile: bool,
only_libcore: bool,
libgccjit12_patches: bool,
sysroot_source: Option<String>,
}
impl PrepareArg {
@ -200,12 +174,23 @@ impl PrepareArg {
let mut only_libcore = false;
let mut cross_compile = false;
let mut libgccjit12_patches = false;
let mut sysroot_source = None;
for arg in std::env::args().skip(2) {
let mut args = std::env::args().skip(2);
while let Some(arg) = args.next() {
match arg.as_str() {
"--only-libcore" => only_libcore = true,
"--cross" => cross_compile = true,
"--libgccjit12-patches" => libgccjit12_patches = true,
"--sysroot-source" => {
if let Some(path) = args.next() {
sysroot_source = Some(path);
} else {
return Err(
"Expected a value after `--sysroot-source`, found nothing".to_string()
);
}
}
"--help" => {
Self::usage();
return Ok(None);
@ -213,11 +198,7 @@ impl PrepareArg {
a => return Err(format!("Unknown argument `{a}`")),
}
}
Ok(Some(Self {
cross_compile,
only_libcore,
libgccjit12_patches,
}))
Ok(Some(Self { cross_compile, only_libcore, libgccjit12_patches, sysroot_source }))
}
fn usage() {
@ -228,6 +209,7 @@ impl PrepareArg {
--only-libcore : Only setup libcore and don't clone other repositories
--cross : Apply the patches needed to do cross-compilation
--libgccjit12-patches : Apply patches needed for libgccjit12
--sysroot-source : Specify custom path for sysroot source
--help : Show this help"#
)
}
@ -238,8 +220,13 @@ pub fn run() -> Result<(), String> {
Some(a) => a,
None => return Ok(()),
};
let sysroot_path = Path::new("build_sysroot");
prepare_libcore(sysroot_path, args.libgccjit12_patches, args.cross_compile)?;
let sysroot_path = get_sysroot_dir();
prepare_libcore(
&sysroot_path,
args.libgccjit12_patches,
args.cross_compile,
args.sysroot_source,
)?;
if !args.only_libcore {
cargo_install("hyperfine")?;

View file

@ -0,0 +1,125 @@
use crate::config::ConfigInfo;
use crate::utils::{
get_toolchain, run_command_with_output_and_env_no_err, rustc_toolchain_version_info,
rustc_version_info,
};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::path::PathBuf;
fn args(command: &str) -> Result<Option<Vec<String>>, String> {
// We skip the binary and the "cargo"/"rustc" option.
if let Some("--help") = std::env::args().skip(2).next().as_deref() {
usage(command);
return Ok(None);
}
let args = std::env::args().skip(2).collect::<Vec<_>>();
if args.is_empty() {
return Err(format!(
"Expected at least one argument for `{}` subcommand, found none",
command
));
}
Ok(Some(args))
}
fn usage(command: &str) {
println!(
r#"
`{}` command help:
[args] : Arguments to be passed to the cargo command
--help : Show this help
"#,
command,
)
}
struct RustcTools {
env: HashMap<String, String>,
args: Vec<String>,
toolchain: String,
config: ConfigInfo,
}
impl RustcTools {
fn new(command: &str) -> Result<Option<Self>, String> {
let Some(args) = args(command)? else { return Ok(None) };
// We first need to go to the original location to ensure that the config setup will go as
// expected.
let current_dir = std::env::current_dir()
.and_then(|path| path.canonicalize())
.map_err(|error| format!("Failed to get current directory path: {:?}", error))?;
let current_exe = std::env::current_exe()
.and_then(|path| path.canonicalize())
.map_err(|error| format!("Failed to get current exe path: {:?}", error))?;
let mut parent_dir =
current_exe.components().map(|comp| comp.as_os_str()).collect::<Vec<_>>();
// We run this script from "build_system/target/release/y", so we need to remove these elements.
for to_remove in &["y", "release", "target", "build_system"] {
if parent_dir.last().map(|part| part == to_remove).unwrap_or(false) {
parent_dir.pop();
} else {
return Err(format!(
"Build script not executed from `build_system/target/release/y` (in path {})",
current_exe.display(),
));
}
}
let parent_dir = PathBuf::from(parent_dir.join(&OsStr::new("/")));
std::env::set_current_dir(&parent_dir).map_err(|error| {
format!("Failed to go to `{}` folder: {:?}", parent_dir.display(), error)
})?;
let mut env: HashMap<String, String> = std::env::vars().collect();
let mut config = ConfigInfo::default();
config.setup(&mut env, false)?;
let toolchain = get_toolchain()?;
let toolchain_version = rustc_toolchain_version_info(&toolchain)?;
let default_version = rustc_version_info(None)?;
if toolchain_version != default_version {
println!(
"rustc_codegen_gcc is built for {} but the default rustc version is {}.",
toolchain_version.short, default_version.short,
);
println!("Using {}.", toolchain_version.short);
}
// We go back to the original folder since we now have set up everything we needed.
std::env::set_current_dir(&current_dir).map_err(|error| {
format!("Failed to go back to `{}` folder: {:?}", current_dir.display(), error)
})?;
let toolchain = format!("+{}", toolchain);
Ok(Some(Self { toolchain, args, env, config }))
}
}
pub fn run_cargo() -> Result<(), String> {
let Some(mut tools) = RustcTools::new("cargo")? else { return Ok(()) };
let rustflags = tools.env.get("RUSTFLAGS").cloned().unwrap_or_default();
tools.env.insert("RUSTDOCFLAGS".to_string(), rustflags);
let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &tools.toolchain];
for arg in &tools.args {
command.push(arg);
}
if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
std::process::exit(1);
}
Ok(())
}
pub fn run_rustc() -> Result<(), String> {
let Some(tools) = RustcTools::new("rustc")? else { return Ok(()) };
let mut command = tools.config.rustc_command_vec();
for arg in &tools.args {
command.push(arg);
}
if run_command_with_output_and_env_no_err(&command, None, Some(&tools.env)).is_err() {
std::process::exit(1);
}
Ok(())
}

View file

@ -1,13 +1,14 @@
use crate::build;
use crate::config::{Channel, ConfigInfo};
use crate::utils::{
get_toolchain, git_clone, git_clone_root_dir, remove_file, run_command, run_command_with_env,
run_command_with_output_and_env, rustc_version_info, split_args, walk_dir,
create_dir, get_sysroot_dir, get_toolchain, git_clone, git_clone_root_dir, remove_file,
run_command, run_command_with_env, run_command_with_output_and_env, rustc_version_info,
split_args, walk_dir,
};
use std::collections::{BTreeSet, HashMap};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::{create_dir_all, remove_dir_all, File};
use std::fs::{remove_dir_all, File};
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::str::FromStr;
@ -19,46 +20,27 @@ type Runners = HashMap<&'static str, (&'static str, Runner)>;
fn get_runners() -> Runners {
let mut runners = HashMap::new();
runners.insert("--test-rustc", ("Run all rustc tests", test_rustc as Runner));
runners
.insert("--test-successful-rustc", ("Run successful rustc tests", test_successful_rustc));
runners.insert(
"--test-rustc",
("Run all rustc tests", test_rustc as Runner),
);
runners.insert(
"--test-successful-rustc",
("Run successful rustc tests", test_successful_rustc),
);
runners.insert(
"--test-failing-rustc",
("Run failing rustc tests", test_failing_rustc),
);
runners.insert(
"--projects",
("Run the tests of popular crates", test_projects),
"--test-failing-ui-pattern-tests",
("Run failing ui pattern tests", test_failing_ui_pattern_tests),
);
runners.insert("--test-failing-rustc", ("Run failing rustc tests", test_failing_rustc));
runners.insert("--projects", ("Run the tests of popular crates", test_projects));
runners.insert("--test-libcore", ("Run libcore tests", test_libcore));
runners.insert("--clean", ("Empty cargo target directory", clean));
runners.insert("--build-sysroot", ("Build sysroot", build_sysroot));
runners.insert("--std-tests", ("Run std tests", std_tests));
runners.insert("--asm-tests", ("Run asm tests", asm_tests));
runners.insert(
"--extended-tests",
("Run extended sysroot tests", extended_sysroot_tests),
);
runners.insert(
"--extended-rand-tests",
("Run extended rand tests", extended_rand_tests),
);
runners.insert("--extended-tests", ("Run extended sysroot tests", extended_sysroot_tests));
runners.insert("--extended-rand-tests", ("Run extended rand tests", extended_rand_tests));
runners.insert(
"--extended-regex-example-tests",
(
"Run extended regex example tests",
extended_regex_example_tests,
),
);
runners.insert(
"--extended-regex-tests",
("Run extended regex tests", extended_regex_tests),
("Run extended regex example tests", extended_regex_example_tests),
);
runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests));
runners.insert("--mini-tests", ("Run mini tests", mini_tests));
runners
@ -71,15 +53,9 @@ fn get_number_after_arg(
match args.next() {
Some(nb) if !nb.is_empty() => match usize::from_str(&nb) {
Ok(nb) => Ok(nb),
Err(_) => Err(format!(
"Expected a number after `{}`, found `{}`",
option, nb
)),
Err(_) => Err(format!("Expected a number after `{}`, found `{}`", option, nb)),
},
_ => Err(format!(
"Expected a number after `{}`, found nothing",
option
)),
_ => Err(format!("Expected a number after `{}`, found nothing", option)),
}
}
@ -110,7 +86,7 @@ fn show_usage() {
struct TestArg {
build_only: bool,
use_system_gcc: bool,
runners: BTreeSet<String>,
runners: Vec<String>,
flags: Vec<String>,
nb_parts: Option<usize>,
current_part: Option<usize>,
@ -130,9 +106,7 @@ impl TestArg {
match arg.as_str() {
"--features" => match args.next() {
Some(feature) if !feature.is_empty() => {
test_arg
.flags
.extend_from_slice(&["--features".into(), feature]);
test_arg.flags.extend_from_slice(&["--features".into(), feature]);
}
_ => {
return Err("Expected an argument after `--features`, found nothing".into())
@ -157,8 +131,10 @@ impl TestArg {
show_usage();
return Ok(None);
}
x if runners.contains_key(x) => {
test_arg.runners.insert(x.into());
x if runners.contains_key(x)
&& !test_arg.runners.iter().any(|runner| runner == x) =>
{
test_arg.runners.push(x.into());
}
arg => {
if !test_arg.config_info.parse_argument(arg, &mut args)? {
@ -211,8 +187,7 @@ fn build_if_no_backend(env: &Env, args: &TestArg) -> Result<(), String> {
fn clean(_env: &Env, args: &TestArg) -> Result<(), String> {
let _ = std::fs::remove_dir_all(&args.config_info.cargo_target_dir);
let path = Path::new(&args.config_info.cargo_target_dir).join("gccjit");
std::fs::create_dir_all(&path)
.map_err(|error| format!("failed to create folder `{}`: {:?}", path.display(), error))
create_dir(&path)
}
fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> {
@ -304,13 +279,8 @@ fn maybe_run_command_in_vm(
let sudo_command: &[&dyn AsRef<OsStr>] = &[&"sudo", &"cp", &exe, &vm_exe_path];
run_command_with_env(sudo_command, None, Some(env))?;
let mut vm_command: Vec<&dyn AsRef<OsStr>> = vec![
&"sudo",
&"chroot",
&vm_dir,
&"qemu-m68k-static",
&inside_vm_exe_path,
];
let mut vm_command: Vec<&dyn AsRef<OsStr>> =
vec![&"sudo", &"chroot", &vm_dir, &"qemu-m68k-static", &inside_vm_exe_path];
vm_command.extend_from_slice(command);
run_command_with_output_and_env(&vm_command, Some(&vm_parent_dir), Some(env))?;
Ok(())
@ -399,11 +369,7 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> {
}
run_command_with_env(&command, None, Some(env))?;
maybe_run_command_in_vm(
&[
&cargo_target_dir.join("std_example"),
&"--target",
&args.config_info.target_triple,
],
&[&cargo_target_dir.join("std_example"), &"--target", &args.config_info.target_triple],
env,
args,
)?;
@ -427,11 +393,7 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> {
command.push(test_flag);
}
run_command_with_env(&command, None, Some(env))?;
maybe_run_command_in_vm(
&[&cargo_target_dir.join("subslice-patterns-const-eval")],
env,
args,
)?;
maybe_run_command_in_vm(&[&cargo_target_dir.join("subslice-patterns-const-eval")], env, args)?;
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[AOT] track-caller-attribute");
@ -447,11 +409,7 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> {
command.push(test_flag);
}
run_command_with_env(&command, None, Some(env))?;
maybe_run_command_in_vm(
&[&cargo_target_dir.join("track-caller-attribute")],
env,
args,
)?;
maybe_run_command_in_vm(&[&cargo_target_dir.join("track-caller-attribute")], env, args)?;
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[AOT] mod_bench");
@ -477,11 +435,7 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
);
let rust_dir_path = Path::new(crate::BUILD_DIR).join("rust");
// If the repository was already cloned, command will fail, so doesn't matter.
let _ = git_clone(
"https://github.com/rust-lang/rust.git",
Some(&rust_dir_path),
false,
);
let _ = git_clone("https://github.com/rust-lang/rust.git", Some(&rust_dir_path), false);
let rust_dir: Option<&Path> = Some(&rust_dir_path);
run_command(&[&"git", &"checkout", &"--", &"tests/"], rust_dir)?;
run_command_with_output_and_env(&[&"git", &"fetch"], rust_dir, Some(env))?;
@ -511,12 +465,8 @@ fn setup_rustc(env: &mut Env, args: &TestArg) -> Result<PathBuf, String> {
}
})?;
let rustc = String::from_utf8(
run_command_with_env(
&[&"rustup", &toolchain, &"which", &"rustc"],
rust_dir,
Some(env),
)?
.stdout,
run_command_with_env(&[&"rustup", &toolchain, &"which", &"rustc"], rust_dir, Some(env))?
.stdout,
)
.map_err(|error| format!("Failed to retrieve rustc path: {:?}", error))
.and_then(|rustc| {
@ -573,13 +523,7 @@ download-ci-llvm = false
llvm_filecheck = llvm_filecheck.trim(),
),
)
.map_err(|error| {
format!(
"Failed to write into `{}`: {:?}",
file_path.display(),
error
)
})?;
.map_err(|error| format!("Failed to write into `{}`: {:?}", file_path.display(), error))?;
Ok(rust_dir_path)
}
@ -591,21 +535,19 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
let extra = if args.is_using_gcc_master_branch() {
""
} else {
" -Csymbol-mangling-version=v0"
};
let extra =
if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" };
let rustc_args = &format!(
r#"-Zpanic-abort-tests \
-Zcodegen-backend="{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}" \
--sysroot "{pwd}/build_sysroot/sysroot" -Cpanic=abort{extra}"#,
--sysroot "{sysroot_dir}" -Cpanic=abort{extra}"#,
pwd = std::env::current_dir()
.map_err(|error| format!("`current_dir` failed: {:?}", error))?
.display(),
channel = args.config_info.channel.as_str(),
dylib_ext = args.config_info.dylib_ext,
sysroot_dir = args.config_info.sysroot_path,
extra = extra,
);
@ -703,20 +645,23 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> {
//"https://github.com/rust-lang/cargo", // TODO: very slow, only run on master?
];
let mut env = env.clone();
let rustflags =
format!("{} --cap-lints allow", env.get("RUSTFLAGS").cloned().unwrap_or_default());
env.insert("RUSTFLAGS".to_string(), rustflags);
let run_tests = |projects_path, iter: &mut dyn Iterator<Item = &&str>| -> Result<(), String> {
for project in iter {
let clone_result = git_clone_root_dir(project, projects_path, true)?;
let repo_path = Path::new(&clone_result.repo_dir);
run_cargo_command(&[&"build", &"--release"], Some(repo_path), env, args)?;
run_cargo_command(&[&"test"], Some(repo_path), env, args)?;
run_cargo_command(&[&"build", &"--release"], Some(repo_path), &env, args)?;
run_cargo_command(&[&"test"], Some(repo_path), &env, args)?;
}
Ok(())
};
let projects_path = Path::new("projects");
create_dir_all(projects_path)
.map_err(|err| format!("Failed to create directory `projects`: {}", err))?;
create_dir(projects_path)?;
let nb_parts = args.nb_parts.unwrap_or(0);
if nb_parts > 0 {
@ -737,9 +682,9 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> {
fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> {
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[TEST] libcore");
let path = Path::new("build_sysroot/sysroot_src/library/core/tests");
let path = get_sysroot_dir().join("sysroot_src/library/core/tests");
let _ = remove_dir_all(path.join("target"));
run_cargo_command(&[&"test"], Some(path), env, args)?;
run_cargo_command(&[&"test"], Some(&path), env, args)?;
Ok(())
}
@ -763,10 +708,8 @@ fn extended_rand_tests(env: &Env, args: &TestArg) -> Result<(), String> {
}
let mut env = env.clone();
// newer aho_corasick versions throw a deprecation warning
let rustflags = format!(
"{} --cap-lints warn",
env.get("RUSTFLAGS").cloned().unwrap_or_default()
);
let rustflags =
format!("{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default());
env.insert("RUSTFLAGS".to_string(), rustflags);
let path = Path::new(crate::BUILD_DIR).join("rand");
@ -788,18 +731,11 @@ fn extended_regex_example_tests(env: &Env, args: &TestArg) -> Result<(), String>
println!("[TEST] rust-lang/regex example shootout-regex-dna");
let mut env = env.clone();
// newer aho_corasick versions throw a deprecation warning
let rustflags = format!(
"{} --cap-lints warn",
env.get("RUSTFLAGS").cloned().unwrap_or_default()
);
let rustflags =
format!("{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default());
env.insert("RUSTFLAGS".to_string(), rustflags);
// Make sure `[codegen mono items] start` doesn't poison the diff
run_cargo_command(
&[&"build", &"--example", &"shootout-regex-dna"],
Some(&path),
&env,
args,
)?;
run_cargo_command(&[&"build", &"--example", &"shootout-regex-dna"], Some(&path), &env, args)?;
run_cargo_command_with_callback(
&[&"run", &"--example", &"shootout-regex-dna"],
@ -810,10 +746,8 @@ fn extended_regex_example_tests(env: &Env, args: &TestArg) -> Result<(), String>
// FIXME: rewrite this with `child.stdin.write_all()` because
// `examples/regexdna-input.txt` is very small.
let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"bash", &"-c"];
let cargo_args = cargo_command
.iter()
.map(|s| s.as_ref().to_str().unwrap())
.collect::<Vec<_>>();
let cargo_args =
cargo_command.iter().map(|s| s.as_ref().to_str().unwrap()).collect::<Vec<_>>();
let bash_command = format!(
"cat examples/regexdna-input.txt | {} | grep -v 'Spawned thread' > res.txt",
cargo_args.join(" "),
@ -841,10 +775,8 @@ fn extended_regex_tests(env: &Env, args: &TestArg) -> Result<(), String> {
println!("[TEST] rust-lang/regex tests");
let mut env = env.clone();
// newer aho_corasick versions throw a deprecation warning
let rustflags = format!(
"{} --cap-lints warn",
env.get("RUSTFLAGS").cloned().unwrap_or_default()
);
let rustflags =
format!("{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default());
env.insert("RUSTFLAGS".to_string(), rustflags);
let path = Path::new(crate::BUILD_DIR).join("regex");
run_cargo_command(
@ -884,7 +816,7 @@ fn extended_sysroot_tests(env: &Env, args: &TestArg) -> Result<(), String> {
Ok(())
}
fn should_not_remove_test(file: &str) -> bool {
fn valid_ui_error_pattern_test(file: &str) -> bool {
// contains //~ERROR, but shouldn't be removed
[
"issues/auxiliary/issue-3136-a.rs",
@ -899,7 +831,8 @@ fn should_not_remove_test(file: &str) -> bool {
.any(|to_ignore| file.ends_with(to_ignore))
}
fn should_remove_test(file_path: &Path) -> Result<bool, String> {
#[rustfmt::skip]
fn contains_ui_error_patterns(file_path: &Path) -> Result<bool, String> {
// Tests generating errors.
let file = File::open(file_path)
.map_err(|error| format!("Failed to read `{}`: {:?}", file_path.display(), error))?;
@ -916,8 +849,8 @@ fn should_remove_test(file_path: &Path) -> Result<bool, String> {
"//~",
"thread",
]
.iter()
.any(|check| line.contains(check))
.iter()
.any(|check| line.contains(check))
{
return Ok(true);
}
@ -925,17 +858,27 @@ fn should_remove_test(file_path: &Path) -> Result<bool, String> {
return Ok(true);
}
}
if file_path
.display()
.to_string()
.contains("ambiguous-4-extern.rs")
{
if file_path.display().to_string().contains("ambiguous-4-extern.rs") {
eprintln!("nothing found for {file_path:?}");
}
Ok(false)
}
fn test_rustc_inner<F>(env: &Env, args: &TestArg, prepare_files_callback: F) -> Result<(), String>
// # Parameters
//
// * `env`: An environment variable that provides context for the function.
// * `args`: The arguments passed to the test. This could include things like the flags, config etc.
// * `prepare_files_callback`: A callback function that prepares the files needed for the test. Its used to remove/retain tests giving Error to run various rust test suits.
// * `run_error_pattern_test`: A boolean that determines whether to run only error pattern tests.
// * `test_type`: A string that indicates the type of the test being run.
//
fn test_rustc_inner<F>(
env: &Env,
args: &TestArg,
prepare_files_callback: F,
run_error_pattern_test: bool,
test_type: &str,
) -> Result<(), String>
where
F: Fn(&Path) -> Result<bool, String>,
{
@ -944,139 +887,138 @@ where
let mut env = env.clone();
let rust_path = setup_rustc(&mut env, args)?;
walk_dir(
rust_path.join("tests/ui"),
|dir| {
let dir_name = dir.file_name().and_then(|name| name.to_str()).unwrap_or("");
if [
"abi",
"extern",
"unsized-locals",
"proc-macro",
"threads-sendsync",
"borrowck",
"test-attrs",
]
.iter()
.any(|name| *name == dir_name)
{
std::fs::remove_dir_all(dir).map_err(|error| {
format!("Failed to remove folder `{}`: {:?}", dir.display(), error)
})?;
}
Ok(())
},
|_| Ok(()),
)?;
// These two functions are used to remove files that are known to not be working currently
// with the GCC backend to reduce noise.
fn dir_handling(dir: &Path) -> Result<(), String> {
if dir
.file_name()
.map(|name| name == "auxiliary")
.unwrap_or(true)
{
return Ok(());
}
walk_dir(dir, dir_handling, file_handling)
}
fn file_handling(file_path: &Path) -> Result<(), String> {
if !file_path
.extension()
.map(|extension| extension == "rs")
.unwrap_or(false)
{
return Ok(());
}
let path_str = file_path.display().to_string().replace("\\", "/");
if should_not_remove_test(&path_str) {
return Ok(());
} else if should_remove_test(file_path)? {
return remove_file(&file_path);
}
Ok(())
}
remove_file(&rust_path.join("tests/ui/consts/const_cmp_type_id.rs"))?;
remove_file(&rust_path.join("tests/ui/consts/issue-73976-monomorphic.rs"))?;
// this test is oom-killed in the CI.
remove_file(&rust_path.join("tests/ui/consts/issue-miri-1910.rs"))?;
// Tests generating errors.
remove_file(&rust_path.join("tests/ui/consts/issue-94675.rs"))?;
remove_file(&rust_path.join("tests/ui/mir/mir_heavy_promoted.rs"))?;
remove_file(&rust_path.join("tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs"))?;
remove_file(&rust_path.join("tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs"))?;
walk_dir(rust_path.join("tests/ui"), dir_handling, file_handling)?;
if !prepare_files_callback(&rust_path)? {
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("Keeping all UI tests");
println!("Keeping all {} tests", test_type);
}
let nb_parts = args.nb_parts.unwrap_or(0);
if nb_parts > 0 {
let current_part = args.current_part.unwrap();
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!(
"Splitting ui_test into {} parts (and running part {})",
nb_parts, current_part
);
let out = String::from_utf8(
run_command(
&[
&"find",
&"tests/ui",
&"-type",
&"f",
&"-name",
&"*.rs",
&"-not",
&"-path",
&"*/auxiliary/*",
],
Some(&rust_path),
)?
.stdout,
)
.map_err(|error| format!("Failed to retrieve output of find command: {:?}", error))?;
let mut files = out
.split('\n')
.map(|line| line.trim())
.filter(|line| !line.is_empty())
.collect::<Vec<_>>();
// To ensure it'll be always the same sub files, we sort the content.
files.sort();
// We increment the number of tests by one because if this is an odd number, we would skip
// one test.
let count = files.len() / nb_parts + 1;
let start = current_part * count;
// We remove the files we don't want to test.
for path in files.iter().skip(start).take(count) {
remove_file(&rust_path.join(path))?;
if test_type == "ui" {
if run_error_pattern_test {
// After we removed the error tests that are known to panic with rustc_codegen_gcc, we now remove the passing tests since this runs the error tests.
walk_dir(
rust_path.join("tests/ui"),
&mut |_dir| Ok(()),
&mut |file_path| {
if contains_ui_error_patterns(file_path)? {
Ok(())
} else {
remove_file(file_path).map_err(|e| e.to_string())
}
},
true,
)?;
} else {
walk_dir(
rust_path.join("tests/ui"),
&mut |dir| {
let dir_name = dir.file_name().and_then(|name| name.to_str()).unwrap_or("");
if [
"abi",
"extern",
"unsized-locals",
"proc-macro",
"threads-sendsync",
"borrowck",
"test-attrs",
]
.iter()
.any(|name| *name == dir_name)
{
std::fs::remove_dir_all(dir).map_err(|error| {
format!("Failed to remove folder `{}`: {:?}", dir.display(), error)
})?;
}
Ok(())
},
&mut |_| Ok(()),
false,
)?;
// These two functions are used to remove files that are known to not be working currently
// with the GCC backend to reduce noise.
fn dir_handling(dir: &Path) -> Result<(), String> {
if dir.file_name().map(|name| name == "auxiliary").unwrap_or(true) {
return Ok(());
}
walk_dir(dir, &mut dir_handling, &mut file_handling, false)
}
fn file_handling(file_path: &Path) -> Result<(), String> {
if !file_path.extension().map(|extension| extension == "rs").unwrap_or(false) {
return Ok(());
}
let path_str = file_path.display().to_string().replace("\\", "/");
if valid_ui_error_pattern_test(&path_str) {
return Ok(());
} else if contains_ui_error_patterns(file_path)? {
return remove_file(&file_path);
}
Ok(())
}
walk_dir(rust_path.join("tests/ui"), &mut dir_handling, &mut file_handling, false)?;
}
let nb_parts = args.nb_parts.unwrap_or(0);
if nb_parts > 0 {
let current_part = args.current_part.unwrap();
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!(
"Splitting ui_test into {} parts (and running part {})",
nb_parts, current_part
);
let out = String::from_utf8(
run_command(
&[
&"find",
&"tests/ui",
&"-type",
&"f",
&"-name",
&"*.rs",
&"-not",
&"-path",
&"*/auxiliary/*",
],
Some(&rust_path),
)?
.stdout,
)
.map_err(|error| format!("Failed to retrieve output of find command: {:?}", error))?;
let mut files = out
.split('\n')
.map(|line| line.trim())
.filter(|line| !line.is_empty())
.collect::<Vec<_>>();
// To ensure it'll be always the same sub files, we sort the content.
files.sort();
// We increment the number of tests by one because if this is an odd number, we would skip
// one test.
let count = files.len() / nb_parts + 1;
// We remove the files we don't want to test.
let start = current_part * count;
for path in files.iter().skip(start).take(count) {
remove_file(&rust_path.join(path))?;
}
}
}
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[TEST] rustc test suite");
println!("[TEST] rustc {} test suite", test_type);
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
let extra = if args.is_using_gcc_master_branch() {
""
} else {
" -Csymbol-mangling-version=v0"
};
let extra =
if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" };
let rustc_args = format!(
"{} -Zcodegen-backend={} --sysroot {}{}",
env.get("TEST_FLAGS").unwrap_or(&String::new()),
args.config_info.cg_backend_path,
args.config_info.sysroot_path,
extra,
"{test_flags} -Zcodegen-backend={backend} --sysroot {sysroot}{extra}",
test_flags = env.get("TEST_FLAGS").unwrap_or(&String::new()),
backend = args.config_info.cg_backend_path,
sysroot = args.config_info.sysroot_path,
extra = extra,
);
env.get_mut("RUSTFLAGS").unwrap().clear();
run_command_with_output_and_env(
&[
&"./x.py",
@ -1085,7 +1027,7 @@ where
&"always",
&"--stage",
&"0",
&"tests/ui",
&format!("tests/{}", test_type),
&"--rustc-args",
&rustc_args,
],
@ -1096,68 +1038,162 @@ where
}
fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
test_rustc_inner(env, args, |_| Ok(false))
//test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?;
test_rustc_inner(env, args, |_| Ok(false), false, "ui")
}
fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
test_rustc_inner(env, args, |rust_path| {
// Removing all tests.
run_command(
&[
&"find",
&"tests/ui",
&"-type",
&"f",
&"-name",
&"*.rs",
&"-not",
&"-path",
&"*/auxiliary/*",
&"-delete",
],
Some(rust_path),
)?;
let result1 = Ok(());
/*test_rustc_inner(
env,
args,
retain_files_callback("tests/failing-run-make-tests.txt", "run-make"),
false,
"run-make",
)*/
let result2 = test_rustc_inner(
env,
args,
retain_files_callback("tests/failing-ui-tests.txt", "ui"),
false,
"ui",
);
result1.and(result2)
}
fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
test_rustc_inner(
env,
args,
remove_files_callback("tests/failing-ui-tests.txt", "ui"),
false,
"ui",
)?;
Ok(())
/*test_rustc_inner(
env,
args,
remove_files_callback("tests/failing-run-make-tests.txt", "run-make"),
false,
"run-make",
)*/
}
fn test_failing_ui_pattern_tests(env: &Env, args: &TestArg) -> Result<(), String> {
test_rustc_inner(
env,
args,
remove_files_callback("tests/failing-ice-tests.txt", "ui"),
true,
"ui",
)
}
fn retain_files_callback<'a>(
file_path: &'a str,
test_type: &'a str,
) -> impl Fn(&Path) -> Result<bool, String> + 'a {
move |rust_path| {
let files = std::fs::read_to_string(file_path).unwrap_or_default();
let first_file_name = files.lines().next().unwrap_or("");
// If the first line ends with a `/`, we treat all lines in the file as a directory.
if first_file_name.ends_with('/') {
// Treat as directory
// Removing all tests.
run_command(
&[
&"find",
&format!("tests/{}", test_type),
&"-mindepth",
&"1",
&"-type",
&"d",
&"-exec",
&"rm",
&"-rf",
&"{}",
&"+",
],
Some(rust_path),
)?;
} else {
// Treat as file
// Removing all tests.
run_command(
&[
&"find",
&format!("tests/{}", test_type),
&"-type",
&"f",
&"-name",
&"*.rs",
&"-not",
&"-path",
&"*/auxiliary/*",
&"-delete",
],
Some(rust_path),
)?;
}
// Putting back only the failing ones.
let path = "tests/failing-ui-tests.txt";
if let Ok(files) = std::fs::read_to_string(path) {
for file in files
.split('\n')
.map(|line| line.trim())
.filter(|line| !line.is_empty())
{
if let Ok(files) = std::fs::read_to_string(&file_path) {
for file in files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) {
run_command(&[&"git", &"checkout", &"--", &file], Some(&rust_path))?;
}
} else {
println!(
"Failed to read `{}`, not putting back failing ui tests",
path
"Failed to read `{}`, not putting back failing {} tests",
file_path, test_type
);
}
Ok(true)
})
}
}
fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> {
test_rustc_inner(env, args, |rust_path| {
// Removing the failing tests.
let path = "tests/failing-ui-tests.txt";
if let Ok(files) = std::fs::read_to_string(path) {
for file in files
.split('\n')
.map(|line| line.trim())
.filter(|line| !line.is_empty())
{
let path = rust_path.join(file);
remove_file(&path)?;
fn remove_files_callback<'a>(
file_path: &'a str,
test_type: &'a str,
) -> impl Fn(&Path) -> Result<bool, String> + 'a {
move |rust_path| {
let files = std::fs::read_to_string(file_path).unwrap_or_default();
let first_file_name = files.lines().next().unwrap_or("");
// If the first line ends with a `/`, we treat all lines in the file as a directory.
if first_file_name.ends_with('/') {
// Removing the failing tests.
if let Ok(files) = std::fs::read_to_string(file_path) {
for file in
files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty())
{
let path = rust_path.join(file);
if let Err(e) = std::fs::remove_dir_all(&path) {
println!("Failed to remove directory `{}`: {}", path.display(), e);
}
}
} else {
println!(
"Failed to read `{}`, not putting back failing {} tests",
file_path, test_type
);
}
} else {
println!(
"Failed to read `{}`, not putting back failing ui tests",
path
);
// Removing the failing tests.
if let Ok(files) = std::fs::read_to_string(file_path) {
for file in
files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty())
{
let path = rust_path.join(file);
remove_file(&path)?;
}
} else {
println!("Failed to read `{}`, not putting back failing ui tests", file_path);
}
}
Ok(true)
})
}
}
fn run_all(env: &Env, args: &TestArg) -> Result<(), String> {
@ -1181,14 +1217,8 @@ pub fn run() -> Result<(), String> {
if !args.use_system_gcc {
args.config_info.setup_gcc_path()?;
env.insert(
"LIBRARY_PATH".to_string(),
args.config_info.gcc_path.clone(),
);
env.insert(
"LD_LIBRARY_PATH".to_string(),
args.config_info.gcc_path.clone(),
);
env.insert("LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone());
env.insert("LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone());
}
build_if_no_backend(&env, &args)?;

View file

@ -1,10 +1,42 @@
use std::collections::HashMap;
#[cfg(unix)]
use std::ffi::c_int;
use std::ffi::OsStr;
use std::fmt::Debug;
use std::fs;
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus, Output};
#[cfg(unix)]
extern "C" {
fn raise(signal: c_int) -> c_int;
}
fn exec_command(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
env: Option<&HashMap<String, String>>,
) -> Result<ExitStatus, String> {
let status = get_command_inner(input, cwd, env)
.spawn()
.map_err(|e| command_error(input, &cwd, e))?
.wait()
.map_err(|e| command_error(input, &cwd, e))?;
#[cfg(unix)]
{
if let Some(signal) = status.signal() {
unsafe {
raise(signal as _);
}
// In case the signal didn't kill the current process.
return Err(command_error(input, &cwd, format!("Process received signal {}", signal)));
}
}
Ok(status)
}
fn get_command_inner(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
@ -37,13 +69,8 @@ fn check_exit_status(
}
let mut error = format!(
"Command `{}`{} exited with status {:?}",
input
.iter()
.map(|s| s.as_ref().to_str().unwrap())
.collect::<Vec<_>>()
.join(" "),
cwd.map(|cwd| format!(" (running in folder `{}`)", cwd.display()))
.unwrap_or_default(),
input.iter().map(|s| s.as_ref().to_str().unwrap()).collect::<Vec<_>>().join(" "),
cwd.map(|cwd| format!(" (running in folder `{}`)", cwd.display())).unwrap_or_default(),
exit_status.code()
);
let input = input.iter().map(|i| i.as_ref()).collect::<Vec<&OsStr>>();
@ -68,11 +95,7 @@ fn check_exit_status(
fn command_error<D: Debug>(input: &[&dyn AsRef<OsStr>], cwd: &Option<&Path>, error: D) -> String {
format!(
"Command `{}`{} failed to run: {error:?}",
input
.iter()
.map(|s| s.as_ref().to_str().unwrap())
.collect::<Vec<_>>()
.join(" "),
input.iter().map(|s| s.as_ref().to_str().unwrap()).collect::<Vec<_>>().join(" "),
cwd.as_ref()
.map(|cwd| format!(" (running in folder `{}`)", cwd.display(),))
.unwrap_or_default(),
@ -88,9 +111,8 @@ pub fn run_command_with_env(
cwd: Option<&Path>,
env: Option<&HashMap<String, String>>,
) -> Result<Output, String> {
let output = get_command_inner(input, cwd, env)
.output()
.map_err(|e| command_error(input, &cwd, e))?;
let output =
get_command_inner(input, cwd, env).output().map_err(|e| command_error(input, &cwd, e))?;
check_exit_status(input, cwd, output.status, Some(&output), true)?;
Ok(output)
}
@ -99,11 +121,7 @@ pub fn run_command_with_output(
input: &[&dyn AsRef<OsStr>],
cwd: Option<&Path>,
) -> Result<(), String> {
let exit_status = get_command_inner(input, cwd, None)
.spawn()
.map_err(|e| command_error(input, &cwd, e))?
.wait()
.map_err(|e| command_error(input, &cwd, e))?;
let exit_status = exec_command(input, cwd, None)?;
check_exit_status(input, cwd, exit_status, None, true)?;
Ok(())
}
@ -113,11 +131,7 @@ pub fn run_command_with_output_and_env(
cwd: Option<&Path>,
env: Option<&HashMap<String, String>>,
) -> Result<(), String> {
let exit_status = get_command_inner(input, cwd, env)
.spawn()
.map_err(|e| command_error(input, &cwd, e))?
.wait()
.map_err(|e| command_error(input, &cwd, e))?;
let exit_status = exec_command(input, cwd, env)?;
check_exit_status(input, cwd, exit_status, None, true)?;
Ok(())
}
@ -127,11 +141,7 @@ pub fn run_command_with_output_and_env_no_err(
cwd: Option<&Path>,
env: Option<&HashMap<String, String>>,
) -> Result<(), String> {
let exit_status = get_command_inner(input, cwd, env)
.spawn()
.map_err(|e| command_error(input, &cwd, e))?
.wait()
.map_err(|e| command_error(input, &cwd, e))?;
let exit_status = exec_command(input, cwd, env)?;
check_exit_status(input, cwd, exit_status, None, false)?;
Ok(())
}
@ -164,10 +174,7 @@ pub fn cargo_install(to_install: &str) -> Result<(), String> {
pub fn get_os_name() -> Result<String, String> {
let output = run_command(&[&"uname"], None)?;
let name = std::str::from_utf8(&output.stdout)
.unwrap_or("")
.trim()
.to_string();
let name = std::str::from_utf8(&output.stdout).unwrap_or("").trim().to_string();
if !name.is_empty() {
Ok(name)
} else {
@ -274,11 +281,7 @@ fn git_clone_inner(
command.push(&"1");
}
run_command_with_output(&command, None)?;
Ok(CloneResult {
ran_clone: true,
repo_name,
repo_dir: dest.display().to_string(),
})
Ok(CloneResult { ran_clone: true, repo_name, repo_dir: dest.display().to_string() })
}
fn get_repo_name(url: &str) -> String {
@ -307,6 +310,25 @@ pub fn git_clone(
git_clone_inner(to_clone, dest, shallow_clone, repo_name)
}
pub fn create_dir<P: AsRef<Path>>(path: P) -> Result<(), String> {
fs::create_dir_all(&path).map_err(|error| {
format!("Failed to create directory `{}`: {:?}", path.as_ref().display(), error)
})
}
pub fn copy_file<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<(), String> {
fs::copy(&from, &to)
.map_err(|error| {
format!(
"Failed to copy file `{}` into `{}`: {:?}",
from.as_ref().display(),
to.as_ref().display(),
error
)
})
.map(|_| ())
}
/// This function differs from `git_clone` in how it handles *where* the repository will be cloned.
/// In `git_clone`, it is cloned in the provided path. In this function, the path you provide is
/// the parent folder. So if you pass "a" as folder and try to clone "b.git", it will be cloned into
@ -318,15 +340,15 @@ pub fn git_clone_root_dir(
) -> Result<CloneResult, String> {
let repo_name = get_repo_name(to_clone);
git_clone_inner(
to_clone,
&dest_parent_dir.join(&repo_name),
shallow_clone,
repo_name,
)
git_clone_inner(to_clone, &dest_parent_dir.join(&repo_name), shallow_clone, repo_name)
}
pub fn walk_dir<P, D, F>(dir: P, mut dir_cb: D, mut file_cb: F) -> Result<(), String>
pub fn walk_dir<P, D, F>(
dir: P,
dir_cb: &mut D,
file_cb: &mut F,
recursive: bool,
) -> Result<(), String>
where
P: AsRef<Path>,
D: FnMut(&Path) -> Result<(), String>,
@ -341,6 +363,9 @@ where
let entry_path = entry.path();
if entry_path.is_dir() {
dir_cb(&entry_path)?;
if recursive {
walk_dir(entry_path, dir_cb, file_cb, recursive)?; // Recursive call
}
} else {
file_cb(&entry_path)?;
}
@ -383,11 +408,7 @@ pub fn split_args(args: &str) -> Result<Vec<String>, String> {
}
}
if !found_end {
return Err(format!(
"Didn't find `{}` at the end of `{}`",
end,
&args[start..]
));
return Err(format!("Didn't find `{}` at the end of `{}`", end, &args[start..]));
}
} else if c == '\\' {
// We skip the escaped character.
@ -403,11 +424,7 @@ pub fn split_args(args: &str) -> Result<Vec<String>, String> {
pub fn remove_file<P: AsRef<Path> + ?Sized>(file_path: &P) -> Result<(), String> {
std::fs::remove_file(file_path).map_err(|error| {
format!(
"Failed to remove `{}`: {:?}",
file_path.as_ref().display(),
error
)
format!("Failed to remove `{}`: {:?}", file_path.as_ref().display(), error)
})
}
@ -427,6 +444,10 @@ pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> R
})
}
pub fn get_sysroot_dir() -> PathBuf {
Path::new(crate::BUILD_DIR).join("build_sysroot")
}
#[cfg(test)]
mod tests {
use super::*;