(TL;DR: how do I determine the minimum set of packages to install, given an initial state and desired package list?)
I'm creating an Ubuntu installer ISO for systems without access to distribution mirrors. These are embedded devices which need only the base packages and a handful of locally-built packages plus all their dependencies. For instance, we're using Qt/Xcb, so the various libqt5*
packages need to be pulled in.
At the moment, I'm using a brute-force approach to determine what to put in the pool/extras directory of the installer. First I seed with the packages that are mentioned in d-i pkgsel/include
answer to debconf, then hunt down the recursive dependencies with the following bash
command:
# 1. Extract the dependencies from packages/*, removing version
# conditions and changing separator to newline.
# 2. Find the recursive dependencies of all these packages using
# package database, extracting Depends and PreDepends.
# 3. For each package we require:
# a. if we find it in our cache, just copy it into the ISO (using
# hard link for speed and space)
# b. else, if we find it already in the ISO, do nothing
# c. else, pass it through to step 4.
# 4. Search for matching packages in repositories that are not virtual
# or foreign-architecture.
# 5. Download matching package files to the ISO.
(for i in "$TOP/packages"/*.deb; do dpkg-deb -f $i depends; done) | sed 's/([^()]*)//g;s/[|,]/\n/g' \
| xargs -r apt-cache --recurse --important depends | sed -re 's/^ (Pre)?Depends://;/[<|:]/d;s/^ *//;' | sort -u \
| ( while read p; \
do test $(find "$TOP/extras" -name "${p}_*.deb" -exec mv '{}' "$REMASTER_HOME"/remaster-iso/pool/extras \; -printf "true" -quit) \
|| test $(find "$REMASTER_HOME"/remaster-iso/pool/{main,extras} -name "${p}_*.deb" -printf "true" -quit) \
|| echo $p; \
done; ) \
| uniq | sed -r 's/[^ ]+/!~rforeign!~v^&$/' | xargs -r aptitude -q2 -F '%p' search \
| (cd "$REMASTER_HOME"/remaster-iso/pool/extras; xargs -r apt-get download)
Here, $TOP/packages
is the seed-package directory, $TOP/extras
is a cache of previously-downloaded packages, and $REMASTER_HOME"/remaster-iso
is the root of the unpacked installer ISO.
Whilst the above works, it tends to fetch much more than we actually require, because apt-cache depends
reports all the alternative dependencies of each package. For example, grub-pc
depends on debconf
; cdebconf
provides debconf
, so both debconf
and cdebconf
are included, along with their dependencies. Also, xserver-xorg
depends on xserver-xorg-video-all | xorg-driver-video
, so my script pulls all the video drivers - but I've already declared a dependency on xserver-xorg-video-fbdev
, so I would like that to satisfy the requirements on its own.
What I would like is the equivalent of running aptitude --download-only install ${MY_PACKAGES}
on the target system. But of course, running that on the build host will miss most of the packages because they are already installed there.
I'm currently looking at the python-apt
library, but I can't find any examples of initializing it for a target system rather than the host where it's running. Has anybody done something similar, from which I can copy?
Alternatively, are there other approaches that might work? I'm considering creating a pristine, minimal chroot and running the aptitude
command in that, but I'm concerned that will be a maintenance burden and hard for others to set up on their own machines.
I'm hoping the solution can be used (with a new initial state) to find the necessary dependencies when any packages are to be updated, as I'll need to write a consistent set to removable media for the target system (still with no access to distribution mirrors).