aboutsummaryrefslogtreecommitdiffstats
path: root/posts/2022/packaging-for-arch-linux.rst
blob: 0b27eeb689d1e53946c896ad779db0b8b724cf98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
.. title: Packaging for Arch Linux
.. slug: packaging-for-arch-linux
.. date: 2022-04-06 13:22:53 UTC+02:00
.. tags: arch linux, packaging, reproducible builds, arch-repo-management, dbscripts
.. category: archlinux
.. link: 
.. description: 
.. type: text

In `Arch, a recap <https://sleepmap.de/2022/arch-a-recap>`_ I elaborated a bit
on my reasons for getting involved with Arch Linux. In this post I would like
to highlight a few technical details and give a "behind the scenes" when it
comes to packaging on and for Arch Linux.
This post is written from the viewpoint of a distribution packager, but it
is likely to contain information also useful to people packaging on different
distributions or for private purposes.

.. TEASER_END

|arch linux| is a |linux distribution|, that offers binary packages in
|software repositories| (aka. repos). To achieve this, packages are built from
source files using tooling that is developed by the distribution and various
volunteers. The resulting binary packages are then provided to users on mirrors
of the distribution (i.e. package files and their cryptographic signatures are
provided by |web servers|) and are downloaded, verified, validated and
installed using a |package manager|.

.. note::

  Other distributions may use different concepts. E.g. |gentoo linux| offers
  installation media that is used to install a base system. From then on users
  rebuild the software on their systems themselves based on distribution
  provided source files. There are no binary packages for users to install.

The upsides of a central software package system facilitating binary repos are

- users do not have to build the software on their systems themselves, which
  e.g. for web browsers can take a very long time and eat a lot of energy
- software for the entire system can be updated with one command and only takes
  as long as download and extraction of a given set of packages

Packages
========

When looking at the concept of binary software packages it probably helps to
consider the point of view from e.g. Windows and macOS, which both provide
software to users in different ways and give a good case for comparison. In
case you already know how binary packages function and compare, skip this
section.

For brevity I will skip the proprietary app stores in the below examples as
they abstract the concept of software installation to the point where this is
opaque to the user and delivers no direct comparison in the context of packages
(while under the hood most app stores use the below mentioned technologies).

Windows
-------

On Windows software is usually provided by the means of an installer (e.g.
shipped as a ``.exe`` or ``.msi`` file). An installer usually needs to be
downloaded from thirdparty websites (often without verification) and then
executed one-by-one. The installer often already contains the (prebuilt) files
to be installed (sometimes files are also downloaded by the installer
application on-the-fly), offers some form of modification (e.g. the
installation location), installs the bundled or downloaded files and modifies
the system's registry (e.g. for auto-start or other features). Although
Microsoft has attempted to consolidate its installation backends, the user
experience is usually still a mixed bag.
System updates (those modifying the operating system) are handled by the OS
itself and the user usually has not much of a say when/ how that happens (this
can be modified to some extent). Additionally, some hardware may not use the
latest version of Windows due to software-based |planned obsolescence|.

MacOS
-----

On macOS software can be installed using images or installers (shipped as e.g.
``.dmg`` and ``.pkg`` respectively). The download of the files in question
usually functions in the same way as it does on Windows (unverified downloads
from thirdparty websites). Where with images the user experience is usually
*"drag and drop"* from a mounted image to the list of applications, installers
on the other hand offer similar functionality to how installers work on Windows
(e.g. setup auto-starting).
System updates, similar to those on Windows are handled by macOS itself and
also here software enforced |planned obsolescence| is a thing.

Looking at the above examples it becomes clear, that automation on both
platforms is quite terrible: The distinction between OS updates and *"other
software"* leads to a mix and match approach towards updates, that is (if at
all) only partly remedied by externally developed and provided package managers
for some of the *"other software"* (e.g. |homebrew| or |chocolatey|), but at
best remains a fragmented experience for the user.

Linux
-----

On distributions that offer binary package repositories, users use a package
manager to install packages and to upgrade **all software** [1]_ on their
system.
Packages are essentially |archive files|, that are downloaded, verified and
extracted by the package manager. As the files contained in (distribution)
packages follow a well-defined location schema (e.g. |filesystem hierarchy
standard| or |file-hierarchy|), the system can check for file conflicts and
users can usually have reasonable assumptions about where files of a package
are located (package managers usually also track the files of all packages).
Additional functionality, such as post install scripts (e.g. to create users or
to change ownership on files) are usually
contained in package files and executed after installation. However, on systemd
based distributions, much of the post installation tasks have been streamlined
with the help of |sysusers.d| and |tmpfiles.d| (more on that later). Some
distributions also make use of non-standardized hooks (see |alpm-hooks| for how
this is implemented for |pacman|), that are used by the package manager for
certain tasks on files that are not owned by one specific package (e.g. update
font cache).

Build tooling
=============

The most basic build tooling for Arch Linux - |makepkg| - is bundled with
|pacman_website| (the package manager used to install all software packages on the
distribution). It is used in conjunction with a |PKGBUILD|, which as a package
source file describes where/ how to get a package's source files (and in which
version), how to build and test it (if applicable) and how/ where to install
it.

In case you have experience with |bash|: Both ``makepkg`` and ``PKGBUILDs`` are
written in it.

When building packages with plain ``makepkg``, the built package will be
created in the context of the user's system and as such will make use of the
software available on the user's system. While this works (given all
dependencies are met) it is not recommended to do so, as the user's
system may use custom packages or settings to ``makepkg`` (see |makepkg.conf|),
that can alter the outcome of the build, which may make the package unusable on
another system.

Clean chroots
=============

To enable builds, that are done in a clean environment (i.e. one that only has
official distribution packages installed and does not depend on configuration
or custom packages on a local system), Arch Linux and various contributors have
created special build tooling, which is contained in the |devtools| project.

With the help of |makechrootpkg| one can run ``makepkg`` in a |systemd-nspawn|
based |chroot|, which will only have the packages installed, that are required
for building, testing and running a given package.

Using |makechrootpkg| and its various repository-specific symlinks is how Arch
Linux packagers build all packages in the official repositories.

.. note::

  It is generally advisable to build **all packages** in a clean environment.

An implict upside of using ``makechrootpkg`` to build packages is, that
|checkpkg| and |namcap| are being run on the resulting package, which can give
valuable hints at possible improvements of the package.

Building packages
=================

First off: There are sometimes a lot of subtleties involved with packaging and
especially producing packages that are of good quality. In the following
sections I will discuss a few tools and packaging specifics, that may seem
quite overwhelming or complicated at first. Luckily, a lot of the tooling is
fairly well documented and it is probably always good to remember, that
everyone is a learner and that as the tooling and the best practices evolve,
this is an open-ended topic.

A good starting point is always to use ``makechrootpkg`` and to adhere to the
|arch package guidelines|.

.. note::

  There is the |arch package guidelines category| of more specific guidelines
  for various programming languages and special use-cases.

Getting package build sources
-----------------------------

The act of getting the sources for a binary package is described in the context
of the |arch build system| (ABS). While users without write access to the Arch
Linux source repositories can rely upon |asp| to get to the package build
sources, the official packagers rely on a rather organically grown packaging
workflow, that is described in |howto be a packager|.

At the time of writing, Arch Linux still relies on two monolithic |svn|
repositories for the package build sources (one for the ``[core]`` and
``[extra]`` repositories and one for the ``[community]`` and ``[multilib]``
repositories) which are exported to |git| via |git-svn| on the official Arch
Linux Github organization (|svntogit-packages| and |svntogit-community|,
respectively).

.. note::

  Work is underway to switch to |git| for the package build sources. However,
  this has implications for maintaining state of resulting binary repositories
  (this is currently done in the svn repositories). While |dbscripts| has been
  used for managing the state for many years, it is likely to be replaced by
  |arch-repo-management| in the future.
  Additionally, it should be mentioned, that such a switch is not trivial after
  20 years, if one is also concerned with the technical feasibility and
  maintainability of the tooling in use.

PKGBUILDs
---------

As mentioned earlier, |PKGBUILD| files are really just |bash| scripts, that are
being evaluated by |makepkg|. As such they define a few variables and functions
(some of which are required, others only being optional).

The below example shows a bare minimum example, derived from the prototype
files, that can be found in ``/usr/share/pacman/``:

.. code:: sh

  # Maintainer: Your Name <youremail@domain.com>
  pkgname=dummy-package
  pkgver=0.1.0
  pkgrel=1
  pkgdesc="A dummy package"
  arch=(any)
  url="https://my-upstream.link/to/dummy-package"
  license=(GPL3)
  depends=(another-package)
  optdepends=('some-additional: for additional feature X')
  source=(https://my-upstream.link/to/$pkgname-$pkgver.tar.gz)
  b2sums=('THISISADUMMYCHECKSUM')

  package() {
    make DESTDIR="$pkgdir" install -C $pkgname-$pkgver
  }

To go through the essentials of this very minimalistic example, which assumes
that we have a project using |make| to install a few files:

- While the ``Maintainer`` comment is technically not required, it is always
  helpful for others trying to contact the author of a given package build
  source
- ``pkgname``: The name of the package. Refer to the wiki section
  |pkgbuild#pkgname| for further info (e.g. restrictions)
- ``pkgver``: The (upstream) version of the package. Refer to the wiki section
  |pkgbuild#pkgver| for further info (e.g. restrictions)
- ``pkgrel``: The release version of the package, which identifies the build of
  the particular package in version ``pkgver``. This is a string specific to
  Arch Linux (see |pkgbuild#pkgrel|) and *is not related to the upstream
  version* of the software.
- ``pkgdesc``: A short description of what this package provides
- ``arch``: The architecture of the resulting package. As this is an array, it
  can contain several entries (``makepkg`` will envoke a build for each
  architecture). At the time of writing Arch Linux only supports the ``x86_64``
  and ``any`` architectures.
- ``url``: The URL of the upstream project (e.g. a website or a link to the
  |version control| sources)
- ``license``: The licenses that apply to the project. This again is an array
  and may contain several licenses. In case licenses that are not covered by
  the |licenses package| are encountered, their license files must be installed
  in the ``package()`` function (refer to the wiki section |pkgbuild#license|
  for further information).
- ``depends``: An array of runtime dependencies for the package. They will be
  installed automatically during build when building with ``makechrootpkg`` or
  ``makepkg -s``.
- ``optdepends``: An array of optional dependencies and a short description
  about their purpose. These packages will not be installed during build time
  (for this ``makedepends`` needs to be used).
- ``source``: An array of resources for ``makepkg`` to retrieve. As |makepkg|
  is able to handle various |version control| systems, local and remote files,
  as well as to rename files, it is advisable to read the relevant man page
  section for ``makepkg``.
- ``b2sums``: An array of checksums for all resources in the ``source`` array.
  It is advisable to use either (or all of) ``sh256sums``, ``sha512sums`` or
  ``b2sums`` as older hashing mechanisms are by now unsafe (see
  |pkgbuild#integrity|). The checksums are used to guard against changing (and
  potentially malicious) upstream resources. The resources and checksums for a
  new version of a given package may be retrieved and updated using
  |updpkgsums| (contained in the |pacman-contrib| package).
- ``package()``: This function defines all steps necessary to install the files
  of the upstream project to an empty location (represented by the *magic
  variable* ``"$pkgdir"``), that will contain all installable files of the
  package. This function is called using |fakeroot|, which means that to the
  installing processes it looks like they are being executed by ``root``.

PGP validation
--------------

Upstream project resources (e.g. signed source tarballs or git tags/ commits)
can be validated using |pgp|.

.. note::

  While other mechanisms are theoretically possible (e.g. |signify| or
  |cosign|), they are currently not implemented in the context of |makepkg|.

Technically all that is required for this is,
that the ``validpgpkeys`` array in the PKGBUILD contains at least one
retrievable PGP key ID and that the ``source`` array contains either a ``.sig``
or ``.asc`` file valid for one of the resources, or that a git object to be
checked is targetted using the ``?signed`` identifier (see
|makepkg#signature_checking| and |pkgbuild#using_vcs_sources|).

Although it is advisable to have cryptographic signature validation (e.g.
using |pgp|) for releases, this should only be considered under the following
circumstances in regards to an upstream project:

* there is a track record of signing releases with the same key ID and the
  project specifically provides the expectable key ID publicly (e.g. on the
  website)
* keeps a |chain of trust| between multiple and/or successive key IDs
* no key easily used by multiple users is used (e.g. Github's PGP key, which
  can be used by multiple users of a given Github project and is not handled by
  the users themselves)

The first point is usually easy to check up on, while the 2nd might require
getting in touch with the project developers if it happens (or happened in the
past) - this is the case more often than you would think and does block package
updates, as a new key ID must not be trusted without investigating the cause
for a missing |chain of trust| to prevent a potential |supply chain attack|!

.. note::

  I am contemplating to prepare an |arch linux rfc| for this, as the handling
  of PGP signed sources is not well defined for the distribution and often
  leads to mishandling (e.g. using new PGP key IDs without having a |chain of
  trust|).

The 3rd point practically provides a false sense of security: A PGP key signed
a release of a project, but in actuality multiple members of a project may have
access to this functionality. From the outside it is impossible to tell who
triggered a release and signed off on it (it could easily be malicious because
someone's Github account has been hacked).

Reproducibility
---------------

Arch Linux as a distribution is committed to packages becoming bit-for-bit
reproducible (have a look at the overarching |reproducible builds| project for
more background information on the general topic). The status of the current
packages in the official repositories is tracked on
https://reproducible.archlinux.org, which is backed by |rebuilderd|.

After building a package it can be rebuilt using |makerepropkg|, which may use
|diffoscope| on the resulting package in case it is not reproducible.

As the use of ``makerepropkg`` requires the ``PKGBUILD`` used to build the
initial package, it can not be used when only a package file is available.
However, for that use-case |repro| may be used.

.. note::

  Both tools make use of the |buildinfo| files contained in each binary
  package.

Dealing with the strange
========================

In `building packages <#building-packages>`_ we have looked at some of the more
basic use-cases. The following sub-sections will deal with more uncommon or
very specific ones as well as problems at the intersection of build tooling and
binary repository management.

Split packages
--------------

There are situations, in which one wants to build several packages from a
single ``PKGBUILD``. Those are usually:

- the documentation of the project is very large
- certain features (e.g. language bindings) are not required by the main
  application or use-case of the project
- specific functionality would require a large tree of dependencies but is not
  required for the main application or use-case

In all three cases this can be handled using a split package setup in which the
extra functionality (as a package) is declared an optional dependency of the
main package.

To create a split package, the ``pkgname`` variable of the ``PKGBUILD`` is
turned into an array, containing multiple package names, while the ``pkgbase``
variable (see |pkgbuild#pkgbase|) should be set. Additionally, the generic
``package()`` function needs to be split up into specific functions for each
package (``prepare()``, ``build()`` and ``check()`` are shared).

Using the example from `PKGBUILDs <#pkgbuilds>`_, this is how it would look
like when e.g. splitting out documentation (assuming that the upstream project
provides separate install targets for the components).

.. code:: sh

  # Maintainer: Your Name <youremail@domain.com>
  pkgbase=dummy-package
  pkgname=(dummy-package dummy-package-docs)
  pkgver=0.1.0
  pkgrel=1
  pkgdesc="A dummy package"
  arch=(any)
  url="https://my-upstream.link/to/dummy-package"
  license=(GPL3)
  makedepends=(another-package)
  source=(https://my-upstream.link/to/$pkgname-$pkgver.tar.gz)
  b2sums=('THISISADUMMYCHECKSUM')

  package_dummy-package() {
    depends=(another-package)
    optdepends=(
      'dummy-package-docs: for documentation'
      'some-additional: for additional feature X'
    )

    make DESTDIR="$pkgdir" install-scripts -C $pkgname-$pkgver
  }

  package_dummy-package-docs() {
    make DESTDIR="$pkgdir" install-docs -C $pkgname-$pkgver
  }

Binary repository management
----------------------------

The resulting packages of a build process can be installed on a local machine,
but are often of course more useful, if more systems can install them as well.
For this purpose the repository sync databases exist, which |pacman| uses (see
|libalpm_databases|) to retrieve the difference between a remote package
repository and a local machine's state and to figure out which packages to
upgrade.

The most rudimentary actions (adding and removing packages, optionally signing
a database) on a binary repository can be done using |repo-add| and
|repo-remove|, which are shipped with |pacman|. As the tooling is very basic,
it does not offer any form of state tracking (i.e. a log of actions, such as
additions or removals done to a sync database by a specific user).

At the time of writing Arch Linux packagers make use of |dbscripts| for the
binary repository management, which also does (a form of) state tracking by
interacting with and using the the two |svn|-based monorepos for package build
sources for this purpose.
The tooling consists of a set of shell scripts (making use of |repo-add| and
|repo-remove| internally), that are being called by authorized users on a
specific host over |ssh|. The user authentification is therefore done using
|ssh| while the user authorization is implemented using plain unix groups
(different sets of packagers have access to ``[core]`` and ``[extra]`` vs.
``[community]`` and ``[multilib]`` - often only for historical reasons).
However, this setup is showing its age and comes with its own set of pitfalls:

- changes to repositories are not externally auditable
- package data is only checked rudimentarily
- integrity of repository sync databases can not be guaranteed
- repository sync databases can not be rebuilt to a specific state
- setting the target binary repository for a package is a manual operation
- due to the blocking nature of dbscripts, it is possible to brick the state of
  a repository if e.g. connection to the host running dbscripts is lost during
  the move of packages between two
  repositories
- it is not possible to setup rebuild-specific staging repositories on the fly
- many users need |ssh| access to a machine

This all being said, work is underway with |arch-repo-management| to provide a
more manageable and easy to configure solution that runs as a service and does
not rely on multiple users to have direct access on a target system.
One of the project's main focusses is to be able to verify incoming package
data and to fully decouple the state from the repository sync databases (to be
able to rebuild them whenever needed).
Going forward it should become more easy to setup ephemeral staging
repositories to build against and safer to move data due to more atomic
repository operations, while allowing externals to audit each repository's
history. Currently the project is still far from being usable though and there
are quite a few things left to be implemented. Switching from the current setup
in which both package build sources and binary repository state are handled by
one |version control| system, to one where these concerns are separated is a
hard problem, especially when one wants to get this right.
I hope that going forward we will end up with a solution that can be easily
contributed to and reused also outside of Arch Linux. I will write another post
in the coming months, that highlights work and concepts of
|arch-repo-management|.

Distributing trust
------------------

Packagers use the |makepkg.conf| variables ``PACKAGER`` and ``GPGKEY`` to set
the packager user ID (i.e. name and e-mail address) and the PGP key ID used for
signing created packages.

Other users that wish to use packages signed by someone else need to import
that other user's PGP public key using |pacman-key|.

Arch Linux maintains a |web of trust| between a set of main signing keys and
all packagers and between all packagers amongst themselves (see the |main keys|
page for an extensive overview). This setup allows for user systems to evaluate
whether a given package signature done by a packager is considered trusted (see
|pacman.conf#package_and_database_signature_checking| for further info).
These constructs are system-wide PGP keyrings for the use with |pacman| and can
be handled with |pacman-key|.

.. note::

  The main signing keys are considered **fully trusted** on a user system. They
  define the root trust for the distribution which is handed down to the
  packagers.

In the |archlinux-keyring| project the distribution trust of Arch Linux is
maintained as a set of decomposed PGP public keys and the signatures on them.
The custom tooling ``keyringctl`` (which uses |sequoia|'s ``sq`` under the
hood) is used to maintain (e.g. import public keys and signatures) a PGP
keyring that is packaged in the |archlinux-keyring package| and which is
automatically added and updated upon install.

More than or equal to three main signing key holders are required to uphold
the web of trust. More than or equal to three valid main key signatures are
required for a packager key (if it is itself still valid) to be allowed for
distributing packages in the official Arch Linux repositories.

.. note::

  Writing the new tooling ``keyringctl`` to manage the distribution trust has
  been a huge topic of the past year, that |Levente Polyak| and I have been
  working on, as the previous setup was very brittle. I will elaborate a bit
  more on that topic in an upcoming post.


Sonames
-------

Linux distributions mostly build C and C++ libraries and executables using
|dynamic linking|. This implies, that shared libraries usually provide a
|soname| (e.g. ``libexample.so.1``), which is in turn used (i.e. linked
against) by one or more other libraries or executables.
If the |application binary interface| (ABI) of the library in question changes,
its |soname| should be increased as well (e.g. ``libexample.so.2``). If a
package with an updated soname is released and installed, without rebuilding
any of the packages depending on it, those will fail to load (the now
non-existent) ``libexample.so.1`` shared object.

A common task as a packager is therefore to do rebuilds for libraries and
executables when a soname change is introduced. Depending on the library
introducing the soname change or the library/executable being affected by it,
this is sometimes a bit of a painful and time consuming experience.
While it is not unheard of that projects either forget to introduce a soname
change (silently breaking consumers) or accidentally downgrade their soname,
consumers are more likely to run into trouble because of not yet implementing
changes introduced by the ABI change (requiring patches not yet included in a
stable release).

To safeguard against cases in which soname changes went unnoticed and packages
are pushed to the repositories, it is possible to make use of |makepkg|'s
builtin dependency resolution. Extending upon the example in `PKGBUILDs
<#pkgbuilds>`_ and assuming that ``libexample`` is the package providing
``libexample.so``:

.. code:: sh

  # Maintainer: Your Name <youremail@domain.com>
  pkgname=libexample
  pkgver=1.0.0
  pkgrel=1
  pkgdesc="A dummy library"
  arch=(any)
  url="https://my-upstream.link/to/libexample"
  license=(GPL3)
  depends=(glibc)
  provides=(libexample.so)
  source=(https://my-upstream.link/to/$pkgname-$pkgver.tar.gz)
  b2sums=('THISISADUMMYCHECKSUM')

  build() {
    make -C $pkgname-$pkgver
  }

  package() {
    make DESTDIR="$pkgdir" install -C $pkgname-$pkgver
  }

.. code:: sh

  # Maintainer: Your Name <youremail@domain.com>
  pkgname=dummy-package
  pkgver=0.1.0
  pkgrel=1
  pkgdesc="A dummy package"
  arch=(any)
  url="https://my-upstream.link/to/dummy-package"
  license=(GPL3)
  depends=(another-package libexample libexample.so)
  optdepends=('some-additional: for additional feature X')
  source=(https://my-upstream.link/to/$pkgname-$pkgver.tar.gz)
  b2sums=('THISISADUMMYCHECKSUM')

  build() {
    make -C $pkgname-$pkgver
  }

  package() {
    make DESTDIR="$pkgdir" install -C $pkgname-$pkgver
  }

If during build time ``libexample`` provided ``libexample.so.1``, the resulting
``dummy-package`` will now depend on ``libexample`` and ``libexample.so=1-64``,
which ``libexample`` provides.

If the ``libexample`` package is then updated while accidentally including a
soname bump to ``libexample.so.2``, |pacman| will prevent this package from
being upgraded on a user's system, because it can no longer provide
``libexample.so.1``, which is required by its consumers (i.e.
``dummy-package``).
This only helps against immediate breakage on already installed systems. On
systems that are about to be installed it would lead to pacman not being able
to resolve the dependencies and bailing out. It is therefore to be considered a
stop-gap solution which allows for fixing the package(s) in question, while not
immediately breaking consumers of ``libexample``.

In the future this feature will be directly built into |makepkg|, removing the
manual process of identifying shared libraries (and their sonames) which are
provided by packages.

.. note::

  To identify the sonames provided by a package, |find-libprovides| can be
  used. Reversely, to identify the sonames required by a specific package
  |find-libdeps| can be used.

Debug packages
--------------

The ability to debug software using e.g. |gdb| is very powerful, as it allows
users to provide vital information about failing software to the packagers and
upstream projects. For this to work, a package's debug symbols need to be
provided to the debugger.
In February 2022 Arch Linux has started using |debug packages and debuginfod|,
which allows just that.

Creating a package and additionally also building its debug symbols has now
become as easy as adding ``debug`` to the ``options`` array in a |pkgbuild|
(until this option eventually is added to the default for packagers of the
distribution).

.. note::

  At the time of writing not all issues with non-|file-hierarchy| compliant
  files and directories have been solved yet, but a large set of debug packages
  have already been built across all official repositories.

Creating users
--------------

Historically, system users and groups for packages have been created
using ``.install`` scripts (see |pkgbuild#install|). This had the downside of
requiring a specific |user identifier| (UID) and/or |group identifier| (GID)
(see |UID/ GID database| for specific assignments) if file ownerships also
needed to be handled in the context of a package.
Additionally, the user and group creation was not standardized and required a
script (run as ``root``), which was only run *after* creating and installing
the package and therefore not easily testable.

With the adoption of |systemd| and specifically |sysusers.d| the workflow has
changed to installing a single file in the context of a package to the vendor
location ``/usr/lib/sysusers.d/``. Based on the |systemd| |alpm-hooks| setup
the configuration is applied using |systemd-sysusers|.

Changing files after package installation
-----------------------------------------

Similar to how system users and groups have been created in the past, file
modifications (e.g. ownership, |extended file attributes| or |setuid|) have
been done using ``.install`` scripts or directly in PKGBUILDs. The problem with
this approach was, that it required the specific assignment and pinning of UIDs
and GIDs when creating the required users and groups, *before* doing the file
modifications (e.g. using |chown|).

This task has been made less complex with |tmpfiles.d|, which allows for
packaging a single file in a package to the vendor location
``/usr/lib/tmpfiles.d/``. Due to the ordering of |alpm-hooks| first users
and groups are created and only afterwards the file configuration is applied
using |systemd-tmpfiles|, allowing for diverse scenarios.

Packaging
=========

Working on packages for software written in different languages (e.g. |php|,
|python|, |ruby|, |c|, |c++| or |rust|) using various build systems surely
makes for a very interesting Github profile eventually (due to providing issue
reports and fixes to many projects).

You can find the |list of my packages| amongst the official repositories.
Moreover I currently maintain two |unofficial user repositories|: |[realtime]|
and |[pro-audio-legacy]|

Packaging can be a fun but also a very time consuming and frustrating pastime.
As such there are many many more examples and specifics I could list (but this
article is already quite dense I am afraid).

.. note::

  None of the Arch Linux package maintainers are payed for their work, which
  they do solely in their free time and the project is not a commercial
  endeavor. Some dependency chains are fairly complex and require time and
  care. The reasons as to why a package is not updated is not always obvious to
  the outside and it could be due to various technical problems or the packager
  just lacking the time. Please be mindful about this and instead of getting
  upset try to lend a helping hand packaging (it is usually much appreciated)
  or consider |getting involved|.

At any rate, I hope I could spark your curiosity! If you are interested in
finding out more about packaging for specific languages or best practices,
the following are some good starting points:

* |arch package guidelines| and |arch package guidelines category|
* |#archlinux| and |#archlinux-aur| on |libera.chat|

.. note::

  Update: I have fixed some typos. Thanks to Andreas Schleifer (Segaja) for
  noticing them!

  Update: I have fixed more typos. Thanks to `doesntthinkmuch
  <https://www.reddit.com/user/doesntthinkmuch/>`_ for noticing them.

.. [1] I am excluding `flatpak <https://flatpak.org/>`_ and `snap
   <https://snapcraft.io/>`_ in this article as they follow an app-store/
   per-user installation paradigm. However, they relate to system's packaging
   in that they provide precompiled binaries in a predefined format.

.. |arch linux| raw:: html

  <a target="blank" href="https://archlinux.org">Arch Linux</a>

.. |linux distribution| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Linux_distribution">Linux distribution</a>

.. |software repositories| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Software_repository">software repositories</a>

.. |web servers| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Web_server">web servers</a>

.. |package manager| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Package_manager">package manager</a>

.. |gentoo linux| raw:: html

  <a target="blank" href="https://www.gentoo.org/">Gentoo Linux</a>

.. |planned obsolescence| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Planned_obsolescence">planned obsolescence</a>

.. |homebrew| raw:: html

  <a target="blank" href="https://brew.sh/">Homebrew</a>

.. |chocolatey| raw:: html

  <a target="blank" href="https://chocolatey.org/">Chocolatey</a>

.. |archive files| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Archive_file">archive files</a>

.. |filesystem hierarchy standard| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard">filesystem hierarchy standard</a>

.. |file-hierarchy| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/core/systemd/file-hierarchy.7.en">file-hierarchy</a>

.. |tmpfiles.d| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/tmpfiles.d.5.en">tmpfiles.d</a>

.. |sysusers.d| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/sysusers.d.5.en">sysusers.d</a>

.. |alpm-hooks| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/alpm-hooks.5">alpm-hooks</a>

.. |makepkg| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/makepkg.8">makepkg</a>

.. |pacman| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/pacman.8">pacman</a>

.. |pacman_website| raw:: html

  <a target="blank" href="https://archlinux.org/pacman/">Pacman</a>

.. |PKGBUILD| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/core/pacman/PKGBUILD.5.en">PKGBUILD</a>

.. |bash| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)">Bash</a>

.. |makepkg.conf| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/makepkg.conf.5">makepkg.conf</a>

.. |devtools| raw:: html

  <a target="blank" href="https://gitlab.archlinux.org/archlinux/devtools">devtools</a>

.. |makechrootpkg| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/makechrootpkg.1">makechrootpkg</a>

.. |systemd-nspawn| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/systemd-nspawn.1">systemd-nspawn</a>

.. |chroot| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/chroot.1.en">chroot</a>

.. |checkpkg| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/checkpkg.1">checkpkg</a>

.. |namcap| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/namcap.1">namcap</a>

.. |arch package guidelines| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/Arch_package_guidelines">Arch package guidelines</a>

.. |arch package guidelines category| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/Category:Arch_package_guidelines">Arch package guidelines category</a>

.. |arch build system| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/Arch_Build_System">Arch Build System</a>

.. |asp| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/asp.1">asp</a>

.. |howto be a packager| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/DeveloperWiki:HOWTO_Be_A_Packager">HOWTO be a packager</a>

.. |svn| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/svn.1">svn</a>

.. |git| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/git.1">git</a>

.. |git-svn| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/extra/git/git-svn.1.en">git-svn</a>

.. |svntogit-packages| raw:: html

  <a target="blank" href="https://github.com/archlinux/svntogit-packages">svntogit-packages</a>

.. |svntogit-community| raw:: html

  <a target="blank" href="https://github.com/archlinux/svntogit-community">svntogit-community</a>

.. |dbscripts| raw:: html

  <a target="blank" href="https://gitlab.archlinux.org/archlinux/dbscripts">dbscripts</a>

.. |arch-repo-management| raw:: html

  <a target="blank" href="https://gitlab.archlinux.org/archlinux/arch-repo-management">arch-repo-management</a>

.. |make| raw:: html

  <a target="blank" href="https://www.gnu.org/software/make/">make</a>

.. |pkgbuild#pkgname| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/PKGBUILD#pkgname">PKGBUILD#pkgname</a>

.. |pkgbuild#pkgver| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/PKGBUILD#pkgver">PKGBUILD#pkgver</a>

.. |pkgbuild#pkgrel| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/PKGBUILD#pkgrel">PKGBUILD#pkgrel</a>

.. |version control| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Version_control">version control</a>

.. |licenses package| raw:: html

  <a target="blank" href="https://archlinux.org/packages/core/any/licenses/">licenses package</a>

.. |pkgbuild#license| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/PKGBUILD#license">PKGBUILD#licenses</a>

.. |pkgbuild#integrity| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/PKGBUILD#Integrity">PKGBUILD#Integrity</a>

.. |updpkgsums| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/updpkgsums.8">updpkgsums</a>

.. |pacman-contrib| raw:: html

  <a target="blank" href="https://archlinux.org/packages/?q=pacman-contrib">pacman-contrib</a>

.. |fakeroot| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/fakeroot.1">fakeroot</a>

.. |pgp| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Pretty_Good_Privacy">PGP</a>

.. |signify| raw:: html

  <a target="blank" href="https://github.com/aperezdc/signify">signify</a>

.. |cosign| raw:: html

  <a target="blank" href="https://github.com/sigstore/cosign">cosign</a>

.. |makepkg#signature_checking| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/Makepkg#Signature_checking">makepkg#signature_checking</a>

.. |pkgbuild#using_vcs_sources| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/PKGBUILD.5.en#USING_VCS_SOURCES">PKGBUILD#USING_VCS_SOURCES</a>

.. |chain of trust| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Chain_of_trust">chain of trust</a>

.. |supply chain attack| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Supply_chain_attack">supply chain attack</a>

.. |arch linux rfc| raw:: html

  <a target="blank" href="https://gitlab.archlinux.org/archlinux/rfcs">Arch Linux RFC</a>

.. |reproducible builds| raw:: html

  <a target="blank" href="https://reproducible-builds.org/">Reproducible Builds</a>

.. |rebuilderd| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/rebuilderd.1">rebuilderd</a>

.. |makerepropkg| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/makerepropkg.1">makerepropkg</a>

.. |diffoscope| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/diffoscope.1">diffoscope</a>

.. |repro| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/repro.8">repro</a>

.. |buildinfo| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/core/pacman/BUILDINFO.5.en">.BUILDINFO</a>

.. |pkgbuild#pkgbase| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/PKGBUILD#pkgbase">PKGBUILD#pkgbase</a>

.. |libalpm_databases| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/libalpm_databases.3">libalpm_databases</a>

.. |repo-add| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/repo-add.8">repo-add</a>

.. |repo-remove| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/repo-add.8">repo-remove</a>

.. |ssh| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/ssh.1">ssh</a>

.. |web of trust| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Web_of_trust">web of trust</a>

.. |main keys| raw:: html

  <a target="blank" href="https://archlinux.org/master-keys/">main keys</a>

.. |pacman.conf#package_and_database_signature_checking| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/pacman.conf.5#PACKAGE_AND_DATABASE_SIGNATURE_CHECKING">pacman.conf#PACKAGE_AND_DATABASE_SIGNATURE_CHECKING</a>

.. |pacman-key| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/pacman-key.8">pacman-key</a>

.. |archlinux-keyring| raw:: html

  <a target="blank" href="https://gitlab.archlinux.org/archlinux/archlinux-keyring/">archlinux-keyring</a>

.. |sequoia| raw:: html

  <a target="blank" href="https://sequoia-pgp.org/">sequoia</a>

.. |archlinux-keyring package| raw:: html

  <a target="blank" href="https://archlinux.org/packages/core/any/archlinux-keyring/">archlinux-keyring package</a>

.. |Levente Polyak| raw:: html

  <a target="blank" href="https://leventepolyak.net/">Levente Polyak</a>

.. |dynamic linking| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Dynamic_linker">dynamic linking</a>

.. |soname| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Soname">soname</a>

.. |application binary interface| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Application_binary_interface">application binary interface</a>

.. |find-libprovides| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/find-libprovides.1">find-libprovides</a>

.. |find-libdeps| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/find-libdeps.1">find-libdeps</a>

.. |gdb| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/gdb.1">gdb</a>

.. |debug packages and debuginfod| raw:: html

  <a target="blank" href="https://archlinux.org/news/debug-packages-and-debuginfod/">debug packages and debuginfod</a>

.. |pkgbuild#install| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/PKGBUILD#install">PKGBUILD#install</a>

.. |user identifier| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/User_identifier">user identifier</a>

.. |group identifier| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Group_identifier">group identifier</a>

.. |UID/ GID database| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/DeveloperWiki:UID_/_GID_Database">UID/ GID database</a>

.. |systemd| raw:: html

  <a target="blank" href="https://systemd.io/">systemd</a>

.. |systemd-sysusers| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/systemd-sysusers.8">systemd-sysusers</a>

.. |extended file attributes| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Extended_file_attributes#Linux">extended file attributes</a>

.. |setuid| raw:: html

  <a target="blank" href="https://en.wikipedia.org/wiki/Setuid">setuid</a>

.. |chown| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/chown.1">chown</a>

.. |systemd-tmpfiles| raw:: html

  <a target="blank" href="https://man.archlinux.org/man/systemd-tmpfiles.8">systemd-tmpfiles</a>

.. |php| raw:: html

  <a target="blank" href="https://www.php.net/">PHP</a>

.. |python| raw:: html

  <a target="blank" href="https://www.python.org/">Python</a>

.. |ruby| raw:: html

  <a target="blank" href="https://www.ruby-lang.org/en/">Ruby</a>

.. |c| raw:: html

  <a target="blank" href="https://www.iso.org/standard/74528.html">C</a>

.. |c++| raw:: html

  <a target="blank" href="https://isocpp.org/">C++</a>

.. |rust| raw:: html

  <a target="blank" href="https://www.rust-lang.org/">Rust</a>

.. |#archlinux| raw:: html

  <a target="blank" href="ircs://irc.libera.chat/archlinux">#archlinux</a>

.. |#archlinux-aur| raw:: html

  <a target="blank" href="ircs://irc.libera.chat/archlinux-aur">#archlinux-aur</a>

.. |libera.chat| raw:: html

  <a target="blank" href="https://libera.chat/">libera.chat</a>

.. |list of my packages| raw:: html

  <a target="blank" href="https://archlinux.org/packages/?sort=&q=&maintainer=dvzrv&flagged=">list of my packages</a>

.. |unofficial user repositories| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/Unofficial_user_repositories">unofficial user repositories</a>

.. |[realtime]| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/Unofficial_user_repositories#realtime">[realtime]</a>

.. |[pro-audio-legacy]| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/Unofficial_user_repositories#pro-audio-legacy">[pro-audio-legacy]</a>

.. |getting involved| raw:: html

  <a target="blank" href="https://wiki.archlinux.org/title/Getting_involved">getting involved</a>