{"id":455218,"date":"2025-04-09T03:00:05","date_gmt":"2025-04-09T03:00:05","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=455218"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=455218","title":{"rendered":"<span>Building Debian Packages for PHP Extensions<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><strong><em>Disclaimer:<\/em><\/strong><em> Initially, this document was intended for our internal documentation repository, but the topic turned out to be not-so-internal and possibly interesting to the broader community. So here we are\u2014publishing the doc, and even the code repositories landed on our <\/em><a href=\"https:\/\/github.com\/habralab\/\" rel=\"noopener noreferrer nofollow\"><em>GitHub<\/em><\/a><em>. That\u2019s how it goes.<\/em><\/p>\n<p><em>A small note: this article omits some important aspects of a Debian Maintainer\u2019s responsibilities, such as proper release naming, lintian compliance, code signing, and generally doing everything by the book to publish a package publicly according to strict Debian guidelines. This is an internal build. But everything is in your hands. If you&#8217;re a bearded Linux maintainer, this may all seem painfully na\u00efve to you\u2014please try not to burst out laughing.<\/em><\/p>\n<p>We use PHP extensively in our projects\u2014for better or worse, trendy or outdated, it works, doesn\u2019t ask for much, and gets the job done. Also, all this runs in stateful containers within a Debian-like OS environment (in our case, Ubuntu 22.04 LTS, though the exact distro isn\u2019t critical).<\/p>\n<p><strong>The problem:<\/strong> Historically, we\u2019ve relied on some fairly obscure PHP extensions\u2014poorly maintained, barely supported by their authors, and completely absent from standard Debian package sources. We follow the principle of \u201cdo it right or don\u2019t do it at all,\u201d so <abbr class=\"habraabbr\" title=\"This term is a remnant of a historical phenomenon from the early days of Linux. It refers to installing software into the system without using a package manager\u2014simply by running make install.\" data-title=\"&lt;p&gt;This term is a remnant of a historical phenomenon from the early days of Linux. It refers to installing software into the system without using a package manager\u2014simply by running &lt;code data-mark=&quot;code&quot;&gt;make install&lt;\/code&gt;.&lt;\/p&gt;\" data-image=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/826\/bf3\/673\/826bf367353b4e4b46b9efe815495cad.jpeg\" data-abbr=\"Slackware-style\" data-image-width=\"625\" data-image-height=\"500\">Slackware-style<\/abbr> binary installs outside package managers are discouraged. Hence, we need to build proper .deb packages for PHP extensions without breaking compatibility with the surrounding environment.<\/p>\n<p>The specific PHP extensions in question\u2014where this all started\u2014are <a href=\"https:\/\/github.com\/alexeyrybak\/blitz\" rel=\"noopener noreferrer nofollow\">Blitz<\/a> (by <a href=\"https:\/\/github.com\/alexeyrybak\" rel=\"noopener noreferrer nofollow\">Alexey Rybak<\/a>) and <a href=\"https:\/\/github.com\/arnaud-lb\/php-rdkafka\" rel=\"noopener noreferrer nofollow\">php-rdkafka<\/a> (by <a href=\"https:\/\/github.com\/arnaud-lb\" rel=\"noopener noreferrer nofollow\">Arnaud Le Blanc<\/a>). They have similar issues, though Blitz&#8217;s are more severe: no packages in standard repos, last release was ages ago, and since then, the author and community have added a bunch of useful patches\u2014some of which fix compatibility issues with newer PHP versions that didn\u2019t exist when the last release was made. In short: no packages, outdated releases, and valuable patches in HEAD.<\/p>\n<p>Let\u2019s go get our homebrew <abbr class=\"habraabbr\" title=\"\" data-title=\"&lt;p&gt;&lt;\/p&gt;\" data-image=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0d1\/491\/cbf\/0d1491cbf15f83a035103abb3db7abb1.jpg\" data-abbr=\"Debian Package Maintainer\" data-image-width=\"620\" data-image-height=\"414\">Debian Package Maintainer<\/abbr> badge.<\/p>\n<blockquote>\n<p>Hint: some of the environment files can be generated automatically or created manually\u2014but that\u2019s not always convenient. Sometimes it\u2019s easier to copy from an existing working solution, and there\u2019s nothing wrong with that.<\/p>\n<\/blockquote>\n<h4>What does a typical PHP extension build process look like?<\/h4>\n<ul>\n<li>\n<p>Install the required PHP version and its dev package (<code>phpM.N-dev<\/code>);<\/p>\n<\/li>\n<li>\n<p>Install required libraries and their dev packages (e.g., <code>librdkafka<\/code> and <code>librdkafka-dev<\/code> for <code>php-rdkafka<\/code>);<\/p>\n<\/li>\n<li>\n<p>Clone the repository or download and extract the source code archive;<\/p>\n<\/li>\n<li>\n<p>Run <code>phpize<\/code> in the extension directory;<\/p>\n<\/li>\n<li>\n<p>Run <code>.\/configure<\/code>;<\/p>\n<\/li>\n<li>\n<p>Run <code>make<\/code>;<\/p>\n<\/li>\n<li>\n<p>Run <code>make install<\/code>;<\/p>\n<\/li>\n<\/ul>\n<p>After that, find the resulting shared library and add it to <code>php.ini<\/code> so that the interpreter picks it up. Most people probably stop here\u2014especially in minimalist or stateless environments like Docker. But we\u2019re going to build packages.<\/p>\n<p>In fact, there\u2019s an entire <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/\" rel=\"noopener noreferrer nofollow\">handbook<\/a> on building Debian packages\u2014packed with useful information and highly recommended to avoid stumbling around in the dark or being surprised by \u201c<abbr class=\"habraabbr\" title=\"All that\u2019s left\u2026 is to unwrap the package\u2026 right where you need it\u2026 Linux street magic.\" data-title=\"&lt;p&gt;All that\u2019s left\u2026 is to &lt;em data-mark=&quot;italic&quot;&gt;unwrap&lt;\/em&gt; the package\u2026 &lt;em data-mark=&quot;italic&quot;&gt;right where you need it&lt;\/em&gt;\u2026 &lt;em data-mark=&quot;italic&quot;&gt;Linux street magic&lt;\/em&gt;.&lt;\/p&gt;&lt;p&gt;&lt;\/p&gt;\" data-image=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/466\/704\/cdb\/466704cdb8346f86abb3c68f70849f76.jpg\" data-abbr=\"black magic\" data-image-width=\"640\" data-image-height=\"339\">black magic<\/abbr>.\u201d I\u2019ll reference it throughout, but generally, I assume the reader has at least a basic understanding of the concepts.<\/p>\n<h4>Package Structure<\/h4>\n<p>A typical Debian package is an archive containing binaries and other payloads, plus a bit of metadata and installer control scripts (for <code>dpkg<\/code>, <code>apt<\/code>, etc.). It&#8217;s the final product, tied to a specific architecture and set of dependencies. There&#8217;s little point in unpacking it\u2014no source code inside. Technically, source code can be included in <code>deb<\/code> packages (the so-called <code>deb-src<\/code>), but we\u2019re starting from scratch here.<\/p>\n<p>Let\u2019s prep a directory for the future package. It should be empty and clean. We\u2019ll do all the work inside this directory, and its structure will be treated as root-relative. We&#8217;ll name it <code>php-something<\/code>, as convention dictates\u2014so, for us, <code>php-rdkafka<\/code>.<\/p>\n<p>All the Debian magic requires a standardized set of files inside a <code>debian<\/code> subdirectory. It\u2019s <abbr class=\"habraabbr\" title=\"Haha, well, the devil\u2019s in the details \u2014 there are a thousand and one ways to shoot yourself in the foot, with a caliber of your choice.\" data-title=\"&lt;p&gt;Haha, well, the devil\u2019s in the details \u2014 there are a thousand and one ways to shoot yourself in the foot, with a caliber of your choice.&lt;\/p&gt;\" data-abbr=\"not complicated\">not complicated<\/abbr>, especially with the help of the <code>dh-php<\/code> <a href=\"https:\/\/salsa.debian.org\/php-team\/dh-php\" rel=\"noopener noreferrer nofollow\">helper<\/a> for the <code>debhelper<\/code> toolset. Why use it? Building a <code>.deb<\/code> package is a relatively standard step-by-step process, no matter what you&#8217;re packaging\u2014from documentation to office suites. It can be done manually without helpers, but it\u2019s a titanic effort. Nix maintainers have created tons of helpers for various scenarios. Writing a <abbr class=\"habraabbr\" title=\"A Makefile is, in fact, a kind of helper for the compiler too.\" data-title=\"&lt;p&gt;A Makefile is, in fact, a kind of helper for the compiler too.&lt;\/p&gt;\" data-abbr=\"Makefile\">Makefile<\/abbr> by hand is now rare.<\/p>\n<p>The build process for Debian packages has several overrideable steps. These overrides go in <code>debian\/rules<\/code>, <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/dreq.en.html#rules\" rel=\"noopener noreferrer nofollow\">which is<\/a> a <code>Makefile<\/code> (usually empty unless customization is needed). We\u2019ll just include the <code>dh-php<\/code> helper.<\/p>\n<p><code>debian\/rules<\/code>:<\/p>\n<pre><code>#!\/usr\/bin\/make -f include \/usr\/share\/dh-php\/pkg-pecl.mk<\/code><\/pre>\n<p>Next, we need a <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/dreq.en.html#control\" rel=\"noopener noreferrer nofollow\">control file<\/a>, which belongs at <code>debian\/control<\/code>. It defines the metadata for the future package. The file follows a strict syntax described in the Debian <a href=\"https:\/\/www.debian.org\/doc\/debian-policy\/ch-controlfields.html\" rel=\"noopener noreferrer nofollow\">handbook<\/a>. The easiest approach is to take an existing control file from another package and tweak it to suit your needs.<\/p>\n<p>The contents of this file determine:<\/p>\n<ul>\n<li>\n<p>the package names and the type of software,<\/p>\n<\/li>\n<li>\n<p>the description, relevant URLs, and maintainer names,<\/p>\n<\/li>\n<li>\n<p>the list of dependencies required to build or install the package.<\/p>\n<\/li>\n<\/ul>\n<p>Since we\u2019ve started dissecting <code>php-rdkafka<\/code>, let\u2019s break down the <code>debian\/control<\/code> file from that package:<\/p>\n<pre><code>Source: php-rdkafka Section: php Priority: optional Maintainer: Habr Team &lt;servers@habr.team&gt; Uploaders: Vadim Rybalko &lt;vadim@habr.team&gt; Build-Depends: debhelper (&gt;= 10~),                dh-php (&gt;= 4~),                liblz4-dev,                libzstd-dev,                librdkafka-dev (&gt;= 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 (&gt;= 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  .<\/code><\/pre>\n<p>The <code>Source<\/code> section describes the source package, while <code>Package<\/code> defines the binary package. The metadata is pretty straightforward, and the dependencies include both general and package-specific ones.<\/p>\n<p>There\u2019s a small trick: this file will be regenerated during the build process, but if it\u2019s missing, several stages will fail. That\u2019s why we save it as <code>debian\/control.in<\/code> and also copy it to the conventional <code>debian\/control<\/code>. The <code>debian\/control.in<\/code> file acts as a template for generating the actual <code>debian\/control<\/code>.<\/p>\n<p>Why? Because the modern style for PHP packages in Debian involves including the PHP version number in the package name. Luckily, <code>dh-php<\/code> takes care of regenerating <code>debian\/control<\/code> with a long list of package sections\u2014one for each supported PHP version\u2014using <code>debian\/control.in<\/code> as the template.<\/p>\n<details class=\"spoiler\">\n<summary>At this stage, we have three files<\/summary>\n<div class=\"spoiler__content\">\n<ul>\n<li>\n<p><code>debian\/control<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control.in<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rules<\/code><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/details>\n<p>Next, we need a <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/dother.en.html#compat\" rel=\"noopener noreferrer nofollow\">compatibility file<\/a> located at <code>debian\/compat<\/code>. It&#8217;s very simple and contains just the <code>debhelper<\/code> compatibility version\u2014say, version 10:<\/p>\n<p><code>debian\/compat<\/code>:<\/p>\n<pre><code>10<\/code><\/pre>\n<p>Then there&#8217;s an optional file, <code>debian\/gbp.conf<\/code>, used by the gbp tool suite (<code>gbp-buildpackage<\/code>, <code>gbp-dch<\/code>, etc.). We&#8217;re not actually using these tools, but the file is there just in case\u2014for example, to enable automatic builds from Git tags. Honestly, we haven\u2019t mastered those utilities (maybe someday), but we still maintain a strict branch and tag structure in Git, as described here, for future use:<\/p>\n<p><code>debian\/gbp.conf<\/code>:<\/p>\n<pre><code>[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']<\/code><\/pre>\n<p>This defines that the main packaging branch is called <code>debian\/main<\/code>, each release is tagged as <code>debian\/%(version)s<\/code>, the branch for upstream source is <code>upstream<\/code>, and each upstream source set is tagged as <code>upstream\/%(version)s<\/code>. We&#8217;ll go over how this works in practice a bit later\u2014Git isn&#8217;t initialized in the directory yet anyway.<\/p>\n<details class=\"spoiler\">\n<summary>At this stage, we have five files<\/summary>\n<div class=\"spoiler__content\">\n<ul>\n<li>\n<p><code>debian\/compat<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control.in<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/gbp.conf<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rules<\/code><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/details>\n<p>Next are two <code>dh-php<\/code> specific files used to create an <code>.ini<\/code> file for automatic inclusion when the package is installed in the system. The first one, <code>debian\/php-rdkafka.php<\/code>, references the second:<\/p>\n<p><code>debian\/php-rdkafka.php<\/code>:<\/p>\n<pre><code>mod debian\/rdkafka.ini<\/code><\/pre>\n<p>The second file, <code>debian\/rdkafka.ini<\/code>, isn\u2019t much more complex.<\/p>\n<p><code>debian\/rdkafka.ini<\/code>:<\/p>\n<pre><code>extension=rdkafka.so<\/code><\/pre>\n<p>At this point, it&#8217;s a good idea to define the patch format\u2014since we\u2019ll definitely need to apply patches. There\u2019s not much choice here, and in 95% of cases, it\u2019s <code>quilt<\/code>. So we\u2019ll declare that in <code>debian\/source\/format<\/code>:<\/p>\n<pre><code>3.0 (quilt)<\/code><\/pre>\n<p>If you ever need to use <a href=\"https:\/\/lintian.debian.org\/manual\/index.html\" rel=\"noopener noreferrer nofollow\">lintian<\/a>\u2014for instance, if you&#8217;re planning to publish your build on Launchpad\u2014you\u2019ll have to learn how to work with override files. That\u2019s because the vast majority of PHP sources will generate warnings out of nowhere. For now, we can simply create a placeholder file to deal with later. It\u2019ll be <code>debian\/source.lintian-overrides<\/code> with minimal content:<\/p>\n<pre><code># Override invalid PHP license problem for PHP extensions<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>At this stage, we have ten files<\/summary>\n<div class=\"spoiler__content\">\n<ul>\n<li>\n<p><code>debian\/compat<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control.in<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/gbp.conf<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/php-rdkafka.php<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rdkafka.ini<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rules<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/source\/format<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/source.lintian-overrides<\/code><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/details>\n<p>We\u2019re almost done with the <code>debian<\/code> directory. Two items remain: the <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/dreq.en.html#changelog\" rel=\"noopener noreferrer nofollow\">changelog<\/a> file (<code>debian\/changelog<\/code>) and the patch directory\u2014but those will be covered in a separate section.<\/p>\n<p>So, <code>debian\/changelog<\/code> holds version numbers and a list of changes. It\u2019s not just a feel-good file for meatbags like most release notes\u2014it actually defines the version number used during package build. While this file can be generated from Git, I haven\u2019t mastered that part, so I edit it manually or via <code>dch<\/code>.<\/p>\n<p>It follows a <a href=\"http:\/\/www.debian.org\/doc\/debian-policy\/ch-source.html#s-dpkgchangelog\" rel=\"noopener noreferrer nofollow\">strict syntax<\/a>. Here&#8217;s what a fragment might look like:<\/p>\n<pre><code>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 &lt;vadim@bionicman.name&gt;  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 &lt;vadim@bionicman.name&gt;  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 &lt;vadim@bionicman.name&gt;  Fri, 25 Nov 2022 19:19:50 +0200  ...<\/code><\/pre>\n<p>What should you pay attention to here? Each section consists of the package name, version (or versions\u2014we\u2019ll get to that in a bit), priority, a description block, and a signature. The version number follows a specific format.<\/p>\n<p>If we\u2019re taking a release from upstream and simply want to reflect that in the changelog, we specify the version as defined by the upstream maintainer. But if we\u2019re modifying it (and we are, since we\u2019re at the very least adding Debian packaging scaffolding), then we append a revision number after a dash.<\/p>\n<p>In the example above, a plus sign is used in the version string\u2014this is a common way to express that the package supports multiple PHP versions, including older ones no longer maintained upstream. A bit later, it\u2019ll become clearer how this versioning trick works.<\/p>\n<p>If we\u2019ve added patches relative to the last upstream release, we increment the revision number after the dash again. This comes in handy when pulling in patches from merge requests in the original Git repository.<\/p>\n<p>If you don\u2019t have the source code at hand yet, you can leave <code>debian\/changelog<\/code> mostly empty and populate it later.<\/p>\n<p>Now that almost everything is ready, it\u2019s time to pull in the source code and wrap this all up into a proper package. Head to the upstream repository, check out the latest release, and save it in a subdirectory named after its version. Alternatively, download the release archive and unpack it:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/680\/a02\/692\/680a02692679458a62ebdd43adc44155.png\" alt=\"I\u2019m not a fan of that approach\u2014I prefer pulling the source from a local Git clone in a neighboring directory. That said, downloading the archive is definitely the easiest way.\" title=\"I\u2019m not a fan of that approach\u2014I prefer pulling the source from a local Git clone in a neighboring directory. That said, downloading the archive is definitely the easiest way.\" width=\"1144\" height=\"1115\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/680\/a02\/692\/680a02692679458a62ebdd43adc44155.png\"\/><\/p>\n<div><figcaption>I\u2019m not a fan of that approach\u2014I prefer pulling the source from a local Git clone in a neighboring directory. That said, downloading the archive is definitely the easiest way.<\/figcaption><\/div>\n<\/figure>\n<p>You should now have a file structure that looks like this:<\/p>\n<ul>\n<li>\n<p><code>debian\/changelog<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/compat<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control.in<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/gbp.conf<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/php-rdkafka.php<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rdkafka.ini<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rules<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/source\/format<\/code> <\/p>\n<\/li>\n<li>\n<p><code>debian\/source.lintian-overrides<\/code><\/p>\n<\/li>\n<li>\n<p><code>rdkafka-6.0.3\/...<\/code><\/p>\n<\/li>\n<\/ul>\n<p>The ellipsis represents the source code files. Now let\u2019s dig into them and find the <code>package.xml<\/code> file. This file is pretty important\u2014it contains key metadata used by <code>dh-php<\/code>. For example, it defines the range of compatible PHP versions:<\/p>\n<pre><code>...  &lt;dependencies&gt;   &lt;required&gt;    &lt;php&gt;     &lt;min&gt;7.0.0&lt;\/min&gt;     &lt;max&gt;8.99.99&lt;\/max&gt;    &lt;\/php&gt;    &lt;pearinstaller&gt;     &lt;min&gt;1.4.8&lt;\/min&gt;    &lt;\/pearinstaller&gt;   &lt;\/required&gt;  &lt;\/dependencies&gt; ...<\/code><\/pre>\n<p>As you can see, this version of <code>php-rdkafka<\/code> is not compatible with PHP 5.6. That\u2019s not a big deal for us here, but other extensions, like Blitz, may have stricter constraints\u2014with hard version ceilings for each PHP major release, and sometimes even issues with minors.<\/p>\n<p>So, let\u2019s use <code>rdkafka<\/code> as a sandbox and add support for building a package for PHP 5.6 as well. By looking through the commit history, we find that the last version compatible with PHP 5.6 is <code>4.1.2<\/code>. Download it and extract it alongside the newer one. Now we have two source directories: <code>rdkafka-6.0.3<\/code> and <code>rdkafka-4.1.2<\/code>. Each of them contains a <code>package.xml<\/code>.<\/p>\n<p>Copy both XML files into the root of your working directory: the current one as <code>package.xml<\/code>, and the one from version <code>4.1.2<\/code> as <code>package-5.xml<\/code>.<\/p>\n<p>The naming convention is important\u2014<code>dh-php<\/code> looks specifically for XML files with names matching the <code>major<\/code> or <code>major.minor<\/code> version suffix.<\/p>\n<pre><code>$ cp rdkafka-6.0.3\/package.xml package.xml $ cp rdkafka-4.1.2\/package.xml package-5.xml<\/code><\/pre>\n<p>Let\u2019s double-check the current directory structure:<\/p>\n<ul>\n<li>\n<p><code>debian\/changelog<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/compat<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control.in<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/gbp.conf<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/php-rdkafka.php<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rdkafka.ini<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rules<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/source\/format<\/code> <\/p>\n<\/li>\n<li>\n<p><code>debian\/source.lintian-overrides<\/code><\/p>\n<\/li>\n<li>\n<p><code>package-5.xml<\/code><\/p>\n<\/li>\n<li>\n<p><code>package.xml<\/code><\/p>\n<\/li>\n<li>\n<p><code>rdkafka-4.1.2\/...<\/code><\/p>\n<\/li>\n<li>\n<p><code>rdkafka-6.0.3\/...<\/code><\/p>\n<\/li>\n<\/ul>\n<p><strong><em>NB!<\/em><\/strong><em> The <\/em><code><em>package.xml<\/em><\/code><em> file may not always be present in the sources. Technically, it\u2019s a PECL manifest and isn\u2019t strictly part of the extension source itself. If it\u2019s missing, you\u2019ll need to create one manually or generate it using a special utility (which is often more trouble than it\u2019s worth). Fortunately, the file is fairly small and declarative\u2014you can safely take one from a similar package and adjust the contents quickly: metadata, changelog, and file list (which is optional, by the way).<\/em><\/p>\n<p>Now\u2019s a good time to initialize Git, just in case. Run <code>git init<\/code>, then create the upstream branch and add the source files:<\/p>\n<pre><code>(our_repo) $ git init (our_repo) $ git checkout -b upstream (our_repo) $ git add rdkafka-4.1.2 rdkafka-6.0.3<\/code><\/pre>\n<p>Commit the changes with a standard message (I borrowed one from the <code>dh-php<\/code> author <a href=\"https:\/\/salsa.debian.org\/ondrej\" rel=\"noopener noreferrer nofollow\">Ond\u0159ej Sur\u00fd<\/a>\u2019s repositories), and create a tag:<\/p>\n<pre><code>(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<\/code><\/pre>\n<p>Now create the <code>debian\/main<\/code> branch, switch to it, and merge in the corresponding upstream tag:<\/p>\n<pre><code>(our_repo) $ git checkout -b debian\/main (our_repo) $ git merge upstream\/6.0.3+4.1.2<\/code><\/pre>\n<p>Time to populate <code>debian\/changelog<\/code> as planned earlier. Pull changelog highlights from <code>package.xml<\/code> or the release page in the upstream repo, and fill in a new section in the file. At this point, everything is nearly ready for building the release.<\/p>\n<p>Let\u2019s commit what we\u2019ve got. Add everything that\u2019s left\u2014both XML files and the entire <code>debian<\/code> directory\u2014then commit to the <code>debian\/main<\/code> branch and create the tag:<\/p>\n<pre><code>(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<\/code><\/pre>\n<p>We now have two branches and two tags:<\/p>\n<pre><code>(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<\/code><\/pre>\n<h2>Building<\/h2>\n<p><strong><em>NB!<\/em><\/strong><em> Chances are it won\u2019t build successfully on the first try\u2014something will probably go wrong. But hey, it\u2019s worth a shot.<\/em><\/p>\n<p>First, install <code>build-essential<\/code> (odd if you don\u2019t already have it), along with the packages listed under <code>Build-Depends<\/code> in <code>debian\/control<\/code>. Then, install the dev packages for all PHP versions you want to target. We&#8217;re going full house here:<\/p>\n<pre><code># 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<\/code><\/pre>\n<p>I\u2019ve already got them installed:<\/p>\n<pre><code>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).<\/code><\/pre>\n<p>Great\u2014now we can build. We&#8217;ll do this in a subdirectory so we don\u2019t clutter the repository with build artifacts:<\/p>\n<pre><code>(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 &amp;&amp; dpkg-buildpackage -b -rfakeroot -us -uc<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>You can wrap all of this into a simple Makefile right away.<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>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 &amp;&amp; dpkg-buildpackage -b -rfakeroot -us -uc cd ..\/..\/  clean: test -d _build || rm -rf _build<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>You\u2019ll see a ton of output. Let\u2019s try to make sense of the key steps:<\/p>\n<ul>\n<li>\n<p>First, it prints out the package metadata\u2014which we\u2019ve already written ourselves.<\/p>\n<\/li>\n<li>\n<p>Then, the script cleans everything and creates a separate build directory for each PHP version.<\/p>\n<\/li>\n<li>\n<p>Inside each build directory, the source code is copied according to the compatibility rules from the XML files, and then <code>phpize<\/code>, <code>.\/configure<\/code>, and <code>make<\/code> are run for each PHP version.<\/p>\n<\/li>\n<li>\n<p>Next come the tests. It\u2019s important that they pass. If they fail but you\u2019re sure it\u2019s a known issue and not a real bug (say, it&#8217;s mentioned in the upstream repo\u2019s issue tracker), you can skip the test phase entirely using the environment variable <code>DEB_BUILD_OPTIONS=\"nocheck\"<\/code> before calling <code>dpkg-buildpackage<\/code>.<\/p>\n<\/li>\n<li>\n<p>After that, controlled installation takes place, and the resulting binaries are wrapped with the standard Debian PHP package structure\u2014along with the appropriate <code>.ini<\/code> file hooks for automatic inclusion.<\/p>\n<\/li>\n<li>\n<p>You\u2019ll see an absurd number of warnings\u2014ignore them. The build will eventually finish.<\/p>\n<\/li>\n<\/ul>\n<p>All the output files will land in the <code>_build<\/code> directory, something like this:<\/p>\n<pre><code>(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\/<\/code><\/pre>\n<p>And there you have it\u2014your packages, ready to be installed manually or published to your own repository. As you wish.<\/p>\n<h3>New level unlocked<\/h3>\n<p>Thought we were done? Not quite. There&#8217;s a decent chance the build won\u2019t succeed and will crash somewhere along the way. Most likely, after a two-year-old release, the build is simply broken for current PHP versions\u2014and you\u2019ll need to apply patches that the maintainer never bothered to wrap into a new release. Or maybe you\u2019ll have to write the patches yourself.<\/p>\n<p>Let\u2019s start by inspecting the upstream repository with git log:<\/p>\n<pre><code>(original_repo) $ git log commit 9cafbba8808963a373374524b0030f63d1b4c471 (HEAD -&gt; 6.x, origin\/HEAD, origin\/6.x) Author: Arnaud Le Blanc &lt;arnaud.lb@gmail.com&gt; Date:   Sat Jun 1 13:35:04 2024 +0200      Update README.md  commit bcd5004f461d1d3a5f879bb21280bdde6f6800c2 Author: cb-freddysart &lt;115113665+cb-freddysart@users.noreply.github.com&gt; Date:   Fri Jan 5 14:11:25 2024 -0500      feat: implement oauthbearer token refresh cb setter (#546)  commit b21a905832202e9051d0627036a96135f9c34da5 Author: Arnaud Le Blanc &lt;arnaud.lb@gmail.com&gt; 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 &lt;dirx@klitsche.de&gt;  commit 0aee7cf70a287d3e901787be144b7ff5a1441701 Author: Arnaud Le Blanc &lt;arnaud.lb@gmail.com&gt; Date:   Tue Jul 26 20:44:00 2022 +0200      Add private constructor on Metadata classes (#531)  commit 428a552c5219120ca456b462fd67cedd53b55325 Author: Arnaud Le Blanc &lt;arnaud.lb@gmail.com&gt; Date:   Sat Jul 2 15:15:04 2022 +0200      Update release notes  commit 413f7cce13d9456bd9bbc30402da86143574cc10 Author: Arnaud Le Blanc &lt;arnaud.lb@gmail.com&gt; Date:   Sat Jul 2 15:10:53 2022 +0200      Automation  commit 9fca57149805f0d07c0cad9a8fd0155da455f2ae (tag: 6.0.3) Author: Arnaud Le Blanc &lt;arnaud.lb@gmail.com&gt; Date:   Sat Jul 2 15:09:30 2022 +0200      release\/6.0.3 (#528)<\/code><\/pre>\n<p>Aha, there they are. Turns out there were 6 commits after the last release\u2014two of them pretty important. We\u2019ll need to grab those. Earlier, we checked out the upstream release tag into the <code>upstream<\/code> branch of our repo. We <em>could<\/em> just check out their latest HEAD, but that wouldn\u2019t be the True Way\u2122. We\u2019ll do it properly\u2014with patches.<\/p>\n<p>The Debian Maintainer\u2019s Handbook has a solid <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/modify.en.html\" rel=\"noopener noreferrer nofollow\">section<\/a> on how to patch third-party code for compatibility. That\u2019s what we\u2019ll follow. First, we need a handy alias for <code>dquilt<\/code>, which we&#8217;ll use to apply our life-saving patches. Add the following to your <code>~\/.bashrc<\/code> (or equivalent shell config\u2014zsh folks know the drill):<\/p>\n<pre><code>alias dquilt=\"quilt --quiltrc=${HOME}\/.quiltrc-dpkg\" . \/usr\/share\/bash-completion\/completions\/quilt complete -F _quilt_completion -o filenames dquilt<\/code><\/pre>\n<p>Then create the file <code>~\/.quiltrc-dpkg<\/code> with the following contents:<\/p>\n<pre><code>d=. ; while [ ! -d $d\/debian -a $(readlink -e $d) != \/ ]; do d=$d\/..; done if [ -d $d\/debian ] &amp;&amp; [ -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<\/code><\/pre>\n<p>Next, add a <code>.gitignore<\/code> file to your repo so intermediate patching or build results don\u2019t get in your way:<\/p>\n<pre><code>.pc\/ _build\/<\/code><\/pre>\n<p>The <code>.pc<\/code> directory is where all the patching magic happens. Once created, the patches will be saved to the <code>debian\/patches<\/code> directory\u2014which we don\u2019t have yet, but <code>dquilt<\/code> will handle that for us.<\/p>\n<p>Almost ready. For convenience, clone the upstream repository into a nearby directory so you can easily merge or copy from it.<\/p>\n<p>Before patching, let\u2019s define a strategy. I prefer creating a separate patch for each commit in the upstream repo, but you can also make a single patch covering a range of commits. Looking at the log, I see six commits since the last release. Let\u2019s pull the list of files changed in each one:<\/p>\n<pre><code>(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<\/code><\/pre>\n<p>Commit <code>9fca57149805f0d07c0cad9a8fd0155da455f2ae<\/code> was the release tag\u2014we\u2019ll start from the commit right after it.<\/p>\n<p>In our local repo, let\u2019s enter patching mode and create the first patch, then specify which files will be modified:<\/p>\n<pre><code>(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 <\/code><\/pre>\n<p>You can name the patch file however you want, but it\u2019s a good idea to give it something meaningful\u2014a brief summary or the commit hash, for example.<\/p>\n<p><strong>Note:<\/strong> you need to prefix the file paths with the name of the directory containing the source files. This way, <code>dquilt<\/code> will track changes to the correct files.<\/p>\n<p>Now, check out the corresponding commit in the upstream repo, and rsync the updated files into the appropriate version directory in your repo:<\/p>\n<pre><code>(original_repo) $ git checkout 413f7cce13d9456bd9bbc30402da86143574cc10 (original_repo) $ rsync -av --delete .\/ ..\/our_repo\/rdkafka-6.0.3\/<\/code><\/pre>\n<p>If everything went smoothly, <code>git status<\/code> should show three modified files. Let\u2019s generate the patch and add a description (you can use the commit message from <code>git log<\/code>):<\/p>\n<pre><code>(our_repo) $ dquilt refresh (our_repo) $ dquilt header -e<\/code><\/pre>\n<p>This results in two new files in the <code>debian<\/code> directory:<\/p>\n<ul>\n<li>\n<p><code>debian\/patches\/series<\/code> \u2014 lists all patches in the order they should be applied<\/p>\n<\/li>\n<li>\n<p><code>debian\/patches\/v6_0_3_413f7cce13d9456bd9bbc30402da86143574cc10.patch<\/code> \u2014 the actual patch file<\/p>\n<\/li>\n<\/ul>\n<p><em>Quick sanity check: if you compare the contents of the patch file with the output of <\/em><code><em>git diff<\/em><\/code><em> between the two commits, they should match. So yes, you can generate patches directly from Git\u2014but I find <\/em><code><em>dquilt<\/em><\/code><em> more convenient.<\/em><\/p>\n<p>Repeat this process for however many patches you need. One thing to watch out for: if the patch modifies <code>package.xml<\/code>, make sure to add both paths for the file (if it exists in multiple directories), and copy the updated XML file from the upstream source into the root of your working directory after applying it.<\/p>\n<pre><code>... (our_repo) $ dquilt add package.xml rdkafka-6.0.3\/package.xml ... \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 rdkafka-6.0.3\/ \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 ... (our_repo) $ cp rdkafka-6.0.3\/package.xml package.xml ...<\/code><\/pre>\n<p>Once all patches are in place, you\u2019ll have several new files in <code>debian\/patches<\/code>. Time for a new release: add a new section to <code>debian\/changelog<\/code>, and bump the revision number (the part after the dash). Then commit the changes:<\/p>\n<pre><code>(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<\/code><\/pre>\n<p>Looks good\u2014but now the repo\u2019s a mess. Because of patching, we\u2019ve got uncommitted changes in the upstream source files. We don\u2019t need those, since they\u2019re already included in the patches. Clean them up using standard methods (<code>git checkout<\/code>, <code>rm -r<\/code>, etc.).<\/p>\n<h3>Surprises<\/h3>\n<p>Expect plenty of them\u2014we\u2019re working with someone else\u2019s code, often abandoned.<\/p>\n<p>For example, there\u2019s a quirk with <code>dh-php<\/code>: you can\u2019t reliably add your own logic to <code>debian\/rules<\/code>, because override targets get intercepted by <code>dh-php<\/code>. In my case, I wanted to tweak the <code>override_dh_auto_test<\/code> target to allow tests to run, but ignore failures (a pretty common pattern). However, my changes were stubbornly ignored. I eventually concluded that dh-php was the culprit and ended up disabling the test stage entirely for the <code>php-blitz<\/code> package using the more heavy-handed <code>DEB_BUILD_OPTIONS=\"nocheck\"<\/code>.<\/p>\n<p>In another repository, the author had added some files to <code>.gitignore<\/code> <em>after<\/em> they were already committed. As a result, those files were only present in my working copy, didn\u2019t make it into my repo because of the nested <code>.gitignore<\/code>, and that broke the build.<\/p>\n<p>Some upstream patches even broke compatibility with older PHP versions. In these cases, you either need to split your releases by PHP version or carefully apply patches by hand\u2014if it\u2019s obvious what needs to be done.<\/p>\n<h3>In the End<\/h3>\n<p>So, what are our options once the build is done?<\/p>\n<ul>\n<li>\n<p>You can grab the <code>.deb<\/code> file you need from the build output and install it directly via <code>dpkg<\/code> on your target system.<\/p>\n<\/li>\n<li>\n<p>You can sign the release and push it to your private package repository.<\/p>\n<\/li>\n<li>\n<p>You can polish the whole setup to full Debian guideline compliance and submit it to a public repo, like Launchpad.<\/p>\n<\/li>\n<\/ul>\n<p>As for us\u2014we just build the packages via CI and store the results in GitLab artifacts. Everything is deployed via Ansible anyway. Still, it&#8217;s worth wrapping things up into a proper repository\u2014because it\u2019s really not that hard.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/898962\/\"> https:\/\/habr.com\/ru\/articles\/898962\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><strong><em>Disclaimer:<\/em><\/strong><em> Initially, this document was intended for our internal documentation repository, but the topic turned out to be not-so-internal and possibly interesting to the broader community. So here we are\u2014publishing the doc, and even the code repositories landed on our <\/em><a href=\"https:\/\/github.com\/habralab\/\" rel=\"noopener noreferrer nofollow\"><em>GitHub<\/em><\/a><em>. That\u2019s how it goes.<\/em><\/p>\n<p><em>A small note: this article omits some important aspects of a Debian Maintainer\u2019s responsibilities, such as proper release naming, lintian compliance, code signing, and generally doing everything by the book to publish a package publicly according to strict Debian guidelines. This is an internal build. But everything is in your hands. If you&#8217;re a bearded Linux maintainer, this may all seem painfully na\u00efve to you\u2014please try not to burst out laughing.<\/em><\/p>\n<p>We use PHP extensively in our projects\u2014for better or worse, trendy or outdated, it works, doesn\u2019t ask for much, and gets the job done. Also, all this runs in stateful containers within a Debian-like OS environment (in our case, Ubuntu 22.04 LTS, though the exact distro isn\u2019t critical).<\/p>\n<p><strong>The problem:<\/strong> Historically, we\u2019ve relied on some fairly obscure PHP extensions\u2014poorly maintained, barely supported by their authors, and completely absent from standard Debian package sources. We follow the principle of \u201cdo it right or don\u2019t do it at all,\u201d so <abbr class=\"habraabbr\" title=\"This term is a remnant of a historical phenomenon from the early days of Linux. It refers to installing software into the system without using a package manager\u2014simply by running make install.\" data-title=\"&lt;p&gt;This term is a remnant of a historical phenomenon from the early days of Linux. It refers to installing software into the system without using a package manager\u2014simply by running &lt;code data-mark=&quot;code&quot;&gt;make install&lt;\/code&gt;.&lt;\/p&gt;\" data-image=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/826\/bf3\/673\/826bf367353b4e4b46b9efe815495cad.jpeg\" data-abbr=\"Slackware-style\" data-image-width=\"625\" data-image-height=\"500\">00&#8243;>Slackware-style<\/abbr> binary installs outside package managers are discouraged. Hence, we need to build proper .deb packages for PHP extensions without breaking compatibility with the surrounding environment.<\/p>\n<p>The specific PHP extensions in question\u2014where this all started\u2014are <a href=\"https:\/\/github.com\/alexeyrybak\/blitz\" rel=\"noopener noreferrer nofollow\">Blitz<\/a> (by <a href=\"https:\/\/github.com\/alexeyrybak\" rel=\"noopener noreferrer nofollow\">Alexey Rybak<\/a>) and <a href=\"https:\/\/github.com\/arnaud-lb\/php-rdkafka\" rel=\"noopener noreferrer nofollow\">php-rdkafka<\/a> (by <a href=\"https:\/\/github.com\/arnaud-lb\" rel=\"noopener noreferrer nofollow\">Arnaud Le Blanc<\/a>). They have similar issues, though Blitz&#8217;s are more severe: no packages in standard repos, last release was ages ago, and since then, the author and community have added a bunch of useful patches\u2014some of which fix compatibility issues with newer PHP versions that didn\u2019t exist when the last release was made. In short: no packages, outdated releases, and valuable patches in HEAD.<\/p>\n<p>Let\u2019s go get our homebrew <abbr class=\"habraabbr\" title=\"\" data-title=\"&lt;p&gt;&lt;\/p&gt;\" data-image=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0d1\/491\/cbf\/0d1491cbf15f83a035103abb3db7abb1.jpg\" data-abbr=\"Debian Package Maintainer\" data-image-width=\"620\" data-image-height=\"414\">Debian Package Maintainer<\/abbr> badge.<\/p>\n<blockquote>\n<p>Hint: some of the environment files can be generated automatically or created manually\u2014but that\u2019s not always convenient. Sometimes it\u2019s easier to copy from an existing working solution, and there\u2019s nothing wrong with that.<\/p>\n<\/blockquote>\n<h4>What does a typical PHP extension build process look like?<\/h4>\n<ul>\n<li>\n<p>Install the required PHP version and its dev package (<code>phpM.N-dev<\/code>);<\/p>\n<\/li>\n<li>\n<p>Install required libraries and their dev packages (e.g., <code>librdkafka<\/code> and <code>librdkafka-dev<\/code> for <code>php-rdkafka<\/code>);<\/p>\n<\/li>\n<li>\n<p>Clone the repository or download and extract the source code archive;<\/p>\n<\/li>\n<li>\n<p>Run <code>phpize<\/code> in the extension directory;<\/p>\n<\/li>\n<li>\n<p>Run <code>.\/configure<\/code>;<\/p>\n<\/li>\n<li>\n<p>Run <code>make<\/code>;<\/p>\n<\/li>\n<li>\n<p>Run <code>make install<\/code>;<\/p>\n<\/li>\n<\/ul>\n<p>After that, find the resulting shared library and add it to <code>php.ini<\/code> so that the interpreter picks it up. Most people probably stop here\u2014especially in minimalist or stateless environments like Docker. But we\u2019re going to build packages.<\/p>\n<p>In fact, there\u2019s an entire <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/\" rel=\"noopener noreferrer nofollow\">handbook<\/a> on building Debian packages\u2014packed with useful information and highly recommended to avoid stumbling around in the dark or being surprised by \u201c<abbr class=\"habraabbr\" title=\"All that\u2019s left\u2026 is to unwrap the package\u2026 right where you need it\u2026 Linux street magic.\" data-title=\"&lt;p&gt;All that\u2019s left\u2026 is to &lt;em data-mark=&quot;italic&quot;&gt;unwrap&lt;\/em&gt; the package\u2026 &lt;em data-mark=&quot;italic&quot;&gt;right where you need it&lt;\/em&gt;\u2026 &lt;em data-mark=&quot;italic&quot;&gt;Linux street magic&lt;\/em&gt;.&lt;\/p&gt;&lt;p&gt;&lt;\/p&gt;\" data-image=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/466\/704\/cdb\/466704cdb8346f86abb3c68f70849f76.jpg\" data-abbr=\"black magic\" data-image-width=\"640\" data-image-height=\"339\">ge-height=&#187;339&#8243;>black magic<\/abbr>.\u201d I\u2019ll reference it throughout, but generally, I assume the reader has at least a basic understanding of the concepts.<\/p>\n<h4>Package Structure<\/h4>\n<p>A typical Debian package is an archive containing binaries and other payloads, plus a bit of metadata and installer control scripts (for <code>dpkg<\/code>, <code>apt<\/code>, etc.). It&#8217;s the final product, tied to a specific architecture and set of dependencies. There&#8217;s little point in unpacking it\u2014no source code inside. Technically, source code can be included in <code>deb<\/code> packages (the so-called <code>deb-src<\/code>), but we\u2019re starting from scratch here.<\/p>\n<p>Let\u2019s prep a directory for the future package. It should be empty and clean. We\u2019ll do all the work inside this directory, and its structure will be treated as root-relative. We&#8217;ll name it <code>php-something<\/code>, as convention dictates\u2014so, for us, <code>php-rdkafka<\/code>.<\/p>\n<p>All the Debian magic requires a standardized set of files inside a <code>debian<\/code> subdirectory. It\u2019s <abbr class=\"habraabbr\" title=\"Haha, well, the devil\u2019s in the details \u2014 there are a thousand and one ways to shoot yourself in the foot, with a caliber of your choice.\" data-title=\"&lt;p&gt;Haha, well, the devil\u2019s in the details \u2014 there are a thousand and one ways to shoot yourself in the foot, with a caliber of your choice.&lt;\/p&gt;\" data-abbr=\"not complicated\">icated&#187;>not complicated<\/abbr>, especially with the help of the <code>dh-php<\/code> <a href=\"https:\/\/salsa.debian.org\/php-team\/dh-php\" rel=\"noopener noreferrer nofollow\">helper<\/a> for the <code>debhelper<\/code> toolset. Why use it? Building a <code>.deb<\/code> package is a relatively standard step-by-step process, no matter what you&#8217;re packaging\u2014from documentation to office suites. It can be done manually without helpers, but it\u2019s a titanic effort. Nix maintainers have created tons of helpers for various scenarios. Writing a <abbr class=\"habraabbr\" title=\"A Makefile is, in fact, a kind of helper for the compiler too.\" data-title=\"&lt;p&gt;A Makefile is, in fact, a kind of helper for the compiler too.&lt;\/p&gt;\" data-abbr=\"Makefile\">Makefile<\/abbr> by hand is now rare.<\/p>\n<p>The build process for Debian packages has several overrideable steps. These overrides go in <code>debian\/rules<\/code>, <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/dreq.en.html#rules\" rel=\"noopener noreferrer nofollow\">which is<\/a> a <code>Makefile<\/code> (usually empty unless customization is needed). We\u2019ll just include the <code>dh-php<\/code> helper.<\/p>\n<p><code>debian\/rules<\/code>:<\/p>\n<pre><code>#!\/usr\/bin\/make -f include \/usr\/share\/dh-php\/pkg-pecl.mk<\/code><\/pre>\n<p>Next, we need a <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/dreq.en.html#control\" rel=\"noopener noreferrer nofollow\">control file<\/a>, which belongs at <code>debian\/control<\/code>. It defines the metadata for the future package. The file follows a strict syntax described in the Debian <a href=\"https:\/\/www.debian.org\/doc\/debian-policy\/ch-controlfields.html\" rel=\"noopener noreferrer nofollow\">handbook<\/a>. The easiest approach is to take an existing control file from another package and tweak it to suit your needs.<\/p>\n<p>The contents of this file determine:<\/p>\n<ul>\n<li>\n<p>the package names and the type of software,<\/p>\n<\/li>\n<li>\n<p>the description, relevant URLs, and maintainer names,<\/p>\n<\/li>\n<li>\n<p>the list of dependencies required to build or install the package.<\/p>\n<\/li>\n<\/ul>\n<p>Since we\u2019ve started dissecting <code>php-rdkafka<\/code>, let\u2019s break down the <code>debian\/control<\/code> file from that package:<\/p>\n<pre><code>Source: php-rdkafka Section: php Priority: optional Maintainer: Habr Team &lt;servers@habr.team&gt; Uploaders: Vadim Rybalko &lt;vadim@habr.team&gt; Build-Depends: debhelper (&gt;= 10~),                dh-php (&gt;= 4~),                liblz4-dev,                libzstd-dev,                librdkafka-dev (&gt;= 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 (&gt;= 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  .<\/code><\/pre>\n<p>The <code>Source<\/code> section describes the source package, while <code>Package<\/code> defines the binary package. The metadata is pretty straightforward, and the dependencies include both general and package-specific ones.<\/p>\n<p>There\u2019s a small trick: this file will be regenerated during the build process, but if it\u2019s missing, several stages will fail. That\u2019s why we save it as <code>debian\/control.in<\/code> and also copy it to the conventional <code>debian\/control<\/code>. The <code>debian\/control.in<\/code> file acts as a template for generating the actual <code>debian\/control<\/code>.<\/p>\n<p>Why? Because the modern style for PHP packages in Debian involves including the PHP version number in the package name. Luckily, <code>dh-php<\/code> takes care of regenerating <code>debian\/control<\/code> with a long list of package sections\u2014one for each supported PHP version\u2014using <code>debian\/control.in<\/code> as the template.<\/p>\n<details class=\"spoiler\">\n<summary>At this stage, we have three files<\/summary>\n<div class=\"spoiler__content\">\n<ul>\n<li>\n<p><code>debian\/control<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control.in<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rules<\/code><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/details>\n<p>Next, we need a <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/dother.en.html#compat\" rel=\"noopener noreferrer nofollow\">compatibility file<\/a> located at <code>debian\/compat<\/code>. It&#8217;s very simple and contains just the <code>debhelper<\/code> compatibility version\u2014say, version 10:<\/p>\n<p><code>debian\/compat<\/code>:<\/p>\n<pre><code>10<\/code><\/pre>\n<p>Then there&#8217;s an optional file, <code>debian\/gbp.conf<\/code>, used by the gbp tool suite (<code>gbp-buildpackage<\/code>, <code>gbp-dch<\/code>, etc.). We&#8217;re not actually using these tools, but the file is there just in case\u2014for example, to enable automatic builds from Git tags. Honestly, we haven\u2019t mastered those utilities (maybe someday), but we still maintain a strict branch and tag structure in Git, as described here, for future use:<\/p>\n<p><code>debian\/gbp.conf<\/code>:<\/p>\n<pre><code>[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']<\/code><\/pre>\n<p>This defines that the main packaging branch is called <code>debian\/main<\/code>, each release is tagged as <code>debian\/%(version)s<\/code>, the branch for upstream source is <code>upstream<\/code>, and each upstream source set is tagged as <code>upstream\/%(version)s<\/code>. We&#8217;ll go over how this works in practice a bit later\u2014Git isn&#8217;t initialized in the directory yet anyway.<\/p>\n<details class=\"spoiler\">\n<summary>At this stage, we have five files<\/summary>\n<div class=\"spoiler__content\">\n<ul>\n<li>\n<p><code>debian\/compat<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control.in<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/gbp.conf<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rules<\/code><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/details>\n<p>Next are two <code>dh-php<\/code> specific files used to create an <code>.ini<\/code> file for automatic inclusion when the package is installed in the system. The first one, <code>debian\/php-rdkafka.php<\/code>, references the second:<\/p>\n<p><code>debian\/php-rdkafka.php<\/code>:<\/p>\n<pre><code>mod debian\/rdkafka.ini<\/code><\/pre>\n<p>The second file, <code>debian\/rdkafka.ini<\/code>, isn\u2019t much more complex.<\/p>\n<p><code>debian\/rdkafka.ini<\/code>:<\/p>\n<pre><code>extension=rdkafka.so<\/code><\/pre>\n<p>At this point, it&#8217;s a good idea to define the patch format\u2014since we\u2019ll definitely need to apply patches. There\u2019s not much choice here, and in 95% of cases, it\u2019s <code>quilt<\/code>. So we\u2019ll declare that in <code>debian\/source\/format<\/code>:<\/p>\n<pre><code>3.0 (quilt)<\/code><\/pre>\n<p>If you ever need to use <a href=\"https:\/\/lintian.debian.org\/manual\/index.html\" rel=\"noopener noreferrer nofollow\">lintian<\/a>\u2014for instance, if you&#8217;re planning to publish your build on Launchpad\u2014you\u2019ll have to learn how to work with override files. That\u2019s because the vast majority of PHP sources will generate warnings out of nowhere. For now, we can simply create a placeholder file to deal with later. It\u2019ll be <code>debian\/source.lintian-overrides<\/code> with minimal content:<\/p>\n<pre><code># Override invalid PHP license problem for PHP extensions<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>At this stage, we have ten files<\/summary>\n<div class=\"spoiler__content\">\n<ul>\n<li>\n<p><code>debian\/compat<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/control.in<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/gbp.conf<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/php-rdkafka.php<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rdkafka.ini<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/rules<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/source\/format<\/code><\/p>\n<\/li>\n<li>\n<p><code>debian\/source.lintian-overrides<\/code><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/details>\n<p>We\u2019re almost done with the <code>debian<\/code> directory. Two items remain: the <a href=\"https:\/\/www.debian.org\/doc\/manuals\/maint-guide\/dreq.en.html#changelog\" rel=\"noopener noreferrer nofollow\">changelog<\/a> file (<code>debian\/changelog<\/code>) and the patch directory\u2014but those will be covered in a separate section.<\/p>\n<p>So, <code>debian\/changelog<\/code> holds version numbers and a list of changes. It\u2019s not just a feel-good file for meatbags like most release notes\u2014it actually defines the version number used during package build. While this file can be generated from Git, I haven\u2019t mastered that part, so I edit it manually or via <code>dch<\/code>.<\/p>\n<p>It follows a <a href=\"http:\/\/www.debian.org\/doc\/debian-policy\/ch-source.html#s-dpkgchangelog\" rel=\"noopener noreferrer nofollow\">strict syntax<\/a>. Here&#8217;s what a fragment might look like:<\/p>\n<pre><code>php-rdkafka (6.0.3+4.1.2-2) stable;<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-455218","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/455218","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=455218"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/455218\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=455218"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=455218"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=455218"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}