1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
use crate::cargo::Cargo; use crate::package::Package; use serde::Deserialize; use std::fs::Permissions; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; use std::process::Command; #[derive(Debug, Default, Clone, Deserialize)] pub struct TomlAppImage { name: Option<String>, icon: Option<String>, } pub struct AppImage { toml: TomlAppImage, } impl AppImage { pub fn new(toml: TomlAppImage) -> Self { Self { toml } } #[cfg(not(unix))] pub fn build( &self, cargo: &Cargo, package: &Package, sign: bool, ) -> Result<(), Box<dyn std::error::Error>> { Err(failure::format_err!("Creating appimages only supported from a unix host.").into()) } #[cfg(unix)] pub fn build( &self, cargo: &Cargo, package: &Package, sign: bool, ) -> Result<(), Box<dyn std::error::Error>> { let build_dir = cargo.build_dir(); let appimage_dir = build_dir.join("appimage"); let name = self.toml.name.as_ref().unwrap_or(&package.name); let exec = &package.name; let icon_path = self .toml .icon .as_ref() .map(PathBuf::from) .unwrap_or_else(|| cargo.workspace().root().join("assets").join("icon.svg")); if !icon_path.exists() { return Err(failure::format_err!("Icon not found {}", icon_path.display()).into()); } let icon = icon_path .file_stem() .map(|f| f.to_str().unwrap()) .unwrap_or("icon") .to_string(); std::fs::remove_dir_all(&appimage_dir).ok(); let bin_dir = appimage_dir.join("usr").join("bin"); std::fs::create_dir_all(&bin_dir)?; for bin in package.bins() { std::fs::copy(bin.path(), bin_dir.join(bin.name()))?; } let lib_dir = appimage_dir.join("usr").join("lib"); std::fs::create_dir_all(&lib_dir)?; for lib in package.libs() { std::fs::copy(lib.path(), lib_dir.join(lib.name()))?; } let asset_dir = appimage_dir.join("usr").join("share"); std::fs::create_dir_all(&asset_dir)?; for asset in package.assets() { copy_dir::copy_dir(asset.path(), asset_dir.join(asset.name()))?; } let apprun = appimage_dir.join("AppRun"); std::fs::write(&apprun, APP_RUN)?; std::fs::set_permissions(&apprun, Permissions::from_mode(0o755))?; let desktop = appimage_dir.join(format!("{}.desktop", exec)); std::fs::write(&desktop, gen_desktop(name, exec, &icon))?; std::fs::set_permissions(&desktop, Permissions::from_mode(0o755))?; std::fs::copy( &icon_path, appimage_dir.join(icon_path.file_name().unwrap()), )?; let mut cmd = Command::new("appimagetool"); cmd.current_dir(&build_dir).arg("appimage"); if sign { cmd.arg("--sign"); } cmd.status().expect("Success"); Ok(()) } } const APP_RUN: &str = r#"#!/bin/sh SELF=$(readlink -f "$0") HERE=${SELF%/*} export PATH="${HERE}/usr/bin/${PATH:+:$PATH}" export LD_LIBRARY_PATH="${HERE}/usr/lib/:${LD_LIBRARY_PATH:+:$LDLIBRARY_PATH}" export FLUTTER_ASSET_DIR="${HERE}/usr/share/flutter_assets" export FLUTTER_AOT_SNAPSHOT="${HERE}/usr/lib/app.so" EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1) exec "${EXEC}" "$@" "#; fn gen_desktop(name: &str, exec: &str, icon: &str) -> String { format!( r#"[Desktop Entry] Name={} Exec={} Icon={} Type=Application Categories=Utility; "#, name, exec, icon ) }