Supposons que j'ai créé une application osX sans utiliser Xcode. Après avoir compilé avec GCC, j'obtiens un exécutable qui est lié à plusieurs autres bibliothèques. Certaines de ces bibliothèques peuvent à nouveau être liées dynamiquement à d'autres bibliothèques système non standard
Existe-t-il un outil qui crée un bundle d'applications OSX en créant d'abord les structures de répertoire requises, puis en copiant / vérifiant / corrigeant les liens de manière récursive pour s'assurer que toutes les dépendances dynamiques sont également dans le bundle d'applications?
Je suppose que je peux essayer d'écrire quelque chose comme ça, mais je me demandais si quelque chose comme ça existe déjà.
Réponses:
Il existe deux façons de créer un bundle d'applications sur MacOSX, le Easy et le Ugly.
Le moyen le plus simple consiste simplement à utiliser XCode. Terminé.
Le problème est que parfois vous ne pouvez pas.
Dans mon cas, je construis une application qui crée d'autres applications. Je ne peux pas supposer que l'utilisateur a installé XCode. J'utilise également MacPorts pour créer les bibliothèques dont dépend mon application. Je dois m'assurer que ces dylibs sont fournis avec l'application avant de la distribuer.
Clause de non-responsabilité: je ne suis absolument pas qualifié pour écrire cet article, tout ce qu'il contient a été lu dans les documents Apple, en sélectionnant les applications existantes et les essais et erreurs. Cela fonctionne pour moi, mais c'est probablement faux. Veuillez m'envoyer un courriel si vous avez des corrections.
La première chose que vous devez savoir est qu'un bundle d'applications n'est qu'un répertoire.
Examinons la structure d'un hypothétique foo.app.
Info.plist est un simple fichier XML. Vous pouvez le modifier avec un éditeur de texte ou l'application Property List Editor fournie avec XCode. (Il se trouve dans le répertoire / Developer / Applications / Utilities /).
Les éléments clés que vous devez inclure sont:
CFBundleName - Le nom de l'application.
CFBundleIcon - Un fichier Icon supposé être dans le répertoire Contents / Resources. Utilisez l'application Icon Composer pour créer l'icône. (Il se trouve également dans le répertoire / Developer / Applications / Utilities /) Vous pouvez simplement faire glisser et déposer un png sur sa fenêtre et devrait générer automatiquement les niveaux mip pour vous.
CFBundleExecutable - Nom du fichier exécutable supposé se trouver dans le sous-dossier Contents / MacOS /.
Il y a beaucoup plus d'options, celles énumérées ci-dessus ne sont que le strict minimum. Voici une documentation Apple sur le fichier Info.plist et la structure du bundle App .
En outre, voici un exemple d'Info.plist.
Dans un monde parfait, vous pouvez simplement déposer votre exécutable dans le répertoire Contents / MacOS / et c'est terminé. Cependant, si votre application a des dépendances dylib non standard, cela ne fonctionnera pas. Comme Windows, MacOS est livré avec son propre type spécial de DLL Hell .
Si vous utilisez MacPorts pour créer des bibliothèques avec lesquelles vous liez, les emplacements des dylibs seront codés en dur dans votre exécutable. Si vous exécutez l'application sur une machine qui a les dylibs exactement au même endroit, elle fonctionnera correctement. Cependant, la plupart des utilisateurs ne les auront pas installés; quand ils double-cliquent sur votre application, cela plante.
Avant de distribuer votre exécutable, vous devrez collecter toutes les dylibs qu'il charge et les copier dans le bundle d'applications. Vous devrez également éditer l'exécutable pour qu'il recherche les dylibs au bon endroit. c'est à dire où vous les avez copiés.
L'édition manuelle d'un exécutable semble dangereuse, non? Heureusement, il existe des outils de ligne de commande pour vous aider.
Cette commande répertorie tous les dylibs dont dépend votre application. Si vous en voyez qui ne sont PAS dans le dossier System / Library ou usr / lib, ce sont ceux que vous devrez copier dans le bundle d'applications. Copiez-les dans le dossier / Contents / MacOS /. Ensuite, vous devrez éditer l'exécutable pour utiliser les nouveaux dylibs.
Tout d'abord, vous devez vous assurer que vous liez à l'aide de l'indicateur -headerpad_max_install_names. Cela garantit simplement que si le nouveau chemin dylib est plus long que le précédent, il y aura de la place pour cela.
Deuxièmement, utilisez l'outil nom_installation_tool pour modifier chaque chemin dylib.
À titre d'exemple pratique, disons que votre application utilise libSDL et que otool répertorie son emplacement sous la forme "/opt/local/lib/libSDL-1.2.0.dylib".
Copiez-le d'abord dans le bundle d'applications.
Modifiez ensuite l'exécutable pour utiliser le nouvel emplacement (REMARQUE: assurez-vous de l'avoir créé avec l'indicateur -headerpad_max_install_names)
Ouf, nous avons presque fini. Il y a maintenant un petit problème avec le répertoire de travail actuel.
Lorsque vous démarrez votre application, le répertoire actuel sera le répertoire ci-dessus où se trouve l'application. Par exemple: si vous placez le fichier foo.app dans le dossier / Applcations, le répertoire actuel lorsque vous lancerez l'application sera le dossier / Applications. Pas le /Applications/foo.app/Contents/MacOS/ comme vous vous en doutez.
Vous pouvez modifier votre application pour en tenir compte, ou vous pouvez utiliser ce petit script de lancement magique qui changera le répertoire actuel et lancera votre application.
Assurez-vous d'ajuster le fichier Info.plist afin que CFBundleExecutable pointe vers le script de lancement et non vers l'exécutable précédent.
Ok, tout est fait maintenant. Heureusement, une fois que vous connaissez tout cela, vous les enterrez dans un script de construction.
la source
J'ai en fait trouvé un outil très pratique qui mérite un peu de crédit ... NON - je n'ai pas développé cela;)
https://github.com/auriamg/macdylibbundler/
Il résoudra toutes les dépendances et «corrigera» votre exécutable ainsi que vos fichiers dylib pour qu'ils fonctionnent correctement dans votre bundle d'applications.
... il vérifiera également les dépendances de vos bibliothèques dynamiques dépendantes: D
la source
J'utilise ceci dans mon Makefile ... Cela crée un bundle d'applications. Lisez-le et comprenez-le, car vous aurez besoin d'un fichier d'icône png dans un dossier macosx / avec les fichiers PkgInfo et Info.plist que j'inclus ici ...
"cela fonctionne sur mon ordinateur" ... Je l'utilise pour plusieurs applications sur Mavericks ...
APPNAME=MyApp APPBUNDLE=$(APPNAME).app APPBUNDLECONTENTS=$(APPBUNDLE)/Contents APPBUNDLEEXE=$(APPBUNDLECONTENTS)/MacOS APPBUNDLERESOURCES=$(APPBUNDLECONTENTS)/Resources APPBUNDLEICON=$(APPBUNDLECONTENTS)/Resources appbundle: macosx/$(APPNAME).icns rm -rf $(APPBUNDLE) mkdir $(APPBUNDLE) mkdir $(APPBUNDLE)/Contents mkdir $(APPBUNDLE)/Contents/MacOS mkdir $(APPBUNDLE)/Contents/Resources cp macosx/Info.plist $(APPBUNDLECONTENTS)/ cp macosx/PkgInfo $(APPBUNDLECONTENTS)/ cp macosx/$(APPNAME).icns $(APPBUNDLEICON)/ cp $(OUTFILE) $(APPBUNDLEEXE)/$(APPNAME) macosx/$(APPNAME).icns: macosx/$(APPNAME)Icon.png rm -rf macosx/$(APPNAME).iconset mkdir macosx/$(APPNAME).iconset sips -z 16 16 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_16x16.png sips -z 32 32 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_16x16@2x.png sips -z 32 32 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_32x32.png sips -z 64 64 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_32x32@2x.png sips -z 128 128 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_128x128.png sips -z 256 256 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_128x128@2x.png sips -z 256 256 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_256x256.png sips -z 512 512 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_256x256@2x.png sips -z 512 512 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_512x512.png cp macosx/$(APPNAME)Icon.png macosx/$(APPNAME).iconset/icon_512x512@2x.png iconutil -c icns -o macosx/$(APPNAME).icns macosx/$(APPNAME).iconset rm -r macosx/$(APPNAME).iconset
Info.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> <string>English</string> <key>CFBundleExecutable</key> <string>MyApp</string> <key>CFBundleGetInfoString</key> <string>0.48.2, Copyright 2013 my company</string> <key>CFBundleIconFile</key> <string>MyApp.icns</string> <key>CFBundleIdentifier</key> <string>com.mycompany.MyApp</string> <key>CFBundleDocumentTypes</key> <array> </array> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>0.48.2</string> <key>CFBundleSignature</key> <string>MyAp</string> <key>CFBundleVersion</key> <string>0.48.2</string> <key>NSHumanReadableCopyright</key> <string>Copyright 2013 my company.</string> <key>LSMinimumSystemVersion</key> <string>10.3</string> </dict> </plist>
PkgInfo
la source
La solution la plus simple est la suivante: créez une fois un projet Xcode sans rien changer (c'est-à-dire gardez l'application simple à une fenêtre que Xcode crée pour vous), construisez-le et copiez le bundle qu'il a créé pour vous. Ensuite, modifiez les fichiers (notamment Info.plist) en fonction de votre contenu, et placez votre propre binaire dans le répertoire Contents / MacOS /.
la source
Il existe des outils open source pour aider à créer des bundles d'applications avec des bibliothèques dépendantes pour des environnements spécifiques, par exemple, py2app pour les applications basées sur Python. Si vous n'en trouvez pas un plus général, vous pouvez peut-être l'adapter à vos besoins.
la source
J'aurais aimé trouver ce post plus tôt ....
Voici ma façon schématique de résoudre ce problème en utilisant une
Run script
phase qui est appelée à chaque fois que je crée uneRelease
version de mon application:# this is an array of my dependencies' libraries paths # which will be iterated in order to find those dependencies using otool -L libpaths=("$NDNRTC_LIB_PATH" "$BOOST_LIB_PATH" "$NDNCHAT_LIB_PATH" "$NDNCPP_LIB_PATH" "/opt/local/lib") frameworksDir=$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH executable=$BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH #echo "libpaths $libpaths" bRecursion=0 lRecursion=0 # this function iterates through libpaths array # and checks binary with "otool -L" command for containment # of dependency which has "libpath" path # if such dependency has been found, it will be copied to Frameworks # folder and binary will be fixed with "install_name_tool -change" command # to point to Frameworks/<dependency> library # then, dependency is checked recursively with resolveDependencies function function resolveDependencies() { local binfile=$1 local prefix=$2 local binname=$(basename $binfile) local offset=$((lRecursion*20)) printf "%s :\t%s\n" $prefix "resolving $binname..." for path in ${libpaths[@]}; do local temp=$path #echo "check lib path $path" local pattern="$path/([A-z0-9.-]+\.dylib)" while [[ "$(otool -L ${binfile})" =~ $pattern ]]; do local libname=${BASH_REMATCH[1]} otool -L ${binfile} #echo "found match $libname" printf "%s :\t%s\n" $prefix "fixing $libname..." local libpath="${path}/$libname" #echo "cp $libpath $frameworksDir" ${SRCROOT}/sudocp.sh $libpath $frameworksDir/$libname $(whoami) local installLibPath="@rpath/$libname" #echo "install_name_tool -change $libpath $installLibPath $binfile" if [ "$libname" == "$binname" ]; then install_name_tool -id "@rpath/$libname" $binfile printf "%s :\t%s\n" $prefix "fixed id for $libname." else install_name_tool -change $libpath $installLibPath $binfile printf "%s :\t%s\n" $prefix "$libname dependency resolved." let lRecursion++ resolveDependencies "$frameworksDir/$libname" "$prefix>$libname" resolveBoostDependencies "$frameworksDir/$libname" "$prefix>$libname" let lRecursion-- fi path=$temp done # while done # for printf "%s :\t%s\n" $prefix "$(basename $binfile) resolved." } # resolveDependencies # for some reason, unlike other dependencies which maintain full path # in "otool -L" output, boost libraries do not - they just appear # as "libboost_xxxx.dylib" entries, without fully qualified path # thus, resolveDependencies can't be used and a designated function is needed # this function works pretty much in a similar way to resolveDependencies # but targets only dependencies starting with "libboost_", copies them # to the Frameworks folder and resolves them recursively function resolveBoostDependencies() { local binfile=$1 local prefix=$2 local binname=$(basename $binfile) local offset=$(((bRecursion+lRecursion)*20)) printf "%s :\t%s\n" $prefix "resolving Boost for $(basename $binfile)..." local pattern="[[:space:]]libboost_([A-z0-9.-]+\.dylib)" while [[ "$(otool -L ${binfile})" =~ $pattern ]]; do local libname="libboost_${BASH_REMATCH[1]}" #echo "found match $libname" local libpath="${BOOST_LIB_PATH}/$libname" #echo "cp $libpath $frameworksDir" ${SRCROOT}/sudocp.sh $libpath $frameworksDir/$libname $(whoami) installLibPath="@rpath/$libname" #echo "install_name_tool -change $libname $installLibPath $binfile" if [ "$libname" == "$binname" ]; then install_name_tool -id "@rpath/$libname" $binfile printf "%s :\t%s\n" $prefix "fixed id for $libname." else install_name_tool -change $libname $installLibPath $binfile printf "%s :\t%s\n" $prefix "$libname Boost dependency resolved." let bRecursion++ resolveBoostDependencies "$frameworksDir/$libname" "$prefix>$libname" let bRecursion-- fi done # while printf "%s :\t%s\n" $prefix "$(basename $binfile) resolved." } resolveDependencies $executable $(basename $executable) resolveBoostDependencies $executable $(basename $executable)
J'espère que cela pourrait être utile à quelqu'un.
la source