Files
covenant-setup/build.rs
T

130 lines
4.4 KiB
Rust

use embed_manifest::embed_manifest;
use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
const UI_BUNDLE_MAGIC: &[u8] = b"COVENANT_SETUP_UI_BUNDLE_V1\n";
fn main() {
println!("cargo:rustc-check-cfg=cfg(covenant_setup_embedded_ui)");
publish_csharp_ui();
embed_manifest(embed_manifest::new_manifest("Comctl32"))
.expect("unable to embed application manifest");
}
fn publish_csharp_ui() {
println!("cargo:rerun-if-changed=ui/Covenant.Setup.Ui/Covenant.Setup.Ui.csproj");
println!("cargo:rerun-if-changed=ui/Covenant.Setup.Ui/Program.cs");
println!("cargo:rerun-if-changed=ui/Covenant.Setup.Ui/InstallerUiWindow.cs");
println!("cargo:rerun-if-changed=ui/Covenant.Setup.Ui/UiProtocol.cs");
println!("cargo:rerun-if-changed=ui/Covenant.Setup.Ui/app.manifest");
let manifest_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let project = manifest_dir.join("ui/Covenant.Setup.Ui/Covenant.Setup.Ui.csproj");
let publish_dir = out_dir.join("csharp-ui");
let dotnet_home = out_dir.join("dotnet-home");
let status = Command::new("dotnet")
.env("DOTNET_CLI_HOME", &dotnet_home)
.env("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1")
.env("DOTNET_CLI_TELEMETRY_OPTOUT", "1")
.arg("publish")
.arg(&project)
.arg("--nologo")
.arg("--configuration")
.arg("Release")
.arg("--runtime")
.arg("win-x64")
.arg("--self-contained")
.arg("true")
.arg("-p:WindowsPackageType=None")
.arg("-p:WindowsAppSDKSelfContained=true")
.arg("-p:PublishSingleFile=false")
.arg("-p:PublishTrimmed=false")
.arg("-p:DebugType=none")
.arg("-p:DebugSymbols=false")
.arg("-o")
.arg(&publish_dir)
.status();
let status = match status {
Ok(status) => status,
Err(err) => {
println!(
"cargo:warning=C# UI helper was not bundled because dotnet publish could not start: {err}"
);
return;
}
};
if !status.success() {
println!(
"cargo:warning=C# UI helper was not bundled because dotnet publish failed with {status}"
);
return;
}
let ui_exe = publish_dir.join("Covenant.Setup.Ui.exe");
if !ui_exe.exists() {
println!(
"cargo:warning=C# UI helper was not bundled because dotnet publish did not produce {}",
ui_exe.display()
);
return;
}
let ui_bundle = out_dir.join("csharp-ui.bundle");
if let Err(err) = write_ui_bundle(&publish_dir, &ui_bundle) {
println!(
"cargo:warning=C# UI helper was not bundled because bundle creation failed: {err}"
);
return;
}
println!("cargo:rustc-cfg=covenant_setup_embedded_ui");
println!(
"cargo:rustc-env=COVENANT_SETUP_UI_BUNDLE={}",
ui_bundle.display()
);
}
fn write_ui_bundle(publish_dir: &Path, bundle_path: &Path) -> io::Result<()> {
let mut files = Vec::new();
collect_files(publish_dir, publish_dir, &mut files)?;
files.sort_by(|left, right| left.0.cmp(&right.0));
let mut bundle = fs::File::create(bundle_path)?;
bundle.write_all(UI_BUNDLE_MAGIC)?;
for (relative_path, path) in files {
let relative_path = relative_path.to_string_lossy().replace('\\', "/");
let relative_path_bytes = relative_path.as_bytes();
let data = fs::read(path)?;
bundle.write_all(&(relative_path_bytes.len() as u32).to_le_bytes())?;
bundle.write_all(&(data.len() as u64).to_le_bytes())?;
bundle.write_all(relative_path_bytes)?;
bundle.write_all(&data)?;
}
bundle.write_all(&0u32.to_le_bytes())?;
bundle.write_all(&0u64.to_le_bytes())?;
Ok(())
}
fn collect_files(
root: &Path,
current: &Path,
files: &mut Vec<(PathBuf, PathBuf)>,
) -> io::Result<()> {
for entry in fs::read_dir(current)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
collect_files(root, &path, files)?;
} else if path.is_file() {
let relative_path = path
.strip_prefix(root)
.map_err(io::Error::other)?
.to_path_buf();
files.push((relative_path, path));
}
}
Ok(())
}