Разработка и сборка пакетов

Я хочу создать пакет для Fedora. Что мне следует знать?

См. здесь и здесь.

Как собрать RPM пакет в mock?

См. здесь.

Как добавить свой пакет в репозиторий Fedora и стать мейнтейнером?

См. здесь.

Что такое Koji?

Fedora Koji – это автоматизированная среда для сборки пакетов для Fedora.

Хочу внести свои правки в пакет и пересобрать его для личных нужд. Как проще это сделать?

Установим утилиты fedpkg и mock:

sudo dnf install fedpkg mock

Скачаем исходники необходимого пакета foo-bar:

fedpkg clone -a foo-bar

Перейдём в каталог с загруженными исходниками и переключимся на ветку для конкретной версии Fedora (если нужна версия из Rawhide – следует использовать master):

cd foo-bar
fedpkg switch-branch f38

Внесём свои правки, сделаем коммит в репозиторий:

git add -A
git commit -m "Description of our changes."

Запустим автоматическую сборку в mock:

fedpkg mockbuild

Как создать tarball с исходниками из Git репозитория?

Если проект по какой-либо причине не поставляет готовые тарболы и отсутствует возможность их скачать напрямую с хостинга VCS, можно создать их из Git.

Клонируем репозиторий источника:

git clone https://example.org/foo-bar.git

Создадим архив с исходниками:

git archive --format=tar --prefix=foo-bar-1.0.0/ HEAD | gzip > ~/rpmbuild/SOURCES/foo-bar-1.0.0.tar.gz

Здесь HEAD – указатель на актуальный коммит (вместо этого можно использовать SHA1 хеш любого коммита, а также имя тега или ветки), foo-bar – название проекта, а 1.0.0 – его версия.

Как переопределить пакет в Koji репозитория RPM Fusion?

Создание build override для репозитория f38-free:

koji-rpmfusion tag f38-free-override foo-bar-1.0-1.fc38

Удаление build override для репозитория f38-free:

koji-rpmfusion untag f38-free-override foo-bar-1.0-1.fc38

Как обновить кэши репозиториев Koji в RPM Fusion?

Запустим обновление кэшей репозиториев для free:

koji-rpmfusion regen-repo f38-free-build --nowait

Запустим обновление кэшей репозиториев для nonfree:

koji-rpmfusion regen-repo f38-nonfree-build --nowait

Как настроить Git для работы с почтовым сервисом Gmail?

Для того, чтобы использовать функцию git send-mail с почтовым сервисом Gmail, необходимо:

  1. включить двухфакторную аутентификацию в настройках Google аккаунта;

  2. в настройках безопасности почтового ящика Gmail разрешить использование «небезопасных приложений» (под небезопасными Google понимает любые, не поддерживающие OAuth2);

  3. там же включить доступ к почте посредством POP3 или IMAP (это активирует также и необходимый для нас протокол SMTP);

  4. в настройках безопасности сгенерировать новый пароль для приложения;

  5. указать в файле ~/.gitconfig параметры почтового сервиса;

  6. когда будет запрошен пароль, ввести созданный ранее пароль приложения.

Пример файла ~/.gitconfig для работы с почтовым сервисом Gmail:

[sendemail]
    smtpEncryption = tls
    smtpServer = smtp.gmail.com
    smtpUser = yourname@gmail.com
    smtpServerPort = 587

Правильно ли использовать dlopen для загрузки динамических библиотек в приложении?

Для загрузки динамических библиотек в приложении использовать dlopen допускается, но мы настоятельно рекомендуем избегать этого и использовать полноценную линковку по следующим причинам:

  1. в каждом дистрибутиве GNU/Linux именование библиотек, особенно если у них нет чётко установленной апстримом SOVERSION константы, ложится на плечи мейнтейнеров. К примеру есть популярная libcurl. Во всех дистрибутивах она линкуется с openssl и называется libcurl.so, а в Debian и Ubuntu была переименована в libcurl-gnutls.so из-за линковки с gnutls;

  2. нет никакой гарантии, что загрузится именно необходимая версия библиотеки, имеющая необходимую функцию, а отсутствии оной приложение будет аварийно завершено с ошибкой сегментирования;

  3. если существует несколько версий библиотеки с разными SOVERSION, необходимо самостоятельно их искать на диске и подгружать с рядом хаков ибо имя libfoo.so без указанной SOVERSION в большинстве дистрибутивов представляет собой символическую ссылку и доступен лишь после установки соответствующего development пакета. Соответственно на машинах обычных пользователей он отсутствует;

  4. о библиотеках, подгружаемых динамически, не в курсе LD, а следовательно он не сможет при загрузке образа приложения подгрузить их в память;

  5. в случае корректной линковки LD перед запуском приложения осуществит автоматический поиск необходимых экспортируемых функций во всех указанных библиотеках. При их отсутствии приложение не будет запущено;

  6. при сборке пакета динамически подгружаемые через dlopen библиотеки не будут определены и прописаны в качестве зависимостей пакета, что может вызвать проблемы у пользователей и падение приложения;

Как получить полный список установленных переменных окружения в текущем терминале?

Получить список установленных переменных окружения можно посредством выполнения утилиты env:

env

Как получить полный список установленных переменных для запущенного процесса?

Получение списка установленных переменных окружения для запущенных процессов:

cat /proc/$PID/environ

Здесь $PIDPID процесса, информацию о котором необходимо получить.

Как задать переменную окружения?

Вариант 1. Запуск процесса с заданной переменной окружения:

FOO=BAR /usr/bin/foo-bar

Вариант 2. Экспорт переменной окружения в запущенном терминале и дальнейший запуск приложения:

export FOO=BAR
/usr/bin/foo-bar

Вариант 3. Модификация директивы Exec= в ярлыке запуска приложения:

Exec=env FOO=BAR /usr/bin/foo-bar

Как удалить переменную окружения?

Вариант 1. Удаление экспортированной переменной окружения при помощи команды оболочки unset:

unset FOO

Вариант 2. Удаление экспортированной переменной окружения в запущенном терминале и дальнейший запуск приложения:

unset FOO
/usr/bin/foo-bar

Вариант 3. Модификация директивы Exec= в ярлыке запуска приложения:

Exec=env -u FOO /usr/bin/foo-bar

Как правильно настроить Git для работы?

Сначала укажем своё имя и адрес электронной почты:

git config --global user.name "Your Name"
git config --global user.email email@example.org

Установим предпочитаемый текстовый редактор для работы с коммитами:

git config --global core.editor vim

Я хочу внести правки в проект. Как правильно отправить их в апстрим?

Если проект хостится на одном из популярных сервисов (GitHub, BitBucket или GitLab), сначала войдём в свой аккаунт (при осутствии создадим) и сделаем форк репозитория.

Осуществим базовую настройку Git клиента если это ещё не было сделано ранее.

Клонируем наш форк:

git clone git@github.com:YOURNAME/foo-bar.git

Создадим ветку new_feature для наших изменений (для каждого крупного изменения следует создавать отдельную ветку и ни в коем случае не коммитить в master):

git checkout -b new_feature

Внесём свои правки в проект, затем осуществим их фиксацию:

git add -A
git commit -s

В появившемся текстовом редакторе укажем подробное описание всех наших изменений на английском языке. Несмотря на то, что параметр -s является опциональным, большинство проектов требуют его использования для автоматического создания подписи вида:

Signed-off-by: Your Name <email@example.org>

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

git remote add upstream https://github.com/foo/foo-bar.git

Скачаем актуальные изменения и выполним rebase основной ветки нашего форка с апстримом:

git fetch upstream
git checkout master
git merge upstream/master

Осуществим rebase ветки с нашими изменениями с основной:

git checkout new_feature
git rebase master

Отправим наши изменения на сервер:

git push -u origin new_feature

Создадим новый Pull Request.

Как скомпилировать простую программу на языке C++ из консоли?

Установим компилятор GCC-C++ (G++) и ряд вспомогательных компонентов:

sudo dnf install gcc-c++ rpm-build

Создадим простейший пример helloworld.cpp:

#include <iostream>

int main(int argc, char *argv[], char *env[])
{
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

Скомпилируем и слинкуем его:

g++ $(rpm -E %{optflags}) -fPIC helloworld.cpp -o helloworld $(rpm -E %{build_ldflags}) -lstdc++

Здесь g++ – запускающий файл файл компилятора, helloworld.cpp – файл с исходным кодом (если их несколько, то разделяются пробелом), helloworld – имя результирующего бинарника, -lstdc++ – указание компоновщику на необходимость линковки со стандартной библиотекой C++.

Корректные флаги компиляции и компоновки вставляются автоматически из соответствующих макросов RPM.

Запустим результат сборки:

./helloworld

Если всё сделано верно, то увидим сообщение Hello, World! в консоли.

Приложение падает. Как мне его отладить?

Для начала рекомендуется (хотя и не обязательно) установить отладочную информацию для данного пакета:

sudo dnf debuginfo-install foo-bar

После завершения процесса отладки символы можно снова удалить.

Чтобы получить бэктрейс падения, нужно выполнить в терминале:

gdb /usr/bin/foo-bar 2>&1 | tee ~/backtrace.log

Далее в интерактивной консоли отладчика ввести: handle SIGPIPE nostop noprint и затем run, дождаться сегфолта и выполнить bt full для получения бэктрейса. Теперь можно прописать quit для выхода из режима отладки.

Далее получившийся файл ~/backtrace.log следует загрузить на любой сервис размещения текстовых файлов.

Также рекомендуется ещё сделать трассировку приложения до момента падения:

strace -o ~/trace.log /usr/bin/foo-bar

Полученный файл ~/trace.log также следует загрузить на сервис.

Безопасно ли использовать LD_PRELOAD для загрузки сторонних библиотек?

Нет, это не безопасно, т.к. существует возможность создания внутри библиотек суперглобальных конструкторов, которые будут выполняться в момент присоединения библиотеки до запуска приложения.

Создадим и скомпилируем простой пример example.c:

#include <stdio.h>

static __attribute__((constructor (200))) void bar()
{
    printf("%s", "Method bar() was called.\n");
}

static __attribute__((constructor (150))) void foo()
{
    printf("%s", "Method foo() was called.\n");
}

Данный метод содержит сразу два суперглобальных конструктора с указанием приоритетов. Чем ниже приоритет, тем скорее данный метод будет исполнен.

Скомпилируем и слинкуем наш пример:

gcc -shared $(rpm -E %{optflags}) -fPIC example.c -o example.so $(rpm -E %{build_ldflags}) -lc

Внедрим нашу библиотеку в известный доверенный процесс, например whoami:

LD_PRELOAD=./example.so whoami

Оба суперглобальных метода будут немедленно исполнены с правами запускаемого приложения и изменят его вывод:

Method foo() was called.
Method bar() was called.
user1

Разумеется, вместо безобидных вызовов функции printf() может находиться абсолютно любой код, в т.ч. вредоносный.

Как активировать LTO-оптимизации при сборке пакета?

Актуальные релизы Fedora автоматически включают LTO оптимизации для всех собираемых пакетов.

Если в проекте применяются статические библиотеки (в т.ч. для внутренних целей), то экспортируем ряд переменных окружения внутри секции %build:

export AR=%{_bindir}/gcc-ar
export RANLIB=%{_bindir}/gcc-ranlib
export NM=%{_bindir}/gcc-nm

В случае использования системы сборки cmake, воспользуемся штатной функцией переопределения встроенных параметров:

%cmake -G Ninja \
    -DCMAKE_BUILD_TYPE=RelWithDebInfo \
    -DCMAKE_AR=%{_bindir}/gcc-ar \
    -DCMAKE_RANLIB=%{_bindir}/gcc-ranlib \
    -DCMAKE_NM=%{_bindir}/gcc-nm \
    ..

В противном случае появится ошибка plugin needed to handle lto object.

Как запретить LTO-оптимизации при сборке пакета?

При необходимости LTO-оптимизации допускается отключить.

Определим переменную _lto_cflags и установим ей пустое значение:

%global _lto_cflags %{nil}

Как программно вывести список установленных пакетов, от которых никто не зависит?

В случае если функциональность плагина dnf-leaves чем-то не устраивает, напишем и скомпилируем небольшую программу на языке C, реализующую вывод списка установленных пакетов, от которых никто не зависит, средствами библиотеки libsolv.

Установим компилятор и необходимые для сборки библиотеки:

sudo dnf install gcc libsolv-devel

Создадим файл rpm-unneeded.c с исходным текстом программы:

#include <solv/pool.h>
#include <solv/poolarch.h>
#include <solv/repo_rpmdb.h>
#include <solv/solver.h>

int main(void)
{
    Pool *pool;
    Repo *rpmdb;
    Solver *solver;
    Queue q;

    pool = pool_create();
    pool_setarch(pool, NULL);
    pool_set_flag(pool, POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS, 1);

    rpmdb = repo_create(pool, "@system");
    repo_add_rpmdb(rpmdb, NULL, 0);
    pool->installed = rpmdb;

    solver = solver_create(pool);
    solver_set_flag(solver, SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES, 1);
    solver_set_flag(solver, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
    solver_set_flag(solver, SOLVER_FLAG_YUM_OBSOLETES, 1);

    queue_init(&q);
    solver_solve(solver, &q);
    solver_get_unneeded(solver, &q, 1);

    for (int i = 0; i < q.count; i++)
    {
        printf("%s\n", pool_solvid2str(pool, q.elements[i]));
    }

    solver_free(solver);
    queue_free(&q);
    pool_free(pool);

    return 0;
}

Скомпилируем и слинкуем приложение:

gcc $(rpm -E %{optflags}) -fPIC rpm-unneeded.c -o rpm-unneeded $(rpm -E %{build_ldflags}) -lsolv -lsolvext

Запустим приложение ./rpm-unneeded и получим результат.

Можно ли использовать cpack для сборки пакетов для GNU/Linux?

Нет, использовать cpack категорически не рекомендуется по следующим причинам:

  • создаёт RPM и DEB пакеты в виде архивов;

  • не добавляет метаданные в создаваемые пакеты;

  • не прописывает зависимости от библиотек и других пакетов;

  • не экспортирует provides;

  • не обрабатывает mime-типы;

  • не добавляет обязательные скриптлеты;

  • не соблюдает гайдлайны дистрибутивов.

Вместо cpack следует собирать нативные пакеты.

Приложение собрано со старой версией библиотеки. Как заставить его работать?

Если приложение было собрано со старой версией библиотеки foo-bar, которой уже нет в репозиториях и его требуется запустить, существует два способа:

  1. LD_PRELOAD – небезопасный – библиотека (или библиотеки) напрямую инъектируется в процесс средствами интерпретатора динамических библиотек LD до его непосредственного запуска;

  2. LD_LIBRARY_PATH – более безопасный – список каталогов, в которых интерпретатор динамических библиотек LD ищет соответствующие so, расширяется на указанные пользователем значения.

Рассмотрим второй способ с переопределением переменной окружения LD_LIBRARY_PATH.

Скачаем RPM пакет foo-bar необходимой версии из любого источника (лучшим вариантом будет конечно же репозитории старых версий Fedora), распакуем его например в ~/lib/foo-bar и извлечём необходимые динамические библиотеки (.so файлы).

Создадим shell-скрипт run-foo.sh для запуска бинарника:

#!/usr/bin/sh
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/lib/foo-bar
/path/to/binary/foo

Здесь foo – имя бинарника, который требуется запустить, а /path/to/binary – каталог, в котором он находится. В качестве разделителя путей LD_LIBRARY_PATH применяется двоеточие. Закрывающий слэш не ставится.

Установим скрипту разрешение не запуск и запустим его:

chmod +x run-foo.sh
./run-foo.sh

Если всё сделано верно, приложение успешно стартует.

Проекты под какими лицензиями допускается распространять в репозиториях?

См. здесь.

В каком порядке запускаются процессы через канал (пайп)?

Если запускается несколько процессов с передачей данных через канал (пайп; pipe), то все они стартуют одновременно, затем начинает выполняться первый, а остальные уходят в состояние ожидания ввода.

Можно ли использовать собственные флаги компиляции при сборке пакета?

Для любых официальных сборок следует использовать исключительно стандартные для дистрибутива флаги, предоставляемые макросами %{optflags} (флаги компилятора) и %{build_ldflags} (флаги компоновки).

Какую IDE использовать для разработки на C++ в Fedora?

Мы рекомендуем Qt Creator, которая одинаково хорошо подходит как для разработки на C++ (с Qt и без него), так и чистого C.

Установим данную IDE, а также компилятор C++ и ряд необходимых библиотек и средств для сборки проектов:

sudo dnf install gcc gcc-c++ qt-creator qt5-qtbase-devel cmake

При необходимости установим также документацию Qt и готовые примеры стандартных приложений:

sudo dnf install qt5-qtbase-doc qt5-qtbase-examples qt-creator-doc

В Qt Creator отсутствует документация. Как исправить?

Если Qt Creator при попытке загрузить документацию выдаёт ошибку Error loading: qthelp://org.qt-project.qtcreator.472/doc/index.html, выберем пункт меню ToolsOptionsHelpDocumentationAdd, затем вручную добавим следующие файлы:

/usr/share/doc/qt5/qmake.qch
/usr/share/doc/qt5/qtconcurrent.qch
/usr/share/doc/qt5/qtcore.qch
/usr/share/doc/qt5/qtdbus.qch
/usr/share/doc/qt5/qtgui.qch
/usr/share/doc/qt5/qtnetwork.qch
/usr/share/doc/qt5/qtnetworkauth.qch
/usr/share/doc/qt5/qtopengl.qch
/usr/share/doc/qt5/qtplatformheaders.qch
/usr/share/doc/qt5/qtprintsupport.qch
/usr/share/doc/qt5/qtsql.qch
/usr/share/doc/qt5/qttestlib.qch
/usr/share/doc/qt5/qtwidgets.qch
/usr/share/doc/qt5/qtxml.qch
/usr/share/doc/qt5/qtxmlpatterns.qch
/usr/share/doc/qtcreator/qtcreator.qch
/usr/share/doc/qtcreator/qtcreator-dev.qch

Изменения вступят в силу после перезапуска IDE.

В Qt Creator отсутствуют компиляторы. Как исправить?

Если Qt Creator не смог самостоятельно обнаружить установленный в системе фреймворк Qt, а также компилятор, то необходимо добавить их самостоятельно.

Для этого войдём в настройки IDE, затем сначала добавим компилятор GCC /usr/bin/gcc, а затем тулчейн Qt – /usr/bin/qmake-qt5. После этого на вкладке Kits создадим новый набор из данных компонентов.

Сохраним изменения в настройках и добавим созданный Kit к своему проекту.

Какую IDE использовать для разработки на Python в Fedora?

Мы рекомендуем PyCharm Community Edition.

Подключим COPR репозиторий:

sudo dnf copr enable phracek/PyCharm

Установим IDE:

sudo dnf install pycharm-community

При необходимости установим также набор популярных плагинов:

sudo dnf install pycharm-community-plugins

Как получить информацию о содержимом образа бинарной прошивки?

Для работы с образами прошивок можно использовать утилиту binwalk. Установим её:

sudo dnf install binwalk

Произведём анализ файла и получим результат:

binwalk foo-bar.bin

Как автоматически скачать исходники, прописанные в SPEC-файле?

Установим необходимые утилиты:

sudo dnf install rpm-build rpmdevtools

Создадим базовую иерархию каталогов для rpmbuild:

rpmdev-setuptree

Скачаем исходники, прописанные в SPEC-файле foo-bar.spec:

spectool -g -R foo-bar.spec

Как автоматически инкрементировать релиз в SPEC-файле?

Установим необходимый для работы пакет:

sudo dnf install rpmdevtools

Инкрементируем релиз SPEC-файла (директива Release) с автоматическим созданием новой строки в %changelog:

rpmdev-bumpspec -c "Updated to latest snapshot."

Как загрузить изменения во всех вложенных репозиториях из данного каталога?

Если Git репозитории были клонированы в общий каталог ~/foo-bar, то загрузим изменения в каждом из вложенных проектов при помощи find и bash:

find ~/foo-bar -maxdepth 1 ! -path . -type d -exec bash -c "pushd '{}' ; git pull ; popd" \;

Как создать пустую ветку в Git без общей истории?

Создадим новую пустую ветку foo-bar от текущего HEAD:

git checkout --orphan foo-bar

Создадим удалим всё проиндексированное содержимое данной ветки:

git reset --hard

Можно ли перенести каталоги сборки и кэшей mock на другой диск?

Система автоматической сборки пакетов mock занимает огромное количество места в корневом разделе, поэтому многие мейнтейнеры хотели бы перенести её на другой диск. Штатно это сделать не представляется возможным ибо значения каталогов по умолчанию /var/cache/mock и /var/lib/mock жёстко прописаны внутри приложения и не подлежат изменению со стороны пользователя, поэтому воспользуемся символическими ссылками.

Создадим на другом накопителе (его файловая система должна поддерживать права доступа Unix) базовый каталог для mock:

cd /media/foo-bar
sudo mkdir mock
sudo chown root:mock mock
sudo chmod 4775 mock

Переместим содержимое текущих рабочих каталогов mock:

sudo mv /var/cache/mock /media/foo-bar/mock/cache
sudo mv /var/lib/mock /media/foo-bar/mock/lib

Создадим символические ссылки на старом месте:

sudo ln -s /media/foo-bar/mock/cache /var/cache/mock
sudo ln -s /media/foo-bar/mock/lib /var/lib/mock

Зададим контекст SELinux по умолчанию для нового хранилища:

sudo semanage fcontext -a -t mock_cache_t "/media/foo-bar/mock/cache(/.*)?"
sudo semanage fcontext -a -t mock_var_lib_t "/media/foo-bar/mock/lib(/.*)?"

Сбросим контекст SELinux для всех рабочих каталогов:

sudo restorecon -Rv /var/cache/mock
sudo restorecon -Rv /var/lib/mock
sudo restorecon -Rv /media/foo-bar/mock/cache
sudo restorecon -Rv /media/foo-bar/mock/lib

Здесь /media/foo-bar – точка монтирования нового накопителя, на котором будут располагаться кэши mock.

Внимание! Раздел назначения должен использовать флаги монтирования по умолчанию defaults. В противном случае не будут выполнены скриптлеты и сборка не завершится успешно.

Как включить отображение текущей ветки Git в Bash?

Модуль интеграции с Bash входит в состав пакета Git. Добавим в приветствие Bash следующую строку:

export PS1='[\u@\h \W$(declare -F __git_ps1 &>/dev/null && __git_ps1 " (%s)")]\$ '

В качестве опциональных параметров поддерживаются GIT_PS1_SHOWDIRTYSTATE (показывать наличие незакреплённых изменений внутри каталога) и GIT_PS1_SHOWUNTRACKEDFILES (учитывать, либо нет не отслеживаемые системой контроля версий файлы):

export GIT_PS1_SHOWDIRTYSTATE=true
export GIT_PS1_SHOWUNTRACKEDFILES=true

Изменения вступят в силу при следующем запуске оболочки.

Как создать унифицированный патч изменений между двумя файлами?

Для создания патча нам необходимо две версии файла: оригинальная и текущая.

Создадим унифицрованный патч с разностью между файлами foo-bar.txt.orig (оригинальный) и foo-bar.txt (текущий):

diff -Naur foo-bar.txt.orig foo-bar.txt > result.patch

Результат будет сохранён в файле result.patch.

Как создать унифицированный патч изменений между двумя каталогами?

Создадим унифицрованный патч с разностью между каталогами foo-bar_orig (оригинальный) и foo-bar (текущий):

diff -Naur foo-bar_orig foo-bar > result.patch

Результат будет сохранён в файле result.patch.

Как применить унифицированный патч?

Проверим возможность применения патча foo-bar.patch без внесения каких-либо изменений:

patch -p0 --dry-run -i foo-bar.patch

Применим патч:

patch -p0 -i foo-bar.patch

Параметром -p задаётся количество каталогов, которые будут отброшены при поиске файлов, указанных внутри унифицированного патча.

Как откатить наложенный унифицированный патч?

Проверим возможность отката патча foo-bar.patch без внесения каких-либо изменений:

patch -p0 -R --dry-run -i foo-bar.patch

Откатитим внесённые изменения:

patch -p0 -R -i foo-bar.patch

Параметром -p задаётся количество каталогов, которые будут отброшены при поиске файлов, указанных внутри унифицированного патча.

Как создать унифицированный патч между двумя коммитами?

Создадим патч между двумя коммитами AAA и BBB:

git diff AAA BBB > result.patch

Создадим патч коммитом CCC и текущим рабочей рабочей версией:

git diff CCC > result.patch

Здесь AAA, BBB и CCC – хеши коммитов в Git репозитории.

Как экспортировать Git коммит для отправки по электронной почте?

В Git имеется встроенное средство экспорта коммитов для их дальнейшей отправки по электронной почте.

Экспортируем один коммит:

git format-patch -1

Экспортируем сразу 3 коммита:

git format-patch -3

Как авторизоваться в инфраструктуре Fedora?

Для авторизации мы должны использовать вход в домен посредством Kerberos:

kinit foo-bar@FEDORAPROJECT.ORG

Здесь foo-bar – логин в FAS. Имя домена должно быть указано строго в верхнем регистре.

Также для некоторых операций необходимо загрузить публичный ключ SSH в FAS аккаунт.

Как авторизоваться в инфраструктуре Fedora с поддержкой 2FA?

Для авторизации в инфраструктуре Fedora с поддержкой двухфакторной аутентификации стандартный вход посредством Kerberos работать не будет из-за возникновения ошибки kinit: Pre-authentication failed: Invalid argument while getting initial credentials, поэтому мы должны использовать альтернативный способ.

Сгенерируем актуальный файл Kerberos Credentials Cache:

kinit -n @FEDORAPROJECT.ORG -c FILE:$HOME/.cache/fedora-armor.ccache

Авторизуемся в домене с указанием KCC-файла:

kinit -T FILE:$HOME/.cache/fedora-armor.ccache foo-bar@FEDORAPROJECT.ORG

Когда сервер запросит ввод Enter OTP Token Value:, укажем свой пароль и текущий код из OTP-аутентификатора по схеме парольКОД без пробелов и прочих знаков.

Здесь foo-bar – логин в FAS. Имя домена должно быть указано строго в верхнем регистре.

Как запросить создание пакета в репозитории?

Сразу после завершения процедуры package review, мейнтейнер должен запросить создание пакета в репозиториях Fedora.

Установим утилиту fedpkg

sudo dnf install fedpkg

Получим новый токен в Pagure, который будет использоваться утилитой fedpkg для создания заявки. Для этого перейдём в раздел SettingsAPI KeysCreate new key, затем в списке доступных разрешений (ACLs) установим флажок только около опции Create a new ticket и нажмём кнопку Add.

Создадим файл конфигурации fedpkg:

mkdir -p ~/.config/rpkg
touch ~/.config/rpkg/fedpkg.conf

Загрузим созданный файл ~/.config/rpkg/fedpkg.conf в любом текстовом редакторе и добавим:

[fedpkg.pagure]
token = XXXXXXXXXX

Здесь XXXXXXXXXX – полученный от Pagure токен.

Запросим создание нового пакета в репозитории, а также веток для всех поддерживаемых релизов Fedora:

fedpkg request-repo --namespace rpms --monitor monitoring foo-bar YYYYYY
fedpkg request-branch --namespace rpms --repo foo-bar --all-releases

Здесь foo-bar – имя пакета, а YYYYYY – номер заявки в Red Hat BugZilla с успешно завершённым package review.

Как загрузить файлы с исходными кодами пакета в систему сборки?

После создания пакета осуществим вход в инфраструктуру Fedora, затем скачаем репозиторий пакета из Fedora SCM, содержащий SPEC файл и набор патчей (при необходимости), а также прочие службные файлы:

fedpkg clone foo-bar
cd foo-bar

Самым простым способом загрузки является импорт готового SRPM файла, поэтому выполним именно эту процедуру:

fedpkg switch-branch master
fedpkg import /путь/к/foo-bar-1.0-1.fc38.src.rpm

Проверим внесённые изменения и если всё верно, жмём Q для выхода. Зафиксируем наши изменения:

git commit -m "Initial import."

При необходимости внесём изменения и в ветки поддерживаемых релизов Fedora:

fedpkg switch-branch f38
git merge master

Отправим изменения на сервер:

git push

Как осуществить сборку пакета для публикации в репозиториях?

После загрузки файлов с исходными кодами пакета, осуществим вход в инфраструктуру Fedora, а затем приступим к непосредственно сборке в Fedora Koji:

cd foo-bar
fedpkg switch-branch master
fedpkg build

При необходимости соберём и для других поддерживаемых релизов Fedora:

fedpkg switch-branch f38
fedpkg build

Как осуществить тестовую сборку пакета для определённой архитектуры?

Осуществим вход в инфраструктуру Fedora.

Выполним стандартную scratch-сборку для всех поддерживаемых данным выпуском архитектур:

cd foo-bar
fedpkg switch-branch master
fedpkg build --scratch

Выполним scratch-сборку только для указанных архитектур:

cd foo-bar
fedpkg switch-branch master
fedpkg scratch-build --arches x86_64 aarch64

Как выложить собранный пакет в репозитории?

По окончании сборки мы можем воспользоваться Fedora Bodhi и выложить обновление в репозитории.

Сначала все обновления попадают в тестовые репозитории Fedora (updates-testing) и лишь после получения положительной кармы от других участников сообщества (уровень задаётся мейнтейнером, но не может быть меньше 1), либо по истечении 7 дней, оно может попасть в стабильные (updates) и будет доставлено конечным пользователям.

Заполним стандартную, хорошо документированную форму, затем нажмём кнопку Submit.

Что разрешается хранить в COPR репозиториях?

В COPR разрешается распространять всё то же, что и в основных репозиториях Fedora. Сборка и публикация запатентованного и проприетарного программного обеспечения в пользовательских оверлеях не допускается.

Что такое linux-vdso.so.1 и почему она загружена в каждый процесс?

Библиотека linux-vdso.so.1 не является обычной динамической библиотекой. Это виртуальный динамически разделяемый объект (VDSO), который отображается на адресное пространство каждого запущенного процесса ядром Linux и представляет собой интерфейс для осуществления быстрых системных вызовов.

Данный объект можно обнаружить в выводе ldd для любого бинарного ELF-файла, но без прямого пути, т.к. он не является реальным файлом.

Как определить зависимости конкретного бинарника?

Для определения зависимостей любых ELF-файлов, воспользуемся утилитой ldd.

Определим зависимости динамически разделяемой библиотеки:

ldd /path/to/shared/library.so.1

Определим зависимости исполняемого файла:

ldd /path/to/application

Если библиотека была найдена в системе, наряду с именем будет указан абсолютный путь к её файлу на диске, а также адрес предполагаемой загрузки.

Исключение составляют виртуальные объекты, для которых будет указан лишь адрес, без пути.

Как изменить адрес Git репозитория после его переезда?

Получим список подключённых удалённых ресурсов текущего Git репозитория:

git remote -v

Изменим апстрим для origin:

git remote set-url origin https://github.com/foo-bar/new_repo.git

После этого команды Git, отвечающие за работу с удалёнными ресурсами, git pull, git fetch, git push, начнут использовать новый апстрим.

Можно ли перенести стандартный каталог сборки rpmbuild?

Да, это возможно. Откроем файл ~/.rpmmacros в любом текстовом редаторе, найдём строку:

%_topdir %(echo $HOME)/rpmbuild

Заменим её на следующую:

%_topdir /media/foo-bar/rpmbuild

Здесь /media/foo-bar – новый каталог размещения базовой иерархии rpmbuild.

Сохраним изменения, которые вступят в силу немеденно.

Как определить какие лицензии используются в проекте?

Установим утилиту licensecheck:

sudo dnf install licensecheck

Запустим проверку проекта:

licensecheck --recursive --merge-licenses --no-verbose /path/to/foo-bar > ~/results.txt

Здесь /path/to/foo-bar – путь к распакованным исходникам проекта, а ~/results.txt – имя файла, в котором будут сохранены результаты проверки.

Как загрузить в gdb отладчик coredump падения?

GDB позволяет не только отлаживать приложения напрямую, но и загружать coredump падений.

Установим утилиту lz4 для распаковки сжатых файлов с дампами:

sudo dnf install lz4

Распакуем coredump:

unlz4 /path/to/coredump.lz4

Воспользуемся описанным выше способом получения backtrace падения, но слегка модифицируем команду запуска отладчика:

gdb /usr/bin/foo-bar /path/to/coredump 2>&1 | tee ~/backtrace.log

Здесь /usr/bin/foo-bar – путь к отлаживаемому приложению, /path/to/coredump – coredump падения (версия приложения и дампа, снятого с него, должны обязательно совпадать), а ~/backtrace.log – файл, в котором будет сохранён трейс падения.

Как собрать пакет с использованием компилятора Clang в Fedora?

Определим переменную toolchain и установим ей значение clang:

%global toolchain clang

Если в проекте применяются статические библиотеки (в т.ч. для внутренних целей), то экспортируем ряд переменных окружения внутри секции %build:

export AR=%{_bindir}/llvm-ar
export RANLIB=%{_bindir}/llvm-ranlib
export LINKER=%{_bindir}/llvm-ld
export OBJDUMP=%{_bindir}/llvm-objdump
export NM=%{_bindir}/llvm-nm

В случае использования системы сборки cmake, воспользуемся штатной функцией переопределения встроенных параметров:

%cmake -G Ninja \
    -DCMAKE_BUILD_TYPE=RelWithDebInfo \
    -DCMAKE_C_COMPILER=%{_bindir}/clang \
    -DCMAKE_CXX_COMPILER=%{_bindir}/clang++ \
    -DCMAKE_AR=%{_bindir}/llvm-ar \
    -DCMAKE_RANLIB=%{_bindir}/llvm-ranlib \
    -DCMAKE_LINKER=%{_bindir}/llvm-ld \
    -DCMAKE_OBJDUMP=%{_bindir}/llvm-objdump \
    -DCMAKE_NM=%{_bindir}/llvm-nm \
    ..

Следует быть осторожным при сборке Qt-приложений данным компилятором при использовании LTO-оптимизаций.

Qt-приложение, собранное Clang с LTO не запускается. Что делать?

Невозможность запуска Qt-приложений, собранных компилятором Clang с включёнными LTO-оптимизациями – это известная проблема, которая в настоящее время не решена.

Для её решения необходимо либо отказаться от использования компилятора Clang и вернуться на GCC, либо отключить LTO-оптимизации.

Безопасно ли использовать LDD для проверки зависимостей бинарника?

Нет, т.к. утилита ldd лишь изменяет ряд переменных окружения, а затем запускает бинарник, чтобы определить все динамически загружаемые библиотеки, от которых он зависит.

Внедоносный код сможет легко перехватить управление и начать выполнять свои задачи в пределах имеющихся полномочий.

Как удалить тег во внешнем Git репозитории?

Удалим локальный тег 1.0.0:

git tag -d v1.0.0

Удалим удалённый тег 1.0.0 из удалённого Git репозитория:

git push --delete origin v1.0.0

Как удалить ветку во внешнем Git репозитории?

Удалим удалённую ветку foo-bar из удалённого Git репозитория:

git push --delete origin foo-bar

Как удалить все теги (локальные и удалённые) в Git репозитории?

Удалим все теги из внешнего Git репозитория:

git push origin --delete $(git tag -l)

Удалим все оставшиеся локальные теги:

git tag -d $(git tag -l)

Как извлечь из Koji какие-либо данные для отправки баг-репорта?

Т.к. Koji автоматически очищает каталог сборки по её завершении с любым статусом, единственная возможность извлечь полезные данные – это закодировать их в формате Uuencode и вывести в общий журнал сборки.

Добавим в SPEC-файл пакета зависимость от sharutils:

BuildRequires: sharutils

Немного доработаем команду сборки (например %make_build), включив в неё упаковку всех необработанных preprocessed sources в архив с кодированием в UUE, при возникновении ошибки:

%make_build || (tar cf - /tmp/cc*.out | bzip2 -9 | uuencode cc.tar.bz2)

Найдём в журнале build.log блок вида:

begin 644 cc.tar.bz2
...
...
end

Извлечём и сохраним его в файл foo-bar.uue.

Установим утилиту uudecode:

sudo dnf install sharutils

Декодируем полезную нагрузку:

uudecode foo-bar.uue

Загрузим полученный архив в баг-репорт.

Как определить, какая из двух версий больше?

Для проверки версий воспользуемся утилитой rpmdev-vercmp, входящей в состав пакета rpmdevtools.

Установим его:

sudo dnf install rpmdevtools

Произведём проверку между X и Y:

rpmdev-vercmp X Y

Данную утилиту также можно использовать в различных скриптах, исходя из возвращаемых ею кодов завершения:

  • 0 – версии равны;

  • 11 – версия X больше, чем Y;

  • 12 – версия X меньше, чем Y.

Как очистить все кэши mock?

Выполним очистку сборочного каталога mock для цели по умолчанию:

mock --scrub=all

Выполним очистку сборочного каталога, а также кэша загруженных пакетов mock для fedora-rawhide-x86_64:

mock -r fedora-rawhide-x86_64 --scrub=all

Как обойти проверку приложением наличия прав суперпользователя?

См. здесь.

Как исключить определённую архитектуру из сборки пакета?

Для исключения указанных архитектур из сборки, добавим в SPEC-файл директиву ExcludeArch.

В качестве примера исключим 32-битные ARM и x86:

ExcludeArch: %{arm} %{ix86}

Как собрать пакет только для определённой архитектуры?

Для выбора конкретных архитектур сборки, добавим в SPEC-файл директиву ExclusiveArch.

В качестве примера включим только 64-битные ARM и x86:

ExcludeArch: aarch64 x86_64

Как исключить библиотеку из списка предоставляемых пакетом?

Исключим все файлы, расположенные в каталоге %{_libdir}/%{name}, из автоматически генерируемого списка предоставляемых пакетом:

%global __provides_exclude_from %{_libdir}/%{name}/.*

При необходимости указать несколько каталогов, разделим их стандартным для регулярного выражения PCRE способом:

%global __provides_exclude_from %{_libdir}/%{name}/foo/.*|%{_libdir}/%{name}/bar/.*

Как исключить библиотеку из списка зависимостей пакета?

Отключим автоматическое построение списка зависимостей пакета для ELF-объектов, расположенных в каталоге %{_libdir}/%{name}:

%global __requires_exclude_from %{_libdir}/%{name}/.*

При необходимости указать несколько каталогов, разделим их стандартным для регулярного выражения PCRE способом:

%global __requires_exclude_from %{_libdir}/%{name}/foo/.*|%{_libdir}/%{name}/bar/.*

Как собрать несколько зависящих друг от друга пакетов?

Если раньше приходилось использовать механизм «build overrides», позволяющий вручную переопределять пакеты в сборочном репозитории, то сейчас рекомендуется для сборки двух и более пакетов применять side-теги, т.к. это более простой и безопасный способ, не создающий конфликтов и проблем с зависимостями.

Мейнтейнер может запросить создание произвольного количества side-тегов и производить сборку в них по своему усмотрению.

Запросим новый side-tag для F38:

fedpkg request-side-tag --base-tag f38-build

В выводе fedpkg сообщит нам уникальный идентификатор созданного репозитория, например f38-build-side-12345.

Ждём его создания и готовности к работе:

koji wait-repo f38-build-side-XXXXX

Произведём сборку первого пакета foo:

fedpkg build --target=f38-build-side-XXXXX

Ожидаем его появления в теге:

koji wait-repo --build=foo-1.0.0-1.fc38 f38-build-side-XXXXX

Собираем все остальные пакеты тем же способом.

По окончании перейдём в Bodhi и выберем Create New UpdateUse Side Tag для выгрузки его в основной репозиторий.

Как собрать несколько зависящих друг от друга пакетов по цепочке?

Существует и более удобный способ – сборки по цепочке (chain builds), доступный для Fedora Rawhide, либо заранее созданных side-тегов.

Мейнтейнер может указать произвольное количество пакетов в пределах одного задания. Koji выполнит задачу строго в указанном порядке.

Внимание! Каталоги с исходниками всех участников группы должны находиться на одном уровне файловой иерархии. При сборке для side-tag, перед запуском процесса необходимо зайти в каталог каждого пакета из цепочки и при помощи команды fedpkg switch-branch f38 переключиться на нужную ветку.

Перейдём в каталог последнего пакета в цепочке (в данном примере это bar) и запустим процесс.

Rawhide:

cd bar
fedpkg switch-branch rawhide
fedpkg chain-build libfoo1 libfoo2

Выделенный тег:

cd bar
fedpkg switch-branch f38
fedpkg chain-build --target f38-build-side-XXXXX libfoo1 libfoo2

Данное действие создаст следующую цепочку libfoo1 -> libfoo2 -> bar.

В случае если некоторые пакеты допустимо собирать параллельно, поделим задание на подгруппы (разделителем является двоеточие).

Rawhide:

cd bar
fedpkg switch-branch rawhide
fedpkg chain-build libfoo1 : libfoo2 libfoo3 :

Выделенный тег:

cd bar
fedpkg switch-branch f38
fedpkg chain-build --target f38-build-side-XXXXX libfoo1 : libfoo2 libfoo3 :

В результате появится цепочка libfoo1 -> libfoo2 + libfoo3 -> bar.

Как определить наличие изменений ABI между разными версиями пакета?

Воспользуемся утилитой rpmsodiff, входящей в состав пакета rpmdevtools.

Установим его:

sudo dnf install rpmdevtools

Выполним проверку ABI-совместимости между версиями 1.1.0 и 1.0.0 пакета foo-bar:

rpmsodiff ./foo-bar-1.1.0-1.fc38.x86_64.rpm ./foo-bar-1.0.0-1.fc38.x86_64.rpm

Как переместить набор коммитов из одной ветки Git-репозитория в другую?

При необходимости перенести несколько коммитов из одной ветки в другую (например, сделаны некоторые правки в ветке, которые не относятся к разрабатываемой функциональности), мы можем запросить список из N коммитов первой ветки, и выполнить cherry-pick во второй.

Создадим отдельный рабочий каталог /tmp/bar для ветки bar и перейдём в него:

git worktree add /tmp/bar bar
pushd /tmp/bar

Произведём перемещение 10 коммитов из ветки foo в bar. Объединим данные действия в одну конвейерную команду:

git log --format=%h -10 foo | tac | xargs git cherry-pick

При этом будет запрошен список хешей коммитов из указанной ветки, затем он будет отсортирован в обратном порядке при помощи утилиты tac, и результат построчно передан git cherry-pick, которая впоследствие и применит каждый коммит в нужную ветку.

Вернёмся назад в основной каталог Git-репозитория и произведём очистку:

popd
git worktree remove /tmp/bar

Как применять патч при сборке пакета только при выполнении условия?

Т.к. согласно гайдлайнам Fedora, все SRPM пакеты должны быть готовы к пересборке для любой поддерживаемой версии дистрибутива, мы не можем использовать условные операторы для директив Source и Patch, поэтому сначала заменим %autosetup на %setup -q, а затем воспользуемся одним из описанных далее способов.

Способ 1. Ручной.

Применим патчи последовательно от 1 до N:

%prep
%setup -q
%if 0%{?fedora} && 0%{?fedora} >= 37
%patch -P1 -p1
%endif
...
%patch -PN -p1

Способ 2. Полуавтоматический.

Изменим нумерацию патчей в директивах Patch так, чтобы от 1 до 9 были патчи, которые требуется использовать при выполнении заданного условия, а с 10 – все остальные:

Patch1: %{name}-foo.patch
Patch10: %{name}-bar.patch
...
PatchN: %{name}-other.patch

Сначала применим условные патчи, а затем, с использованием %autopatch, все остальные, начиная с номера 10:

%prep
%setup -q
%if 0%{?fedora} && 0%{?fedora} >= 37
%patch -P1 -p1
%endif
%autopatch -M10 -p1

Как настроить автоматическую подпись Git-коммитов?

См. здесь.

Как создать Git-коммит с указанной датой?

Воспользуемся опциональным параметром --date для указания произвольной даты и времени для коммита:

git commit --date="ГГГГ-ММ-ДД ЧЧ:ММ:СС"

Внимание! Если в репозитории или глобально включена GPG-подпись коммитов, то в них будет указана текущая дата и время. Решить это можно лишь изменив настройки всей системы.