Дерево зависимостей CocoaPods tree

20.06.2020
3 мин

В прошлой части я упомянул об одной идее idea. Недавно мне потребовалось определить порядок поднятия зависимостей CocoaPods в рабочем проекте. В этом посте я расскажу как сделать это с помощью Ruby.

aim Цель

Есть большой проект со своим репозиторием зависимостей. Нужно поднять минорную или мажорную версию к примеру, Alamofire.

  • Скорее всего от такой популярной библиотеки зависит много других подов. Чтобы поднять версию в проекте, необходимо поднять версию Alamofire во всех зависимостях;
  • Все это усложняется тем, что под может не напрямую зависеть от Alamofire, а косвенно через другие зависимости;
  • Поднимать версии зависимостей нужно в определенной последовательности. Иначе ничего не выйдет;
  • У каждой зависимости может быть свой отдельный репозиторий. Из-за этого неудачная попытка поднять версию может затянуться;
  • CocoaPods показывает только одну ошибку несовместимости версий:
pods install
In Podfile:
Core (~> 2.1.4) was resolved to 2.1.4, which depends on Network (~> 2.0.1)
Authorization (~> 1.1.3) was resolved to 1.1.3, which depends on Network (~> 1.1.0)

idea Идея

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

Для этого нам нужно:

  • Найти все зависимости (к счастью, они уже есть в Podfile.lock);
  • Построить дерево зависимостей;
  • Построить поддерево, корнем которого будет указанный под;
  • Нарисовать это дерево.

Ниже я очень поверхностно опишу идею работы такой утилиты. Если у вас останутся вопросы, то смело задавайте их мне в Twitter. Также я не претендую на оптимальное решение и буду рад если вы предложите улучшение.

ruby Выбор языка

Для решения этой задачи я выбрал язык Ruby.

  • Он проще чем Bash;
  • Утилиту можно легко отредактировать и запустить;
  • Не нужно компилировать;
  • Также можно использовать через Fastlane rocket.

bricks Разбор Podfile.lock

Для начала нам нужно разобрать часть Podfile.lock. Пропускаем все зависимости без версий, так как их поднимать не надо. Все зависимости вида Moya/Core считаем одной зависимостью Moya. И затем строим дерево.

Рассмотрим небольшой пример. Получаем дерево с корнем Alamofire. У корня один ребенок Moya. Также ребенок знает, что его родитель Alamofire.

PODS:
- Moya/Core (13.0.1): # Учитываем лишь основное название Moya
- Alamofire (~> 4.1)
- Result # Пропускаем без версии
- Alamofire (4.8.2)

map Шаг за шагом

Когда дерево готово, нам нужно построить в нем поддерево с указанным подом в корне. Для этого сначала определим всех родителей указанного пода. Затем будем рассматривать детей каждого родителя и пытаться найти пересечение детей и остальных родителей. Если пересечения нет, то это означает у нас нет блокирующих зависимостей.

Другими словами, мы можем поднять версию у рассмотренного родителя на первом шаге. Затем исключаем таких родителей и повторяем поиск уже второго шага. И так далее.

Рассмотрим простой пример. Родители Alamofire: Moya и Network. У Network есть ребенок Moya, который пересекается с родителями. А у Moya пересечения нет. Поэтому на первый шаг попадет Moya, а на второй Network.

PODS:
- Alamofire (1.0.0)
- Network (1.0.0):
- Moya (= 1.0.0)
- Alamofire (= 1.0.0)
- Moya (1.0.0):
- Alamofire (= 1.0.0)

tree Дерево из шагов

Чтобы построить дерево, нужно после определения всех зависимостей для текущего шага рассмотреть детей у каждой зависимости. Найти таких детей, которые были определены на предыдущем шаге. Они станут родителями для рассматриваемой зависимости.

И снова небольшой пример. Воспользуемся данными из секции выше. Alamofire в корне. Затем на первом шаге у нас Moya, так как её ребенком является Alamofire и он есть на прошлом шаге (в корне). Затем Network, так как у него два ребенка Alamofire и Moya. Но только Moya находится на предыдущем шаге.

Alamofire ➡︎ Moya ➡︎ Network

draw Рисуем

Задача уже решена, но осталось нарисовать дерево. Для этого будем спускаться от корня вниз. На каждом шаге перебираем всех детей и запускаемся от них рекурсивно. И добавим немного красок.

Alamofire
Moya
Network

rocket Как использовать

Финальное решение я выложил на GithubGist. Также я добавил подробную справку по использованию.

ruby PodTree.rb help
Tiny utility for visualise Pod dependencies tree.
Skips subspecs and pods without versions.
Help options: ["-h", "--help", "help"]
• 1st argument will be used as root Pod
• 2nd one is path to Podfile (pwd by default)
ruby PodTree.rb A Podfile.lock
Podfile.lock: Output:
- A (1.0.0) A
- B (1.0.0): D
- A (= 1.0.0) C
- C (= 1.0.0) B
- D (= 1.0.0) E
- C (1.0.0):
- A (= 1.0.0)
- D (= 1.0.0)
- D (1.0.0):
- A (= 1.0.0)
- E (1.0.0):
- A (= 1.0.0)
- D (= 1.0.0)

party Результат

  • Проблема решена;
  • Дерево tree посажено нарисовано;
  • Рад был поделиться решением с вами;
  • Напишите мне, если у вас есть вопросы или идеи idea по улучшению.

Всем спасибо за внимание! Надеюсь пост вам понравился. А на этом пока всё.
Начинаю проходить Last of Us 2 и скоро буду смотреть WWDC 2020 popcorn.


💬 Пожалуйста, оставьте любой фидбек в этом твите.
Это поможет мне улучшить посты и продолжить писать новые.