Skip to content

Instantly share code, notes, and snippets.

@zamfofex
Created December 22, 2021 09:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zamfofex/45f8cb51cf243a31166a8f5527d6e67a to your computer and use it in GitHub Desktop.
Save zamfofex/45f8cb51cf243a31166a8f5527d6e67a to your computer and use it in GitHub Desktop.

bootstrapping Guix from an existing operating system

(Note: None of this is done or even started, this is just a thought dump.)

Lately I have been reading about GNU Mes and boostrapping. The goal of stage0 is very ambitious — to bootstrap the entire operating system taking a very small set of prebuilt binaries installed.

In order to achieve that goal, I feel like we must at first try to reduce its scope. Instead of assuming that no binaries are in the system, what if we assumed that we already have an operating system, and tried building another operating system (in this case Guix) entirely from source?

In order to do that, we should avoid using binaries that are already on the system, and try to instead provide our minimal binary set, then downloading and building further binaries from source.

In effect, the role of the existing operating system in this case is to provide a filesystem for us, and to allow the user to fetch a limited set of our sources (and our limited binaries) using their own means.

The limited binary we take for granted in this case is merely mes.

We would provide a small Guix, “Guix 0”, which would be an implementation of very little Guix features in order to start building packages. This Guix 0 would be compatible with Mes.

One thing that would be really useful would be to be able to bring Guile closer to the start of the bootstrapping process, so that it can be used in place of Mes earlier, and thus we could use more features from full Guix to define packages. In order to achieve that sensibly, Guile would need to change to provide a minimal build option with fewer dependencies (hopefully only TCC and Gash, but a few others would likely be tolerable).

The file bootstrap0.scm would define the packages needed to build Guile, namely mes (binary), gash and tcc, as well as guile itself. This file would be written intentionally using a limited set of Guix definitions that Guix 0 would be able to understand.

Guix 0 would be a really simple tool, doing very little of what Guix actually does. It wouldn’t actually build packages in a container or interact with the store in any way, instead it would build them in a subdirectory with at most (but not necessarily) chroot. In order to verify reproducibility, the hash of the resulting directory would be computed and compared against a hash provided in the package definition. Guix 0 would halt if the hashes don’t match. Guix 0 would also not download tarballs, but rather take them for granted (assume that the user has them downloaded in the current directory), but it would veirfy that tarball’s hash. It would support a limited set of build systems and package fields.

Indeed, this means that gash, tcc, and guile tarballs would have to already be downloaded in the current directory, but their hashes would be checked for integrity.

; Example package definitions in ‘boostrap0.scm’ for Guix 0.

(define-public mes
  (package
    (name "mes")
    (version "0")
    (source #f)
    (build-system trivial-build-system)
    (inputs
      ("exe"
       ,(origin
          (method url-fetch)
            (uri "https://example.org/mes") ; TODO: Actually use a URL pointing to the Mes executable.
            (sha256 (base32 "xxxxxx")))))
    (arguments
     `(#:builder
        (begin
          (mkdir (assoc-ref %outputs "out"))
          (copy-file (assoc-ref %build-inputs "exe") (assoc-ref %outputs "out")))))
    (home-page #f)
    (synopsis "...")
    (description "...")
    (license license:gpl3+)))

(define-public gash
  (package
    (name "gash")
    (version "0.2.0")
    (source
      (origin (method url-fetch)
        (uri (string-append "mirror://savannah/gash/gash-0.2.0.tar.gz"))
        (sha256 (base32 "xxxxxx"))))
    (build-system gnu-build-system)
    (inputs (list guile-3.0))
    (arguments '(#:output-hash "xxxxxx"))
    (home-page #f)
    (synopsis "...")
    (description "...")
    (license license:gpl3+)))

(define-public tcc
  ...)

(define-public guile
  ...)

Guix 0 would compute the hash of the packages and put their output at e.g. ./store0/xxx-gash-0.2.0. From there, it would compute the hash of the output and match it against the given #:output-hash. The inputs would be taken from the current directory instead of downloaded. The mapping could be made from the package name to the expected filename in the current directory. That would map e.g. "gash" to ./gash.tar and "mes" to ./mes (not a tarball for Mes, since it would be the plain executable assumed and used to run Guix 0). It would be trivial to ensure that the package names don’t clash in this file.

Guix 0 would be invoked as:

% ls
bootstrap0.scm bootstrap1.scm gash.tar guile.tar guix.tar guix0.scm guix1.scm mes tcc.tar

% ./mes guix0.scm

As soon as Guile is built, it would then be used to establish a larger subset of Guix, “Guix 1”. Guix 1 would take the same approach to building packages as Guix 0 (and thus wouldn’t need a daemon running), except that it would actually download the tarballs, and, in addition, it would support more syntax, and thus would be able to borrow more definitions from Guix proper.

After building Guile, Guix 0 would automatically use it to invoke Guix 1 (as e.g. ./store0/xxxxxx-guile-2.0/bin/guile guix1.scm), and thus we wouldn’t need to run it by ourselves.

boostrap1.scm would contain further definitions used during the bootstrap process used by Guix 1 until at least the definition of full Guix. Guix 1 would build these definitions to e.g. ./store1/xxxxxx-guix-1.3.0, similar to Guix 0 (but with ./store1 instead of ./store0). It would then rebuild the packages already built by Guix 0 alongside the packages defined in bootstrap1.scm up to Guix proper.

Then, Guix 1 would invoke the Guix daemon and the Guix CLI from its ./store1/bin directory, which would then rebuild all the packages built until now, this time to /gnu/store proper.

From there, we can trivially build a system image using guix system image ... as we normally would.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment