From 61fdd8e0a9bba0353fbaa720cbed45095edf013c Mon Sep 17 00:00:00 2001 From: Isaac Mills Date: Sun, 24 Oct 2021 17:32:32 -0400 Subject: [PATCH] Much better error management and better libs management --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 12 ++- libs/ld-linux-x86-64.so.2 | 1 + src/main.rs | 164 +++++++++++++++++++++++++++----------- 5 files changed, 128 insertions(+), 53 deletions(-) create mode 120000 libs/ld-linux-x86-64.so.2 diff --git a/Cargo.lock b/Cargo.lock index d60db89..e0d249a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" [[package]] name = "cargo-appimage" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "cargo_toml", diff --git a/Cargo.toml b/Cargo.toml index 7b90670..aa8397f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-appimage" -version = "1.1.0" +version = "1.2.0" authors = ["Isaac Mills ", "Jim Hessin "] edition = "2018" license = "GPL-3.0" diff --git a/README.md b/README.md index 7106d8a..8759b15 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,19 @@ cargo install cargo-appimage assets = ["images", "sounds"] ``` -5. (optional) By default, cargo-appimage will embed all the shared objects your executable uses into the appimage. To not do this, create a section in your Cargo.toml similar to the following - with: +5. (optional) If you are using external crates that use other programs or are not written in pure rust, you may want to check if you need to embed some shared libraries into your AppImage: + + 1. Running `cargo appimage` with this option in your Cargo.toml will automatically make a libs folder and put all of the shared objects your rust program uses in their respective directories. + ```toml [package.metadata.appimage] - link_deps = false + auto_link = true ``` + 2. AppImages aren't supposed to have EVERY library that your executable links to inside of the AppImage, so delete the libraries that you expect will be on every linux system (libc, libgcc, libpthread, ld-linux, libdl, etc.) + + 3. Remove the Cargo.toml option above and run `cargo appimage` again, now only the libs you want should be embedded in the Appimage + 6. run this command ```shell diff --git a/libs/ld-linux-x86-64.so.2 b/libs/ld-linux-x86-64.so.2 new file mode 120000 index 0000000..e80d418 --- /dev/null +++ b/libs/ld-linux-x86-64.so.2 @@ -0,0 +1 @@ +/usr/lib64/ld-linux-x86-64.so.2 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index defe593..c5d13a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use cargo_toml::Value; use fs_extra::dir::CopyOptions; use std::{ @@ -10,15 +10,12 @@ const APPDIRPATH: &str = "target/cargo-appimage.AppDir/"; fn main() -> Result<()> { let appdirpath = std::path::Path::new(APPDIRPATH); - match Command::new("cargo") + Command::new("cargo") .arg("build") .arg("--release") .args(std::env::args().nth(2)) .status() - { - Ok(_) => {} - Err(_) => bail!("Failed to build package"), - } + .context("Failed to build package")?; if !std::path::Path::new("./icon.png").exists() { std::fs::write("./icon.png", &[]).context("Failed to generate icon.png")?; @@ -31,55 +28,71 @@ fn main() -> Result<()> { let assets; let link_deps; - match meta - .metadata - .unwrap_or_else(|| Value::Array(Vec::with_capacity(0))) - { - Value::Table(t) => match t.get("appimage") { - Some(Value::Table(t)) => { - match t.get("assets") { - Some(Value::Array(v)) => { - assets = v - .to_vec() - .into_iter() - .filter_map(|v| match v { - Value::String(s) => Some(s), - _ => None, - }) - .collect() + if let Some(meta) = meta.metadata.as_ref() { + match meta { + Value::Table(t) => match t.get("appimage") { + Some(Value::Table(t)) => { + match t.get("assets") { + Some(Value::Array(v)) => { + assets = v + .to_vec() + .into_iter() + .filter_map(|v| match v { + Value::String(s) => Some(s), + _ => None, + }) + .collect() + } + _ => assets = Vec::with_capacity(0), + } + match t.get("auto_link") { + Some(Value::Boolean(v)) => link_deps = v.to_owned(), + _ => link_deps = false, } - _ => assets = Vec::with_capacity(0), } - match t.get("link_deps") { - Some(Value::Boolean(v)) => link_deps = v.to_owned(), - _ => link_deps = true, + _ => { + assets = Vec::with_capacity(0); + link_deps = false } - } + }, _ => { assets = Vec::with_capacity(0); - link_deps = true + link_deps = false } - }, - _ => { - assets = Vec::with_capacity(0); - link_deps = true - } - }; + }; + } else { + assets = Vec::with_capacity(0); + link_deps = false; + } + + fs_extra::dir::create_all(appdirpath.join("usr"), true) + .with_context(|| format!("Error creating {}", appdirpath.join("usr").display()))?; - fs_extra::dir::create_all(appdirpath.join("usr/bin"), false)?; + fs_extra::dir::create_all(appdirpath.join("usr/bin"), true) + .with_context(|| format!("Error creating {}", appdirpath.join("usr/bin").display()))?; if link_deps { + if !std::path::Path::new("libs").exists() { + std::fs::create_dir("libs").context("Could not create libs directory")?; + } let awk = std::process::Command::new("awk") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .arg("NF == 4 {print $3}; NF == 2 {print $1}") - .spawn()?; + .spawn() + .context("Could not start awk")?; awk.stdin .context("Make sure you have awk on your system")? .write_all( &std::process::Command::new("ldd") .arg(format!("target/release/{}", meta.name)) - .output()? + .output() + .with_context(|| { + format!( + "Failed to run ldd on {}", + format!("target/release/{}", meta.name) + ) + })? .stdout, )?; @@ -88,20 +101,60 @@ fn main() -> Result<()> { .context("Unknown error ocurred while running awk")? .read_to_string(&mut linkedlibs)?; + fs_extra::dir::create("libs", true).context("Failed to create libs dir")?; + for line in linkedlibs.lines() { if line.starts_with("/") { - fs_extra::dir::create_all( - appdirpath.join( - std::path::Path::new(&line[1..]) - .parent() - .context("Lib has no parent path")?, - ), - false, - )?; - std::fs::copy(line, appdirpath.join(&line[1..]))?; + if !std::path::Path::new("libs").join(&line[1..]).exists() { + std::os::unix::fs::symlink( + line, + std::path::Path::new("libs").join( + std::path::Path::new(line) + .file_name() + .with_context(|| format!("No filename for {}", line))?, + ), + ) + .with_context(|| { + format!( + "Error symlinking {} to {}", + line, + std::path::Path::new("libs").join(&line[1..]).display() + ) + })?; + } } } } + + if std::path::Path::new("libs").exists() { + for i in std::fs::read_dir("libs").context("Could not read libs dir")? { + let ref path = i?.path(); + let link = std::fs::read_link(path) + .with_context(|| format!("Error reading link in libs {}", path.display()))?; + + if fs_extra::dir::create_all( + appdirpath.join( + &link + .parent() + .with_context(|| format!("Lib {} has no parent dir", &link.display()))? + .to_str() + .with_context(|| format!("{} is not valid Unicode", link.display()))?[1..], + ), + false, + ) + .is_err() + {} + let dest = appdirpath.join( + &link + .to_str() + .with_context(|| format!("{} is not valid Unicode", link.display()))?[1..], + ); + std::fs::copy(&link, &dest).with_context(|| { + format!("Error copying {} to {}", &link.display(), dest.display()) + })?; + } + } + std::fs::copy( format!("target/release/{}", meta.name), appdirpath.join("usr/bin/bin"), @@ -125,11 +178,26 @@ fn main() -> Result<()> { "[Desktop Entry]\nName={}\nExec=bin\nIcon=icon\nType=Application\nCategories=Utility;", meta.name ), - )?; + ) + .with_context(|| { + format!( + "Error writing desktop file {}", + appdirpath.join("cargo-appimage.desktop").display() + ) + })?; std::fs::copy( std::path::PathBuf::from(std::env::var("HOME")?).join(".cargo/bin/cargo-appimage-runner"), appdirpath.join("AppRun"), - )?; + ) + .with_context(|| { + format!( + "Error copying {} to {}", + std::path::PathBuf::from(std::env::var("HOME").unwrap()) + .join(".cargo/bin/cargo-appimage-runner") + .display(), + appdirpath.join("AppRun").display() + ) + })?; Command::new("appimagetool") .arg(appdirpath)