Создание пакета приложений OSX
предположим, что я сделал приложение osX без использования Xcode. После компиляции с GCC я получаю исполняемый файл, который связан с несколькими другими библиотеками. Некоторые из этих библиотек могут быть снова динамически связаны с другими нестандартными системными библиотеками
существует ли какой-либо инструмент, который создает пакет приложений OSX, сначала создавая необходимые структуры каталогов, а затем рекурсивно копируя/проверяя / фиксируя ссылки, чтобы убедиться, что все динамические зависимости также находятся в приложении бандл?
Я думаю, я могу попробовать написать что-то вроде этого, но мне было интересно, если нечто подобное уже существует.
7 ответов:
Существует два способа создания пакета приложений на MacOSX: простой и уродливый.
Самый простой способ-это просто использовать XCode. Сделанный.
Проблема в том, что иногда вы не можете.
В моем случае я создаю приложение, которое создает другие приложения. Я не могу предположить, что у пользователя установлен XCode. Я также использую MacPorts чтобы построить библиотеки, от которых зависит мое приложение. Мне нужно убедиться, что эти dylibs поставляются в комплекте с приложением, прежде чем распространять оно.
отказ от ответственности: Я совершенно не квалифицирован, чтобы написать этот пост, все в is был отблеск от Apple docs, выбирая между существующими приложениями и методом проб и ошибок. Это работает для меня, но, скорее всего, неправильно. Пожалуйста напишите Мне, если у вас есть какие-то корректировки.
Первое, что вы должны знать, это то, что пакет приложений-это просто каталог.
Рассмотрим структуру гипотетического foo.приложение.
foo.app/ Contents/ Info.plist MacOS/ foo Resources/ foo.icnsИнформация.plist-это простой XML-файл. Вы можете редактировать его с помощью текстового редактора или редактора списка свойств, который поставляется в комплекте с XCode. (Он находится в каталоге/Developer/Applications/ Utilities/).
Ключевые вещи, которые вы должны включить являются:
CFBundleName - имя приложения.
CFBundleIcon - файл значка, предположительно находящийся в Contents/Resources dir. Используйте значок Композитор приложение для создания значка. (Он также находится в каталоге/разработчик/приложения/ утилиты / каталог) Вы можете просто перетащить png на его окно и должны автоматически генерировать MIP-уровни для вас.
CFBundleExecutable - имя исполняемого файла, который предположительно находится в папке Contents / MacOS/sub.
Есть еще много вариантов, те, которые перечислены выше, являются только самым минимальным. Вот некоторые документы Apple о информация.файл plist файл и структура пакета приложений.
Кроме того, Вот пример информации.файл 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>CFBundleGetInfoString</key> <string>Foo</string> <key>CFBundleExecutable</key> <string>foo</string> <key>CFBundleIdentifier</key> <string>com.your-company-name.www</string> <key>CFBundleName</key> <string>foo</string> <key>CFBundleIconFile</key> <string>foo.icns</string> <key>CFBundleShortVersionString</key> <string>0.01</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>IFMajorVersion</key> <integer>0</integer> <key>IFMinorVersion</key> <integer>1</integer> </dict> </plist>В идеальном мире вы могли бы просто бросить свой исполняемый файл в Содержание / MacOS / dir и быть сделано. Однако, если ваше приложение имеет какие-либо нестандартных зависимостей dylib нужна, он не будет работать. Как Windows, MacOS поставляется с собственным особым видом DLL ад.
Если вы используете MacPorts для создания библиотек, с которыми вы связываетесь, местоположения dylibs будут жестко закодированы в вашем исполняемом файле. Если вы запустите приложение на машине, которая имеет dylibs в том же месте, он будет работать нормально. Однако большинство пользователей не будут их устанавливать; когда они дважды щелкнут ваше приложение, оно просто рухнет.
Прежде чем распространять исполняемый файл, вам нужно собрать все загружаемые им dylibs и скопировать их в пакет приложений. Вы также нужно отредактировать исполняемый файл так, чтобы он искал dylibs в правильном месте. то есть, куда вы их скопировали.
Ручное редактирование исполняемого файла звучит опасно, не так ли? К счастью, есть инструменты командной строки, чтобы помочь.
otool -L executable_nameЭта команда выведет список всех dylibs, от которых зависит ваше приложение. Если вы видите какие-либо из них, которые не находятся в папке System/Library или usr/lib, это те, которые вам нужно скопировать в пакет приложений. Скопируйте их в /Содержимое папки/Макосе/. Затем вам нужно будет отредактировать исполняемый файл, чтобы использовать новые dylibs.
Во-первых, вы должны убедиться, что вы связываете с помощью-headerpad_max_install_names флаг. Это просто гарантирует, что если новый путь dylib длиннее предыдущего, для него будет место.
Во-вторых, используйте install_name_tool для изменения каждого пути dylib.
install_name_tool -change existing_path_to_dylib @executable_path/blah.dylib executable_nameВ качестве практического примера предположим, что ваше приложение использует libSDL, и otool перечисляет его местоположение как " / opt/local/lib / libSDL-1.2.0.dylib нужна".
Сначала скопируйте его в пакет приложений.
cp /opt/local/lib/libSDL-1.2.0.dylib foo.app/Contents/MacOS/Затем отредактируйте исполняемый файл, чтобы использовать новое расположение (Примечание: убедитесь, что вы создали его с флагом-headerpad_max_install_names)
install_name_tool -change /opt/local/lib/libSDL-1.2.0.dylib @executable_path/libSDL-1.2.0.dylib foo.app/Contents/MacOS/fooНу, мы почти закончили. Теперь есть небольшая проблема с текущим рабочим каталогом.
Когда вы начинаете свой приложение текущий каталог будет каталогом выше, где находится приложение. Например: если вы поместите foo.приложение в папке / Applcations, то текущий каталог при запуске приложения будет папка /Applications. Не то / приложения / фу.приложение / содержание / MacOS / как и следовало ожидать.
Вы можете изменить свое приложение, чтобы учесть это, или вы можете использовать этот волшебный маленький скрипт запуска, который изменит текущий каталог и запустит ваш приложение.
#!/bin/bash cd "${0%/*}" ./fooУбедитесь, что вы настроили информацию.файл plist, так что CFBundleExecutable указывает на сценарий запуска, а не на предыдущий исполняемый файл.
Хорошо, теперь все сделано. К счастью, как только вы узнаете все это, вы похороните его в сценарии сборки.
Я на самом деле нашел очень удобный инструмент, который заслуживает уважения ... Нет - я этого не развивал;)
https://github.com/auriamg/macdylibbundler/
он разрешит все зависимости и" исправит " ваш исполняемый файл, а также ваши файлы dylib для бесперебойной работы в вашем пакете приложений.
... он также будет проверять зависимости ваших зависимых динамических библиотек :D
Я использую это в моем Makefile... Он создает пакет приложений. Прочитайте его и поймите, потому что вам понадобится файл значка png в папке macosx/ вместе с PkgInfo и Info.plist файлы, которые я включаю здесь...
"Она работает на моем компьютере"... Я использую это для нескольких приложений на Маверикс...
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информация.файл 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
самое простое решение: создайте один раз проект Xcode, ничего не меняя (т. е. сохраните простое приложение с одним окном, которое Xcode создает для вас), создайте его и скопируйте пакет, который он создал для вас. Затем отредактируйте файлы (в частности, информацию.plist) в соответствии с вашим контентом и поместите свой собственный двоичный файл в каталог Contents/MacOS/.
в этом пакете действительно нет ничего волшебного-просто прочитайте документацию Apple по нему и имитируйте ее. На базе, вам нужна информация.plist, содержимое / MacOS / binary и значок.
существуют некоторые инструменты с открытым исходным кодом, которые помогают создавать пакеты приложений с зависимыми библиотеками для конкретных сред, например,py2app для приложений на основе Python. Если вы не найдете более общего, возможно, вы сможете адаптировать его к своим потребностям.
жаль, что я не нашел этот пост раньше....
вот мой сомнительный способ решения этой проблемы с помощью
Run scriptфаза, которая вызывается каждый раз, когда я строюReleaseверсия моего приложения:# 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= local prefix= 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= local prefix= 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)надеюсь, что это может быть полезным кому-то.
Comments