Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Checking installability

A run that goes twenty-six for twenty-six says every component built. It says nothing about whether the packages it produced can be installed. Those are different questions, and a package answers the first while failing the second whenever a runtime Depends names something the target suite does not have — a package that only exists in Ubuntu, one that was transitional in the last release and is gone from this one, or one that is simply not packaged yet.

src2deb check asks the second question:

src2deb check recipes/cosmic-epoch --work /mnt/build/work
src2deb: reading the archives for trixie/arm64 to check 27 package(s)
src2deb: arm64: cosmic-settings: Depends: network-manager-gnome
src2deb: arm64: cosmic-initial-setup-casper: Depends: casper
src2deb: arm64: 27 package(s), 214 dependencies, 2 unsatisfiable
src2deb: 2 unsatisfiable dependencies across 1 pool(s); apt will refuse those
packages until something provides what they name

It exits non-zero when anything is unsatisfiable, so it belongs between a build and a publish.

Before the build

src2deb check answers after the build, which for a large recipe is hours after the recipe declared the dependency that will fail. src2deb plan --runtime-deps answers before it:

src2deb plan recipes/pop-desktop-data --work /mnt/build/work --runtime-deps
src2deb: arm64: pop-gtk-theme: pop-gtk-theme: Depends: gtk2-engines-murrine
src2deb: arm64: 11 runtime relationship(s) declared, 1 unsatisfiable

It reads each component’s debian/control — the same file the build order comes from — for the Pre-Depends, Depends, and Recommends its binary stanzas declare, and answers each against the target suite, the recipe’s repositories, the pool as it stands, and the packages the recipe itself will build. That last is what makes the question answerable at all before a build: a metapackage depending on everything its recipe produces is satisfied by its siblings, none of which is in any archive yet.

The two answer the same question at different strengths and different times, and neither replaces the other:

plan --runtime-depscheck
Readsdebian/controlthe built .debs in the pool
Sees ${shlibs:Depends}no, it is still a substitutionyes, expanded
Reports Recommendsyesno
Answersbefore the buildafter it
Exit statusunaffectednon-zero when anything is unsatisfiable

A plan reports and does not gate, for the reason a build’s closing check does not: a pool is often built before the packages that complete it. Recommends is included here and not there because the questions differ — apt passes over a Recommends it cannot satisfy, so it is not part of installability, but a Recommends nothing will ever satisfy is a gap in what a recipe delivers.

Costs one release and index fetch per architecture the recipe names, and nothing when the flag is absent.

What it reads

The pool, not the recipe. That matters, because debian/control declares ${shlibs:Depends} while the .deb carries what that expanded to — every library package the build actually linked against. The pool’s index holds each package’s control stanza verbatim, so a check sees the dependencies a client will see rather than the ones the packaging was written with.

It follows that a check covers whatever the pool holds, whichever recipe built it. A work directory shared by three recipes checks as one archive, the same way pruning does, because a pool for a suite and architecture is one archive.

Every pool the suite holds is checked. --architecture narrows that, and is repeatable:

src2deb check recipes/cosmic-epoch --architecture arm64

What counts as available

The same archives a build root is provisioned from: the target suite, any additional repositories the recipe declares, and the pool itself. A dependency is satisfiable when one of those offers a package that satisfies it.

Two consequences worth knowing:

  • A dependency the pool satisfies itself is fine. The packages a recipe builds resolve against each other, so a metapackage depending on everything it pulls in checks clean once those are built.
  • A dependency satisfied only by a declared extra repository checks clean. A recipe declaring a repository is declaring where its packages come from, and the check takes it at its word. A client that does not have that repository configured still cannot install the package.

Alternatives and virtual packages are honoured, over exactly the archives that provision build roots. a | b is satisfied by either; x-terminal-emulator is satisfied by anything that Provides it.

A dependency on a name something provides is reported at --verbose, because being satisfied that way is weaker than being satisfied directly — the clause installs because apt picks one of several providers, and which it picks is apt’s decision rather than the packaging’s:

src2deb: arm64: pop-icon-theme: Depends: adwaita-icon-theme-full is provided by
adwaita-icon-theme

The line says the name is provided, not that it is only provided. Debian policy lets a package provide a name a real package also carries — a transitional package is the usual case — and the archives are read as the names they offer rather than as a catalogue that separates the two.

What is checked

Depends and Pre-Depends — the relationships that make a package installable.

Recommends is not checked. apt passes over a Recommends it cannot satisfy rather than refusing the package, so it does not belong in an answer about installability.

Names are checked, not versions. A dependency’s version constraint is not enforced, for the same reason build roots do not enforce one: a suite is internally consistent, and the version a package resolves to in it is the version that suite ships. What this catches is a dependency on a package that is not there at all, which is the failure that reaches a target machine.

After a build

A build ends with the same check over the pools it published into — every architecture that built something, and no others:

src2deb: 26 built, 0 failed, 1 skipped
src2deb: reading the archives for trixie/arm64 to check 27 package(s)
src2deb: arm64: 27 package(s), 214 dependencies, all satisfiable
src2deb: 1 pool(s) checked: 27 package(s), every dependency satisfiable

It is a note, not a gate: an unsatisfiable dependency does not fail the run. A pool is often built before the packages that complete it — the recipe supplying a dependency may not have run yet — so failing here would refuse a legitimate order of work. src2deb check is where the same answer decides an exit status.

The note is skipped for a run that built nothing and for one that was cancelled. A run that could not reach the archive to ask says so and leaves its own outcome alone: failing to ask whether packages install is not a failure to build them.

It describes the pool as it stands, which is worth remembering after a --keep-going run: a component that failed is a component whose packages are not in the pool, so anything depending on them is reported alongside the failure rather than instead of it.

Acting on a finding

An unsatisfiable dependency has three ways out, and which applies is a property of the package rather than of src2deb:

  • Build it. The dependency is a package that could be built from source, and the answer is a recipe or a component for it. Four of the six COSMIC hit on Debian became one recipe of their own.
  • Drop it. The dependency belongs to a platform this build is not for, and the answer is a patch to debian/control. casper is Ubuntu’s live-boot system, reachable only through one binary package.
  • Point it elsewhere. The dependency named a package that has been renamed or superseded, and something in the suite replaces it. network-manager-gnome is transitional in trixie and absent from forky, where nm-connection-editor takes over.

A dependency reported for one suite and not another is the ordinary case, not a contradiction: suites move, and that is the whole reason the check reads the suite the packages were built for.

What it costs

One pass over the archives per pool: each one’s release and package index, fetched and projected down to the names it offers. A couple of seconds on a warm link, for a pool of any size — the cost is the index, not the number of dependencies asked about.

Nothing is resolved. A check computes no install closure and downloads no package; it reads what the archives carry and answers each dependency against that. So a pool for a foreign architecture is checked as readily as one for the host’s.

In a scheduled build

#!/bin/sh
set -eu
src2deb build recipes/cosmic-epoch --work /mnt/build/work \
    --suite trixie --skip-published --keep 2
src2deb check recipes/cosmic-epoch --work /mnt/build/work --suite trixie
src2deb export recipes/cosmic-epoch --work /mnt/build/work \
    --suite trixie --to /srv/drop/rk1

set -eu makes the check the gate: a suite that moved underneath a package stops the publish rather than reaching a target machine as an apt error. See Publishing to an archive.