mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-30 20:44:34 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			241 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::collections::HashMap;
 | |
| use std::ffi::OsStr;
 | |
| use std::fmt::Debug;
 | |
| use std::fs;
 | |
| use std::path::Path;
 | |
| use std::process::{Command, ExitStatus, Output};
 | |
| 
 | |
| fn get_command_inner(
 | |
|     input: &[&dyn AsRef<OsStr>],
 | |
|     cwd: Option<&Path>,
 | |
|     env: Option<&HashMap<String, String>>,
 | |
| ) -> Command {
 | |
|     let (cmd, args) = match input {
 | |
|         [] => panic!("empty command"),
 | |
|         [cmd, args @ ..] => (cmd, args),
 | |
|     };
 | |
|     let mut command = Command::new(cmd);
 | |
|     command.args(args);
 | |
|     if let Some(cwd) = cwd {
 | |
|         command.current_dir(cwd);
 | |
|     }
 | |
|     if let Some(env) = env {
 | |
|         command.envs(env.iter().map(|(k, v)| (k.as_str(), v.as_str())));
 | |
|     }
 | |
|     command
 | |
| }
 | |
| 
 | |
| fn check_exit_status(
 | |
|     input: &[&dyn AsRef<OsStr>],
 | |
|     cwd: Option<&Path>,
 | |
|     exit_status: ExitStatus,
 | |
| ) -> 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(),
 | |
|         ))
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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(" "),
 | |
|         cwd.as_ref()
 | |
|             .map(|cwd| format!(" (running in folder `{}`)", cwd.display(),))
 | |
|             .unwrap_or_default(),
 | |
|     )
 | |
| }
 | |
| 
 | |
| pub fn run_command(input: &[&dyn AsRef<OsStr>], cwd: Option<&Path>) -> Result<Output, String> {
 | |
|     run_command_with_env(input, cwd, None)
 | |
| }
 | |
| 
 | |
| pub fn run_command_with_env(
 | |
|     input: &[&dyn AsRef<OsStr>],
 | |
|     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))?;
 | |
|     check_exit_status(input, cwd, output.status)?;
 | |
|     Ok(output)
 | |
| }
 | |
| 
 | |
| 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))?;
 | |
|     check_exit_status(input, cwd, exit_status)?;
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| pub fn run_command_with_output_and_env(
 | |
|     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)?;
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| pub fn cargo_install(to_install: &str) -> Result<(), String> {
 | |
|     let output = run_command(&[&"cargo", &"install", &"--list"], None)?;
 | |
| 
 | |
|     let to_install_needle = format!("{to_install} ");
 | |
|     // cargo install --list returns something like this:
 | |
|     //
 | |
|     // mdbook-toc v0.8.0:
 | |
|     //     mdbook-toc
 | |
|     // rust-reduce v0.1.0:
 | |
|     //     rust-reduce
 | |
|     //
 | |
|     // We are only interested into the command name so we only look for lines ending with `:`.
 | |
|     if String::from_utf8(output.stdout)
 | |
|         .unwrap()
 | |
|         .lines()
 | |
|         .any(|line| line.ends_with(':') && line.starts_with(&to_install_needle))
 | |
|     {
 | |
|         return Ok(());
 | |
|     }
 | |
|     // We voluntarily ignore this error.
 | |
|     if run_command_with_output(&[&"cargo", &"install", &to_install], None).is_err() {
 | |
|         println!("Skipping installation of `{to_install}`");
 | |
|     }
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| 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();
 | |
|     if !name.is_empty() {
 | |
|         Ok(name)
 | |
|     } else {
 | |
|         Err("Failed to retrieve the OS name".to_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())
 | |
| }
 | |
| 
 | |
| 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(),
 | |
|             )
 | |
|         }
 | |
|     };
 | |
|     match content
 | |
|         .split('\n')
 | |
|         .map(|line| line.trim())
 | |
|         .filter(|line| !line.is_empty())
 | |
|         .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()),
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub struct CloneResult {
 | |
|     pub ran_clone: bool,
 | |
|     pub repo_name: 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());
 | |
|     if dest.is_dir() {
 | |
|         return Ok(CloneResult {
 | |
|             ran_clone: false,
 | |
|             repo_name,
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     run_command_with_output(&[&"git", &"clone", &to_clone, &dest], None)?;
 | |
|     Ok(CloneResult {
 | |
|         ran_clone: true,
 | |
|         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>,
 | |
|     D: FnMut(&Path) -> Result<(), String>,
 | |
|     F: FnMut(&Path) -> Result<(), String>,
 | |
| {
 | |
|     let dir = dir.as_ref();
 | |
|     for entry in fs::read_dir(dir)
 | |
|         .map_err(|error| format!("Failed to read dir `{}`: {:?}", dir.display(), error))?
 | |
|     {
 | |
|         let entry = entry
 | |
|             .map_err(|error| format!("Failed to read entry in `{}`: {:?}", dir.display(), error))?;
 | |
|         let entry_path = entry.path();
 | |
|         if entry_path.is_dir() {
 | |
|             dir_cb(&entry_path)?;
 | |
|         } else {
 | |
|             file_cb(&entry_path)?;
 | |
|         }
 | |
|     }
 | |
|     Ok(())
 | |
| }
 | 
