вторник, 19 апреля 2016 г.

[prog] Mxx_ru-1.6.10 с обновлением MxxRu::externals

Mxx_ru обновился до версии 1.6.10 (актуальная точная версия на данный момент 1.6.10.1)

Установить Mxx_ru можно командой gem install Mxx_ru

Обновить Mxx_ru можно командой gem update Mxx_ru

Так же Mxx_ru можно загрузить с SourceForge (gem-файл).

В версии 1.6.10 MxxRu::externals теперь может самостоятельно обнаруживать, что изменилось в файле описаний внешних зависимостей и предпринимать соответствующие действия. Подробнее под катом.

Предшествующие версии MxxRu::externals не могли автоматически удалять устаревшие зависимости и устанавливать вместо них новые. Например, пусть я изначально определил в своем externals.rb вот такую внешнюю зависимость:

MxxRu::arch_externals :libmosquitto do |e|
  e.url 'http://mosquitto.org/files/source/mosquitto-1.4.8.tar.gz'

  e.map_dir 'lib' => 'dev/libmosquitto'
  e.map_file 'config.h' => 'dev/libmosquitto/*'
end

Установил ее и некоторое время с ней нормально работал. Но затем оказалось, что мне нужно поменять эту зависимость на немного другую:

MxxRu::arch_externals :libmosquitto do |e|
  e.url 'https://github.com/eao197/mosquitto/archive/v1.4.8-p1.tar.gz'

  e.map_dir 'lib' => 'dev/libmosquitto'
  e.map_file 'config.h' => 'dev/libmosquitto/*'
end

Чтобы это сделать мне нужно было поступить следующим образом:

# Удаляю старую зависимость.
rake -f externals.rb libmosquitto:remove
# Меняю описание зависимости.
vim externals.rb
# Устанавливаю новую зависимость.
rake -f externals.rb libmosquitto

Это само по себе не очень удобно, т.к. можно забыть сделать remove для старой версии зависимости. А уж при командной работе можно вообще нарваться на то, что коллега по разработке исправил externals.rb, ты обновился из репозитория и только после того, как у тебя уже оказалась новая версия externals.rb обнаружил, что тебе нужно переходить на новую версию зависимости.

Версия Mxx_ru-1.6.10 решает эту проблему. Правда ценой изменения способа работы с файлом externals.rb.

Теперь для работы с внешними зависимостями вместо rake -f externals.rb нужно запускать специальную утилитку mxxruexternals, которая устанавливается автоматически вместе с gem-ом Mxx_ru-1.6.10.

Сейчас нужно запускать так: mxxruexternals -f externals.rb.

Либо, если описания внешних зависимостей лежат в файле с именем externals.rb, то просто mxxruexternals без параметров. Имя externals.rb будет использовано по умолчанию.

Утилита mxxruexternals поддерживает три типа операций: install (по-умолчанию), remove и reget. Т.е., если нужно установить все зависимости, то:

mxxruexternals install
# Или просто:
mxxruexternals

Если нужно удалить все зависимости, то:

mxxruexternals remove

Если нужно полностью переустановить все зависимости, то:

mxxruexternals reget

Управления отдельными зависимостями, как это было в предыдущих версиях, больше нет. Сейчас mxxruexternals сам может понять, что происходит с отдельными зависимостями.

Допустим, я проделываю первую инсталляцию внешней зависимости libmosquitto (как показано выше -- оригинальную версию от разработчика mosquitto). Получаю что-то вроде:

[Info] Generation of auxilary rakefile: ./mxxruexternals-temp20160419-1928-1qchow0
[Info] Launching rake with: rake -f ./mxxruexternals-temp20160419-1928-1qchow0 install
mkdir -p .externals
mkdir .externals/libmosquitto.5896.20014960
cd .externals/libmosquitto.5896.20014960
wget v.1.17.1 found
wget -O mosquitto-1.4.8.tar.gz http://mosquitto.org/files/source/mosquitto-1.4.8.tar.gz
--2016-04-19 15:35:15--  http://mosquitto.org/files/source/mosquitto-1.4.8.tar.gz
Resolving mosquitto.org (mosquitto.org)... 85.119.83.194, 2001:ba8:1f1:f271::2
Connecting to mosquitto.org (mosquitto.org)|85.119.83.194|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 326120 (318K) [application/octet-stream]
Saving to: 'mosquitto-1.4.8.tar.gz'

mosquitto-1.4.8.tar.gz        100%[=================================================>] 318.48K   399KB/s    in 0.8s

2016-04-19 15:35:16 (399 KB/s) - 'mosquitto-1.4.8.tar.gz' saved [326120/326120]

tar x -f mosquitto-1.4.8.tar.gz
rm mosquitto-1.4.8.tar.gz
...
mv .externals/libmosquitto.5896.20014960 .externals/libmosquitto
mkdir -p dev/libmosquitto
cp -r .externals/libmosquitto/lib dev/libmosquitto/lib
cp .externals/libmosquitto/config.h dev/libmosquitto/config.h
cp externals.rb .externals/previous.rb

Здесь mxxruexternals написал, что он сгенерировал новый вспомогательный rake-скрипт с уникальным именем и запустил rake с этим скриптом. Далее можно увидеть след загрузки и установки новой зависимости libmosquitto.

Самой важной в контексте данного разговора является последняя строка:
cp externals.rb .externals/previous.rb

Благодаря тому, что mxxruexternals скопировал файл с описаниями зависимостей к себе, он сможет затем определять, изменилось ли что-нибудь в описаниях зависимостей. Допустим, что я меняю описание libmosquitto в своем externals.rb и вместо оригинальной версии от разработчика mosquitto, указываю собственную, пропатченную мной версию с github-а. Запускаю mxxruexternals с обновленным файлом externals.rb и получаю что-то вроде:

[Info] Generation of auxilary rakefile: ./mxxruexternals-temp20160419-6440-1jhchn1
[Info] Launching rake with: rake -f ./mxxruexternals-temp20160419-6440-1jhchn1 install
[Note] Externals description changed from the last time
[Note] Contents of .externals/previous.rb will be used
[Note] libmosquitto previous rules found
[Note] libmosquitto previous rules differs with current
rm dev/libmosquitto/config.h
rm -r dev/libmosquitto/lib
rm -r .externals/libmosquitto
mv .externals/previous.rb .externals/previous.rb.1.bak
mkdir .externals/libmosquitto.4648.20342640
cd .externals/libmosquitto.4648.20342640
wget v.1.17.1 found
wget -O v1.4.8-p1.tar.gz https://github.com/eao197/mosquitto/archive/v1.4.8-p1.tar.gz
--2016-04-19 15:41:55--  https://github.com/eao197/mosquitto/archive/v1.4.8-p1.tar.gz
Resolving github.com (github.com)... 192.30.252.130
Connecting to github.com (github.com)|192.30.252.130|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/eao197/mosquitto/tar.gz/v1.4.8-p1 [following]
--2016-04-19 15:41:56--  https://codeload.github.com/eao197/mosquitto/tar.gz/v1.4.8-p1
Resolving codeload.github.com (codeload.github.com)... 192.30.252.161
Connecting to codeload.github.com (codeload.github.com)|192.30.252.161|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: 'v1.4.8-p1.tar.gz'

v1.4.8-p1.tar.gz                  [    <=>                                           ] 284.09K   346KB/s    in 0.8s

2016-04-19 15:41:57 (346 KB/s) - 'v1.4.8-p1.tar.gz' saved [290908]

tar x -f v1.4.8-p1.tar.gz
rm v1.4.8-p1.tar.gz
...
mv .externals/libmosquitto.4648.20342640 .externals/libmosquitto
cp -r .externals/libmosquitto/lib dev/libmosquitto/lib
cp .externals/libmosquitto/config.h dev/libmosquitto/config.h
cp externals.rb .externals/previous.rb

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

[Note] Externals description changed from the last time
[Note] Contents of .externals/previous.rb will be used

Это примечание о том, что у mxxruexternals есть описание предыдущей версии внешних зависимостей и что она отличается от предыдущей. Поэтому mxxruexternals попробует использовать описание из предыдущей версии для того, чтобы отследить изменения. И тут же находит эти самые изменения:

[Note] libmosquitto previous rules found
[Note] libmosquitto previous rules differs with current
rm dev/libmosquitto/config.h
rm -r dev/libmosquitto/lib
rm -r .externals/libmosquitto

В предыдущих описаниях зависимостей было найдено описание для libmosquitto. И это описание было другим, оно отличается от того, что есть сейчас. Поэтому старая версия зависимости удаляется. А новая версия скачивается и устанавливается.

Допустим, я не вносил никаких изменений в externals.rb. Но случайно удалил какую-то часть установленной зависимости (файл config.h из libmosquitto). Просто запукаю mxxruexternals и получают что-то вроде:

[Info] Generation of auxilary rakefile: ./mxxruexternals-temp20160419-5080-196hqqs
[Info] Launching rake with: rake -f ./mxxruexternals-temp20160419-5080-196hqqs install
[Note] It seems there is no changes in externals description
[Note] Contents of .externals/previous.rb won't be used
cp .externals/libmosquitto/config.h dev/libmosquitto/config.h

Ничего в externals.rb не изменилось, поэтому предыдущая версия описаний зависимостей не используется.

Если я внесу в externals.rb какие-то косметические изменения, скажем, поменяю пробелы на табуляции. Контрольная сумма файла с описаниями изменится. Поэтому mxxruexternals попробует сравнить старое и новое описания:

[Info] Generation of auxilary rakefile: ./mxxruexternals-temp20160419-7904-k0b1e0
[Info] Launching rake with: rake -f ./mxxruexternals-temp20160419-7904-k0b1e0 install
[Note] Externals description changed from the last time
[Note] Contents of .externals/previous.rb will be used
[Note] libmosquitto previous rules found
[Note] libmosquitto previous rules the same with current
mv .externals/previous.rb.1.bak .externals/previous.rb.2.bak
mv .externals/previous.rb .externals/previous.rb.1.bak
cp externals.rb .externals/previous.rb

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

Если я уберу из externals.rb зависимость libmosquitto.rb и запущу mxxruexternals, то ранее установленая версия libmosquitto будет удалена:

[Info] Generation of auxilary rakefile: ./mxxruexternals-temp20160419-6004-ntmf0u
[Info] Launching rake with: rake -f ./mxxruexternals-temp20160419-6004-ntmf0u install
[Note] Externals description changed from the last time
[Note] Contents of .externals/previous.rb will be used
[Note] libmosquitto not present in the current version
[Note] libmosquitto WILL BE REMOVED
rm dev/libmosquitto/config.h
rm -r dev/libmosquitto/lib
rm -r .externals/libmosquitto
mv .externals/previous.rb.2.bak .externals/previous.rb.3.bak
mv .externals/previous.rb.1.bak .externals/previous.rb.2.bak
mv .externals/previous.rb .externals/previous.rb.1.bak
cp externals.rb .externals/previous.rb

Резюмируя можно сказать, что mxxruexternals теперь знает, что было установлено в последний раз и чем оно отличается от текущей версии описаний зависимости. Если какая-то зависимость больше не представлена в текущей версии описания, то эта зависимость удаляется. Если зависимость представлена, но как-то изменилась (поменялся url зависимости или же изменился список параметров, или же поменялись значения в методах map_dir/map_file), то она переустанавливается.

При этом, если какая-то зависимость удалена из текущей версии описаний, то эта зависимость будет удалена при выполнении любых операций (будь то install, remove или reget). Так что даже если у вас изначально список зависимостей состоял из A, B, C, D и E, а потом вы поменяли его на I, G, K и сразу же запустили mxxruexternals с командой reget или remove, то mxxruexternals все равно удалит A, B, C, D и E, даже если про них нет ни слова в текущей версии externals.rb.

Еще одно нововведение в версии 1.6.10 -- это объявление метода map как deprecated. Использовать этот метод все еше можно, но mxxruexternals будет ругаться и просить заменить map на map_dir.

Формат описания зависимостей не изменился. Т.е. старые файлы externals.rb будут поддерживаться без каких-либо изменений. Единственное, что следует запомнить -- это использовать mxxruexternals вместо прямого вызова rake.


Для MxxRu::externals эти изменения являются довольно большой фичей. Удобство использования MxxRu::externals должно сильно возрасти. Но т.к. изменения серьезные, то наверняка где-то какие-то ошибки затаились. Так, в процессе подготовки данной заметки была найдена проблемка в версии 1.6.10 и была сделана версия 1.6.10.1 :) Поэтому нужно поробовать, если что-то будет вылезать, то буду править и обновлять. Тем более, что использование MxxRu::externals для управления зависимостями вовсе не требует использовать MxxRu для компиляции.

Есть еще две фичи, которые хочется добавить в MxxRu::externals, но до которых руки пока не дошли:

  • файлы-рецепты для того, чтобы проект мог описать, как же его устанавливать в виде внешней зависимости. По сути, файл-рецепт может содержать инструкции вида:
    MxxRu::Externals::recipe do |e|
      e.option '-q'
      e.option '--native-eol''LF'

      e.map_dir 'dev/so_5/*' => e.target
      e.map_dir 'dev/sample/so_5/*' => e.target('sample')
    end
    И при описании зависимости можно сослаться на этот рецепт:
    MxxRu::svn_externals :so5 do |e|
      e.url 'http://svn.code.sf.net/p/sobjectizer/repo/tags/so_5/5.5.16'
      e.recipe_file 'http://svn.code.sf.net/p/sobjectizer/repo/tags/so_5/5.5.16/mxxru_recipe.rb'
    end
    В принципе, тут идея более-менее понятная. Нужно только найти время и сделать :)
  • Рекурсивные зависимости. Т.е., я могу у себя в зависимости указать проект C, который в свою очередь зависит от проектов A и B. И вместо того, чтобы прописывать A, B и C в своем externals.rb, я бы просто указал проект C. После его загрузки mxxruexternals определил бы, что нужны еще и A+B. Скачал бы и установил еще и их.

    Проблема здесь в том, что в mxxruexternals нет сейчас понятия версии зависимости. И если C зависит от A-1.2.5, а еще один нужный мне проект D зависит от A-2.8.16, то mxxruexternals ничего с этим сделать не сможет. А должен заставить пользователя как-то разрулись эту ситуацию. В общем, тут еще есть над чем думать и работать.
Отправить комментарий