From acacf5d5e155e4a569e8c9c225ad5232f973791d Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Wed, 9 Oct 2024 15:12:58 -0700 Subject: [PATCH] Support packaging Robrix into app bundles using `cargo-packager` * Add `before-packaging-command` binary that actually builds Robrix and handles the correct Robrix-specific and Makepad-specific steps as "callbacks" that get invoked by `cargo-packager`. * Add package metadata to `Cargo.toml` that specifies all the packaging metadata required for Robrix. * Add Robrix icon, `.dmg` background, and other related packaging files. * Depend on our fork of Makepad that adds the `apple_bundle` cfg option, which supports querying an app bundle's resource path on macOS, tvOS, and iOS. This works by having Makepad prepend the resource path onto all resource file paths, which is `Contents/Resources/` for Apple bundles. * This is implemented via `NSBundle::resourcePath`. --- .gitignore | 5 +- Cargo.lock | 64 +-- Cargo.toml | 96 +++- LICENSE-MIT | 2 +- packaging/Robrix macOS dmg background.png | Bin 0 -> 19332 bytes packaging/before-packaging-command/.gitignore | 6 + packaging/before-packaging-command/Cargo.lock | 299 ++++++++++++ packaging/before-packaging-command/Cargo.toml | 10 + .../before-packaging-command/src/main.rs | 460 ++++++++++++++++++ packaging/robrix.desktop | 14 + packaging/robrix_logo_alpha.png | Bin 0 -> 16234 bytes packaging/robrix_logo_square.png | Bin 0 -> 14221 bytes 12 files changed, 921 insertions(+), 35 deletions(-) create mode 100644 packaging/Robrix macOS dmg background.png create mode 100644 packaging/before-packaging-command/.gitignore create mode 100644 packaging/before-packaging-command/Cargo.lock create mode 100644 packaging/before-packaging-command/Cargo.toml create mode 100644 packaging/before-packaging-command/src/main.rs create mode 100644 packaging/robrix.desktop create mode 100644 packaging/robrix_logo_alpha.png create mode 100644 packaging/robrix_logo_square.png diff --git a/.gitignore b/.gitignore index 3d903545..5a0c6f67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ +## Cargo generated directory /target -.vscode +## cargo-packager generated directory +/dist +.vscode .DS_Store ## Temp hack to support login on mobile platforms. diff --git a/Cargo.lock b/Cargo.lock index 18306a9d..80113f47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1723,7 +1723,7 @@ dependencies = [ [[package]] name = "makepad-derive-live" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", "makepad-micro-proc-macro", @@ -1732,7 +1732,7 @@ dependencies = [ [[package]] name = "makepad-derive-wasm-bridge" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-micro-proc-macro", ] @@ -1740,7 +1740,7 @@ dependencies = [ [[package]] name = "makepad-derive-widget" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", "makepad-micro-proc-macro", @@ -1749,7 +1749,7 @@ dependencies = [ [[package]] name = "makepad-draw" version = "0.6.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "ab_glyph_rasterizer", "fxhash", @@ -1766,17 +1766,17 @@ dependencies = [ [[package]] name = "makepad-futures" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-futures-legacy" version = "0.7.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-html" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", ] @@ -1784,7 +1784,7 @@ dependencies = [ [[package]] name = "makepad-http" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-jni-sys" @@ -1795,7 +1795,7 @@ checksum = "9775cbec5fa0647500c3e5de7c850280a88335d1d2d770e5aa2332b801ba7064" [[package]] name = "makepad-live-compiler" version = "0.5.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-derive-live", "makepad-live-tokenizer", @@ -1805,7 +1805,7 @@ dependencies = [ [[package]] name = "makepad-live-id" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id-macros", ] @@ -1813,7 +1813,7 @@ dependencies = [ [[package]] name = "makepad-live-id-macros" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-micro-proc-macro", ] @@ -1821,7 +1821,7 @@ dependencies = [ [[package]] name = "makepad-live-tokenizer" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", "makepad-math", @@ -1831,7 +1831,7 @@ dependencies = [ [[package]] name = "makepad-markdown" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", ] @@ -1839,17 +1839,17 @@ dependencies = [ [[package]] name = "makepad-math" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-micro-proc-macro" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-micro-serde" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", "makepad-micro-serde-derive", @@ -1858,7 +1858,7 @@ dependencies = [ [[package]] name = "makepad-micro-serde-derive" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-micro-proc-macro", ] @@ -1866,12 +1866,12 @@ dependencies = [ [[package]] name = "makepad-objc-sys" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-platform" version = "0.6.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "hilog-sys", "makepad-android-state", @@ -1893,7 +1893,7 @@ dependencies = [ [[package]] name = "makepad-rustybuzz" version = "0.8.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -1908,7 +1908,7 @@ dependencies = [ [[package]] name = "makepad-shader-compiler" version = "0.5.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-compiler", ] @@ -1916,7 +1916,7 @@ dependencies = [ [[package]] name = "makepad-vector" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "resvg", "ttf-parser", @@ -1925,7 +1925,7 @@ dependencies = [ [[package]] name = "makepad-wasm-bridge" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-derive-wasm-bridge", "makepad-live-id", @@ -1934,7 +1934,7 @@ dependencies = [ [[package]] name = "makepad-widgets" version = "0.6.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-derive-widget", "makepad-draw", @@ -1948,16 +1948,16 @@ dependencies = [ [[package]] name = "makepad-windows" version = "0.51.1" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ - "windows-core 0.51.1 (git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content)", + "windows-core 0.51.1 (git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path)", "windows-targets 0.48.5", ] [[package]] name = "makepad-zune-core" version = "0.2.14" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "bitflags 2.4.1", ] @@ -1965,7 +1965,7 @@ dependencies = [ [[package]] name = "makepad-zune-inflate" version = "0.2.54" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "simd-adler32", ] @@ -1973,7 +1973,7 @@ dependencies = [ [[package]] name = "makepad-zune-jpeg" version = "0.3.17" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-zune-core", ] @@ -1981,7 +1981,7 @@ dependencies = [ [[package]] name = "makepad-zune-png" version = "0.2.1" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-zune-core", "makepad-zune-inflate", @@ -3967,7 +3967,7 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "ttf-parser" version = "0.21.1" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "typenum" @@ -4373,7 +4373,7 @@ dependencies = [ [[package]] name = "windows-core" version = "0.51.1" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "windows-targets 0.48.5", ] diff --git a/Cargo.toml b/Cargo.toml index 9deb4084..e7ae1e39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,8 @@ metadata.makepad-auto-version = "zqpv-Yj-K7WNVK2I8h5Okhho46Q=" [dependencies] # makepad-widgets = { git = "https://github.com/makepad/makepad", branch = "rik" } -makepad-widgets = { git = "https://github.com/kevinaboos/makepad", branch = "modal_sends_dismissed_action_to_inner_modal_content" } +makepad-widgets = { git = "https://github.com/kevinaboos/makepad", branch = "apple_bundle_resource_path" } + ## Including this crate automatically configures all `robius-*` crates to work with Makepad. robius-use-makepad = "0.1.0" @@ -98,3 +99,96 @@ all-features = true # Temporarily include all debug info even for release builds. [profile.release] debug = "full" + + + + +## Configuration for `cargo packager` +[package.metadata.packager] +product_name = "Robrix" +identifier = "org.robius.robrix" +category = "SocialNetworking" +authors = ["Project Robius ", "Kevin Boos "] +publisher = "robius" +license_file = "LICENSE-MIT" +copyright = "Copyright 2023-2024, Project Robius" +homepage = "https://github.com/project-robius" +### Note: there is an 80-character max for each line of the `long_description`. +long_description = """ +Robrix is a multi-platform Matrix chat client written in pure Rust +using the Makepad UI framework (https://github.com/makepad/makepad) +and the Project Robius app dev framework and platform abstractions +(https://github.com/project-robius). +Robrix runs on all major desktop and mobile platforms: +macOS, Windows, Linux, Android, and iOS. +""" +icons = ["./packaging/robrix_logo_alpha.png"] +out_dir = "./dist" + +## The below command uses cargo-metadata to determine the path of the `makepad_widgets` crate on the host build system, +## and copies the `makepad-widgets/resources` directory to the `./dist/resources/makepad_widgets` directory. +## We also copy the Robrix project's `resources/` directory to the `./dist/resources/robrix` directory. +## +## This is necessary because the `cargo packager` command only supports defining resources at a known path +## (see the below `resources = [...]` block below), +## so we need to copy the resources to a known fixed (static) path before packaging, +## such that cargo-packager can locate them and include them in the final package. +before-packaging-command = """ +cargo run --manifest-path packaging/before-packaging-command/Cargo.toml \ + before-packaging \ + --binary-name robrix \ + --path-to-binary ./target/release/robrix +""" + +## See the above paragraph comments for more info on how we create/populate the below `src` directories. +resources = [ + { src = "./dist/resources/makepad_widgets", target = "makepad_widgets" }, + { src = "./dist/resources/robrix", target = "robrix" }, +] + +## We then build the entire Robrix project and set the `MAKEPAD_PACKAGE_DIR` env var to the proper value. +## * For macOS app bundles, this should be set to `./` because the default working directory +## for macOS app bundles is the `Contents/Resources/` directory, which is already where the resources are. +## * For Debian `.deb` packages, this should be set to `/usr/lib/`, +## which is currently `/usr/lib/robrix`. +## This is the directory in which `dpkg` copies app resource files to when installing the `.deb` package. +## * On Linux, we also strip the binaries of unneeded content, as required for Debian packages. +## * For Debian and Pacman (still a to-do!) packages, we also auto-generate the list of dependencies required by Robrix. +## +before-each-package-command = """ +cargo run --manifest-path packaging/before-packaging-command/Cargo.toml \ + before-each-package \ + --binary-name robrix \ + --path-to-binary ./target/release/robrix +""" + +deep_link_protocols = [ + { schemes = ["robrix", "matrix"], role = "viewer" }, ## `name` is left as default +] + +[package.metadata.packager.deb] +depends = "./dist/depends_deb.txt" +desktop_template = "./packaging/robrix.desktop" +section = "utils" + +[package.metadata.packager.macos] +minimum_system_version = "11.0" +frameworks = [ ] +signing_identity = "Developer ID Application: AppChef Inc. (SFVQ5V48GD)" + + +## Configuration for `cargo packager`'s generation of a macOS `.dmg`. +[package.metadata.packager.dmg] +background = "./packaging/Robrix macOS dmg background.png" +window_size = { width = 960, height = 540 } +app_position = { x = 200, y = 250 } +application_folder_position = { x = 760, y = 250 } + + +## Configuration for `cargo packager`'s generation of a Windows `.exe` setup installer. +[package.metadata.packager.nsis] +## See this: +appdata_paths = [ + "$APPDATA/$PUBLISHER/$PRODUCTNAME", + "$LOCALAPPDATA/$PRODUCTNAME", +] diff --git a/LICENSE-MIT b/LICENSE-MIT index 15120410..87eac651 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2023 Project Robius Developers +Copyright (c) 2023-2024 Project Robius Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/packaging/Robrix macOS dmg background.png b/packaging/Robrix macOS dmg background.png new file mode 100644 index 0000000000000000000000000000000000000000..e5197b7e1c18f84f1c8a56e3b81514f388f669ea GIT binary patch literal 19332 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 z zNz5aQ+FP8?W;IsINMM(@C#;?ip{Vi@@Id0y^)|(S;6MO{l*hZK(S_X?Cm9OGl6$<= zWb9>jp63dkn>b}O1~ev}Oc#!o4`{Kjk^sQfK4$k+Z1)>khB}|_W7ZxJb75cxIcdqR z4p=x?N%3kH*oO6BI&7glmB^K~Wu@l9Az0EM#q|w}NliNm#bk=oIT%e3Bs^;Q|y& z6Z*k^_om0`^HJ}HZi0Pp=-*7NN|2VB@>eO5ozwn<#_DMF72=}3`&@_^0>0%}^!_$AN|YgnF(H zUPrUpg-^4QTngAnx^C`}o3wx2rsFiD8z{x)GW6~9QSC#aKEjJi*x8HRFYm|bVcY~m z_9?R?s6z~evde0IxB2uRB^yN0Cejo2Jm$KHG*O|Z$q&Ri0hFJx@`Cv4rlutlv2^@~ zDF=qaXd~Y36=#a)+}s6FzyV^%2`|xjfIj}YJTYB60ffBi&wA_g_U)r@xyJ{22*b^5 zgW;bC2iHacOMG+j$DaoWe>m`e9oYJLaB!ve*t7*6aG{?E2Uo)C=fOcBl$JN&{D7C` z59>6u>D<*OcUtS<0WH#XX9<90@bi598hZacAHV8g>&}({G^;KhMYi0GxH2`I+XH9cDv9|NrLSz#z7( cvDsPIDP9_AXc_R|wLlj&bu}>OZ~XOt05b06$N&HU literal 0 HcmV?d00001 diff --git a/packaging/before-packaging-command/.gitignore b/packaging/before-packaging-command/.gitignore new file mode 100644 index 00000000..04e2336d --- /dev/null +++ b/packaging/before-packaging-command/.gitignore @@ -0,0 +1,6 @@ +## Cargo generated directory +/target + +.vscode +.DS_Store + diff --git a/packaging/before-packaging-command/Cargo.lock b/packaging/before-packaging-command/Cargo.lock new file mode 100644 index 00000000..a2f3c86c --- /dev/null +++ b/packaging/before-packaging-command/Cargo.lock @@ -0,0 +1,299 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "before-packaging-command" +version = "0.1.0" +dependencies = [ + "cargo_metadata", + "directories", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "camino" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/packaging/before-packaging-command/Cargo.toml b/packaging/before-packaging-command/Cargo.toml new file mode 100644 index 00000000..87e0f9af --- /dev/null +++ b/packaging/before-packaging-command/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "before-packaging-command" +version = "0.1.0" +edition = "2021" +rust-version = "1.79" ## required by cargo-packager +description = "A small Rust program that is run by cargo-packager's 'before packaging' commands." + +[dependencies] +cargo_metadata = "0.18" +directories = "5.0.1" diff --git a/packaging/before-packaging-command/src/main.rs b/packaging/before-packaging-command/src/main.rs new file mode 100644 index 00000000..74004869 --- /dev/null +++ b/packaging/before-packaging-command/src/main.rs @@ -0,0 +1,460 @@ +//! This small program is invoked by cargo-packager during its before-packaging steps. +//! +//! This program must be run from the root of the project directory, +//! which is also where the cargo-packager command must be invoked from. +//! +//! This program runs in two modes, one for each kind of before-packaging step in cargo-packager. +//! It requires passing in three arguments: +//! 1. An operating mode, either `before-packaging` or `before-each-packager`. +//! * Passing in `before-packaging` specifies that the `before-packaging-command` is being run +//! by cargo-packager, which gets executed only *once* before cargo-packager generates any package bundles. +//! * Passing in `before-each-package` specifies that the `before-each-package-command` is being run +//! by cargo-packager, which gets executed multiple times: once for *each* package that +//! cargo-packager is going to generate. +//! * The environment variable `CARGO_PACKAGER_FORMAT` is set by cargo-packager to +//! the declare which package format is about to be generated, which include the values +//! given here: . +//! * `app`, `dmg`: for macOS. +//! * `deb`, `appimage`, `pacman`: for Linux. +//! * `nsis`: for Windows; `nsis` generates an installer `setup.exe`. +//! * `wix`: (UNSUPPORTED) for Windows; generates an `.msi` installer package. +//! 2. The `--binary-name` argument, which specifies the name of the main binary that cargo-packager +//! is going to package. This is the name of the binary that is generated by your app's main crate. +//! 3. The `--path-to-binary` argument, which specifies the path to the main binary that cargo-packager +//! is going to package. This is the path to the binary that is generated by cargo when compiling +//! your app's main crate. +//! +//! This program uses the `CARGO_PACKAGER_FORMAT` environment variable to determine +//! which specific build commands and configuration options should be used. +//! + +use core::panic; +use std::{ffi::OsStr, fs, path::{Path, PathBuf}, process::{Command, Stdio}}; +use cargo_metadata::MetadataCommand; + +const EMPTY_ARGS: std::iter::Empty<&str> = std::iter::empty::<&str>(); +const EMPTY_ENVS: std::iter::Empty<(&str, &str)> = std::iter::empty::<(&str, &str)>(); + +/// Returns the value of the `MAKEPAD_PACKAGE_DIR` environment variable +/// that must be set for the given package format. +/// +/// * For macOS app bundles, this should be set to the current directory `.` +/// * This only works because we enable the Makepad `apple_bundle` cfg option, +/// which tells Makepad to invoke Apple's `NSBundle` API to retrieve the resource path at runtime. +/// This resource path points to the bundle's `Contents/Resources/` directory. +/// * For AppImage packages, this should be set to a relative path that goes up two parent directories +/// to account for the fact that AppImage binaries run in a simulated `usr/bin/` directory. +/// * Thus, we need to get to the simulated `usr/lib/` directory to find the resources, +/// which currently works out to "./usr/lib/robrix`. +/// * Note that this must be a relative path, not an absolute path. +/// * For Debian `.deb` packages, this should be set to `/usr/lib/`. +/// * This is the directory in which `dpkg` copies app resource files to +/// when a user installs the `.deb` package. +/// * For Windows NSIS packages, this should be set to `.` (the current dir). +/// * This is because the NSIS installer script copies the resources to the same directory +/// as the installed binaries. +fn makepad_package_dir_value(package_format: &str, main_binary_name: &str) -> String { + match package_format { + "app" | "dmg" => format!("."), + "appimage" => format!("./usr/lib/{}", main_binary_name), + "deb" | "pacman" => format!("/usr/lib/{}", main_binary_name), + "nsis" => format!("."), + _other => panic!("Unsupported package format: {}", _other), + } +} + + +fn main() -> std::io::Result<()> { + let mut is_before_packaging = false; + let mut is_before_each_package = false; + let mut main_binary_name = None; + let mut path_to_binary = None; + let mut host_os_opt: Option = None; + + let mut args = std::env::args().peekable(); + while let Some(arg) = args.next() { + if arg.ends_with("before-packaging") || arg.ends_with("before_packaging") { + is_before_packaging = true; + } + if arg.contains("before-each") || arg.contains("before_each") { + is_before_each_package = true; + } + if arg == "--binary-name" { + main_binary_name = Some(args.next().expect("Expected a binary name after '--binary-name'.")); + } + if arg == "--path-to-binary" { + let path = PathBuf::from(args.next().expect("Expected a path after '--path-to-binary'.")); + if path.exists() { + path_to_binary = Some(path); + } else { + panic!("The '--path-to-binary' path does not exist: '{}'", path.display()); + } + } + if host_os_opt.is_none() && (arg.contains("host_os") || arg.contains("host-os")) + { + host_os_opt = arg + .split("=") + .last() + .map(|s| s.to_string()) + .or_else(|| args.peek().map(|s| s.to_string())); + } + } + + let main_binary_name = main_binary_name.expect("Missing required argument '--binary-name'"); + let path_to_binary = path_to_binary.expect("Missing required argument '--path-to-binary'"); + let host_os = host_os_opt.as_deref().unwrap_or(std::env::consts::OS); + + match (is_before_packaging, is_before_each_package) { + (true, false) => before_packaging(host_os, &main_binary_name), + (false, true) => before_each_package(host_os, &main_binary_name, &path_to_binary), + (true, true) => panic!("Cannot run both 'before-packaging' and 'before-each-package' commands at the same time."), + (false, false) => panic!("Please specify either the 'before-packaging' or 'before-each-package' command."), + } +} + +/// This function is run only *once* before cargo-packager generates any package bundles. +/// +/// ## Functionality +/// 1. Creates a directory for the resources to be packaged, which is currently `./dist/resources/`. +/// 2. Recursively copies the resources from the `makepad-widgets` crate to a subdirectory +/// of the directory created in step 1, which is currently `./dist/resources/makepad_widgets/`. +/// The location of the `makepad-widgets` crate is determined using `cargo-metadata`. +/// 3. Recursively copies the app-specific `./resources` directory to `./dist/resources//`. +fn before_packaging(_host_os: &str, main_binary_name: &str) -> std::io::Result<()> { + let cwd = std::env::current_dir()?; + let dist_resources_dir = cwd.join("dist").join("resources"); + fs::create_dir_all(&dist_resources_dir)?; + + let app_resources_dest = dist_resources_dir.join(main_binary_name).join("resources"); + let app_resources_src = cwd.join("resources"); + let makepad_widgets_resources_dest = dist_resources_dir.join("makepad_widgets").join("resources"); + let makepad_widgets_resources_src = { + let cargo_metadata = MetadataCommand::new() + .exec() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + + let makepad_widgets_package = cargo_metadata + .packages + .iter() + .find(|package| package.name == "makepad-widgets") + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "makepad-widgets package not found"))?; + + makepad_widgets_package.manifest_path + .parent() + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "makepad-widgets package manifest path not found"))? + .join("resources") + }; + + /// Copy files from source to destination recursively. + fn copy_recursively(source: impl AsRef, destination: impl AsRef) -> std::io::Result<()> { + fs::create_dir_all(&destination)?; + for entry in fs::read_dir(source)? { + let entry = entry?; + let filetype = entry.file_type()?; + if filetype.is_dir() { + copy_recursively(entry.path(), destination.as_ref().join(entry.file_name()))?; + } else { + fs::copy(entry.path(), destination.as_ref().join(entry.file_name()))?; + } + } + Ok(()) + } + + println!("Copying makepad-widgets resources...\n --> From: {}\n to: {}", makepad_widgets_resources_src.as_std_path().display(), makepad_widgets_resources_dest.display()); + copy_recursively(&makepad_widgets_resources_src, &makepad_widgets_resources_dest)?; + println!(" --> Done!"); + println!("Copying app-specific resources...\n --> From {}\n to: {}", app_resources_src.display(), app_resources_dest.display()); + copy_recursively(&app_resources_src, &app_resources_dest)?; + println!(" --> Done!"); + + Ok(()) +} + + +/// The function that is run by cargo-packager's `before-each-package-command`. +/// +/// It's just a simple wrapper that invokes the function for each specific package format. +fn before_each_package>( + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + // The `CARGO_PACKAGER_FORMAT` environment variable is required. + let format = std::env::var("CARGO_PACKAGER_FORMAT") + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + + let package_format = format.as_str(); + println!("Running before-each-package-command for {package_format:?}"); + match package_format { + "app" | "dmg" => before_each_package_macos( package_format, host_os, &main_binary_name, &path_to_binary), + "deb" => before_each_package_deb( package_format, host_os, &main_binary_name, &path_to_binary), + "appimage" => before_each_package_appimage(package_format, host_os, &main_binary_name, &path_to_binary), + "pacman" => before_each_package_pacman( package_format, host_os, &main_binary_name, &path_to_binary), + "nsis" => before_each_package_windows( package_format, host_os, &main_binary_name, &path_to_binary), + _other => return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Unknown/unsupported package format {_other:?}"), + )), + } +} + + +/// Runs the macOS-specific build commands for "app" and "dmg" package formats. +/// +/// This function effectively runs the following shell commands: +/// ```sh +/// MAKEPAD_PACKAGE_DIR=../Resources cargo build --workspace --release \ +/// && install_name_tool -add_rpath "@executable_path/../Frameworks" ./target/release/_moly_app; +/// ``` +fn before_each_package_macos>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "macos", "'app' and 'dmg' packages can only be created on macOS."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + [("MAKEPAD", "apple_bundle")], + )?; + + // Use `install_name_tool` to add the `@executable_path` rpath to the binary. + let install_name_tool_cmd = Command::new("install_name_tool") + .arg("-add_rpath") + .arg("@executable_path/../Frameworks") + .arg(path_to_binary.as_ref()) + .spawn()?; + + let output = install_name_tool_cmd.wait_with_output()?; + if !output.status.success() { + eprintln!("Failed to run install_name_tool command: {} + ------------------------- stderr: ------------------------- + {:?}", + output.status, + String::from_utf8_lossy(&output.stderr), + ); + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Failed to run install_name_tool command for macOS")); + } + + Ok(()) + +} + +/// Runs the Linux-specific build commands for AppImage packages. +fn before_each_package_appimage>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "linux", "AppImage packages can only be created on Linux."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + EMPTY_ENVS, + )?; + + strip_unneeded_linux_binaries(host_os, path_to_binary)?; + + Ok(()) +} + + +/// Runs the Linux-specific build commands for Debian `.deb` packages. +/// +/// This function effectively runs the following shell commands: +/// ```sh +/// for path in $(ldd target/release/_moly_app | awk '{print $3}'); do \ +/// basename "$/path" ; \ +/// done \ +/// | xargs dpkg -S 2> /dev/null | awk '{print $1}' | awk -F ':' '{print $1}' | sort | uniq > ./dist/depends_deb.txt; \ +/// echo "curl" >> ./dist/depends_deb.txt; \ +/// +fn before_each_package_deb>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "linux", "'deb' packages can only be created on Linux."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + EMPTY_ENVS, + )?; + + + // Create Debian dependencies file by running `ldd` on the binary + // and then running `dpkg -S` on each unique shared libraries outputted by `ldd`. + let ldd_output = Command::new("ldd") + .arg(path_to_binary.as_ref()) + .output()?; + + let ldd_output = if ldd_output.status.success() { + String::from_utf8_lossy(&ldd_output.stdout) + } else { + eprintln!("Failed to run ldd command: {} + ------------------------- stderr: ------------------------- + {:?}", + ldd_output.status, + String::from_utf8_lossy(&ldd_output.stderr), + ); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to run ldd command on {host_os} for package format {package_format:?}") + )); + }; + + let mut dpkgs = Vec::new(); + for line_raw in ldd_output.lines() { + let line = line_raw.trim(); + let lib_name_opt = line.split_whitespace() + .next() + .and_then(|path| Path::new(path) + .file_name() + .and_then(|f| f.to_str().to_owned()) + ); + let Some(lib_name) = lib_name_opt else { continue }; + + let dpkg_output = Command::new("dpkg") + .arg("-S") + .arg(lib_name) + .stderr(Stdio::null()) + .output()?; + let dpkg_output = if dpkg_output.status.success() { + String::from_utf8_lossy(&dpkg_output.stdout) + } else { + // Skip shared libraries that dpkg doesn't know about, e.g., `linux-vdso.so*` + continue; + }; + + let Some(package_name) = dpkg_output.split(':').next() else { continue }; + println!("Got dpkg dependency {package_name:?} from ldd output: {line:?}"); + dpkgs.push(package_name.to_string()); + } + dpkgs.sort(); + dpkgs.dedup(); + println!("Sorted and de-duplicated dependencies: {:#?}", dpkgs); + std::fs::write("./dist/depends_deb.txt", dpkgs.join("\n"))?; + + strip_unneeded_linux_binaries(host_os, path_to_binary)?; + Ok(()) +} + + +/// Runs the Linux-specific build commands for PacMan packages. +/// +/// This is untested and may be incomplete, e.g., dependencies are not determined. +fn before_each_package_pacman>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "linux", "Pacman packages can only be created on Linux."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + EMPTY_ENVS, + )?; + + strip_unneeded_linux_binaries(host_os, path_to_binary)?; + Ok(()) +} + +/// Runs the Windows-specific build commands for WiX (`.msi`) and NSIS (`.exe`) packages. +fn before_each_package_windows>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + _path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "windows", "'.exe' and '.msi' packages can only be created on Windows."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + EMPTY_ENVS, + )?; + + Ok(()) +} + +fn cargo_build( + package_format: &str, + _host_os: &str, + main_binary_name: &str, + extra_args: I, + extra_env: E, +) -> std::io::Result<()> +where + I: IntoIterator, + A: AsRef, + E: IntoIterator, + K: AsRef, + V: AsRef, +{ + let cargo_build_cmd = Command::new("cargo") + .arg("build") + .arg("--workspace") + .arg("--release") + .args(extra_args) + .env("MAKEPAD_PACKAGE_DIR", &makepad_package_dir_value(package_format, main_binary_name)) + .envs(extra_env) + .spawn()?; + + let output = cargo_build_cmd.wait_with_output()?; + if !output.status.success() { + eprintln!("Failed to run cargo build command: {} + ------------------------- stderr: ------------------------- + {:?}", + output.status, + String::from_utf8_lossy(&output.stderr), + ); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to run cargo build command on {_host_os} for package format {package_format:?}") + )); + } + + Ok(()) +} +/// Strips unneeded symbols from the Linux binary, which is required for Debian `.deb` packages +/// and recommended for all other Linux package formats. +fn strip_unneeded_linux_binaries>(host_os: &str, path_to_binary: P) -> std::io::Result<()> { + assert!(host_os == "linux", "'strip --strip-unneeded' can only be run on Linux."); + let strip_cmd = Command::new("strip") + .arg("--strip-unneeded") + .arg("--remove-section=.comment") + .arg("--remove-section=.note") + .arg(path_to_binary.as_ref()) + .spawn()?; + + let output = strip_cmd.wait_with_output()?; + if !output.status.success() { + eprintln!("Failed to run strip command: {} + ------------------------- stderr: ------------------------- + {:?}", + output.status, + String::from_utf8_lossy(&output.stderr), + ); + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Failed to run strip command for Linux")); + } + + Ok(()) +} diff --git a/packaging/robrix.desktop b/packaging/robrix.desktop new file mode 100644 index 00000000..7da8abba --- /dev/null +++ b/packaging/robrix.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Categories={{categories}} +{{#if comment}} +Comment={{comment}} +{{/if}} +Exec={{exec}} {{exec_arg}} +Icon={{icon}} +Name={{name}} +Terminal=false +Type=Application +StartupWMClass=robrix +{{#if mime_type}} +MimeType={{mime_type}} +{{/if}} diff --git a/packaging/robrix_logo_alpha.png b/packaging/robrix_logo_alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..e0ba3a65f2218830d5151b2bb27db683187636ff GIT binary patch literal 16234 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 WN4pjlx`qA*xx9?BbjcH=H~$ZiNOSN2 literal 0 HcmV?d00001 diff --git a/packaging/robrix_logo_square.png b/packaging/robrix_logo_square.png new file mode 100644 index 0000000000000000000000000000000000000000..47815640344334a44216171050d7a0701ad8fb77 GIT binary patch literal 14221 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 literal 0 HcmV?d00001