some structure refactor

This commit is contained in:
2026-07-20 21:58:04 +03:00
parent fc0730c751
commit 1172683d9a
26 changed files with 493 additions and 38 deletions
+125
View File
@@ -0,0 +1,125 @@
# Сравнение set-зависимостей Sisyphus с `newset.c`
Скрипт [`scripts/compare_sisyphus_set_versions.sh`](../scripts/compare_sisyphus_set_versions.sh)
сравнивает set-строки из RPM-заголовка с результатом `reimplement/newset.c`.
## Что именно сравнивается
1. Из изолированных APT-индексов `Sisyphus/x86_64` и `Sisyphus/noarch`
берутся `Package`, `Architecture`, `Version`, `Filename`, `MD5Sum`,
`Provides` и `Depends`/`Pre-Depends`.
2. RPM скачивается непосредственно из `Sisyphus/files/<arch>/RPMS` и
проверяется по `MD5Sum` из индекса.
3. Эталонные зависимости читаются из RPM-заголовка через массивы тегов:
`PROVIDENAME/PROVIDEFLAGS/PROVIDEVERSION` и
`REQUIRENAME/REQUIREFLAGS/REQUIREVERSION`.
4. Для воспроизведения зависимостей вызываются установленные вместе с
`rpm-build` программы `%_rpmlibdir/find-provides` и
`%_rpmlibdir/find-requires` с методом `none,lib`.
5. В каталоге инструментов заменяется **только** `mkset`: вместо штатного
`mkset` подставляется совместимая обвязка над `newset.c`. `provided_symbols`,
`ldd --bindings`, `eu-readelf`, `objdump`, `file` и остальные средства
отбора/извлечения символов остаются штатными инструментами `rpm-build`.
Для `Requires` APT получает точный уже скачанный RPM-файл как вход и скачивает
его полный runtime dependency closure в пустой пользовательский RPM root. Так
версия и архитектура целевого пакета не выбираются повторно по имени. APT
запускается с пустым `APT_CONFIG`; source list, package cache, archives, lists,
status и preferences перенаправлены во временный каталог, поэтому настройки,
hooks и RPM-база основной системы не участвуют в разрешении зависимостей.
Перед распаковкой проверяются имена элементов cpio и цели символических ссылок.
Абсолютная RPM-ссылка считается путём внутри виртуального package root: например,
`/usr/sbin/update-alternatives -> /bin/true` безопасно переписывается в
`../../bin/true`, а не направляется в `/bin/true` основной системы. Все symlink
сначала собираются из RPM-заголовков и создаются в порядке от родительских путей
к дочерним; затем `cpio` извлекает только нессылочные элементы. Благодаря этому
usrmerge-ссылки вроде `/bin -> usr/bin` создаются до файлов `/bin/*`.
Имена элементов с выходом через `..` и относительные symlink, фактически
выходящие из временного root, по-прежнему отклоняются. После проверки RPM-файлы
распаковываются без установки. Это позволяет штатному `rpm-build/ldd`
использовать интерпретатор и библиотеки из того же снимка Sisyphus, не изменяя
систему.
## Ограниченный тест
На ALT p11 с установленным `rpm-build`:
```sh
bash scripts/compare_sisyphus_set_versions.sh \
--package strace \
--report strace-set-report.tsv
```
Несколько пакетов:
```sh
bash scripts/compare_sisyphus_set_versions.sh \
--package strace \
--package libdw \
--report sample-set-report.tsv
```
Либо первые десять записей индекса:
```sh
bash scripts/compare_sisyphus_set_versions.sh \
--limit 10 \
--report sample-set-report.tsv
```
Без `--all`, `--limit` или `--package` скрипт завершится до обращения к
репозиторию. Это защищает от случайного полного запуска.
## Полный запуск
Команда предусмотрена, но в ходе разработки не запускалась:
```sh
bash scripts/compare_sisyphus_set_versions.sh \
--all \
--report sisyphus-set-report.tsv
```
Продолжение после прерывания:
```sh
bash scripts/compare_sisyphus_set_versions.sh \
--all \
--resume \
--report sisyphus-set-report.tsv
```
`--resume` пропускает только уже завершённую комбинацию
`package/architecture/version`; обновившийся пакет будет обработан заново.
Завершённость подтверждается маркером `complete=1` в полной 11-польной строке
`SUMMARY`, поэтому оборванная при записи строка не считается результатом.
Во время работы отчёт можно наблюдать отдельно:
```sh
tail -f sisyphus-set-report.tsv
```
`START` записывается до обработки пакета, а `DEPENDENCY` и `SUMMARY` — сразу
после сравнения пакета. Поэтому файл обновляется в процессе, а не только после
завершения всего обхода.
## Статусы отчёта
- `match` — set-строка из RPM совпала с результатом `newset.c`;
- `mismatch` — capability и оператор совпали, set-строки различаются;
- `missing_generated` — RPM содержит set-зависимость, но повторный запуск
`rpm-build` её не сгенерировал;
- `extra_generated` — повторный запуск сгенерировал зависимость, которой нет в
RPM-заголовке;
- `no_set_metadata` — в APT metadata пакета нет set-строк;
- `*_error` — ошибка скачивания, контрольной суммы, распаковки либо генератора.
`extra_generated` показывается в строках `DEPENDENCY` и делает итоговый статус
пакета равным `mismatch`. При анализе нужно учитывать, что бинарный RPM не
содержит исходных spec-фильтров (`filter_from_requires` и аналогичных), поэтому
часть таких расхождений может относиться не к `newset.c`, а к невозможности
полностью воспроизвести фильтрацию исходного spec. Их число отдельно указано в
поле `extras=N` строки `SUMMARY`.
+49
View File
@@ -0,0 +1,49 @@
# Notes
## to read
https://www.altlinux.org/Features/Specific
## Hash
в данный момент используется Jenkins OAAT. Считается чуть устаревшим(check), главным аналогом является:
https://github.com/cyan4973/xxhash
xxHash64 (XXH64): относительно простая имплементация на си, хвалят за скорость, 64 бит, параллель
необходимы тесты и графики:
1. насколько быстрее/медленнее
2. коллизии на наборе букв в сравнении с рандомными данными
3. коллизии у первого и второго хэшей между собой
> В XXH3 внутренний цикл, который оптимально обрабатывается векторизацией. Благодаря этому функция использует аппаратную поддержку на наборах инструкций SSE2, AVX2 и NEON. Производительность зависит от комилятора. Неожиданно оказалось, что версия, скомпилированная clang, намного превосходит остальные. Ян Колле даже подумал, что производительность хеш-функции здесь превысит пропускную способность памяти. Этой версии на графике соответствует пунктирная линия.
есть ещё cityhash от гугла, но она c++
в случае XXH64 строк кода ~200, можно вставить в файл
в случае XXH3 строк кода ~2к, стоит использовать библиотеку напрямую, бессмысленно
## about collizions
> А вы пробовали побитовую вероятностную карту строить?
> Есть у нас по условию множество из входных значений, которые замапились в N хешей (включая коллизии!). Эти N хешей имеют одинаковый размер и, по сути, являются массивами из K бит.
> Так вот можно среди этих хешей посчитать 0 и посчитать 1, получится K распределений. Чем они все ближе к пополамному ± 6%, тем лучше.
> https://raw.altlinux.team/arseny/snippets/2026-06/QSIrJSuwxp.txt
> https://raw.altlinux.team/arseny/snippets/2026-06/QSIrJSuwxp.c
## free
free не делает очистку самой структуры, valgrind
## data
алфавит для символ библиотек:
```
.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz
```
- предоставленные/определенные символы из библиотек обычно используют @@VERSION
- обязательные/неопределенные символы из двоичных файлов обычно используют @VERSION
+46
View File
@@ -0,0 +1,46 @@
# raw_data
## messages from tg
[6/23/26 9:57PM] Arseny: Кстати. У меня есть серьёзная математическая, она же очень серьёзная алгоритмическая задача на исследование. ALT set:version (https://git.altlinux.org/gears/r/rpm.git?a=blob;f=lib/set.c) нужен майнтейнер.
[6/23/26 9:57PM] Arseny: Вы хотели алгоритмов? Вот, расскажите мне про этот.
[6/23/26 9:57PM] Arseny: весь алгоритм можно разбить на 4 последовательных стадии.
0) Задача в том, чтобы проверять, выполняется ли R ⊂ P.
множества, стоящие в позиции P, даются командой навроде nm --dynamic -j -U /usr/lib64/libyourfavourite.so.N.
множества, стоящие в позиции R, даются командой навроде nm --dynamic -j -u /usr/bin/yourfavouriteprog, или там может быть иной .so.
Тут, наверное, ещё и введение в предметную область должно быть.
1) hash: N элементов сбрасываются в этот самый хеш; его эффективность бы стоило оценить хорошенько. (гипотеза от меня: он вообще-то плохой и даёт много коллизий на интересных нам инпутах), в результате K <= N отсортированных хешей, чисел из [0; 2**10) (по умолчанию 10 бит на хеш);
2) delta: K <= N чисел из прошлого шага заменяются на разности с предыдущими, и получаются по-другому распределённые числа, чаще маленькие и очень, очень мало сверхбольших; есть мнение, что они для равномерно распределённых хешей распределены геометрически;
3) golomb: эти K разностей кодируются по Rice-Golomb (https://en.wikipedia.org/wiki/Golomb_coding), делитель по умолчанию 2**7; получается префиксный код из K битострок, которые можно слева направо прочесть без lookahead и восстановить массив разностей;
4) (якобы) base62: их конкатенация кодируется в буквы.
(2) и (3) близки к очевидно-понятным; я поизучал, реализовал на питоне (https://altlinux.space/arseny/atsv-research) и снова поизучал.
А вот (1) и (4) я не занимался совсем.
От (4) у меня тоже башка взрывается, а (1) можно очень хорошо так поизучать, и существует, наверное, обширная литература.
[6/23/26 9:57PM] Arseny: grep -A16 Jenkins lib/set.c
вот в этом дереве исходников (https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=lib/set.c;h=9474a2ee6d7c;hb=a01a87db6e8a)
[6/23/26 9:57PM] Arseny: Пусть параметр-делитель равен M, и энкодеру на вход поступает число A. Тогда ищутся такие q и r < q, что A = qM + r. Частное кодируется унарной кодировкой из единиц, за ним следует нулик, а далее ровно log₂M бит на остаток. Подобрать M нужно с умом.
```sh
python3 stringset.py 10 7 <<EOF
foo
bar
quux
EOF
hashes=[298, 487, 588]
deltas=[298, 189, 101]
codes=[b'1100101010', b'100111101', b'01100101']
golomb divisor: 128 (minimum word bit length: 8)
total bit length: 27
per-elem bit length: 9.000
```
Но здесь не хеш из файла выше, а питонический hash() обычный. Чтобы смотреть на актуальные результаты, нужно подсунуть актуальный.
[6/23/26 9:58PM] Arseny: А вот страничка set:version на ALT Wiki (https://www.altlinux.org/Set-version)
[6/23/26 9:58PM] Arseny: Putze, F.; Sanders, P.; Singler, J. (2007),
253 * "Cache-, Hash- and Space-Efficient Bloom Filters",
254 * http://algo2.iti.uni-karlsruhe.de/singler/publications/cacheefficientbloomfilters-wea2007.pdf
[6/23/26 9:58PM] Arseny: Сайта нет, но статью поискать https://t.me/c/1622441719/1359/9771
[6/23/26 9:59PM] Arseny: И ещё наследие Алексея Турбина:
https://ftp.altlinux.org/pub/people/at/
https://github.com/svpv
+444
View File
@@ -0,0 +1,444 @@
# `rpmquery` в ALT Linux
## Краткий вывод
`rpmquery` — отдельная точка входа в тот же механизм запросов RPM, что и
`rpm -q`. В ALT Linux p11 `/usr/bin/rpmquery` является символьной ссылкой на
ELF-программу `/usr/lib/rpm/rpmq`; имя запуска заранее включает режим запроса,
поэтому `-q` обычно не нужен:
```sh
rpmquery rpm
rpmquery -q rpm
rpm -q rpm
```
Все три команды дают один и тот же результат. При этом `rpmquery` не является
интерфейсом к репозиторию APT: без `-p` он читает только установленную базу RPM,
по умолчанию `/var/lib/rpm`. Для поиска доступных, но не установленных пакетов
нужны `apt-cache`, `apt-get` или веб/RDB-интерфейсы ALT.
Исследование проверено на официальном контейнере `alt:p11`, обновлённом до
`rpm-4.13.0.1-alt45.x86_64`, и по ветке `p11` исходников RPM, commit
`7e71ed3da438289d916f1cccc9c0a83e594455dc`.
## Где ищет `rpmquery`
| Вызов | Источник данных |
|---|---|
| `rpmquery NAME` | установленная база RPM |
| `rpmquery -a` | все пакеты из установленной базы RPM |
| `rpmquery -p FILE.rpm` | заголовок указанного RPM-файла, установка не требуется |
| `rpmquery --root ROOT ...` | база и файловая система относительно `ROOT` |
| `rpmquery --dbpath DIR ...` | явно указанная база RPM |
Практическая граница была проверена так: `apt-cache show strace` находил пакет
в p11, тогда как `rpmquery strace` отвечал `package strace is not installed` и
завершался с кодом `1`.
## Работа с APT-репозиторием
APT-RPM загружает индексы подключённых репозиториев в `/var/lib/apt/lists`.
Обычный порядок работы:
```sh
# Показать настроенные источники ALT, если установлен пакет apt-repo.
apt-repo
# Обновить локальные индексы после изменения источников.
apt-get update
```
В минимальных контейнерах `apt-repo` может отсутствовать; источники всё равно
задаются файлами `/etc/apt/sources.list` и `/etc/apt/sources.list.d/*.list`.
Сами `pkglist.*` являются бинарными header-list файлами RPM, поэтому искать в
них обычным `grep` не следует. Для доступа к кэшу предназначен `apt-cache`:
| Задача | Команда |
|---|---|
| Поиск по имени и описанию | `apt-cache search REGEX` |
| Перечень имён | `apt-cache pkgnames` |
| Выбранная версия и приоритеты источников | `apt-cache policy PACKAGE` |
| Полная доступная запись, включая версии зависимостей | `apt-cache show PACKAGE` |
| Граф разрешения зависимостей | `apt-cache depends PACKAGE` |
| Обратные зависимости | `apt-cache whatdepends PACKAGE` |
| Низкоуровневое представление версий и зависимостей | `apt-cache showpkg PACKAGE` |
| Метаданные исходного пакета | `apt-cache showsrc PACKAGE` |
| Все доступные записи репозиториев | `apt-cache dumpavail` |
| Проверка плана установки без изменений | `apt-get -s install PACKAGE` |
| Только скачать бинарный пакет и зависимости | `apt-get -d install PACKAGE` |
| Скачать исходники | `apt-get source PACKAGE` |
`apt-cache depends` удобен для просмотра найденных провайдеров, но в его
обычном выводе set-версия зависимости теряется. Для полных set-строк нужны
`apt-cache show`, `apt-cache showpkg`, скачанный RPM или RDB API.
## Set-строки пакета из APT-репозитория
### Способ 1: прямо из локального кэша APT
Это самый короткий способ, установка и скачивание RPM не нужны:
```sh
pkg=libdw
# Сначала проверить, какую версию и из какого источника выбрал APT.
apt-cache policy "$pkg"
# Полные поля с set-Requires и set-Provides.
apt-cache show "$pkg" |
sed -n -E '/^(Pre-Depends|Depends|Provides):/p' |
grep -F 'set:'
```
В проверенном p11 поля имели вид:
```text
Depends: ... liblzma.so.5()(64bit) (>= set:kiyIz7cr3p0), ...
Provides: libdw.so.1()(64bit) (= set:ldm5ZH1pjPBqjZ9bdJbLDnKGbC1hkJSX...)
```
`Depends` и `Pre-Depends` соответствуют требованиям пакета, `Provides` — тому,
что пакет предоставляет. `apt-cache show` может вывести несколько записей,
если в подключённых источниках доступны разные версии; выбранный кандидат
виден в `apt-cache policy`.
Для выгрузки всего подключённого репозитория можно использовать:
```sh
apt-cache dumpavail >available.txt
grep -E '^(Pre-Depends|Depends|Provides):.*set:' available.txt
```
В отличие от HTML-страниц `packages.altlinux.org`, кэш APT содержит полные
set-строки без визуального многоточия.
### Способ 2: скачать RPM через APT и запросить его заголовок
Это наиболее точный локальный способ: выводятся исходные массивы тегов RPM и
их удобно преобразовать в TSV. `apt-get -d` скачивает также отсутствующие
зависимости, но ничего не устанавливает.
```sh
pkg=strace
cache=$(mktemp -d)
trap 'rm -rf "$cache"' EXIT
mkdir -p "$cache/partial"
apt-get -y -d \
-o "Dir::Cache::archives=$cache" \
install "$pkg"
rpmfile=$(find "$cache" -maxdepth 1 -type f \
-name "${pkg}_*.rpm" -print -quit)
```
Set-Requires выбранного пакета:
```sh
rpmquery -p --qf \
'[%{REQUIRENAME}\t%{REQUIREFLAGS:depflags}\t%{REQUIREVERSION}\n]' \
"$rpmfile" |
awk -F '\t' '$3 ~ /^set:/'
```
Set-Provides выбранного пакета:
```sh
rpmquery -p --qf \
'[%{PROVIDENAME}\t%{PROVIDEFLAGS:depflags}\t%{PROVIDEVERSION}\n]' \
"$rpmfile" |
awk -F '\t' '$3 ~ /^set:/'
```
Для `strace-7.0-alt1.x86_64.rpm` в p11 получены два требования:
```text
libdw.so.1()(64bit) >= set:lgHyMNBkvSTjjY0en8Yi3vFojgjVM7jRsRF4zCFBzNVmhhGF
libselinux.so.1()(64bit) >= set:liZ0N709Wr2dbKPs2
```
Set-Provides у этого пакета нет: исполняемый `strace` требует библиотеки, но не
экспортирует собственную разделяемую библиотеку. У скачанного вместе с ним
пакета `libdw` присутствует длинный Provide
`libdw.so.1()(64bit) = set:...`.
Если пакет уже установлен и APT не скачивает его повторно, его заголовок можно
прочитать напрямую командой `rpmquery PACKAGE`; для гарантированного получения
конкретного репозиторного RPM удобнее использовать RDB API.
### Способ 3: структурированные данные RDB API
RDB удобен для автоматизации, другой ветки/архитектуры и больших выборок. Он
возвращает полные строки и явно различает `provide`, `require` и `conflict`:
```sh
branch=p11
arch=x86_64
pkg=libdw
hash=$(curl -fsS \
"https://rdb.altlinux.org/api/site/pkghash_by_binary_name?branch=$branch&name=$pkg&arch=$arch" |
jq -r '.pkghash')
curl -fsS \
"https://rdb.altlinux.org/api/dependencies/binary_package_dependencies/$hash" |
jq -r '
.dependencies[]
| select(
(.type == "provide" or .type == "require") and
(.version | startswith("set:"))
)
| [.type, .name, .version]
| @tsv
'
```
Проверенный результат для `libdw` содержит один set-Provide и четыре
set-Requires. Поле `flag_decoded` того же JSON позволяет восстановить оператор:
`RPMSENSE_EQUAL` для обычного Provide и
`RPMSENSE_GREATER` + `RPMSENSE_EQUAL` для `>=` Require.
Через RDB можно также получить URL самого RPM:
```sh
curl -fsS \
"https://rdb.altlinux.org/api/site/package_downloads_bin/$hash?branch=$branch&arch=$arch" |
jq -r '.downloads[].packages[].url'
```
## Основные запросы
### Пакет и его метаданные
```sh
# Версия установленного пакета в стандартном формате NVRA.
rpmquery rpm
# Подробная информация. В ALT вывод включает DistTag.
rpmquery -i rpm
rpmquery --info rpm
# Стабильный формат для скрипта.
rpmquery --qf '%{NAME}|%{EPOCHNUM}|%{VERSION}|%{RELEASE}|%{DISTTAG}|%{ARCH}\n' rpm
# Доступные имена тегов заголовка.
rpmquery --querytags
```
В `rpmquery` короткая опция `-i` означает `--info`, а не установку пакета. Это
ALT-алиас из `rpmpopt`; он появился в changelog RPM в версии
`4.13.0-alt7`. Для установки используется другой режим программы `rpm`, но в
обычной работе с ALT зависимости и репозитории следует поручать APT.
### Файлы
```sh
# Какому установленному пакету принадлежит файл.
rpmquery -f /usr/bin/rpmquery
# Все файлы пакета.
rpmquery -l rpm
# Только конфигурационные или документационные файлы.
rpmquery -c rpm
rpmquery -d rpm
# Пакет из файла без установки.
rpmquery -p ./package.rpm
rpmquery -lp ./package.rpm
rpmquery -ip ./package.rpm
```
Для аргумента-пути `--whatprovides` сначала обращается к индексу установленных
файлов, затем к индексу capability. Поэтому для существующего установленного
файла эти команды обычно совпадают:
```sh
rpmquery -f /usr/bin/rpmquery
rpmquery --whatprovides /usr/bin/rpmquery
```
### Provides, Requires и обратные запросы
```sh
# Что объявляет один пакет.
rpmquery --provides rpm
rpmquery --requires rpm
rpmquery -R rpm
# Какие установленные пакеты объявляют capability или требуют его.
rpmquery --whatprovides 'libpopt.so.0()(64bit)'
rpmquery --whatrequires 'libpopt.so.0()(64bit)'
```
`--provides`, `--requires`, `--info`, `--last`, `--scripts` и ряд других
удобных режимов реализованы как `popt`-алиасы над `--queryformat`, а не как
отдельные алгоритмы запросов.
## Формат запросов для автоматической обработки
Стандартный вывод удобен человеку, но надёжнее не разбирать его пробелами.
Массивы тегов RPM обходятся форматом в квадратных скобках. Чтобы внутри такого
итератора повторять скалярный тег для каждого элемента массива, знак `=` ставят
внутри имени тега: `%{=NAME}`.
### Все set-Requires в TSV
```sh
LC_ALL=C rpmquery -a --qf \
'[%{=NAME}\t%{REQUIRENAME}\t%{REQUIREFLAGS:depflags}\t%{REQUIREVERSION}\n]' |
awk -F '\t' '$4 ~ /^set:/'
```
Поля: пакет, имя capability, оператор, версия зависимости.
Пример:
```text
rpm libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0
```
### Все set-Provides в TSV
```sh
LC_ALL=C rpmquery -a --qf \
'[%{=NAME}\t%{PROVIDENAME}\t%{PROVIDEFLAGS:depflags}\t%{PROVIDEVERSION}\n]' |
awk -F '\t' '$4 ~ /^set:/'
```
Пример:
```text
libpopt libpopt.so.0()(64bit) = set:jdtcJcAdqxTmPJUyuYcVIbLNhqmj...
```
Это предпочтительнее `grep` по человекочитаемому выводу, если нужны имена
пакетов, операторы или однозначное разделение полей.
## Что `rpmquery` делает и не делает с `set:version`
### Показывает сохранённые зависимости
`rpmquery --requires` и `rpmquery --provides` извлекают из заголовков пакетов
имя capability, флаги отношения и строку версии. Они не декодируют `set:` и не
показывают исходные ELF-символы.
Для установленного `rpm` в p11 наблюдалось:
```text
libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0
```
Установленный провайдер находился по одному имени capability:
```sh
rpmquery --whatprovides 'libpopt.so.0()(64bit)'
# libpopt-1.18-alt1.x86_64
```
### Не сравнивает выражение, переданное в `--whatprovides`
Передача полного выражения не запускает `rpmsetcmp()`:
```sh
rpmquery --whatprovides \
'libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0'
```
Проверенный результат:
```text
no package provides libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0
```
Для непутевого аргумента `--whatprovides` делает точный поиск по индексу
`PROVIDENAME`. Поэтому сначала нужно искать провайдера по имени capability, а
его set-строку получать отдельным запросом:
```sh
name='libpopt.so.0()(64bit)'
provider=$(rpmquery --whatprovides "$name" | head -n1)
rpmquery --qf \
'[%{PROVIDENAME}\t%{PROVIDEFLAGS:depflags}\t%{PROVIDEVERSION}\n]' \
"$provider" |
grep -F "$name"
```
Настоящее сравнение двух `set:`-версий происходит в `rpmdsCompareEVR()` при
проверке совместимости зависимостей. Если обе версии начинаются с `set:`, код
вызывает `rpmsetcmp()`; если `set:` имеет только одна сторона, зависимости не
пересекаются. Следовательно, `rpmquery` полезен для извлечения корпуса строк,
но не является CLI для непосредственного сравнения двух наборов.
## Как `rpmquery` используется при сборке самого ALT RPM
В `alt/rpm.spec` ветки p11 после первой сборки выполняется:
```sh
rpmquery -a --provides | fgrep '= set:' | sort >P
rpmquery -a --requires | fgrep '= set:' | sort >R
join -o 1.3,2.3 P R | shuf >setcmp-data
time ./setcmp <setcmp-data >/dev/null
```
Смысл конвейера:
1. Из локальной базы сборочного окружения собираются все set-Provides и
set-Requires.
2. Строка поиска `= set:` захватывает как Provides с `= set:`, так и Requires с
`>= set:`, потому что вторая строка тоже содержит подстроку `= set:`.
3. После сортировки `join` соединяет файлы по первому полю — имени capability.
4. `-o 1.3,2.3` оставляет только третьи поля: set провайдера и set требования.
5. Полученные реальные пары подаются в `tools/setcmp` для замера реализации
`rpmsetcmp()` и профиль-управляемой пересборки `lib/set.c`.
В обновлённом минимальном контейнере p11 точный конвейер дал:
```text
P: 75 строк
R: 109 строк, из них 13 полных дублей
setcmp-data: 109 пар
```
Эти числа описывают только состав минимального контейнера, а не весь p11 или
Sisyphus. Также конвейер spec не сохраняет имя capability и пакет-владелец;
для исследовательского корпуса удобнее TSV-форматы выше.
Современная замена устаревающего `fgrep` без изменения смысла:
```sh
grep -F '= set:'
```
## Практические ограничения
- `rpmquery -a` означает все **установленные** пакеты, а не весь репозиторий.
- `--whatprovides` и `--whatrequires` ищут заголовки по имени capability; они
не принимают полноценное выражение зависимости как запрос сравнения.
- Несколько установленных версий или провайдеров могут дать несколько строк.
- Set-строки крупных библиотек очень длинные; лучше писать полный вывод в файл,
а в терминале показывать только статистику или начало строки.
- Для скриптов следует фиксировать `LC_ALL=C` и использовать `--queryformat`.
- `--requires`/`--provides` отражают метаданные RPM, а не текущее содержимое ELF
на диске. Для восстановления исходных символов нужны `nm`, `readelf` и
генераторы автозависимостей rpm-build.
- При запросе другой системы безопаснее явно использовать `--root`; `--dbpath`
меняет только путь к БД и требует внимательности к относительным путям.
- ALT предоставляет `--nowait-lock` как popt-алиас, отключающий ожидание
блокировки БД. Его стоит применять только когда отказ предпочтительнее
ожидания, а не как способ обходить активную транзакцию RPM/APT.
## Источники
1. [Документация платформы ALT 10.1: утилита RPM](https://docs.altlinux.org/ru-RU/platform/10.1/html/alt-platform/utilita_komandnoj_stroki_rpm.html) — RPM работает с файлами, пакетами, зависимостями и собственной БД, но не знает о репозиториях.
2. [Настройка списка репозиториев APT в ALT](https://docs.altlinux.org/ru-RU/platform/10.1/html/alt-platform/nastrojka_spiska_repozitoriev_apt.html).
3. [Пакет `rpm` в p11](https://packages.altlinux.org/en/p11/srpms/rpm/) и [specfile](https://packages.altlinux.org/en/p11/srpms/rpm/specfiles/).
4. [RDB: текущий бинарный пакет `rpm` для p11/x86_64](https://rdb.altlinux.org/api/site/pkghash_by_binary_name?branch=p11&name=rpm&arch=x86_64).
5. [`rpmqv.c`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=rpmqv.c;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — выбор режима Query по имени `rpmquery`/`rpmq`.
6. [`rpmpopt.in`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=rpmpopt.in;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — ALT-алиасы `--requires`, `--provides`, `--info`, `-i`, `--nowait-lock`.
7. [`lib/query.c`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=lib/query.c;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — индексы для `--whatprovides`, запросы файлов и формат `%{nvra}` по умолчанию.
8. [`lib/rpmds.c`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=lib/rpmds.c;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — вызов `rpmsetcmp()` при сравнении двух set-версий.
9. [`alt/rpm.spec`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=alt/rpm.spec;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — построение `setcmp-data` через `rpmquery`.
10. [Официальный контейнер ALT](https://hub.docker.com/_/alt) — среда воспроизведения `alt:p11`.
+80
View File
@@ -0,0 +1,80 @@
rg rpmsetcmp:
```
tools/setcmp.c
12-{
13: int cmp = rpmsetcmp(s1, s2);
14- switch (cmp) {
alt/rpm.spec
709-_ Mon Nov 25 2019 Andrew Savchenko <bircoph@altlinux.org> 4.13.0.1-alt15
710:- Support rpmsetcmp profiling on E2K.
711-
--
713-- Added triggers circumvention for packagekit offline update (by Aleksei Nikiforov).
714:- Imported rpmsetcmp optimization from rpm-build.
715-
--
1094-- set.c: Increased cache size from 160 to 256 slots, 75 percent hit ratio.
1095:- set.c: Implemented 4-byte and 8-byte steppers for rpmsetcmp main loop.
1096-
--
1314-_ Mon Sep 20 2010 Alexey Tourbin <at@altlinux.ru> 4.0.4-alt98.47
1315:- set.c (rpmsetcmp): Fixed check for set2 decoding error.
1316-- brp-cleanup: Updated for /usr/lib64/perl5 and /usr/share/perl5.
lib/rpmds.c
1087- if (aset && bset) {
1088: sense = rpmsetcmp(AEVR, BEVR);
1089- if (sense < -1) {
---
rg set_new:
none
---
rg set_add
none
---
rg set_fini:
none
---
rg set_free:
none
```
---
rg setcmp:
```
Makefile.am
218-
219:noinst_PROGRAMS = setcmp
220:setcmp_SOURCES = tools/setcmp.c
221:setcmp_LDADD = lib/librpm.la
222-
alt/rpm.spec
311-rpmquery -a --requires |fgrep '= set:' |sort >R
312:join -o 1.3,2.3 P R |shuf >setcmp-data
313:time ./setcmp <setcmp-data >/dev/null
314-rm lib/set.lo lib/librpm.la
--
318-%if_with profile
319:time ./setcmp <setcmp-data >/dev/null
320-rm lib/set.lo lib/librpm.la
321-%make_build -C lib set.lo librpm.la CFLAGS="$set_c_cflags -fprofile-generate"
322:./setcmp <setcmp-data >/dev/null
323-%ifnarch %e2k
--
332-%make_build
333:time ./setcmp <setcmp-data >/dev/null
334-
```
+99
View File
@@ -0,0 +1,99 @@
rg rpmsetcmp:
```
tools/setcmp.c
12-{
13: int cmp = rpmsetcmp(s1, s2);
14- switch (cmp) {
lib/depends.c
124- if (aset && bset) {
125: sense = rpmsetcmp(AEVR, BEVR);
126- if (sense < -1) {
rpm-4_0.spec
1231-- set.c: Increased cache size from 160 to 256 slots, 75 percent hit ratio.
1232:- set.c: Implemented 4-byte and 8-byte steppers for rpmsetcmp main loop.
1233-
--
1451-* Mon Sep 20 2010 Alexey Tourbin <at@altlinux.ru> 4.0.4-alt98.47
1452:- set.c (rpmsetcmp): Fixed check for set2 decoding error.
1453-- brp-cleanup: Updated for /usr/lib64/perl5 and /usr/share/perl5.
build/reqprov.c
175- if (aset && bset) {
176: sense = rpmsetcmp(Aevr, Bevr);
177- if (sense < -1)
```
---
rg set_new:
```
tools/mkset.c
11- assert(bpp <= 32);
12: struct set *set = set_new();
13- char *line = NULL;
```
---
rg set_add:
```
tools/mkset.c
21- continue;
22: set_add(set, line);
23- added++;
```
---
rg set_fini:
```
tools/mkset.c
25- assert(added > 0);
26: const char *str = set_fini(set, bpp);
27- assert(str);
```
---
rg set_free:
none
---
rg mkset:
```
rpm-4_0.spec
431-# set-version helpers
432:%rpmattr %_rpmlibdir/mkset
433-%rpmattr %_rpmlibdir/setcmp
--
1458-- rpmlibprov.c: Added rpmlib(SetVersions) feature.
1459:- %_rpmlibdir/mkset: Command-line helper for making set-versions.
1460-- lib.prov: Implemented soname set-versioning with exported symbols.
tools/Makefile.am
37- relative \
38: mkset \
39- setcmp \
--
62-
63:mkset_SOURCES = mkset.c
64-setcmp_SOURCES = setcmp.c
autodeps/lib.prov.in
112- Info "$f: $n symbols, $bpp bpp"
113: set=$(printf '%s\n' "$sym" |"${RPMB_TOOLS_DIR-@RPMCONFIGDIR@}/mkset" "$bpp")
114- printf '%s = %s\n' "$provname" "$set"
autodeps/lib.req.in
306- #printf '%s\n' "$reqsym" |LC_ALL=C sort -c -u
307: set=$(printf '%s\n' "$reqsym" |"${RPMB_TOOLS_DIR-@RPMCONFIGDIR@}/mkset" "$bpp")
308- printf '%s >= %s\n' "$dep" "$set"
```