最佳實務
本節包含一些最佳實務與建議,說明如何一般性地設計與撰寫軟體,以便能輕鬆打包成 AppImage。
一般建議
務必理解 AppImage 只是用來散布應用程式的格式。就這點而言,AppImage 類似於 .zip 或 .iso 檔案。它並不定義如何編譯應用程式,也不是建置系統。
務必在 AppImage 中放入能與多種目標系統相容的二進位檔。放入 AppImage 的內容稱為「payload」或「ingredients」。製作 payload 需要一些考量,因為您希望 AppImage 能在越多目標系統上執行越好。
要讓 AppImage 在大多數系統上執行,需符合以下條件:
:ref:`二進位檔不得使用編譯時寫死的絕對路徑 <ref-binaries-no-abs-paths>`(若有使用,需進行二進位修補)
AppImage 必須包含所有不一定存在於其目標基礎系統中的函式庫與其他相依項目。
AppImage 內的二進位檔必須在不新於其預計支援的最舊基礎系統的環境中編譯。
AppImage 應實際在其預計支援的基礎系統上測試。
二進位檔不得使用編譯時寫死的絕對路徑
由於 AppImage 每次執行時都會掛載到檔案系統中的不同位置,因此絕對不能使用編譯時寫死的絕對路徑。例如,若應用程式要存取影像等資源,應該使用相對於主執行檔的位置。不幸的是,許多應用程式在編譯時就寫死了絕對路徑($PREFIX,最常見為 /usr)。
開放原始碼應用程式
在可行的情況下,應修改應用程式原始碼以避免使用絕對路徑。有多種做法,其中在 Linux 上的典型方式是解析 proc/self/exe 取得主執行檔路徑,再由此建立相對路徑。如此可同時在一般安裝與可搬移安裝(如 AppImage)中正常運作。
有些函式庫可簡化此流程,例如 BinReloc。也可參考 Resourceful,該專案研究跨平台技術,用於建置會使用資源檔(如圖示、設定、資料)的應用程式與函式庫。
某些應用程式框架(如 Qt)已內建此功能,例如 :code:`QString QCoreApplication::applicationDirPath()`(`參見文件`_),並由此建立指向 ../share/kaidan/images/ 的 相對 路徑。
警告
QStandardPaths::standardLocations(QStandardPaths::AppDataLocation) 無法可靠運作。
依 Qt 文件,其會解析為 ~/.local/share/<APPNAME>、/usr/local/share/<APPNAME>、/usr/share/<APPNAME>,但很明顯 /usr 並不是 AppImage 中這些內容所在的位置。
含有編譯時寫死絕對路徑的閉源應用程式
若無法更改應用程式原始碼(例如閉源應用程式),可以對可執行檔進行二進位修補。
技巧是在二進位檔中搜尋 /usr,並將其替換為等長字串 :code:`././`(意即「這裡」)。可用以下命令執行:
find usr/ -type f -executable -exec sed -i -e "s|/usr|././|g" {} \;
此命令也可在 AppImage/pkg2appimage/functions.sh#L79 的 bash 函式集合中找到。要讓二進位修補後的應用程式運作,啟動前需切換到應用程式目錄內的 usr/ 目錄。
在足夠舊的基礎系統上編譯二進位檔
AppImage 使用的 ingredients 不應在比其預計支援的最舊基礎系統還新的系統上建置。
某些核心函式庫(如 glibc)常會破壞與舊版基礎系統的相容性,這代表二進位檔能在較新的系統上執行,但無法在比其編譯環境更舊的系統上執行。
若您遇到如下錯誤:
failed to initialize: /lib/tls/i686/cmov/libc.so.6: version `GLIBC_2.11' not found
這表示該二進位檔是在比您嘗試執行它的系統更新的系統上編譯的。您應使用在較舊系統上編譯的二進位檔。不幸的是,多數散布版通常只在最新系統上編譯最新版應用程式,因此您很難找到能在舊系統上執行的前沿軟體二進位檔。其中一種做法是自己在較舊的基礎系統上編譯相依項目,或使用 LibcWrapGenerator、glibc_version_header 或 bingcc。
在為 Subsurface 專案製作 AppImage 時,我們使用 CentOS 7 得到非常好的結果。這是撰寫本文時仍受支援的最舊 Linux 散布版,且不算太新。不過在 EPEL 與 devtools-2 儲存庫(社群版的 Red Hat Developer Toolset 2)中仍有最新的 Qt 與現代編譯器。在此散布版上編譯出的二進位檔幾乎可在任何散布版上執行,包括 Debian oldstable。
務必查看 https://github.com/AppImage/pkg2appimage,這是我用來建置並託管 AppImage 的方式,以及在雲端使用 travis-ci、docker、docker-hub 與 bintray 產出它們的建置系統。特別請查看 Subsurface 與 Scribus 的 recipes。
請參閱 https://github.com/AppImage/AppImageKit/wiki/Docker-Hub-Travis-CI-Workflow,了解如何建立包含 GitHub 儲存庫、Docker Hub 與 Travis CI 的全自動持續建置流程。
您也可以考慮將某些較特殊的函式庫以靜態方式連結。是的,連 Debian 都這麼做:https://wiki.debian.org/Lintian
也參考
此概念也在 在舊系統上建置,在新系統上執行 中說明。
libstdc++.so.6
備註
一般經驗法則是:請使用不新於您仍想支援的最舊散布版所隨附的 libstdc++.so.6,亦即仍受支援的最舊 Ubuntu LTS 版本。