[devel] I: embedded language for src.rpm/spec file editing (part II).
Igor Vlasenko
vlasenko на imath.kiev.ua
Сб Ноя 5 22:18:43 UTC 2011
Уважаемые коллеги, продолжение вводной документации.
== программирование на языке манипуляций spec-файлом ==
=== Загрузка полезного кода опциями --hook ===
начнем знакомство с файла template.pl, содержащего шаблон для наиболее
частых правок, которые приходится вносить в спек-файлы при импорте из Fedora.
На основе такого шаблона при необходимости удобно создавать личный
hook пакета с именем %{name}.pl, который в дальнейшем будет
автоматически использоваться при импорте следующих версий этого пакета
в системе fedoraimport.
#!/usr/bin/perl -w
Итак, файл:
push @SPECHOOKS,
sub {
my ($spec, $parent) = @_;
$spec->add_patch('',STRIP=>1);
$spec->get_section('package','')->subst_body(qr'','');
$spec->get_section('package','')->subst_body_if(qr'','',qr'Requires:');
$spec->get_section('prep')->push_body(q!!."\n");
$spec->get_section('package','')->unshift_body('BuildRequires: '."\n");
};
Собственно, минимально необходимая обвязка - это
push @SPECHOOKS,
sub {
my ($spec, $parent) = @_;
};
Мы видим, что в наш код утилита передает 2 объекта perl:
$spec, который соответствует текущему редактируемому spec-файлу или
src.rpm пакету, и $parent, который соответствует предку
редактируемого пакета - например, предыдущей версии этого пакета в
Сизифе при импорте или старой версии пакета при обновлении.
$spec определен всегда, $parent может и не быть определен.
В наших простых примерах $parent не понадобится.
Методы объекта $spec делятся на методы, применяемые ко всему
spec-файлу, и методы, применяемые к отдельным секциям spec-файла.
Примером методов на уровне spec-файла является метод add_patch.
push @SPECHOOKS,
sub {
my ($spec, $parent) = @_;
$spec->add_patch('foo-1.2-alt-fix-something.patch',STRIP=>3);
};
Этот код скопирует файл foo-1.2-alt-fix-something.patch из ./patches
в %_sourcedir данного пакета (возможно, временный каталог, созданный
утилитой srpmnmu или ей родственной); Добавит в спек тег
PatchXX: foo-1.2-alt-fix-something.patch,
где XX -- некоторый незанятый номер, и добавит в секцию %prep строку
%patchXX -p3
где 3 указано через параметр STRIP=>3.
Другой пример -- метод add_source.
Допустим, мы хотим провести NMU -- добавить в пакеты файлы .service
для systemd. Насобираем коллекцию .service файлов, названных по имени пакета
(вида %name.service), в папке ./patches. Создадим файл add_systemd_service.pl
push @SPECHOOKS,
sub {
my ($spec, $parent) = @_;
my $sourcenum=$spec->add_source('%name.service');
$spec->get_section('install')->push_body(
'install -Dm644 %{SOURCE'.$sourcenum.'} %buildroot%_systemd/%name.service'."n");
$spec->get_section('files')->push_body('%_systemd/%name.service'."n");
};
Передадим этот файл в утилиту
girar-nmu-prepare --hook add_systemd_service.pl ...
Метод add_source разворачивает '%name.service' в момент выполнения,
поэтому наш код будет работать для каждого пакета, обрабатываемого с
помощью girar-nmu utils, для которого найдется ./patches/%name.service.
Метод add_source скопирует файл, добавит в спек тег
SourceXX: %name.service
Метод add_source возвращает число XX, которое позднее использовано в
коде для добавления в секцию %install строки
install -Dm644 %{SOURCEXX} %buildroot%_systemd/%name.service
=== Методы редактирования на уровне секции. ===
В предыдущем примере использовался метод редактирования на уровне
секции push_body. В действительности, использовалась связка
$spec->get_section('...'[,'...'])->push_body('some text'."\n");
Этот код можно еще переписать
my $section = $spec->get_section('install');
$section->push_body('some text'."\n");
Т.е. сначала получаем объект секции %install, затем добавляем в конец
секции строку 'some text'."\n" .
Это отражает дизайн: сначала находим в spec-файлe нужную секцию,
затем редактируем ее.
=== Получаем секции ===
Как получить секцию? примеры с get_section:
# одна и та же главная секция (секция,
# с которой начинается любой spec-файл)
$spec->get_section('package','');
$spec->get_section('package');
# выделенный метод специально для главной секции
$spec->get_main_section;
# разные извращения, которые тоже работают
# и возвращают главную секцию (Name: foo)
$spec->get_section('package','-n foo');
$spec->get_section('package','-n %name');
# секция package для подпакета doc
$spec->get_section('package','doc');
$spec->get_section('package','-n foo-doc');
$spec->get_section('package','-n %name-doc');
# секция files для подпакета doc
$spec->get_section('files','doc');
Секции можно найти и по-другому. Переберем всевозможные
У объекта $section есть методы get_type, get_canonical_package.
Для секции files подпакета doc get_type равно 'files',
get_canonical_package равно 'doc'.
Пример: сосчитаем число секций %changelog. Наш rpm не допускает
больше одной секции %changelog, но в сети можно найти всякое.
push @SPECHOOKS,
sub {
my ($spec, $parent) = @_;
my $count_changelog=0;
foreach my $section ($spec->get_sections()) {
$count_changelog++ if $section->get_type() eq 'changelog';
}
print "What a horrible spec! $count_changelog changelogs." if $count_changelog>1;
};
Заметим, что метод get_sections создает временный массив. и не
годится для некоторых специальных случаев, когда мы в цикле удаляем
секции, которых еще не посетили.
Для таких особых случаев лучше использовать итератор:
my $section = $spec->get_main_section;
do {
...
} while ($section=$section->next);
Продолжение в части III:
==== Работа с телом секции ====
--
Dr. Igor Vlasenko
--------------------
Topology Department
Institute of Math
Kiev, Ukraine
--
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
Подробная информация о списке рассылки Devel