Misalkan saya telah membuat aplikasi osX tanpa menggunakan Xcode. Setelah dikompilasi dengan GCC, saya mendapatkan file yang dapat dieksekusi yang ditautkan ke beberapa perpustakaan lain. Beberapa pustaka tersebut mungkin lagi ditautkan secara dinamis ke pustaka sistem non-standar lainnya
Apakah ada alat yang ada yang membuat OSX App bundle dengan terlebih dahulu membuat struktur direktori yang diperlukan dan kemudian secara rekursif menyalin / memeriksa / memperbaiki link untuk memastikan semua dependensi dinamis juga ada dalam app bundle?
Saya kira saya dapat mencoba menulis sesuatu seperti ini tetapi saya bertanya-tanya apakah sesuatu seperti ini sudah ada.
Jawaban:
Ada dua cara untuk membuat bundel aplikasi di MacOSX, Mudah dan Jelek.
Cara termudah adalah dengan menggunakan Xcode. Selesai.
Masalahnya terkadang Anda tidak bisa.
Dalam kasus saya, saya sedang membangun aplikasi yang membangun aplikasi lain. Saya tidak dapat berasumsi bahwa pengguna telah menginstal XCode. Saya juga menggunakan MacPorts untuk membangun perpustakaan tempat aplikasi saya bergantung. Saya perlu memastikan bahwa dylib ini digabungkan dengan aplikasi sebelum saya mendistribusikannya.
Penafian: Saya benar-benar tidak memenuhi syarat untuk menulis posting ini, semua yang ada di dalamnya telah dikilaukan dari dokumen Apple, memilah-milah aplikasi yang ada dan coba-coba. Ini berhasil untuk saya, tetapi kemungkinan besar salah. Silakan email saya jika Anda memiliki koreksi.
Hal pertama yang harus Anda ketahui adalah bahwa app bundle hanyalah sebuah direktori.
Mari kita periksa struktur hipotetis foo.app.
Info.plist adalah file XML biasa. Anda dapat mengeditnya dengan editor teks atau aplikasi Editor Daftar Properti yang dibundel dengan XCode. (Ada di / Developer / Applications / Utilities / direktori).
Hal-hal utama yang perlu Anda sertakan adalah:
CFBundleName - Nama aplikasi.
CFBundleIcon - File Ikon yang diasumsikan ada di direktori Contents / Resources. Gunakan aplikasi Icon Composer untuk membuat ikon. (Ini juga ada di / Developer / Applications / Utilities / direktori) Anda cukup menarik dan melepas png ke jendelanya dan secara otomatis akan menghasilkan mip-level untuk Anda.
CFBundleExecutable - Nama file yang dapat dieksekusi yang diasumsikan berada di sub-folder Contents / MacOS /.
Ada lebih banyak pilihan, yang tercantum di atas hanyalah minimum. Berikut beberapa dokumentasi Apple tentang file Info.plist dan struktur App bundle .
Juga, Ini contoh Info.plist.
Di dunia yang sempurna Anda bisa memasukkan file yang dapat dieksekusi ke dalam Contents / MacOS / dir dan selesai. Namun, jika aplikasi Anda memiliki dependensi dylib non-standar, itu tidak akan berfungsi. Seperti Windows, MacOS hadir dengan DLL Hell jenis khusus itu sendiri .
Jika Anda menggunakan MacPorts untuk membangun pustaka yang Anda tautkan, lokasi dylib akan di-hardcode ke dalam file yang dapat dieksekusi. Jika Anda menjalankan aplikasi pada mesin yang dylibnya berada di lokasi yang sama persis, aplikasi akan berjalan dengan baik. Namun, sebagian besar pengguna tidak akan menginstalnya; ketika mereka mengklik dua kali aplikasi Anda, itu hanya akan mogok.
Sebelum mendistribusikan file executable, Anda harus mengumpulkan semua dylib yang dimuatnya dan menyalinnya ke app bundle. Anda juga perlu mengedit file yang dapat dieksekusi agar itu akan mencari dylib di tempat yang benar. yaitu tempat Anda menyalinnya.
Mengedit tangan yang dapat dieksekusi terdengar berbahaya bukan? Untungnya ada alat baris perintah untuk membantu.
Perintah ini akan mencantumkan semua dylib yang bergantung pada aplikasi Anda. Jika Anda melihat ada yang TIDAK ada di folder System / Library atau usr / lib, itu adalah yang harus Anda salin ke app bundle. Salin ke folder / Contents / MacOS /. Selanjutnya, Anda harus mengedit file yang dapat dieksekusi untuk menggunakan dylib baru.
Pertama, Anda perlu memastikan bahwa Anda menautkan menggunakan tanda -headerpad_max_install_names. Ini hanya memastikan bahwa jika jalur dylib baru lebih panjang dari yang sebelumnya, akan ada ruang untuk itu.
Kedua, gunakan install_name_tool untuk mengubah setiap jalur dylib.
Sebagai contoh praktis, Misalkan aplikasi Anda menggunakan libSDL , dan otool mencantumkan lokasinya sebagai "/opt/local/lib/libSDL-1.2.0.dylib".
Salin dulu ke app bundle.
Kemudian edit file yang dapat dieksekusi untuk menggunakan lokasi baru (CATATAN: pastikan Anda membuatnya dengan flag -headerpad_max_install_names)
Wah, kita hampir selesai. Sekarang ada masalah kecil dengan direktori kerja saat ini.
Saat Anda memulai aplikasi, direktori saat ini adalah direktori di atas tempat aplikasi berada. Misalnya: Jika Anda menempatkan foo.app di folder / Applcations, maka direktori saat ini saat Anda meluncurkan aplikasi adalah folder / Applications. Bukan /Applications/foo.app/Contents/MacOS/ seperti yang Anda harapkan.
Anda dapat mengubah aplikasi Anda ke akun untuk ini, atau Anda dapat menggunakan skrip peluncur kecil ajaib ini yang akan mengubah direktori saat ini dan meluncurkan aplikasi Anda.
Pastikan Anda menyesuaikan file Info.plist sehingga CFBundleExecutable menunjuk ke skrip peluncuran dan bukan ke eksekusi sebelumnya.
Oke, sudah selesai sekarang. Untungnya, setelah Anda mengetahui semua hal ini, Anda menguburnya dalam skrip build.
sumber
Saya benar-benar menemukan alat yang sangat berguna yang pantas mendapatkan pujian ... TIDAK - saya tidak mengembangkan ini;)
https://github.com/auriamg/macdylibbundler/
Ini akan menyelesaikan semua dependensi dan "memperbaiki" file executable serta dylib Anda agar berfungsi dengan lancar di app bundle Anda.
... itu juga akan memeriksa dependensi libs dinamis dependen Anda: D
sumber
Saya menggunakan ini di Makefile saya ... Ini membuat bundel aplikasi. Baca dan pahami, karena Anda memerlukan file ikon png di folder macosx / bersama dengan file PkgInfo dan Info.plist yang saya sertakan di sini ...
"ini berfungsi di komputer saya" ... Saya menggunakan ini untuk beberapa aplikasi di 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
sumber
Solusi paling sederhana adalah: buat sekali proyek Xcode tanpa mengubah apa pun (yaitu, simpan aplikasi satu jendela sederhana yang dibuat Xcode untuk Anda), buat, dan salin bundel yang dibuatnya untuk Anda. Kemudian, edit file (terutama Info.plist) agar sesuai dengan konten Anda, dan letakkan biner Anda sendiri di direktori Contents / MacOS /.
sumber
Ada beberapa alat open source untuk membantu membangun app bundle dengan pustaka dependen untuk lingkungan tertentu, misalnya, py2app untuk aplikasi berbasis Python. Jika Anda tidak menemukan yang lebih umum, mungkin Anda dapat menyesuaikannya dengan kebutuhan Anda.
sumber
Saya berharap saya telah menemukan posting ini sebelumnya ....
Inilah cara samar saya untuk menyelesaikan masalah ini menggunakan
Run script
fase yang dipanggil setiap kali saya membuatRelease
versi aplikasi saya:# 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)
Semoga ini bisa bermanfaat bagi seseorang.
sumber
Solusi agar menu berfungsi di Mac dengan kode wxWidget adalah dengan:
Saya setuju bahwa app bundle adalah cara yang benar untuk membuat program di Mac. Ini hanyalah solusi sederhana untuk bantuan selama debugging.
Sunting: Ini di Mac Catalina, wxWidgets 3.1.4, dengan g ++ 4.2.1 (Nov 2020)
sumber