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