Edificio OSX App Bundle

Supponiamo che ho fatto un osX app senza l’utilizzo di Xcode. Dopo la compilazione con GCC ottengo un file eseguibile che è collegato a diverse altre biblioteche. Alcune di queste librerie, può ancora essere collegati in modo dinamico ad altri non-standard librerie di sistema

C’è qualche strumento che esiste, che fa di un’OSX App bundle rendendo le necessarie strutture di directory e quindi in modo ricorsivo copia/controllare/correggere i link per assicurarsi che tutte le dinamiche dipendenze sono anche in app bundle?

Credo che posso provare a scrivere qualcosa di simile a questo, ma mi chiedevo se qualcosa di simile esiste già.

  • Non posso usare Xcode a causa di diversi motivi. di cui uno è perché io uso personalizzato di gcc. Xcode non mi permette di specificare un diverso gcc. Sto usando cmake per costruire il mio makefile.
  • >> Alcune di queste librerie, può ancora essere collegati in modo dinamico ad altri non-standard librerie di sistema << La risposta selezionata non aiutare con questo caso. Come hai fatto a risolvere?
InformationsquelleAutor Yogi | 2009-10-20

 

6 Replies
  1. 122

    Ci sono due modi per creare un’app bundle su MacOSX, il Facile e il Brutto.

    Il modo più semplice è utilizzare XCode.
    Fatto.

    Il problema è che a volte non è possibile.

    Nel mio caso sto costruendo un app che costruisce altre applicazioni.
    Io non posso assumere che l’utente ha XCode installato.
    Sto usando anche MacPorts per creare le librerie della mia app dipende.
    Ho bisogno di assicurarsi che questi dylibs ottenere in bundle con l’applicazione prima di distribuirlo.

    Disclaimer: sono totalmente incompetenti a scrivere questo post, tutto in
    è spuntata da parte di Apple di documenti, raccolta di oltre alle applicazioni esistenti e
    prova ed errore. A me funziona, ma è probabilmente sbagliato. Si prega di
    email me se avete eventuali correzioni.

    Prima cosa da sapere è che un app bundle è solo una directory.

    Esaminiamo la struttura di un ipotetico pippo.app.

    pippo.app/
    Contenuto/
    Info.plist 
    MacOS/
    pippo 
    Risorse/
    pippo.icns 
    

    Info.plist è un semplice file XML.
    Si può modificare con un editor di testo o Property List Editor app che viene fornito in bundle con XCode.
    (In /Developer/Applications/Utilities/directory).

    La chiave di cose che avete bisogno sono:

    key > cfbundlename – Il nome dell’app.

    CFBundleIcon – Un file di Icona considerato in Contents/Resources dir.
    Utilizzare l’Icona Compositore app per creare l’icona. (È anche in /Developer/Applications/Utilities/directory)
    Si può semplicemente trascinare e rilasciare un file png sul finestra e dovrebbe generare automaticamente il mip-livelli per voi.

    CFBundleExecutable – Nome del file eseguibile considerato in Contents/MacOS/sotto-cartella.

    Ci sono molte più opzioni, quelli sopra elencati sono solo il minimo indispensabile.
    Ecco alcuni Apple documentazione
    Info.plist
    file e
    App bundle struttura.

    Inoltre, Ecco un esempio di una 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> 
    <i>CFBundleGetInfoString</a chiave> 
    <string>Pippo</string> 
    <i>CFBundleExecutable</a chiave> 
    <string>pippo</string> 
    <i>CFBundleIdentifier</a chiave> 
    <string>com.la tua-azienda-nome.www</string> 
    <i>key > cfbundlename</a chiave> 
    <string>pippo</string> 
    <i>CFBundleIconFile</a chiave> 
    <string>pippo.icns</string> 
    <i>CFBundleShortVersionString</a chiave> 
    <string>0.01</string> 
    <i>CFBundleInfoDictionaryVersion</a chiave> 
    <string>6.0</string> 
    <i>CFBundlePackageType</a chiave> 
    <string>APPL</string> 
    <i>IFMajorVersion</a chiave> 
    <intero>0</integer> 
    <i>IFMinorVersion</a chiave> 
    <intero>1</integer> 
    </dict> 
    </plist> 
    

    In un mondo perfetto si può solo rilasciare il tuo file eseguibile in
    Contents/MacOS/dir e il gioco è fatto. Tuttavia, se la vostra applicazione ha alcun
    non-standard dylib dipendenze non funziona. Come Windows, MacOS
    viene con il suo proprio tipo speciale di DLL Hell.

    Se si utilizza MacPorts per costruire librerie di collegamento di contro, le posizioni di la dylibs sarà codificato nell’eseguibile.
    Se si esegue l’applicazione su una macchina che ha il dylibs nella stessa posizione esatta, funziona bene.
    Tuttavia, la maggior parte degli utenti non li hanno installati; quando si fare doppio clic l’applicazione si è appena andrà in crash.

    Prima della distribuzione è eseguibile è necessario raccogliere tutte le dylibs carica e copiarli nella cartella app bundle.
    Avrete anche bisogno di modificare il file eseguibile in modo che si avrà un aspetto per il dylibs nel posto giusto. cioè dove è copiata.

    Mano di modifica di un file eseguibile suoni pericoloso giusto?
    Per fortuna ci sono strumenti a riga di comando per aiutare.

    otool -L executable_name 
    

    Questo comando elenca tutti i dylibs che l’app dipende.
    Se vedi uno che NON si trovano in System/Library o usr/lib cartella, sono proprio quelle che dovrai copiare nella app bundle.
    Copiarli nella cartella /Contents/MacOS/cartella.
    Dopo dovrete modificare il file eseguibile per utilizzare il nuovo dylibs.

    Innanzitutto, è necessario assicurarsi che si collega con-headerpad_max_install_names bandiera.
    Questo fa sì che se il nuovo dylib percorso è più lungo del precedente, ci sarà spazio per farlo.

    In secondo luogo, utilizzare il install_name_tool di cambiare ogni dylib percorso.

    install_name_tool -cambiare existing_path_to_dylib @executable_path/bla.dylib executable_name 
    

    Per fare un esempio pratico, supponiamo che la tua app utilizza libSDL, e otool liste di posizione, come in “/opt/local/lib/libSDL-1.2.0.dylib”.

    Prima copia nella app bundle.

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

    Quindi modificare il file eseguibile per utilizzare la nuova posizione (NOTA: assicurarsi che si costruisce con l’-headerpad_max_install_names bandiera)

    install_name_tool -modifica /opt/local/lib/libSDL-1.2.0.dylib @executable_path/libSDL-1.2.0.dylib pippo.app/Contents/MacOS/pippo 
    

    Accidenti, abbiamo quasi finito.
    Ora c’è un piccolo problema con la directory di lavoro corrente.

    Quando si avvia l’app la directory corrente sarà la directory in cui si trova l’applicazione.
    Per esempio: Se il pippo.app in /Applications cartella la directory corrente, quando si avvia l’app sarà la cartella /Applicazioni.
    Non l’ /Applicazioni/foo.app/Contents/MacOS/come ci si potrebbe aspettare.

    Si può cambiare la vostra app per tenere conto di questo, oppure è possibile utilizzare questo po ‘ di magia di avvio script che cambia la directory corrente e lanciare la vostra applicazione.

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

    Assicurarsi di modificare le Informazioni.file plist in modo che CFBundleExecutable punti per il lancio di script e non il precedente di un file eseguibile.

    Ok, tutto fatto ora.
    Per fortuna, una volta che si conosce tutta questa roba si seppellisce in uno script di compilazione.

    • +1. Tuttavia, il cane zombi mostri a me. Si potrebbe usare meno cicatrici immagine per illustrare il DLL hell? (Per non parlare che il termine “DLL hell” non si applica. Il metodo preferito per la distribuzione di librerie dinamiche tramite quadri di evitare la maggior parte dei problemi. Ancora, l’ .dylib approccio è utile per stile UNIX librerie.)
    • Come possiamo risolvere doppio dipendenze? Come un dylib fa riferimento a un altro dylib ?
    • Non c’è modo di compilare il executible con le posizioni corrette così non devi modificare?
  2. 17

    In realtà l’ho trovato uno strumento molto utile che merita un po ‘ di credito … NO, non posso sviluppare questo 😉

    https://github.com/auriamg/macdylibbundler/

    Sarà risolvere tutte le dipendenze e “fissare” le eseguibile e il file dylib di lavorare senza problemi nel vostro app bundle.

    … verifica anche per le dipendenze della dinamica dipendenti libs 😀

  3. 7

    La soluzione più semplice è: creare una volta che un progetto Xcode senza cambiare nulla (cioè mantenere la semplice finestra di app Xcode crea per voi), la costruzione, e copia del pacchetto è stato creato per voi. Quindi, modificare il file (in particolare le Info.plist) per soddisfare i tuoi contenuti, e mettere il proprio binario in Contents/MacOS/directory.

    • La domanda chiede esplicitamente come farlo senza xcode. Io sono nella stessa barca, e vorrei una risposta reale.
    • Bene, con questo, devi solo usare XCode una volta nella vita 🙂 o chiedere a qualcuno di farlo per voi.
  4. 7

    Io la uso nel mio Makefile… crea un’app bundle. Leggere e capire, perché avrete bisogno di una icona png file in un macosx/cartella con i PkgInfo e Info.plist file io sono qui…

    “funziona sul mio computer”… io uso questo per diverse applicazioni su 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].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/[email protected].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/[email protected].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/[email protected].png
        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].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

    APPLMyAp
  5. 2

    Ci sono alcuni strumenti open source per aiutare a costruire app bundle con librerie indipendenti per ambienti specifici, per esempio, py2app per Python-based di applicazioni. Se non trovi più generale, forse si può adattare alle tue esigenze.

  6. 2

    Desidero ho trovato questo post precedenti….

    Ecco il mio abbozzato modo per risolvere questo problema utilizzando un Run script fase che viene invocato ogni volta che costruisco una Release versione della mia app:

    # 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)

    Spero che questo possa essere utile a qualcuno.

Lascia un commento