use std::{
fs,
io::{self, Write},
path::{Path, PathBuf},
process::Command,
};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error: {0:?}")]
Io(#[from] io::Error),
#[error("Metadata error: {0:?}")]
Metadata(#[from] super::metadata::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
pub fn bundle(app_path: &Path, target_path: &Path, executable_name: &str) -> Result<PathBuf> {
let cef_path = cef_dll_sys::get_cef_dir().unwrap();
copy_directory(&cef_path, app_path)?;
const LOCALES_DIR: &str = "locales";
copy_directory(&cef_path.join(LOCALES_DIR), &app_path.join(LOCALES_DIR))?;
copy_app(app_path, target_path, executable_name)
}
pub fn build_bundle(app_path: &Path, executable_name: &str, release: bool) -> Result<PathBuf> {
let cargo_metadata = super::metadata::get_cargo_metadata()?;
let target_path =
cargo_metadata
.target_directory()
.join(if release { "release" } else { "debug" });
cargo_build(executable_name, release)?;
bundle(app_path, &target_path, executable_name)
}
const MANIFEST_CONTENT: &[u8] = include_bytes!("cef-app.exe.manifest");
fn copy_app(app_path: &Path, target_path: &Path, executable_name: &str) -> Result<PathBuf> {
let mut manifest_file =
fs::File::create(app_path.join(format!("{executable_name}.exe.manifest")))?;
manifest_file.write_all(MANIFEST_CONTENT)?;
#[cfg(feature = "sandbox")]
{
let dll_name = format!("{executable_name}.dll");
let dll_path = app_path.join(&dll_name);
let target_dll = target_path.join(&dll_name);
fs::copy(&target_dll, &dll_path)?;
let pdb_name = format!("{executable_name}.pdb");
let pdb_path = app_path.join(&pdb_name);
let target_pdb = target_path.join(&pdb_name);
fs::copy(&target_pdb, &pdb_path)?;
let executable_name = format!("{executable_name}.exe");
let executable_path = app_path.join(&executable_name);
let cef_path = cef_dll_sys::get_cef_dir().unwrap();
let target_executable = cef_path.join("bootstrap.exe");
fs::copy(&target_executable, &executable_path)?;
Ok(executable_path)
}
#[cfg(not(feature = "sandbox"))]
{
let executable_name = format!("{executable_name}.exe");
let executable_path = app_path.join(&executable_name);
let target_executable = target_path.join(executable_name);
fs::copy(&target_executable, &executable_path)?;
Ok(executable_path)
}
}
fn copy_directory(src: &Path, dst: &Path) -> io::Result<()> {
fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let dst_path = dst.join(entry.file_name());
if entry.file_type()?.is_file()
&& !entry
.path()
.extension()
.map(|ext| ext == "exe")
.unwrap_or_default()
{
fs::copy(entry.path(), &dst_path)?;
}
}
Ok(())
}
fn cargo_build(name: &str, release: bool) -> Result<()> {
println!("Building {name}...");
let mut args = vec!["build"];
if release {
args.push("--release");
}
#[cfg(feature = "sandbox")]
args.extend_from_slice(&["-p", name, "--lib"]);
#[cfg(not(feature = "sandbox"))]
args.extend_from_slice(&["--no-default-features", "--bin", name]);
let status = Command::new(super::cargo_path()).args(args).status()?;
if status.success() {
Ok(())
} else {
Err(io::Error::from(io::ErrorKind::Interrupted).into())
}
}