How to download a deb package with all of its dependencies

Ubuntu is a wonderful distribution for people who don’t want to spend a lot of their time focusing on their computer instead of focusing on what they can do with it. I use it both personnaly and professionaly, and though I tried a lot of other distribution, I always went back, mostly for the pre-cited reason.

Not wanting to obsess over your distribution internal mechanisms1 is not the same as not knowing how it works, though. And I recently found myself confronted by the apparent limitations of a core mechanism: the package manager.

In the world we live in, they are basically two kind of packages for linux distributions. Okay, mainly two kind. RPM is a file format used by RedHat and OpenSuse distributions (among others), DEB is used by Debian and its derivatives, Ubuntu included.

DEB packages on Ubuntu (and Debian) are managed using a serie of specific tools:

  • dpkg, to install and remove packages, and track what is installed and what is not

  • apt, built upon dpkg, to get packages from different sources and automatically fetch dependencies

  • aptitude, built upon apt and dpkg, to do the same thing but with more features and a nicer UI

The problem

Recently found myself confronted with a rather simple problem: how to download a package and all of its dependencies?

At first, it seems that’s exactly what apt or aptitude do. But apt and aptitude are used to install packages, and not to download packages you already installed. Aye, there’s the rub.

Let’s say I need to get the apt DEB package file and all of it’s dependencies. I can use a line like:

$> sudo apt-get install --download-only apt

The needed packages will then be downloaded in the archives cache directory of apt (/var/cache/apt/archives by default). But the important word here is needed: the only packages I will download will be the one that are not installed. But if I am using apt-get, that probably means I already installed the apt DEB package. So nothing will be downloaded. 2

By browsing the man page, I became quickly aware of a --reinstall option that forces apt to… well… reinstall the specified package. But that only works for the specified package, not its dependencies.

A web search does not yield interesting results. There are not a lot of tools out there to parse and browse the DEB repositories. There is a tool named apt-rdepends to get a list of dependencies for a package. I could use that and the --reinstall --download-only options of apt-get and put a script together, but I’m lazy.

So, what to do ?

Let’s spawn another apt instance

Yep, that’s my solution.

I envisioned first to build a complete chroot system3 but by reading the man pages about apt configuration, I quickly came to the conclusion that it was overkill.

So let’s do this !

I need a root. I’ll put it in /tmp, because it’s just for test. I will also need some directories in that root:

$> mkdir -p /tmp/apt/cache

$> mkdir -p /tmp/apt/state

$> mkdir -p /tmp/apt/dpkg

I could create a complete configuration for apt, with its own sources.list file, but I will use my existing configuration, because that’s what I need here.

Now let’s try it:

$> apt-get -qq \
$> -o Dir::Cache=/tmp/apt/cache \
$> -o Dir::State=/tmp/apt/state \
$> -o Dir::State::status=/tmp/apt/dpkg/status \
$> update

E: Could not open file /tmp/apt/dpkg/status - open (2: No such file or directory)
E: The package lists or status file could not be parsed or opened.

Note how I use -qq not to bury you under the log reports. Note also how I don’t need sudo because I’m only using user-owned files and directories. Finally, note how this fails pathetically. Oh, nevermind, I just need one more file because apparently apt-get cannot create it himself.

$> touch /tmp/apt/dpkg/status

$> apt-get -qq \
$> -o Dir::Cache=/tmp/apt/cache \
$> -o Dir::State=/tmp/apt/state \
$> -o Dir::State::status=/tmp/apt/dpkg/status \
$> update

Yey, no error, this time it worked! Now on to the reason I needed this: let’s download a package and all of it’s dependencies. Here I will use the tree package as example4, but it will work with any package just the same. The --download-only5 flag will be your friend here:

$> apt-get -qq \
$> -o Dir::Cache=/tmp/apt/cache \
$> -o Dir::State=/tmp/apt/state \
$> -o Dir::State::status=/tmp/apt/dpkg/status \
$> install --assume-yes --download-only tree

$> ls /tmp/apt/cache/archives/*.deb

/tmp/apt/cache/archives/gcc-4.9-base_4.9.1-16ubuntu6_amd64.deb
/tmp/apt/cache/archives/libc6_2.19-10ubuntu2.3_amd64.deb
/tmp/apt/cache/archives/libgcc1_1%3a4.9.1-16ubuntu6_amd64.deb
/tmp/apt/cache/archives/multiarch-support_2.19-10ubuntu2.3_amd64.deb
/tmp/apt/cache/archives/tree_1.7.0-1_amd64.deb

Just to be sure, I compare it with the result of apt-rdepends, the tool I mentioned earlier:

$> apt-rdepends tree | grep -v "^ "

[...]

tree
libc6
libgcc1
gcc-4.9-base
multiarch-support

Well, it looks like I have the five packages I needed, mission complete. :]

Going farther

For more information or to go farther read the man pages on the following topics:

  • apt.conf

  • apt-get

  • apt-cache

  • apt-config

As always for comments, suggestions or insults, use my twitter account.

Edit 20150821: typos

  1. And by “obsess over” I mean “insult continuously”
  2. Except if some of the packages need updates, but I obviousy can’t rely on that
  3. That is to say, build a complete distribution with its binaries, its configuration files and (more importantly in the present case) its own apt and dpkg databases; chroot is a tool to “change the root” of your system
  4. It’s a really useful and small package by the way, I install it on all my systems
  5. or -d if you’re into the whole brevity thing

Written by Lertsenem in misc on Mon 27 April 2015. Tags: technical, apt, deb,