Hacking anything with GNU Guix
gexp.no
gexp.no
This keeps things tidy in terms of just having packages I need all the time installed, while still being very quick after the first run. (There are some caveats about garbage collection, but I rarely do that.)
And as the article mentioned there's also containers...lots of useful tools.
Here is the guix.scm file: https://git.dthompson.us/chickadee.git/tree/guix.scm
Here is another project (a static site generator) with a much simpler guix.scm file: https://git.dthompson.us/haunt.git/tree/guix.scm
In both cases, this same file can be used to create a development environment via 'guix shell', build from scratch in an isolated environment (handy to run before a release) via 'guix build', and it can even be installed with 'guix package'. It's super handy.
{docker, nix, guix} take any two. What are their differences?
A guix.scm file is a mostly declarative file, where one specifies the "what" not the "how", in contrast to a Dockerfile for example, where one writes sequential steps to take, to install all that is needed, which is of course much more prone to making mistakes. In guix one relies on the package specifications, which are already in guix for the dependencies and guix' machinery to process them. In docker one uses the directives offered by docker to write a Dockerfile instead.
I guess one could say guix is a bit higher level than docker and does not require you to know how to install something you depend on. On the other hand you need to know about guix' way of describing a package, if you want to create one. But for mere dependency specification, guix is much simpler than writing a Dockerfile.
I haven't used it yet, but I love the concept - I use small environmental scripts all the time, explicitly to not have to deal with vms, this sounds like it is much more flexible and crucially much more reproducable.
Silverblue gives you an immutable root filesystem, which is great for building a reliable and reproducible system, but it makes installing software more complicated. Containers are the main solution, and stuff like flatpaks and toolbox. That (mostly) works, but it’s a bit heavy handed and not without annoyances.
But with guix, your packages live in the guix store, and don’t touch your os packages. So that seems like a good way to manage software on top of an immutable OS.
Unfortunately when I went to try it out, I learned that the only way to change the default location of the guix store (since /gnu/store won’t work in silverblue, and Guix isn’t available as a package for fedora) is to build it from source... and then I ran into some build issues when trying to do that and gave up.
...So maybe someone less lazy than I will pull it off and report back whether it’s actually a good idea or not!
...Well, it might. I actually had zero experience with both ostree/silverblue and guix when I tried doing this, and may have made some hasty assumptions.
EDIT: I do know however that guix doesn't work if the store is a symlink. I tried doing that a while back and it didn't work.
chattr -i /
mkdir -p /gnu/store
chattr +i /
The last command restores the immutable attribute on the root filesystem. I am able to write arbitrary files (e.g. `touch /gnu/store/test`) even with that attribute set, so it isn't inherited by subdirectories (maybe obvious).Now, persistence of the /gnu/store data could be an issue, I'm not sure, but maybe this gets you started?
EDIT: next step is to get Guix installed and then try to update the system, we shall see how it goes
I think the solution will require using systemd to recreate that folder on boot, and do it early enough to not make the system panic like that.
Maybe that's a bad idea, I don't know enough about ostree/silverblue. However, after some more digging it seems like this solution would be preferable to changing the default guix store with a custom build, since doing that means you can't use any of the pre-compiled binary packages from the official guix servers.
Additionally, I have found some posts as far back as 2017 regarding using Guix or Nix on Fedora Atomic Linux or Fedora Container Linux (it seems the Silverblue name is more recent) and the fact that we're still puzzling it out 5 years later without much in the way of documented success does make me question the viability of the idea a bit.
EDIT: Or is the filesystem not literally read-only? Sibling comment seems to imply that it's just chattr +I'd
I could install Guix on top of something like Ubuntu or Fedora workstation for the same effect, but then I'll have two system package managers side by side, which is pointless and probably error prone.
In theory something like it could be a giant escape hatch so that if you run into Guix packaging difficulties for some software that, e.g., tries to fetch from the internet at runtime, you can just build with autoconf and make or `npm build` or whatever against the Fedora base system. Silverblue might be quirky enough that it has its own hiccups though, idk.
Maybe there are docs on repurposing that as a general packaging escape hatch, which someone more familiar with nonguix could point to in a reply.
Apparently there is an rpm-ostree-friendly RPM build of Nix that can be used to install Nix on Silverblue as well: https://github.com/nix-community/nix-installers/pull/8
It includes some code to get around the group/user management quirks that some other commenters have noted b0rked the Guix installer with Silverblue, so perhaps it gives an outline of how to install Guix on Silverblue for a hacker who'd like to try it :)
So doing a guix shell --container --emulate-fhs <packages> will have you in a container with the usual /lib, /bin, and so on, which should work just as a "typical" Linux distro. You'll want to share things from the host to the container (e.g. graphics) but that is all standard guix container options.
Either way, that's pretty exciting and I hope it can be merged soon!
https://guix.gnu.org/manual/devel/en/html_node/Invoking-guix...
https://guix.gnu.org/en/manual/en/html_node/Invoking-guix-en...
Whenever I need a disposable environment I reach for lxc/lxd.
Nix/Guix are basically the "glue" that connects two worlds.
See e.g.:
https://gitlab.com/nonguix/nonguix
> Guix channel for packages that can't be included upstream. Please do NOT promote or refer to this repository on any official Guix communication channels.
If you are already a Scheme user, you may find Guix more accessible for you. If you are not a C++ programmer, you may find hacking on the package manager itself easier with Guix.
In terms of the CLI interface, Guix really shines here. Guix also has a more centralized approach to documentation, which you may find helpful.
Beyond the surface-level language differences, Guix has gone with different abstractions than Nix in some key areas, and has some features that Nix lacks. One big one is that GuixSD uses a different model for defining the options that can be used to configure the system. Guix's approach¹ is more explicit, and features some provenance tracking for configured options— it can draw a graph for you showing where each setting on your system came from.
Guix also takes a different approach to pinning package versions and defining repositories of source packages, and its conventions for doing those things are more settled than their equivalents and alternatives in the Nix world.
Guix also supports a feature called 'grafts'² that allows you to avoid rebuilding the world in case of things like mission-critical security updates to glibc. This is a really cool and useful feature!
I'm sure there are other things that more serious Guix users can better highlight than this dilettante. :)
The Guix blog is really excellent! I strongly recommend it for getting a sense of what problems Guix tries to solve and how it sometimes approaches them differently than Nix does.
--
1: https://guix.gnu.org/manual/en/html_node/Defining-Services.h...
I think there's absolutely room to solve the same set of problems better than Nix does:
1. The number 1 problem for me has been documentation of nixpkgs. Nix lang is a bit funky but even if I was writing python the problem is that you're writing code to assign magic objects to magic variables and the only way to find the right ones is to read the nixpkgs source (and given the size of all-packages.nix and the limit of github's web viewer, maintain a local checkout).
2. Second place goes to the flake/non-flake divide where the nix community generally implies flakes are better but apart from the nix cli detailed docs, most things refuse to acknowledge its existence in the official docs.
3. Portability between macOS and Linux, where flakes actually make the situation worse as the root config is now system specific.
4. Tools like flakes, niv, the suggested way to write a shell.nix all want you to handle full commit hashes directly which is kinda unergonomic.
None of these are inherent to the problem space. If I was to keep writing the list, maybe around 9 and 10 are the things around nixlang being a funky language or /nix directory that the "you just need to understand functional languages/content addressable stores" discussion seems to think are the top ones.
Re: 4, Flakes do let you define inputs in terms of Git tags and branches and then have the computer resolve those to commit hashes for you, which is good.
Overall, I agree that it's not fair to think of package managers that work in the same basic paradigm as Nix as mere also-rans or clones. There's a lot of room to meaningfully experiment in the space and Guix's developers have proven thoughtful about where they want to differ in technical and ergonomic matters.
There is still some division in the Nix ecosystem between the pre-flake and the experimental post-flake way of doing things.
Guix uses an established programming language for configuration, which some might find attractive (I actually quite like Nixlang after getting used to it).
Guix makes installing non-free software a hassle (you have to include community sources). Nixpkgs doesn't impose this restriction, though you still have to explicitly allow the install of non-free packages when running Nix.
Overall, Guix seems like the more polished product, though NixOS/Nix is similar in functionality and has a larger collection of packages and more traction in general.
Possibly when comparing the Guix vs. Nix package managers, but for Linux distributions GuixSD ("Guix System" now) is very far behind NixOS in this regard. I've tried to install GuixSD on different hardware several times over the past couple years, and failed every time, between a lack of drivers and unpolished or buggy installer. Last time the installer wiped out my partition table without prompting when I went in to manually partition (to set up dual boot).
NixOS on the other hand has always been flawless to install, and now there's even a modern GUI installer.
I like Guix better in theory, but Nix wins in practicality.
So, do I agree necessarily agree with GNU's official distributions having this philosophy? Not really. Firmware and drivers is probably the one area I think the GNU people should finally throw in the towel. It keeps people out of running it, or doing so with relative ease. And the battle is for the most part lost on that end. But they do essentially tell you that it is likely it won't work across a lot of hardware out of the box.
But here is the download page [0] https://guix.gnu.org/en/download/ That mentions the Libre kernel with link to information about the Libre kernel [1] https://www.fsfla.org/ikiwiki/selibre/linux-libre/ With the following information:
"Linux, the kernel developed and distributed by Linus Torvalds et al, contains non-Free Software, i.e., software that does not respect your essential freedoms, and it induces you to install additional non-Free Software that it doesn't contain. Even after allegedly moving all firmware to a separate project as of release 4.14, Linux so-called "sources" published by Mr Torvalds still contain non-Free firmware disguised as source code. Stux, a cute penguin. Few realize he's not Free
GNU Linux-libre is a project to maintain and publish 100% Free distributions of Linux, suitable for use in Free System Distributions, removing software that is included without source code, with obfuscated or obscured source code, under non-Free Software licenses, that do not permit you to change the software so that it does what you wish, and that induces or requires you to install additional pieces of non-Free Software."
(I am former nixos user but now on guix)
https://gitlab.com/nonguix/nonguix
looks intentional to me
> GNOME, Xfce, LXDE, and Enlightenment are available (see Desktop Services), as well as a number of X11 window managers. However, KDE is currently missing.
[1]: https://guix.gnu.org/manual/en/html_node/Limitations.html
Does that mean that Guix just exports the required environment variables in the shell rather than wrapping each executable with a bash script [1] like nix does?
If yes, that's great, because the wrapper approach feels like an ugly hack. I found some executables on my nixos installation that are behind three layers of wrappers, and that's probably not the maximum.
I guess nix could improve this situation by making `wrapProgram` smarter (if the executable to be wrapped is already a wrapper, merge the inner and outer wrapper), but even single-layer wrappers are annoying, and I imagine they have some performance impact.
EDIT:
I forgot about nix-shell, which does actually export the right environment variables directly to the shell.
[1] https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-supp...
I haven't used nixos in a while and I got confused about this because to install python packages globally I used `python3.withPackages` and that does use the wrappers.
As background, we've long had a set of evolving shell scripts that fetch and build dependencies at specific versions -- like bash/dash/zsh/mksh/busybox to test against, re2c to generate code, CommonMark, Python 3.10, MyPy with pip dependencies, and (bonus) R with CRAN dependencies.
I wrote about 2 problems here: https://lobste.rs/s/s5co2f/where_contributors_have_problems_...
1. OS X and libc, which is not really a problem since our existing scripts don't solve it either. It was just one motivation for Nix that didn't quite work out.
2. The file system layout becomes different, and Oil's shell tests rely on that. So containers ended up being easier. The whole build and test system runs in OCI containers under Docker and podman now, so it's pretty reproducible and automated.
But I still think it would be nice if someone who actually knows Nix and Guix (unlike me) tries again. The dependencies are more stable now than 2 years ago.
I think you have to write like 10 Nix or Guix expressions from scratch with the exact tarballs that we use. (Otherwise the tests will break even more, because Oil's tests are extremely detailed and find bugs in specific versions of specific shells.)
Right now we have a 134 line shell.nix that tries to reuse Oil's scripts, but I think it doesn't gibe with the way that Nix and Guix are meant to be used. Probably the real solution would be more like 1000 lines from scratch?
I remember that Nix Flakes was what I thought Nix was going to be, but at the time it wasn't ready. I thought Nix was supposed to solve the "it works on my machine" problem but it actually doesn't -- you still need a CI because it's possible to write .nix expressions in ways that break the sandboxing (unlike Bazel where you always get it).
I had ran this by someone who knows Guix and my takeaway was that Guix is basically the same in that regard.
A key problem is that, at least for awhile, I want it to work in parallel with our existing system ... not have a "big break".
Though maybe a hybrid approach can work? Or does that defeat the purpose? I think a problem with these kinds of systems is that they can be "all or nothing". If you're halfway in, and halfway out, you don't get any benefits.
There's some ongoing work to better support 'generating' those Nix expressions and using them in builds without explicitly storing them anywhere, as well (John Ericson's RFCs on 'import-from-derivation' and related functionality), and for unifying such tooling (dream2nix). (This kind of thing is what the `guix import` CLI does, but it has some limitations.)
There's some talk of possibly using Oil Shell for a port of Nixpkgs to Windows, although another language might be chosen by whoever really takes up that work. People interested in adding native Windows support to Nix might be interested in helping you sort out Nix packaging for Oil (I see there is a package currently, but it's not a source build).
Nix doesn't sandbox builds on macOS at all by default, and turning sandboxing on breaks some builds: https://github.com/NixOS/nix/pull/1821
But the libc issue goes deeper. This blog post has some explanation: https://matthewbauer.us/blog/darwin-stdenv-update.html
> Linux is unique among operating systems due to the fact that the Kernel and Libc are developed independently. Linux is maintained by creator Linus Torvalds and a community of contributors. Glibc, the most popular Libc for Linux, is maintained by the GNU project. As a result, Linux has a strong separation between Syscalls and Libc.
> [ ... ]
> To accomplish this, Linux provides a stable list of syscalls that it has maintained across many versions. This is specified for i386 at arch/x86/entry/syscalls/syscall_32.tbl in the kernel tree. The syscalls specified here are the interface through which the Libc communicates with the kernel. As a result, applications built in 1992 can run on a modern kernel, provided it comes with copies of all its libraries.
> The macOS Libc is called libSystem. It is available on all macOS systems at /usr/lib/libSystem.B.dylib. This library is the main interface that binary compatibility is maintained in macOS. Unlike Linux, macOS maintains a stable interface in libSystem that all executables are expected to link to. This interface is guaranteed by Apple to be stable between versions.
> In Nixpkgs, we maintain this compatibility through a list of symbols that are exported by libSystem. This is a simple text list and is available for viewing at NixOS/nixpkgs/pkgs/os-specific/darwin/apple-source-releases/Libsystem/system_c_symbols. The symbol list is created by listing symbols (nm) on the minimum macOS version that we support (for my PR, 10.12). We do some linking tricks to ensure that everything that we build in Nixpkgs only contains those symbols. This means that we can reproducibly build on newer versions of macOS, while maintaining compatibility with older macOS versions. Unfortunately, newer symbols introduced in later versions cannot be used even on systems that have those symbols.
> A side effect of macOS design, is that fully static executables are not supported in macOS as they are on Linux. Without a stable syscall interface, there is nothing to provide compatibility between versions. As a result, Apple does not support this type of linking.
So Nixpkgs does do some things to try to let you peg builds only to what's available in older versions of the macOS libc, but it doesn't try to let you bring your own libc.
There's at least one libc that does claim this kind of portability of course, and is the subject of lots of good posts here on HN. Maybe the author of Cosmopolitan libc ( https://justine.lol/cosmopolitan/index.html ) might have something insightful to say here.
(Although Oil will obviously run on OS X because it uses plain libc from source, as is officially supported)
Cosmopolitan libc does seem interesting, because a shell basically only depends on libc.
It would be interesting to get Oil to run like that, on platforms where fork() is supported (not bare metal I assume).
Would it work on Windows? How do they do fork() emulation? I think that's very hard on Windows
Oh yeah it looks like it's slow on Windows (and OpenBSD)
https://justine.lol/cosmopolitan/functions.html
So I don't think it would make sense as a path for portability for a "production" shell
That said, I think it can also makes sense to aim higher in terms of performance at the cost of a more bespoke Windows implementation. lol
https://lobste.rs/s/jiyetr/hacking_anything_with_gnu_guix#c_...
https://github.com/oilshell/oil/blob/master/README.md -- click "Contribute with Gitpod"
Even Python's virtual environment mechanisms are opaque to me and guix seems to be able to basically do that - but for anything you can think of (?).
User namespaces are a Linux kernel feature. You can learn more about them from the man pages: https://www.man7.org/linux/man-pages/man7/user_namespaces.7....
But what happens when program A needs a library X version between 1.0 and 1.9, and program B needs library X version between 2.0 and 5.0? Both programs assume the system will have only one version on library X, and need it to meet their version requirements. Enter Guix (and it's cousin Nix).
With Guix, instead of the single global setting listing a couple directories to find all libraries in, a unique environment is crafted for running program A where a large number of folders appear to be listed in the setting, each including exactly one version of one library. In parallel a different unique environment is crafted for program B in the same way. But in A's environment the folder for library X is the one with library X version 1.5, and in B's environment the folder for library X is the one for version 3.2, but they might both use the same folder for library Y version 0.6. This may seem obvious, but it's not as easy as this all sounds. There are actually a lot of global settings for where to find dependencies, some of them have to match each other, and which ones get used depends on what the programs being run are. Also there's more at play then just the version number of the library, what optional parts are included also plays a role. This gives a huge number of possible variations of packages, not all of which are easily summarized by something as simple as a version number. Therefore Guix had to have some way of consistently referring to a package that used a specific version of code, included specific options, and a while list of other factors that affect what results (called a "build closure"). How do you make sure you actually found everything that affects the build result though? You run it again under conditions that shouldn't change it and confirm it's the same output. Successfully doing this is called "deterministic builds", and allows you to conclusively state that a list of these inputs (the build closure) will always produce exactly these outputs. This may involve doing things like making the build of the package always think it's 1970-01-01 00:00:00 UTC+0 so builds that embed a timestamp always have the same timestamp, which is why Guix has had to create a large list of packages you need to pull from. One you have these deterministic builds, you can ask for a package via build closure, Guix can download (or locally build and populate) the package into your cache, and every subsequent request in a Guix-managed she'll used to run a program can point to the library folder in the cache.
Making this even more difficult, building code has it's own dependencies added to the ones necessary for running it ("run time" vs "build time" dependencies). For compiled code this can be tools like the compiler used for example.
When trying to figure out what's in the build closure, it can be very difficult. One way to make sure you really do control everything is to isolate it. That's what containers do.
When I think of "hacking code you didn't write" one of the biggest hurdles for me is "inserting my changes to the behavior of the code with minimal refactoring of the original" - attacking the problem from, perhaps, compiler instrumentation level.
e.g. suppose when I use some library function f, there is a deeply nested function, g, that has access to some data x, which is cleaned up somewhere before the API returns its result.
How do I tell my compiler "I want a new function h, which, when given the same arguments as f, returns x, by returning from within g" ?
Edit: for the source of a package guix build --source thepackage will return the path to the source (as stored in the store). This includes any patches or transformations (e.g. you can pass patches to be included or a git/branch/commit location to pull the source from instead of what is defined in the package definition)
It's likely a lot smaller than a Docker container that contains a whole Ubuntu runtime, and it'll automatically share a cache of deoendencies with any Guix packaged you may have installed locally. It also has normal/native access to your filesystem (and thus your dotfiles and your homedir), since it doesn't live in a container.