Création d’un ensemble d’applications OSX

Supposons que j’ai créé une application osX sans utiliser Xcode. Après avoir compilé avec GCC, je reçois un exécutable lié à plusieurs autres bibliothèques. Certaines de ces bibliothèques pourraient à nouveau être liées dynamicment à d’autres bibliothèques de systèmes non standard

Existe-t-il un outil permettant de créer un ensemble d’applications OSX en créant d’abord les structures de répertoires requirejses, puis en copiant / vérifiant / corrigeant les liens de manière récursive pour s’assurer que toutes les dépendances dynamics figurent également dans le bundle d’application?

Je suppose que je peux essayer d’écrire quelque chose comme ça, mais je me demandais si quelque chose comme ça existe déjà.

Il existe deux manières de créer un bundle d’application sur MacOSX, Easy et the Ugly.

Le moyen le plus simple consiste à utiliser XCode. Terminé.

Le problème est que parfois tu ne peux 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 aussi MacPorts pour créer les bibliothèques dont dépend mon application. Je dois m’assurer que ces dylibs sont livrés avec l’application avant de la dissortingbuer.

Clause de non-responsabilité: Je ne suis absolument pas qualifié pour écrire ce message, tout ce qui est dans Apple a été scanné, en sélectionnant les applications existantes et les essais et erreurs. Cela fonctionne pour moi, mais est probablement faux. S’il vous plaît écrivez-moi si vous avez des corrections.

La première chose à savoir, c’est qu’un bundle d’application n’est qu’un répertoire.
Examinons la structure d’un foo.app hypothétique.

 foo.app/
     Contenu/
         Info.plist
         MacOS /
             foo
         Ressources/
             foo.icns

Info.plist est un fichier XML simple. 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 à inclure sont les suivants:

CFBundleName – Le nom de l’application.

CFBundleIcon – Un fichier d’icône supposé se trouver 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 glisser-déposer un png sur sa fenêtre et 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 ssortingct minimum. Voici quelques documents Apple sur le fichier Info.plist et la structure du bundle App .

En outre, voici un exemple Info.plist.

 
 
 
 
    CFBundleGetInfoSsortingng 
    Foo 
    CFBundleExecutable 
    foo 
    CFBundleIdentifier 
    com.votre-nom-société.www 
    CFBundleName 
    foo 
    CFBundleIconFile 
    foo.icns 
    CFBundleShortVersionSsortingng 
    0,01 
    CFBundleInfoDictionaryVersion 
    6.0 
    CFBundlePackageType 
    APPL 
    IFMajorVersion 
    0 
    IFMinorVersion 
    1 
 
 

Dans un monde parfait, vous pouvez simplement déposer votre fichier exécutable dans le répertoire Contents / MacOS / et être terminé. Cependant, si votre application a des dépendances Dylib non standard, cela ne fonctionnera pas. Comme Windows, MacOS possède son propre type de DLL Hell .

Si vous utilisez MacPorts pour créer des bibliothèques avec lesquelles vous établissez des liens, 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; lorsqu’ils cliquent deux fois sur votre application, elle ne fonctionnera plus.

Avant de dissortingbuer votre fichier exécutable, vous devez collecter tous les fichiers téléchargés et les copier dans le groupe d’applications. Vous devrez également modifier l’exécutable pour qu’il recherche les dylibs au bon endroit. c’est-à-dire où vous les avez copiés.

Editer à la main un exécutable sonne bien, n’est-ce pas? Heureusement, il existe des outils en ligne de commande pour vous aider.

 otool -L nom_exécutable

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, vous devrez les copier dans le bundle de l’application. 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 créez un lien à l’aide de l’indicateur -headerpad_max_install_names. Cela permet de s’assurer que si le nouveau chemin Dylib est plus long que le précédent, il y aura de la place pour cela.

Ensuite, utilisez l’outil install_name_tool pour changer chaque chemin d’access.

 install_name_tool -change chemin_existant_dylib @ chemin_exécutable / blah.dylib nom_exécutable

En guise d’exemple pratique, supposons que votre application utilise libSDL et que otool indique son emplacement comme “/opt/local/lib/libSDL-1.2.0.dylib”.

Tout d’abord, copiez-le dans le lot d’applications.

 cp /opt/local/lib/libSDL-1.2.0.dylib foo.app/Contents/MacOS/

Ensuite, éditez l’exécutable pour utiliser le nouvel emplacement (NOTE: assurez-vous de l’avoir créé avec l’indicateur -headerpad_max_install_names)

 install_name_tool -change /opt/local/lib/libSDL-1.2.0.dylib @ chemin_exécutable / libSDL-1.2.0.dylib foo.app/Contents/MacOS/foo

Ouf, on a presque fini. Maintenant, il y a un petit problème avec le répertoire de travail actuel.

Lorsque vous démarrez votre application, le répertoire en cours sera le répertoire ci-dessus où se trouve l’application. Par exemple: Si vous placez foo.app dans le dossier / Applcations, le répertoire en cours au lancement de 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 en cours et lancera votre application.

 #! / bin / bash
 cd "$ {0% / *}"
 ./foo

Assurez-vous d’ajuster le fichier Info.plist pour 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 toutes ces choses, vous les enterrez dans un script de compilation.

J’ai en fait trouvé un outil très pratique qui mérite un certain 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 fichier 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 dynamics dépendantes: D

Il n’y a vraiment rien de magique dans le bundle – lisez simplement la documentation d’Apple à ce sujet et imitez-la. À la base, vous avez besoin d’un Info.plist, du Contents / MacOS / binary et d’une icône.

La solution la plus simple est la suivante: créez une fois un projet Xcode sans rien changer (c.-à-d. Conservez l’application simple fenêtre créée par Xcode pour vous), comstackz-la et copiez le lot créé pour vous. Ensuite, modifiez les fichiers (notamment Info.plist) en fonction de votre contenu et placez votre propre fichier binary dans le répertoire Contents / MacOS /.

Je l’utilise dans mon Makefile … Il crée un bundle d’application. Lisez-le et comprenez-le, car vous aurez besoin d’un fichier icône png dans un dossier macosx / avec les fichiers PkgInfo et Info.plist que j’inclus ici …

“ça marche 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/[email protected] 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/[email protected] 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/[email protected] 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/[email protected] sips -z 512 512 macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_512x512.png cp macosx/$(APPNAME)Icon.png macosx/$(APPNAME).iconset/[email protected] iconutil -c icns -o macosx/$(APPNAME).icns macosx/$(APPNAME).iconset rm -r macosx/$(APPNAME).iconset 

Info.plist

     CFBundleDevelopmentRegion English CFBundleExecutable MyApp CFBundleGetInfoSsortingng 0.48.2, Copyright 2013 my company CFBundleIconFile MyApp.icns CFBundleIdentifier com.mycompany.MyApp CFBundleDocumentTypes   CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionSsortingng 0.48.2 CFBundleSignature MyAp CFBundleVersion 0.48.2 NSHumanReadableCopyright Copyright 2013 my company. LSMinimumSystemVersion 10.3   

PkgInfo

 APPLMyAp 

Il existe des outils open source pour vous aider à créer des ensembles 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 ne trouvez pas une version plus générale, vous pouvez peut-être l’adapter à vos besoins.

J’aimerais avoir trouvé ce post plus tôt ….

Voici une façon sommaire de résoudre ce problème en utilisant une phase de Run script exécution qui est appelée chaque fois que je construis une version de 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/ 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" ensortinges, 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.