Создайте живую статическую библиотеку (устройство + симулятор) с помощью Xcode и SDK 4+

Похоже, что мы можем – теоретически – построить единую статическую библиотеку, которая включает в себя как симулятор, так и iPhone и iPad.

Однако у Apple нет документации по этому вопросу, которую я могу найти, а шаблоны по умолчанию Xcode НЕ настроены для этого.

Я ищу простой, переносимый, повторно используемый метод, который можно сделать внутри Xcode.

Некоторая история:

  • В 2008 году мы имели обыкновение создавать единые статические библиотеки, включающие как симулятор, так и устройство. Apple отключила это.
  • В течение 2009 года мы создали пары статических libs – один для sim, один для устройства. Apple также отключила это.

Рекомендации:

  1. Это отличная идея, это отличный подход, но он не работает: http://www.drobnik.com/touch/2010/04/universal-static-libraries/

    • В его сценарии есть некоторые ошибки, которые означают, что он работает только на его машине – он должен использовать BUILT_PRODUCTS_DIR и / или BUILD_DIR вместо того, чтобы «оценивать» их)
    • Последний Xcode от Apple не позволяет вам делать то, что он сделал – он просто не будет работать из-за (Documented) изменения в том, как Xcode обрабатывает цели)
  2. Другой опросчик SO спросил, как это сделать БЕЗ xcode, и с ответами, которые были сосредоточены на группе arm6 vs arm7, но проигнорировали часть i386: Как собрать статическую библиотеку (жир) для armv6, armv7 и i386

    • Начиная с последних изменений Apple, часть Simulator больше не отличается от разницы arm6 / arm7 – это другая проблема, см. Выше)

АЛЬТЕРНАТИВЫ:

Легкая копия / вставка последней версии (но инструкции по установке могут измениться – см. Ниже!)

Библиотека Карла требует гораздо больше усилий для настройки, но гораздо более приятного долгосрочного решения (оно преобразует вашу библиотеку в структуру).

Используйте это, а затем настройте его, чтобы добавить поддержку архивных сборок – комментарий cf @ Frederik ниже о тех изменениях, которые он использует, чтобы сделать эту работу красивой в режиме архива.


ПОСЛЕДНИЕ ИЗМЕНЕНИЯ: 1. Добавлена ​​поддержка iOS 10.x (при сохранении поддержки старых платформ)

  1. Информация о том, как использовать этот скрипт с проектом-внедренным в другой проект (хотя я настоятельно рекомендую НЕ делать это, когда-либо – у Apple есть несколько ошибок show-stopper в Xcode, если вы встраиваете проекты друг в друга, из Xcode 3.x до Xcode 4.6.x)

  2. Бонусный скрипт, позволяющий автоматически включать Bundles (например, включать PNG-файлы, файлы PLIST и т. Д. Из вашей библиотеки!) – см. Ниже (прокрутите вниз)

  3. теперь поддерживает iPhone5 (используя обходное решение Apple для ошибок в липо). ПРИМЕЧАНИЕ. Инструкции по установке были изменены (возможно, я могу упростить это, изменив сценарий в будущем, но не хочу рисковать этим сейчас)

  4. Раздел «заголовки копий» теперь учитывает настройку сборки для размещения публичных заголовков (любезно предоставлено Фредериком Валльнером)

  5. Добавлена ​​явная настройка SYMROOT (может быть, необходимо установить OBJROOT?), Благодаря Doug Dickinson


SCRIPT (это то, что вам нужно скопировать / вставить)

Инструкции по использованию / установке см. Ниже.

########################################## # # cf https://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4 # # Version 2.82 # # Latest Change: # - MORE tweaks to get the iOS 10+ and 9- working # - Support iOS 10+ # - Corrected typo for iOS 1-10+ (thanks @stuikomma) # # Purpose: # Automatically create a Universal static library for iPhone + iPad + iPhone Simulator from within XCode # # Author: Adam Martin - http://twitter.com/redglassesapps # Based on: original script from Eonil (main changes: Eonil's script WILL NOT WORK in Xcode GUI - it WILL CRASH YOUR COMPUTER) # set -e set -o pipefail #################[ Tests: helps workaround any future bugs in Xcode ]######## # DEBUG_THIS_SCRIPT="false" if [ $DEBUG_THIS_SCRIPT = "true" ] then echo "########### TESTS #############" echo "Use the following variables when debugging this script; note that they may change on recursions" echo "BUILD_DIR = $BUILD_DIR" echo "BUILD_ROOT = $BUILD_ROOT" echo "CONFIGURATION_BUILD_DIR = $CONFIGURATION_BUILD_DIR" echo "BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR" echo "CONFIGURATION_TEMP_DIR = $CONFIGURATION_TEMP_DIR" echo "TARGET_BUILD_DIR = $TARGET_BUILD_DIR" fi #####################[ part 1 ]################## # First, work out the BASESDK version number (NB: Apple ought to report this, but they hide it) # (incidental: searching for substrings in sh is a nightmare! Sob) SDK_VERSION=$(echo ${SDK_NAME} | grep -o '\d\{1,2\}\.\d\{1,2\}$') # Next, work out if we're in SIM or DEVICE if [ ${PLATFORM_NAME} = "iphonesimulator" ] then OTHER_SDK_TO_BUILD=iphoneos${SDK_VERSION} else OTHER_SDK_TO_BUILD=iphonesimulator${SDK_VERSION} fi echo "XCode has selected SDK: ${PLATFORM_NAME} with version: ${SDK_VERSION} (although back-targetting: ${IPHONEOS_DEPLOYMENT_TARGET})" echo "...therefore, OTHER_SDK_TO_BUILD = ${OTHER_SDK_TO_BUILD}" # #####################[ end of part 1 ]################## #####################[ part 2 ]################## # # IF this is the original invocation, invoke WHATEVER other builds are required # # Xcode is already building ONE target... # # ...but this is a LIBRARY, so Apple is wrong to set it to build just one. # ...we need to build ALL targets # ...we MUST NOT re-build the target that is ALREADY being built: Xcode WILL CRASH YOUR COMPUTER if you try this (infinite recursion!) # # # So: build ONLY the missing platforms/configurations. if [ "true" == ${ALREADYINVOKED:-false} ] then echo "RECURSION: I am NOT the root invocation, so I'm NOT going to recurse" else # CRITICAL: # Prevent infinite recursion (Xcode sucks) export ALREADYINVOKED="true" echo "RECURSION: I am the root ... recursing all missing build targets NOW..." echo "RECURSION: ...about to invoke: xcodebuild -configuration \"${CONFIGURATION}\" -project \"${PROJECT_NAME}.xcodeproj\" -target \"${TARGET_NAME}\" -sdk \"${OTHER_SDK_TO_BUILD}\" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO" BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" SYMROOT=\"${SYMROOT}\" xcodebuild -configuration "${CONFIGURATION}" -project "${PROJECT_NAME}.xcodeproj" -target "${TARGET_NAME}" -sdk "${OTHER_SDK_TO_BUILD}" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}" ACTION="build" #Merge all platform binaries as a fat binary for each configurations. # Calculate where the (multiple) built files are coming from: CURRENTCONFIG_DEVICE_DIR=${SYMROOT}/${CONFIGURATION}-iphoneos CURRENTCONFIG_SIMULATOR_DIR=${SYMROOT}/${CONFIGURATION}-iphonesimulator echo "Taking device build from: ${CURRENTCONFIG_DEVICE_DIR}" echo "Taking simulator build from: ${CURRENTCONFIG_SIMULATOR_DIR}" CREATING_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal echo "...I will output a universal build to: ${CREATING_UNIVERSAL_DIR}" # ... remove the products of previous runs of this script # NB: this directory is ONLY created by this script - it should be safe to delete! rm -rf "${CREATING_UNIVERSAL_DIR}" mkdir "${CREATING_UNIVERSAL_DIR}" # echo "lipo: for current configuration (${CONFIGURATION}) creating output file: ${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}" xcrun -sdk iphoneos lipo -create -output "${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}" "${CURRENTCONFIG_DEVICE_DIR}/${EXECUTABLE_NAME}" "${CURRENTCONFIG_SIMULATOR_DIR}/${EXECUTABLE_NAME}" ######### # # Added: StackOverflow suggestion to also copy "include" files # (untested, but should work OK) # echo "Fetching headers from ${PUBLIC_HEADERS_FOLDER_PATH}" echo " (if you embed your library project in another project, you will need to add" echo " a "User Search Headers" build setting of: (NB INCLUDE THE DOUBLE QUOTES BELOW!)" echo ' "$(TARGET_BUILD_DIR)/usr/local/include/"' if [ -d "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}" ] then mkdir -p "${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}" # * needs to be outside the double quotes? cp -r "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"* "${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}" fi fi 

УСТАНОВКА ИНСТРУКЦИЙ

  1. Создать статический проект lib
  2. Выберите цель
  3. На вкладке «Настройки сборки» установите «Только для сборки активной архитектуры» в «НЕТ» (для всех элементов)
  4. На вкладке «Сборка фаз» выберите «Добавить … Новая фаза сборки … Новая сцена запуска сценария запуска»
  5. Скопируйте / вставьте скрипт (выше) в поле

… БОНУС ОПЦИОНАЛЬНОЕ использование:

  1. ДОПОЛНИТЕЛЬНО: если у вас есть заголовки в вашей библиотеке, добавьте их в фазу «Копировать заголовки»
  2. ДОПОЛНИТЕЛЬНО: … и перетащите их из раздела «Проект» в раздел «Публичный»
  3. ДОПОЛНИТЕЛЬНО: … и они будут автоматически экспортироваться каждый раз, когда вы создадите приложение, в подкаталог каталога «debug-universal» (они будут в usr / local / include)
  4. ДОПОЛНИТЕЛЬНО: ПРИМЕЧАНИЕ. Если вы также попытаетесь перетащить проект в другой проект Xcode, это приведет к ошибке в Xcode 4, где он не сможет создать файл .IPA, если у вас есть публичные заголовки в проекте drag / drop. Обходной путь: dont ’embed xcode projects (слишком много ошибок в коде Apple!)

Если вы не можете найти выходной файл, обходной путь:

  1. Добавьте следующий код в самый конец скрипта (любезно предоставил Фредерик Валнер): откройте «$ {CREATING_UNIVERSAL_DIR}»

  2. Apple удаляет весь вывод после 200 строк. Выберите свою цель, а на этапе запуска сценария вы НЕ ДОПУСКАЕТЕ: «Показать переменные среды в журнале построения»,

  3. если вы используете пользовательский каталог «build output» для XCode4, то XCode помещает все ваши «неожиданные» файлы в неправильное место.

    1. Построить проект
    2. Нажмите на последний значок справа, в верхней левой области Xcode4.
    3. Выберите верхний элемент (это ваша «последняя assembly». Apple должна автоматически выбрать ее, но они не думали об этом)
    4. в главном окне прокрутите вниз. Самая последняя строка должна выглядеть так: lipo: для текущей конфигурации (Debug) создание выходного файла: /Users/blah/Library/Developer/Xcode/DerivedData/AppName-ashwnbutvodmoleijzlncudsekyf/Build/Products/Debug-universal/libTargetName.a

    … это местоположение вашей универсальной сборки.


Как включить в проект файлы «non sourcecode» (PNG, PLIST, XML и т. Д.)

  1. Сделайте все выше, проверьте, работает ли он
  2. Создайте новую фазу сценария запуска, которая появляется ПОСЛЕ ПЕРВОГО ОДНОГО (скопируйте / вставьте код ниже)
  3. Создайте новую цель в Xcode, типа “bundle”
  4. В своем ГЛАВНОМ ПРОЕКТЕ, в разделе «Фазы сборки», добавьте новый пакет как что-то, что «зависит от» (верхняя часть, нажмите кнопку «плюс», прокрутите вниз, найдите файл «.bundle» в своих продуктах)
  5. В своей новой задаче BUNDLE TARGET в «Build Phases» добавьте раздел «Ресурсы копирования Bundle» и перетащите все PNG-файлы и т. Д.

Скрипт для автоматической копирования встроенных пакетов в ту же папку, что и ваша статическая библиотека FAT:

 echo "RunScript2:" echo "Autocopying any bundles into the 'universal' output folder created by RunScript1" CREATING_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal cp -r "${BUILT_PRODUCTS_DIR}/"*.bundle "${CREATING_UNIVERSAL_DIR}" 

Я потратил много часов на создание толстой статической библиотеки, которая будет работать на armv7, armv7s и симуляторе. Наконец нашел решение .

Суть заключается в том, чтобы создать две библиотеки (одну для устройства, а затем одну для симулятора) отдельно, переименовать их, чтобы отличить друг от друга, а затем липо-соединить их в одну библиотеку.

 lipo -create libPhone.a libSimulator.a -output libUniversal.a 

Я попробовал, и он работает!

Я создал шаблон проекта XCode 4, который позволяет создавать универсальную структуру так же легко, как создание обычной библиотеки.

Существует утилита командной строки xcodebuild и вы можете запускать команду оболочки в xcode. Итак, если вы не против использования пользовательского скрипта, этот скрипт может вам помочь.

 #Configurations. #This script designed for Mac OS X command-line, so does not use Xcode build variables. #But you can use it freely if you want. TARGET=sns ACTION="clean build" FILE_NAME=libsns.a DEVICE=iphoneos3.2 SIMULATOR=iphonesimulator3.2 #Build for all platforms/configurations. xcodebuild -configuration Debug -target ${TARGET} -sdk ${DEVICE} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO xcodebuild -configuration Debug -target ${TARGET} -sdk ${SIMULATOR} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO xcodebuild -configuration Release -target ${TARGET} -sdk ${DEVICE} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO xcodebuild -configuration Release -target ${TARGET} -sdk ${SIMULATOR} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO #Merge all platform binaries as a fat binary for each configurations. DEBUG_DEVICE_DIR=${SYMROOT}/Debug-iphoneos DEBUG_SIMULATOR_DIR=${SYMROOT}/Debug-iphonesimulator DEBUG_UNIVERSAL_DIR=${SYMROOT}/Debug-universal RELEASE_DEVICE_DIR=${SYMROOT}/Release-iphoneos RELEASE_SIMULATOR_DIR=${SYMROOT}/Release-iphonesimulator RELEASE_UNIVERSAL_DIR=${SYMROOT}/Release-universal rm -rf "${DEBUG_UNIVERSAL_DIR}" rm -rf "${RELEASE_UNIVERSAL_DIR}" mkdir "${DEBUG_UNIVERSAL_DIR}" mkdir "${RELEASE_UNIVERSAL_DIR}" lipo -create -output "${DEBUG_UNIVERSAL_DIR}/${FILE_NAME}" "${DEBUG_DEVICE_DIR}/${FILE_NAME}" "${DEBUG_SIMULATOR_DIR}/${FILE_NAME}" lipo -create -output "${RELEASE_UNIVERSAL_DIR}/${FILE_NAME}" "${RELEASE_DEVICE_DIR}/${FILE_NAME}" "${RELEASE_SIMULATOR_DIR}/${FILE_NAME}" 

Может быть, выглядит неэффективным (я не очень хорошо разбираюсь в сценарии оболочки), но его легко понять. Я настроил новую цель, запускающую только этот скрипт. Сценарий предназначен для командной строки, но не проверен в 🙂

Основная концепция – xcodebuild и xcodebuild .

Я пробовал много конфигураций в интерфейсе Xcode, но ничего не получилось. Потому что это своего рода пакетная обработка, поэтому дизайн командной строки более подходит, поэтому Apple постепенно удалила функцию пакетной сборки из Xcode. Поэтому я не ожидаю, что в будущем они будут предлагать функцию пакетной сборки на основе пользовательского интерфейса.

Мне нужен толстый статический lib для JsonKit, поэтому был создан статический проект lib в Xcode, а затем запущен этот скрипт bash в каталоге проекта. Пока вы сконфигурировали проект xcode с отключенной «Build active configuration», вы должны получить все архитектуры в одной библиотеке.

 #!/bin/bash xcodebuild -sdk iphoneos xcodebuild -sdk iphonesimulator lipo -create -output libJsonKit.a build/Release-iphoneos/libJsonKit.a build/Release-iphonesimulator/libJsonKit.a 

Обновление IOS 10:

У меня возникла проблема с созданием fatlib с iphoneos10.0, потому что регулярное выражение в скрипте ожидает только 9.x и ниже и возвращает 0.0 для ios 10.0

исправить это просто заменить

 SDK_VERSION=$(echo ${SDK_NAME} | grep -o '.\{3\}$') 

с

 SDK_VERSION=$(echo ${SDK_NAME} | grep -o '[\\.0-9]\{3,4\}$') 

Я сделал это в шаблоне Xcode 4 , в том же ключе, что и шаблон статической frameworks Karl.

Я обнаружил, что создание статических фреймворков (вместо простых статических библиотек) вызывало случайные сбои с LLVM из-за очевидной ошибки компоновщика – поэтому, я думаю, статические библиотеки по-прежнему полезны!

Отличная работа! Я взломал что-то подобное, но мне пришлось запускать его отдельно. Наличие его просто в процессе сборки делает его намного проще.

Один пункт примечания. Я заметил, что он не копирует ни один из включенных файлов, которые вы отмечаете как общедоступные. Я адаптировал то, что у меня было в своем сценарии, и это работает довольно хорошо. Вставьте следующее в конец вашего скрипта.

 if [ -d "${CURRENTCONFIG_DEVICE_DIR}/usr/local/include" ] then mkdir -p "${CURRENTCONFIG_UNIVERSAL_DIR}/usr/local/include" cp "${CURRENTCONFIG_DEVICE_DIR}"/usr/local/include/* "${CURRENTCONFIG_UNIVERSAL_DIR}/usr/local/include" fi 

Я на самом деле просто написал свой собственный сценарий для этой цели. Он не использует Xcode. (Он основан на аналогичном сценарии в проекте Gambit Scheme.)

В принципе, он запускается ./configure и делает три раза (для i386, armv7 и armv7s) и объединяет каждую из полученных библиотек в fat lib.

  • Xcode - Предупреждение: неявное объявление функции недействительно в C99
  • Xcode 5: ошибки при подписании кода
  • Простой пример XML-анализа libxml2 с использованием Objective-c, Xcode и HTMLparser.h
  • Что именно делает делегат в проекте xcode ios?
  • Что такое ошибка связи: OS_xpc_error в Xcode 6?
  • Расположение профилей XCode Provisioning
  • Как удалить производные данные в Xcode 8?
  • Ошибка Apple Mach-O Linker при компиляции для устройства
  • Перерыв на EXC_BAD_ACCESS в XCode?
  • Xcode, дублированный символ _main
  • Округленный UIView с использованием CALayers - только некоторые углы - как?
  • Interesting Posts

    Есть ли определенный флаг компилятора Xcode, который устанавливается при компиляции для iPad?

    Как настроить громкость звука каждый раз, когда я нажимаю клавиши изменения громкости?

    Преобразование из EPS в формат SVG

    Метод Image.FromStream () возвращает исключение Invalid Argument

    Репликация данных из отдельных файлов в OpenOffice Calc

    Когда требуется ключевое слово «typename»?

    Мой коллега часто закрывает мою машину через ЛВС – как мне ее предотвратить?

    Добавление строки в файл hosts, получение разрешения отклонено при использовании sudo – Mac

    Что означает двойной двоеточие (: :) в CSS?

    Как я могу ввести тильду (~) в терминале Ubuntu с помощью норвежской клавиатуры?

    В чем разница между Apache Spark SQLContext и HiveContext?

    Как получить доступ к внешнему жесткому диску с NTFS из моего iMac бесплатно?

    Что написано на стороне «ключа тайны» на клавиатуре IBM PC 3180?

    Ожидание до завершения асинхронной задачи

    MySQL Не удается удалить индекс, необходимый для ограничения внешнего ключа

    Давайте будем гением компьютера.