{"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\/de\/2024\/12\/11\/dynamic-linking-qt-app-macos\/","title":{"rendered":"Dynamische Verkn\u00fcpfung von Qt-Anwendungen unter macOS"},"content":{"rendered":"<p>Heute habe ich eine Version von RaidenVideoRipper f\u00fcr Apple-Ger\u00e4te mit macOS und M1\/M2\/M3\/M4-Prozessoren (Apple Silicon) ver\u00f6ffentlicht. RaidenVideoRipper ist eine schnelle Videobearbeitungsanwendung, mit der Sie einen Teil einer Videodatei in eine neue Datei schneiden k\u00f6nnen. Sie k\u00f6nnen auch GIFs erstellen und die Audiospur als MP3 exportieren.<\/p>\n<p>Als n\u00e4chstes werde ich kurz beschreiben, welche Befehle ich verwendet habe, um dies zu erreichen. Die Theorie, was hier passiert, die Dokumentation der Versorgungsunternehmen, kann unter den folgenden Links nachgelesen werden:<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>Installieren Sie zun\u00e4chst Qt auf Ihrem macOS und installieren Sie auch die Umgebung f\u00fcr Qt Desktop Development. Anschlie\u00dfend stellen Sie Ihr Projekt beispielsweise in Qt Creator zusammen. Anschlie\u00dfend beschreibe ich, was erforderlich ist, um sicherzustellen, dass Abh\u00e4ngigkeiten mit externen dynamischen Bibliotheken bei der Verteilung der Anwendung an Endbenutzer korrekt verarbeitet werden.<\/p>\n<p>Erstellen Sie ein Frameworks-Verzeichnis im Ordner YOUR_APP.app\/Contents Ihrer Anwendung und platzieren Sie darin externe Abh\u00e4ngigkeiten. So sehen beispielsweise die Frameworks f\u00fcr die RaidenVideoRipper-Anwendung aus:<\/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>Der Einfachheit halber habe ich nur die zweite Verschachtelungsebene gedruckt.<\/p>\n<p>Als n\u00e4chstes drucken wir die aktuellen dynamischen Abh\u00e4ngigkeiten Ihrer Anwendung aus:<\/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>Ausgabe f\u00fcr die RaidenVideoRipper-Bin\u00e4rdatei, die sich in RaidenVideoRipper.app\/Contents\/MacOS befindet:<\/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>Wie im RaidenVideoRipper in den Qt- und dullahan_ffmpeg-Abh\u00e4ngigkeiten zu sehen ist. Dullahan FFmpeg ist ein Zweig von FFmpeg, der seine Funktionalit\u00e4t in einer dynamischen Bibliothek kapselt und die M\u00f6glichkeit bietet, den aktuellen Ausf\u00fchrungsfortschritt und den Abbruch mithilfe von C-Routinen abzurufen.<br \/>\nErsetzen Sie als N\u00e4chstes die Pfade der Anwendung und aller erforderlichen Bibliotheken durch install_name_tool.<\/p>\n<p>Der Befehl hierf\u00fcr lautet:<\/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>Anwendungsbeispiel:<\/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>Nachdem Sie alle Pfade korrekt eingegeben haben, sollte die Anwendung korrekt starten. \u00dcberpr\u00fcfen Sie, ob alle Pfade zu Bibliotheken relativ sind, \u00fcbertragen Sie die Bin\u00e4rdatei und \u00f6ffnen Sie sie erneut.<br \/>\nWenn Sie einen Fehler sehen, \u00fcberpr\u00fcfen Sie die Pfade \u00fcber otool und \u00e4ndern Sie sie erneut \u00fcber install_name_tool.<\/p>\n<p>Es gibt auch einen Fehler bei der Verwechslung von Abh\u00e4ngigkeiten. Wenn die von Ihnen ersetzte Bibliothek kein Symbol in der Tabelle hat, k\u00f6nnen Sie das Vorhandensein oder Fehlen eines Symbols wie folgt \u00fcberpr\u00fcfen:<\/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>Nach der Ausf\u00fchrung sehen Sie die gesamte Symboltabelle der Bibliothek oder Anwendung.<br \/>\nEs ist auch m\u00f6glich, dass Sie die Abh\u00e4ngigkeiten der falschen Architektur kopiert haben. Sie k\u00f6nnen dies mit der Datei \u00fcberpr\u00fcfen:<\/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>Das Dateidienstprogramm zeigt Ihnen, zu welcher Architektur die Bibliothek oder Anwendung geh\u00f6rt.<\/p>\n<p>Qt ben\u00f6tigt au\u00dferdem einen Plugins-Ordner im Contents-Ordner Ihres YOUR_APP.app-Verzeichnisses. Kopieren Sie die Plugins von Qt nach Contents. \u00dcberpr\u00fcfen Sie als N\u00e4chstes die Funktionalit\u00e4t der Anwendung. Anschlie\u00dfend k\u00f6nnen Sie mit der Optimierung des Plugins-Ordners beginnen, Elemente aus diesem Ordner l\u00f6schen und die Anwendung testen.<\/p>\n<h2>macOS-Sicherheit<\/h2>\n<p>Nachdem Sie alle Abh\u00e4ngigkeiten kopiert und die Pfade f\u00fcr die dynamische Verkn\u00fcpfung korrigiert haben, m\u00fcssen Sie die Anwendung mit der Signatur des Entwicklers signieren und zus\u00e4tzlich die Anwendungsversion zur Beglaubigung an Apple senden.<\/p>\n<p>Wenn Sie keine 100 US-Dollar f\u00fcr eine Entwicklerlizenz haben oder nichts unterschreiben m\u00f6chten, schreiben Sie Ihren Benutzern Anweisungen zum Starten der Anwendung.<\/p>\n<p>Diese Anleitung funktioniert auch f\u00fcr RaidenVideoRipper:<\/p>\n<ul>\n<li>Gatekeeper deaktivieren: spctl &#8211;master-disable<\/li>\n<li>Start von beliebigen Quellen in \u201eDatenschutz und Sicherheit\u201c zulassen: Wechseln von Anwendungen zu \u201e\u00dcberall\u201c zulassen<\/li>\n<li>Entfernen Sie die Quarant\u00e4ne-Flagge nach dem Herunterladen aus einer ZIP- oder DMG-Anwendung: xattr -d com.apple.quarantine app.dmg<\/li>\n<li>\u00dcberpr\u00fcfen Sie, ob das Quarant\u00e4ne-Flag (com.apple.quarantine) fehlt: ls -l@ app.dmg\n<li>F\u00fcgen Sie bei Bedarf eine Best\u00e4tigung zum Starten der Anwendung in Datenschutz und Sicherheit hinzu<\/li>\n<\/ul>\n<p>Ein Fehler mit der Quarant\u00e4ne-Flagge wird normalerweise dadurch reproduziert, dass auf dem Bildschirm des Benutzers die Fehlermeldung \u201eAnwendung ist besch\u00e4digt\u201c erscheint. In diesem Fall m\u00fcssen Sie das Quarant\u00e4ne-Flag aus den Metadaten entfernen.<\/p>\n<p>Link zum Erstellen von RaidenVideoRipper f\u00fcr Apple Silicon:<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>Heute habe ich eine Version von RaidenVideoRipper f\u00fcr Apple-Ger\u00e4te mit macOS und M1\/M2\/M3\/M4-Prozessoren (Apple Silicon) ver\u00f6ffentlicht. RaidenVideoRipper ist eine schnelle Videobearbeitungsanwendung, mit der Sie einen Teil einer Videodatei in eine neue Datei schneiden k\u00f6nnen. Sie k\u00f6nnen auch GIFs erstellen und die Audiospur als MP3 exportieren. Als n\u00e4chstes werde ich kurz beschreiben, welche Befehle ich verwendet<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/de\/2024\/12\/11\/dynamic-linking-qt-app-macos\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Dynamische Verkn\u00fcpfung von Qt-Anwendungen unter 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":"de","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\/de\/wp-json\/wp\/v2\/posts\/4056","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/comments?post=4056"}],"version-history":[{"count":7,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts\/4056\/revisions"}],"predecessor-version":[{"id":4063,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/posts\/4056\/revisions\/4063"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/media?parent=4056"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/categories?post=4056"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/de\/wp-json\/wp\/v2\/tags?post=4056"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}