[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