it-swarm.com.ru

Компиляция внешней библиотеки C++ для использования с проектом iOS

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

У меня есть внешняя библиотека C++, которую я пытаюсь использовать с проектом iOS. Библиотека следует шаблону configure, make, make для вывода файла библиотеки .a. Когда я пытаюсь добавить этот файл библиотеки в XCode, я получаю следующую ошибку:

игнорирование файла /Users/Developer/iOS/TestProj/libpresage.a, файл был построен для архива, который не связан с архитектурой (i386):

/Users/Developer/iOS/TestProj/libpresage.a

Исходя из этот вопрос я попытался повернуть Build Active Architecture Only на NO, и я получаю ту же ошибку. Это заставляет меня подозревать, что я скомпилировал библиотеку для неправильной архитектуры. 

Запуск lipo -info в файле .a дает: 

входной файл libpresage.a не является жирным файлом Не жирный файл: libpresage.a

это архитектура: x86_64

Учитывая, что это не armv7s, armv7 или arm64, я снова пытаюсь скомпилировать библиотеку C++ со следующими параметрами:

1) Попробуйте 

./configure CC="gcc -Arch armv7s" \
                 CXX="g++ -Arch armv7s" \
                 CPP="gcc -E" CXXCPP="g++ -E"

Ошибка в компиляции, я получаю:

ld: library not found for -lcrt1.3.1.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

2) Попробуйте 

./configure CC="gcc -Arch arm64" \
                 CXX="g++ -Arch arm64" \
                 CPP="gcc -E" CXXCPP="g++ -E"

Ошибка в компиляции, я получаю:

ld: warning: ld: warning: игнорирование файла /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib, отсутствует требуемая архитектура arm64 в файле /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libSystem.dylib (2 среза) игнорирование файла /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib, отсутствует требуемая архитектура arm64 в файле /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libstdc++.dylib (2 ломтика)

ld: динамические основные исполняемые файлы должны связываться с libSystem.dylib для архитектура arm64 clang: ошибка: сбой команды компоновщика с кодом выхода 1 (используйте -v, чтобы увидеть вызов)

Есть ли что-то очевидное, чего мне не хватает?

Правка:

Спасибо за ответы, так что мне удалось перевести библиотеку в Xcode в качестве пользовательской цели сборки, указав команду make на библиотеки MakeFile. Эта сборка в порядке.

Мои шаги отсюда:

  • Добавьте зависимость от моей цели приложения iOS Objective C к пользовательской цели сборки.
  • Сделайте ссылку на библиотеку и создайте оболочку Objective C++. 
  • Это нормально, пока мне не нужно вызывать внешнюю библиотеку C++, тогда я получаю ошибку при компиляции:

Неопределенные символы для архитектуры armv7: "Presage :: Presage (PresageCallback *)", на который ссылаются из: - [PresageBridge init] в PresageBridge.o "Presage :: ~ Presage ()", на который ссылаются из: - [PresageBridge init] в PresageBridge.o ld: символ (ы) не найден для архитектуры armv7 clang: error: команда компоновщика завершилась неудачно с кодом выхода 1 (используйте -v для просмотра вызова)

  • Моя оболочка Objective C++ (связывающая внешний заголовок библиотеки C++ presage.h):

    #import "PresageBridge.h"
    #include "presage.h"
    
    @implementation PresageBridge
    
    - (instancetype)init
    {
        if(self = [super init])
        {
    
           Presage hello(&callback);
        }
    
        return self;
    }
    
  • Исходя из приведенного выше кода, не похоже, что мне не хватает заголовка, и что интересно, я также пытался создать экземпляр других классов во внешней библиотеке, и они, кажется, работают, что предполагает, что Xcode может не правильно связать presage.h по какой-то причине. 

16
HHHH

Поэтому я использовал много сторонних библиотек C++ в своих проектах iOS. Есть разные стратегии, которые люди используют для этого. Как уже упоминали некоторые, вы можете напрямую включить код в проект, собрать статическую библиотеку с помощью Xcode или создать ее из командной строки. В случае кроссплатформенных библиотек C++, использующих систему конфигурирования и сборки GNU, я предпочитаю командную строку. Вам нужно собрать его только один раз, и вам нужно только вернуться к нему, если вам нужно обновить версию или добавить новый фрагмент архитектуры. 

Обобщенный подход, который вы хотите:

  • Выясните правильные аргументы конфигурации, чтобы использовать для создания каждого среза. Как правило, вам нужно сосредоточиться только на работе одной руки, а также на i386. Остальные легки, ты это сделал. В некоторых случаях вам действительно нужно изменить файл конфигурации, чтобы добавить хост, или внести некоторые другие изменения.

  • Как только вы сможете собрать все фрагменты, вы захотите запустить lipo для создания толстого бинарного файла.

Лучший способ справиться с этим - создать скрипт сборки, который сделает всю работу за вас. Таким образом, легче повторить. Что еще более важно, вы можете повторно использовать сценарий или изменить его для создания других внешних библиотек.

Есть много способов, которыми вы можете создать скрипт. Вот один. У меня есть несколько вариантов этого типа сценария. Этот скрипт был использован для сборки cURL. Это более или менее сработало для preage с очень небольшим модом (т.е. изменил curl на preage). Обратите внимание, что я не тестировал его в Xcode (т.е. связывал и запускал). Я обнаружил, что должен был отключить sqlite, иначе он создавал элементы инструментов, которые не собирались правильно. Если вам это нужно, вы можете понять эту часть.

Есть много способов сделать его более привлекательным. Например, используя массив для хранения всех архитектур. Это просто грубая сила.

Ключевые моменты сценария:

  1. Получение последней версии SDK
  2. Создание каждого среза
  3. Потом бегом липо

Обратите внимание, что это должно работать из коробки, однако, YMMV. Будьте готовы отладить его при необходимости. Например, я не подтвердил тип хоста, но обычно это то, что я всегда использовал. Вы хотите поместить это в каталог для preage (тот же каталог, где сконфигурирован). Когда это сделано, все архитектуры находятся в выходном каталоге. Универсальная библиотека находится в каталоге preage.

Также помните, что вы должны правильно ссылаться в универсальной библиотеке, а также правильно определять путь поиска файлов заголовков.

#!/bin/bash

PLATFORMPATH="/Applications/Xcode.app/Contents/Developer/Platforms"
TOOLSPATH="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin"
export IPHONEOS_DEPLOYMENT_TARGET="8.0"
pwd=`pwd`

findLatestSDKVersion()
{
    sdks=`ls $PLATFORMPATH/$1.platform/Developer/SDKs`
    arr=()
    for sdk in $sdks
    do
       arr[${#arr[@]}]=$sdk
    done

    # Last item will be the current SDK, since it is alpha ordered
    count=${#arr[@]}
    if [ $count -gt 0 ]; then
       sdk=${arr[$count-1]:${#1}}
       num=`expr ${#sdk}-4`
       SDKVERSION=${sdk:0:$num}
    else
       SDKVERSION="8.0"
    fi
}

buildit()
{
    target=$1
    hosttarget=$1
    platform=$2

    if [[ $hosttarget == "x86_64" ]]; then
        hostarget="i386"
    Elif [[ $hosttarget == "arm64" ]]; then
        hosttarget="arm"
    fi

    export CC="$(xcrun -sdk iphoneos -find clang)"
    export CPP="$CC -E"
    export CFLAGS="-Arch ${target} -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk -miphoneos-version-min=$SDKVERSION"
    export AR=$(xcrun -sdk iphoneos -find ar)
    export RANLIB=$(xcrun -sdk iphoneos -find ranlib)
    export CPPFLAGS="-Arch ${target}  -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk -miphoneos-version-min=$SDKVERSION"
    export LDFLAGS="-Arch ${target} -isysroot $PLATFORMPATH/$platform.platform/Developer/SDKs/$platform$SDKVERSION.sdk"

    mkdir -p $pwd/output/$target

     ./configure --prefix="$pwd/output/$target" --disable-shared --disable-sqlite --Host=$hosttarget-Apple-darwin

    make clean
    make
    make install
}

findLatestSDKVersion iPhoneOS

buildit armv7 iPhoneOS
buildit armv7s iPhoneOS
buildit arm64 iPhoneOS
buildit i386 iPhoneSimulator
buildit x86_64 iPhoneSimulator

LIPO=$(xcrun -sdk iphoneos -find lipo)
$LIPO -create $pwd/output/armv7/lib/libpresage.a  $pwd/output/armv7s/lib/libpresage.a $pwd/output/arm64/lib/libpresage.a $pwd/output/x86_64/lib/libpresage.a $pwd/output/i386/lib/libpresage.a -output libpresage.a
29
Mobile Ben

Учитывая, что вы новичок в библиотеках C++, я думаю, вам нужно будет немного больше исследовать.

Тем не менее, я постараюсь наметить некоторые шаги, которые вы должны принять во внимание:

  • вам нужно убедиться, что вы компилируете для одной архитектуры и статическую библиотеку (.a), и проект
  • исходя из вашей ошибки, вам нужно скомпилировать статическую библиотеку для i386 OR изменить ваш проект на x86_64 (разница между этими архитектурами немного сложнее, но сейчас давайте скажем, что i386 означает 32-битный десктоп, а x86_64 означает десктопный 64 бит)
  • архитектура arm для iPhone, а не для MacOS (поэтому он не может найти библиотеки с архитектурой arm в папке MacOSX)!

Есть несколько способов решения этих проблем.

Для первого я бы предложил включить в ваше рабочее пространство статическую библиотеку и добавить ее в качестве зависимости к вашей цели сборки. Для этого вам нужно понять сборки XCode.

Я предполагаю, что вы на самом деле пытаетесь создать приложение для телефона, поэтому для третьего варианта вам нужно настроить сборку g ++ для просмотра iPhoneSDK из XCode при связывании конечных целей (посмотрите на iPhoneOS.platform) для этого.

Создание сборки руки будет работать только на iPhone. Если вы хотите, чтобы он работал на симуляторе, вам нужно связать статическую библиотеку с библиотеками внутри iPhoneSimulator.platform.

Если вы хотите, чтобы ваша статическая библиотека работала как на iPhone, так и на симуляторе iPhone, вам понадобится сделать полную библиотеку (в основном библиотека, содержащая символы для обеих платформ)

Если вам не хватает этих платформ, вы можете скачать их с XCode (но я верю, что они есть)

Как вы можете видеть, все будет становиться все более и более сложным, поэтому я настоятельно рекомендую использовать XCode для компиляции статической библиотеки (это все еще выполнимо с g ++ thou).

Я полагаю, что следующие концепции, которые вы были бы полезны для исследования:

  • arm, x86, x86_64
  • статическая библиотека
  • статическая связь
  • fat lib (универсальная библиотека)
  • Рабочая область XCode с несколькими проектами

Надеюсь это поможет :).

6
MichaelCMS

Вот что сработало для меня в Xcode 9 для устройств iOS (iPhone X):
1) Скомпилируйте dylib, установив следующие флаги:
а) «Каталог установки»: @ Executable_path/Каркасы б) «Путь поиска пути»: @ executable_path/Каркасы
Смотрите картинку ниже:
настройки dylib в Xcode 9

2) В проекте Xcode, где используется/связан dylib:
а) «Путь поиска по пути»:
@ executable_path/Каркасы
б) В «Фазе сборки-> Вставить библиотеки» убедитесь, что вы выбрали «Место назначения» в качестве «Исполняемые файлы» и Подпуть в качестве «Каркасы», отметили «Код подписи на копии»:
Настройка связывающего приложения iOS

Этот метод протестирован и используется с Xcode 9.2 и iPhone X. 

Дэвид

1
us_david

Установите для своей архитектуры значение по умолчанию, затем попробуйте следующее . 1. В фазе сборки-> Link Binary With Libraries добавьте библиотеки libz.dylib и libxml2.dylib в ваш проект. 2. В BuildSettings-> Search Paths установите для параметра «Всегда искать пути пользователя» значение «Да», а в разделе «Пути поиска платформы» добавьте правильный путь вашей платформы. Используйте терминал, чтобы получить правильный путь вашего фреймворка. 3. Попробуйте установить «Исходник компилятора как» на C++ или используйте пробную версию и проверьте все параметры. Complier Source As также находится под BuildSettings. Используйте cmd + f для поиска.

Попробуйте это и дайте мне знать, также расскажите мне о фреймворке или SDK, которые вы пытаетесь использовать в своем проекте. 

0
Nauman

C++ работает на iOS, вы можете просто добавить его в свой проект. Или, если вы действительно хотите иметь свою динамическую библиотеку, вы можете скомпилировать ее с помощью Xcode и указать целевую архитектуру. 

0
debug

Сценарий от Mobile Ben великолепен, за исключением последней строки, жестко кодирует имя файла библиотеки в сценарии. Следующее является заменой последней команды lipo и динамически использует липосакции для каждого файла библиотеки, созданного после компиляции.

Изменить линию мобильного Бена:

$LIPO -create $pwd/output/armv7/lib/libpresage.a  $pwd/output/armv7s/lib/libpresage.a $pwd/output/arm64/lib/libpresage.a $pwd/output/x86_64/lib/libpresage.a $pwd/output/i386/lib/libpresage.a -output libpresage.a

чтобы:

for t in `ls $pwd/output/armv7/lib/*.a` ;
do
        export LIB=`basename $t`
        export ARCHSTRING=""
        for a in `ls $pwd/output`
        do
                export ARCSTRING="$ARCSTRING $pwd/output/$a/lib/$LIB "
        done
        $LIPO -create $ARCSTRING  -output $LIB
done

Примечание. Я не хотел редактировать ответ Mobile Ben, добавив в него новый код и добавив его в качестве комментария, потеряв форматирование.

0
A.Badger