use std::{
cell::RefCell,
path::{Path, PathBuf},
};
use anyhow::Context;
use clap::{Args, ValueEnum};
use reqwest::Url;
use crate::{
config::get_config,
get_home, get_project_root,
plugin::{load_plugins, load_plugins_from_paths},
};
use super::Submodule;
thread_local! {
pub static BUILD_STEP: RefCell<Step> = RefCell::new(Step::PRE);
}
#[derive(Clone, Args)]
pub struct BuildArgs {
pub step: Option<Step>,
}
pub struct Build {
pub args: BuildArgs,
}
#[derive(Clone, Copy, ValueEnum, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Step {
PRE,
AAPT,
COMPILE,
DEX,
BUNDLE,
POST,
}
impl Build {
pub fn new(args: &BuildArgs) -> Self {
Self { args: args.clone() }
}
}
impl Submodule for Build {
fn run(&mut self) -> anyhow::Result<()> {
let order: Vec<Step> = if let Some(step) = self.args.step {
vec![step]
} else {
vec![
Step::PRE,
Step::AAPT,
Step::COMPILE,
Step::DEX,
Step::BUNDLE,
Step::POST,
]
};
let mut home = get_home().context("Failed to load plugin home")?;
home.push("plugins");
let config = get_config().context("Failed to load plugins list from config")?;
let mut paths: Vec<PathBuf> = vec![];
if let Some(plugins) = config.plugins {
paths.extend(plugins.iter().map(|(name, plugin)| {
if let Some(location) = &plugin.location {
if Url::parse(location.as_str()).is_ok() {
let mut h = home.clone();
h.push(format!("{}-{}", name.clone(), plugin.version.clone()));
h
} else {
PathBuf::from(location)
}
} else {
let mut h = home.clone();
h.push(format!("{}-{}", name.clone(), plugin.version.clone()));
h
}
}));
}
{
let mut root = get_project_root()
.context("Failed to read the project root folder")?
.clone();
root.push("plugins");
if root.exists() {
for path in (root.read_dir()?).flatten().map(|entry| entry.path()) {
if path.is_dir() {
paths.push(path);
}
}
}
}
let plugin_list = load_plugins_from_paths(paths).context("Failed to load plugins")?;
let mut map = load_plugins(plugin_list).context("Error loading plugin configurations")?;
for step in order {
BUILD_STEP.with(|s| {
*s.borrow_mut() = step;
});
if let Some(plugins) = map.get_mut(&step) {
plugins.sort_by(|a, b| b.priority.partial_cmp(&a.priority).unwrap());
'_loop: for plugin in plugins {
if let Some((inputs, outputs)) = &plugin.dependents {
for output in outputs {
let is_stale = inputs
.iter()
.any(|input| is_file_newer(input, output).unwrap_or(false));
if !is_stale {
continue '_loop;
}
}
}
let exe = plugin.load().context(format!(
"Error loading plugin: {}:{} at build step {:?}",
plugin.name, plugin.version, plugin.step
))?;
let chunk = exe.load().context(format!(
"Error loading lua code for {}:{} at build step {:?}",
plugin.name, plugin.version, plugin.step
))?;
chunk.exec().context(format!(
"Failed to execute plugin code {:?} for plugin {}:{} at build step {:?}",
plugin.path, plugin.name, plugin.version, plugin.step
))?;
}
}
}
Ok(())
}
}
pub fn is_file_newer(a: &Path, b: &Path) -> std::io::Result<bool> {
if !b.exists() {
return Ok(true);
}
if !a.exists() {
return Ok(false);
}
let metadata_a = match a.metadata() {
Ok(metadata) => metadata,
Err(err) => return Err(err),
};
let metadata_b = match b.metadata() {
Ok(metadata) => metadata,
Err(err) => return Err(err),
};
let modification_a = match metadata_a.modified() {
Ok(modified) => modified,
Err(err) => return Err(err),
};
let modification_b = match metadata_b.modified() {
Ok(modified) => modified,
Err(err) => return Err(err),
};
Ok(modification_a > modification_b)
}