Створення набору програм для OSX


90

Припустимо, я створив додаток osX без використання Xcode. Після компіляції з GCC я отримую виконуваний файл, який пов'язаний з кількома іншими бібліотеками. Деякі з цих бібліотек можуть знову бути динамічно пов'язані з іншими нестандартними системними бібліотеками

Чи існує якийсь інструмент, який створює набір додатків OSX, спочатку створюючи необхідні структури каталогів, а потім рекурсивно копіюючи / перевіряючи / виправляючи посилання, щоб переконатися, що всі динамічні залежності також містяться в наборі програм?

Я думаю, я можу спробувати написати щось подібне, але мені було цікаво, чи щось подібне вже існує.


4
Я не можу використовувати Xcode з кількох причин. одна з яких тому, що я використовую користувацькі gcc. Xcode не дозволяє мені вказати інший gcc. Я використовую cmake для створення своїх файлів make.
Йог

>> Деякі з цих бібліотек можуть знову бути динамічно пов'язані з іншими нестандартними системними бібліотеками << Вибрана відповідь не допомагає у цьому випадку. Як ви це вирішили?
FlowUI. SimpleUITesting.com

Відповіді:


143

Існує два способи створення набору додатків на MacOSX - Easy і Ugly.

Найпростіший спосіб - просто використовувати XCode. Готово.

Проблема іноді в тому, що ви не можете.

У моєму випадку я будую додаток, який створює інші додатки. Я не можу припустити, що у користувача встановлений XCode. Я також використовую MacPorts для створення бібліотек, від яких залежить моя програма. Мені потрібно переконатись, що ці диліби входять до комплекту додатка, перш ніж розповсюджувати його.

Застереження: Я абсолютно некваліфікований писати цю публікацію, все, що є, було висвітлено з документів Apple, виділяючи існуючі програми та спроби та помилки. Це працює для мене, але, швидше за все, неправильно. Будь ласка, напишіть мені, якщо у вас є якісь виправлення.

Перше, що ви повинні знати, це те, що набір додатків - це просто каталог.
Давайте вивчимо структуру гіпотетичного foo.app.

foo.app/
    Зміст /
        Info.plist
        MacOS /
            foo
        Ресурси /
            foo.icns

Info.plist - це звичайний XML-файл. Ви можете редагувати його за допомогою текстового редактора або програми редактора списку властивостей, яка постачається в комплекті з XCode. (Він знаходиться в каталозі / Developer / Applications / Utilities /).

Ключові речі, які потрібно включити:

CFBundleName - назва програми.

CFBundleIcon - Файл піктограм, який передбачається знаходити в папці Зміст / Ресурси. Для створення піктограми використовуйте програму Icon Composer. (Це також знаходиться в каталозі / Developer / Applications / Utilities /). Ви можете просто перетягнути PNG у вікно, і він повинен автоматично генерувати для вас рівні mip.

CFBundleExecutable - ім'я виконуваного файлу, який, як передбачається, знаходиться в папці Contents / MacOS /

Є набагато більше варіантів, перераховані вище - це лише мінімальний мінімум. Ось деяка документація Apple щодо файлу Info.plist та структури набору додатків .

Крім того, ось зразок 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">
<вироку>
  <key> CFBundleGetInfoString </key>
  <string> Фу </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>

У ідеальному світі ви можете просто залишити свій виконуваний файл у Contents / MacOS / dir і закінчити. Однак якщо у вашої програми є якісь нестандартні залежності від dylib, вона не буде працювати. Як і Windows, MacOS поставляється зі своїм особливим видом пекла DLL .

Якщо ви використовуєте MacPorts для побудови бібліотек, до яких ви посилаєтеся, розташування дилібів буде жорстко закодовано у ваш виконуваний файл. Якщо ви запускаєте програму на машині, що має диліби в точно такому ж місці, вона буде працювати нормально. Однак більшість користувачів їх не встановлюватимуть; коли вони двічі клацнуть вашу програму, вона просто вийде з ладу.

Перш ніж розповсюджувати виконуваний файл, вам потрібно зібрати всі диліби, які він завантажує, і скопіювати їх у пакет програм. Вам також потрібно буде відредагувати виконуваний файл, щоб він шукав диліби у правильному місці. тобто куди ви їх скопіювали.

Ручне редагування виконуваного файлу звучить небезпечно, чи не так? На щастя, є інструменти командного рядка, які допоможуть.

otool -L ім'я виконуваного файлу

Ця команда перелічить усі диліби, від яких залежить ваш додаток. Якщо ви бачите щось, чого НЕ є в папці System / Library або usr / lib, саме це вам потрібно буде скопіювати в пакет програм. Скопіюйте їх у папку / Contents / MacOS /. Далі вам потрібно буде відредагувати виконуваний файл, щоб використовувати нові диліби.

По-перше, вам потрібно переконатися, що ви посилаєтеся за допомогою прапора -headerpad_max_install_names. Це лише гарантує, що якщо новий шлях Dylib довший за попередній, для нього буде місце.

По-друге, використовуйте install_name_tool для зміни кожного шляху dylib.

install_name_tool -змінити існуючий_ шлях_до_дилібу @ виконуваний_путь / blah.dylib ім'я_виконавчого

Як практичний приклад, припустимо, ваш додаток використовує 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.app у папці / Applcations, то поточним каталогом під час запуску програми буде папка / Applications. Не /Applications/foo.app/Contents/MacOS/, як ви могли очікувати.

Ви можете змінити свій додаток, щоб це врахувати, або ви можете використовувати цей чарівний скрипт запуску, який змінить поточний каталог і запустить вашу програму.

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

Переконайтеся, що ви налаштували файл Info.plist так, щоб CFBundleExecutable вказував на сценарій запуску, а не на попередній виконуваний файл.

Гаразд, все готово. На щастя, коли ви знаєте все це, ви закопуєте його у сценарій збірки.


7
+1. Однак собака зомбі мене збиває з розуму. Чи не могли б ви використати малюнок з меншими рубцями для ілюстрації пекла DLL? (Не кажучи вже про те, що термін "пекло DLL" не застосовується. Кращий метод розповсюдження динамічних бібліотек - це фреймворки, які дозволяють уникнути більшості проблем. Проте підхід .dylib корисний для бібліотек у стилі UNIX.)
Генріх Апфельмус,

1
Як виправити подвійні залежності? Як диліб посилається на інший диліб?
FlowUI. SimpleUITesting.com

Чи немає способу скомпілювати виконуваний файл із правильними місцями, щоб вам не потрібно було його редагувати?
синхронізатор

Чи могло б це навіть спрацювати на Каталіні, враховуючи, наскільки жорсткою стала безпека? (модифікація дилібів, двійкових файлів тощо)
синхронізатор

20

Я насправді знайшов дуже зручний інструмент, який заслуговує на якусь заслугу ... НІ - я цього не розробляв;

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

Це дозволить вирішити всі залежності та "виправити" ваш виконуваний файл, а також ваші файли dylib для безперебійної роботи у вашому додатку.

... він також перевірить на залежність ваших залежних динамічних бібліотек: D


це досить корисний інструмент! Дякуємо, що поділилися та розвинули його.
xpnimi

Тут відсутні залежності залежностей. Виправити?
Петро

9

Я використовую це у своєму Makefile ... Це створює пакет програм. Прочитайте і зрозумійте, тому що вам знадобиться файл значка png у папці macosx / разом із файлами PkgInfo та Info.plist, які я тут включаю ...

"це працює на моєму комп'ютері" ... Я використовую це для кількох програм на 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

APPLMyAp

Ви можете зробити так, щоб Info.plist містив текстові заповнювачі, а потім використовував щось на зразок sed або awk для створення остаточного Info.plist з правильними полями. Або, можливо, навіть використовуйте plutil для створення plist.
Марк Хіт

8

Найпростішим рішенням є: створити один раз проект Xcode, не змінюючи нічого (тобто зберегти просту програму з одним вікном, яку створює Xcode для вас), побудувати його та скопіювати набір, створений для вас. Потім відредагуйте файли (зокрема Info.plist) відповідно до вашого вмісту та вставте власну двійкову систему в каталог Contents / MacOS /.


4
Питання прямо задає питання, як це зробити без xcode. Я в одному човні і хотів би справжньої відповіді.
гіперлогічний

5
Ну, з цим вам потрібно використовувати XCode лише один раз у житті :) або попросити когось зробити це за вас.
F'x

@ F'x Чи можете ви просто опублікувати згенерований .app як приклад?
ендоліти

3

Існує кілька інструментів з відкритим кодом, які допомагають створювати набори додатків із залежними бібліотеками для певних середовищ, наприклад, py2app для програм на основі Python. Якщо ви не знайдете більш загального, можливо, ви зможете пристосувати його до своїх потреб.


2

Шкода, що я знайшов цю публікацію раніше ....

Ось мій схематичний спосіб вирішення цієї проблеми за допомогою 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=$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)

Сподіваюся, це може комусь стати в нагоді.


0

Вихідне рішення, щоб меню працювало на Mac з кодом wxWidget, це просто:

  1. Запустіть програму в терміналі, як зазвичай (./appname)
  2. Графічний інтерфейс програми запускається як зазвичай, і натисніть термінал, щоб програма втратила фокус
  3. Клацніть графічний інтерфейс, щоб відновити фокус, і пункти меню працюють.

Я погоджуюсь, що пакет додатків - це правильний спосіб створення програми на Mac. Це лише просте обхідне рішення щодо допомоги під час налагодження.

Редагувати: це на Mac Catalina, wxWidgets 3.1.4, з g ++ 4.2.1 (листопад 2020)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.