Building Debian packages for PHP extensions

Disclaimer: in general, it was planned that this document would be in our internal documentation repository, but it seemed that the topic is not very internal and may be of interest to the community. In the end, it turned out that the document was published, and even the code repositories were put on our Github. That's it.

A little more: here are omitted quite important aspects of the Debian Maintainer's work, such as: correctly specifying releases, how to pass lintian, signing the code, and in general, being a good guy and putting the package in the public according to strict Debian guidelines. This is an internal build. But everything is in your hands. If you are a bearded Linux maintainer, then for you this text may be extremely naive, please do not burst from laughter.

We actively use PHP in our projects, whether it is good or bad, fashionable or not, we will leave it out of the equation, it works, does not ask for porridge and successfully performs its functions. And we also have all this running in stateful containers under the Debian-like operating system environment (in our case, Ubuntu 22.04 LTS, but this is not very important).

Professor Fortran will not advise anything bad

Professor Fortran will not advise anything bad

Problem: Historically, we use some not very popular PHP extensions, written and somehow (almost not at all) supported by their authors, and these extensions are not in the typical sources of deb packages. We postulate the principle “do it right – it will be right”, so we do not welcome slackware-style installation of binaries into the system outside of package managers. Therefore, we will compile full-fledged deb-packages for PHP extensions, without breaking compatibility with the existing environment.

Actually, the subject PHP extensions, where it all started: Blitz for authorship Alexey Rybak And php-rdkafka name Arnaud Le Blanc. They have a similar problem, although Blitz has a more serious one: there are no packages in the standard repositories, the last release was a long time ago, and since the release tag, the author and the community have thrown in a lot of useful patches, some of which also fix compatibility issues with current versions of PHP that did not exist at the time of the last releases. In short: there are no packages, the releases were a long time ago, there are useful patches in HEAD.

Well, let's go get the achievement. Debian Package Maintainer home-brewed.

Hint: some environment files can be generated automatically or manually, but this is not always convenient. Sometimes it is easier to copy-paste from a ready-made solution, which should not be neglected.

What does a typical PHP extension build instruction look like?

  • install the required version of PHP and the assembly package for it (phpM.N-dev);

  • install the required dependent libs and their dev packages (for example librdkafka And librdkafka-dev For php-rdkafka);

  • clone the repository or download and unzip the extension source code archive;

  • execute in directory phpize;

  • to execute ./configure;

  • to execute make;

  • to execute make install;

Next, you need to find the required shared library file and add it to php.iniso that it is picked up by the interpreter. This is probably what most people do, especially in minimalist and stateless environments like Docker. But we will be building packages.

In general, there is a whole handbook how to build packages for Debian, there is a lot of useful information there, it is highly recommended to read it, so as not to poke around blindly and not be surprised where something comes from black magic occurs. Throughout the text I will provide useful references to this treatise, but overall it is assumed that the reader will be at least somewhat familiar with the basic concepts.

Package structure

A typical deb package is an archive with binaries and other payload + some meta-information and control scripts for the installer (dpkg, apt, etc.). It can only be installed, it is the final product of software assembly for a specific architecture and with specific dependencies. It is rather pointless to analyze it, there are no source codes. In fairness, source codes can also be stored in deb packages, but this is a separate category of packages, the so-called deb-srcin our case they are not there and we will have to start from scratch.

So, let's prepare a directory for the future package. It should be an empty directory without any junk inside. We will do all the work inside this directory and the file structure will be considered from this directory as the root one. Let's call the future package directory php-что-то-тамas is generally accepted, in our case let it be php-rdkafka.

All debian magic requires a set of standardized files in a subdirectory debianthere actually is nothing complicated. All the more reason to help us helper for a deb helper called dh-php. Why is it needed? In general, building a deb package is a relatively standard step-by-step procedure, regardless of what is being built, a documentation package or an office package with a bunch of dependencies. Almost everything can be built without helpers, but this will be titanic manual labor, so nix maintainers have introduced a huge number of helpers for any occasion. Yes, Makefile for packages, few people write manually now. Inside the build process for deb packages, there is a fairly large set of steps, and each of them can be redefined and customized to build what is needed as needed. These redefinitions are in the file debian/rules. By format this file is a Makefile and is usually empty (do not override anything). We add the required helper to it dh-php via include, now the assembly will be controlled dh-phpwhich already redefines a lot of things so that PHP packages are assembled “canonical”:

debian/rules:

#!/usr/bin/make -f
include /usr/share/dh-php/pkg-pecl.mk

Next we need control filewhose place is in debian/control. It specifies the meta-information of the future packet. It has strict syntaxwhich is described in the handbook, it is easiest to take the existing one from another package and finish it to the required state. The contents of this file depend on:

  • package names and software type indication

  • description, links, maintainers names

  • list of dependencies for building or installing a package

When we started to dissect php-rdkafkawe will analyze the file from this package:

debian/control:

Source: php-rdkafka
Section: php
Priority: optional
Maintainer: Habr Team <servers@habr.team>
Uploaders: Vadim Rybalko <vadim@habr.team>
Build-Depends: debhelper (>= 10~),
               dh-php (>= 4~),
               liblz4-dev,
               libzstd-dev,
               librdkafka-dev (>= 0.11~),
               php-all-dev
Standards-Version: 4.5.1
Homepage: https://pecl.php.net/package/rdkafka

Package: php-rdkafka
Priority: optional
Section: php
Architecture: amd64
Pre-Depends: php-common (>= 2:69~)
Depends: ${misc:Depends},
         ${pecl:Depends},
         ${php:Depends},
         ${shlibs:Depends}
Breaks: ${pecl:Breaks}
Replaces: ${pecl:Replaces}
Suggests: ${pecl:Suggests}
Provides: ${pecl:Provides},
          ${php:Provides}
Description: PHP-rdkafka is a stable Kafka client for PHP based on librdkafka
 .

Section Source describes the assembly, Package — binary package. Meta-information is obvious, the dependencies contain basic + specific ones for a particular package. There is a small trick: this file will be recreated during assembly, but without it there will be errors at a number of stages. Therefore, we will place this file under the name debian/control.in and we will duplicate it under the traditional one debian/control. File debian/control.in will be used as a template for creating debian/control. Why? The modern style of PHP packages for Debian implies the presence of the PHP version number in the package name, fortunately dh-php will recreate debian/control with a long list of package sections for each version of PHP, using debian/control.in as a template.

At this stage we have three files
  • debian/control

  • debian/control.in

  • debian/rules

Next we need compatibility file with the path debian/compatit is very simple and contains only the compatibility version debhelperfor example, the tenth:

debian/compat:

10

There is an optional file below debian/gbp.conf for a series of utilities gbp-buildpackage, gbp-dchetc., we don't use them, but the file is still there just in case, including for automatic build from git I use tags. Frankly speaking, utilities were not mastered, maybe some other time. But in git, nevertheless, we keep everything strictly according to the described structure of branches and tags, for the future:

debian/gbp.conf:

[DEFAULT]
debian-branch = debian/main
debian-tag = debian/%(version)s
upstream-branch = upstream
upstream-tag = upstream/%(version)s
pristine-tar = True

[dch]
meta = 1

[import-orig]
filter = ['.gitignore','debian']

It is described here that the main branch is called debian/maineach release has a tag debian/%(version)sbranch for external sources upstreameach set of sources is marked with a tag upstream/%(version)s. How all this is implemented in practice will be described below, while there is still no git in the directory.

Check: at this stage we have five files
  • debian/compat

  • debian/control

  • debian/control.in

  • debian/gbp.conf

  • debian/rules

Next are two specific ones dh-php files that are used to create an ini file for automatic include when installing the final package into the system. The first debian/php-rdkafka.php contains a link to the second one:

debian/php-rdkafka.php:

mod debian/rdkafka.ini

Second debian/rdkafka.ini not much more sophisticated:

debian/rdkafka.ini:

extension=rdkafka.so

It is worth adding a definition of the patch format right away, since we will have to apply them. The choice here is small and in 95 percent of cases it is quiltso we'll write it down in the file debian/source/format:

3.0 (quilt)

If need has forced you to use lintianfor example, you want to put your assembly on Launchpad, then you will have to master override files, since the vast majority of php sources will generate warnings out of the blue. For now, we can simply create a placeholder file, for the future, this will be the file debian/source.lintian-overrides with simple contents:

# Override invalid PHP license problem for PHP extensions
Check: at this stage we have nine files

Almost done with the directory debian. There are two entities left to analyze: change file debian/changelog and a directory of patches, but they will be discussed in a separate section. So, the file debian/changelog contains version numbers and a list of changes. This is not a useless file intended solely for a leather bag, as release notes usually are. When building a package, the version number is taken from here. In general, this file can be generated from git, but I could not master this, so I edit it manually or through dch. He has his own strict syntaxa fragment of it looks like this:

debian/changelog:

php-rdkafka (6.0.3+4.1.2-2) stable; urgency=medium

  * Release for version 6.0.3
   - Ability to provide custom `librdkafka` path during pecl install (#526, @Wirone)
  * Unreleased patches for 6.0.3
   - Automation at Sat Jul 2 15:10:53 2022 +0200
   - Update release notes at Sat Jul 2 15:15:04 2022 +0200
   - Add private constructor on Metadata classes (#531) at Tue Jul 26 20:44:00 2022 +0200
   - Test against PHP 8.3 and librdkafka 1.9, 2.3 (#545) at Mon Dec 4 15:01:53 2023 +0100
   - feat: implement oauthbearer token refresh cb setter (#546) at Fri Jan 5 14:11:25 2024 -0500
   - Update README.md at Sat Jun 1 13:35:04 2024 +0200
  * Release for version 4.1.2
   - Enabled features on windows build: headers, purge, murmur (#410, @nick-zh, @cmb69)

 -- Vadim Rybalko <vadim@bionicman.name>  Mon, 28 Jul 2024 01:08:0230 +0100

php-rdkafka (6.0.3+4.1.2-1) stable; urgency=medium

  * Release for version 6.0.3
   - Ability to provide custom `librdkafka` path during pecl install (#526, @Wirone)
  * Release for version 4.1.2
   - Enabled features on windows build: headers, purge, murmur (#410, @nick-zh, @cmb69)

 -- Vadim Rybalko <vadim@bionicman.name>  Fri, 25 Nov 2022 19:30:45 +0200

php-rdkafka (6.0.2+4.1.2-1) stable; urgency=medium

  * Release for version 6.0.2
   - Fixed signature of RdKafka\KafkaConsumer::getMetadata() (#521, @arnaud-lb)
  * Release for version 4.1.2
   - Enabled features on windows build: headers, purge, murmur (#410, @nick-zh, @cmb69)

 -- Vadim Rybalko <vadim@bionicman.name>  Fri, 25 Nov 2022 19:19:50 +0200

...

What do you need to pay attention to here? Each section consists of the package name, version or versions (more on that later), priority, descriptive part, and signature. The version number has its own format. If we took a release from upstream and wanted to add it to the changelog, then we simply specify the version in the source code maintainer's notation. If we modify (and we at least add a debian rig), then we specify the modification number through a hyphen. In the example above, a construction with a plus is used, this way you can express packages for several versions for backward compatibility with old PHP versions, the development for which was stopped by the maintainer earlier. A little further on, it will become clear how this magic works. If we added patches relative to the old release of the source code, then we iterate the modification version after the hyphen, this will be needed when adding patches from merge requests in the original git repository.

Until we have the source code, the file debian/changelog You can create it empty and stuff it later.

So, almost everything is ready, it's time to pull out the sources and put it all together. We look in the source code repository, what's going on with the releases, look for the latest one, check it out and save it to a subdirectory with the version number. Alternatively, you can download the archive and unpack it:

I don't like this approach, but I pull it out of the git turnip from the neighboring directory, nevertheless, this is the easiest.

I don't like this approach, but I pull it out of the git turnip from the neighboring directory, nevertheless, this is the easiest.

The resulting file hierarchy will be:

Where the three dots are the source code files. Now let's rummage through them and find the file package.xml. This is a pretty important file and it contains the necessary control information for dh-php. For example, indicating a corridor of compatible PHP versions:

...
 <dependencies>
  <required>
   <php>
    <min>7.0.0</min>
    <max>8.99.99</max>
   </php>
   <pearinstaller>
    <min>1.4.8</min>
   </pearinstaller>
  </required>
 </dependencies>
...

As you can see, with PHP version 5.6 this version php-rdkafka is not compatible. We don't really need it, but there are more severe options, the same Blitz has a new version ceiling for each of the major PHP releases (and in some places there are compatibility issues with minor ones). So let's practice on rdkafka and enable the ability to build a package for PHP 5.6 too. By studying the change history, we establish that the latest version compatible with 5.6 is 4.1.2. Download it, unpack it next to it. Now we see two directories with source codes: rdkafka-6.0.3 And rdkafka-4.1.2Each has its own package.xml. Next, we copy these XML files to the root of the directory. The current one under its own name package.xmland from 4.1.2 under the name package-5.xml. The naming format is important, dh-php directly searches for xml files under the required name and version major or major.minor with a hyphen:

$ cp rdkafka-6.0.3/package.xml package.xml
$ cp rdkafka-4.1.2/package.xml package-5.xml

Let's once again go through the hierarchy of files in the directory:

NB! File package.xml may not be in the sources. Strictly speaking, this is a PECL control file and has no direct relation to the extension sources. If it is not there, you will have to compile it manually, or with a special utility, which is of little use (it takes longer to figure out). The file is small, quite declarative. You can safely take it from another package and quickly rewrite the contents: metainformation, changelog and file list (which is not required, by the way).

Now is the time to initialize git just in case. Let's do it git initthen we create an upstream branch and add the source code files there:

(our_repo) $ git init
(our_repo) $ git checkout -b upstream
(our_repo) $ git add rdkafka-4.1.2 rdkafka-6.0.3

Commit with a standard text (I saw it somewhere in the author's repositories dh-php Ondrej Sury) and hang the tag:

(our_repo) $ git commit -m "New upstream version 6.0.3+4.1.2"
(our_repo) $ git tag upstream/6.0.3+4.1.2

Now let's create a branch debian/mainwe go into it and merge from the corresponding upstream tag:

(our_repo) $ git checkout -b debian/main
(our_repo) $ git merge upstream/6.0.3+4.1.2

It's time to cram in debian/changelogas planned earlier. We pull out the changelog milestones from package.xml or release page of the source code and fill in a new section in the file. Now almost everything is ready for the release assembly.

Let's throw what we got into git. We add everything that remains, these are XML files and a directory debiancommit to the branch debian/main and we hang the tag:

(our_repo) $ git add *.xml debian
(our_repo) $ git commit -m "Release for 6.0.3+4.1.2-1"
(our_repo) $ git tag debian/6.0.3+4.1.2-1

We now have two branches and two tags:

(our_repo) $ git branch
* debian/main
  upstream
(our_repo) $ git tag
debian/6.0.3+4.1.2-1
upstream/6.0.3+4.1.2

Assembly

NB! Most likely we won't be able to assemble it right away and something will go wrong. But nothing hurts to try.

We put build-essential (it's strange that you don't have it yet) and the packages listed in Build-Depends in file debian/control. Next, we install dev packages of all PHP versions for which we want to build the package. We will have a full house:

# apt install php5.6-dev php7.0-dev php7.1-dev \
php7.2-dev php7.3-dev php7.4-dev php8.0-dev \
php8.1-dev php8.2-dev php8.3-dev

I already have them:

php5.6-dev is already the newest version (5.6.40-77+ubuntu22.04.1+deb.sury.org+1).
php7.0-dev is already the newest version (7.0.33-75+ubuntu22.04.1+deb.sury.org+1).
php7.1-dev is already the newest version (7.1.33-63+ubuntu22.04.1+deb.sury.org+1).
php7.2-dev is already the newest version (7.2.34-50+ubuntu22.04.1+deb.sury.org+1).
php7.3-dev is already the newest version (7.3.33-19+ubuntu22.04.1+deb.sury.org+1).
php7.4-dev is already the newest version (1:7.4.33-13+ubuntu22.04.1+deb.sury.org+1).
php8.0-dev is already the newest version (1:8.0.30-7+ubuntu22.04.1+deb.sury.org+1).
php8.1-dev is already the newest version (8.1.29-1+ubuntu22.04.1+deb.sury.org+1).
php8.2-dev is already the newest version (8.2.21-1+ubuntu22.04.1+deb.sury.org+1).
php8.3-dev is already the newest version (8.3.9-1+ubuntu22.04.1+deb.sury.org+1).

That's great. We can build. We'll do it in a subdirectory so as not to pollute the repository with the results of the build:

(our_repo) $ mkdir -p _build/src
(our_repo) $ cp -r rdkafka-* _build/src/
(our_repo) $ cp -r debian _build/src/
(our_repo) $ cp -r package*.xml _build/src/
(our_repo) $ cd _build/src && dpkg-buildpackage -b -rfakeroot -us -uc
You can immediately formalize all this stuff into a primitive Makefile
SHELL := /bin/sh
.DEFAULT_GOAL := build

build:
	mkdir -p _build/src
	rm -rf _build/src/*
	cp -r rdkafka-* _build/src/
	cp -r debian _build/src/
	cp -r package*.xml _build/src/
	cd _build/src && dpkg-buildpackage -b -rfakeroot -us -uc
	cd ../../

clean:
	test -d _build || rm -rf _build

There will be a rich conclusion, the main points of which we will try to understand:

  • first there will be output of meta-information, we have already seen it and even wrote it ourselves. Then the script will clean everything and create its own assembly subdirectory for each version of PHP;

  • in the assembly subdirectory the source code will be copied according to the compatibility rules from XML files, it will be executed phpize, ./configure And make for each version of PHP;

  • next will be the automated tests, it is important that they pass without problems. If the tests fail and there is confidence that this is a feature and not a bug (known from issues in the source code repository), then you can cancel this stage entirely using an environment variable DEB_BUILD_OPTIONS="nocheck"inserted before dpkg-buildpackage;

  • then a controlled installation will be performed and the resulting binaries will be compiled with the etc-binding of PHP packages for automatic installation and linking with ini-files;

  • an unimaginable number of warnings will pop up, which we will ignore and the execution will end.

All files will be located in the directory _buildsomething like this:

(our_repo) $ ll _build
total 2168
drwxrwxr-x  3 vadim vadim   4096 Jul 28 00:59 ./
drwxr-xr-x  8 vadim vadim   4096 Jul 28 00:54 ../
-rw-r--r--  1 vadim vadim   1634 Jul 28 00:59 php-rdkafka-all-dev_6.0.3+4.1.2-1_amd64.deb
-rw-rw-r--  1 vadim vadim  19702 Jul 28 00:59 php-rdkafka_6.0.3+4.1.2-1_amd64.buildinfo
-rw-rw-r--  1 vadim vadim   8780 Jul 28 00:59 php-rdkafka_6.0.3+4.1.2-1_amd64.changes
-rw-r--r--  1 vadim vadim   1580 Jul 28 00:59 php-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 148470 Jul 28 00:59 php5.6-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  33582 Jul 28 00:59 php5.6-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 171860 Jul 28 00:59 php7.0-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  37500 Jul 28 00:59 php7.0-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 175604 Jul 28 00:59 php7.1-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  37358 Jul 28 00:59 php7.1-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 171798 Jul 28 00:59 php7.2-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  37250 Jul 28 00:59 php7.2-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 174434 Jul 28 00:59 php7.3-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  36804 Jul 28 00:59 php7.3-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 176770 Jul 28 00:59 php7.4-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  36780 Jul 28 00:59 php7.4-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 179426 Jul 28 00:59 php8.0-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  37282 Jul 28 00:59 php8.0-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 182130 Jul 28 00:59 php8.1-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  37518 Jul 28 00:59 php8.1-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 186640 Jul 28 00:59 php8.2-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  37508 Jul 28 00:59 php8.2-rdkafka_6.0.3+4.1.2-1_amd64.deb
-rw-r--r--  1 vadim vadim 186568 Jul 28 00:59 php8.3-rdkafka-dbgsym_6.0.3+4.1.2-1_amd64.ddeb
-rw-r--r--  1 vadim vadim  37688 Jul 28 00:59 php8.3-rdkafka_6.0.3+4.1.2-1_amd64.deb
drwxrwxr-x 15 vadim vadim   4096 Jul 28 00:59 src/

Actually, here are the bags, you can either lift your turnip or put them down with your hands – as you wish.

New level unlocked

Did you think it was the end? Well, no. Because there is a chance that it won't build and will crash somewhere. Most likely, after the release two years ago, the build basically broke for current versions of PHP, and you need to apply patches that the maintainer didn't bother to build into the new release. Or maybe write patches yourself. Let's start by studying the source code repository using git log:

(original_repo) $ git log
commit 9cafbba8808963a373374524b0030f63d1b4c471 (HEAD -> 6.x, origin/HEAD, origin/6.x)
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Sat Jun 1 13:35:04 2024 +0200

    Update README.md

commit bcd5004f461d1d3a5f879bb21280bdde6f6800c2
Author: cb-freddysart <115113665+cb-freddysart@users.noreply.github.com>
Date:   Fri Jan 5 14:11:25 2024 -0500

    feat: implement oauthbearer token refresh cb setter (#546)

commit b21a905832202e9051d0627036a96135f9c34da5
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Mon Dec 4 15:01:53 2023 +0100

    Test against PHP 8.3 and librdkafka 1.9, 2.3 (#545)
    
    
    
    Co-authored-by: Dirk Adler <dirx@klitsche.de>

commit 0aee7cf70a287d3e901787be144b7ff5a1441701
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Tue Jul 26 20:44:00 2022 +0200

    Add private constructor on Metadata classes (#531)

commit 428a552c5219120ca456b462fd67cedd53b55325
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Sat Jul 2 15:15:04 2022 +0200

    Update release notes

commit 413f7cce13d9456bd9bbc30402da86143574cc10
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Sat Jul 2 15:10:53 2022 +0200

    Automation

commit 9fca57149805f0d07c0cad9a8fd0155da455f2ae (tag: 6.0.3)
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Sat Jul 2 15:09:30 2022 +0200

    release/6.0.3 (#528)

Yeah, these guys. It turns out that 6 patches were accepted from the release, two of which are quite important. We should take them for ourselves. At one time, we checked out the source code release tag in the upstream branch of our repo. You can spit and checkout someone else's HEAD there, but that's not the true way. We'll apply patches.

How to patch someone else's code for compatibility is pretty good scheduled in the same handbook for Debian Package Maintainers. We'll follow it. We need to assemble an alias team dquiltwhich we will apply life-giving patches to. Add to your ~/.bashrc (or similar shell file) alias is something like this (zsh fans traditionally customize it for themselves):

alias dquilt="quilt --quiltrc=${HOME}/.quiltrc-dpkg"
. /usr/share/bash-completion/completions/quilt
complete -F _quilt_completion -o filenames dquilt

Next you need to create a file ~/.quiltrc-dpkg with contents:

d=. ; while [ ! -d $d/debian -a $(readlink -e $d) != / ]; do d=$d/..; done
if [ -d $d/debian ] && [ -z $QUILT_PATCHES ]; then
    # if in Debian packaging tree with unset $QUILT_PATCHES
    QUILT_PATCHES="debian/patches"
    QUILT_PATCH_OPTS="--reject-format=unified"
    QUILT_DIFF_ARGS="-p ab --no-timestamps --no-index --color=auto"
    QUILT_REFRESH_ARGS="-p ab --no-timestamps --no-index"
    QUILT_COLORS="diff_hdr=1;32:diff_add=1;34:diff_rem=1;31:diff_hunk=1;33:diff_ctx=35:diff_cctx=33"
    if ! [ -d $d/debian/patches ]; then mkdir $d/debian/patches; fi
fi

Next, we should add to our turnip .gitignoreso that intermediate patching or assembly results do not interfere with us:

.pc/
_build/

In the directory .pc all patching will take place, after which the created patches will go to the subdirectory debian/patcheswhich we don’t have yet, but everything will be done by ours dquilt on one's own.

Almost everything is ready. For convenience, we clone the source year repo somewhere nearby, so that it is convenient to merge/copy. First, we define the policy for creating patches. I prefer to make a separate patch for each commit in the source code repository, but nothing prevents us from making a common patch for the commit interval. I see that there have been 6 commits since the commit of the last release according to the log. I pull out the list of files affected by each commit:

(original_repo) $ git diff --name-only 9fca57149805f0d07c0cad9a8fd0155da455f2ae 413f7cce13d9456bd9bbc30402da86143574cc10
.github/workflows/release.yml
tools/extract-release-notes.php
tools/prepare-release.sh
(original_repo) $ git diff --name-only 413f7cce13d9456bd9bbc30402da86143574cc10 428a552c5219120ca456b462fd67cedd53b55325
package.xml
(original_repo) $ git diff --name-only 428a552c5219120ca456b462fd67cedd53b55325 0aee7cf70a287d3e901787be144b7ff5a1441701
metadata.c
metadata.stub.php
metadata_arginfo.h
metadata_broker.c
metadata_broker.stub.php
metadata_broker_arginfo.h
metadata_broker_legacy_arginfo.h
metadata_collection.c
metadata_collection.stub.php
metadata_collection_arginfo.h
metadata_collection_legacy_arginfo.h
metadata_legacy_arginfo.h
metadata_partition.c
metadata_partition.stub.php
metadata_partition_arginfo.h
metadata_partition_legacy_arginfo.h
metadata_topic.c
metadata_topic.stub.php
metadata_topic_arginfo.h
metadata_topic_legacy_arginfo.h
package.xml
php_rdkafka_priv.h
tests/metadata_001.phpt
tests/metadata_broker_001.phpt
tests/metadata_collection_001.phpt
tests/metadata_partition_001.phpt
tests/metadata_topic_001.phpt
tests/topic_partition_001.phpt
tests/topic_partition_002.phpt
topic_partition.c
(original_repo) $ git diff --name-only 0aee7cf70a287d3e901787be144b7ff5a1441701 b21a905832202e9051d0627036a96135f9c34da5
.github/workflows/test.yml
(original_repo) $ git diff --name-only b21a905832202e9051d0627036a96135f9c34da5 bcd5004f461d1d3a5f879bb21280bdde6f6800c2
.github/workflows/test/build-librdkafka.sh
conf.c
conf.h
conf.stub.php
conf_arginfo.h
conf_legacy_arginfo.h
config.m4
package.xml
tests/conf_callbacks.phpt
tests/conf_callbacks_rdkafka11.phpt
(original_repo) $ git diff --name-only bcd5004f461d1d3a5f879bb21280bdde6f6800c2 9cafbba8808963a373374524b0030f63d1b4c471
README.md

Tag 9fca57149805f0d07c0cad9a8fd0155da455f2ae was the release one, and we'll go with the next one.

In our turnip we open the patching mode and create the first patch, then we indicate which files will be changed:

(our_repo) $ dquilt new v6_0_3_413f7cce13d9456bd9bbc30402da86143574cc10.patch
(our_repo) $ dquilt add rdkafka-6.0.3/.github/workflows/release.yml \
rdkafka-6.0.3/tools/extract-release-notes.php \
rdkafka-6.0.3/tools/prepare-release.sh 

The name of the patch file is completely variable, but it is worth throwing something meaningful into it: either a three-word text summary or a commit hash.

Note that you need to add the name of the directory with the files as a path prefix. Now changes in these files will be tracked. Check in to this commit in the source code repo and rsynch the source code into the directory of the required version in our repo:

(original_repo) $ git checkout 413f7cce13d9456bd9bbc30402da86143574cc10
(original_repo) $ rsync -av --delete ./ ../our_repo/rdkafka-6.0.3/

If we haven't made any mistakes, then the control git status will show that three files have changed. We make a patch out of them and set a description (the output from git log):

(our_repo) $ dquilt refresh
(our_repo) $ dquilt header -e

After this, two new files will arrive in the directory. debian:

  • debian/patches/series — all patches are listed here in the order of application;

  • debian/patches/v6_0_3_413f7cce13d9456bd9bbc30402da86143574cc10.patch — patch file

Check: if we compare the patch file listing and the command output git diff between two commits, we will see that their volume is equivalent. That is, yes, patches can be directly pulled from git and correctly laid out. But it is more convenient for me through dquilt.

Repeat the procedure as many times as necessary. It is worth paying attention if there is an affected file among the patch files. package.xmlthen you need to add both paths of this file to the new patch, and after copying the source code files, copy this file from the source directory to the root directory.

...
(our_repo) $ dquilt add package.xml rdkafka-6.0.3/package.xml
...
копирование в rdkafka-6.0.3/ из внешнего источника
...
(our_repo) $ cp rdkafka-6.0.3/package.xml package.xml
...

When we do all the patches, we will have debian/patches there will be several new files. We are making a new release: adding a new section to debian/changelog and iterate the version number after the hyphen relative to the previous one. Add them to git:

(our_repo) $ git add debian/patches
(our_repo) $ git add debian/changelog
(our_repo) $ git commit -m "Release for 6.0.3+4.1.2-2"
(our_repo) $ git tag debian/6.0.3+4.1.2-2

Everything is great, but the turnip is a mess. Due to patching, we have uncommitted edits to the upstream release sources. We don't need them, since they are taken into account in the patches, we clean them up with available means (git checkout, rm -r).

Surprises

There will most likely be a lot of them, we work with someone else's code, often abandoned.

For example, there is a feature dh-php in that it will no longer work in debian/rules add something that is needed because override targets are intercepted dh-php. In my case, I wanted to add to the pseudo-target override_dh_auto_test condition that tests pass, but are ignored when they fail (usually this is easy to do). But my edits were ignored, which led me to conclude that this was precisely because dh-php and was forced to completely disable the testing phase for the package php-blitz resorting to a more rigid design DEB_BUILD_OPTIONS="nocheck".

In another repository, the author ignored it in his .gitignore a number of files, however, that were already in his repository. As a result, I only had them in the working copy, and they did not get into my repository because of the nested .gitignorewhich broke the assembly.

Some patches from upstream broke the build under previous versions of PHP, here either split releases by PHP versions, or apply the patch yourself, if it is obvious how.

What's the bottom line?

What liberties can we allow ourselves in terms of the result?

  • You can take the deb file we need from the ones compiled and install it via dpkg on the target system.

  • You can sign the release and push it to your own repository.

  • You can finish the entire binding until it fully complies with Debian guidelines and send it to one of the public package repositories, for example, Launchpad.

We just collect packages via CI and store the result in Gitlab artifacts, all this is installed by Ansible anyway. But it would be correct to format it as a turnip, so that it is not difficult.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *