Anti-gallery post: gentoo and homebrew

Gentoo Prefix

This was my first time seriously using Portage, their package manager. And although that’s a personal note in what’s meant to be a technical report, you can understand it to mean that the answer to many implicit “why did you do that” questions that come up below is “because I literally didn’t know the right way to do it.”

Getting it on Glitch: patches

It definitely fails to bootstrap using their provided script, because it sets up a modern glibc, which doesn’t work in our project containers due to the kernel giving our container EPERM on newer syscalls Observations on future-readiness, Things encountered while building nixpkgs 22.05. We need those patches for faccessat2 (programs appear not to have permission to do things, or files appear not to exist) and clone3 (threaded programs fail, and indeed Gentoo bothers to use multithreaded xz to unpack source tarballs).

Comparison with Nix: adding patches

Gentoo makes it much easier to direct the package manager to add some custom patches to a given package. It’s easier both in terms of the number of steps and the less quantifiable amount that you need to understand fixed points in lambda calculus. In Gentoo, you make a directory in your /etc/portage/patches (or prefixed location as we’re doing here) named after the package name, and you put patch files for that package in that directory.

You’ll be done in the time it took you to find the circular flowchart on the Nix documentation Overlays - NixOS Wiki that makes you realize you knew nothing about functional programming.

Getting it on Glitch: saving compiled things

The other usual thing I need is a way to save my work and resume it, because you only get 12 hours between /tmp wipes (less if you’re unlucky and your project starts in a container with higher uptime (or is uptime rather a property of the host server?)), and even within those 12 hours, it’s kinda hard to make sure you don’t forget and close your browser.

The Gentoo bootstrap process is split up into 3 stages.

  1. Stage 1 gets some software without the help of a package manager. Think raw wget, ./configure, and make with your host compiler. It sets up the package manager (Portage) and its dependencies.
  2. Stage 2 uses the package manager from stage 1 to build a good enough compiler, using your host compiler, to use in the rest of the boostrap process.
  3. Stage 3 uses the compiler from stage 2 to build the real compiler and real package manager and then uses those to build and install the base Gentoo system.

Stage 1 is comparatively quick. In my opinion, you can just get through it in one sitting. It produces some stuff in a temporary area that won’t be included in the final bootstrapped system. You can just archive this directory up into your ~/.data directory. A .tar.xz of it was only ~54 MB. I further moved it to assets to save space.

By the time you get to stage 2, you have Portage, so you can start using the built in support for binary packages Binary package guide - Gentoo wiki. To oversimplify countless pages of wiki stuff and manual pages:

  • Run emerge (that’s the command in Portage for installing a package) with the --buildpkg (-b) and --usepkg (-k) flags.
  • This will put built packages in a directory /var/cache/binpkgs (or prefixed location as we’re doing here) and look for them there to skip having to rebuild them.
  • Oh but the bootstrap script runs emerge for you, so actually put those buildpkg+usepkg flags into a config file somewhere.

And then you can sync that binpkgs directory into your ~/.data directory to persist it. When you configure Portage to use xz compression, the packages take ~82 MB. After stage 2 finished, I further archived them up and moved it to assets to save space.

And stage 3 is similar, because it’s just using Portage again. But it’s too big to fit into ~/.data. Using xz compression, the total size of all packages built in stage 3 came out to ~311 MB. At that point I had to build a script Assets 🔄 /tmp sync to sync this binpkgs directory with assets instead of ~/.data.

Comparison with Nix: how hacky is the bootstrap

Gentoo Prefix’s bootstrap does a lot to manage the different tools leading up to the final system. There’s all sorts of hacks built into the script to make each stage use the right toolchain and stuff. On the other hand, this kind of thing is Nix’s forte, with each build knowing exactly what program to use (except for a few weird cases).

The Gentoo Prefix bootstrap installs some preliminary tools built with your host compiler into (your-prefix)/tmp, while putting a couple of suites of more proper tools built with Gentoo’s own modern compiler into (your-prefix). This too is where Nix has it much easier. Nix natively keeps all builds separate, taking into account what tools were used to build each package.

But then, there’s this philosophical thing about Nix. You can never get to this state of nirvana where “the compiler that comes with this distribution can compile the distribution itself.” Because some hidden precursor compiler compiled at least the core build tools in that distribution. Thus you’d be using a different compiler in those cases, which would cause the resulting packages to have different hashes.

Comparison with Nix: less recompiling

Nix is very liberal about when it recompiles things. Bugfix in the compiler? Recompile all programs that you compiled with it. Minor change in a shared library? Recompile all dependents. Gentoo I believe lets you get away with much more changes without recompiling a package.

Getting it on Glitch: how to serve binpkgs

I don’t have a nice way to do this yet.

Binary package servers need to have a specific structure Binary package guide - Gentoo wiki, where there’s a “Packages” file at the top level that lists everything it has, and then binary package files organized into directories. That is:

  • There’s this Packages file that needs to be updated every time you add a new binary package.
  • There’s the actual binary packages, which are big.
  • And these had better be on one host in the specified organization.

That right there is kind of an unwholesome combination for Glitch, because:

  • Put it on assets → you can’t update the Packages file because it’ll get cached for too long.
  • Put it on a static site → you run out of room to put the actual binary packages because you’re limited to 200 MB.
  • Put binary packages on assets and the Packages file on a static site → assets are in a separate domain, so you’ll break the organization that Portage needs.
  • Redirect from the project to the assets → you can’t make actual HTTP redirects in a static site.
  • Use a full stack site → you’re limited to whatever few requests per hour, and you use up project hours.

Comparison with Nix: binary package servers

A nice thing about Gentoo is that there’s no rule that you have to serve the whole dependency tree for any packages that you have on your server. Nix requires that.

A nice thing about Nix is that you don’t need a central “Packages” index file. Nix can look up the package you need by a hash of all the source code, dependencies, and settings used to compile the binary package that it’s looking for. Gentoo needs to look through the package index to find one that matches the name, version, and compilation settings. You can just add built packages and not edit anything on the way.

Comparison with Nix: what counts as a dependency

Gentoo has this very detailed specification Package Manager Specification of what environment Portage builds run in. And anything installed in order to meet that specification—build tools, system utilities, common libraries—doesn’t count as a dependency. Nix on the other hand is much more explicit: assume basically nothing else exists other than your dependencies.

The result of this is that the minimal installation of Gentoo is quite some larger than Portage itself. You need a decent suite of compiler tools just to meet the Package Manager Specification. The minimal installation of Gentoo that comes from the Prefix bootstrap is ~264 MB (compressed with xz). The minimal Nix, being the package manager itself, is ~31 MB (compressed with gzip, so not exactly apples-to-apples).

Progress report

With the patches, the bootstrap works, and it gets you to a point where you can use the bootstrapped system. And with the binary packages synced to assets, a solo developer can build packages incrementally.

But I don’t have a good sense of what to do with the resulting bootstrapped system. It’s kinda big.

And I don’t have a way set up Glitch to distribute binary packages.

And, something I didn’t even mention, the current Gentoo distribution is kinda broken right now, with a dependency loop between curl and nghttp2 901101 – Running stage3 failed *Error: circular dependencies net-misc/curl-7.88.1-r1 (Change USE: -http2), so whatever normal emerge (something) commands I tried would just error out. They hack in an export USE="... -http2" in the bootstrap script, but after the bootstrapping you’re on your own. Am I reading this right? Is this something Gentoo users just have had to deal with since March? Does non-Prefix Gentoo somehow not have this problem? How? Aaaaaaaah!

So anyway, I gave up for now.


You’re-cool-if-you-care-about-this-stuff-but-please note: sizes in this post are measured in MB (10^6 bytes), not MiB (2^20 bytes).

I’ll post again about Homebrew. I’ll just tell you in advance: it’ll be less than this.

edit: Glitch :・゚✧ here’s the project, btw. it’s just whatever state it was in when I gave up.

2 Likes