use crate::tools::{find_files_because_the_user_is_too_lazy, install_to_bin};
use colored::Colorize;
use dialoguer::console::strip_ansi_codes;
use path_absolutize::Absolutize;
use std::{
fs,
io::{BufRead, Write},
path::PathBuf,
process::exit,
};
use unin_bin::{UninPackage, return_registry_path};
pub fn build_make(directory: PathBuf, noinstall: bool) {
let dir: PathBuf = PathBuf::from("/usr/local/bin");
let before_install_files = find_files_because_the_user_is_too_lazy(dir);
let absolute_path = directory.absolutize().unwrap();
let makefile_path = PathBuf::from(format!("{}/Makefile", absolute_path.to_str().unwrap()));
println!(
"Makefile path: {}",
makefile_path.to_str().unwrap().yellow()
);
let num_cpus = num_cpus::get();
let mut make_process = std::process::Command::new("make")
.args(["-j", &num_cpus.to_string()])
.current_dir(&directory)
.stderr(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.spawn()
.expect("Couldn't start the make process.");
let stderr = make_process.stderr.take().unwrap();
let reader = std::io::BufReader::new(stderr);
let mut full_content = String::new();
let mut has_error = false;
for line in reader.lines() {
if let Ok(mut content) = line {
let raw_content = content.clone();
if content.contains("CC")
|| content.contains("LD")
|| content.contains("LINK")
|| content.contains("AR")
|| content.contains("checking")
|| content.contains("CCLD")
|| content.contains("LDSHARED")
|| content.contains("RANLIB")
|| content.contains("Building")
|| content.contains("Built")
{
content = strip_ansi_codes(&content.to_string().to_owned()).to_string();
content = content.trim().to_string();
let mut content_vec = content
.split_whitespace()
.map(String::from)
.collect::<Vec<String>>();
content_vec[0] = content_vec[0].blue().bold().to_string();
content_vec[1..].iter_mut().for_each(|s| {
*s = s.purple().bold().to_string();
});
content = content_vec.join(" ");
print!("\r\x1B[K{}", content.trim_end());
std::io::stdout().flush().unwrap();
} else if content.contains("error:") {
has_error = true;
full_content.push_str(&raw_content.clone());
full_content.push('\n');
}
full_content.push_str(&raw_content.clone());
full_content.push('\n');
}
}
let _waiter = make_process
.wait()
.unwrap_or_else(|_| panic!("Couldn't wait for the make process to finish."));
if has_error {
println!("{}", "\nAn error occurred while compiling.".red().bold());
println!("{}", "This might be due to a missing dependency or unset environment variables. Check README.md for exact building information.".red().bold());
println!(
"Full error: \n{}",
full_content.trim_end().replace("error:", &"error".red())
);
exit(0)
}
if noinstall {
println!();
println!(
"As make does not provide a reliable to expose binary locations, I can't tell you the paths to the binaries. Find them yourself, idk bro."
);
println!(
"If you still see some file paths shown then that's a small scan, okay? Don't trust this completely."
);
let binaries = find_files_because_the_user_is_too_lazy(directory.clone());
binaries
.iter()
.for_each(|b| println!("{}", b.to_str().unwrap()));
exit(0) }
println!(
"An installation using \"make install\" will be attempted. This will not work if the Makefile does not define an \"install\" rule."
);
let file_contents = fs::read_to_string(makefile_path.clone()).unwrap();
let mut prefix_argument = String::new();
let mut path_already_defined: bool = false;
let mut has_install_rule: bool = false;
for line in file_contents.lines() {
if line.contains("/usr/local/") {
path_already_defined = true;
break;
} else if line.contains(".PHONY : install") {
has_install_rule = true;
continue;
} else {
path_already_defined = false;
continue;
}
}
if !has_install_rule {
println!("The Makefile does not define an \"install\" rule. Aborting.");
exit(2);
} else {
println!("The Makefile defines an \"install\" rule. Continuing.");
}
if !path_already_defined {
prefix_argument = "PREFIX=/usr/local".trim().to_string();
} else {
prefix_argument = prefix_argument.trim().to_string();
}
let registry_path = return_registry_path();
let before_install = find_files_because_the_user_is_too_lazy(registry_path.clone());
before_install
.iter()
.for_each(|b| println!("{}", b.to_str().unwrap()));
let mut installation_process = std::process::Command::new("sudo")
.arg("make")
.arg(prefix_argument)
.arg("install")
.current_dir(&directory)
.stderr(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.spawn()
.expect("Couldn't start the make process.");
let stderr = installation_process.stderr.take().unwrap();
let reader = std::io::BufReader::new(stderr);
let mut full_content = String::new();
let mut has_error = false;
for mut line in reader.lines() {
if let Ok(ref mut content) = line {
let raw_content = content.clone();
if line.as_mut().unwrap().trim().contains("error") {
has_error = true;
print!("\r\x1B[KError: {}", line.expect("REASON").red());
std::thread::sleep(std::time::Duration::from_millis(1000));
full_content.push_str(&raw_content.clone());
full_content.push('\n');
continue;
} else {
print!("\r\x1B[K{}", line.expect("REASON").purple());
}
}
}
let waiter = installation_process
.wait()
.unwrap_or_else(|_| panic!("Couldn't wait for the make process to finish."));
if !waiter.success() {
println!("Installation failed.");
}
let after_install = find_files_because_the_user_is_too_lazy(registry_path.clone());
let uniques = crate::tools::only_unique(&before_install_files, &after_install);
install_to_bin(uniques).unwrap();
if has_error {
println!("{}", "\nAn error occurred while installing.".red().bold());
println!(
"Full error: \n{}",
full_content.trim_end().replace("error:", &"error:".red())
);
exit(0)
}
println!("Installation finished successfully.");
println!("You can now use the binaries in your PATH.");
}
pub fn clean(directory: PathBuf) {
let mut clean_process = std::process::Command::new("make")
.args(["clean"])
.current_dir(&directory)
.stderr(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.spawn()
.expect("Couldn't start the make process.");
let waiter = clean_process
.wait()
.unwrap_or_else(|_| panic!("Couldn't wait for the make process to finish."));
if waiter.success() {
println!("Cleaned successfully.");
} else {
println!("Cleaning failed.");
println!(
"If you want, you can try to clean manually by running \"make clean\" in the project root."
)
}
}