use std::env;
use crate::build::{LibraryType, BuildStepError};
use std::path::PathBuf;
use std::sync::Arc;
use std::ops::Deref;
use std::fmt::{Debug, Formatter};
use std::process::{Command};
macro_rules! resolve_env_var {
($build_name:ident, $key:expr) => {{
let full_name = format!("rbuild_{}_{}", $build_name, $key);
let fallback_name = format!("rbuild_{}", $key);
if let Ok(value) = env::var(full_name) {
Some(value)
} else if let Ok(value) = env::var(fallback_name) {
Some(value)
} else {
None
}
}};
}
pub enum BuildLibraryTypeError {
NotPresent,
InvalidValue(String)
}
fn parse_build_library_type(library_type: &str) -> Option<LibraryType> {
let library_type = library_type.to_lowercase();
match library_type.as_ref() {
"static" => Some(LibraryType::Static),
"shared" => Some(LibraryType::Shared),
_ => None
}
}
pub fn build_library_type(build_name: &str) -> Result<LibraryType, BuildLibraryTypeError> {
if let Some(value) = resolve_env_var!(build_name, "library_type") {
parse_build_library_type(&value)
.ok_or(BuildLibraryTypeError::InvalidValue(value))
} else {
Err(BuildLibraryTypeError::NotPresent)
}
}
pub fn install_prefix(build_name: &str) -> Option<PathBuf> {
resolve_env_var!(build_name, "install_prefix")
.map(PathBuf::from)
}
struct TemporaryPathInner {
path: PathBuf,
released: bool
}
impl Drop for TemporaryPathInner {
fn drop(&mut self) {
if !self.released {
if let Err(error) = std::fs::remove_dir_all(&self.path) {
eprintln!("Failed to remote temporary directory: {:?}", error);
}
}
}
}
#[derive(Clone)]
pub struct TemporaryPath {
inner: Arc<TemporaryPathInner>
}
impl TemporaryPath {
pub fn from_persistent(path: PathBuf) -> Self {
TemporaryPath{
inner: Arc::new(TemporaryPathInner{ path, released: true })
}
}
pub fn path(&self) -> &PathBuf {
&self.inner.path
}
pub fn release(&self) -> &Self {
let mut_released = unsafe { &mut *(&self.inner.released as *const bool as *mut bool) };
*mut_released = true;
self
}
}
impl Deref for TemporaryPath {
type Target = PathBuf;
fn deref(&self) -> &Self::Target {
self.path()
}
}
impl Debug for TemporaryPath {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.inner.path.fmt(f)
}
}
pub fn create_temporary_path(folder_name: &str, base_dir: Option<PathBuf>) -> std::io::Result<TemporaryPath> {
let path = base_dir.unwrap_or_else(env::temp_dir).join(folder_name);
std::fs::create_dir_all(&path).map(|_| TemporaryPath{ inner: Arc::new(TemporaryPathInner{ path, released: false })})
}
fn verbose_commands_enabled() -> bool {
true
}
pub fn execute_build_command(command: &mut Command, error_detail: &str) -> Result<(String, String), BuildStepError> {
let output = command.output()
.map_err(|err| BuildStepError::new_io(error_detail, err))?;
let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
if verbose_commands_enabled() {
let error_code = if let Some(code) = output.status.code() { format!("{}", code) } else { "no error code".to_owned() };
println!("> {:?} -> {}", command, error_code);
if !stdout.is_empty() {
println!("----------------- Stdout -----------------");
println!("{}", &stdout);
}
if !stderr.is_empty() {
println!("----------------- Stderr -----------------");
println!("{}", &stderr);
}
}
if !output.status.success() {
return Err(BuildStepError::new(error_detail.to_owned(), stdout, stderr));
}
Ok((stdout, stderr))
}