{"id":4056,"date":"2024-12-11T18:49:01","date_gmt":"2024-12-11T15:49:01","guid":{"rendered":"https:\/\/demensdeum.com\/blog\/2024\/12\/11\/dynamic-linking-qt-app-macos\/"},"modified":"2024-12-16T22:32:09","modified_gmt":"2024-12-16T19:32:09","slug":"dynamic-linking-qt-app-macos","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/fr\/2024\/12\/11\/dynamic-linking-qt-app-macos\/","title":{"rendered":"Liaison dynamique des applications Qt sur macOS"},"content":{"rendered":"<p>Aujourd&#8217;hui, j&#8217;ai publi\u00e9 une version de RaidenVideoRipper pour les appareils Apple \u00e9quip\u00e9s de processeurs macOS et M1\/M2\/M3\/M4 (Apple Silicon). RaidenVideoRipper est une application de montage vid\u00e9o rapide qui vous permet de couper une partie d&#8217;un fichier vid\u00e9o dans un nouveau fichier. Vous pouvez \u00e9galement cr\u00e9er des gifs et exporter la piste audio au format mp3.<\/p>\n<p>Ensuite, je d\u00e9crirai bri\u00e8vement les commandes que j&#8217;ai utilis\u00e9es pour accomplir cela. La th\u00e9orie de ce qui se passe ici, la documentation des utilitaires, peut \u00eatre lue sur les liens suivants :<br \/>\n<a href=\"https:\/\/www.unix.com\/man-page\/osx\/1\/otool\/\" rel=\"noopener\" target=\"_blank\">https:\/\/www.unix.com\/man-page\/osx\/1\/otool\/<\/a><br \/>\n<a href=\"https:\/\/www.unix.com\/man-page\/osx\/1\/install_name_tool\/\" rel=\"noopener\" target=\"_blank\">https:\/\/www.unix.com\/man-page\/osx\/1\/install_name_tool\/<\/a><br \/>\n<a href=\"https:\/\/llvm.org\/docs\/CommandGuide\/llvm-nm.html\" rel=\"noopener\" target=\"_blank\">https:\/\/llvm.org\/docs\/CommandGuide\/llvm-nm.html<\/a><br \/>\n<a href=\"https:\/\/linux.die.net\/man\/1\/file\" rel=\"noopener\" target=\"_blank\">https:\/\/linux.die.net\/man\/1\/file<\/a><br \/>\n<a href=\"https:\/\/www.unix.com\/man-page\/osx\/8\/SPCTL\/\" rel=\"noopener\" target=\"_blank\">https:\/\/www.unix.com\/man-page\/osx\/8\/SPCTL\/<\/a><br \/>\n<a href=\"https:\/\/linux.die.net\/man\/1\/chmod\" rel=\"noopener\" target=\"_blank\">https:\/\/linux.die.net\/man\/1\/chmod<\/a><br \/>\n<a href=\"https:\/\/linux.die.net\/man\/1\/ls\" rel=\"noopener\" target=\"_blank\">https:\/\/linux.die.net\/man\/1\/ls<\/a><br \/>\n<a href=\"https:\/\/man7.org\/linux\/man-pages\/man7\/xattr.7.html\" rel=\"noopener\" target=\"_blank\">https:\/\/man7.org\/linux\/man-pages\/man7\/xattr.7.html<\/a><br \/>\n<a href=\"https:\/\/doc.qt.io\/qt-6\/macos-deployment.html\" rel=\"noopener\" target=\"_blank\">https:\/\/doc.qt.io\/qt-6\/macos-deployment.html<\/a><\/p>\n<p>Pour commencer, installez Qt sur votre macOS et installez \u00e9galement l&#8217;environnement pour Qt Desktop Development. Apr\u00e8s cela, assemblez votre projet, par exemple, dans Qt Creator, puis je d\u00e9crirai ce qui est n\u00e9cessaire pour garantir que les d\u00e9pendances avec des biblioth\u00e8ques dynamiques externes sont trait\u00e9es correctement lors de la distribution de l&#8217;application aux utilisateurs finaux.<\/p>\n<p>Cr\u00e9ez un r\u00e9pertoire Frameworks dans le dossier YOUR_APP.app\/Contents de votre application et placez-y des d\u00e9pendances externes. Par exemple, voici \u00e0 quoi ressemble le Frameworks de l&#8217;application RaidenVideoRipper\u00a0:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"bash\"><code>Frameworks\n\u251c\u2500\u2500 DullahanFFmpeg.framework\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dullahan_ffmpeg.a\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 libavcodec.60.dylib\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 libavdevice.60.dylib\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 libavfilter.9.dylib\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 libavformat.60.dylib\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 libavutil.58.dylib\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 libpostproc.57.dylib\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 libswresample.4.dylib\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 libswscale.7.dylib\n\u251c\u2500\u2500 QtCore.framework\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Headers -> Versions\/Current\/Headers\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 QtCore -> Versions\/Current\/QtCore\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Resources -> Versions\/Current\/Resources\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Versions\n\u251c\u2500\u2500 QtGui.framework\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Headers -> Versions\/Current\/Headers\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 QtGui -> Versions\/Current\/QtGui\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Resources -> Versions\/Current\/Resources\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Versions\n\u251c\u2500\u2500 QtMultimedia.framework\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Headers -> Versions\/Current\/Headers\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 QtMultimedia -> Versions\/Current\/QtMultimedia\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Resources -> Versions\/Current\/Resources\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Versions\n\u251c\u2500\u2500 QtMultimediaWidgets.framework\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Headers -> Versions\/Current\/Headers\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 QtMultimediaWidgets -> Versions\/Current\/QtMultimediaWidgets\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Resources -> Versions\/Current\/Resources\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Versions\n\u251c\u2500\u2500 QtNetwork.framework\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Headers -> Versions\/Current\/Headers\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 QtNetwork -> Versions\/Current\/QtNetwork\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Resources -> Versions\/Current\/Resources\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Versions\n\u2514\u2500\u2500 QtWidgets.framework\n    \u251c\u2500\u2500 Headers -> Versions\/Current\/Headers\n    \u251c\u2500\u2500 QtWidgets -> Versions\/Current\/QtWidgets\n    \u251c\u2500\u2500 Resources -> Versions\/Current\/Resources\n    \u2514\u2500\u2500 Versions\n<\/code><\/pre>\n<\/div>\n<p>Pour plus de simplicit\u00e9, j&#8217;ai imprim\u00e9 uniquement le deuxi\u00e8me niveau d&#8217;imbrication.<\/p>\n<p>Ensuite, nous imprimons les d\u00e9pendances dynamiques actuelles de votre application\u00a0:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"bash\"><code>otool -L RaidenVideoRipper \n<\/code><\/pre>\n<\/div>\n<p>Sortie pour le binaire RaidenVideoRipper, qui se trouve dans RaidenVideoRipper.app\/Contents\/MacOS\u00a0:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"bash\"><code>RaidenVideoRipper:\n\t@rpath\/DullahanFFmpeg.framework\/dullahan_ffmpeg.a (compatibility version 0.0.0, current version 0.0.0)\n\t@rpath\/QtMultimediaWidgets.framework\/Versions\/A\/QtMultimediaWidgets (compatibility version 6.0.0, current version 6.8.1)\n\t@rpath\/QtWidgets.framework\/Versions\/A\/QtWidgets (compatibility version 6.0.0, current version 6.8.1)\n\t@rpath\/QtMultimedia.framework\/Versions\/A\/QtMultimedia (compatibility version 6.0.0, current version 6.8.1)\n\t@rpath\/QtGui.framework\/Versions\/A\/QtGui (compatibility version 6.0.0, current version 6.8.1)\n\t\/System\/Library\/Frameworks\/AppKit.framework\/Versions\/C\/AppKit (compatibility version 45.0.0, current version 2575.20.19)\n\t\/System\/Library\/Frameworks\/ImageIO.framework\/Versions\/A\/ImageIO (compatibility version 1.0.0, current version 1.0.0)\n\t\/System\/Library\/Frameworks\/Metal.framework\/Versions\/A\/Metal (compatibility version 1.0.0, current version 367.4.0)\n\t@rpath\/QtNetwork.framework\/Versions\/A\/QtNetwork (compatibility version 6.0.0, current version 6.8.1)\n\t@rpath\/QtCore.framework\/Versions\/A\/QtCore (compatibility version 6.0.0, current version 6.8.1)\n\t\/System\/Library\/Frameworks\/IOKit.framework\/Versions\/A\/IOKit (compatibility version 1.0.0, current version 275.0.0)\n\t\/System\/Library\/Frameworks\/DiskArbitration.framework\/Versions\/A\/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)\n\t\/System\/Library\/Frameworks\/UniformTypeIdentifiers.framework\/Versions\/A\/UniformTypeIdentifiers (compatibility version 1.0.0, current version 709.0.0)\n\t\/System\/Library\/Frameworks\/AGL.framework\/Versions\/A\/AGL (compatibility version 1.0.0, current version 1.0.0)\n\t\/System\/Library\/Frameworks\/OpenGL.framework\/Versions\/A\/OpenGL (compatibility version 1.0.0, current version 1.0.0)\n\t\/usr\/lib\/libc++.1.dylib (compatibility version 1.0.0, current version 1800.101.0)\n\t\/usr\/lib\/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)\n<\/code><\/pre>\n<\/div>\n<p>Comme on peut le voir dans RaidenVideoRipper dans les d\u00e9pendances Qt et dullahan_ffmpeg. Dullahan FFmpeg est un fork de FFmpeg qui encapsule ses fonctionnalit\u00e9s dans une biblioth\u00e8que dynamique, avec la possibilit\u00e9 d&#8217;obtenir la progression et l&#8217;annulation de l&#8217;ex\u00e9cution en cours \u00e0 l&#8217;aide de routines C.<br \/>\nEnsuite, remplacez les chemins de l&#8217;application et toutes les biblioth\u00e8ques n\u00e9cessaires \u00e0 l&#8217;aide de install_name_tool.<\/p>\n<p>La commande pour cela est\u00a0:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"bash\"><code>install_name_tool -change old_path new_path target\n<\/code><\/pre>\n<\/div>\n<p>Exemple d&#8217;utilisation\u00a0:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"bash\"><code>install_name_tool -change \/usr\/local\/lib\/libavfilter.9.dylib @rpath\/DullahanFFmpeg.framework\/libavfilter.9.dylib dullahan_ffmpeg.a\n<\/code><\/pre>\n<\/div>\n<p>Apr\u00e8s avoir saisi tous les chemins corrects, l\u2019application devrait d\u00e9marrer correctement. V\u00e9rifiez que tous les chemins d&#8217;acc\u00e8s aux biblioth\u00e8ques sont relatifs, transf\u00e9rez le binaire et rouvrez-le.<br \/>\nSi vous voyez une erreur, v\u00e9rifiez les chemins via otool et modifiez \u00e0 nouveau via install_name_tool.<\/p>\n<p>Il y a aussi une erreur de confusion des d\u00e9pendances, lorsque la biblioth\u00e8que que vous avez remplac\u00e9e n&#8217;a pas de symbole dans le tableau vous pouvez v\u00e9rifier la pr\u00e9sence ou l&#8217;absence d&#8217;un symbole comme ceci :<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"bash\"><code>nm -gU path\n<\/code><\/pre>\n<\/div>\n<p>Une fois ex\u00e9cut\u00e9, vous verrez toute la table des symboles de la biblioth\u00e8que ou de l&#8217;application.<br \/>\nIl est \u00e9galement possible que vous ayez copi\u00e9 les d\u00e9pendances d&#8217;une mauvaise architecture, vous pouvez le v\u00e9rifier \u00e0 l&#8217;aide du fichier\u00a0:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-bash\" data-lang=\"bash\"><code>file path\n<\/code><\/pre>\n<\/div>\n<p>L&#8217;utilitaire de fichiers vous montrera \u00e0 quelle architecture appartient la biblioth\u00e8que ou l&#8217;application.<\/p>\n<p>Qt n\u00e9cessite \u00e9galement un dossier Plugins dans le dossier Contents de votre r\u00e9pertoire YOUR_APP.app, copiez les plugins de Qt vers Contents. Ensuite, v\u00e9rifiez la fonctionnalit\u00e9 de l&#8217;application, apr\u00e8s quoi vous pourrez commencer \u00e0 optimiser le dossier Plugins, \u00e0 supprimer des \u00e9l\u00e9ments de ce dossier et \u00e0 tester l&#8217;application.<\/p>\n<h2>S\u00e9curit\u00e9 macOS<\/h2>\n<p>Apr\u00e8s avoir copi\u00e9 toutes les d\u00e9pendances et corrig\u00e9 les chemins pour les liens dynamiques, vous devrez signer l&#8217;application avec la signature du d\u00e9veloppeur et envoyer en outre la version de l&#8217;application \u00e0 Apple pour l\u00e9galisation.<\/p>\n<p>Si vous ne disposez pas de 100 $ pour une licence de d\u00e9veloppeur ou si vous ne souhaitez rien signer, \u00e9crivez des instructions \u00e0 vos utilisateurs sur la fa\u00e7on de lancer l&#8217;application.<\/p>\n<p>Cette instruction fonctionne \u00e9galement pour RaidenVideoRipper\u00a0:<\/p>\n<ul>\n<li>D\u00e9sactiver Gatekeeper\u00a0: spctl &#8211;master-disable<\/li>\n<li>Autoriser le lancement \u00e0 partir de n&#8217;importe quelle source dans Confidentialit\u00e9 et s\u00e9curit\u00e9\u00a0: autoriser les applications \u00e0 basculer vers n&#8217;importe o\u00f9<\/li>\n<li>Supprimez l&#8217;indicateur de quarantaine apr\u00e8s le t\u00e9l\u00e9chargement \u00e0 partir d&#8217;une application zip ou dmg\u00a0: xattr -d com.apple.quarantine app.dmg<\/li>\n<li>V\u00e9rifiez que l&#8217;indicateur de quarantaine (com.apple.quarantine) est manquant\u00a0: ls -l@ app.dmg\n<li>Ajouter une confirmation pour lancer l&#8217;application si n\u00e9cessaire dans Confidentialit\u00e9 et s\u00e9curit\u00e9<\/li>\n<\/ul>\n<p>Une erreur avec l\u2019indicateur de quarantaine est g\u00e9n\u00e9ralement reproduite par l\u2019erreur \u00ab L\u2019application est corrompue \u00bb apparaissant sur l\u2019\u00e9cran de l\u2019utilisateur. Dans ce cas, vous devez supprimer l&#8217;indicateur de quarantaine des m\u00e9tadonn\u00e9es.<\/p>\n<p>Lien pour cr\u00e9er RaidenVideoRipper pour Apple Silicon\u00a0:<br \/>\n<a href=\"https:\/\/github.com\/demensdeum\/RaidenVideoRipper\/releases\/download\/1.0.1.0\/RaidenVideoRipper-1.0.1.0.dmg\" rel=\"noopener\" target=\"_blank\">https:\/\/github.com\/demensdeum\/RaidenVideoRipper\/releases\/download\/1.0.1.0\/RaidenVideoRipper-1.0.1.0.dmg<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Aujourd&#8217;hui, j&#8217;ai publi\u00e9 une version de RaidenVideoRipper pour les appareils Apple \u00e9quip\u00e9s de processeurs macOS et M1\/M2\/M3\/M4 (Apple Silicon). RaidenVideoRipper est une application de montage vid\u00e9o rapide qui vous permet de couper une partie d&#8217;un fichier vid\u00e9o dans un nouveau fichier. Vous pouvez \u00e9galement cr\u00e9er des gifs et exporter la piste audio au format mp3.<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/fr\/2024\/12\/11\/dynamic-linking-qt-app-macos\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Liaison dynamique des applications Qt sur macOS&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[49],"tags":[],"class_list":["post-4056","post","type-post","status-publish","format-standard","hentry","category-blog","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"fr","enabled_languages":["en","ru","zh","de","fr","ja","pt"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"ru":{"title":true,"content":true,"excerpt":false},"zh":{"title":true,"content":true,"excerpt":false},"de":{"title":true,"content":true,"excerpt":false},"fr":{"title":true,"content":true,"excerpt":false},"ja":{"title":true,"content":true,"excerpt":false},"pt":{"title":true,"content":true,"excerpt":false}}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts\/4056","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/comments?post=4056"}],"version-history":[{"count":7,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts\/4056\/revisions"}],"predecessor-version":[{"id":4063,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts\/4056\/revisions\/4063"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/media?parent=4056"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/categories?post=4056"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/tags?post=4056"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}