Merge commit 'b385428e3ddf330805241e7758e773f933357c4b' into subtree-update_cg_gcc_2024-03-05
This commit is contained in:
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, ExitStatus, Output};
|
||||
|
||||
fn get_command_inner(
|
||||
@@ -29,22 +29,40 @@ fn check_exit_status(
|
||||
input: &[&dyn AsRef<OsStr>],
|
||||
cwd: Option<&Path>,
|
||||
exit_status: ExitStatus,
|
||||
output: Option<&Output>,
|
||||
show_err: bool,
|
||||
) -> Result<(), String> {
|
||||
if exit_status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(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(),
|
||||
exit_status.code(),
|
||||
))
|
||||
return Ok(());
|
||||
}
|
||||
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(),
|
||||
exit_status.code()
|
||||
);
|
||||
let input = input.iter().map(|i| i.as_ref()).collect::<Vec<&OsStr>>();
|
||||
if show_err {
|
||||
eprintln!("Command `{:?}` failed", input);
|
||||
}
|
||||
if let Some(output) = output {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if !stdout.is_empty() {
|
||||
error.push_str("\n==== STDOUT ====\n");
|
||||
error.push_str(&*stdout);
|
||||
}
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
if !stderr.is_empty() {
|
||||
error.push_str("\n==== STDERR ====\n");
|
||||
error.push_str(&*stderr);
|
||||
}
|
||||
}
|
||||
Err(error)
|
||||
}
|
||||
|
||||
fn command_error<D: Debug>(input: &[&dyn AsRef<OsStr>], cwd: &Option<&Path>, error: D) -> String {
|
||||
@@ -73,7 +91,7 @@ pub fn run_command_with_env(
|
||||
let output = get_command_inner(input, cwd, env)
|
||||
.output()
|
||||
.map_err(|e| command_error(input, &cwd, e))?;
|
||||
check_exit_status(input, cwd, output.status)?;
|
||||
check_exit_status(input, cwd, output.status, Some(&output), true)?;
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
@@ -86,7 +104,7 @@ pub fn run_command_with_output(
|
||||
.map_err(|e| command_error(input, &cwd, e))?
|
||||
.wait()
|
||||
.map_err(|e| command_error(input, &cwd, e))?;
|
||||
check_exit_status(input, cwd, exit_status)?;
|
||||
check_exit_status(input, cwd, exit_status, None, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -100,7 +118,21 @@ pub fn run_command_with_output_and_env(
|
||||
.map_err(|e| command_error(input, &cwd, e))?
|
||||
.wait()
|
||||
.map_err(|e| command_error(input, &cwd, e))?;
|
||||
check_exit_status(input, cwd, exit_status)?;
|
||||
check_exit_status(input, cwd, exit_status, None, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_command_with_output_and_env_no_err(
|
||||
input: &[&dyn AsRef<OsStr>],
|
||||
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))?;
|
||||
check_exit_status(input, cwd, exit_status, None, false)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -143,80 +175,157 @@ pub fn get_os_name() -> Result<String, String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rustc_host_triple() -> Result<String, String> {
|
||||
let output = run_command(&[&"rustc", &"-vV"], None)?;
|
||||
let content = std::str::from_utf8(&output.stdout).unwrap_or("");
|
||||
|
||||
for line in content.split('\n').map(|line| line.trim()) {
|
||||
if !line.starts_with("host:") {
|
||||
continue;
|
||||
}
|
||||
return Ok(line.split(':').nth(1).unwrap().trim().to_string());
|
||||
}
|
||||
Err("Cannot find host triple".to_string())
|
||||
#[derive(Default, PartialEq)]
|
||||
pub struct RustcVersionInfo {
|
||||
pub short: String,
|
||||
pub version: String,
|
||||
pub host: Option<String>,
|
||||
pub commit_hash: Option<String>,
|
||||
pub commit_date: Option<String>,
|
||||
}
|
||||
|
||||
pub fn get_gcc_path() -> Result<String, String> {
|
||||
let content = match fs::read_to_string("gcc_path") {
|
||||
Ok(content) => content,
|
||||
Err(_) => {
|
||||
return Err(
|
||||
"Please put the path to your custom build of libgccjit in the file \
|
||||
`gcc_path`, see Readme.md for details"
|
||||
.into(),
|
||||
)
|
||||
pub fn rustc_toolchain_version_info(toolchain: &str) -> Result<RustcVersionInfo, String> {
|
||||
rustc_version_info_inner(None, Some(toolchain))
|
||||
}
|
||||
|
||||
pub fn rustc_version_info(rustc: Option<&str>) -> Result<RustcVersionInfo, String> {
|
||||
rustc_version_info_inner(rustc, None)
|
||||
}
|
||||
|
||||
fn rustc_version_info_inner(
|
||||
rustc: Option<&str>,
|
||||
toolchain: Option<&str>,
|
||||
) -> Result<RustcVersionInfo, String> {
|
||||
let output = if let Some(toolchain) = toolchain {
|
||||
run_command(&[&rustc.unwrap_or("rustc"), &toolchain, &"-vV"], None)
|
||||
} else {
|
||||
run_command(&[&rustc.unwrap_or("rustc"), &"-vV"], None)
|
||||
}?;
|
||||
let content = std::str::from_utf8(&output.stdout).unwrap_or("");
|
||||
|
||||
let mut info = RustcVersionInfo::default();
|
||||
let mut lines = content.split('\n');
|
||||
info.short = match lines.next() {
|
||||
Some(s) => s.to_string(),
|
||||
None => return Err("failed to retrieve rustc version".to_string()),
|
||||
};
|
||||
|
||||
for line in lines.map(|line| line.trim()) {
|
||||
match line.split_once(':') {
|
||||
Some(("host", data)) => info.host = Some(data.trim().to_string()),
|
||||
Some(("release", data)) => info.version = data.trim().to_string(),
|
||||
Some(("commit-hash", data)) => info.commit_hash = Some(data.trim().to_string()),
|
||||
Some(("commit-date", data)) => info.commit_date = Some(data.trim().to_string()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if info.version.is_empty() {
|
||||
Err("failed to retrieve rustc version".to_string())
|
||||
} else {
|
||||
Ok(info)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_toolchain() -> Result<String, String> {
|
||||
let content = match fs::read_to_string("rust-toolchain") {
|
||||
Ok(content) => content,
|
||||
Err(_) => return Err("No `rust-toolchain` file found".to_string()),
|
||||
};
|
||||
match content
|
||||
.split('\n')
|
||||
.map(|line| line.trim())
|
||||
.filter(|line| !line.is_empty())
|
||||
.filter_map(|line| {
|
||||
if !line.starts_with("channel") {
|
||||
return None;
|
||||
}
|
||||
line.split('"').skip(1).next()
|
||||
})
|
||||
.next()
|
||||
{
|
||||
Some(gcc_path) => {
|
||||
let path = Path::new(gcc_path);
|
||||
if !path.exists() {
|
||||
Err(format!(
|
||||
"Path `{}` contained in the `gcc_path` file doesn't exist",
|
||||
gcc_path,
|
||||
))
|
||||
} else {
|
||||
Ok(gcc_path.into())
|
||||
}
|
||||
}
|
||||
None => Err("No path found in `gcc_path` file".into()),
|
||||
Some(toolchain) => Ok(toolchain.to_string()),
|
||||
None => Err("Couldn't find `channel` in `rust-toolchain` file".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CloneResult {
|
||||
pub ran_clone: bool,
|
||||
pub repo_name: String,
|
||||
pub repo_dir: String,
|
||||
}
|
||||
|
||||
pub fn git_clone(to_clone: &str, dest: Option<&Path>) -> Result<CloneResult, String> {
|
||||
let repo_name = to_clone.split('/').last().unwrap();
|
||||
let repo_name = match repo_name.strip_suffix(".git") {
|
||||
Some(n) => n.to_string(),
|
||||
None => repo_name.to_string(),
|
||||
};
|
||||
|
||||
let dest = dest
|
||||
.map(|dest| dest.join(&repo_name))
|
||||
.unwrap_or_else(|| Path::new(&repo_name).into());
|
||||
fn git_clone_inner(
|
||||
to_clone: &str,
|
||||
dest: &Path,
|
||||
shallow_clone: bool,
|
||||
repo_name: String,
|
||||
) -> Result<CloneResult, String> {
|
||||
if dest.is_dir() {
|
||||
return Ok(CloneResult {
|
||||
ran_clone: false,
|
||||
repo_name,
|
||||
repo_dir: dest.display().to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
run_command_with_output(&[&"git", &"clone", &to_clone, &dest], None)?;
|
||||
let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"git", &"clone", &to_clone, &dest];
|
||||
if shallow_clone {
|
||||
command.push(&"--depth");
|
||||
command.push(&"1");
|
||||
}
|
||||
run_command_with_output(&command, None)?;
|
||||
Ok(CloneResult {
|
||||
ran_clone: true,
|
||||
repo_name,
|
||||
repo_dir: dest.display().to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_repo_name(url: &str) -> String {
|
||||
let repo_name = url.split('/').last().unwrap();
|
||||
match repo_name.strip_suffix(".git") {
|
||||
Some(n) => n.to_string(),
|
||||
None => repo_name.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_clone(
|
||||
to_clone: &str,
|
||||
dest: Option<&Path>,
|
||||
shallow_clone: bool,
|
||||
) -> Result<CloneResult, String> {
|
||||
let repo_name = get_repo_name(to_clone);
|
||||
let tmp: PathBuf;
|
||||
|
||||
let dest = match dest {
|
||||
Some(dest) => dest,
|
||||
None => {
|
||||
tmp = repo_name.clone().into();
|
||||
&tmp
|
||||
}
|
||||
};
|
||||
git_clone_inner(to_clone, dest, shallow_clone, repo_name)
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// `a/b`.
|
||||
pub fn git_clone_root_dir(
|
||||
to_clone: &str,
|
||||
dest_parent_dir: &Path,
|
||||
shallow_clone: bool,
|
||||
) -> 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,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn walk_dir<P, D, F>(dir: P, mut dir_cb: D, mut file_cb: F) -> Result<(), String>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
@@ -238,3 +347,105 @@ where
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn split_args(args: &str) -> Result<Vec<String>, String> {
|
||||
let mut out = Vec::new();
|
||||
let mut start = 0;
|
||||
let args = args.trim();
|
||||
let mut iter = args.char_indices().peekable();
|
||||
|
||||
while let Some((pos, c)) = iter.next() {
|
||||
if c == ' ' {
|
||||
out.push(args[start..pos].to_string());
|
||||
let mut found_start = false;
|
||||
while let Some((pos, c)) = iter.peek() {
|
||||
if *c != ' ' {
|
||||
start = *pos;
|
||||
found_start = true;
|
||||
break;
|
||||
} else {
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
if !found_start {
|
||||
return Ok(out);
|
||||
}
|
||||
} else if c == '"' || c == '\'' {
|
||||
let end = c;
|
||||
let mut found_end = false;
|
||||
while let Some((_, c)) = iter.next() {
|
||||
if c == end {
|
||||
found_end = true;
|
||||
break;
|
||||
} else if c == '\\' {
|
||||
// We skip the escaped character.
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
if !found_end {
|
||||
return Err(format!(
|
||||
"Didn't find `{}` at the end of `{}`",
|
||||
end,
|
||||
&args[start..]
|
||||
));
|
||||
}
|
||||
} else if c == '\\' {
|
||||
// We skip the escaped character.
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
let s = args[start..].trim();
|
||||
if !s.is_empty() {
|
||||
out.push(s.to_string());
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> Result<(), String> {
|
||||
#[cfg(windows)]
|
||||
let symlink = std::os::windows::fs::symlink_file;
|
||||
#[cfg(not(windows))]
|
||||
let symlink = std::os::unix::fs::symlink;
|
||||
|
||||
symlink(&original, &link).map_err(|err| {
|
||||
format!(
|
||||
"failed to create a symlink `{}` to `{}`: {:?}",
|
||||
original.as_ref().display(),
|
||||
link.as_ref().display(),
|
||||
err,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_split_args() {
|
||||
// Missing `"` at the end.
|
||||
assert!(split_args("\"tada").is_err());
|
||||
// Missing `'` at the end.
|
||||
assert!(split_args("\'tada").is_err());
|
||||
|
||||
assert_eq!(
|
||||
split_args("a \"b\" c"),
|
||||
Ok(vec!["a".to_string(), "\"b\"".to_string(), "c".to_string()])
|
||||
);
|
||||
// Trailing whitespace characters.
|
||||
assert_eq!(
|
||||
split_args(" a \"b\" c "),
|
||||
Ok(vec!["a".to_string(), "\"b\"".to_string(), "c".to_string()])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user