Liaison dynamique des applications Qt sur macOS

Aujourd’hui, j’ai publié une version de RaidenVideoRipper pour les appareils Apple équipés de processeurs macOS et M1/M2/M3/M4 (Apple Silicon). RaidenVideoRipper est une application de montage vidéo rapide qui vous permet de couper une partie d’un fichier vidéo dans un nouveau fichier. Vous pouvez également créer des gifs et exporter la piste audio au format mp3.

Ensuite, je décrirai brièvement les commandes que j’ai utilisées pour accomplir cela. La théorie de ce qui se passe ici, la documentation des utilitaires, peut être lue sur les liens suivants :
https://www.unix.com/man-page/osx/1/otool/
https://www.unix.com/man-page/osx/1/install_name_tool/
https://llvm.org/docs/CommandGuide/llvm-nm.html
https://linux.die.net/man/1/file
https://www.unix.com/man-page/osx/8/SPCTL/
https://linux.die.net/man/1/chmod
https://linux.die.net/man/1/ls
https://man7.org/linux/man-pages/man7/xattr.7.html
https://doc.qt.io/qt-6/macos-deployment.html

Pour commencer, installez Qt sur votre macOS et installez également l’environnement pour Qt Desktop Development. Après cela, assemblez votre projet, par exemple, dans Qt Creator, puis je décrirai ce qui est nécessaire pour garantir que les dépendances avec des bibliothèques dynamiques externes sont traitées correctement lors de la distribution de l’application aux utilisateurs finaux.

Créez un répertoire Frameworks dans le dossier YOUR_APP.app/Contents de votre application et placez-y des dépendances externes. Par exemple, voici à quoi ressemble le Frameworks de l’application RaidenVideoRipper :

Frameworks
├── DullahanFFmpeg.framework
│   ├── dullahan_ffmpeg.a
│   ├── libavcodec.60.dylib
│   ├── libavdevice.60.dylib
│   ├── libavfilter.9.dylib
│   ├── libavformat.60.dylib
│   ├── libavutil.58.dylib
│   ├── libpostproc.57.dylib
│   ├── libswresample.4.dylib
│   └── libswscale.7.dylib
├── QtCore.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtCore -> Versions/Current/QtCore
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
├── QtGui.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtGui -> Versions/Current/QtGui
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
├── QtMultimedia.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtMultimedia -> Versions/Current/QtMultimedia
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
├── QtMultimediaWidgets.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtMultimediaWidgets -> Versions/Current/QtMultimediaWidgets
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
├── QtNetwork.framework
│   ├── Headers -> Versions/Current/Headers
│   ├── QtNetwork -> Versions/Current/QtNetwork
│   ├── Resources -> Versions/Current/Resources
│   └── Versions
└── QtWidgets.framework
    ├── Headers -> Versions/Current/Headers
    ├── QtWidgets -> Versions/Current/QtWidgets
    ├── Resources -> Versions/Current/Resources
    └── Versions

Pour plus de simplicité, j’ai imprimé uniquement le deuxième niveau d’imbrication.

Ensuite, nous imprimons les dépendances dynamiques actuelles de votre application :

otool -L RaidenVideoRipper 

Sortie pour le binaire RaidenVideoRipper, qui se trouve dans RaidenVideoRipper.app/Contents/MacOS :

RaidenVideoRipper:
	@rpath/DullahanFFmpeg.framework/dullahan_ffmpeg.a (compatibility version 0.0.0, current version 0.0.0)
	@rpath/QtMultimediaWidgets.framework/Versions/A/QtMultimediaWidgets (compatibility version 6.0.0, current version 6.8.1)
	@rpath/QtWidgets.framework/Versions/A/QtWidgets (compatibility version 6.0.0, current version 6.8.1)
	@rpath/QtMultimedia.framework/Versions/A/QtMultimedia (compatibility version 6.0.0, current version 6.8.1)
	@rpath/QtGui.framework/Versions/A/QtGui (compatibility version 6.0.0, current version 6.8.1)
	/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 2575.20.19)
	/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 367.4.0)
	@rpath/QtNetwork.framework/Versions/A/QtNetwork (compatibility version 6.0.0, current version 6.8.1)
	@rpath/QtCore.framework/Versions/A/QtCore (compatibility version 6.0.0, current version 6.8.1)
	/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
	/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers (compatibility version 1.0.0, current version 709.0.0)
	/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1800.101.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)

Comme on peut le voir dans RaidenVideoRipper dans les dépendances Qt et dullahan_ffmpeg. Dullahan FFmpeg est un fork de FFmpeg qui encapsule ses fonctionnalités dans une bibliothèque dynamique, avec la possibilité d’obtenir la progression et l’annulation de l’exécution en cours à l’aide de routines C.
Ensuite, remplacez les chemins de l’application et toutes les bibliothèques nécessaires à l’aide de install_name_tool.

La commande pour cela est :

install_name_tool -change old_path new_path target

Exemple d’utilisation :

install_name_tool -change /usr/local/lib/libavfilter.9.dylib @rpath/DullahanFFmpeg.framework/libavfilter.9.dylib dullahan_ffmpeg.a

Après avoir saisi tous les chemins corrects, l’application devrait démarrer correctement. Vérifiez que tous les chemins d’accès aux bibliothèques sont relatifs, transférez le binaire et rouvrez-le.
Si vous voyez une erreur, vérifiez les chemins via otool et modifiez à nouveau via install_name_tool.

Il y a aussi une erreur de confusion des dépendances, lorsque la bibliothèque que vous avez remplacée n’a pas de symbole dans le tableau vous pouvez vérifier la présence ou l’absence d’un symbole comme ceci :

nm -gU path

Une fois exécuté, vous verrez toute la table des symboles de la bibliothèque ou de l’application.
Il est également possible que vous ayez copié les dépendances d’une mauvaise architecture, vous pouvez le vérifier à l’aide du fichier :

file path

L’utilitaire de fichiers vous montrera à quelle architecture appartient la bibliothèque ou l’application.

Qt nécessite également un dossier Plugins dans le dossier Contents de votre répertoire YOUR_APP.app, copiez les plugins de Qt vers Contents. Ensuite, vérifiez la fonctionnalité de l’application, après quoi vous pourrez commencer à optimiser le dossier Plugins, à supprimer des éléments de ce dossier et à tester l’application.

Sécurité macOS

Après avoir copié toutes les dépendances et corrigé les chemins pour les liens dynamiques, vous devrez signer l’application avec la signature du développeur et envoyer en outre la version de l’application à Apple pour légalisation.

Si vous ne disposez pas de 100 $ pour une licence de développeur ou si vous ne souhaitez rien signer, écrivez des instructions à vos utilisateurs sur la façon de lancer l’application.

Cette instruction fonctionne également pour RaidenVideoRipper :

  • Désactiver Gatekeeper : spctl –master-disable
  • Autoriser le lancement à partir de n’importe quelle source dans Confidentialité et sécurité : autoriser les applications à basculer vers n’importe où
  • Supprimez l’indicateur de quarantaine après le téléchargement à partir d’une application zip ou dmg : xattr -d com.apple.quarantine app.dmg
  • Vérifiez que l’indicateur de quarantaine (com.apple.quarantine) est manquant : ls -l@ app.dmg
  • Ajouter une confirmation pour lancer l’application si nécessaire dans Confidentialité et sécurité

Une erreur avec l’indicateur de quarantaine est généralement reproduite par l’erreur « L’application est corrompue » apparaissant sur l’écran de l’utilisateur. Dans ce cas, vous devez supprimer l’indicateur de quarantaine des métadonnées.

Lien pour créer RaidenVideoRipper pour Apple Silicon :
https://github.com/demensdeum/RaidenVideoRipper/releases/download/1.0.1.0/RaidenVideoRipper-1.0.1.0.dmg

Leave a Comment

Your email address will not be published. Required fields are marked *