Package formats (package)
The package-format registry and its built-in formats. build writes one package
per declared package from the file DB and the build root. A third-party format
subclasses PackageFormat with its own NAME and advertises it in the
pkgforge.package_formats entry-point group. The packages' user and group
names, modes and times are read from the DB and the build root the same way for
every format.
pkgforge.package.build(db, fmt, output, *, buildroot, config=None, config_path=None, arch=None, compression=None)
Build one package per declared package from the live entries of db
and the files under buildroot, in the format fmt.
output is the directory the packages are written into (created with the
first package; a failed build leaves nothing behind). config names the
packages (default: an empty configuration, which no format accepts) and
config_path is the file it was read from.
arch names the architecture of an any package in the format's own
spelling (default: this machine's); compression is one the format
offers (default: its first). Build-time stamps and file times are
SOURCE_DATE_EPOCH when it is set, so two builds of one build root are
byte-identical.
Returns the paths written, the main package first, then each
[[packages]] element in order.
Raises :class:~pkgforge.exceptions.UsageError for an unknown format, a
compression or architecture the format cannot take, a [package] key the
format needs, a SOURCE_DATE_EPOCH that is not a number, an entry of a
package config does not declare, a recorded path missing from (or of
another type in) buildroot or below a symlink that leads outside it,
or a missing optional dependency;
:class:~pkgforge.exceptions.InvalidEntryError for an entry no format can
represent and :class:~pkgforge.exceptions.DumpError for one this format
cannot.
Source code in src/pkgforge/package/__init__.py
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 | |
pkgforge.package.BuildContext(config, buildroot, output, compression, arch, epoch, config_path=None, writer=PackageWriter())
dataclass
What a format builds against besides the payloads.
config is the project configuration, buildroot the directory that
maps to / in the DB, output the directory the packages are written
into, compression the chosen payload compression (None for a
format that offers none), arch the --arch value, epoch the
SOURCE_DATE_EPOCH seconds (None when unset) and config_path the
file config was read from when known. A format creates every file it
writes through writer, which keeps the build all or nothing and makes
output only once there is a file to put in it.
arch
instance-attribute
buildroot
instance-attribute
compression
instance-attribute
config
instance-attribute
config_path = None
class-attribute
instance-attribute
epoch
instance-attribute
output
instance-attribute
writer = dataclasses.field(default_factory=PackageWriter, repr=False, compare=False)
class-attribute
instance-attribute
pkgforge.package.PackagePayload
Bases: NamedTuple
One package to build.
arch
instance-attribute
entries
instance-attribute
info
instance-attribute
main
instance-attribute
name
instance-attribute
pkgforge.package.PackageFormat
Bases: Registered, ABC
A package format, selected by package -f NAME.
A subclass registers by declaring its own NAME (plus, optionally,
ALIASES); see :class:pkgforge._registry.Registered.
ALIASES = ()
class-attribute
ALL_ARCH = 'all'
class-attribute
COMPRESSIONS = ()
class-attribute
MACHINES = {}
class-attribute
NAME = ''
class-attribute
REQUIRED = ()
class-attribute
SUB_REQUIRED = ()
class-attribute
arch_name(payload, context)
The architecture name payload carries in this format:
:attr:ALL_ARCH for an all package, else context.arch
verbatim, else this machine's through :attr:MACHINES.
Raises :class:~pkgforge.exceptions.UsageError for a machine the
table lacks.
Source code in src/pkgforge/package/__init__.py
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | |
build(payloads, context)
abstractmethod
Write one package per payload into context.output and return
their paths in payload order.
Source code in src/pkgforge/package/__init__.py
108 109 110 111 112 113 | |
pkgforge.package.PackageWriter()
Creates the package files of one :func:~pkgforge.package.build.
Every file is written to a sibling .NAME.pkgforge-tmp and renamed over
its final path by :meth:commit, so a build that fails leaves every target
as it was. The output directory, and any parent it lacks, is created with
the first file and removed again when the build fails.
Source code in src/pkgforge/package/_writer.py
23 24 25 26 | |
abort()
Remove every file not yet renamed and every directory created.
Source code in src/pkgforge/package/_writer.py
70 71 72 73 74 75 76 77 78 79 80 81 82 | |
commit()
Rename every file to its path, in creation order.
Every target is checked before the first rename, so a directory in the way fails the build with nothing renamed.
Source code in src/pkgforge/package/_writer.py
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | |
create(path)
Open the file that :meth:commit renames to path, for writing.
The caller closes it before :meth:commit.
Source code in src/pkgforge/package/_writer.py
41 42 43 44 45 46 47 48 | |
move(source, path)
Take the finished file source as the content of path.
Source code in src/pkgforge/package/_writer.py
50 51 52 | |
Built-in formats
pkgforge.package.ApkFormat
Bases: PackageFormat
NAME-VERSION-rREL.apk per package: an apk v2 file, which apk-tools 2
and 3 install with --allow-untrusted.
It is two gzip members back to back: the control tar (.PKGINFO and the
scripts, with no end-of-archive blocks) and the data tar (pax, owner names
from the DB, and a APK-TOOLS.checksum.SHA1 record on every file and
symlink). .PKGINFO carries the datahash of the data member, which
apk-tools 3 requires. apk v3 packages are not written.
ALL_ARCH = 'noarch'
class-attribute
instance-attribute
COMPRESSIONS = ('gz',)
class-attribute
instance-attribute
MACHINES = {'x86_64': 'x86_64', 'amd64': 'x86_64', 'aarch64': 'aarch64', 'arm64': 'aarch64', 'armv7l': 'armv7', 'i686': 'x86', 'i386': 'x86', 'riscv64': 'riscv64', 'ppc64le': 'ppc64le', 's390x': 's390x'}
class-attribute
instance-attribute
NAME = 'apk'
class-attribute
instance-attribute
REQUIRED = ('version', 'summary')
class-attribute
instance-attribute
SUB_REQUIRED = ('summary',)
class-attribute
instance-attribute
build(payloads, context)
Source code in src/pkgforge/package/_apk.py
213 214 215 216 217 218 219 220 221 222 223 224 | |
pkgforge.package.ArchFormat
Bases: PackageFormat
NAME-VERSION-PKGREL-ARCH.pkg.tar.{zst,xz,gz} per package.
The tar is GNU, with .PKGINFO, .MTREE and, when it has a command,
.INSTALL ahead of the payload. pacman ignores the owner names of a
tar, so every header is root:root and .INSTALL applies each other
owner or group (creating the accounts first); pacman -Qkk reports an
owner mismatch for those paths.
ALL_ARCH = 'any'
class-attribute
instance-attribute
COMPRESSIONS = ('zst', 'xz', 'gz')
class-attribute
instance-attribute
MACHINES = {'x86_64': 'x86_64', 'amd64': 'x86_64', 'aarch64': 'aarch64', 'arm64': 'aarch64', 'armv7l': 'armv7h', 'i686': 'i686', 'i386': 'i686', 'riscv64': 'riscv64'}
class-attribute
instance-attribute
NAME = 'arch'
class-attribute
instance-attribute
REQUIRED = ('version', 'summary')
class-attribute
instance-attribute
SUB_REQUIRED = ('summary',)
class-attribute
instance-attribute
build(payloads, context)
Source code in src/pkgforge/package/_arch.py
209 210 211 212 213 214 215 216 217 218 219 220 221 222 | |
pkgforge.package.DebFormat
Bases: PackageFormat
NAME_VERSION[-RELEASE]_ARCH.deb per package.
The ar archive holds debian-binary, control.tar and data.tar,
both GNU tar with a ./ root, as debhelper builds them. The control
file, conffiles (regular files below /etc and config kinds
elsewhere), md5sums and the maintainer scripts match what debhelper
builds from the same configuration; postinst, prerm and postrm
are written only with a command in them (sysusers, tmpfiles, units,
dpkg-statoverride for an account created at install time, setcap).
ALL_ARCH = 'all'
class-attribute
instance-attribute
COMPRESSIONS = ('xz', 'gz', 'zst', 'none')
class-attribute
instance-attribute
MACHINES = {'x86_64': 'amd64', 'amd64': 'amd64', 'aarch64': 'arm64', 'arm64': 'arm64', 'armv7l': 'armhf', 'i686': 'i386', 'i386': 'i386', 'riscv64': 'riscv64', 'ppc64le': 'ppc64el', 's390x': 's390x'}
class-attribute
instance-attribute
NAME = 'deb'
class-attribute
instance-attribute
REQUIRED = ('version', 'summary', 'maintainer')
class-attribute
instance-attribute
SUB_REQUIRED = ('summary',)
class-attribute
instance-attribute
build(payloads, context)
Source code in src/pkgforge/package/_deb.py
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | |
pkgforge.package.RpmFormat
Bases: PackageFormat
NAME-VERSION-RELEASE.ARCH.rpm per package, built by rpmbuild.
The files are the recorded, non-ghost entries of the build root, packed into
a tar that the spec's %install unpacks, so rpm applies the DB's modes,
owners and kinds through the %files lists dbdump -f rpmspecfiles
writes (the -pre419 variants below rpm 4.19). The spec is the one
dbdump -f rpmspec writes, with the unit scriptlets (they expand to
nothing, with a warning, where rpm has no systemd macros). Needs
rpmbuild on PATH and a summary per [[packages]] element;
--compression is refused, since rpm owns its payload, and --arch is
passed as rpmbuild --target. The build happens in a temporary directory
that is removed afterwards; the packages are moved into the output
directory.
NAME = 'rpm'
class-attribute
instance-attribute
REQUIRED = ('version', 'summary', 'license')
class-attribute
instance-attribute
SUB_REQUIRED = ('summary',)
class-attribute
instance-attribute
build(payloads, context)
Source code in src/pkgforge/package/_rpm.py
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 | |
pkgforge.package.TarFormat
Bases: PackageFormat
NAME-VERSION[-RELEASE].tar[.gz|.xz|.zst] per package.
GNU tar, names without ./ and no entry for the root, so extracting over
/ touches nothing it does not ship. Ghost entries are left out. A file
capability cannot be stored in the archive; it is logged as a warning.
COMPRESSIONS = ('gz', 'xz', 'zst', 'none')
class-attribute
instance-attribute
NAME = 'tar'
class-attribute
instance-attribute
REQUIRED = ('version',)
class-attribute
instance-attribute
build(payloads, context)
Source code in src/pkgforge/package/_tar.py
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 | |