[devel] gtk-update-icon-cache filetrigger (+ libtcl deficiency)

Alexey Tourbin =?iso-8859-1?q?at_=CE=C1_altlinux=2Eru?=
Ср Сен 10 00:23:45 MSD 2008


Напомню, что filetriggers -- это скрипты /usr/lib/rpm/*.filetrigger,
которые запускаются после успешного завершения транзакции.  Они получают
на вход список файлов, затронутых транзакцией (построчно на stdin),
то есть как бы вывод 'rpm -ql' от вновь установленных, обновлённых
и удалённых пакетов.  Механизм специально сделан максимально упрощенным
(дополнительную информацию можно получить естественным образом --
например, чтобы определить, был ли файл добавлен/обновлен или же удалён,
можно использовать простой тест типа [ -f "$f"] ).

* * *

Посмотрим, какой расклад мы имеем с кешем gtk2.  Библиотека libgtk+2
использует кеш по умолчанию, если он существует (это ускоряет запуск
приложений, а также экономит память, т.к. иконки расшариваются между
приложениями).  Есть стандартная программа обновления кеша
gtk-update-icon-cache.  Кроме библиотеки libgtk+2 этот кеш больше никто
не использует.

Следуя принципу правильной группировки файлов в пакетах, можно
заключить, что библиотеку libgtk+2, программу gtk-update-icon-cache
и триггер /usr/lib/rpm/gtk-icon-cache.filetrigger следует запаковать
в один и тот же пакет (libgtk+2).

Принцип правильной группировки файлов между пакетами состоит в том,
что совместно используемые файлы нужно паковать в один и тот же пакет.
Действительно, программа gtk-update-icon-cache сама по себе, по
отдельности, имеет мало смысла (т.к. результат её работы представляет
интерес только для библиотеки libgtk+2).  А триггер
/usr/lib/rpm/gtk-icon-cache.filetrigger, в свою очередь, сможет сделать
что-либо только при наличии программы gtk-update-icon-cache.  Короче,
эти две программы просто "обслуживают" библиотеку libgtk+2.

С другой стороны, этот принцип не является настолько однозначным, чтобы
можно было применять его механически.  Нужно думать.  Ведь библиотека
libgtk+2, хотя и использует кеш иконок по умолчанию, всё же обходится
без него, если кеш отсутствует.  Так что кто-то может возразить, что
мы кладём в libgtk+2 лишние файлы, без которых, строго говоря, можно
обойтись.  На что мы можем парировать позитивной интерпретацией: кеш
иконок работает "из коробки" (а в противном случае возможны проблемы
с инвалидацией кеша, то есть битые иконки в приложениях).

* * *

Хотя принцип правильной группировки файлов не всегда можно толковать
однозначно, на практике он имеет большое значение.  Приведу пример
пакета, в котором принцип правильной группировки файлов между пакетами
явно нарушен -- это пакет libtcl.  Tcl может использоваться как embedded
language, т.е. приложения пишут обёртку для libtcl и дальше Tcl как язык
может использоваться внутри приложения.  Такие приложения получают
зависимость на libtcl soname.

Напишем простейшую программу, которая встраивает libtcl.

$ cat test.c
#include <tcl.h>
int main(int argc, char **argv)
{
        Tcl_Main(argc,argv,Tcl_Init);
        return 0;
}
$ gcc -Wall test.c -o test -ltcl
$

Теперь посмотрим, будет ли она работать в среде, где установлена
одна только библиотека libtcl.

$ hsh --init
$ hsh-install 
...
$ hsh-install libtcl
<13>Sep  9 23:05:07 rpmi: libtcl-8.5.4-alt1 installed
$ cp -pv ./test ~tmp/build/chroot/.in/ && hsh-run ./test
`./test' -> `/tmp/.private/at/build/chroot/.in/test'
application-specific initialization failed: Can't find a usable init.tcl in the following directories: 
    /usr/share/tcl/tcl8.5 /lib/tcl8.5 /lib/tcl8.5 /library /library /tcl8.5.4/library /tcl8.5.4/library
This probably means that Tcl wasn't installed properly.
$ 

Увы.  Приложения, слинковавшиеся с libtcl, громко обломятся (с диагностикой
"Tcl wasn't installed properly"!).  Если же установить пакет tcl, то уже
всё работает.

$ cp -pv ./test ~tmp/build/chroot/.in/ && hsh-run ./test && echo $? 
`./test' -> `/tmp/.private/at/build/chroot/.in/test'
0
$

Библиотека libtcl имеет "непрозрачную" зависимость на init.tcl, этот
файл загружается при инициализации библиотеки.  Без init.tcl приложения,
слинованные c libtcl, обламываются; а файл init.tcl имеет смысл только
в связи с наличием конкретной библиотеки libtcl.  Это достаточное
основание для того, чтобы применить принцип правильной группировки
файлов между пакетами -- файлы libtcl*so* и init.tcl должны быть
запакованы в один и тот же пакет.  На практике возможны два решения:
либо перенести init.tcl (и, возможно, ещё некоторые файлы) из пакета tcl
в пакет libtcl, либо полностью внести libtcl в tcl (то есть исключить
отдельный пакет с библиотекой, если библиотека "сама по себе"
не работает).

* * *

Вернёмся к кешу иконок libgtk+2 и его специфике.  Кеш создаётся для
каждой отдельной "темы" иконок, а темы раскладываются по отдельным
каталогам.  Для каждой темы должен существовать файл index.theme
(для темы "hicolor", используемой по умолчанию, это будет
/usr/share/icons/hicolor/index.theme в пакете icon-theme-hicolor).
Программа gtk-update-icon-cache по умолчанию отказывается создавать 
кеш, если отсутствует файл index.theme.

Однако пакет libgtk+2 не имеет зависимости на icon-theme-hicolor,
то есть в базовой установке для дефолтной темы hicolor отсутствует файл
index.theme.  Хотелось бы выяснить, в какой степени файл index.theme
реально необходим для использования иконок (и для создания кеша иконок).

Кроме того, тема hicolor является "расширяемой", т.е. пакеты приложений
кладут свои дополнительные иконки в /usr/share/icons/hicolor.  Остальные
темы являются как бы замкнутыми и независимыми (например, всё содержимое
каталога /usr/share/icons/gnome принадлежит пакету gnome-icon-theme,
а другие приложения туда иконок не кладут.

Короче, для триггера возможна следующая логика обновления кеша иконок:
1) каталог /usr/share/icons/hicolor обрабатывается специально;
2) остальные темы обрабатываются по факту наличия в траназкции файла
index.theme.

Тогда возможна следующая реализация /usr/lib/rpm/gtk-icon-cache.filetrigger:
	#!/bin/sh
	hicolor=
	while read -r f; do
		case "$f" in
			/usr/share/icons/hicolor/*)
				hicolor=1
				;;
			/usr/share/icons/*/index.theme)
				if [ -f "$f" ]; then
					gtk-update-icon-cache "${f%/*}"
				else
					rm -f "${f%/*}"/icon-theme.cache
				fi
				;;
		esac
	done
	if [ -n "$hicolor" ]; then
		gtk-update-icon-cache --ignore-theme-index /usr/share/icons/hicolor
	fi

То есть для любых изменений в /usr/share/icons/hicolor вызывается
gtk-update-icon-cache с опцией --ignore-theme-index (отключается
проверка наличия index.theme).  Все остальные темы обрабатываются по
файлу index.theme: если он добавился или обновился, то gtk-update-icon-cache
запускается обычным способом; если же файл index.theme удалился, то
удаляется и соответствующий ему кеш icon-theme.cache.

Мы исходили из предположения, что все остальные темы, кроме hicolor,
являются замкнутыми и независимыми, поэтому обновления кеша этих тем
можно привязать к соответствующему файлу index.theme.  Я сейчас на
всякий случай проверил, и оказалось, что предположение не всегда
выполняется.

$ grep /usr/share/icons/gnome/ ~tmp/build/cache/contents/contents_index_all |awk '$2!="gnome-icon-theme"'
/usr/share/icons/gnome/32x32    /usr/share/icons/gnome/32x32
/usr/share/icons/gnome/32x32/apps       /usr/share/icons/gnome/32x32/apps
/usr/share/icons/gnome/48x48    /usr/share/icons/gnome/48x48
/usr/share/icons/gnome/48x48/apps       /usr/share/icons/gnome/48x48/apps
/usr/share/icons/gnome/48x48/mimetypes  gdesklets
/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-anjuta.png      anjuta2
/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-codeblocks-workspace.png        codeblocks
/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-codeblocks.png  codeblocks
/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-gdesklets-display.png   gdesklets
/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-littlewizard.png        littlewizard
/usr/share/icons/gnome/48x48/mimetypes/gnome-mime-application-x-ptoptimizer-script.png  hugin
/usr/share/icons/gnome/scalable/mimetypes/gnome-mime-application-x-anjuta.svg   anjuta2
/usr/share/icons/gnome/scalable/mimetypes/gnome-mime-application-x-littlewizard.svg     littlewizard
$ 

То есть существуют приложения, хотя и немногочисленные, которые кладут
иконки в тему "gnome".  По-видимому, в таких случаях кеш темы gnome
также следует обновлять; так что предложенная логика обновления
дополнительных тем уже оказывается недостаточной.

* * *

Триггер после транзакции -- это хорошо, но нужно также рассмотреть
вопрос, что происходит в самом начале и в самом конце.  Когда библиотека
libgtk+2 устанавливается в первый раз, некоторые темы иконок уже могут
быть установлены (в более ранних транзакциях).  Также, при обновлении
библиотеки libgtk+2 формат кеша может измениться (более или менее
совместимым образом).  Короче, после установки или обновления libgtk+2
желательно обновить кеш всех существующих тем.  Смотрите, как красиво
это можно сделать:

%post
find /usr/share/icons -type f |/usr/lib/rpm/gtk-icon-cache.filetrigger

То есть мы запускаем триггер от имени %post-скрипта и как бы говорим
ему: "посмотри, все эти иконки как будто только что добавились".
Дальше триггер уже сам решит, какие там есть темы и как обновлять кеш.

А при окончательном удалении библиотеки libgtk+2 кеш иконок больше
никому не нужен.  Так что можно написать

%postun
if [ $1 = 0 ]; then
	rm -f /usr/share/icons/*/icon-theme.cache
fi

* * *

Что до самой библиотеки libgtk+2, то, во-первых, недавно вышла новая
версия 2.14.  Во-вторых, мне не нравится существующий распил на libgtk+2
и libgtk+2-common.  Может быть, вместе с триггером я бы посмотрел новую
версию и попилил бы её на свой вкус.
----------- следующая часть -----------
Было удалено вложение не в текстовом формате...
Имя     : =?iso-8859-1?q?=CF=D4=D3=D5=D4=D3=D4=D7=D5=C5=D4?=
Тип     : application/pgp-signature
Размер  : 197 байтов
Описание: =?iso-8859-1?q?=CF=D4=D3=D5=D4=D3=D4=D7=D5=C5=D4?=
Url     : <http://lists.altlinux.org/pipermail/devel/attachments/20080910/b22e10d3/attachment-0002.bin>


Подробная информация о списке рассылки Devel