博客 / 詳情

返回

Linux 通用軟件包 AppImage 打包詳解

近水樓台先得月,向陽花木易為春。

導航

  • 格式介紹 - AppImage
  • 手動打包 - appimagetool
  • 自動打包 - linuxdeploy
  • 雜七雜八

格式介紹 - AppImage

AppImage 是 Linux 系統中一種新型的軟件包格式,它與 rpm、deb 這些軟件包格式相比最大的不同便是:(1)無需安裝,即用即刪。(2)只需打包一次,便可到處運行。完美的解決了不同 Linux 發行版(Ubuntu/Debian/Fedora/CentOS)之間軟件包不統一的問題。

它的工作原理便是將程序運行所需的文件全部打包在一個文件中,待程序運行時再將這些文件提取在 /tmp/.mount_xxxxxxx/ 目錄中,然後執行 AppRun 腳本啓動程序以進行資源的調用。以下便是一個 AppImage 文件內部包含的目錄樹結構:

AppDir/
 ├── AppRun
 ├── 應用圖標.png
 ├── 程序名.desktop
 ├── usr/
     ├── bin/
     ├── lib/
     ├── share/

它本質上就是一個 squashfs 文件系統 + runtime 執行器

特別注意:

(1)要實現跨平台運行,待打包的程序最好是在 CentOS 7 系統上進行編譯 ,然後再進行打包。【注:編譯 C/C++ 程序所使用的系統庫 glibc 在 Linux 系統上幾乎肯定存在,而該庫有着良好的向後兼容性,因此使用舊版本的 glibc 庫編譯出來的程序幾乎可以完美的運行在新版本的 glibc 系統上。而在 CentOS 7 上的 glibc 版本是 2.17,該版本較舊且兼容性較好,因此在其系統上編譯出來的 C 程序通常也可以在大部分的 Linux 發行版系統中使用。】

(2)待打包程序依賴的 lib 文件中最好只包含其專屬的庫文件即可,不要包含類似 glibc 這樣的系統庫文件。【注:這是因為在 A 系統中的 glibc 文件通常並不可以在 B 系統中使用,因此為了避免 AppImage 程序運行錯誤,請勿這樣去做。再者,glibc 在 Linux 系統中是肯定會存在的,因此也並不需要額外去包含這樣的依賴文件。】

手動打包 - appimagetool

appimagetool 是由 AppImage 官方製作的打包工具,在使用它進行打包時,必須要先分析待打包程序的動態庫依賴情況,然後再完成對 AppDir 目錄的裝填,最後才能使用 appimagetool 完成對程序的打包。由於分析程序的依賴情況是個很複雜的問題,因此該工具在使用上體驗並不太好。

接下來,我將演示如何對一個簡單的 C 程序完成打包過程:

(1)文件準備:hello.c。

// 主文件 hello.c
#include <stdio.h>

int main() {
    printf("Hello Appimage\n");
    return 0;
}

(2)編譯並打包

#(1)編譯及檢驗運行
gcc -o hello hello.c
./hello

#(2)製作 AppDir 目錄樹
mkdir -p AppDir/usr/bin/
cp ./hello AppDir/usr/bin/
wget https://github.com/boolean-world/appimage-resources/blob/master/hello-world-appimage/hello-world-icon.png -O AppDir/hello.png	#任意圖片文件即可
nano AppDir/hello.desktop #文件內容見下方
nano AppDir/AppRun #腳本內容見下方

#(3)開始製作 AppImage 程序
/root/appimagetool-x86_64.AppImage AppDir/

附註:

hello.desktop 文件如下:

[Desktop Entry]
Name=hello
Exec=hello
Icon=hello
Type=Application
Categories=Utility;
Terminal=true

AppRun 腳本如下:

#!/bin/sh
APPDIR="$(dirname "$(readlink -f "$0")")"

# 添加庫目錄
if [ -d "$APPDIR/lib64" ]; then
    export LD_LIBRARY_PATH="$APPDIR/lib64:$LD_LIBRARY_PATH"
fi
if [ -d "$APPDIR/usr/lib" ]; then
    export LD_LIBRARY_PATH="$APPDIR/usr/lib:$LD_LIBRARY_PATH"
fi

# 啓動主程序 //注意:不同應用主程序路徑需要修改
exec "$APPDIR/usr/bin/hello" "$@"

AppDir 目錄樹結構如下:

AppDir/
├── AppRun		//啓動程序,可以是簡單的腳本,也可以是 ELF,只要保證運行該腳本主程序能被啓動即可。
├── hello.desktop	//注意 EXEC 的值,它對應的是/usr/bin/目錄中的程序,而 Icon 對應的是當前目錄
├── hello.png		//也支持 svg 格式
└── usr
    └── bin
        └── hello

自動打包 - linuxdeploy

linuxdeploy 是一個由第三方製作的 AppImage 打包工具,與 appimagetool 不同的是,它可以對待打包程序自動進行依賴分析,並自動將所需的依賴及資源文件按照 AppDir 的目錄格式給裝填完畢,用户只需將模版化的 desktop 文件和 icon 文件準備好即可,使用起來簡直美滋滋。

【示例一】:接下來,我將演示如何對一個需要依賴的簡單 C 程序完成打包過程:

(1)文件準備:mylib.h、mylib.c、main.c、Makefile。

// 動態庫頭文件 mylib.h
#ifndef MYLIB_H
#define MYLIB_H

int add(int a, int b);
void hello();

#endif
// 動態庫源碼 mylib.c
#include <stdio.h>
#include "mylib.h"

int add(int a, int b) {
    return a + b;
}

void hello() {
    printf("Hello from my dynamic library!\n");
}
// 主程序 main.c
#include <stdio.h>
#include "mylib.h"

int main() {
    hello();
    int result = add(3, 5);
    printf("3 + 5 = %d\n", result);
    return 0;
}
# Makefile 文件
CC=gcc
CFLAGS=-fPIC -Wall
LDFLAGS=-shared
TARGET_LIB=libmylib.so
TARGET_MAIN=main

all: $(TARGET_LIB) $(TARGET_MAIN)

$(TARGET_LIB): mylib.o
	$(CC) $(LDFLAGS) -o $(TARGET_LIB) mylib.o

mylib.o: mylib.c mylib.h
	$(CC) $(CFLAGS) -c mylib.c

$(TARGET_MAIN): main.o $(TARGET_LIB)
	$(CC) main.o -L. -lmylib -o $(TARGET_MAIN)

main.o: main.c mylib.h
	$(CC) -c main.c

clean:
	rm -f *.o $(TARGET_MAIN) $(TARGET_LIB)

(2)編譯並打包

#(1)編譯及檢驗運行
cd myapp
make
mv libmylib.so /lib64/libmylib.so
./main

#(2)製作的 main.desktop 文件內容
cat main.desktop

[Desktop Entry]
Name=main
Exec=main
Icon=main
Type=Application
Categories=Utility;
Terminal=true

#(3)獲取一個 Icon 文件
wget https://github.com/boolean-world/appimage-resources/blob/master/hello-world-appimage/hello-world-icon.png -O main.png

#(4)開始製作 AppImage 程序
/root/linuxdeploy-x86_64.AppImage --appdir /root/myapp --output appimage --icon-file main.png --desktop-file main.desktop -e main
ls -l main*.AppImage 

【示例二】:最後,我再演示如何對一個系統命令 find 完成打包過程:

#(1)製作的 find.desktop 文件內容
cat find.desktop

[Desktop Entry]
Name=find
Exec=find
Icon=find
Type=Application
Categories=Utility;
Terminal=true

#(2)獲取一個 Icon 文件
wget https://github.com/boolean-world/appimage-resources/blob/master/hello-world-appimage/hello-world-icon.png -O find.png

#(3)開始製作 AppImage 程序
cd $(dirname $(which find))
/root/linuxdeploy-x86_64.AppImage --appdir /root/find --output appimage --icon-file find.png --desktop-file find.desktop -e find
ls -l find*.AppImage 

注意:(1)建議將 icon 和 desktop 文件放置在 find 命令根目錄下,這樣在打包的時候能夠避免很多問題。(2)由於 linuxdeploy 在打包環節調用的是 appimagetool,而 appimagetool 在打包的時候會在 github 上拉取 runtime 文件,因此在使用前建議設置全局代理以確保 github 可訪問。

雜七雜八

  1. AppImage 參考:官網、參考文檔、軟件分發
  2. linuxdeploy 插件系統:awesome-linuxdeploy,插件 linuxdeploy-plugin-checkrt 在打包較複雜的 C 程序時可能會比較有用。
  3. 理想的編譯環境:CentOS 7 x64、CentOS 6 x32。【注意:64 位系統打包出的 AppImage 不可在 32 位系統使用。 】
  4. pkg2appimage 工具支持將 deb 軟件包轉換給 AppImage 格式的軟件包。【注:理論是美好的,但實際打包時失敗率太高且太折騰,不推薦使用,還是老實用 linuxdeploy 吧。】
  5. 已打包的 AppImage 軟件包,可通過 ./app*.AppImage --appimage-extract 將其包含的文件重新提取出來,以供參考或重複打包。

(*)全局代理設置

export http_proxy=http://192.168.56.1:7890
export https_proxy=http://192.168.56.1:7890
export no_proxy=192.168.56.1,localhost
export HTTP_PROXY=http://192.168.56.1:7890
export HTTPS_PROXY=http://192.168.56.1:7890
export NO_PROXY=192.168.56.1,localhost
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.