Боремся с The ‘Pods...’ target has transitive dependencies that include static binaries

 
 

Суть проблемы следующаяя

  1. Используем CocoaPods (про Carthage знаю, по некоторым причинам не хочу использовать)
  2. Проект на Swift
  3. Имеет swift’овые зависимости, поэтому use_frameworks!
  4. Имеет старые Objective-C’шные зависимости, в которых есть транзитивные зависимости в которых есть статические либы (something.a, можно в vendored_libraries)

При попытке сделать pod install получаем

The ‘Pods-...’ target has transitive dependencies that include static binaries

Сие подробно дискутируется тут и тут

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

pre_install do |installer|
    def installer.verify_no_static_framework_transitive_dependencies; end
end

Который просто напросто отключает проверку.

P.S. Правда сий хак не всегда помогает :( и вы можете получить unresolved symbols

P.P.S. Если же вы все-таки хотите transitive static binaries и use_frameworks! то решение существует - необходимо этот transitive static binary обернуть руками в framework а в под зашить vendored_frameworks примерно следующим образом

Нам нужен скрипт создания фреймворка (будем показывать на примере OpenSSL for iPhone)

create-openssl-framework.sh
#!/bin/sh

FWNAME=openssl

if [ ! -d lib ]; then
    echo "Please run build-libssl.sh first!"
    exit 1
fi

if [ -d $FWNAME.framework ]; then
    echo "Removing previous $FWNAME.framework copy"
    rm -rf $FWNAME.framework
fi

if [ "$1" == "dynamic" ]; then
    LIBTOOL_FLAGS="-dynamic -undefined dynamic_lookup"
else
    LIBTOOL_FLAGS="-static"
fi

echo "Creating $FWNAME.framework"
mkdir -p $FWNAME.framework/Headers
libtool -no_warning_for_no_symbols $LIBTOOL_FLAGS -o $FWNAME.framework/$FWNAME -install_name @rpath/$FWNAME.framework/$FWNAME lib/libcrypto.a lib/libssl.a
cp -r include/$FWNAME/* $FWNAME.framework/Headers/
echo "Created $FWNAME.framework"

ВАЖНО: чтобы имелся параметр -install_name … так как без него у вас все соберется, но при запуске будет Library not loaded: Image not found

Сам же podspec файл будет выглядеть примерно следующим образом

{
  "name": "OpenSSL-for-iOS",
  ...
  "prepare_command": "./build-libssl.sh\n./create-openssl-framework.sh\n",
  "ios": {
    "vendored_frameworks": "openssl.framework"
  },
  ...
}

И далее цепляете под себе в Podfile обычным образом

pod 'OpenSSL-for-iOS'

С включенным use_frameworks!

Путь до сборочной директории xCode

 

Тут:

~/Library/Developer/Xcode/DerivedData

GIT для пользователей SVN

 

Записал слайдкаст с краткими замечаниями по GIT’у для пользователей SVN

Создание новых веток в SVN при работе с GIT-SVN

 

Если вам требуется создать ветку в SVN, но при этом вы работаете в GIT, то ситуация сначала может показаться странной, ведь если вы создали ветку от svn/trunk то svn dcommit будет отправлять изменения в svn/trunk а не в вашу ветку. Проблема решается достаточно просто, вот рецепт:

$ git svn branch foo
$ git checkout -b foo -t svn/foo
$ ...
$ git commit
$ git svn dcommit

Передача веток и коммитов между двумя GIT-репозиториями

 

Случилась такая задача. Есть два git-репозитория (один из которых ко всему прочему соединен с SVN через git-svn). Между ними необходимо передавать ветки и коммиты. Если бы они имели прямую связь через файловую систему или например через http, особых проблем бы не было. Однако, никакой связи кроме человека с флэшкой между этими репозиториями нет. Поэтому пришлось изобретать некий workflow который позволил бы эффективно эту проблему решить.

Вот картинка того, что требуется сделать:

branches pass between isolated repos

Решить эту проблему можно тремя способами:

  • Скопировать весь локальный репозиторий одной стороны (например, Repo A) и перенести его на флэшке рядом с репозиторием (Repo B) чтобы между ними можно было установить прямую связь через локальную файловую систему
  • Использовать механизм патчей (git am)
  • Использовать механизм пакетов git’а (git bundles)

Первый способ нам не подошел. Хотя бы потому, что репозиторий весит около 1.5 ГБ в сжатом виде. А заливать его приходилось в том числе через RDP соединение. Хотя если у вас есть такая возможность – это самый правильный и самый лучший вариант. Нам увы не подошел, поэтому идем дальше.

Второй способ мы даже активно использовали. Workflow там примерно следующий. С какого-то определенного коммита мы делаем набор патчей для каждого коммита выбранной ветки. На другом репозитории мы переключаемся (или создаем если такой ветки еще нет) на нужную ветку и делаем Apply Patch Serial. В принципе это все работает, но есть проблемы. Проблема первая – фактически в двух репозиториях мы имеем две разные ветки, хоть они одинаково и называются. И содержат разные коммиты, хоть и они содержат одно и то же. Кроме того, возникают нетривиальные вещи связанные с разрешением коллизий. Вообщем, достаточно громоздко и сложно, хотя и работает.

Третий способ – git bundles. Вот его и рассмотрим.

Начнем с того, что git bundle – это такой специальный файл. В который во-первых упакованы нужные ветки и нужные коммиты (вы их указываете сами). Во-вторых, он может представляться как удаленный git-репозиторий, который можно добавить в remotes и работать с ним как с полноценным удаленным репозиторием. Скажем так – этот подход – лайт версия первого подхода, когда вы тащите за собой весь репозиторий. Только здесь вы тащите один файл с тем, что надо. Места он правда может занять тоже весьма нехило, но в общем гораздо меньше (при правильном подходе), чем весь репозиторий.

Теперь давайте рассмотрим как это дело провернуть. Предположим у вас есть репозиторий A из которого необходимо перетащить ветку в репозиторий B (который пуст).

Имеем несколько файлов в репозитории A:

repo a

И ветку master:

master branch

Создание бандлов

Теперь нам нужно создать (пересоздать) бандл. Тут два пути – если он уже был создан когда-то, или же его еще не было.

Случай, когда мы создаем новый бандл

# Создаем новый бандл для ветки мастер (веток может быть несколько)
$ git bundle create ../master.bundle master
# Помечаем последний коммит на ветке мастер который мы себе забрали (чтобы в следующий раз не тащить все)
# lastR2bundle - просто некоторое имя
$ git tag -f lastR2bundle master

Случай, если бандл уже создавали (и появились новые изменения)

bundle been created

# Создаем бандл, но уже с указанием места, с которого его создавали в предыдущий раз
$ git bundle create ../master2.bundle lastR2bundle..master
# Обновляем указатель последнего коммита бандла
$ git tag -f lastR2bundle master

Разворачивание бандлов

Теперь бандл у нас есть, необходимо развернуть (обновить) его на другом репозитории RepoB.

В случае если бандл мы принесли первый раз и репозитория нет.

Тогда можно просто склонировать репозиторий прямо с бандла:

# Клонируем репозиторий с бандла в текущую папку
$ git clone -b master C:/Temp/gittest/master.bundle .

Ветка автоматически разворачивается до текущего состояния (флаг -b master указывает нужную ветку)

branch updated

Случай, когда бандл принесли первый раз, но репозиторий уже есть

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

# создаем новый remote с именем RepoA
[remote "RepoA"]
# указываем путь до бандла
url = C:/Temp/gittest/master.bundle
# указываем способ получения веток из бандла
fetch = +refs/heads/*:refs/remotes/RepoA/*

После этого делаем

$ git pull RepoA master

И получаем выгруженную историю из бандла.

Случай, когда бандл уже приносили

В этом случае достаточно заменить старую версию бандла новым, и сделать git pull. Бандлы, сделанные не с самого «начала времен» а с определенного места занимают не так много места.

Надеюсь всем было все понятно и это поможет вам вести удобную разработку с использованием git’а.

P.S. Механизм бандлов работает в обе стороны, изменения в нашей схеме можно переносить не только из RepoA в RepoB, но и наоборот.