ADR 010 Planning an installation - nekrozis/qtogo GitHub Wiki
Status: Accepted (2026-10-09).
- A request names a host, a target, a version and — usually — an architecture, and
sometimes a set of modules. Answering it means turning a repository's metadata
into a definite list of archives to fetch and a destination for each. This record
fixes how that list is built, because the plan is what
plan install-qt --jsonpublishes and whatinstall-qtwill execute — a contract, not an internal step. - The repository's
Updates.xmlis already read byrepository.ParseUpdatesXMLintoPackageUpdatevalues: a name, a version carrying a build stamp, download file names, and anOperationsblock.internal/discoveryalready walks a target to the leaf holding that document. What was missing is the layer between them. - The reference tool is the compatibility target, so its behaviour is the default
unless there is a reason to differ. Its selection, URL and destination rules were
read directly (
aqt/archives.py), and one earlier note in this project's plan — that an older package's destination is derived from its version and architecture — turned out to be wrong.
-
Selection is by package name and architecture; the metadata's dependency fields are not followed. A component is chosen when its name is one the request spells — the base package
qt.qt<major>.<token>.<arch>(or theqt.<token>.<arch>form), and, per requested module, the candidate names a module can take — and the architecture appears in that name.<token>is the version directory's own spelling (680,6110,59), which is what the repository used to address the version.DependenciesandAutoDependOnare parsed but not consumed. The reference tool reads them, even carries a closure helper, and never calls it in the install path; it selects by name and architecture, and where one component truly needs another (a phone or wasm target needing a desktop one) it uses a bespoke rule rather than these fields. Following it keeps the installed set observable-compatible; a naive closure would instead pull in documentation, examples and tools the user never asked for, which is worse than a gap.If a concrete case appears where a component is genuinely required, is neither the base nor a requested module, and is missed by name matching — an incomplete or broken install — then a narrow closure over
Dependenciesalone (notAutoDependOn, which is a different, selection-time semantic) is implemented and recorded as a difference. Until such a case is shown, there is no closure. -
An archive's URL is the leaf, the package's name, and the package's own version stamp followed by the archive's file name. In turn:
<leafPath>/<package name>/<package version><archive file name>, where the version is the metadata's<Version>verbatim (6.8.0-0-202410030750) — the package's identity, not the release the request named. -
Where an archive lands is decided per package, from its own
Extractoperations, and a package with none extracts to the install root. TheExtractarguments come in pairs — a destination and an archive name — and the destination has the@TargetDir@/prefix removed; the archive names there are the package's downloads. With noExtract, the package's downloads have no destination of their own and land at the root.This is per package, never per version. The boundary is not a release number: one repository has packages with
Extractand packages without, and the same version differs between hosts. And it is not a derivation: an archive that predates theExtractblocks carries its version and architecture inside itself, so extracting it at the root lands it correctly, and adding a derived<version>/<arch>would place it one level too deep.Archive.InstallPathis therefore either the explicit destination or the root — there is no third, conventional value (this corrects ADR-001). -
The plan has a fixed shape and a fixed order. It names the version, its directory token and the resolved architecture; the packages selected, base first and then the requested modules in the order they were asked for; and the archives, in package order and then in the order the package lists them (operation order where there are operations, download order otherwise). Each archive carries its file name, its package, its URL, and its destination — the root when there is none. The order is fixed so a plan is reproducible, and the destinations are explicit so
installneeds no further decision. -
A plan that cannot be built fails as "not found". A version the target does not offer, a base package the metadata does not carry, and a requested module with no matching package are all exit 3: the user asked for something the repository does not have. An architecture that was left out and cannot be narrowed to one is the same, and carries a suggestion to name it. Nothing is guessed: a component that is not there is an error, not a smaller plan.
-
A module form joins the list only with a captured sample. The candidate names a module can take are the reference tool's minus the ones no sample shows — the Qt 6.8+ spellings and the bare
qt.<token>.<module>.<arch>form. The consequence is that a module the reference tool resolves can be refused here, which is an open difference rather than a hidden one: it closes by adding the form with a fixture (ADR-003's rule). Theallmodule keyword is likewise out until the module listing a target offers lands, since it needs that rule.
- The catalog is pure: it takes parsed packages, the leaf path and a request, and returns a plan. It reaches no network and no filesystem, so it is tested with literals and needs no fixtures.
- The order of the repository layers — walk, read, plan — lives in
internal/service, one step per layer; the catalog knows nothing of discovery. - A plan is deterministic and its destinations are explicit, which is what lets a dry run and a real install share the planner and be compared directly.
- The no-closure decision carries a trigger: a case that shows it wrong is a bug to
fix with a narrow
Dependenciesclosure, not a reason to have built the general thing early. -
Archive.InstallPathlosing its "conventional" branch is a revision of ADR-001, which is stated there so neither record can drift from the other.
-
A transitive closure over
Dependencies/AutoDependOn. Rejected for now: it is not what the reference does, and on the real metadata it would add tools and documentation a plain install does not produce — a difference that shows up as surprise, not as correctness. Kept as an evidence-triggered upgrade, not a default. -
Deriving the destination from the version and architecture when there is no
Extract. Rejected: the archives already carry that path internally, so a derived prefix double-nests them. Measured, not assumed. -
Branching on the version to decide whether
Extractshould be present. Rejected:Extractis per package, and the same release differs across hosts, so a version test would be wrong for the very case it tries to describe. - Choosing a default architecture per host. Rejected: it invents a rule the reference does not have and the metadata already answers. An omitted architecture is resolved only when the choice is unambiguous; otherwise it is asked for.