From e4b8406e9031c9eb343ec7e4b1b1e52f7663d922 Mon Sep 17 00:00:00 2001 From: Piotr Siuszko Date: Wed, 24 Sep 2025 13:24:50 +0200 Subject: [PATCH] Bundler --- .github/workflows/bundle.yml | 49 +++++++++++++++++ crates/rpack_egui/Cargo.toml | 3 +- crates/rpack_egui/justfile | 53 ++++-------------- crates/rpack_egui/src/app.rs | 16 +++--- crates/rpack_egui/src/fonts.rs | 99 +++++++++++++++++++++++++++++----- crates/rpack_egui/src/main.rs | 2 +- 6 files changed, 154 insertions(+), 68 deletions(-) create mode 100644 .github/workflows/bundle.yml diff --git a/.github/workflows/bundle.yml b/.github/workflows/bundle.yml new file mode 100644 index 0000000..3489173 --- /dev/null +++ b/.github/workflows/bundle.yml @@ -0,0 +1,49 @@ +name: Bundle + +on: + workflow_call: + inputs: + runningSystem: + description: "System to run the job on" + required: true + type: string + workflow_dispatch: + inputs: + runningSystem: + type: choice + description: "System to run the job on" + required: true + options: + - ubuntu-latest + - windows-latest + - macos-15 + +jobs: + build: + runs-on: ${{ inputs.runningSystem }} + steps: + - uses: actions/checkout@v5 + - uses: actions/cache@v4 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + ~/.cache/sccache + target/ + key: ${{ inputs.runningSystem }}-build-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ inputs.runningSystem }}-build- + - uses: extractions/setup-just@v2 + - name: Install Cargo Bundle + run: cargo install --git https://github.com/Leinnan/cargo-bundler + - name: Bundle mac + if: inputs.bundle == true && startsWith(inputs.runningSystem, 'macos') + working-directory: crates/rpack_egui + run: | + just bundle + - uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.runningSystem }} + path: target/release/bundle diff --git a/crates/rpack_egui/Cargo.toml b/crates/rpack_egui/Cargo.toml index 13b1113..b2f9391 100644 --- a/crates/rpack_egui/Cargo.toml +++ b/crates/rpack_egui/Cargo.toml @@ -58,7 +58,8 @@ js-sys = "0.3" [package.metadata.bundle] name = "Rpack" -resources = ["static/resources/*"] +icon = ["static/base_icon.png"] +resources_mapping = [["static/JetBrains*","./"]] identifier = "io.github.leinnan.rpack" osx_url_schemes = ["io.github.leinnan.rpack"] short_description = "Tilemap Editor" diff --git a/crates/rpack_egui/justfile b/crates/rpack_egui/justfile index df4964d..ccd506e 100644 --- a/crates/rpack_egui/justfile +++ b/crates/rpack_egui/justfile @@ -1,50 +1,15 @@ +format_bundle := if os() == "windows" { "--format wxsmsi" } else { if os() == "linux" { "--format appimage" } else { "--format osx" } } -_gen_icon size postfix: - sips -z {{size}} {{size}} static/base_icon.png --out static/icon.iconset/icon_{{postfix}}.png - cp static/icon.iconset/icon_{{postfix}}.png static/resources/{{postfix}}.png +# list available commands +list: + just --list make_win_icon: convert static/base_icon.png -define icon:auto-resize=16,24,32,48,64,128,256,512 static/icon.ico -# Build the icon for macOS app -make_mac_icon: - rm -rf static/icon.iconset - mkdir -p static/icon.iconset - rm -rf static/resources - mkdir -p static/resources - just _gen_icon 16 "16x16" - just _gen_icon 32 "32x32" - just _gen_icon 64 "64x64" - just _gen_icon 128 "128x128" - just _gen_icon 256 "256x256" - just _gen_icon 512 "512x512" - just _gen_icon 1024 "1024x1024" - just _gen_icon 32 "16x16@2x" - just _gen_icon 64 "32x32@2x" - just _gen_icon 128 "64x64@2x" - just _gen_icon 256 "128x128@2x" - just _gen_icon 512 "256x256@2x" - just _gen_icon 1024 "512x512@2x" +create_mac_installer: bundle + pkgbuild --install-location /Applications --component ../../target/release/bundle/osx/Rpack.app ../../target/release/bundle/osx/Rpack.pkg - iconutil -c icns static/icon.iconset - rm -rf static/icon.iconset - mv static/icon.icns static/resources/icon.icns - -# Build the macOS app -build_mac_app: make_mac_icon - rm -rf Rpack.app - cargo build --release - mkdir -p Rpack.app - mkdir -p Rpack.app/Contents - mkdir -p Rpack.app/Contents/MacOS - mkdir -p Rpack.app/Contents/Resources - cp static/Info.plist Rpack.app/Contents/Info.plist - cp static/resources/* Rpack.app/Contents/Resources/ - cp ../../target/release/rpack_egui Rpack.app/Contents/MacOS/rpack_egui - -create_mac_installer: build_mac_app - pkgbuild --install-location /Applications --component Rpack.app RpackInstaller.pkg - -# Create the installer using cargo-bundle -create_installer: - cargo bundle --release +# Create the installer using cargo-bundler +bundle: + cargo bundler -r diff --git a/crates/rpack_egui/src/app.rs b/crates/rpack_egui/src/app.rs index 2e0a4d4..c625bd4 100644 --- a/crates/rpack_egui/src/app.rs +++ b/crates/rpack_egui/src/app.rs @@ -360,14 +360,14 @@ impl eframe::App for Application { /// Called each time the UI needs repainting, which may be many times per second. fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { { - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] puffin::profile_scope!("handle_undo"); self.undoer .feed_state(ctx.input(|input| input.time), &self.data); } if !INPUT_QUEUE.is_empty() { let mut rebuild = false; - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] puffin::profile_scope!("loading_images"); #[allow(dead_code)] @@ -456,7 +456,7 @@ impl eframe::App for Application { egui::TopBottomPanel::top("topPanel") .frame(egui::Frame::canvas(&ctx.style())) .show(ctx, |ui| { - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] puffin::profile_scope!("top_panel"); // ui.add_space(TOP_SIDE_MARGIN); #[cfg(not(target_arch = "wasm32"))] @@ -505,7 +505,7 @@ impl eframe::App for Application { // }); }); ctx.input(|i| { - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] puffin::profile_scope!("dropped_files"); if i.raw.dropped_files.is_empty() { return; @@ -555,7 +555,7 @@ impl eframe::App for Application { egui::TopBottomPanel::bottom("bottom_panel") .frame(egui::Frame::canvas(&ctx.style())) .show(ctx, |ui| { - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] puffin::profile_scope!("bottom_panel"); ui.add_space(5.0); ui.horizontal(|ui| { @@ -592,7 +592,7 @@ impl eframe::App for Application { .id_salt("rightPanel_scroll") .show(ui, |ui| { ui.add_enabled_ui(!self.output.is_building(), |ui| { - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] puffin::profile_scope!("right_panel"); let mut changed = false; Grid::new("settings_grid") @@ -686,7 +686,7 @@ impl eframe::App for Application { ui.separator(); { ui.style_mut().interaction.selectable_labels = false; - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] puffin::profile_scope!("image_list"); ui.horizontal(|ui| { @@ -821,7 +821,7 @@ impl eframe::App for Application { egui::CentralPanel::default() .frame(Frame::central_panel(&ctx.style()).inner_margin(16i8)) .show(ctx, |ui| { - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] puffin::profile_scope!("central_panel"); if let Some(error) = &self.last_error { let text = egui::RichText::new(format!("Error: {}", &error)) diff --git a/crates/rpack_egui/src/fonts.rs b/crates/rpack_egui/src/fonts.rs index 0a6dc7b..bab5fca 100644 --- a/crates/rpack_egui/src/fonts.rs +++ b/crates/rpack_egui/src/fonts.rs @@ -1,3 +1,5 @@ +use std::path::Path; + pub fn setup_custom_fonts(ctx: &egui::Context) { // Start with the default fonts (we will be adding to them rather than replacing them). let mut fonts = egui::FontDefinitions::default(); @@ -41,26 +43,95 @@ pub fn setup_custom_fonts(ctx: &egui::Context) { }); } -#[cfg(not(windows))] +#[cfg(target_arch = "wasm32")] fn get_fonts() -> anyhow::Result<(Vec, Vec)> { - use std::fs; - - let font_path = std::path::Path::new("/System/Library/Fonts"); - - let regular = fs::read(font_path.join("SFNSRounded.ttf"))?; - let semibold = fs::read(font_path.join("SFCompact.ttf"))?; + let regular = include_bytes!("../static/JetBrainsMonoNL-Regular.ttf").to_vec(); + let semibold = include_bytes!("../static/JetBrainsMono-SemiBold.ttf").to_vec(); Ok((regular, semibold)) } -#[cfg(windows)] +#[cfg(not(target_arch = "wasm32"))] fn get_fonts() -> anyhow::Result<(Vec, Vec)> { - use std::fs; - let app_data = std::env::var("APPDATA")?; - let font_path = std::path::Path::new(&app_data); - - let regular = fs::read(font_path.join("../Local/Microsoft/Windows/Fonts/aptos.ttf"))?; - let semibold = fs::read(font_path.join("../Local/Microsoft/Windows/Fonts/aptos-semibold.ttf"))?; + let Some(regular) = + try_get_font_from_list(&["JetBrainsMonoNL-Regular", "SFNSRounded", "aptos"]) + else { + anyhow::bail!("Failed to find a suitable font"); + }; + let Some(semibold) = + try_get_font_from_list(&["JetBrainsMono-SemiBold", "SFNSRounded", "aptos-semibold"]) + else { + anyhow::bail!("Failed to find a suitable font"); + }; Ok((regular, semibold)) } + +fn try_get_font_from_list(font_names: &[&str]) -> Option> { + for font_name in font_names { + if let Some(font) = try_get_font(font_name) { + return Some(font); + } + } + None +} + +fn try_get_font(font_name: &str) -> Option> { + for dir in font_dirs() { + if let Ok(font) = std::fs::read(Path::new(&dir).join(format!("{}.ttf", font_name))) { + return Some(font); + } + if let Ok(font) = std::fs::read(Path::new(&dir).join(format!("{}.otf", font_name))) { + return Some(font); + } + } + None +} + +fn font_dirs() -> Vec { + let mut dirs = Vec::new(); + + #[cfg(target_os = "linux")] + { + dirs.push("/usr/share/fonts".into()); + dirs.push("/usr/share/fonts/truetype".into()); + } + #[cfg(unix)] + { + use std::{path::PathBuf, str::FromStr}; + + #[cfg(target_os = "macos")] + { + dirs.push("/System/Library/Fonts".into()); + if let Some(resources_font_dir) = std::env::current_exe().ok().and_then(|p| { + p.ancestors() + .nth(2) + .map(|p| p.join("Resources/fonts").to_string_lossy().into_owned()) + }) { + eprintln!("{}", &resources_font_dir); + dirs.push(resources_font_dir); + } + } + if let Some(home) = + std::env::var_os("HOME").and_then(|s| PathBuf::from_str(&s.to_string_lossy()).ok()) + { + #[cfg(target_os = "macos")] + { + dirs.push(format!("{}/Library/Fonts", home.display())); + } + #[cfg(target_os = "linux")] + { + dirs.push(format!("{}/.local/share/fonts", home.display())); + } + } + } + #[cfg(target_os = "windows")] + { + if let Ok(dir) = std::env::var("APPDATA") { + let font_path = std::path::Path::new(&dir).join("../Local/Microsoft/Windows/Fonts/"); + dirs.push(font_path.display().to_string()); + } + } + + dirs +} diff --git a/crates/rpack_egui/src/main.rs b/crates/rpack_egui/src/main.rs index 66c518c..b3a995e 100644 --- a/crates/rpack_egui/src/main.rs +++ b/crates/rpack_egui/src/main.rs @@ -30,7 +30,7 @@ fn start_puffin_server() { #[cfg(not(target_arch = "wasm32"))] fn main() -> eframe::Result<()> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). - #[cfg(feature = "profiler")] + #[cfg(all(not(target_arch = "wasm32"), feature = "profiler"))] start_puffin_server(); let file_arg: Option = if std::env::args().len() > 1 { std::env::args().skip(1).next()