Патчим на живую: разборки с Flatpak
«Плевое дело», «тут работы на пару минут» — знакомо? Конечно знакомо, еще не родился программист, не дающий такую оценку чужим задачам направо и налево.
Рассказываю на конкретном примере чем это может закончиться.
Постановка
Существует на свете такое банальное интересное приложение Evince, универсальный просмотрщик документов разных форматов.
Разрабатывается в рамках проекта Gnome, выглядит как-то так:
Вообщем так получилось, что ввиду специфики Gnome, а именно их подхода к тотальному урезанию и упрощению интерфейса, в данной программе не сделали никакого способа поменять цвет фона страницы:
он либо ярко белый с черными буквами, либо черный, белыми — т. н. «ночной режим».
Вот такой вот программный фашизм.
Но внезапно оказалось, что помимо слепых и дальтоников, на свете существуют еще и слабовидящие — люди с ослабленным зрением (различной степени), которым белый фон выдерживать уже тяжело, а полностью темный пока еще не нужен.
Вот так и возникла эта задача, с поста на одном известном форуме.
Я конечно же решил, что столь серьезному джентельмену поправить такое — как «сходить до ветру»: быстро, просто и без последствий.
Но конечно дальше начались сплошные будни разведки разработки и разнообразные сложности.
Эти самые цвета фона страницы разработчики зашили непросредственно в код, без возможности изменения через стили оформления Gtk (графический фреймворк, на котором реализован интерфейс) или тему самого окружения Gnome.
Так что нужно было сделать правку и сборку приложения, сгенерировав еще и финальный пакет для установки.
Надо сказать что обойтись «малой кровью» — подсказками по сборке для пользователя, который все это затеял не вышло.
С одной стороны была нехватка опыта (пользователь был самым обычным пользователем, не разработчиком), с другой — оочень большая специфика этого проекта и очевидное отсутствие документации.
Так что пришлось проходить правку и сборку целиком своими силами.
Сборка проекта реализована через новомодные и стандартные для новых выпусков Gnome meson и ninja, а итоговый дистрибьютив это образ Flatpak — все «как в лучших домах Парижу», но разумеется п#здец и ужас для всех непричастных.
Все манипуляции проводились на Mageia Linux 9
Хотя первую успешную сборку evince я сделал все же на FreeBSD, по традиции.
Правка
Собственно правки кода всего две, в разных местах одного и того же файла libdocument/ev-document-misc.c, обе достаточно тривиальны.
Первая в районе 485й строки:
void
ev_document_misc_invert_surface (cairo_surface_t *surface) {
cairo_t *cr;
cr = cairo_create (surface);
/* white + DIFFERENCE -> invert */
// заменяем вот эти два вызова
// cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);
// cairo_set_source_rgb (cr, 1., 1., 1.);
// на вот такие:
cairo_set_operator (cr, CAIRO_OPERATOR_DARKEN);
cairo_set_source_rgb (cr, 0.8, 0.9098, 0.8117647);
cairo_paint(cr);
cairo_destroy (cr);
}Эта правка заставит использовать «мразотно-зеленый» (не знаю как правильно он называется) цвет фона вместо черного при включении «ночного режима».
Вторая правка находится в районе 512й строки:
void
ev_document_misc_invert_pixbuf (GdkPixbuf *pixbuf)
{
guchar *data, *p;
guint width, height, x, y, rowstride, n_channels;
n_channels = gdk_pixbuf_get_n_channels (pixbuf);
g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
g_assert (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
/* First grab a pointer to the raw pixel data. */
data = gdk_pixbuf_get_pixels (pixbuf);
/* Find the number of bytes per row (could be padded). */
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
for (x = 0; x < width; x++) {
for (y = 0; y < height; y++) {
/* Calculate pixel's offset into the data array. */
p = data + x * n_channels + y * rowstride;
/* Change the RGB values*/
// заменяем вот это:
//p[0] = 255 - p[0];
//p[1] = 255 - p[1];
//p[2] = 255 - p[2];
// на это:
p[0] = 204; /* cc */
p[1] = 232; /* e8 */
p[2] = 207; /* cf */
}
}
}
Результат этих правок выглядит вот так:
Все счастливы и довольны, если бы не одно но:
сборка «дистрибьютива» в виде образа Flatpak.
Быстрое решение или Gnome Builder
Поскольку я не следил особо за прогрессом разработки проекта Gnome — не был в курсе о существовании аж целой специализированной IDE, которая команда создала и поддерживает для своих проектов.
С ее помощью весь процесс от скачивания исходников, внесения изменений и до генерации .flatpak файла оказался тривиальным:
- Открываешь Gnome builder
- Нажимаешь Clone repository.. и вставляешь ссылку в поле Repository URL: https://gitlab.gnome.org/GNOME/evince.git и жмешь Clone repository
- Делаешь свои правки
- Жмешь Run, чтобы собрать и проверить работоспособность
- Жмешь Creating flatpak bundle
Вот так выглядит открытый и собранный проект:
Повторюсь что я не знал о существовании сего замечательного инструмента, поэтому полез делать сборку и упаковку вручную.
Собственно я постоянно так делаю, еще временами ору Гойдааа! во весь голос.
Особенности профессии, что поделать.
Пох#й пляшем: долгое и сложное решение
Первым делом клонируем репозиторий с исходниками:
git clone https://gitlab.gnome.org/GNOME/evince.git
Поскольку мы настоящие джентельмены — никаких релизных веток использовать не будем, только trunk master current main, только хардкор!
meson -Dlibhandy:examples=false -Dlibhandy:tests=false -Dlibhandy:vapi=false -Dlibhandy:glade_catalog=disabled -Dlibhandy:introspecti on=disabled _build
Внимательно смотрим вывод, поскольку будут сыпаться ошибки об отсутствующих библиотеках — их все последовательно придется ставить в систему.
После того как meson отработает без ошибок, надо запустить ninja:
ninja -C _build all
Это уже непосредственно компиляция.
Обе полные команды сборки находятся в файле .gitlab-ci.yml откуда их можно вытащить при изменениях.
_build это название временного каталога, в который пишется результат сборки. После того как ninja отработает без ошибок, пожно попробовать запустить результат:
./_build/shell/evince
К сожалению evince использует динамическую загрузку своих библиотек из заранее определенного пути, поэтому при попытке запуска будет вот такая ошибка:
(evince:7168): EvinceDocument-WARNING **: 22:14:12.259: Error opening directory “/usr/local/lib64/evince/4/backends”: No such file or directory
Чтобы это обойти можно поставить «системый» evince из репозитория ОС, тогда собранный успешно подхватит эти библиотеки и запустится:
Если вы дошли до этого места — поздравляю, ибо это была лишь подготовка.
Теперь возьмемся всерьез за Flatpak.
Файлы, описывающие сборку находятся в папке build-aux/flatpak,основной это org.gnome.Evince.json и нам нужно будет его поправить.
Ключевая проблема с Flatpak заключается в том что он «слишком умный», поэтому сам выкачивает все зависимости, сам собирает и сам упаковывает.
Естественно из-за столь большого круга задач он лажает и падает.
Простой совет авторам, который они конечно никогда не услышат и не поймут. (поскольку не говорят по-русски)
Именно поэтому мы выше запускали meson и ninja вручную.
Второй важный момент в flatpak это использование репозитория git в качестве источника для сборки. Поскольку наши правки локальны, то это немедленно создает проблему:
изменения в исходниках не попадают в сборку.
Чтобы решить эту проблему и заодно разобраться со стадиями Flatpak, мы берем и выносим сборку Flatpak из проекта — в совершенно отдельную папку.
Создаем новый каталог, вне структуры проекта, пусть для примера будет:
/opt/work/tmp/evince-build
Копируем внутрь скрипты сборки:
cp /opt/src/evince/build-aux/*.json /opt/work/tmp/evince-build
И правим последний блок в org.gnome.Evince.json, меняем вот это:
"sources": [
{
"type": "git",
"branch": "main",
"url": "https://gitlab.gnome.org/GNOME/evince.git"
}
]
"sources": [
{
"type": "dir",
"path": "/opt/src/evince"
}
]Где в path указываем полный путь до исходников.
После этого будут подхватываться ваши правки в коде без необходимости их коммита в репозиторий.
flatpak-builder --keep-build-dirs --user --disable-rofiles-fuse stage1 org.gnome.Evince.json
Пару слов о том откуда взялась эта команда со всеми параметрами.
Вот отсюда.
Дело в том что совсем недавно проект Gnome реализовал для себя специальный плагин для Gitlab CI, поэтому вместо явных команд сборки и упаковки в файле проекта теперь лишь вызов этого плагина:
flatpak:
extends: .flatpak
stage: build
variables:
MANIFEST_PATH: "build-aux/flatpak/org.gnome.Evince.json"
RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo"
FLATPAK_MODULE: "evince"
MESON_ARGS: "-Dnautilus=false -Dgtk_doc=false"
APP_ID: "org.gnome.Evince"
artifacts:
# Rewrite paths and reports to "remove" testlog.txt and junit files,
# which do not exists in evince build, yet.
paths:
- "${BUNDLE}"
- 'repo.tar'
- '.flatpak-builder/build/${FLATPAK_MODULE}/_flatpak_build/meson-logs/meson-log.txt'
reports:
junit: []
Удачи разобраться, как говорится. Не зная этого момента, вызвать правильно сборку вручную не выйдет. Вот такое тут убойное веселье.
flatpak build-export stage2 stage1
flatpak build-bundle stage2 evince-patched.flatpak
В результате в корне текущего каталога будет файл evince-patched.flatpak — это и есть тот самый готовый образ, который можно распространять и ставить в другие ОС:
flatpak install evince-patched.flatpak
Поскольку это development-версия, в ее зависимостях указаны development- версии библиотек Gnome, которые выкладываются в специальном репозитории.
Без указания этого репозитория установка не пройдет:
flatpak remote-add gnome-nightly https://nightly.gnome.org/gnome-nightly.flatpakrepo
Команду выше нужно выполнить лишь раз но на всех машинах, где будет разворачиваться этот .flatpak файл.
Эпилог
Эта статья идеально иллюстрирует хитровы#банность сложность современного софта, где давно уже недостаточно знать один лишь язык на котором написана программа, а актуальные знания включают в себя опыт работы с линуксами, средами разработки, инструментами сборки, ядерными реакторами и чертом лысым — словом со всей возможной дичью, встречающейся на просторах ИТ-индустрии последние десять-двадцать лет.
Я потратил примерно 5 минут на саму правку, еще 10 на сборку и блин три дня горячей #бли на итоговую сборку .flatpak файла!
Имейте ввиду такие расклады при оценке сроков в ваших проектах, ибо подобная разбивка, где 5% это сама работа, а 95% — непонятная долбильня головой в стену встречается все чаще и чаще.