Перейти к содержимому






Фотография
* * * * * 2 голосов

Разрешение зависимостей в пакетном менеджере с версиями

Написано Fingercomp , в Пакетные менеджеры, Tutorials 23 Октябрь 2016 · 364 просмотров

programming algorithms package dependency resolve
Разрешение зависимостей в пакетном менеджере с версиями

В прошлой части:

Вы-то прогу скопировать/разархировать и сами можете, вот только если программа зависит от другой, а та — от двух других, и т. д., вам это надоест. Людям надоело. Создали пакетные менеджеры.

 

 

Итак, давайте сделаем программу для установки пакетов. Очевидно, что просто так в рандомном порядке пакеты поставить нельзя: нам надо сначала брать пакеты без неразрешённых зависимостей и подниматься вверх.

 

 

Итак, у нас есть простая функция, которая составляет список пакетов для последовательной установки без ломаний....

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

 

Зачем нужны версии? Нуууу, наверное, чтобы отмечать разные варианты кода.
Зачем ПМ нужны версии? Хм, чтобы быть уверенным, что при установке пакета, зависимости будут именно такие, которые были при написании кода.

 

Ну, то есть. Вчера у нас была одна функция сделатьХорошо() и попала в релиз 1.0.0, а сегодня она переименована в сделатьПлохо(), сменили код этой функции, да ещё впридачу накинули ничегоНеСделать(). Всё это в релизе 2.0.0. Но если новый код будет использовать ничегоНеСделать(), то его версия 1.0.0 не устроит — там ведь функции нет. А если нужно будет сделатьХорошо(), то в версии 2.0.0 её уже не будет.

 

Люди — существа чертовски изобретательные, так что форматы версий у нас тоже всяко-разно изобретательны. Начиная от даты релиза (20161023) и номером билда (1243, она ещё ревизией зовётся), заканчивая полноценным семантическим версионированием. Первые потуги нас интересовать будут не сильно, а вот про SemVer можно поговорить.

 

Спецификация SemVer прямо говорит: версия задаётся тремя числами, разделённые точкой. Первое число — мажорная версия, увеличивается, когда происходят "ломающие" изменения типа удаления старых функций, второе — минорная версия, которая на новых фичах обычно увеличивается, а третье — патчи, это всякие багфиксы.

 

Вот сферическая версия в вакууме: 1.2.3.

 

На самом деле, в описании semver задано несколько правил, например, место пререлиза (например, 1.2.3-dev), метаданных (например, 1.2.3+build-15+20161023+amd64), ну и т.д. Если интересно — можете почитать, ссылочка в конце.

 

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

 

Начнём с манифеста. Дополним его указанием версий:

{ "name": "pkg1"
, "versions":
  [ { "number": "1.0.0"
    , "files":
      [ { "url": "http://example.com/pkg1/1.0.0/file1", "path": "/opt/pkg1/file1" }
      , { "url": "http://example.com/pkg1/1.0.0file2", "path": "/opt/pkg1/file2" }
      ]
    , "depends":
      [ { "name": "pkg11", "version": "^1" }
      , { "name": "pkg12", "version": "1.6.2" }
      ]
    }
  ]
}
Ну и по аналогии с другими пакетами. Вот такое чудо у нас должно получиться.

Изображение

 

Напомню, чем у нас закончилась прошлая часть:

def resolveDeps(name, resolved=None, unresolved=None):
  resolved = resolved or []
  unresolved = unresolved or []
  if name in unresolved:
    raise ValueError("circular dependencies detected")
  if name in resolved:
    return resolved
  unresolved.append(name)
  if not isInstalled(name):
    manifest = getManifest(name)
    for dep in manifest["deps"]:
      resolveDeps(dep, resolved, unresolved)
    resolved.append(name)
  del unresolved[unresoved.index(name)]
  return resolved
Давайте перепишем эту функцию так, чтобы она работала после наших изменений в манифесты:
def resolveDeps(name, resolved=None, unresolved=None):
  resolved = resolved or []
  unresolved = unresolved or []
  if name in unresolved:
    raise ValueError("circular dependencies detected")
  if name in resolved:
    return resolved
  unresolved.append(name)
  if not isInstalled(name):
    manifest = getManifest(name)
    version, data = getLatestVersion(manifest)  # получаем последнюю версию
    for dep in latest["deps"]:
      resolveDeps(dep["name"], resolved, unresolved)
    resolved.append({"name": name, "version": version)
  del unresolved[unresoved.index(name)]
  return resolved
Всё хорошо и замечательно, вот только толку от того, что мы ввели версии, как-то нет совсем.

 

А вот далее нам потребуется очень серьёзная либа-парсер семверов. На Python есть semantic_version, которую я ещё портировал на MoonScript — очень доволен. Но это так, будем пока плавать на более высоком уровне абстракции.

 

Итак, версии. Тот самый граф, ещё раз:

Изображение

 

Около стрелочек висят какие-то штуки, ^1, например. Эти штуки, которые мы ещё вписываем в манифесты пакетов, ограничивают варианты версий пакетов, которые можно поставить. ^1 говорит, что можно брать любую версию не менее 1.0.0 и не более следующего мажорного релиза (2.0.0). * говорит, что пофигу абсолютно, какая версия встанет. А точное указание версии, как, например, в случае с 1.6.2, не даёт установиться какой-либо другой версии.

 

Ну а так как они ограничивают, то и называются они ограничениями (или constriants). Пакетный менеджер — скажем так, классическая задача о соблюдении ограничений. Отнюдь не простая.

 

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

def resolveDeps(name, vconstraint="*" resolved=None, unresolved=None):
  resolved = resolved or []
  unresolved = unresolved or []
  if name in unresolved:
    raise ValueError("circular dependencies detected")
  if name in resolved:
    return resolved
  unresolved.append(name)
  # Создаём объект ограничения из строки
  vconstraint = createSemVerConstraint(vconstraint)
  if not isInstalled(name):
    manifest = getManifest(name)
    version, data = vconstraint.match(manifest)  # получаем версию, соответствующую ограничению
    for dep in data["deps"]:
      resolveDeps(dep["name"], dep["version"], resolved, unresolved)  # и не забываем передавать версию требуемую
    resolved.append({"name": name, "version": version)
  del unresolved[unresoved.index(name)]
  return resolved
При запуске resolveDeps("pkg1") мы теперь получим
[ { "name": "pkg1-1-1"
  , "version": "1.0.1"
  }
, { "name": "pkg1-1"
  , "version": "1.2.4"
  }
, { "name": "pkg1-2"
  , "version": "1.6.2"
  }
, { "name": "pkg1"
  , "version": "1.2.3"
  }
]
Вот как это на графе будет:

Изображение

 

Давайте теперь приспособим функцию install к установке:

def install(name):
  depList = resolveDeps(name)
  for pkg in depList:
    manifest = getManifest(pkg["name"])
    data = getVersion(manifest, pkg["version"])
    for file in data["files"]:
      download(file["url"], file["path"]
В общем-то, это всё. У нас есть вполне рабочая функция установки пакетов с учётом версии по Semantic Versioning. Однако у неё есть некоторые проблемы.

 

Ну, во-первых, мы ошибочно считаем, что если пакет установлен, то у него нужная версия. Если у нас уже будет пакет версии 1.12.53, а мы потребуем ^2, то новая версия не поставится. Рискуем попасть на глюки, баги. Кажется, надо просто обновить пакет!..

 

Но при таком решении невозможно организовать обновление пакетов.
Вообще. Никак. А почему?

 

У нас зависимости задаются в версиях. Каждая версия может сменить зависимости. При этом каждая новая версия может не соблюдать ограничения, которые дают зависимые от данного пакеты.

 

А вот вам адская ситуация:

Изображение

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

 

А пока можете почитать спецификацию SemVer.

 

Изображение.



  • Totoro, LeshaInc, Saintmare и еще 1 это нравится



Какая-то сложная жесть.

Короче - Фингер опять норкоманит.

|

Уууу... СЛОЖНА И НИПАНЯТНА!

Лучше бы вообще не читал, чем оставлял идиотские комменты пятилетнего ребёнка.

    • Saintmare это нравится

Классно оформлено, текст интересно читать, респект с:

Теперь буду знать немнооожко больше.


Обратные ссылки на эту запись [ URL обратной ссылки ]

Обратных ссылок на эту запись нет

Последние версии

Стабильная: 1.6.1+hotfix.1
Последняя: 1.6.1+hotfix.1

Мои программы

Автокрафт на OpenComputers и AE

http://computercraft...-opencomputers/

 

Минималистичные и красивые часы

http://computercraft...0-finger-clock/

 

Чат на GoogolGlasses

http://computercraft...-googolglasses/

 

Лампомат

http://computercraft...-oc-lamp-o-mat/

 

Автокрафт 2: без АЕ

http://computercraft...2-teper-bez-ae/

 

Клиент Гиста

http://computercraft...-opencomputers/

 

Управление нанитами

http://computercraft...nanitami-snova/

 

Чат на OpenPeripheral а-ля IRC

http://computercraft.ru/topic/1517-

Искать в моем блоге

Новые записи

Новые комментарии

Январь 2017

В П В С Ч П С
1234567
891011121314
1516 17 18192021
22232425262728
293031    

Полезные ссылки

Официальный сайт OpenComputers

http://oc.cil.li/

 

Официальный форум OpenComputers

http://oc.cil.li/index.php?/index

 

Репозиторий OpenComputers на ГитХабе

https://github.com/M...s/OpenComputers

 

Последние релизы OpenComputers

https://github.com/M...puters/releases

 

Jenkins-сервер

http://ci.cil.li/job...s-dev-MC1.7.10/
 

Репозиторий OpenPrograms

https://github.com/OpenPrograms

 

Репозиторий моих программ

https://github.com/O...rcomp-Programs/

0 посетителей

0 пользователей, 0 гостей, 0 анонимных

Последние посетители

  • Фотография
    Alex
    Вчера, 02:53
  • Фотография
    vx13
    15 янв 2017 - 20:09
  • Фотография
    Stanislavich
    14 янв 2017 - 23:24
  • Фотография
    Xytabich
    13 янв 2017 - 14:30
  • Фотография
    Litvinov
    13 янв 2017 - 03:24
  • Фотография
    ivan52945
    12 янв 2017 - 23:11
  • Фотография
    electronic_steve
    12 янв 2017 - 21:11
  • Фотография
    Stirn
    12 янв 2017 - 19:48
  • Фотография
    Programist135
    12 янв 2017 - 18:44
  • Фотография
    MeXaN1cK
    12 янв 2017 - 18:29
  • Фотография
    _Star_Craft_
    12 янв 2017 - 17:53
  • Фотография
    Saintmare
    11 янв 2017 - 08:26
  • Фотография
    Asior
    11 янв 2017 - 07:45
  • Фотография
    Fedel
    11 янв 2017 - 03:30
  • Фотография
    Cleveron
    10 янв 2017 - 20:28
  • Фотография
    slavik95_ua
    10 янв 2017 - 01:28
  • Фотография
    LeshaInc
    08 янв 2017 - 14:52
  • Фотография
    ALeXeR
    08 янв 2017 - 11:00
  • Фотография
    cyber01
    07 янв 2017 - 23:16
  • Фотография
    POMAH3
    07 янв 2017 - 19:44
  • Фотография
    incvIZ
    07 янв 2017 - 17:31
  • Фотография
    Totoro
    07 янв 2017 - 15:48
  • Фотография
    qwertyMAN
    07 янв 2017 - 12:52
  • Фотография
    Sharplook
    07 янв 2017 - 00:10
  • Фотография
    SDV
    05 янв 2017 - 21:39
  • Фотография
    cloud
    05 янв 2017 - 14:16
  • Фотография
    mrlobaker
    04 янв 2017 - 16:19
  • Фотография
    Kartograf
    04 янв 2017 - 13:43
  • Фотография
    DimaZCOM
    03 янв 2017 - 21:29
  • Фотография
    mrGreen
    02 янв 2017 - 18:43
  • Фотография
    Nikitat
    02 янв 2017 - 14:51
  • Фотография
    Zabqer
    02 янв 2017 - 12:17
  • Фотография
    prostoshu
    02 янв 2017 - 09:32
  • Фотография
    Vait
    01 янв 2017 - 13:17
  • Фотография
    YuRaNnNzZZ
    30 дек 2016 - 20:58
  • Фотография
    Kid
    29 дек 2016 - 15:38
  • 26 дек 2016 - 11:07
  • Фотография
    LuaGamer
    26 дек 2016 - 08:23
  • Фотография
    Misha123
    25 дек 2016 - 17:23
  • Фотография
    DarthWirthe
    25 дек 2016 - 16:09
  • Фотография
    Quant
    24 дек 2016 - 11:42
  • Фотография
    NEO
    23 дек 2016 - 20:29
  • Фотография
    VankaPapanka
    23 дек 2016 - 12:26
  • Фотография
    GeorgSorok16
    23 дек 2016 - 10:33
  • Фотография
    HalfLolek
    21 дек 2016 - 14:21
  • Фотография
    i_ivanov10
    21 дек 2016 - 08:26
  • Фотография
    lLuffy
    21 дек 2016 - 03:42
  • Фотография
    SGMack
    19 дек 2016 - 17:45
  • Фотография
    Arseniy10
    19 дек 2016 - 16:14
  • Фотография
    newbie
    19 дек 2016 - 15:11

Лицензия


Яндекс.Метрика