par , 21/05/2020 à 16h17 (1169 Affichages)
Bonjour, dans ce billet je vais essayer de parler simplement de la création d'une AppImage pour un binaire graphique Rust GTK, en utilisant une image Docker en mode intéractif.
Prérequis
Pour pouvoir suivre ce billet, il est recommandé de connaître les bases de
- Rust: syntaxe de base, ownership/borrowing, struct/impl/enum, cargo, (juste pour comprendre les sources du projet si vous le souhaitez)
- Gtk+Relm: les bases, j'ai présenté cette combinaison dans un billet précédent,
- Docker: création/exécution d'images, mode intéractif (tty), volumes,
- Git: pour pouvoir récupérer les sources du projet "cobaye"
- Commandes de bases dans un terminal linux: cd, ls, notamment. En effet, l'utilisation du terminal sera massive dans ce tutoriel.
Le format AppImage
Il s'agit en quelque sortes d'un fichier compressée, embarquant un programme et ses dépendances dynamiques notamment, et qui se monte sur son propre systèmes de fichiers. Ce format tente donc de résoudre la problématique du déploiement d'application vis-à-vis de la fragmentation des nombreuses distributions Linux.
Cependant, il est basé sur l'utilisation de l'utilitaire Fuse. Ce programme, disponible sous forme de paquets dans certaines distributions Linux (s'il n'est pas déjà installé par défaut), permet de monter un système de fichiers sans pour autant que l'on soit administrateur (root).
Le programme "cobaye" et ses dépendances
Pour effectuer notre expérimentation, nous allons compiler une calculatrice on ne peut plus simple et ridicule et qui, pour des raisons pratiques, n'a pas d'opérateur division.
Veuillez cloner le projet dans le dossier de votre choix:
1 2
|
$> git clone https://github.com/loloof64/SimpleCalculator.git calculatrice |
En examinant les sources et le fichier Cargo.toml, vous pouvez constater qu'il s'agit d'une application Gtk+Relm minimaliste.
Veuillez compiler (et exécuter) le programme afin de pouvoir en analyser les dépendances dynamiques: (j'utilise la version 2018 de Rust, la dernière version à ce jour devrait convenir)
1 2
|
$> cargo run --release |
Si tout se passe bien, l'exécutable se trouve dans le sous-dossier target/release. Analysons-donc les dépendances dynamiques à l'aide de la commande ldd. Voici ce que cela a donné de mon côté:
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
|
$> ldd target/release/simple-calc
linux-vdso.so.1 (0x00007fff669c3000)
libgtk-3.so.0 => /lib/x86_64-linux-gnu/libgtk-3.so.0 (0x00007f8a33d5c000)
libgdk-3.so.0 => /lib/x86_64-linux-gnu/libgdk-3.so.0 (0x00007f8a33c58000)
libpango-1.0.so.0 => /lib/x86_64-linux-gnu/libpango-1.0.so.0 (0x00007f8a33c0c000)
libcairo-gobject.so.2 => /lib/x86_64-linux-gnu/libcairo-gobject.so.2 (0x00007f8a33c00000)
libcairo.so.2 => /lib/x86_64-linux-gnu/libcairo.so.2 (0x00007f8a33ae0000)
libgdk_pixbuf-2.0.so.0 => /lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0 (0x00007f8a33ab8000)
libgio-2.0.so.0 => /lib/x86_64-linux-gnu/libgio-2.0.so.0 (0x00007f8a338db000)
libgobject-2.0.so.0 => /lib/x86_64-linux-gnu/libgobject-2.0.so.0 (0x00007f8a3387e000)
libglib-2.0.so.0 => /lib/x86_64-linux-gnu/libglib-2.0.so.0 (0x00007f8a33756000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f8a33750000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8a3372d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f8a33713000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8a33520000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8a344dd000)
libgmodule-2.0.so.0 => /lib/x86_64-linux-gnu/libgmodule-2.0.so.0 (0x00007f8a3351a000)
libpangocairo-1.0.so.0 => /lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 (0x00007f8a33509000)
libX11.so.6 => /lib/x86_64-linux-gnu/libX11.so.6 (0x00007f8a333cb000)
libXi.so.6 => /lib/x86_64-linux-gnu/libXi.so.6 (0x00007f8a333b9000)
libXfixes.so.3 => /lib/x86_64-linux-gnu/libXfixes.so.3 (0x00007f8a331b3000)
libatk-1.0.so.0 => /lib/x86_64-linux-gnu/libatk-1.0.so.0 (0x00007f8a33187000)
libatk-bridge-2.0.so.0 => /lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0 (0x00007f8a33151000)
libepoxy.so.0 => /lib/x86_64-linux-gnu/libepoxy.so.0 (0x00007f8a3301f000)
libfribidi.so.0 => /lib/x86_64-linux-gnu/libfribidi.so.0 (0x00007f8a33002000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8a32eb3000)
libharfbuzz.so.0 => /lib/x86_64-linux-gnu/libharfbuzz.so.0 (0x00007f8a32dbc000)
libpangoft2-1.0.so.0 => /lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 (0x00007f8a32da0000)
libfontconfig.so.1 => /lib/x86_64-linux-gnu/libfontconfig.so.1 (0x00007f8a32d5a000)
libfreetype.so.6 => /lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007f8a32c9f000)
libXinerama.so.1 => /lib/x86_64-linux-gnu/libXinerama.so.1 (0x00007f8a32c9a000)
libXrandr.so.2 => /lib/x86_64-linux-gnu/libXrandr.so.2 (0x00007f8a32c8d000)
libXcursor.so.1 => /lib/x86_64-linux-gnu/libXcursor.so.1 (0x00007f8a32c80000)
libXcomposite.so.1 => /lib/x86_64-linux-gnu/libXcomposite.so.1 (0x00007f8a32c79000)
libXdamage.so.1 => /lib/x86_64-linux-gnu/libXdamage.so.1 (0x00007f8a32c74000)
libxkbcommon.so.0 => /lib/x86_64-linux-gnu/libxkbcommon.so.0 (0x00007f8a32c33000)
libwayland-cursor.so.0 => /lib/x86_64-linux-gnu/libwayland-cursor.so.0 (0x00007f8a32c2a000)
libwayland-egl.so.1 => /lib/x86_64-linux-gnu/libwayland-egl.so.1 (0x00007f8a32c25000)
libwayland-client.so.0 => /lib/x86_64-linux-gnu/libwayland-client.so.0 (0x00007f8a32c14000)
libXext.so.6 => /lib/x86_64-linux-gnu/libXext.so.6 (0x00007f8a32bfd000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f8a32bf2000)
libthai.so.0 => /lib/x86_64-linux-gnu/libthai.so.0 (0x00007f8a32be7000)
libpixman-1.so.0 => /lib/x86_64-linux-gnu/libpixman-1.so.0 (0x00007f8a32b40000)
libpng16.so.16 => /lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f8a32b08000)
libxcb-shm.so.0 => /lib/x86_64-linux-gnu/libxcb-shm.so.0 (0x00007f8a32b01000)
libxcb.so.1 => /lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f8a32ad8000)
libxcb-render.so.0 => /lib/x86_64-linux-gnu/libxcb-render.so.0 (0x00007f8a32ac9000)
libXrender.so.1 => /lib/x86_64-linux-gnu/libXrender.so.1 (0x00007f8a328bf000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f8a328a3000)
libmount.so.1 => /lib/x86_64-linux-gnu/libmount.so.1 (0x00007f8a32843000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f8a32816000)
libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f8a327fb000)
libffi.so.6 => /lib/x86_64-linux-gnu/libffi.so.6 (0x00007f8a327f1000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f8a3277d000)
libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f8a3272e000)
libatspi.so.0 => /lib/x86_64-linux-gnu/libatspi.so.0 (0x00007f8a326f5000)
libgraphite2.so.3 => /lib/x86_64-linux-gnu/libgraphite2.so.3 (0x00007f8a326c8000)
libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f8a3269a000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f8a32691000)
libdatrie.so.1 => /lib/x86_64-linux-gnu/libdatrie.so.1 (0x00007f8a32687000)
libXau.so.6 => /lib/x86_64-linux-gnu/libXau.so.6 (0x00007f8a3267f000)
libXdmcp.so.6 => /lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f8a32677000)
libblkid.so.1 => /lib/x86_64-linux-gnu/libblkid.so.1 (0x00007f8a32620000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f8a3259b000)
libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f8a324f3000)
libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f8a324d7000)
liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f8a324b0000)
liblz4.so.1 => /lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f8a32490000)
libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007f8a32372000)
libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f8a3234f000) |
Elles sont toutes résolues : c'est la seule prérogative pour pouvoir construire un fichier AppImage à partir du binaire.
Nous pouvons donc passer à l'étape de la création du fichier AppImage. Pour cela je vais me servir de Docker, car il est recommandé de construire l'AppImage sur une version de linux assez ancienne afin qu'elle puisse ensuite s'exécuter sur la majorité des distributions existantes. Sans rentrer dans les détails, c'est notamment à cause de la libc.
J'ai donc opté pour l'utilisation de Docker pour me faciliter la tâche, sachant que je pourrais monter un volume sur l'image docker afin de pouvoir partager des fichiers depuis mon système hôte.
Sachez également que Travis CI (entre autres) permet de faire cela plus simplement depuis un repository Github, mais cela sort du cadre de ce billet.
Construction avec Docker
L'image personnalisée pour l’environnement de travail
Tout d'abord, nous pouvons nous constituer une image Docker qui contiendra à minima l'installation de Rust et les utilitaires pour construire l'AppImage. Par la suite nous lancerons un conteneur basé sur cette image (que l'on pourra réutiliser à souhait), en mode intéractif, afin de construire l'AppImage
Veuillez créer un conteneur basé sur la version 16.04 d'Ubuntu, en démarrant un bash en mode interactif:
1 2
|
$> docker run -t -i --name ubuntu_16_rust_setup ubuntu:16.04 /bin/bash |
Voici la liste des commandes à entrer, une à une, une fois que le conteneur a démarré (attention à ne pas insérer mes commentaires
):
1 2 3 4 5
|
apt update
apt install wget curl git fuse build-essential libgtk-3-dev -y
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # installation de Rust : veuillez chosir l'option par défaut
exit |
De retour dans votre terminal hôte (grâce à la commande exit), veuillez transformer le conteneur juste créé en image:
1 2
|
$> docker commit ubuntu_16_rust_setup ubuntu_16_rust:v1 |
Comme précisé plus haut, cette image pourra être réutilisée plusieurs fois.
Construction de l'AppImage
Nous pouvons donc démarrer un conteneur à partir de notre base de travail:
- j'utilise encore le mode intéractif
- je monte un volume me permettant d'accéder aux ressources que j'ai préparées pour mon projet (fichier desktop, icone): ici avec le dossier courant ($PWD)
- j'indique également quelques drapeaux supplémentaires afin de pouvoir créer l'AppImage sans tracas
- ce conteneur sera détruit (--rm) en le quittant : vous pouvez en décider autrement si vous le souhaitez (c'est juste dans mon utilisation personnelle, j'ai trouvé cela plus pratique)
1 2
|
$> docker run -t -i -v "$PWD:/home/host_documents" --cap-add SYS_ADMIN --cap-add MKNOD --device=/dev/fuse --security-opt apparmor:unconfined --rm ubuntu_16_rust:v1 /bin/bash |
Préparatifs côté hôte
Avant de commencer la construction de l'AppImage, préparons les ressources nécessaires, que nous placerons dans le dossier hôte sur lequel nous avons monté notre image de travail:
- un fichier .desktop : je vous passe le contenu du mien plus bas
- une icône png (la taille 16x16 peut faire l'affaire)
- le script qui nous simplifiera la tâche, je vous en passe le contenu plus bas
Voici un fichier .desktop (SimpleCalc.desktop) que vous pouvez utiliser:
1 2 3 4 5 6 7 8 9
|
[Desktop Entry]
Version=1.0
Type=Application
Name=Simple calculator
Comment=A ridiculously simple calculator
Exec=simple-calc
Icon=calculator
Categories=Utility;Education; |
Il requiert donc que l'icône utilisée s'appelle calculator.png.
Voici le script que nous utiliserons dans le conteneur (build_appimage.sh) :
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
|
#!/bin/bash
##############################################################
# Building exectuable into AppImage (Laurent Bernabe) #
# from an Ubuntu 14.04 x86_64 docker image. #
# #
# Also these arguments are needed when lauching docker run ! #
# --cap-add SYS_ADMIN #
# --cap-add MKNOD #
# --device=/dev/fuse #
# --security-opt apparmor:unconfined #
# #
# Volume /home/host_documents should point to #
# the build files folder ! #
# #
# Arguments: #
# 1 => Path to AppImage target (with AppImage extension) #
# 2 => Path to executable to wrap #
# 3 => Desktop file name (with .desktop extension) #
# 4 => Icon file name (with extension) #
##############################################################
if [[ "$#" -lt 4 ]]; then
echo "Usage: build_appimage TARGET_APPIMAGE EXECUTABLE DESKTOP_FILE ICON_FILE"
exit 1
fi
apt update
apt install wget fuse file -y
wget -P /tmp https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget -P /tmp https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x /tmp/linuxdeploy-x86_64.AppImage /tmp/appimagetool-x86_64.AppImage
cd /home/host_documents
/tmp/linuxdeploy-x86_64.AppImage -e $2 -d $3 -i $4 --appdir $1
/tmp/appimagetool-x86_64.AppImage $1
exit 0 |
Notamment, ce script:
- Télécharge les deux utilitaires necessaires à la création d'AppImage à partir d'un binaire existant,
- construit un dossier AppImage à partir de l'exécutable et de ses ressources,
- construit une archive AppImage à partir du dossier ainsi généré.
Etape finale
Allons-y !
Rendons les commandes de Rust disponibles:
1 2
|
$> source $HOME/.cargo/env |
Crééons un dossier projets:
1 2 3
|
$> mkdir -p /home/projects
$> cd /home/projects |
Clonons le projet et compilons-le:
1 2 3 4
|
$> git clone https://github.com/loloof64/SimpleCalculator.git calculatrice
$> cd calculatrice
$> cargo build --release |
Enfin passons à la production de l'AppImage, en n'oubliant pas de renseigner les différents paramètres
1 2 3 4
|
$> cd /home/host_documents
$> chmod +x build_appimage.sh
$> ./build_appimage.sh SimpleCalculator.AppImage /home/projects/calculatrice/target/release/simple-calc ./SimpleCalc.desktop ./calculator.png |
Normalement vous devriez disposer du fichier AppImage dans votre dossier monté sur le système hôte. N'oubliez pas de quitter le conteneur
Conclusion
Voilà en ce qui concerne la création de l'AppImage, j'espère que cela n'a pas été indigeste.
Vous avez donc la possibilité de tester le programme sur différentes distributions Linux. Sachez que pour ma part, j'ai réussi à l'exécuter sur des distributions Ubuntu (Ubuntu, Kubuntu, Xubuntu), Gentoo, Manjaro Linux, Linux Mint datant de 2016/2017 et même 2020 entre autres.
Je tiens quand même à remercier les contributeurs des projets relatifs à AppImageKit, qui m'ont gentiment répondu à mes interrogations sur leur chat Irc : en particulier TheNinjaAssasin.
Mis à jour 21/05/2020 à 18h59 par tails
- Catégories
-
Rust