use anyhow::{anyhow, Context, Result};
use std::path::{Path, PathBuf};
use std::process::Command;
use whisker_cng::{discover_plugins, DiscoveredPlugin, Engine, SubprocessPlugin};
use whisker_config::Config;
use whisker_dev_server::Target;
pub fn sync_for_target(
target: Target,
app_config: &Config,
crate_dir: &Path,
workspace_root: &Path,
package: &str,
) -> Result<PlatformSync> {
match target {
Target::Android => sync_android(app_config, crate_dir, workspace_root, package),
Target::IosSimulator => sync_ios(app_config, crate_dir, workspace_root, package),
}
}
#[derive(Debug, Clone)]
pub struct PlatformSync {
pub gen_dir: PathBuf,
pub regenerated: bool,
}
const WHISKER_SDK_VERSION: &str = "0.1.1";
const WHISKER_GRADLE_PLUGIN_VERSION: &str = "0.4.0";
const WHISKER_MAVEN_URL: &str = "https://whiskerrs.github.io/whisker/maven";
const LYNX_MAVEN_URL: &str = "https://whiskerrs.github.io/lynx/maven";
fn sync_android(
app_config: &Config,
crate_dir: &Path,
workspace_root: &Path,
package: &str,
) -> Result<PlatformSync> {
let workspace_path = workspace_root.to_path_buf();
let engine = build_engine_with_discovered_plugins(workspace_root, package)?;
let inputs = whisker_cng::android::inputs_from_with_engine(
&engine,
app_config,
package.replace('-', "_"),
workspace_path,
package.to_string(),
WHISKER_SDK_VERSION.to_string(),
WHISKER_GRADLE_PLUGIN_VERSION.to_string(),
WHISKER_MAVEN_URL.to_string(),
LYNX_MAVEN_URL.to_string(),
)?;
let gen_dir = crate_dir.join("gen/android");
let regenerated = whisker_cng::sync_android(&gen_dir, &inputs).context("render gen/android")?;
Ok(PlatformSync {
gen_dir,
regenerated,
})
}
fn sync_ios(
app_config: &Config,
crate_dir: &Path,
workspace_root: &Path,
package: &str,
) -> Result<PlatformSync> {
let gen_dir = crate_dir.join("gen/ios");
let whisker_runtime = workspace_root.join("platforms/ios");
let whisker_modules = gen_dir.join("whisker_modules");
let engine = build_engine_with_discovered_plugins(workspace_root, package)?;
let inputs = whisker_cng::ios::inputs_from_with_engine(
&engine,
app_config,
whisker_runtime,
whisker_modules,
workspace_root.to_path_buf(),
package.to_string(),
)?;
let regenerated = whisker_cng::sync_ios(&gen_dir, &inputs).context("render gen/ios")?;
Ok(PlatformSync {
gen_dir,
regenerated,
})
}
fn build_engine_with_discovered_plugins(
workspace_root: &Path,
user_package: &str,
) -> Result<Engine> {
let manifest_path = workspace_root.join("Cargo.toml");
let discovered = discover_plugins(&manifest_path, user_package)
.with_context(|| format!("discover Whisker CNG plugins for `{user_package}`"))?;
let mut engine = Engine::with_builtins();
if discovered.is_empty() {
return Ok(engine);
}
build_discovered_plugins(workspace_root, &discovered)?;
let target_dir = workspace_root.join("target/debug");
for plugin in discovered {
let binary_path = target_dir.join(&plugin.bin_target_name);
if !binary_path.exists() {
return Err(anyhow!(
"discovered plugin `{}` (from crate `{}`) declared bin = `{}` \
but `cargo build` did not produce `{}`. Check that the bin \
target is declared correctly in `{}/Cargo.toml`.",
plugin.name,
plugin.source_crate,
plugin.bin_target_name,
binary_path.display(),
plugin.source_manifest_dir.display(),
));
}
engine.register_subprocess(
SubprocessPlugin::new(plugin.name.clone(), binary_path)
.after(plugin.after.clone())
.before(plugin.before.clone()),
);
}
Ok(engine)
}
fn build_discovered_plugins(workspace_root: &Path, discovered: &[DiscoveredPlugin]) -> Result<()> {
let bins: Vec<&str> = discovered
.iter()
.map(|p| p.bin_target_name.as_str())
.collect();
let step = whisker_build::ui::step("compile", format!("plugins ({})", bins.join(", ")));
let mut cmd = Command::new("cargo");
cmd.arg("build").current_dir(workspace_root);
for plugin in discovered {
cmd.arg("--bin")
.arg(&plugin.bin_target_name)
.arg("--package")
.arg(&plugin.source_crate);
}
let status = step
.pipe(&mut cmd)
.with_context(|| "spawn `cargo build` for discovered Whisker CNG plugin binaries")?;
if !status.success() {
step.fail(format!("{status}"));
return Err(anyhow!(
"`cargo build` for discovered Whisker CNG plugin binaries exited with {status}. \
Re-run with `RUST_BACKTRACE=1 cargo build --bin <bin> --package <crate>` to see \
the underlying compile error."
));
}
step.done("");
Ok(())
}