24 августа 2016

День рождения Ники

Сегодня Нике исполнилось бы пять лет.

Плюс-минус несколько дней, потому что точную дату ее рождения нам сказать не смогли…

11 ноября 2014

Щенки

щенки    Вот и закончилась наша небольшая история. Остались смешанные чувства — и радость и печаль, жизнь возвращается в прежнее русло. Но конец нашей истории — это начало четырёх других историй, радостных, интересных и наполненных любовью. Иначе и быть не может! ;)

27 сентября 2014

Преобразование XML в CSV

Недавно я нашёл интересный файл в исходниках пакета iso-codes. Вернее, даже несколько файлов. В файле iso_639.xml (просмотреть) есть двух- и трёхбуквенные коды языков и их полное наименование. А в файле iso_3166.xml (просмотреть) — двух- и трёхбуквенные коды стран и территорий и их полное наименование. Вот кусочек одного файла:

 <iso_639_entry
  iso_639_2B_code="afr"
  iso_639_2T_code="afr"
  iso_639_1_code="af"
  name="Afrikaans" />
 <iso_639_entry
  iso_639_2B_code="ain"
  iso_639_2T_code="ain"
  name="Ainu" />

Я пишу в основном на shell для busybox. И я не работал с форматом XML. Гораздо проще было бы обрабатывать простой текстовый файл, в котором то же самое было бы записано вот так:

|af|afr|afr|Afrikaans|
|  |ain|ain|Ainu|

Тогда при помощи grep можно было бы выбрать из файла строку с нужным языком, дальше при помощи cut в этой строке выделить наименование языка. А уже потом, при необходимости, можно это наименование перевести на другой язык, используя переводы из этого же пакета iso-codes. Вот так:

LANGUAGE_CODE="af"

LANGUAGE=$(grep -F "|$LANGUAGE_CODE|" /path/to/iso_639.tab | cut -d'|' -f5)

echo "$LANGUAGE_CODE : $LANGUAGE : $(gettext -d iso_639 $LANGUAGE)"

Дело осталось за малым — нужно сделать эти tab-файлы. Можно, конечно, использовать текстовый редактор и при помощи поиска с заменой, а потом еще и при помощи напильника выполнить задачу. Но это отнимает много времени, и всё придётся повторять при выходе новой версии пакета iso-codes. Нужно это как-то автоматизировать.

Первым делом в голову почему-то пришло название — xmllint. Я повозился с ним, погуглил, но так и не смог добиться цели. Максимум пользы от xmllint было в том, что если ему на вход подать xml-файл, то он его «облагородит»:

xmllint /path/to/iso_639.xml
 <iso_639_entry iso_639_2B_code="afr" iso_639_2T_code="afr" iso_639_1_code="af" name="Afrikaans" />
 <iso_639_entry iso_639_2B_code="ain" iso_639_2T_code="ain" name="Ainu" />

Так, уже лучше. Это уже почти то, что нужно — одно определение теперь занимает одну строку. Что ж, вот несложный скрипт в качестве «напильника»:

#!/bin/sh

TEMP=$(mktemp)
xmllint $1 | grep '<iso_639_entry' > $TEMP

    while read line; do
        iso_639_2B_code="$(echo $line | sed 's|.* iso_639_2B_code=\"\([^"]*\)\".*|\1|')"
        iso_639_2T_code="$(echo $line | sed 's|.* iso_639_2T_code=\"\([^"]*\)\".*|\1|')"
        iso_639_1_code="$(echo $line | grep iso_639_1_code | sed 's|.* iso_639_1_code=\"\([^"]*\)\".*|\1|')"
        name="$(echo $line | sed 's|.* name=\"\([^"]*\)\".*|\1|')"
        common_name="$(echo $line | grep common_name | sed 's|.* common_name=\"\([^"]*\)\".*|\1|')"

        [ "x$iso_639_1_code" == "x" ] && iso_639_1_code="  "

        echo "|$iso_639_1_code|$iso_639_2B_code|$iso_639_2T_code|$name|$common_name"

    done < $TEMP

Скрипт работает, выполняет своё дело. Но какой же он медленный!

real 0m 49.83s
user 0m 44.09s
sys 0m 12.40s

Да к тому же и процессор нагружает около 100%.

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

Я погуглил ещё на тему преобразования и трансформации XML и в конце-концов вышел на хороший пример, который пришлось только немного переделать под мой файл.

На сцену выходит утилита xsltproc. Она умеет преобразовывать файл XML в соответствии с правилами, заложенными в указанном файле XSL. Некоторая аналогия с информацией, находящейся в HTML и правилами в CSS. Да, как-то много лет назад я возился с XML и XSL но, конечно же, подробности забылись и тогда я не сделал каких-то особенных успехов. Получится ли сейчас? Да, к счастью, задача оказалась простой. Вот файл iso_639.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text" />
 <xsl:template match="/">
  <xsl:for-each select="//iso_639_entries/*">
   <xsl:text>|</xsl:text>
   <xsl:if test="not(@iso_639_1_code)">
    <xsl:text>  </xsl:text>
   </xsl:if>
   <xsl:value-of select="@iso_639_1_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@iso_639_2B_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@iso_639_2T_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@name" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@common_name" />
   <xsl:text>&#10;</xsl:text>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

Здесь для каждой записи выводятся через разделители «|» параметры iso_639_1_code, iso_639_2B_code, iso_639_2T_code, name, common_name. Причём iso_639_1_code и common_name могут отсутствовать. Я «для красоты» сделал так, если отсутствует iso_639_1_code (двухбуквенный код), то выводить два пробела. Так табличка выглядит ровной при просмотре глазами. Запускается преобразование командой:

xsltproc iso_639.xsl iso_639.xml > iso_639.tab

Вот его время работы:

real 0m 0.03s
user 0m 0.03s
sys 0m 0.00s

Чёрт! Впечатляет!

Почти так же был написан и файл iso_3166.xsl. Отличие только в том, что теперь файл XML состоит из двух частей. В первой части — действующие коды, а во второй — исторические, как например, СССР. Для исторических кодов применяется немного другой формат, например указано, когда этот СССР развалился и код перестал действовать.

Вот, собственно, и сам файл iso_3166.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text" />
 <xsl:template match="/">
  <xsl:for-each select="//iso_3166_entries/iso_3166_entry">
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@alpha_2_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@alpha_3_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@numeric_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@name" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@official_name" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@common_name" />
   <xsl:text>|</xsl:text>
   <xsl:text>&#10;</xsl:text>
  </xsl:for-each>

  <xsl:text>&#10;&#10;</xsl:text>

  <xsl:for-each select="//iso_3166_entries/iso_3166_3_entry">
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@alpha_4_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@alpha_3_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@numeric_code" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@date_withdrawn" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@names" />
   <xsl:text>|</xsl:text>
   <xsl:value-of select="@comment" />
   <xsl:text>|</xsl:text>
   <xsl:text>&#10;</xsl:text>
  </xsl:for-each>

 </xsl:template>
</xsl:stylesheet>

Ну вот и всё. Если мне когда-нибудь понадобится преобразовать XML в CSV, я буду знать что делать.

13 марта 2013

Как создаются пакеты SliTaz

Я хочу провести небольшую, но содержательную экскурсию на тему «От идеи до готового пакета».

Всё начинается с идеи, с желания видеть определенную программу в SliTaz.

Сделаем шаг в сторону. Существует множество дистрибутивов, основанных на пакетной базе Debian. Эти дистрибутивы просто берут у них готовые пакеты, возможно, перепаковывают в свой формат, но это не наш путь. В SliTaz имеется лишь небольшое количество «позаимствованных» пакетов. Для получения этих пакетов разработаны скрипты, названия которых начинаются на «get-»: вот их полный список. Во всех остальных случаях пакеты для SliTaz компилируются из исходного кода и упаковываются в самом SliTaz. Это позволяет компилировать программы выбранным компилятором, с выбранными опциями (иногда можно запретить или разрешить какую-то особенность программы, что в конечном итоге повлияет на полезность и размер программы), с точно совпадающим к нашим условиям набором системных библиотек (программа, скомпилированная в более новой среде в «чужом» дистрибутиве, будет требовать новые библиотеки).

Возвращаемся из этого закоулка на наш путь. Программы (пакеты) компилируются из исходников. Это можно сделать вручную, но тут больше рутины, чем творчества, а также присутствует элемент случайности и непредсказуемости. Количество готовых пакетов в SliTaz медленно, но верно приближается к 4 тысячам (3899). Был разработан инструмент, убирающий рутину из процесса создания пакетов. Он настолько удобный, что я уже давно ничего не компилирую вручную. Текущая инкарнация этого инструмента называется Cookutils. Это «сборочный бот». Он получает на входе «рецепт» с четкими указаниями и выдает на выходе готовый пакет.

Вот как выглядит рецепт для GPartEd: pkgs.slitaz.org/search.sh?receipt=gparted&version=cooking

Формат рецепта совместим с форматом shell-скриптов. Здесь мы видим комментарии, начинающиеся со знака «#», определение всех необходимых переменных и функций. В функции бота входят: загрузить архив с исходниками, распаковать его, установить все пакеты, необходимые для компиляции и сборки данного пакета, запустить { конфигурирование исходников, их сборку и установку }(по правилам, предусмотренным разработчиками пакета) [с учетом параметров, которые нам необходимы, например, устанавливать программу не в /usr/local/bin, а в /usr/bin], собрать из скомпилированных файлов кусочек файловой системы и создать из него пакет, после этого «подчистить за собой», убрав пакеты, которые были установлены перед компиляцией этой программы, вернув сборочную систему в исходное состояние.

Рутина убрана, осталось творчество ;)

Обычно, рецепты написаны таким образом, что достаточно изменить в нём номер версии в переменной VERSION для того, чтобы можно было собрать пакет с новой версией (если разработчики данной программы не привнесли туда чего-то кардинально нового, т.е. в случае минорных изменений).

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

Для хранения рецептов применяется система контроля версий Mercurial (Hg, ртуть). Это очень удобно, все изменения записываются, их можно окинуть взглядом и проанализировать. Вот так, например, выглядит «биография», история рецепта пакета GPartEd (начало внизу): hg.slitaz.org/wok/log/tip/gparted/receipt

Оказывается, ему уже 5 лет! И он вырос с версии 0.3.3 до 0.14.1 за это время.

Кстати, система Mercurial консольная, она установлена на одном из серверов проекта SliTaz, а то, что мы видели сейчас — это ее веб-интерфейс. Это очень удобно, потому-что веб-браузеры сейчас есть сейчас не то, что в мобилках, но даже в некоторых холодильниках! ;)

Mercurial позволяет разработчикам совместно выполнять свою работу. Достаточно установить себе Mercurial и сделать локальную копию хранилища рецептов (называется wok), а потом периодически обновлять локальную копию, чтобы получить изменения сделанные другими, а также вносить свои изменения в копию, находящуюся на сервере, чтобы наши изменения могли увидеть другие.

Нужно отметить, что имеется три версии wok: набор рецептов для текущей стабильной версии SliTaz (wok-stable), для разрабатываемой следующей версии (wok) и экспериментальный (wok-undigest, где допустимы любые эксперименты с «отклонением от генеральной линии партии»).

Остался один, вполне логичный шаг от wok к сборочному боту. Посредством cron бот активируется и проверяет изменения во всех wok (для wok и wok-stable это происходит каждые 2 часа, а для wok-undigest — реже, каждые 4 часа). При отсутствии изменений бот опять «впадает в спячку», а при наличии — собирает новые пакеты.

Сборочный бот имеет свой веб-интерфейс: cook.slitaz.org.

Здесь можно увидеть его текущее состояние, какие пакеты собрались, а какие — нет, просмотреть журналы компиляции. После сборки пакетов, раз в сутки они отправляются с сервера бота на сервер Mirror, откуда они становятся доступными всем пользователям SliTaz. С сервера Mirror происходит «зеркалирование» и на другие сервера.

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

Откуда берётся номенклатура пакетов? Ходим-бродим по интернету, ищем, чего бы еще собрать. Прислушиваемся к пожеланиям пользователей. Не всё удается собрать. Иногда не хватает нужных пакетов-зависимостей, иногда — опыта.

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

Почему мы не можем иметь в текущей стабильной версии SliTaz новые версии пакетов? Ведь в разрабатываемой версии давно уже имеются работающие новые версии и новые пакеты. Ключевое слово здесь: «стабильность». Инфраструктура пакетов имеет сложную взаимосвязь. Никогда не слышали присказку: «не было печали, апдейтов накачали»? У нас пока еще нет инструмента, позволяющего определенно сказать, будет ли всё работать, или (уже бывшая) система «рухнет» или «накроется медным тазом». Разве что, кто-нибудь напишет этот инструмент. Нет людей, готовых тестировать новые пакеты для текущей версии в связке с другими пакетами. Перед выпуском новой стабильной версии произойдут обычные и закономерные действия. Сначала все пакеты программ и их зависимости (и зависимости зависимостей), в общем, все будут пару раз (для верности) пересобраны сборочным ботом. При наличии ошибок, они будут устраняться. При отсутствии ошибок будет выпущен RC1 (Release Candidate No. 1) — кандидат на выпуск следующей версии SliTaz. После тестирования RC1 на различном оборудовании и в различных условиях всеми желающими пользователями SliTaz, будут исправляться ошибки и недочеты в работе RC1. Будет выпущен RC2, и так далее, пока команде разработчиков система не покажется достаточно пригодной и стабильной. И тогда, какой-нибудь RC3 и будет назван новой стабильной версией.

Как проверяются зависимости? Эта работа возлагается на конфигуратор, находящийся в пакете с исходниками. Он ищет всё необходимое для того, чтобы пакет мог быть собран. Если чего-то не хватает, то он «громко падает» и до компиляции дело не доходит.

Принимаются вопросы, пожелания, уточнения…

Статья была написана как ответ на тему форума SliTaz Как обновляются пакеты?

16 июня 2012

Slitaz is also ♥

Прочитав оригинальную заметку Кристофа Линкольна, основателя проекта с открытым исходным кодом — дистрибутива SliTaz, мне сильно захотелось, чтобы и вы её прочитали. Здесь я привожу свой перевод этой заметки на русский язык.