<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title>Nemin's Blog</title><id>https://nemin.hu/feed.xml</id><subtitle>Recent Posts</subtitle><updated>2026-03-13T08:18:49Z</updated><link href="https://nemin.hu/feed.xml" rel="self" /><link href="https://nemin.hu" /><entry><title>Guix System - One Month Later</title><id>https://nemin.hu/guix-one-month-later.html</id><author><name>Nemin</name></author><updated>2026-03-02T08:58:00Z</updated><link href="https://nemin.hu/guix-one-month-later.html" rel="alternate" /><content type="html">&lt;div role=&quot;doc-toc&quot; id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div role=&quot;doc-toc&quot; id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#community&quot;&gt;1. Community&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#irc&quot;&gt;1.1. IRC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mailing-lists&quot;&gt;1.2. Mailing lists&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#subreddit&quot;&gt;1.3. Subreddit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#fediverse&quot;&gt;1.4. Fediverse&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#packaging-wows-and-woes&quot;&gt;2. Packaging wows and woes&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#i-really-love-the-go-getter-attitude-of-guix&quot;&gt;2.1. I really love the go-getter attitude of Guix&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#a-side-note-about-speed&quot;&gt;2.1.1. A side-note about speed&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#debugging-is-an-unpleasant-experience&quot;&gt;2.2. Debugging is an unpleasant experience&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#missing-parentheses&quot;&gt;2.2.1. Missing parentheses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#non-existent-variables&quot;&gt;2.2.2. Non-existent variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#circular-dependencies&quot;&gt;2.2.3. Circular dependencies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#a-cornucopia-of-styles&quot;&gt;2.3. A cornucopia of styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#changelog-is-confusing&quot;&gt;2.4. ChangeLog is confusing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#system-ups-and-downs&quot;&gt;3. System ups and downs&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#stability-of-my-config&quot;&gt;3.1. Stability of my config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#interfacing-with-my-phone&quot;&gt;3.2. Interfacing with my phone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#blunderbird&quot;&gt;3.3. Blunderbird&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#nvidia-drivers-and-non-lts-kernels&quot;&gt;3.4. NVIDIA drivers and non-LTS kernels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#no-easy-way-of-seeing-what-is-updated&quot;&gt;3.5. No easy way of seeing what is updated&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sleep-causes-crashes&quot;&gt;3.6. Sleep causes crashes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#odd-freezes-and-crashes&quot;&gt;3.7. Odd freezes and crashes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;4. Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
It's been about a month since I &lt;a href=&quot;./guix.html&quot;&gt;installed Guix System&lt;/a&gt; on my PC and now that I had some time to play around with it more seriously, I figured I'd record my additional thoughts, positive and negative, and why I ultimately chose not to stick with it.
&lt;/p&gt;
&lt;div id=&quot;outline-container-community&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;community&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;1.&lt;/span&gt; Community&lt;/h2&gt;
&lt;div id=&quot;text-1&quot; class=&quot;outline-text-2&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-irc&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;irc&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;1.1.&lt;/span&gt; IRC&lt;/h3&gt;
&lt;div id=&quot;text-1-1&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
I am happy to report, that the &lt;a href=&quot;https://logs.guix.gnu.org/guix&quot;&gt;IRC chat&lt;/a&gt; of Guix is both surprisingly active and very welcoming. I was able to join in and participate in discussions as a complete newcomer and I never once felt like people were ignoring me or valuing my opinion less due to my newness. This is excellent and a high standard for any other community to strive for.
&lt;/p&gt;

&lt;p&gt;
My general impression was that there is plenty of good-hearted banter, people helping each other figure stuff out, people discussing their configs and such. Hell, I even got a few shout outs for my previous post, which was super encouraging as a new author.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-mailing-lists&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;mailing-lists&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;1.2.&lt;/span&gt; Mailing lists&lt;/h3&gt;
&lt;div id=&quot;text-1-2&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
I feel like it's much harder to engage with Guix discussions on a longer-term scale. By this I mean that there are a few &lt;a href=&quot;https://guix.gnu.org/en/contact/&quot;&gt;mailing lists&lt;/a&gt;, which seem quite active, but as someone who grew up &lt;i&gt;after&lt;/i&gt; mailing lists were cool, they're a bit intimidating to me. This doesn't mean I cannot find my way around (with some effort), but it's definitely a barrier compared to having a conventional forum.
&lt;/p&gt;

&lt;p&gt;
However, I recognise that this is a me / my generation problem and that likely for many others forums would prove just as cumbersome. Mailing lists are a very common thing in GNU/FSF related projects and, without even looking at the larger GNU projects and just by perusing the Guix archives, it's very clear that they're an effective way of communication, that requires no account and is compatible with a ton of configurations.
&lt;/p&gt;

&lt;p&gt;
I just wonder if there could be a better way of handling them, that doesn't feel as daunting to people who didn't grow up with this form of communication. I think even something as simple as a bit of CSS applied to the online interface of the mailing list could make it immediately a lot more appealing, but perhaps I'm simply being shallow.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-subreddit&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;subreddit&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;1.3.&lt;/span&gt; Subreddit&lt;/h3&gt;
&lt;div id=&quot;text-1-3&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
&lt;a href=&quot;https://reddit.com/r/GUIX/&quot;&gt;r/GUIX&lt;/a&gt; is not an official community, but it doesn't seem bad. People generally seem to only post questions, with the exception of the occasional screenshot of their config. It is a little barren, with only a couple posts a month, but Guix System is a niche distro and most communication happens on either the mailing list or IRC (or on the Codeberg issue tracker), which leaves only a small subset of people who are both interested enough in Guix too want to talk about it, but are also willing to put up with a closed and not exactly sqeaky-clean platform like Reddit.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-fediverse&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;fediverse&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;1.4.&lt;/span&gt; Fediverse&lt;/h3&gt;
&lt;div id=&quot;text-1-4&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
Guix has a presence on &lt;a href=&quot;https://hachyderm.io/@guix&quot;&gt;Hachyderm&lt;/a&gt;, which is one of the more known Mastodon servers out there. I have little experience with the Fediverse (I've never even used Twitter much, which is the mainstream equivalent to Mastodon), but I'm glad that it exists. The account seems quite active too, the team running it is diligently sharing Guix-related posts and even makes some posts of their own.
&lt;/p&gt;

&lt;p&gt;
They actually &lt;a href=&quot;https://hachyderm.io/@guix/116000285085032513&quot;&gt;shared&lt;/a&gt; my own article on their page, which I'm super happy about. If you're reading this, thank you very much! Interactions like this make me want to look deeper into this corner of the internet.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-packaging-wows-and-woes&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;packaging-wows-and-woes&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;2.&lt;/span&gt; Packaging wows and woes&lt;/h2&gt;
&lt;div id=&quot;text-2&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
After my success with packaging &lt;a href=&quot;./guix-packaging.html&quot;&gt;WezTerm&lt;/a&gt;, I decided to jump into upgrading some other packages too. These upgrades were mostly minor stuff, not really worth their own posts, but they did come with recurring issues and notes, that I figured would be worth collecting here.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-i-really-love-the-go-getter-attitude-of-guix&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;i-really-love-the-go-getter-attitude-of-guix&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;2.1.&lt;/span&gt; I really love the go-getter attitude of Guix&lt;/h3&gt;
&lt;div id=&quot;text-2-1&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
You want to upgrade a package? Go ahead, do it. With the exception of trying to do something so big that it requires a &lt;a href=&quot;https://consensus.guix.gnu.org/&quot;&gt;team consensus&lt;/a&gt; or is so huge that it requires a dedicated team (think GNOME or KDE), nobody will say &amp;quot;That's not your authority!&amp;quot;. You can just open a PR, get some eyes on your change and that's it.
&lt;/p&gt;

&lt;p&gt;
At the time I was writing this, I was &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/6726&quot;&gt;packaging&lt;/a&gt; the &lt;a href=&quot;https://odin-lang.org/&quot;&gt;Odin language&lt;/a&gt; for Guix. Apparently nobody did it before me, their compilation guide seemed simple enough, so I went and did it myself. As someone who works in a corporate environment, this sort of freedom is exhilarating, especially since you're not just simply helping yourself, but all the other people who might want to try this language in the future and happen to be using Guix too.
&lt;/p&gt;

&lt;p&gt;
This is very much unlike Nix, where every package has one or more so-called maintainers, who have first say in how a package is developed and upgraded. On one hand, this does provide a bit of accountability. People will feel stronger about keeping stuff up to date and tidy if it's at least superficially &amp;quot;theirs&amp;quot;. On the other hand, it's yet another hurdle for those who would like to help out, but aren't yet in the maintainers list and it makes Nix feel more private than it really is.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-a-side-note-about-speed&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;a-side-note-about-speed&quot;&gt;&lt;span class=&quot;section-number-4&quot;&gt;2.1.1.&lt;/span&gt; A side-note about speed&lt;/h4&gt;
&lt;div id=&quot;text-2-1-1&quot; class=&quot;outline-text-4&quot;&gt;
&lt;p&gt;
The only real negative part of this whole contributing experience is the speed of reviews. There are PRs open from eight months ago. While such is not unusual in corporate environments (hell, make it years), I believe it's not healthy for a rolling release Linux distro, except in very specific cases (projects of such magnitude that you really need a year-ish time to develop them).
&lt;/p&gt;

&lt;p&gt;
By the time people actually take a look at the PR, the project had moved on and is substantially different from when the PR was opened. At best this will result in headache-inducing merge conflicts, at worst the PR is no longer relevant, because some other development had obsoleted it. While I haven't experienced such myself yet, I bet it'd be a pretty disheartening feeling.
&lt;/p&gt;

&lt;p&gt;
Now, I'll freely admit, my PRs so far had generally speedy review processes, so I personally don't have anything to complain about. My point more is that this is not a given thing, but rather something that comes down to the maintainers free time and available effort. And long delays can result from something as mild as some irritation to actual breakages (see the section about &lt;a href=&quot;#blunderbird&quot;&gt;Thunderbird&lt;/a&gt; of this article).
&lt;/p&gt;

&lt;p&gt;
This is nothing new though. The Guix team itself &lt;a href=&quot;https://slides.com/futurile/survey-says-5-guix-dev-priorities&quot;&gt;identified&lt;/a&gt; the slowness of reviews as one of the main things people find cumbersome about the project. This presentation is from a year, so hopefully the gears are turning in the background as I'm writing this and soon enough this problem will be a thing of the past. Until then, any prospective packagers should be aware that they are more than welcome, but they should also come prepared for long waits of up to a week or more, before their package is considered.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-debugging-is-an-unpleasant-experience&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;debugging-is-an-unpleasant-experience&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;2.2.&lt;/span&gt; Debugging is an unpleasant experience&lt;/h3&gt;
&lt;div id=&quot;text-2-2&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
In my experience, errors messages in Guix are not very helpful. This is not super surprising seeing Guix is a massive build system built on a completely dynamic language, but regardless it makes debugging a real pain in the neck. Which is a shame, because one of the main selling points of Lisp(likes) is the fact that their REPL experience is unparalleled and that all other languages lag behind with their tacked-on / simplistic debuggers.
&lt;/p&gt;

&lt;p&gt;
In Guix' case, while it is &lt;a href=&quot;https://guix.gnu.org/manual/devel/en/html_node/Using-Guix-Interactively.html&quot;&gt;possible to use&lt;/a&gt; the Guile REPL to interface with the build system, so far I didn't find it useful enough to replace the basic &amp;quot;Edit in editor -&amp;gt; Compile in terminal -&amp;gt; Try to figure out errors&amp;quot; loop.
&lt;/p&gt;

&lt;p&gt;
The three most confusing issues I've experienced so far were:
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-missing-parentheses&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;missing-parentheses&quot;&gt;&lt;span class=&quot;section-number-4&quot;&gt;2.2.1.&lt;/span&gt; Missing parentheses&lt;/h4&gt;
&lt;div id=&quot;text-2-2-1&quot; class=&quot;outline-text-4&quot;&gt;
&lt;p&gt;
For this first example, I'll include the entire stack trace. In the other two, it will be truncated, but be aware that with each error we get this much on our screen.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;  /home/nemin/programming/projects/guix/gnu/packages/guile-xyz.scm:7973:1: unexpected end of input while searching for: )
error: googletest: unbound variable
hint: Did you forget a `use-modules' form?

error: bzip2: unbound variable
hint: Did you forget a `use-modules' form?

error: libusb: unbound variable
hint: Did you forget a `use-modules' form?

error: make-lld-wrapper: unbound variable
hint: Did you forget `(use-modules (gnu packages llvm))' or `#:use-module (gnu packages llvm)'?

error: tcc: unbound variable
hint: Did you forget a `use-modules' form?

error: cross-gcc-toolchain: unbound variable
hint: Did you forget `(use-modules (gnu packages cross-base))' or `#:use-module (gnu packages
cross-base)'?

error: gnu-make: unbound variable
hint: Did you forget a `use-modules' form?

error: tar: unbound variable
hint: Did you forget a `use-modules' form?

error: gcc-toolchain: unbound variable
hint: Did you forget a `use-modules' form?

error: xdgpp: unbound variable
hint: Did you forget a `use-modules' form?

error: cross-binutils: unbound variable
hint: Did you forget `(use-modules (gnu packages cross-base))' or `#:use-module (gnu packages
cross-base)'?

Throw to key `unbound-variable' with args `(&amp;quot;resolve-interface&amp;quot; &amp;quot;no binding `~A' in module ~A&amp;quot; (shared-mime-info (gnu packages freedesktop)) #f)'.
Backtrace:
In guix/status.scm:
    842:4 19 (call-with-status-report _ _)
In ice-9/boot-9.scm:
  1752:10 18 (with-exception-handler _ _ #:unwind? _ # _)
In guix/store.scm:
   504:37 17 (thunk)
   1119:8 16 (call-with-build-handler #&amp;lt;procedure 7f6d9e2ab390 at g���&amp;gt; ���)
In guix/scripts/build.scm:
    646:2 15 (_)
   695:42 14 (loop _ _ ())
In gnu/packages.scm:
    512:2 13 (%find-package &amp;quot;haunt-next&amp;quot; &amp;quot;haunt-next&amp;quot; #f)
    392:6 12 (find-best-packages-by-name _ _)
   322:56 11 (_ &amp;quot;haunt-next&amp;quot; _)
In unknown file:
          10 (force #&amp;lt;promise #&amp;lt;procedure 7f6d9e306700 at gnu/packag���&amp;gt;)
In gnu/packages.scm:
   244:33  9 (fold-packages #&amp;lt;procedure 7f6d9d90b778 at gnu/package���&amp;gt; ���)
In guix/discovery.scm:
   158:11  8 (all-modules _ #:warn _)
In srfi/srfi-1.scm:
   460:18  7 (fold #&amp;lt;procedure 7f6d9e3437e0 at guix/discovery.scm:1���&amp;gt; ���)
In guix/discovery.scm:
   148:19  6 (_ _ ())
    115:5  5 (scheme-modules _ _ #:warn _)
In srfi/srfi-1.scm:
   691:23  4 (filter-map #&amp;lt;procedure 7f6d9e343680 at guix/discove���&amp;gt; . #)
In guix/discovery.scm:
   123:24  3 (_ . _)
In guix/ui.scm:
    365:2  2 (report-unbound-variable-error _ #:frame _)
In ice-9/boot-9.scm:
  1685:16  1 (raise-exception _ #:continuable? _)
  1685:16  0 (raise-exception _ #:continuable? _)

ice-9/boot-9.scm:1685:16: In procedure raise-exception:
Throw to key `match-error' with args `(&amp;quot;match&amp;quot; &amp;quot;no matching pattern&amp;quot; (unbound-variable &amp;quot;resolve-interface&amp;quot; &amp;quot;no binding `~A' in module ~A&amp;quot; (shared-mime-info (gnu packages freedesktop)) #f))'.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Admittedly in this case it's not too bad, because Guile does actually inform you that a closing parenthesis is missing, but the error is still buried under a load of completely irrelevant error messages.
&lt;/p&gt;

&lt;p&gt;
Additionally, if this happens, &lt;code&gt;guix edit&lt;/code&gt; no longer works and you need to manually open the file that contains the issue and find the package. Not a big deal, but a bummer.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-non-existent-variables&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;non-existent-variables&quot;&gt;&lt;span class=&quot;section-number-4&quot;&gt;2.2.2.&lt;/span&gt; Non-existent variables&lt;/h4&gt;
&lt;div id=&quot;text-2-2-2&quot; class=&quot;outline-text-4&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;error: reversion: unbound variable
hint: Did you forget a `use-modules' form?

error: googletest: unbound variable
hint: Did you forget a `use-modules' form?

error: bzip2: unbound variable
hint: Did you forget a `use-modules' form?

&amp;lt;... a lot of other stuff ...&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
It happened to me once or twice that I was either still referencing a variable that I previously cut or I made an accidental typo.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.1&quot; href=&quot;#fn.1&quot; class=&quot;footref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;p&gt;
Just like with the missing parentheses, you technically get an error, but it's buried and blends in with all the other completely irrelevant errors (which are actually not even legitimate errors, they're just caused by the process buckling on itself).
&lt;/p&gt;

&lt;p&gt;
Of course, it's hard to blame Guix here. There is no programmatic way of telling if the user was just silly or if there really is a variable somewhere in some yet unused module. But it'd still be so much better if the error stopped right after the first (and only legitimate) missing variable.
&lt;/p&gt;

&lt;p&gt;
Also &lt;code&gt;guix edit&lt;/code&gt; breaks as before.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-circular-dependencies&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;circular-dependencies&quot;&gt;&lt;span class=&quot;section-number-4&quot;&gt;2.2.3.&lt;/span&gt; Circular dependencies&lt;/h4&gt;
&lt;div id=&quot;text-2-2-3&quot; class=&quot;outline-text-4&quot;&gt;
&lt;p&gt;
This one in my opinion is the most problematic common issue a package author might face. Chiefly because the error message you get in this case is not even useful:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;error: gcc-toolchain: unbound variable
hint: Did you forget a `use-modules' form?

error: xdgpp: unbound variable
hint: Did you forget a `use-modules' form?

error: cross-binutils: unbound variable
hint: Did you forget `(use-modules (gnu packages cross-base))' or `#:use-module (gnu packages
cross-base)'?

Throw to key `unbound-variable' with args `(&amp;quot;resolve-interface&amp;quot; &amp;quot;no binding `~A' in module ~A&amp;quot; (shared-mime-info (gnu packages freedesktop)) #f)'.

&amp;lt;... a lot of other irrelevant junk ...&amp;gt;

ice-9/boot-9.scm:1685:16: In procedure raise-exception:
Throw to key `match-error' with args `(&amp;quot;match&amp;quot; &amp;quot;no matching pattern&amp;quot; (unbound-variable &amp;quot;resolve-interface&amp;quot; &amp;quot;no binding `~A' in module ~A&amp;quot; (shared-mime-info (gnu packages freedesktop)) #f))'.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The untrained packager might start pulling in stuff like &lt;code&gt;gcc-toolchain&lt;/code&gt; or &lt;code&gt;xdgpp&lt;/code&gt;, not understanding why they even need these or start poking around &lt;code&gt;(gnu packages freedesktop)&lt;/code&gt; to no avail. As far as I know, there is no good method for figuring out what is causing a circular dependency beyond good intuition and bisecting your code until you find the culprit.
&lt;/p&gt;

&lt;p&gt;
I've struggled quite a bit with this while &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/6398#issuecomment-10783709&quot;&gt;packaging Hare 0.26.0&lt;/a&gt; and, while I did manage to solve the issue I faced there, I wasn't extremely happy with the solution:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-scheme&quot;&gt;(build-system hare-build-system)
(native-inputs
   (list scdoc
         (module-ref (resolve-interface '(gnu packages hare)) 'harec)
         qbe))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
In this instance, as you may have guessed, &lt;code&gt;harec&lt;/code&gt; is the culprit. I'm still not 100% on all the details, but I believe the problem is caused by &lt;code&gt;hare-build-system&lt;/code&gt; pulling in the hare module (which &lt;code&gt;harec&lt;/code&gt; belongs to) and so when we're trying to import it in the package definition, it can't work out the right order.
&lt;/p&gt;

&lt;p&gt;
Whatever's the reason, the solution isn't super difficult, but it is also not exactly obvious. Firstly, we need to get the module &amp;quot;behind Guix's back&amp;quot;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-info&quot;&gt;-- Scheme Procedure: resolve-interface name [#:select=#f] [#:hide='()]
        [#:prefix=#f] [#:renamer=#f] [#:version=#f]
   Find the module named NAME as with ���resolve-module��� and return its
   interface.  The interface of a module is also a module object, but
   it contains only the exported bindings.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Next, we need to extract &lt;code&gt;harec&lt;/code&gt; from it:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-info&quot;&gt;-- Scheme Procedure: module-ref module name
   Look up the value bound to NAME in MODULE.  Like ���module-variable���,
   but also does a ���variable-ref��� on the resulting variable, raising
   an error if NAME is unbound.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
What we're ultimately left with is the same as if we simply imported the module normally and then put &lt;code&gt;harec&lt;/code&gt; into the list. The trick here is that the various inputs Guix takes (including &lt;code&gt;native-inputs&lt;/code&gt;) are &lt;i&gt;&lt;a href=&quot;https://stackoverflow.com/questions/925365/what-is-a-thunk-as-used-in-scheme-or-in-general&quot;&gt;thunked&lt;/a&gt;&lt;/i&gt;. This means the code inside these forms isn't immediately run, but is instead delayed to a later moment, unlike the build system which is already resolved during the start of evaluation.
&lt;/p&gt;

&lt;p&gt;
In effect, by only ever referring to &lt;code&gt;harec&lt;/code&gt; in a &lt;i&gt;thunked&lt;/i&gt; environment, we've completely side-stepped the whole &amp;quot;where should we import the module&amp;quot; problem by forcing the package definition to wait.
&lt;/p&gt;

&lt;p&gt;
I'll freely admit that I would have not figured this out had there not been many other packages whose authors have faced this exact problem before and before the kind help of &lt;a href=&quot;https://codeberg.org/tinystar&quot;&gt;tinystar&lt;/a&gt;, who guided me towards targeting the &lt;code&gt;native-inputs&lt;/code&gt; instead of my original idea.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.2&quot; href=&quot;#fn.2&quot; class=&quot;footref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-a-cornucopia-of-styles&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;a-cornucopia-of-styles&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;2.3.&lt;/span&gt; A cornucopia of styles&lt;/h3&gt;
&lt;div id=&quot;text-2-3&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
Guix is a very flexible system. This is usually a great thing, because the last thing you want is your build system to constrain you from including a package that otherwise fits into the repo. However, this also bring with itself the fact that (beyond what the linting script offers) there is little guidance on how your package should be organised.
&lt;/p&gt;

&lt;p&gt;
This shows up both in small and big things. A good example is that the order of fields in the package definition is arbitrary. People generally put &lt;code&gt;name&lt;/code&gt; first, but there is nothing actually stopping you from putting it last. Sure, this probably wouldn't pass peer-review, but with other fields, the situation is far less obvious.
&lt;/p&gt;

&lt;p&gt;
For instance, should you introduce all static metadata first or should you go by the usual order and put &lt;code&gt;arguments&lt;/code&gt; before the description and license? How do you break lists aesthetically? Do you use &lt;code&gt;(arguments (list stuff))&lt;/code&gt; or &lt;code&gt;(arguments `(stuff))&lt;/code&gt;?
&lt;/p&gt;

&lt;p&gt;
And then there's the even larger differences:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;p&gt;
&lt;b&gt;Path construction:&lt;/b&gt; When it comes to constructing paths,
&lt;/p&gt;

&lt;div class=&quot;sameline&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 1: &lt;/span&gt;Do you use &lt;code&gt;format&lt;/code&gt;?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(format #f &lt;span class=&quot;org-string&quot;&gt;&amp;quot;~a/path/to/bin&amp;quot;&lt;/span&gt; #$output)
&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 2: &lt;/span&gt;Or &lt;code&gt;string-append&lt;/code&gt;?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(string-append #$output &lt;span class=&quot;org-string&quot;&gt;&amp;quot;/path/to/bin&amp;quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 3: &lt;/span&gt;Or perhaps &lt;code&gt;in-vicinity&lt;/code&gt;?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(in-vicinity #$output &lt;span class=&quot;org-string&quot;&gt;&amp;quot;path/to/bin&amp;quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;
&lt;b&gt;Code repetition:&lt;/b&gt; When installing completions,
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 4: &lt;/span&gt;Do you spell out each shell?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(&lt;span class=&quot;org-keyword&quot;&gt;with-output-to-file&lt;/span&gt; bash-file
  (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _ (invoke &lt;span class=&quot;org-string&quot;&gt;&amp;quot;binary&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;output-bash&amp;quot;&lt;/span&gt;)))
(&lt;span class=&quot;org-keyword&quot;&gt;with-output-to-file&lt;/span&gt; zsh-file
  (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _ (invoke &lt;span class=&quot;org-string&quot;&gt;&amp;quot;binary&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;output-zsh&amp;quot;&lt;/span&gt;)))
(&lt;span class=&quot;org-keyword&quot;&gt;with-output-to-file&lt;/span&gt; fish-file
  (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _ (invoke &lt;span class=&quot;org-string&quot;&gt;&amp;quot;binary&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;output-fish&amp;quot;&lt;/span&gt;)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 5: &lt;/span&gt;Or do you just delegate it to a &lt;code&gt;for-each&lt;/code&gt;-&lt;code&gt;match-lambda&lt;/code&gt; pair?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;
(&lt;span class=&quot;org-keyword&quot;&gt;for-each&lt;/span&gt;
 (&lt;span class=&quot;org-keyword&quot;&gt;match-lambda&lt;/span&gt;
   ((file-name . command)
    (&lt;span class=&quot;org-keyword&quot;&gt;with-output-to-file&lt;/span&gt; file-name
      (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _ (invoke &lt;span class=&quot;org-string&quot;&gt;&amp;quot;binary&amp;quot;&lt;/span&gt; command))))
   '((bash-file . &lt;span class=&quot;org-string&quot;&gt;&amp;quot;output-bash&amp;quot;&lt;/span&gt;)
     (zsh-file . &lt;span class=&quot;org-string&quot;&gt;&amp;quot;output-zsh&amp;quot;&lt;/span&gt;)
     (fish-file . &lt;span class=&quot;org-string&quot;&gt;&amp;quot;output-fish&amp;quot;&lt;/span&gt;))))
&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;
&lt;b&gt;Environment variables:&lt;/b&gt; If you need to set environment variables,
&lt;/p&gt;

&lt;div class=&quot;sameline&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 6: &lt;/span&gt;Do you make a whole phase just for this purpose?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(modify-phases %standard-phases
  (add-before 'build 'set-environment
    (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _
      (setenv &lt;span class=&quot;org-string&quot;&gt;&amp;quot;VAR1&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;VALUE1&amp;quot;&lt;/span&gt;)
      (setenv &lt;span class=&quot;org-string&quot;&gt;&amp;quot;VAR2&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;VALUE2&amp;quot;&lt;/span&gt;)))
  (replace 'build
    (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _ ...)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 7: &lt;/span&gt;Or do you use &lt;code&gt;setenv&lt;/code&gt; inside the &lt;code&gt;build&lt;/code&gt; phase?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(modify-phases %standard-phases
  (replace 'build
    (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _
      (setenv &lt;span class=&quot;org-string&quot;&gt;&amp;quot;VAR1&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;VALUE1&amp;quot;&lt;/span&gt;)
      (setenv &lt;span class=&quot;org-string&quot;&gt;&amp;quot;VAR2&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;VALUE2&amp;quot;&lt;/span&gt;)
      ...)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;
&lt;b&gt;Post-processing:&lt;/b&gt; If you need your built binary for post-processing (e.g. installing completions, generating extra files, etc.),
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 8: &lt;/span&gt;Do you call it from your build folder?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(&lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; ((bin &lt;span class=&quot;org-string&quot;&gt;&amp;quot;./build/folder/your-binary&amp;quot;&lt;/span&gt;))
  (invoke bin &lt;span class=&quot;org-string&quot;&gt;&amp;quot;postprocess&amp;quot;&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 9: &lt;/span&gt;Or your output folder?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;&lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;Also, do note that the whole previous debacle of which
&lt;/span&gt;&lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;path joining method to use still applies!
&lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; ((bin (string-append #$output &lt;span class=&quot;org-string&quot;&gt;&amp;quot;/bin/your-binary&amp;quot;&lt;/span&gt;)))
  (invoke bin &lt;span class=&quot;org-string&quot;&gt;&amp;quot;postprocess&amp;quot;&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 10: &lt;/span&gt;Do you even call it straight or do invoke it indirectly to account for cross-compilation?&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;&lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;Taken from gnu/packages/rust-apps.scm, &amp;quot;just&amp;quot; package
&lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; ((just (&lt;span class=&quot;org-keyword&quot;&gt;if&lt;/span&gt; ,(%current-target-system)
                (search-input-file native-inputs &lt;span class=&quot;org-string&quot;&gt;&amp;quot;/bin/just&amp;quot;&lt;/span&gt;)
                (string-append out &lt;span class=&quot;org-string&quot;&gt;&amp;quot;/bin/just&amp;quot;&lt;/span&gt;))))
  (&lt;span class=&quot;org-keyword&quot;&gt;with-output-to-file&lt;/span&gt;
             (string-append bash-completions-dir &lt;span class=&quot;org-string&quot;&gt;&amp;quot;/just&amp;quot;&lt;/span&gt;)
             (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _ (invoke just &lt;span class=&quot;org-string&quot;&gt;&amp;quot;--completions&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;bash&amp;quot;&lt;/span&gt;))))
&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
I'm sure there's a lot more, but these are the stuff that I personally experienced. The issue is that for most of these you might feel like one or the other is the &lt;i&gt;obvious&lt;/i&gt; answer, but if you scroll through the package definitions, I'd bet money on it that you'd find multiple examples of all of them.
&lt;/p&gt;

&lt;p&gt;
Now, don't get me wrong, I'm not pointing fingers at anybody. Guix is a volunteer project to which hundreds, if not thousands of people contribute to. You cannot expect perfect coordination from so many people, especially volunteers who might just want to see X or Y package in the registry and don't necessarily care about digging super deep into how things work. Not to mention, having ten different similarly good ways of doing something is a known Lisp curse. When you have barely any syntax and very convenient tools to mess with ASTs, all hell breaks loose.
&lt;/p&gt;

&lt;p&gt;
But I think it'd still make sense to codify some basics at least. Have a given order where package fields should go. If someone strays from this, have them justify it with a solid reason. Give pointers on divisive styles like the examples above, providing reasons why Guix recommends one over the other.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.3&quot; href=&quot;#fn.3&quot; class=&quot;footref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;p&gt;
This would serve two important purposes:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Firstly, with less noise in the code, it'd be much easier for everyone to study the code, make changes, and review others' changes. Mundane code beats smart code when it comes to working together and with Lisp(likes) one already needs discipline to write &amp;quot;standardised&amp;quot; code.&lt;/li&gt;
&lt;li&gt;Secondly, with these things codified, newcomers would have one less thing to be stumped by. I was so surprised when during my first PR my usage of &lt;code&gt;string-append&lt;/code&gt; was replaced with &lt;code&gt;in-vicinity&lt;/code&gt;, not only because I had no idea that this function existed in the first place, but because I was working based on another package where &lt;code&gt;string-append&lt;/code&gt; was used and didn't even consider it's not the only option.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-changelog-is-confusing&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;changelog-is-confusing&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;2.4.&lt;/span&gt; ChangeLog is confusing&lt;/h3&gt;
&lt;div id=&quot;text-2-4&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
I briefly touched on ChangeLog in my WezTerm post, but I wanted to bring it up again, because despite my best efforts at trying to build &amp;quot;muscle memory&amp;quot; for it, I still feel nearly as lost as I did at the start.
&lt;/p&gt;

&lt;p&gt;
Okay, that's not entirely fair. I was able to get the provided Emacs's yasnippet templates loaded, which automate some of the tedium that comes with the ChangeLog commit message style. I've also been made aware that there is a file under &lt;code&gt;etc&lt;/code&gt; called &lt;code&gt;commiter.scm&lt;/code&gt;, which practically writes all the boilerplate for you.
&lt;/p&gt;

&lt;p&gt;
However, it's not really the tedium that bothers me. If ChangeLog were really just a very rigid and verbose system where each possibility is set in stone and obvious from a glance, I probably wouldn't mention it or, hell, maybe even would have put it in as a positive.
&lt;/p&gt;

&lt;p&gt;
But that isn't the case, because where these templates cannot help is the nuanced parts. It is not entirely obvious how involved you need to make your commit messages and, even from my &lt;b&gt;very&lt;/b&gt; limited experience, it differs greatly between maintainers who accepts what.
&lt;/p&gt;

&lt;p&gt;
For instance, with Rust applications you are &lt;a href=&quot;https://guix.gnu.org/cookbook/en/html_node/Common-Workflow-for-Rust-Packaging.html&quot;&gt;expected&lt;/a&gt; to import all dependencies into the registry. So far so good. But how does one reflect this in their commits?
&lt;/p&gt;

&lt;p&gt;
For one, you must include your note about the crate import in the same commit as the one that introduces your package. This goes against the usual convention where each change gets their own commit, but (while I don't remember it mentioned in the guide) it's obvious enough to figure out based on a quick &lt;code&gt;git log&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
What's far less obvious, however, is how to phrase this import. To keep everyone in the loop, an entry in a ChangeLog-formatted commit message looks like the following:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;An asterisk to denote a new entry,&lt;/li&gt;
&lt;li&gt;The name of the file that was edited,&lt;/li&gt;
&lt;li&gt;Then the name of the variable / function that was edited in parentheses,&lt;/li&gt;
&lt;li&gt;Then, only if what you were editing is a struct with multiple fields, the name of the field in square brackets,&lt;/li&gt;
&lt;li&gt;Then, if said field has its own sections, the name of the section in curly brackets,&lt;/li&gt;
&lt;li&gt;Finally, after a set of colons, you add a short textual description of what you've done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
For example:
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;* foo/bar/baz.scm (example-struct)[section]{subsection}: Frobnicated some walruses.&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;
Now that we're on the same page, let's try to construct a line like this for our Rust imports. The start of the line is easy: &lt;code&gt;* gnu/packages/rust-crates.scm&lt;/code&gt;. But what comes right afterwards and especially in the description field is nontrivial and wildly varies between authors.
&lt;/p&gt;

&lt;p&gt;
From what I can see, there are two bigger schools of thought:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-change-log&quot;&gt;* gnu/packages/rust-apps.scm ($PACKAGE): Init at $VERSION.
* gnu/packages/rust-crates.scm (lookup-cargo-inputs): Added imports.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This variant only records the essentials. &amp;quot;We're handling $PACKAGE, which is a Rust application&amp;quot; and &amp;quot;We've imported a given number of crates&amp;quot;. The pros of this is that it's extremely short and simple, and it conveys intent without going into the details. The con is that it doesn't mention anything about which crates exactly were imported.
&lt;/p&gt;

&lt;p&gt;
And that is exactly what the second variant focuses on:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-change-log&quot;&gt;* gnu/packages/rust-apps.scm ($PACKAGE): Init at $VERSION.
* gnu/packages/rust-crates.scm ($CRATE1, $CRATE2, $CRATE3, ...): New variables.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This is the polar opposite of the first version. Very specific, clearly spells out what crates were imported into the registry and at what version, and fits more into the usual commit message schema.
&lt;/p&gt;

&lt;p&gt;
On the other hand, it occasionally produces monsters like this:
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-02-24-guix_one_month_later/cargo_imports.avif&quot; alt=&quot;cargo_imports.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 1: &lt;/span&gt;This goes on for four more pages, by the way.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
In my personal and, indeed, just as subjective opinion, Variant 1 is the clear winner. The point of a commit message is to convey intent or, as the ChangeLog manual puts it:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;&lt;a href=&quot;https://www.gnu.org/prep/standards/html_node/Change-Log-Concepts.html&quot;&gt;ChangeLog Concepts&lt;/a&gt;:&lt;/b&gt; People can see the current version; they don���t need the change log to tell them what is in it. What they want from a change log is a clear explanation of how the earlier version differed.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Yes, spelling out the whole list is &amp;quot;clear&amp;quot; in a certain sense, but since these crates were imported by a script and (beyond a couple of cases) aren't really touched by human hands, I believe the only relevant thing worth recording is that the importing process happened.
&lt;/p&gt;

&lt;p&gt;
But, while I have laser-focused on a single pain area, it's not the only place where disagreements like this happen. I've been given recommendations to rephrase several of my commit messages and it's not that I'm lazy to do this (in fact I've followed this advice most of the time), I just feel like the system is both rigid enough not to be very pleasant to use, but also loose to be truly automated.
&lt;/p&gt;

&lt;p&gt;
Again, Guix is a very flexible system, so maybe this &amp;quot;looseness&amp;quot; is actually a sheep in wolf's clothing. Better this than constant overrides in case the rigid system doesn't quite cover all the needs of maintainers.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-system-ups-and-downs&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;system-ups-and-downs&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;3.&lt;/span&gt; System ups and downs&lt;/h2&gt;
&lt;div id=&quot;text-3&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
While the previous section was about my experience with packaging stuff, this will be about using others' packages.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-stability-of-my-config&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;stability-of-my-config&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.1.&lt;/span&gt; Stability of my config&lt;/h3&gt;
&lt;div id=&quot;text-3-1&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
One thing I found interesting is how stable my config files ended up being while using Guix. Beyond the occasional addition to my home configuration, I found that I didn't have to touch my system config file at all.
&lt;/p&gt;

&lt;p&gt;
While this is obviously partly because I already experimented a bunch on NixOS before finding the right subset of applications that I need to be productive, I also feel like Guix is generally the less fiddly system of the two (in this particular aspect).
&lt;/p&gt;

&lt;p&gt;
Even more notably, I haven't had to touch Shepherd, Guix System's service manager once yet. Everything Just Works���, including changing over to PipeWire (my preferred audio server), which is both &lt;a href=&quot;https://guix.gnu.org/manual/1.5.0/en/html_node/Sound-Home-Services.html&quot;&gt;very well documented&lt;/a&gt; and only required a couple of lines in my home config:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 11: &lt;/span&gt;All you need to switch over.&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(service home-dbus-service-type)
(service home-pipewire-service-type)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
If everything was this simple in the Guix world, I'd have nothing to complain about. This is a gold standard.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-interfacing-with-my-phone&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;interfacing-with-my-phone&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.2.&lt;/span&gt; Interfacing with my phone&lt;/h3&gt;
&lt;div id=&quot;text-3-2&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
I wanted to move some files from my phone to my PC. On every other distro I've tried before, all this took was plugging it in and clicking on &amp;quot;Mount filesystem&amp;quot;. Or, perhaps if it was a more frugal distro, I had to issue &lt;code&gt;lsblk&lt;/code&gt;, find the right disk drive, and manually &lt;code&gt;mount&lt;/code&gt; it at a folder of my liking.
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-02-24-guix_one_month_later/android.avif&quot; alt=&quot;android.avif&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
With Guix System, I was unable to do this. I plugged my phone in, KDE Plasma offered to mount it. I clicked on it and all I got was a really confusing error. Finally, after quite a bit of digging on Google, I found a topic on the &lt;a href=&quot;https://bbs.archlinux.org/viewtopic.php?id=235200&quot;&gt;Arch Linux forums&lt;/a&gt; where someone mentioned &lt;code&gt;kio-extras&lt;/code&gt; and &lt;code&gt;kio-fuse&lt;/code&gt;. By adding these to my profile, the phone was finally able to connect.
&lt;/p&gt;

&lt;p&gt;
This particular instance wasn't a huge ordeal, but it is an annoying paper-cut, that makes Guix System feel slightly less polished. I believe the existence of a &amp;quot;Guix Wiki&amp;quot; (in the spirit of the Gentoo and Arch wikis) would help a lot in situations like this.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.4&quot; href=&quot;#fn.4&quot; class=&quot;footref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-blunderbird&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;blunderbird&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.3.&lt;/span&gt; Blunderbird&lt;/h3&gt;
&lt;div id=&quot;text-3-3&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
I generally use Thunderbird (or, since Guix doesn't provide Mozilla-branded binaries, &lt;a href=&quot;https://directory.fsf.org/wiki/Icedove&quot;&gt;Icedove&lt;/a&gt;) to handle my mail. It's a far more pleasant process in my opinion than constantly having a tab open in your browser.
&lt;/p&gt;

&lt;p&gt;
Well, as it turns out an update recently broke Icedove, because it implicitly relies on the other Mozilla-packages being the same version. Because of this, none of the build servers were able to build the package. This ultimately resulted in me being unable to update my system (without removing Icedove), because the moment I tried, Guix would notice that there is no pre-built package and began building it on my PC.
&lt;/p&gt;

&lt;p&gt;
This would be fine in most other cases. I do have a 12 core Ryzen CPU and 32 GBs of RAM,&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.5&quot; href=&quot;#fn.5&quot; class=&quot;footref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; so it's not like I don't have the hardware usually. However, in this particular instance, even such mighty specs failed to cope with all of Icedove's dependencies (I believe it's &lt;code&gt;webkit-gtk&lt;/code&gt; that fails specifically, but I'm not 100% sure), so after my PC crashed twice trying to update, I decided to try to pursue different avenues.
&lt;/p&gt;

&lt;p&gt;
My first attempt was using GNOME's Evolution mail client. No luck. No matter what I tried, it couldn't connect GMail using OAuth2 and all the errors I've got were very cryptic.
&lt;/p&gt;

&lt;p&gt;
Fine, whatever, I use KDE Plasma anyway, so why not give KMail a shot? I installed KMail and…
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-02-24-guix_one_month_later/kmail.avif&quot; alt=&quot;kmail.avif&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Well, that's annoying. Neither the link, nor the button worked. I tried looking up what's going on and was led to believe the issue is caused by another dependency named &lt;code&gt;akonadi&lt;/code&gt; missing. It's packaged in Guix, so I added it to my profile, reconfigured the environment, restarted KMail and… Nothing. Still the same error.
&lt;/p&gt;

&lt;p&gt;
A bit more investigation later, I found that running &lt;code&gt;akonadictl status&lt;/code&gt; should give me a bit more insight:
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-02-24-guix_one_month_later/akonadi.avif&quot; alt=&quot;akonadi.avif&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
So it's unable to connect to MariaDB (which I've also added to my profile off-screen). At this point I was kind of stuck and abandoned this experiment.
&lt;/p&gt;

&lt;p&gt;
I don't know, maybe there was an easy solution and I was merely a step away from making KMail work, but manually pulling in 5-6 dependencies for an application on a declarative distro is kind of above my tolerance. I get that not everything can work out of the box, but this style of debugging fits Slackware&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.6&quot; href=&quot;#fn.6&quot; class=&quot;footref&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; a lot more than it does a modern Linux distro.
&lt;/p&gt;

&lt;p&gt;
Well, with the obvious alternatives ruled out, I was left with figuring out what to do with the Icedove situation. At the time I started writing this article, the fix was &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/6706&quot;&gt;yet unmerged&lt;/a&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.7&quot; href=&quot;#fn.7&quot; class=&quot;footref&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; so there were only a couple of options someone who uses the package could take:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;&lt;b&gt;Not update:&lt;/b&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Pros: Requires no actions.&lt;/li&gt;
&lt;li&gt;Cons: No upgrades until the situation is resolved.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Remove &lt;code&gt;icedove&lt;/code&gt; from your profile:&lt;/b&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Pros: Very simple to do. You get other upgrades for everything else.&lt;/li&gt;
&lt;li&gt;Cons: You have to use another method to manage your mail. As seen above, if you don't want to use the browser route, this isn't necessarily an easy process.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Use an &lt;a href=&quot;https://guix.gnu.org/manual/1.5.0/en/html_node/Inferiors.html&quot;&gt;Inferior&lt;/a&gt;:&lt;/b&gt; (a.k.a. instruct Guix to pull in an older channel's package)
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Pros: You still have Icedove (even if it's an older version) &lt;i&gt;and&lt;/i&gt; upgrades for everything else.&lt;/li&gt;
&lt;li&gt;Cons: Requires tracking down the last commit that still worked, setting up the inferior, and then eventually removing it once the upgrade is done.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Ultimately I chose option #3 and made an inferior for Icedove. In this instance, it was thankfully super easy to find the last good commit, because someone else &lt;a href=&quot;https://codeberg.org/guix/guix/issues/6714&quot;&gt;already did&lt;/a&gt; the dirty work. Despite the warning in the manual, it didn't even take particularly long to compute the new Guix derivation.
&lt;/p&gt;

&lt;p&gt;
On one hand, I suppose it's another praise towards Guix's flexibility to be able to &amp;quot;patch&amp;quot; the package lookup with an arbitrary older (or even completely separate) channel. Doing so on a traditional, imperative package manager would be a giant pain.
&lt;/p&gt;

&lt;p&gt;
On the other hand, it's an unfortunate situation to see a mainstream application breaking people's systems and no urgent action being taken for nearly a week, when a PR is already ready and waiting.
&lt;/p&gt;

&lt;p&gt;
This is, of course, the blessing and curse of a volunteer project. Just as you can jump into any task that you fancy, nobody is obligated to check out your work, which can (in unfortunate cases, such as this) cause disruptions.
&lt;/p&gt;

&lt;p&gt;
For some more positive news, this seems to be going to be at least partly addressed soon as Guix is &lt;a href=&quot;https://codeberg.org/guix-foundation/website/raw/branch/main/downloads/guix-foundation-fundraising-future-2026.pdf&quot;&gt;considering&lt;/a&gt; a grant system, which would financially support certain developers working in key areas.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-nvidia-drivers-and-non-lts-kernels&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;nvidia-drivers-and-non-lts-kernels&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.4.&lt;/span&gt; NVIDIA drivers and non-LTS kernels&lt;/h3&gt;
&lt;div id=&quot;text-3-4&quot; class=&quot;outline-text-3&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Massive disclaimer here:&lt;/b&gt; This is a Nonguix &amp;quot;issue&amp;quot;. And in-fact it's not even an issue, it's more just a case of &amp;quot;things could be explained much better&amp;quot;. Still, it was something that caused me quite a bit of pain and if I can spare only one other person from experiencing it, it was already a win in my book.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
After one of the updates, my system started being unable to properly start up my desktop environment. I was able to get to GDM, enter my login info, press login, and then it'd either hang there or I'd get my windows stuck in the top left corner, without any of the usual Plasma UX decorations.
&lt;/p&gt;

&lt;p&gt;
Thankfully, as Guix is a generation-based declarative distro, I was able to roll back to an older version and have a working PC, but I found it really annoying that no matter how much I upgraded my system, it never quite seemed to help.
&lt;/p&gt;

&lt;p&gt;
I was quite close to giving up and going back to Nix or some other distro, when I finally found a lead. By reading the &lt;code&gt;dmesg&lt;/code&gt; logs, I found out that the problem was the kernel module not finding certain functions:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 12: &lt;/span&gt;This is not my exact kernel logs (&lt;a href=&quot;https://reddit.com/r/Gentoo/comments/1edo01u/bootup_error_lsmod_could_not_insert_nvidia/&quot;&gt;source&lt;/a&gt;), but I saw the same error messages.&lt;/label&gt;&lt;pre class=&quot;src src-nil&quot;&gt;[   95.122493] nvidia: loading out-of-tree module taints kernel.
[   95.122503] nvidia: module license 'NVIDIA' taints kernel.
[   95.122507] nvidia: module verification failed: signature and/or required key missing - tainting kernel
[   95.122508] nvidia: module license taints kernel.
[   95.595636] nvidia-nvlink: Nvlink Core is being initialized, major device number 508
[   95.596880] nvidia 0000:01:00.0: enabling device (0406 -&amp;gt; 0407)
[  101.074782] nvidia_drm: Unknown symbol nvKmsKapiGetFunctionsTable (err -2)
[12407.716914] nvidia_drm: Unknown symbol nvKmsKapiGetFunctionsTable (err -2)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
While this didn't quite solve the issue yet, it gave me a vital pointer, leading back to the &lt;a href=&quot;https://gitlab.com/nonguix/nonguix#:~:text=%2E%2E%2E%29-,NVIDIA%20graphics%20card,-NVIDIA%20support%20in&quot;&gt;Nonguix README&lt;/a&gt;. I gave the NVIDIA section a closer look and lo, the culprit was found:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;nonguix-transformation-nvidia [#:driver nvda]
                              [#:open-source-kernel-module? #f]
                              [#:s0ix-power-management? #f]
                              [#:kernel-mode-setting? #t]
                              [#:configure-xorg? #f]

Return a procedure that transforms an operating system, setting up
DRIVER (default: nvda) for NVIDIA graphics card.

OPEN-SOURCE-KERNEL-MODULE? (default: #f) only supports Turing and later
architectures and is expected to work with 'linux-lts'.

S0IX-POWER-MANAGEMENT? (default: #f) improves suspend and hibernate on systems
with supported graphics cards.

KERNEL-MODE-SETTING? (default: #t) is required for Wayland and rootless Xorg
support.

CONFIGURE-XORG? (default: #f) is required for Xorg display managers.  Currently
this argument configures the one used by '%desktop-services', GDM or SDDM.

Use 'replace-mesa', for application setup out of the operating system
declaration.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Can you see it? If not, here's the line of note: &lt;i&gt;&amp;quot;and is expected to work with 'linux-lts'&amp;quot;.&lt;/i&gt; Yup, that's all the warning you get that &amp;quot;if you use the usual &lt;code&gt;linux&lt;/code&gt; package as your kernel, it can crash on you at any time.&amp;quot;
&lt;/p&gt;

&lt;p&gt;
Obviously, this doesn't mean that the Nonguix team hasn't done their due-diligence. After all, the solution is there. But I definitely think it'd be worth a stronger emphasis on the fact that you can and will break your install if you don't use the LTS kernel with a newer card / open-source driver.
&lt;/p&gt;

&lt;p&gt;
I'm also certain I'm not the only one who fell for this as there is already an &lt;a href=&quot;https://gitlab.com/nonguix/nonguix/-/issues/441&quot;&gt;issue&lt;/a&gt; on Nonguix' Gitlab, where the ticket opener's phrasing strongly implies they had to figure out the &lt;code&gt;linux-lts&lt;/code&gt; solution themselves as well.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-no-easy-way-of-seeing-what-is-updated&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;no-easy-way-of-seeing-what-is-updated&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.5.&lt;/span&gt; No easy way of seeing what is updated&lt;/h3&gt;
&lt;div id=&quot;text-3-5&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
One thing I sorely feel is missing (though Nix is no better in this regard) is the fact that there is no easy built in way to see what upgrading will do to your system.
&lt;/p&gt;

&lt;p&gt;
There are approximations, sure. You can either pass &lt;code&gt;--dry-run&lt;/code&gt; to Guix and get something along the lines of work items. There is also a new project by LiterateLisp's author Wilko, which allows you to &lt;a href=&quot;https://me.literatelisp.eu/hacklog-diffing-and-comparing-guix-derivations-using-breadth-first-search--jaccard.html&quot;&gt;diff two derivations&lt;/a&gt;, which is super cool and I hope one day it'll be upstreamed into Guix proper, but (unless I massively misunderstood things) it's a tool for after the fact. That is to say this only tells you what changed once the update is done.
&lt;/p&gt;

&lt;p&gt;
Now, of course, there is an argument to be made that, due to the way declarative distros work, you don't really have to care what's what version and it's not a huge deal to just roll back if you're not happy with the changes. But every imperative distro manages to give you a list of &amp;quot;here's what's gonna happen&amp;quot; and give you a choice if you consent to the upgrade or not, and I really miss it from here.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sleep-causes-crashes&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;sleep-causes-crashes&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.6.&lt;/span&gt; Sleep causes crashes&lt;/h3&gt;
&lt;div id=&quot;text-3-6&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
I don't know if it's me doing things wrong or the driver not being good enough or something completely different causing it, but I am unable to use the Sleep function of the PC. The moment I press it, the PC freezes and requires a hard restart.
&lt;/p&gt;

&lt;p&gt;
What this means is that, if I know I need to go away for some time, I either leave things on (which wastes a lot of electricity) or I turn my PC off (not a huge deal, but a bit more inconvenient than just continuing from where you've left off).
&lt;/p&gt;

&lt;p&gt;
It's not exactly a vital feature, but all previous distros I've used had this working out of the box, so Guix System struggling with it is really unfortunate.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-odd-freezes-and-crashes&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;odd-freezes-and-crashes&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.7.&lt;/span&gt; Odd freezes and crashes&lt;/h3&gt;
&lt;div id=&quot;text-3-7&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
I cannot effectively quantify this, but Guix System doesn't really feel stable in my experience. In this ~1 month of use, I've experienced at least 4-5 spontaneous freezes and choppy performance while not doing anything particularly strenuous on the PC, which does not inspire confidence when it comes to working uninterrupted.
&lt;/p&gt;

&lt;p&gt;
I had freezes during listening to Youtube, browsing files in Dolphin, writing text in Emacs. Usually these issues went away on their own after a while, but not only does it make me worry that something (physical or digital) will end up damaged, I also get completely thrown out of my flow state.
&lt;/p&gt;

&lt;p&gt;
I definitely can't rule it out that some (perhaps even all) of these were caused by the notoriously finicky NVIDIA drivers. But it is the unfortunate reality, that I cannot swap GPUs for the sake of a distro, no matter how much I like it and none of the other distros I've tried had produced instability this bad before.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-conclusion&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;4.&lt;/span&gt; Conclusion&lt;/h2&gt;
&lt;div id=&quot;text-4&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
On a conceptual level, I love Guix and Guix System. I much prefer writing Scheme over Nix's language and I really like that there is far lesser of a divide between the builders and the package definitions.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.8&quot; href=&quot;#fn.8&quot; class=&quot;footref&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;p&gt;
I also love the low barrier of entry when it comes to contributing to the ecosystem. I'd wager Guix might be one of the easiest distros to have packages accepted for, if you have a bit of Scheme experience and are willing to oblige the maintainers.
&lt;/p&gt;

&lt;p&gt;
But, and I'm sad to say this 'but', both still feel unpolished to me. Ever since I've grown accustomed to Linux in a way that no longer makes me want to distro-hop, my journey was always about finding a distro that maximises stability and package freshness, and settling for it.
&lt;/p&gt;

&lt;p&gt;
Guix System sadly neither fulfilled the stability (due to all the crashes), nor the freshness (due to many of the packages lagging far behind upstream) in the way I was hoping for and I am unlikely to stick with it much longer. I will likely still have Guix present on whichever distro I go back to, but for now I'm buying out of the GNU dream.
&lt;/p&gt;

&lt;p&gt;
Now, I don't want to sound entitled and talk down a project I'm otherwise very much aligned with. I recognise the gargantuan effort that went into getting things this far, especially considering that Guix isn't just aiming towards x86 and ARM, but also to be a host and accelerator for &lt;a href=&quot;https://guix.gnu.org/en/blog/2026/the-64-bit-hurd/&quot;&gt;HURD&lt;/a&gt;. I also recognise and respect that most of this effort was accomplished with no monetary gain expected and on what is considered (at least in corporate terms) a &lt;a href=&quot;https://guix.gnu.org/en/blog/2026/result-of-sustain-and-strengthen-fundraising/&quot;&gt;shoestring-budget&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Even more importantly, things aren't happening in a vacuum and, while Guix's current state didn't prove to be quite what I was looking for, it doesn't mean it's not going to continue developing. Not only are they pulling in fresh funds &lt;a href=&quot;https://codeberg.org/guix-foundation/website/raw/branch/main/downloads/guix-foundation-fundraising-future-2026.pdf&quot;&gt;to fund infrastructure and projects&lt;/a&gt;, there's also a lot of ideas floating around how to make things better.
&lt;/p&gt;

&lt;p&gt;
I will only bring one example, but it will be comprehensive: The &lt;a href=&quot;https://codeberg.org/guix/maintenance/src/branch/master/doc/guix-days-2026/shared-cryptpad-guix-days-2026.md&quot;&gt;Shared Cryptpad of Guix Days 2026&lt;/a&gt;. This is the loose log of a couple of brainstorming sessions of Guix veterans and core developers, which touches on a massive amount of potential ways to make Guix more approachable including:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;How to manage secrets declaratively (this is &lt;a href=&quot;https://github.com/Mic92/sops-nix&quot;&gt;not simple&lt;/a&gt; even on Nix),&lt;/li&gt;
&lt;li&gt;How to communicate between the teams better,&lt;/li&gt;
&lt;li&gt;How to introduce breaking changes in a way that won't cause pain for everyone,&lt;/li&gt;
&lt;li&gt;Bringing a new GC to Guile, that should speed everything up (especially multi-threaded code),&lt;/li&gt;
&lt;li&gt;What they could learn from Nix,&lt;/li&gt;
&lt;li&gt;How to make the substitution situation better,&lt;/li&gt;
&lt;li&gt;How to automate PRs,&lt;/li&gt;
&lt;li&gt;How to make package breaks rarer and how to respond to them quicker,&lt;/li&gt;
&lt;li&gt;And (this was the one I found most interesting) thoughts about breaking with the GNU hardline: The possibility of enabling installing firmware in the installer, breaking the self-imposed wall between Guix and Nonguix, and even vaguely alluding towards the option of dropping the GNU label as a whole, while still retaining their best qualities and the commitment to free software.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The list goes on. Obviously, all of these exist only as ideas and plans so far, but the important part is that the maintainers aren't sitting on their laurels and doing nothing. They're smart people trying their best to make Guix an even better experience than it already is.
&lt;/p&gt;

&lt;p&gt;
I wanted to end my post on a positive note, especially since much of my post has been anything but. Even though Guix System as it is today didn't quite live up to my expectations, I have no doubts that in a couple years time, if the project continues with this fervour and dedication, it will be a very serious contender. Perhaps still a niche one, because its commitments to Scheme and only free software severely limits its audience, but a well-polished and incredibly powerful system for those that can live within these bounds.
&lt;/p&gt;

&lt;p&gt;
In fact, I've read some comments of people who have been using Guix System for over a decade. You cannot get that amount of dedication, if you yourselves (i.e. the maintainers) aren't dedicated yourself, and Guix's clearly are. I wish them nothing but the best and it's not like I'll completely remove myself from the project's orbit, as I still have a couple packages I'd love to see merged and I'm still interested in the project's future developments.
&lt;/p&gt;

&lt;p&gt;
Thanks for reading!
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;footnotes&quot;&gt;
&lt;h2 class=&quot;footnotes&quot;&gt;Footnotes: &lt;/h2&gt;
&lt;div id=&quot;text-footnotes&quot;&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.1&quot; href=&quot;#fnr.1&quot; class=&quot;footnum&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
At work I happen to use &lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt;, which has an extension called &amp;quot;&lt;a href=&quot;https://django-reversion.readthedocs.io/en/stable/&quot;&gt;reversion&lt;/a&gt;&amp;quot;. It allows reverting objects in your database to a previous revision. Pretty clever name, but it burnt into my mind so much, I accidentally mixed up the two.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.2&quot; href=&quot;#fnr.2&quot; class=&quot;footnum&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
I immediately went gung-ho and put both &lt;code&gt;harec&lt;/code&gt; and &lt;code&gt;qbe&lt;/code&gt; (which doesn't even cause a dependency issue) into &lt;code&gt;hare-build-system&lt;/code&gt;.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
In hindsight, while I can justify my actions (my reasoning was that &lt;code&gt;harec&lt;/code&gt; and &lt;code&gt;qbe&lt;/code&gt; are used by the compiler, therefore it makes sense to have them present in the build system), it wasn't the right course of action.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
Especially because this issue only really came up with a single package. Just like with any other project a big blast radius has to make sense. In this case, it didn't.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.3&quot; href=&quot;#fnr.3&quot; class=&quot;footnum&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Obviously the best would be if the &lt;code&gt;guix lint&lt;/code&gt; script was able to identify and fix these, but let's be real, making a script to rewrite an arbitrary input to something that conforms to a given output is a fool's errand.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.4&quot; href=&quot;#fnr.4&quot; class=&quot;footnum&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
In the strictest sense there is already one or rather two. There is &lt;a href=&quot;https://libreplanet.org/wiki/Group:Guix&quot;&gt;Libreplanet&lt;/a&gt;, which has a Guix page, but it's pretty much only used by direct maintainers for storing information that doesn't directly belong in the Guix Manual. For the sake of completeness, there is also the &lt;a href=&quot;https://wiki.systemcrafters.net/guix/&quot;&gt;System Crafters&lt;/a&gt; Wiki, which at the time of writing hasn't seen any contributions in two years.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.5&quot; href=&quot;#fnr.5&quot; class=&quot;footnum&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Before you ask, my toilet isn't made from gold. I bought my sticks before the price increase for a third of what they cost now… I'm not rich, just lucky.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.6&quot; href=&quot;#fnr.6&quot; class=&quot;footnum&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
No disrespect towards Slackware or its users. It's a very cool distribution and the fact that it is still going strong is nothing short of impressive. But it's &amp;quot;do it yourself&amp;quot; approach is exactly the opposite of what I'm looking for in a declarative distribution. In an ideal world, your entire setup would be a couple files of Scheme code and you'd practically never have to &amp;quot;figure things out&amp;quot;.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.7&quot; href=&quot;#fnr.7&quot; class=&quot;footnum&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
The fix was ultimately merged after six days and I have since removed the inferior from my install.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.8&quot; href=&quot;#fnr.8&quot; class=&quot;footnum&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
In Nix, you're expected to write Bash snippets in your build stages with some rudimentary substitution of variables provided by the system. This works, but is (in my opinion) far less ergonomic or elegant than Guix's all or nothing approach to Scheme.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;/div&gt;</content></entry><entry><title>LLMs Are Taking the Joy Out of Our Lives</title><id>https://nemin.hu/llm.html</id><author><name>Nemin</name></author><updated>2026-02-11T16:50:00Z</updated><link href="https://nemin.hu/llm.html" rel="alternate" /><content type="html">&lt;div role=&quot;doc-toc&quot; id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div role=&quot;doc-toc&quot; id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#job&quot;&gt;1. Job&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#hype-driven-death-march&quot;&gt;2. Hype-driven death march&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#art-robbery&quot;&gt;3. Art-robbery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#post-truth-society&quot;&gt;4. Post-truth society&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#we-re-burning-our-planet-for-this&quot;&gt;5. We're burning our planet for… this?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#i-m-not-fully-innocent-but-at-least-i-care&quot;&gt;6. I'm not fully innocent, but at least I care&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
This website does not, will not, and has not willingly used AI for its creation. I write every word myself and rely on traditional methods to publish it to the internet.
&lt;/p&gt;

&lt;p&gt;
That means I correct my spelling using &lt;a href=&quot;https://github.com/hunspell/hunspell&quot;&gt;Hunspell&lt;/a&gt; in &lt;a href=&quot;https://www.gnu.org/software/emacs/&quot;&gt;Emacs&lt;/a&gt; with its convenient &lt;code&gt;flyspell-mode&lt;/code&gt; checker. I do not generate pictures for my articles, I either create them myself or take them from the Internet with proper credits.
&lt;/p&gt;

&lt;p&gt;
Obviously, I use tools like LSPs and linters gratuitously, but I write the code I present in my articles by hand. I don't even look at AI output.
&lt;/p&gt;

&lt;p&gt;
If I don't know something, I go on the internet and search. If that doesn't work, I go to a community and ask. You'd be surprised how kind and helpful people are, if you show them a modicum of politeness.
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Note:&lt;/b&gt; This is a largely unedited rant. You've been warned.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div id=&quot;outline-container-job&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;job&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;1.&lt;/span&gt; Job&lt;/h2&gt;
&lt;div id=&quot;text-1&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
&lt;b&gt;I don't like AI.&lt;/b&gt; Not because I feel like it's endangering my job—even though for many others, including people I hold dearly—it is a very real possibility. I am embedded nicely into my team and my manager is level-headed enough to understand you cannot pass off our job to a machine and expect things not to collapse eventually.
&lt;/p&gt;

&lt;p&gt;
I don't like it, because the code produced using LLMs are at best laughably bad tangles of over-complicated boilerplate and cookie-cutter repetition of code that could very easily be DRY-d. At worst they generate a simulacrum of what a person might write, causing your intuition (trained on how real people think and what real people write, and which would otherwise help you catch bugs and issues) to become unreliable.
&lt;/p&gt;

&lt;p&gt;
And with how much less effort it takes for an AI to spit out a thousand lines, than it takes a person to review said thousand lines, we have no chance of inspect code with nearly as much care and scrutiny as before.
&lt;/p&gt;

&lt;p&gt;
Projects, both professional and hobby-driven, are suffocating under vibe-code pushed onto them both by people meaning well and people intending to profit. Because of the aforementioned ease of generating more code and how convincingly AI can masquerade as a person, the ever-increasing load of dealing with the crap-deluge is added atop the already thankless workload of these contributors, wasting valuable hours and manpower. &lt;a href=&quot;https://github.com/curl/curl/pull/20312&quot;&gt;cURL&lt;/a&gt; may have been the first, but it sure as hell won't be the last.
&lt;/p&gt;

&lt;p&gt;
The solution? &lt;i&gt;Just use AI to filter out the AI!&lt;/i&gt; This is untenable. Models made specifically to suss out LLMs written text &lt;a href=&quot;https://www.scientificamerican.com/article/chatgpt-detector-catches-ai-generated-papers-with-unprecedented-accuracy/#:~:text=By%20contrast%2C%20the%20AI%20detector%20ZeroGPT%20identified%20AI%2Dwritten%20introductions%20with%20an%20accuracy%20of%20only%20about%2035%E2%80%9365%25%2C&quot;&gt;aren't at all reliable&lt;/a&gt;. With code you have even less to rely on, because professional code generally doesn't have &amp;quot;character&amp;quot;.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.1&quot; href=&quot;#fn.1&quot; class=&quot;footref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;p&gt;
So we're left with rudimentary tools, like:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&amp;quot;All PRs above X thousand lines are blanket banned&amp;quot;,&lt;/li&gt;
&lt;li&gt;&amp;quot;PRs committed by an LLM are blanket banned&amp;quot;,&lt;/li&gt;
&lt;li&gt;&amp;quot;PRs where the author, after a lengthy questioning, cannot answer questions in a satisfying way are banned&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
It's an awkward cat-and-mouse game of developers chasing down more and more convincing models and conniving people, while the reliability of code isn't improving at nearly the same rate.
&lt;/p&gt;

&lt;p&gt;
And this is just programming. In many companies CEOs and boards are using AI as an excuse to lay people off:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Oh, who needs a new generation to replace the old one? We'll just do it with the AI!
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
And when that old generation ages out and there's nobody to train new people, what will happen then, huh?
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Oh, but AI won't make everyone jobless, you'll just be able to do 20 people's job alone!
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
First, that's bullshit. Second, you can be grateful it's bullshit, because the job market is already terrible. Imagine if suddenly there were 20x less jobs. It'd be catastrophic.
&lt;/p&gt;

&lt;p&gt;
A friend of mine studied visual arts. After many years in university, he entered a job market, that no longer had a need for him. He wasn't a &amp;quot;pro&amp;quot; (despite being skilled), so he had no foot in the door. And he couldn't become a &amp;quot;pro&amp;quot;, because all the jobs he tried to apply to declined, saying they'll just use GenAI to make stuff! How do you build a portfolio that way?
&lt;/p&gt;

&lt;p&gt;
He had spent a couple years living off odd jobs and now is re-educating himself as a programmer. While I'm glad he found something else that also interests him, I think it's still tragic he wasn't able to get work doing his dream job. Potential greatness dying at the feet of &amp;quot;eh, good enough.&amp;quot;
&lt;/p&gt;

&lt;p&gt;
Or there is that funny story where a business owner didn't bother to hire customer support. Their AI &lt;a href=&quot;https://reddit.com/r/LegalAdviceUK/comments/1qxc7x9/an_ai_chatassist_created_and_offered_a_customer/&quot;&gt;offered a customer an 80% discount&lt;/a&gt;. In this instance, the owner was extremely lucky, because they weren't obligated to fulfil the order, but imagine if they were. We might intuitively think a chatbot's responses aren't legally binding, but why not? If companies think they can offload customer complaints to these bots, surely they are created to offer only company-sanctioned information.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-hype-driven-death-march&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;hype-driven-death-march&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;2.&lt;/span&gt; Hype-driven death march&lt;/h2&gt;
&lt;div id=&quot;text-2&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
&lt;b&gt;I don't like AI.&lt;/b&gt; Not because I'm a Luddite&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.2&quot; href=&quot;#fn.2&quot; class=&quot;footref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, I'm not. For most of my childhood and early adult years, the march of technology was something I beheld in awe.
&lt;/p&gt;

&lt;p&gt;
I was born right before smartphones as we know them today were a thing. I saw mobiles go from bricks to hand-held supercomputers. While this too has its own detriments (unchecked social media exposure has made the childhood experience much more stressful than it needs to be), it also brought with itself undeniable benefits.
&lt;/p&gt;

&lt;p&gt;
A minimal smartphone nowadays is affordable to just about anyone, who isn't completely destitute. It won't be fast, won't do most of the stuff a top-of-the-line model can, but it can give you access to a lot of things. Banking, applying to work, handling your communication (both synchronous and asynchronous), entertainment, even security through 2FA/MFA.
&lt;/p&gt;

&lt;p&gt;
Smartphones are indeed so convenient, the idea of life without them is almost unthinkable to many of us. Whether or not that's a good thing is arguable (and you can probably argue towards &amp;quot;it's not&amp;quot; easier), but what nobody can deny is that smartphones are an utter financial and cultural success.
&lt;/p&gt;

&lt;p&gt;
With AI, meanwhile, companies are still figuring out how to even make it profitable. You know, when it's actually AI and not just &lt;a href=&quot;https://en.wikipedia.org/wiki/Builder.ai&quot;&gt;An Indian&lt;/a&gt;. It's not particularly good at coding, you cannot trust important choices on it, because someone has to be responsible for them, it cannot really innovate, just repeat variations. Last year OpenAI &lt;a href=&quot;https://www.theregister.com/2025/10/29/microsoft_earnings_q1_26_openai_loss/&quot;&gt;lost $11.5 billion&lt;/a&gt;. Things are fine, trust us. And all that keeps this bubble of kinda-sorta-works-trust-us cycle alive is &lt;a href=&quot;https://en.wikipedia.org/wiki/AI_bubble#:~:text=money%20by%20mid%2D2027%2E%5B26%5D-,Circular%20investment,-%5Bedit%5D&quot;&gt;five or so companies&lt;/a&gt; slipping money into each others' pockets.
&lt;/p&gt;

&lt;p&gt;
When this pops, and I frankly see no possibility that it won't, everyone will suffer. Maybe it will be another 2008 even. My tech ETFs are 25% up since I bought them a year ago. That kind of growth is unnatural and I dread the day the debt catches up or the investors wisen up to the ploy. I'm sure many of them already have, but it's more profitable to keep up the charade.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-art-robbery&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;art-robbery&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;3.&lt;/span&gt; Art-robbery&lt;/h2&gt;
&lt;div id=&quot;text-3&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
&lt;b&gt;I don't like AI.&lt;/b&gt; Not because I'm good at art. I can barely draw stick figures. For people like me, Generative AI is supposedly a boon and the great equaliser that allows even us to materialise our dreams.
&lt;/p&gt;

&lt;p&gt;
Except all of these models were trained on artists who did not consent to their works being chewed up and regurgitated in a thousand different, yet eerily similar ways, losing all the emotion and originality, that these people poured into each and every piece.
&lt;/p&gt;

&lt;p&gt;
And then there's all the authors, whose books were scraped and fed into LLMs. That resulted in &lt;a href=&quot;https://www.bbc.com/news/articles/c5y4jpg922qo&quot;&gt;a lawsuit or two&lt;/a&gt;, yes, but the damage is already done. You cannot exactly &amp;quot;untrain&amp;quot; a model. You can only put safeguards and rules on it, that clever people will always be able to dodge.
&lt;/p&gt;

&lt;p&gt;
And even if you could selectively remove all this knowledge from them, who in their right mind would? In this rat race, only the smartest, most knowledgeable AI wins, everyone else loses big. Sans comprehensive governmental crackdowns, no company would willingly cripple their own chances. And governments won't crack down on it, because then other, less copyright-concerned countries' AIs will take the lead. That does not make the line go up.
&lt;/p&gt;

&lt;p&gt;
Art is an inherently sentient thing. I originally considered saying it is a &amp;quot;human thing&amp;quot;, but there are instances of animals making art too. The loftiest art-piece down to the raunchiest fetish-art is all made because someone wanted someone else to feel something. The actual art-piece may not appeal, but the sentiment is universally beautiful.
&lt;/p&gt;

&lt;p&gt;
So when you delegate this to a machine, that has no understanding of anything more than &amp;quot;what pixel usually goes next to what other pixels&amp;quot;, everything you make is hollow. It might be very technically impressive (though, it wasn't you who impressed, but the machine), but there is no intent behind it.
&lt;/p&gt;

&lt;p&gt;
I feel nothing, but disgust when I look at AI &amp;quot;art&amp;quot;. It's always the same. Shrimp-Jesus, hyper-sexualised big tiddy anime waifus, diabetes-inducing sugary-cute animals with eyes as big as fists, comically obese people falling through glass bridges, buff gigachads with glowing red eyes telling you that you're not sigma. I will not bring examples, I'm sure you've seen all of these already. Just writing this list down makes me exasperated.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-post-truth-society&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;post-truth-society&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;4.&lt;/span&gt; Post-truth society&lt;/h2&gt;
&lt;div id=&quot;text-4&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
&lt;b&gt;I don't like AI.&lt;/b&gt; Not because I'm not tired of Google giving terrible results or because I enjoy digging into ancient threads &lt;i&gt;that&lt;/i&gt; much. I don't like it, because you can never be quite sure if what you're reading is true or not without putting in your own research and that the LLM will go out of its way to convince you to trust it.
&lt;/p&gt;

&lt;p&gt;
I can begrudgingly accept that chatbots can be a good springboard, if you have no idea how to start researching something. By demanding sources and justifications, you can get by, perhaps even achieve more than if you were left to use Google alone.
&lt;/p&gt;

&lt;p&gt;
However, many people think it's a miracle-machine and trust its output blindly, leading to at best embarrassment, at worst death. AI struggles with the 'r'-s in &amp;quot;strawberry&amp;quot;. LLMs will swear functions that never existed are parts of API-s. People, places, events, just about anything can be hallucinated. Chatbots melt into sobbing messes, if you &lt;a href=&quot;https://futurism.com/chatgpt-haywire-seahorse-emoji&quot;&gt;ask about seahorses&lt;/a&gt;. It &lt;a href=&quot;https://www.acpjournals.org/doi/10.7326/aimcc.2024.1260&quot;&gt;drove a man&lt;/a&gt; to drink Bromine and nearly end his own life.
&lt;/p&gt;

&lt;p&gt;
Hell, forget about &amp;quot;nearly&amp;quot;, there is now a &lt;a href=&quot;https://en.wikipedia.org/wiki/Deaths_linked_to_chatbots&quot;&gt;list on Wikipedia&lt;/a&gt; to count the amount of suicides and deaths attributed to LLMs. At the time of writing &lt;i&gt;fourteen&lt;/i&gt; people could be still alive. AI is not just a tool to kill people, but also to make the living more miserable.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.3&quot; href=&quot;#fn.3&quot; class=&quot;footref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; But trust us, just 50 billion more parameters and it'll be &lt;i&gt;perfect!&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
And even when things don't end so grimly, AI is still a tool for radicalisation. It has never been easier to make up legit sounding hogwash. Claude, give me a 200 page PDF about how my political opponent will bankrupt old people, raises taxes to unprecedented levels, and send off all young men to war. You think that's a ridiculous example, &lt;a href=&quot;https://telex.hu/english/2025/09/08/orban-announces-national-consultation-on-tiszas-supposed-leaked-tax-plans&quot;&gt;it happened&lt;/a&gt; in my country.
&lt;/p&gt;

&lt;p&gt;
Old and/or tech illiterate people not only have no idea if what they're looking at is legit or not, they might not even realize it's a question that needs to be asked. Ten-fifteen years ago something happening on video was either legit or very obviously fake. The few hoaxes that reached the mainstream were memorable exceptions. Today generating stuff that's not outrageously obvious and can easily fool many is an everyday occasion.
&lt;/p&gt;

&lt;p&gt;
Even I get occasionally fooled and that terrifies me, because I'm supposed to be tech savvy. Technology is my livelihood. I've been banging bits together since I was eight years old. And yet every once in a while my judgement lapses and I read a comment or watch a video, and I don't spot the obvious. The whiplash I feel when other comments inform me that I was engaging with slop without knowing is always stinging.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-we-re-burning-our-planet-for-this&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;we-re-burning-our-planet-for-this&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;5.&lt;/span&gt; We're burning our planet for… this?&lt;/h2&gt;
&lt;div id=&quot;text-5&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
&lt;b&gt;I don't like AI.&lt;/b&gt; Not because, it's the sole cause for climate change. It existed before AI. Arguably AI isn't even the biggest contributor, global shipping and the private flights of billionaires probably cause magnitudes more problems than Joe Schmoe asking the magic answering machine if he should invest into NFTs in 2026.
&lt;/p&gt;

&lt;p&gt;
But the fact that we're wasting perfectly good land and, far more importantly, perfectly good water to waste on dystopian giga-complexes for AI loads that haven't even materialised yet, while also shipping off nearly all future RAM and GPUs for these same hypothetical AI loads. Now that both baffles me and infuriates me.
&lt;/p&gt;

&lt;p&gt;
This is pure line-go-up mindset. Tomorrow be damned, today we have to shovel a few extra cents into $NVDA. Meanwhile, from June to August, we can barely go outside for a few hours, because temperatures are more often closer to 40°C than they are to 25°C. When I was a child (and I'm in my mid-twenties at the time of writing this, I'm not exactly old!) winters had snow for weeks. Summers were balmy, but never asphalt-melting hot.
&lt;/p&gt;

&lt;p&gt;
Is providing more means of generating zero-views slop really our first priority? Is that really what we need right now?
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-i-m-not-fully-innocent-but-at-least-i-care&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;i-m-not-fully-innocent-but-at-least-i-care&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;6.&lt;/span&gt; I'm not fully innocent, but at least I care&lt;/h2&gt;
&lt;div id=&quot;text-6&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
&lt;b&gt;I don't like AI.&lt;/b&gt; And yet, strictly at work, I do occasionally use AI-enabled code completion. Or sometimes make the AI generate me boilerplate for unit tests, that practically any templating system could also generate, just perhaps a bit more rigidly. And every couple of months I toy around with the freely available models online and run a few queries out of curiosity. And for that, it's &lt;i&gt;fine.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
I cannot hate people for wanting to use a tool, nor would I want to. It's a shiny toy, with some uses (albeit with caveats). My problem is with this dogged insistence on growth towards some vague faraway goal that seems to shift between AGI, replacing workers, enhancing workers, enhancing processes, replacing processes, etc. ad nauseam. It's gross, it does nothing but generate profits for a very tiny fraction of people out of thin air, while also causing pain on several scales. And it's just plain emotionally and mentally draining.
&lt;/p&gt;

&lt;p&gt;
And for that, as long as I'm able to, I'm keeping AI out of my hobby. Thanks for reading.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;footnotes&quot;&gt;
&lt;h2 class=&quot;footnotes&quot;&gt;Footnotes: &lt;/h2&gt;
&lt;div id=&quot;text-footnotes&quot;&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.1&quot; href=&quot;#fnr.1&quot; class=&quot;footnum&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Of course, this isn't universally true, there's definitely eccentric and unusual coding styles out there, that AI has no hope of replicating. However, such style is generally frowned upon in professional contexts, because you're not writing for yourself, rather for a team, that might one day contain people, who you'll never meet.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.2&quot; href=&quot;#fnr.2&quot; class=&quot;footnum&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
The actual &lt;a href=&quot;https://en.wikipedia.org/wiki/Luddite#Legacy:~:text=These%20attacks%20on%20machines%20did%20not%20imply%20any%20necessary%20hostility%20to%20machinery%20as%20such%3B%20machinery%20was%20just%20a%20conveniently%20exposed%20target%20against%20which%20an%20attack%20could%20be%20made&quot;&gt;historical Luddites&lt;/a&gt; get a really bad rep. While their methods were destructive, it's hard to seriously blame them. It was never about &amp;quot;uhh, machines &lt;b&gt;bad!&lt;/b&gt;&amp;quot; More &amp;quot;this new technology is rapidly causing mass unemployment and that's endangering tons of livelihoods.&amp;quot;
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
Considering how similar this is to today's situation (though one may wonder if AI truly brings as much benefit to society as the mechanic looms did), maybe I am a bit of a Luddite. At least in spirit, since I cannot exactly bring a hammer against a program.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.3&quot; href=&quot;#fnr.3&quot; class=&quot;footnum&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Yes, this is intentionally mimicking the usual AI slop marker of &amp;quot;It's not just X, it's Y&amp;quot;. If you're gonna think this whole text is AI generated too just because of this, I'm going to send you a very mean stare.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;/div&gt;</content></entry><entry><title>Adventures in Guix Packaging</title><id>https://nemin.hu/guix-packaging.html</id><author><name>Nemin</name></author><updated>2026-02-01T15:58:00Z</updated><link href="https://nemin.hu/guix-packaging.html" rel="alternate" /><content type="html">&lt;div role=&quot;doc-toc&quot; id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div role=&quot;doc-toc&quot; id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;1. Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#baby-steps&quot;&gt;2. Baby steps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#enter-wezterm&quot;&gt;3. Enter WezTerm&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#a-mediocre-first-attempt&quot;&gt;3.1. A mediocre first attempt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#a-brief-second-attempt-at-packaging-locally&quot;&gt;3.2. A brief second attempt at packaging locally&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#third-time-s-the-charm&quot;&gt;4. Third time's the charm&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#setting-things-up&quot;&gt;4.1. Setting things up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#importing-cargo-crates&quot;&gt;4.2. Importing Cargo crates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#patching-cargo-toml&quot;&gt;4.3. Patching Cargo.toml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#we-have-liftoff-almost&quot;&gt;4.4. We have liftoff… Almost&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#gpu-maladies&quot;&gt;4.5. GPU maladies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#finding-fonts&quot;&gt;4.6. Finding fonts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#it-s-vulkan-not-vulkan-t&quot;&gt;4.7. It's Vulkan, not Vulkan't&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#a-short-aside-committing-is-hard&quot;&gt;4.8. A short aside: Committing is hard!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#and-yet-some-stuff-is-still-better-left-to-the-professionals&quot;&gt;5. And yet, some stuff is still better left to the Professionals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;6. Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-introduction&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;introduction&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;1.&lt;/span&gt; Introduction&lt;/h2&gt;
&lt;div id=&quot;text-1&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
Having &lt;a href=&quot;./guix.html&quot;&gt;freshly jumped&lt;/a&gt; into Guix System, it didn't take long for me to want to attempt packaging something for the distro.
&lt;/p&gt;

&lt;p&gt;
Unlike the usual, &amp;quot;imperative&amp;quot; distributions, whose build scripts mostly interact with your system directly, Nix and Guix &amp;quot;recipes&amp;quot; provide a set of instructions for the build environment how it needs to compile your package's contents &amp;quot;from zero&amp;quot; and then where to place each of the resulting files.
&lt;/p&gt;

&lt;p&gt;
My first choice was &lt;a href=&quot;https://github.com/ValveSoftware/gamescope/&quot;&gt;Gamescope&lt;/a&gt;, Valve's home-brewed compositor that enabled HDR gaming under Linux.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.1&quot; href=&quot;#fn.1&quot; class=&quot;footref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; However, I quickly realized I bit bigger than I could chew. It took me less than an hour of trying to run into a wall that was (at least at that time) seemingly completely insurmountable.
&lt;/p&gt;

&lt;p&gt;
The errors I received were somewhat vague, the system couldn't find the necessary dependencies, and I really wasn't sure what I was doing. In hindsight, it really wasn't the right approach and had I went about this project with more planning, it may have gone much better. Luckily dropping this thread allowed me to pick up a similarly interesting, but much more fruitful project, which I'd like to showcase to you in this article.
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;TL;DR:&lt;/b&gt; I spent about a week packaging &lt;a href=&quot;https://github.com/wezterm/wezterm&quot;&gt;WezTerm&lt;/a&gt; and learning the ropes of being a Guix contributor along the way.
&lt;/p&gt;

&lt;p&gt;
During the packaging process I stumble many times, only to stand back up and figure out a solution. I also explain some of my complaints about the peculiarities of the process, but also provide plenty of praise about of how much the system tries to enable you to do your job. Finally, I also touch on how positive the experience of the code review was.
&lt;/p&gt;

&lt;p&gt;
If you just want to use WezTerm, do a &lt;code&gt;guix pull&lt;/code&gt; and &lt;code&gt;guix install wezterm&lt;/code&gt;.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-baby-steps&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;baby-steps&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;2.&lt;/span&gt; Baby steps&lt;/h2&gt;
&lt;div id=&quot;text-2&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
Being somewhat disappointed in how quickly I failed with my initial attempt, I decided to instead start small and work my way up. To do this, I picked one of Gamescope's dependencies, &lt;a href=&quot;https://github.com/ValveSoftware/openvr&quot;&gt;OpenVR&lt;/a&gt;, and &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/5962&quot;&gt;updated its package&lt;/a&gt; to its newest version.
&lt;/p&gt;

&lt;p&gt;
This might sound impressive without context, but in reality it was little more than me just replacing a hash and a version number in a package definition someone else already wrote:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 1: &lt;/span&gt;My massive changes.&lt;/label&gt;&lt;pre class=&quot;src src-diff&quot;&gt;&lt;span class=&quot;org-diff-context&quot;&gt;  diff --git a/gnu/packages/game-development.scm b/gnu/packages/game-development.scm
&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;index 56f147f956..5cecbf6963 100644
--- &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;a/gnu/packages/game-development.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
+++ &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;b/gnu/packages/game-development.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -3294,7 +3294,7 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public instead&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt; (define-public openvr
   (package
     (name &amp;quot;openvr&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-removed&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;    (version &amp;quot;&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;&lt;span class=&quot;org-diff-refine-removed&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;&lt;span class=&quot;org-diff-refine-removed&quot;&gt;26&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;&lt;span class=&quot;org-diff-refine-removed&quot;&gt;7&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;    (version &amp;quot;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;12&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;14&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;     (home-page &amp;quot;https://github.com/ValveSoftware/openvr/&amp;quot;)
     (source
      (origin
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -3304,7 +3304,7 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public openvr&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;              (commit (string-append &amp;quot;v&amp;quot; version))))
        (file-name (git-file-name name version))
        (sha256
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-removed&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;        (base32 &amp;quot;&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;&lt;span class=&quot;org-diff-refine-removed&quot;&gt;09rvrja3pz6ggs41ra71p4dwjl4n&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;&lt;span class=&quot;org-diff-refine-removed&quot;&gt;rpqrqw&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;&lt;span class=&quot;org-diff-refine-removed&quot;&gt;jiy92xl33hhxbsmx&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;&amp;quot;))))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;        (base32 &amp;quot;0&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;i85awq7w669j0x091chma&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;rcx1zqwn1j4v0d42bcjcvhqa6iv0v&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&amp;quot;))))
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;     (build-system cmake-build-system)
     (arguments
&lt;/span&gt;      ;; No tests.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Even though the changes I made are trivial, to get here, I had to set up the development environment, I had to set up a fork of the repository, learn about the project's committing rules, how opening PR-s for Guix works, and how I could test my changes locally. (I'm only making a tally here, all of these will be elaborated on in more detail in the rest of the article.)
&lt;/p&gt;

&lt;p&gt;
I'd really encourage anyone interested in how the packaging process works to seek out similar low hanging fruits to get down the basics in an environment where you have basically no chance of failing.
&lt;/p&gt;

&lt;p&gt;
You walk away with knowledge and a basis to build more involved stuff and in turn some members of the community might just get an update to a package they've been waiting for or someone might get inspired to add yet another package that they couldn't until now.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-enter-wezterm&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;enter-wezterm&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;3.&lt;/span&gt; Enter WezTerm&lt;/h2&gt;
&lt;div id=&quot;text-3&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
&lt;a href=&quot;https://wezterm.org/&quot;&gt;WezTerm&lt;/a&gt; is one of the many GPU-accelerated terminal emulators out there, distinguishing itself with a fairly broad Lua-based configuration API and having tabs as a built-in feature among other things.
&lt;/p&gt;

&lt;p&gt;
I admit, I haven't really used or interacted much with it previously. If I remember correctly, I tried it years ago before quickly moving on to other applications for my terminal needs, which begs the question: &lt;i&gt;Why even bother to spend quite a few days on packaging it?&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
Well, I've basically been nerd-sniped into it. While I was still figuring out my own woes with Guix System's installation, I was frequently reading r/Guix in hopes of getting a bit further. And it just so happened that one of the posts on the front page at the time was &lt;a href=&quot;https://reddit.com/r/GUIX/comments/1qni0hn/wezterm/&quot;&gt;a plea&lt;/a&gt; from a prospective Guix user for a native WezTerm package for Guix. What I quickly discovered is that there was none. I couldn't even really find discussions about it. This felt like a &lt;i&gt;call.&lt;/i&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Note from the future:&lt;/b&gt; Along the post, you'll occasionally see little boxes like this one. While the main body of the article documents my thinking and justifications during development, in these I'll bring attention to parts where I've either did things in a not entirely idiomatic way or where there's a much better / easier solution.
&lt;/p&gt;

&lt;p&gt;
The point of this post is less &amp;quot;How to write a 100% correct package definition that will get immediately merged into Guix&amp;quot; and more &amp;quot;What sort of journey would someone face, who has &lt;i&gt;some&lt;/i&gt; declarative packaging and Scheme knowledge, but is still quite new to the whole topic&amp;quot;.
&lt;/p&gt;

&lt;p&gt;
While the final code is indubitably much better than what I started with, I believe there is more merit in showcasing my iterative process instead of simply going through the merged package, as it doesn't really give any insight into how one would go about packaging something from zero.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-a-mediocre-first-attempt&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;a-mediocre-first-attempt&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.1.&lt;/span&gt; A mediocre first attempt&lt;/h3&gt;
&lt;div id=&quot;text-3-1&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
To start out, I wanted to try to compile WezTerm in an ad-hoc environment to get an idea what it'd need. I cloned it using &lt;code&gt;git clone https://github.com/wezterm/wezterm&lt;/code&gt; and created a new shell environment using &lt;code&gt;guix shell rust cargo&lt;/code&gt;… Except this didn't actually work, as Guix immediately spit the following error: &lt;code&gt;guix shell: error: cargo: unknown package&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Oh, right. With a cursory read of the Rust package, it quickly turned out Cargo isn't a separate package, but rather an &lt;i&gt;output&lt;/i&gt; of Rust. Outputs in Guix are basically separate &amp;quot;sub-packages&amp;quot; of packages, that maintainers can use to group related artifacts together, without all of them having to be installed at the same time in case the user doesn't need them.
&lt;/p&gt;

&lt;p&gt;
For instance most packaged libraries include both dynamic variants under &lt;code&gt;out&lt;/code&gt; (which is the default output) and statically-linked ones under &lt;code&gt;static&lt;/code&gt;. With Rust, we have the following options (you can find these using &lt;code&gt;guix search&lt;/code&gt;):
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;name: rust
version: 1.85.1
outputs:
  + rust-src: [description missing]
  + tools: [description missing]
  + cargo: [description missing]
  + out: everything else
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
For our purposes, we need &lt;code&gt;out&lt;/code&gt;, which contains the Rust compiler &lt;code&gt;rustc&lt;/code&gt;, and we also need &lt;code&gt;cargo&lt;/code&gt;. To instruct the shell to load both, the command we actually need is &lt;code&gt;guix shell rust rust:cargo&lt;/code&gt;. We could have also explicitly used &lt;code&gt;rust:out&lt;/code&gt; for &lt;code&gt;rustc&lt;/code&gt;, but if we leave it out, the shell uses it by default anyway.
&lt;/p&gt;

&lt;p&gt;
Armed with the right toolchain, I issued a naive &lt;code&gt;cargo build&lt;/code&gt; and for a while things seemed to be in order, only to suddenly come to a crashing halt with the following error:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;error occurred in cc-rs: failed to find tool &amp;quot;cc&amp;quot;:
No such file or directory (os error 2)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
It might come as a surprise, but due to the expectation that most things will be handled using declarative configuration, Guix System doesn't set the &lt;code&gt;CC&lt;/code&gt; environment variable (nor &lt;code&gt;CXX&lt;/code&gt;, though we won't be using that in this project).
&lt;/p&gt;

&lt;p&gt;
In fact, there isn't even any C compiler toolchain installed by default. This causes any programs that expect Autoconf/Makefile-like conventions to not be able to compile stuff. We can really easily solve it by adding &lt;code&gt;gcc-toolchain&lt;/code&gt; to our shell and setting &lt;code&gt;CC&lt;/code&gt; to &lt;code&gt;gcc&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;guix shell rust rust:cargo gcc-toolchain
&lt;span class=&quot;org-builtin&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;CC&lt;/span&gt;=gcc
cargo build
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Moments later we experience a new error (which, just like before, I'll abridge by a lot, because it's several pages long):
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;Could not find openssl via pkg-config:
Could not run `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 pkg-config --libs --cflags openssl`
The pkg-config command could not be found.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The issue here is twofold, though both come from the same source. The compiler is looking for OpenSSL using &lt;code&gt;pkg-config&lt;/code&gt;, but our environment doesn't contain either. While we could do what we previously did and exit the shell, add &lt;code&gt;pkg-config&lt;/code&gt; and &lt;code&gt;openssl&lt;/code&gt;, go back, call Cargo, and fail again at the next missing dependency, there is a slightly better way.
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Note from the future:&lt;/b&gt; Look, I'll be honest, the first time I did this process, I was actually doing this completely &amp;quot;manually&amp;quot; as described above. That is to say, I tried compiling and when it eventually failed with an error about a missing dependency, I just added it to the list, rinse and repeat.
&lt;/p&gt;

&lt;p&gt;
It's a horribly inefficient method and I didn't want to drag this post down by emulating it.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Though WezTerm has no Guix package (we're working on it right now!), it does come with a &lt;a href=&quot;https://nixos-and-flakes.thiscute.world/nixos-with-flakes/introduction-to-flakes&quot;&gt;Nix flake&lt;/a&gt; and, though Nix and Guix have diverged quite a bit, it's still very helpful to see the list of packages spelled out for us by someone else:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nix&quot;&gt;&lt;span class=&quot;org-nix-attribute&quot;&gt;nativeBuildInputs&lt;/span&gt; =
  &lt;span class=&quot;org-nix-keyword&quot;&gt;with&lt;/span&gt; pkgs;
  [
    installShellFiles
    ncurses &lt;span class=&quot;org-comment&quot;&gt;# tic for terminfo
&lt;/span&gt;    pkg-config
    python3
  ]
  ++ lib.optional stdenv.isDarwin perl;

&lt;span class=&quot;org-nix-attribute&quot;&gt;buildInputs&lt;/span&gt; =
  &lt;span class=&quot;org-nix-keyword&quot;&gt;with&lt;/span&gt; pkgs;
  [
    fontconfig
    openssl
    zlib
  ]
  ++ lib.optionals stdenv.isLinux [
    libxkbcommon
    wayland

    xorg.libX11
    xorg.libxcb
    xorg.xcbutil
    xorg.xcbutilimage
    xorg.xcbutilkeysyms
    xorg.xcbutilwm &lt;span class=&quot;org-comment&quot;&gt;# contains xcb-ewmh among others
&lt;/span&gt;  ]
  ++ lib.optionals stdenv.isDarwin ([
    libiconv
  ]);
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Obviously we cannot use this 1:1 as the two systems have different packaging conventions, but we can at least extract a good first approximation of the dependency list:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;guix shell rust rust:cargo gcc-toolchain pkg-config openssl wayland libx11 &lt;span class=&quot;org-sh-escaped-newline&quot;&gt;\&lt;/span&gt;
     libxcb xcb-util libxkbcommon xcb-util-image freetype fontconfig
&lt;span class=&quot;org-builtin&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;CC&lt;/span&gt;=gcc
cargo build --bin wezterm
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And, guess what, the application builds. Victory! Or so we think until we actually try to run it using &lt;code&gt;cargo run --bin wezterm&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;ERROR  wezterm_gui::frontend &amp;gt; Failed to create window: with_egl_lib failed:
libEGL.so.1: libEGL.so.1: cannot open shared object file: No such file or directory,
libEGL.so: libEGL.so: cannot open shared object file: No such file or directory,
libEGL.so.1: libEGL.so.1: cannot open shared object file: No such file or directory,
libEGL.so: libEGL.so: cannot open shared object file: No such file or directory
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;a id=&quot;egl-first&quot;&gt;&lt;/a&gt;Bummer. WezTerm is a GPU-accelerated terminal emulator, so it (unsurprisingly) needs to communicate with our GPU. Under NVIDIA , this happens with the help of a library called &lt;code&gt;libEGL&lt;/code&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.2&quot; href=&quot;#fn.2&quot; class=&quot;footref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, which our binary cannot locate without some additional help. The reason for this is that Guix isn't using the standard &lt;a href=&quot;https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard&quot;&gt;Linux filesystem hierarchy&lt;/a&gt;, so &lt;code&gt;libEGL&lt;/code&gt; is nowhere to be found on the list of places the library loader checks by default.
&lt;/p&gt;

&lt;p&gt;
The &amp;quot;fix&amp;quot; I used to circumvent this is really ugly, but for a quick attempt it sufficed. Firstly, I needed to figure out where &lt;code&gt;libEGL.so&lt;/code&gt; even is. Thankfully all this took was a simple &lt;code&gt;guix locate libEGL.so&lt;/code&gt; (this command is case-sensitive, I learned it the hard way), which spits out a list such as this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;fhs-union-32@0.0     /gnu/store/wr6kq2ixxcnh4nidg64ivsns19k7ynnn-fhs-union-32-0.0/lib/libEGL.so
fhs-union-64@0.0     /gnu/store/9jn31ksjz3frkhwdk5f72s65lrn3gbs7-fhs-union-64-0.0/lib/libEGL.so
mesa@25.2.3          /gnu/store/4kp4rn5vnnaj57464k72wqpg45k45x56-nvda-580.12/lib/libEGL.so
nvda@580.12          /gnu/store/d7vln3cxysi04wf7p8nzwgwabjysznjd-nvda-580.12/lib/libEGL.so
libglvnd@1.7.0       /gnu/store/qhc0y0xzmdmh38bpr9yv2il8alrv5l4g-libglvnd-1.7.0/lib/libEGL.so
mesa@21.3.8          /gnu/store/7rn2s4ip6926sdn8h23qbdyhsr4lj4xk-mesa-21.3.8/lib/libEGL.so
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
As you can see, due to the way Guix works, multiple versions of the library can coexist without conflicting with each other. As I'm running an NVIDIA card, I picked the line for &lt;code&gt;nvda&lt;/code&gt;. Next, I had to set the special environment variable &lt;code&gt;LD_PRELOAD&lt;/code&gt; to this file's path and then call &lt;code&gt;wezterm&lt;/code&gt;. What this does is instruct the dynamic library loader to add the contents of the environment variable to its load path&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.3&quot; href=&quot;#fn.3&quot; class=&quot;footref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; and thus the library is finally visible to the application.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;&lt;span class=&quot;org-variable-name&quot;&gt;LD_PRELOAD&lt;/span&gt;=/gnu/store/d7vln3cxysi04wf7p8nzwgwabjysznjd-nvda-580.12/lib/libEGL.so &lt;span class=&quot;org-sh-escaped-newline&quot;&gt;\&lt;/span&gt;
    ./target/debug/wezterm
&lt;/pre&gt;
&lt;/div&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-02-01-guix_packaging/wezterm_manual.avif&quot; alt=&quot;wezterm_manual.avif&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;i&gt;Et voila,&lt;/i&gt; we have WezTerm running. Reddit thread &lt;a href=&quot;https://old.reddit.com/r/GUIX/comments/1qni0hn/wezterm/o2a3ebf/&quot;&gt;solved&lt;/a&gt;. Well, kind of. Our victory is a bit of a hollow one. While we did compel Cargo to build the terminal and it technically works, we have not made a package. Guix isn't aware of WezTerm and anyone else who wants to build the application has to follow these haphazard steps, including overriding a &amp;quot;potentially dangerous&amp;quot; environment variable.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-a-brief-second-attempt-at-packaging-locally&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;a-brief-second-attempt-at-packaging-locally&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.2.&lt;/span&gt; A brief second attempt at packaging locally&lt;/h3&gt;
&lt;div id=&quot;text-3-2&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
To turn our fickle experiment into a proper package, we need to create a build recipe, which we can later integrate into the &lt;a href=&quot;https://codeberg.org/guix/guix&quot;&gt;guix/guix&lt;/a&gt; Codeberg repository. The customary filename for such recipes in Guix is, perhaps unsurprisingly, &lt;code&gt;guix.scm&lt;/code&gt;. Inside this file you're expected to return a &lt;code&gt;(package ...)&lt;/code&gt; form as your final value, which Guix can then process and turn into a derivation&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.4&quot; href=&quot;#fn.4&quot; class=&quot;footref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; for you.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-scheme&quot;&gt;&lt;span class=&quot;linenr&quot;&gt; 1: &lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;define-module&lt;/span&gt; (&lt;span class=&quot;org-type&quot;&gt;wezterm&lt;/span&gt;))
&lt;span class=&quot;linenr&quot;&gt; 2: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt; 3: &lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;use-modules&lt;/span&gt; (guix git)
&lt;span class=&quot;linenr&quot;&gt; 4: &lt;/span&gt;             (guix git-download)
&lt;span class=&quot;linenr&quot;&gt; 5: &lt;/span&gt;             (guix packages)
&lt;span class=&quot;linenr&quot;&gt; 6: &lt;/span&gt;             ((guix licenses) &lt;span class=&quot;org-builtin&quot;&gt;#:prefix&lt;/span&gt; license:)
&lt;span class=&quot;linenr&quot;&gt; 7: &lt;/span&gt;             (guix build-system cargo)
&lt;span class=&quot;linenr&quot;&gt; 8: &lt;/span&gt;             (gnu packages)
&lt;span class=&quot;linenr&quot;&gt; 9: &lt;/span&gt;             (gnu packages commencement)
&lt;span class=&quot;linenr&quot;&gt;10: &lt;/span&gt;             (gnu packages tls)
&lt;span class=&quot;linenr&quot;&gt;11: &lt;/span&gt;             (gnu packages freedesktop)
&lt;span class=&quot;linenr&quot;&gt;12: &lt;/span&gt;             (gnu packages xorg)
&lt;span class=&quot;linenr&quot;&gt;13: &lt;/span&gt;             (gnu packages xdisorg)
&lt;span class=&quot;linenr&quot;&gt;14: &lt;/span&gt;             (gnu packages fontutils)
&lt;span class=&quot;linenr&quot;&gt;15: &lt;/span&gt;             (gnu packages autotools)
&lt;span class=&quot;linenr&quot;&gt;16: &lt;/span&gt;             (gnu packages guile)
&lt;span class=&quot;linenr&quot;&gt;17: &lt;/span&gt;             (gnu packages pkg-config)
&lt;span class=&quot;linenr&quot;&gt;18: &lt;/span&gt;             (gnu packages sdl)
&lt;span class=&quot;linenr&quot;&gt;19: &lt;/span&gt;             (gnu packages texinfo))
&lt;span class=&quot;linenr&quot;&gt;20: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;21: &lt;/span&gt;(package
&lt;span class=&quot;linenr&quot;&gt;22: &lt;/span&gt;  (name &lt;span class=&quot;org-string&quot;&gt;&amp;quot;wezterm&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;linenr&quot;&gt;23: &lt;/span&gt;  (version &lt;span class=&quot;org-string&quot;&gt;&amp;quot;05343b3&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;linenr&quot;&gt;24: &lt;/span&gt;  (home-page &lt;span class=&quot;org-string&quot;&gt;&amp;quot;https://wezterm.org/&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;linenr&quot;&gt;25: &lt;/span&gt;  (synopsis &lt;span class=&quot;org-string&quot;&gt;&amp;quot;TODO&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;linenr&quot;&gt;26: &lt;/span&gt;  (description &lt;span class=&quot;org-string&quot;&gt;&amp;quot;TODO&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;linenr&quot;&gt;27: &lt;/span&gt;  (license license:expat)
&lt;span class=&quot;linenr&quot;&gt;28: &lt;/span&gt;  (source (git-checkout (url &lt;span class=&quot;org-string&quot;&gt;&amp;quot;https://github.com/wezterm/wezterm&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;linenr&quot;&gt;29: &lt;/span&gt;                        (commit version)))
&lt;span class=&quot;linenr&quot;&gt;30: &lt;/span&gt;  (inputs (append (list gcc-toolchain pkg-config openssl wayland
&lt;span class=&quot;linenr&quot;&gt;31: &lt;/span&gt;                        libx11 libxcb xcb-util libxkbcommon xcb-util-image
&lt;span class=&quot;linenr&quot;&gt;32: &lt;/span&gt;                        freetype fontconfig)))
&lt;span class=&quot;linenr&quot;&gt;33: &lt;/span&gt;  (build-system cargo-build-system))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Let's go through our code, section by section. We begin by declaring a module for our package. This is not strictly necessary, but it's common practice with Guile code and (as you'll soon see) it won't really matter in the long run anyway. Next we import a bunch of utilities and packages. A common rule of thumb is that packages under &lt;code&gt;guix&lt;/code&gt; are utilities, while &lt;code&gt;gnu packages&lt;/code&gt; are packages.
&lt;/p&gt;

&lt;p&gt;
We import stuff like methods for importing from a Git repository, definitions of software licenses (more on that below), and Guix's Cargo-based builder preset. The observant might notice that we &lt;b&gt;don't&lt;/b&gt; pull in &lt;code&gt;rust&lt;/code&gt;, &lt;code&gt;rust:cargo&lt;/code&gt;, and &lt;code&gt;gcc-toolchain&lt;/code&gt;. All of these are handled by the preset.
&lt;/p&gt;

&lt;p&gt;
Next comes the main package definition. We declare some static metadata, such as the name of the package, its version number (I'm using the current commit of the repository, as WezTerm hasn't had a release since 2024), the project's website, and we leave the synopsis and description empty for now, as I'd rather first have the package working before wasting time on bookkeeping busywork.
&lt;/p&gt;

&lt;p&gt;
Finally we set the package's license. In Guix (and Nix too), licenses aren't simple text fields, so we can't simply say &lt;code&gt;&amp;quot;MIT&amp;quot;&lt;/code&gt; and move on with our lives. Rather they are objects of their own. This both allows recording any necessary to fully specify the license and also removes a lot of guesswork from the packagers' workflow such as:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&amp;quot;Do I specify 'MIT'? Or 'Expat'? Or 'MIT/Expat'?&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Is the field case sensitive?&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Is it 'GPL-3' or 'GPL3' or maybe 'GPLv3'?&amp;quot;&lt;/li&gt;
&lt;li&gt;And so on and so forth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
To access these license objects, we need to pull in the &lt;code&gt;(guix licenses)&lt;/code&gt; module and specify from it the exact variant we need. In our case this is &lt;code&gt;license:expat&lt;/code&gt;, the MIT license's other name. As you can see, in the imports section I specified &lt;code&gt;license:&lt;/code&gt; as the license module's prefix. This means all the identifiers exported from this module are automatically renamed to contain &lt;code&gt;license:&lt;/code&gt; in the front. This is done to prevent overshadowing unrelated names and prefixing licenses this way is an idiomatic practice in Guix.
&lt;/p&gt;

&lt;p&gt;
This is followed by the actually interesting stuff. First we declare the source of our package's code. This can be many things, an archive downloaded from the Internet, a Git repository like we see in this case, or even a local folder as seen in this &lt;a href=&quot;https://dthompson.us/posts/guix-for-development.html&quot;&gt;excellent tutorial&lt;/a&gt;. We declare our commit to be the same as what we set for our version, i.e. the Git commit with the (short) ID &lt;code&gt;05343b3&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Next we declare our &amp;quot;inputs&amp;quot;, these are all the packages that we either need to use to build our package or we need at runtime. Note the vagueness of the word &amp;quot;use&amp;quot; here: Inputs may not just be source code dependencies. For instance, as with the manual method, &lt;code&gt;pkg-config&lt;/code&gt; is used to find other sources' locations and won't actually be part of the final executable. But we might also include tools that generate files. Or we might request assets, like fonts. It's best to think of inputs as not just the ingredients, but the entire kitchen supply.
&lt;/p&gt;

&lt;p&gt;
After that comes the build system. Just like our source, this too can be &lt;a href=&quot;https://guix.gnu.org/manual/devel/en/html_node/Build-Systems.html&quot;&gt;many things&lt;/a&gt; and it is what primarily decides what our build process is going to look like. Since we're trying to make a Rust package, we use &lt;code&gt;cargo-build-system&lt;/code&gt;, which itself is based on the &lt;code&gt;gnu-build-system&lt;/code&gt; (which is a smarter codification of the good old &lt;code&gt;./configure&lt;/code&gt;, &lt;code&gt;make&lt;/code&gt;, &lt;code&gt;make install&lt;/code&gt; ritual), except with all the GNU-specific build tools replaced with calls to Cargo.
&lt;/p&gt;

&lt;p&gt;
Well, let's give it a shot. Surely, things cannot be much more complex than this.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 2: &lt;/span&gt;Crash and burn…&lt;/label&gt;&lt;pre class=&quot;src src-nil&quot;&gt;error: failed to get `finl_unicode` as a dependency of package `termwiz v0.24.0 (/tmp/guix-build-wezterm-05343b3.drv-0/source/termwiz)`

Caused by:
  failed to load source for dependency `finl_unicode`

Caused by:
  Unable to update https://github.com/wez/finl_unicode.git?branch=no_std

Caused by:
  can't checkout from 'https://github.com/wez/finl_unicode.git': you are in the offline mode (--offline)
error: in phase 'build': uncaught exception:
%exception #&amp;lt;&amp;amp;invoke-error program: &amp;quot;cargo&amp;quot; arguments: (&amp;quot;build&amp;quot; &amp;quot;--offline&amp;quot; &amp;quot;-j&amp;quot; &amp;quot;24&amp;quot; &amp;quot;--release&amp;quot; &amp;quot;-p&amp;quot; &amp;quot;wezterm-gui&amp;quot; &amp;quot;--features&amp;quot; &amp;quot;distro-defaults&amp;quot;) exit-status: 101 term-signal: #f stop-signal: #f&amp;gt; 
phase `build' failed after 0.0 seconds
command &amp;quot;cargo&amp;quot; &amp;quot;build&amp;quot; &amp;quot;--offline&amp;quot; &amp;quot;-j&amp;quot; &amp;quot;24&amp;quot; &amp;quot;--release&amp;quot; &amp;quot;-p&amp;quot; &amp;quot;wezterm-gui&amp;quot; &amp;quot;--features&amp;quot; &amp;quot;distro-defaults&amp;quot; failed with status 101
build process 18 exited with status 256
builder for `/gnu/store/nllax38xh9v0hcsc3g9vviw5mqnrj5x0-wezterm-05343b3.drv' failed with exit code 1
build of /gnu/store/nllax38xh9v0hcsc3g9vviw5mqnrj5x0-wezterm-05343b3.drv failed
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Things can't be as simple as we'd like, huh?
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-third-time-s-the-charm&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;third-time-s-the-charm&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;4.&lt;/span&gt; Third time's the charm&lt;/h2&gt;
&lt;div id=&quot;text-4&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
The main reason we failed is because Guix is very particular about the way it allows source code to be fetched&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.5&quot; href=&quot;#fn.5&quot; class=&quot;footref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; and, though Cargo has an ecosystem of its own, Guix wants to have the final say. If packages, such as the previously seen &lt;code&gt;finl_unicode&lt;/code&gt; aren't known by Guix, then even though Cargo could fetch it for us, the build system will refuse to proceed.
&lt;/p&gt;

&lt;p&gt;
&lt;a id=&quot;import-mention&quot;&gt;&lt;/a&gt;The good news is that Guix has built in features to allow mass-importing crates, so we don't have to manually go through every single of WezTerm's dozen crates. The bad news is that, for this to work, you need to be working inside Guix's primary repository or else you're unleashing an unreasonable amount of extra work on yourself. Since our end goal is to upstream the package definition anyway, it really is the right choice to just bite the bullet and do it properly.
&lt;/p&gt;

&lt;p&gt;
So, back to &lt;i&gt;tabula rasa&lt;/i&gt;, let's create a package inside the actual Guix package repository.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-setting-things-up&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;setting-things-up&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;4.1.&lt;/span&gt; Setting things up&lt;/h3&gt;
&lt;div id=&quot;text-4-1&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
I'm going to assume at least a bit of Git knowledge, as this post will be long enough as-is without starting from the very basics. Due to my previous tiny contribution with OpenVR, I already had a fork repository of &lt;a href=&quot;https://codeberg.org/guix/guix&quot;&gt;guix/guix&lt;/a&gt;, so that's one hurdle down. Next, we do the usual, update the &lt;code&gt;master&lt;/code&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.6&quot; href=&quot;#fn.6&quot; class=&quot;footref&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; branch, create a new branch, switch to it, then prepare our working environment:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;git pull
git switch --create add-wezterm
guix shell -D guix -CPWN
./bootstrap  &lt;span class=&quot;org-comment-delimiter&quot;&gt;# &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;runs autoreconf, creates configure
&lt;/span&gt;./configure  &lt;span class=&quot;org-comment-delimiter&quot;&gt;# &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;creates Makefile
&lt;/span&gt;make
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The first two &lt;code&gt;git&lt;/code&gt; commands are fairly trivial, but the third command is worth talking about a little. The interesting thing about Guix development is that it happens 90% inside Guix itself! Isn't that cool? By issuing &lt;code&gt;guix shell -D guix -CPWN&lt;/code&gt;, we are:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;-C&lt;/code&gt;: are transported into a container, a fully isolated environment, in which only the source tree is available,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-D guix&lt;/code&gt;: with the development inputs of the &lt;code&gt;guix&lt;/code&gt; pacakge included,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-P&lt;/code&gt;: with the profile initialized to the source tree's environment,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-W&lt;/code&gt;: with the &lt;code&gt;guix&lt;/code&gt; executable available,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-N&lt;/code&gt;: and finally we request network access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
But, before we can throw ourselves into writing our package with wild abandon, we have one more preliminary thing to take care. We need to set up our development environment by first running &lt;code&gt;./bootstrap&lt;/code&gt;, followed by &lt;code&gt;./configure&lt;/code&gt;. These two commands generate the necessary Makefiles and are a one time cost (unless you're nuking your entire repo checkout and recreating it from scratch, you don't really have to call these ever again). For more info about this part of the process, please see the &lt;a href=&quot;https://guix.gnu.org/manual/devel/en/html_node/Building-from-Git.html&quot;&gt;appropriate manual chapter&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Then, by issuing &lt;code&gt;make&lt;/code&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.7&quot; href=&quot;#fn.7&quot; class=&quot;footref&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;, the development environment does several things at once:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;It &lt;a href=&quot;https://www.gnu.org/software/guile/manual/html_node/Compilation.html&quot;&gt;compiles&lt;/a&gt; all existing Scheme files into &lt;code&gt;.go&lt;/code&gt; files, which aren't &lt;a href=&quot;https://go.dev/&quot;&gt;Golang&lt;/a&gt; source files, but rather Guile byte-code. This speeds up execution as instead of interpreting source code, Guile can work based on pre-chewed binary data,&lt;/li&gt;
&lt;li&gt;It compiles all documentation. To our purposes this isn't hugely relevant, but it's a one-time cost, so there's not much point in figuring out elaborate ways to avoid it,&lt;/li&gt;
&lt;li&gt;&lt;p&gt;
Finally, and most importantly, it places a new script into our workspace named &lt;code&gt;pre-inst-env&lt;/code&gt;. The name stands for &amp;quot;[run with] pre-installation environment&amp;quot; and it is the primary driver of our Code-&amp;gt;Compile-&amp;gt;Test loop.
&lt;/p&gt;

&lt;p&gt;
By prepending all our &lt;code&gt;guix&lt;/code&gt; invocations with &lt;code&gt;./pre-inst-env&lt;/code&gt;, we can instruct Guix to use the workspace package registry as its source and thus we can build and test our package without the horribly slow process of manually doing &lt;code&gt;guix pull&lt;/code&gt; on our work directory and &lt;code&gt;guix build &amp;lt;package&amp;gt;&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
What's even better is that this works even outside the container, so we can use all our usual tools while testing or build in the restricted environment to make sure we're not introducing any external dependencies.
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
After a couple minutes of wait, our workspace is finally ready for action. Now we can finally handle the actual Guix stuff. First order of business is figuring out where even our package should go. The Guix repository's folder structure is logical, but it is still occasionally non-trivial to figure out where some packages might be located.
&lt;/p&gt;

&lt;p&gt;
A good rule of thumb is that usually things go into general categories (e.g. &lt;code&gt;emulators.scm&lt;/code&gt;), but if a set of packages only make sense together (an example being &lt;code&gt;django.scm&lt;/code&gt; for all the Python &lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; stuff out there), it can go into a category, and thus, a new file of its own. For us, there are two possible candidates:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://codeberg.org/guix/guix/src/commit/69cc00def4f85724bb6698f2365f661e5a946691/gnu/packages/rust-apps.scm&quot;&gt;rust-apps&lt;/a&gt;, whose purpose I'm still not 100% sure about, since the only thing that connects the stuff inside it seems to be that it's all Rust-based applications,&lt;/li&gt;
&lt;li&gt;and &lt;a href=&quot;https://codeberg.org/guix/guix/src/commit/69cc00def4f85724bb6698f2365f661e5a946691/gnu/packages/terminals.scm&quot;&gt;terminals.scm&lt;/a&gt;, which is for terminal emulators. This is the one I ultimately went with, as it already had &lt;a href=&quot;https://github.com/alacritty/alacritty&quot;&gt;Alacritty&lt;/a&gt; inside, which is also a Rust-based terminal emulator, just like WezTerm.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
So, let's just plop our previous package definition (extended a little bit) into this file and give it a whirl.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-scheme&quot;&gt;(&lt;span class=&quot;org-keyword&quot;&gt;define-public&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;wezterm&lt;/span&gt;
  (package
    (name &lt;span class=&quot;org-string&quot;&gt;&amp;quot;wezterm&amp;quot;&lt;/span&gt;)
    (version &lt;span class=&quot;org-string&quot;&gt;&amp;quot;05343b387085842b434d267f91b6b0ec157e4331&amp;quot;&lt;/span&gt;)
    (source
     (origin
       (method git-fetch)
       (file-name &lt;span class=&quot;org-string&quot;&gt;&amp;quot;wezterm&amp;quot;&lt;/span&gt;)
       (uri (git-reference
              (url &lt;span class=&quot;org-string&quot;&gt;&amp;quot;https://github.com/wezterm/wezterm&amp;quot;&lt;/span&gt;)
              (recursive? #t)
              (commit version)))
       (sha256
        (base32 &lt;span class=&quot;org-string&quot;&gt;&amp;quot;0q3f1y3bx3g2k21yzp6wkws6kyxsmk4pscmvd8gqmjbbss8az9ap&amp;quot;&lt;/span&gt;))))
    (native-inputs (list pkg-config))
    (inputs (append (cargo-inputs 'wezterm)
                    (list openssl wayland libx11
                          libxcb xcb-util xcb-imdkit
                          libxkbcommon xcb-util-image
                          freetype fontconfig libssh2
                          libgit2 sqlite `(,zstd &lt;span class=&quot;org-string&quot;&gt;&amp;quot;lib&amp;quot;&lt;/span&gt;)
                          mesa)))
    (build-system cargo-build-system)
    (home-page &lt;span class=&quot;org-string&quot;&gt;&amp;quot;https://wezterm.org/&amp;quot;&lt;/span&gt;)
    (synopsis &lt;span class=&quot;org-string&quot;&gt;&amp;quot;Powerful cross-platform terminal emulator and multiplexer&amp;quot;&lt;/span&gt;)
    (description
     &lt;span class=&quot;org-string&quot;&gt;&amp;quot;A GPU-accelerated cross-platform terminal emulator and
multiplexer written by wez and implemented in Rust.  Features:

@itemize
@item Runs on Linux, macOS, Windows 10, FreeBSD and NetBSD,
@item Multiplex terminal panes, tabs and windows on local and
remote hosts, with native mouse and scrollback,
@item Ligatures, Color Emoji and font fallback, with true color
and dynamic color schemes,
@item Hyperlinks.
@end itemize
&amp;quot;&lt;/span&gt;)
    (license license:expat)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
There's only really three things of note:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Instead of simply doing a &amp;quot;bare&amp;quot; &lt;code&gt;package&lt;/code&gt; form, we wrap our package definition into a &lt;code&gt;define-public&lt;/code&gt; form. This is how we ensure that multiple package definitions can coexist in a single file.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;
Secondly, the package source form was rewritten to use &lt;code&gt;origin&lt;/code&gt; instead, which allows for a lot more flexibility in how we download stuff from the net.
&lt;/p&gt;

&lt;p&gt;
Among other things, it allows us to rewrite parts of the source, delete unnecessary files, create wholly new files, and (as can be seen here) it also allows us to cryptographically check whether the source files we get really are what we expect them to be. Packages meant for the main Guix repo all must have their hash checked, so adding it wasn't merely my frivolousness.
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Note from the future:&lt;/b&gt; As it turns out, as helpful as this step was during development, it is entirely possible to make WezTerm work without recursive cloning and, in fact, this option is only allowed in extraordinary cases.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
I also enabled recursive cloning of the repo as there are some submodules involved and once we're past the source-fetching stage, the network is disabled and we wouldn't be able to download them anymore.
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;
Thirdly you may wonder about the weird &lt;code&gt;`(,zstd &amp;quot;lib&amp;quot;)&lt;/code&gt; form in our inputs. As inputs are actual other packages, not strings-based specifications, we can no longer do the previous &amp;quot;package:output&amp;quot; way of referring to alternative outputs.
&lt;/p&gt;

&lt;p&gt;
For that, we need to create a two-element list, where the first is the dependency and the second is the textual name of the output. So, really, this is just the packaging way of saying &lt;code&gt;zstd:lib&lt;/code&gt;.
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-importing-cargo-crates&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;importing-cargo-crates&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;4.2.&lt;/span&gt; Importing Cargo crates&lt;/h3&gt;
&lt;div id=&quot;text-4-2&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
Still, with all this work, if we run this using &lt;code&gt;./pre-inst-env guix build wezterm&lt;/code&gt;, we will be met with the previous error about Cargo not being able to fetch &lt;code&gt;finl_unicode&lt;/code&gt;. Which shouldn't be surprising, since &lt;a href=&quot;#import-mention&quot;&gt;as mentioned&lt;/a&gt; earlier we haven't imported our Cargo dependencies into Guix yet.
&lt;/p&gt;

&lt;p&gt;
To do this, we will need to issue &lt;code&gt;guix import&lt;/code&gt; in our previously checked out WezTerm Git repository in the following way:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;guix import -i &amp;lt;path to Guix repo&amp;gt;/gnu/packages/rust-crates.scm &lt;span class=&quot;org-sh-escaped-newline&quot;&gt;\&lt;/span&gt;
     crate -f &amp;lt;path to Git repo&amp;gt;/Cargo.lock &lt;span class=&quot;org-sh-escaped-newline&quot;&gt;\&lt;/span&gt;
     wezterm
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
What this does is go through every individual Cargo dependency in the &lt;code&gt;Cargo.lock&lt;/code&gt; file and compares it to the list found in &lt;code&gt;rust-crates.scm&lt;/code&gt;. If a matching entry is found, nothing is done, if not, the importer adds a new entry to the file. If a crate has a matching crates.io page, an entry such as this is generated:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-scheme&quot;&gt;(&lt;span class=&quot;org-keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;rust-libssh-rs-0.3.6&lt;/span&gt;
  (crate-source &lt;span class=&quot;org-string&quot;&gt;&amp;quot;libssh-rs&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;0.3.6&amp;quot;&lt;/span&gt;
                &lt;span class=&quot;org-string&quot;&gt;&amp;quot;11f6fj59dqpy7n0g74s7vnnyrbpxbrcyxhnrvfnsb5dvsq8f2rih&amp;quot;&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
If not, however, then we get a slightly bigger variant:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-scheme&quot;&gt;(&lt;span class=&quot;org-keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;rust-finl-unicode-1.3.0.a1892f2&lt;/span&gt;
  &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;TODO REVIEW: Define standalone package if this is a workspace.
&lt;/span&gt;  (origin
    (method git-fetch)
    (uri (git-reference (url &lt;span class=&quot;org-string&quot;&gt;&amp;quot;https://github.com/wez/finl_unicode.git&amp;quot;&lt;/span&gt;)
                        (commit &lt;span class=&quot;org-string&quot;&gt;&amp;quot;a1892f26245529f2ef3877a9ebd610c96cec07a6&amp;quot;&lt;/span&gt;)))
    (file-name (git-file-name &lt;span class=&quot;org-string&quot;&gt;&amp;quot;rust-finl-unicode&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;1.3.0.a1892f2&amp;quot;&lt;/span&gt;))
    (sha256 (base32 &lt;span class=&quot;org-string&quot;&gt;&amp;quot;0g9lqwrzm7ca54vlq8sgix3wvbsxwp7glkx3dzjdd591grfbmi6z&amp;quot;&lt;/span&gt;))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
It bears resemblance to our own package's &lt;code&gt;origin&lt;/code&gt; field  and it also has a foreboding comment about how we need to review this generated package to check if it's a &amp;quot;workspace&amp;quot;. Elaborating on &lt;a href=&quot;https://doc.rust-lang.org/cargo/reference/workspaces.html&quot;&gt;what a workspace is&lt;/a&gt; in Rust is beyond the scope of this article, but the long story short is that a single Cargo project may contain multiple, related subprojects and the importer is not able to untangle these by itself.
&lt;/p&gt;

&lt;p&gt;
If you're packaging something that depends on such a project, you must manually make sure every single subproject gets its own definition, which call back to the main workspace. &lt;code&gt;finl_unicode&lt;/code&gt; isn't such a project, however, so we can safely ignore the warning for now.
&lt;/p&gt;

&lt;p&gt;
Where we cannot ignore the comment, however, is with the bindings for OpenSSL, &lt;code&gt;rust-openssl-sys-0.9.111&lt;/code&gt;. If we were to leave things as-is, we'd soon find that our project does not build due to not finding &lt;code&gt;openssl-src&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Thankfully, we don't have to figure it out how to fix this issue ourselves, as the package already has several other versions in the Guix repo. We simply have to copy someone else's solution and we're one step closer to making WezTerm build:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-diff&quot;&gt;&lt;span class=&quot;org-diff-header&quot;&gt;diff --git a/gnu/packages/rust-crates.scm b/gnu/packages/rust-crates.scm
index 74fba63cd8..213882b4a2 100644
--- &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;a/gnu/packages/rust-crates.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
+++ &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;b/gnu/packages/rust-crates.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -13605,9 +13605,19 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define rust-openssl-sys-0.9.110&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;                     (copy-file &amp;quot;Cargo.toml.orig&amp;quot; &amp;quot;Cargo.toml&amp;quot;))))

 (define rust-openssl-sys-0.9.111
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-removed&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;  ;; TODO REVIEW: Check bundled sources.
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;   (crate-source &amp;quot;openssl-sys&amp;quot; &amp;quot;0.9.111&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-removed&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;                &amp;quot;08f3mpsabivfi3fd0qv9231qidqy68lr8a4qi32y6xda43av5jl2&amp;quot;))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;               &amp;quot;08f3mpsabivfi3fd0qv9231qidqy68lr8a4qi32y6xda43av5jl2&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;               #:snippet
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;               #~(begin
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                   ;; Remove dependency on boringssl and vendor openssl source.
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                   (substitute* &amp;quot;Cargo.toml.orig&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                     ((&amp;quot;vendored = .*&amp;quot;) &amp;quot;vendored = []\n&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                     ((&amp;quot;.*bssl.*&amp;quot;) &amp;quot;&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                     ((&amp;quot;.*openssl-src.*&amp;quot;) &amp;quot;&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                     ;; Allow any version of bindgen.
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                     ((&amp;quot;(bindgen = \\{ version =) \&amp;quot;.*\&amp;quot;,&amp;quot; _ bindgen)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                      (string-append bindgen &amp;quot;\&amp;quot;*\&amp;quot;,&amp;quot;)))
&lt;/span&gt;+                   (copy-file &amp;quot;Cargo.toml.orig&amp;quot; &amp;quot;Cargo.toml&amp;quot;))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Once the importer has gone through every dependency, it also generates a lookup table entry in &lt;code&gt;rust-crates.scm&lt;/code&gt; in the form of &lt;code&gt;package-name =&amp;gt; (list &amp;lt;list-of-dependencies&amp;gt;)&lt;/code&gt;. This is ultimately the mechanism that pairs user defined Rust Guix packages to their Cargo defined dependencies. For instance, since we entered &lt;code&gt;wezterm&lt;/code&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.8&quot; href=&quot;#fn.8&quot; class=&quot;footref&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; in our &lt;code&gt;cargo import&lt;/code&gt; query, we get the following list:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-scheme&quot;&gt;(wezterm =&amp;gt;
       (list rust-addr2line-0.25.1
        rust-adler-1.0.2
        rust-adler2-2.0.1
        rust-adler32-1.2.0
        rust-ahash-0.8.12
        rust-aho-corasick-1.1.4
        rust-aligned-vec-0.6.4
        ... and like 600 more crates ...
        ))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And somewhere in this list, &lt;code&gt;finl_unicode&lt;/code&gt; can also be found. Finally, we are able to provide all our dependencies to the build system:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 3: &lt;/span&gt;The build system has access to all the crates that we automagically imported from the Cargo lockfile.&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;&lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;cons* takes an arbitrary amount of elements and a list and prepends the former to the latter.
&lt;/span&gt;&lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;E.g. (cons* 'a 'b 'c '(d e)) =&amp;gt; (a b c d e)
&lt;/span&gt;(inputs (cons*
         openssl
         wayland
         libx11
         ... our previous list of packages ...
         (cargo-inputs 'wezterm)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
We issue &lt;code&gt;./pre-inst-env guix build wezterm&lt;/code&gt;… and run into the same error. What gives?
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-patching-cargo-toml&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;patching-cargo-toml&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;4.3.&lt;/span&gt; Patching Cargo.toml&lt;/h3&gt;
&lt;div id=&quot;text-4-3&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
Though the code itself is fairly trivial, understanding the solution to this issue was perhaps the most difficult part of the entire process to me. Largely because there is almost zero indication what and how you need to do, and the docs &lt;a href=&quot;https://guix.gnu.org/cookbook/en/html_node/Cargo-Workspaces-and-Development-Snapshots.html#:~:text=To%20use%20our%20packaged%20development%20snapshots%2C%20it%E2%80%99s%20also%20necessary%20to%20modify%20Cargo%2Etoml%20in%20a%20build%20phase%2C%20with%20a%20package%2Dspecific%20substitution%20pattern%2E&quot;&gt;barely touch upon&lt;/a&gt; the existence of this issue and its fix either. I had to resort to reading other packages' definitions, which worked, but definitely wasn't the smooth-sailing as things had been up to this point.
&lt;/p&gt;

&lt;p&gt;
To finally reveal this hidden menace that has been keeping us from compiling this poor application till now, let's take a look at two different dependencies from WezTerm's &lt;code&gt;Cargo.toml&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-toml&quot;&gt;&lt;span class=&quot;org-variable-name&quot;&gt;finl_unicode&lt;/span&gt; = { version = &lt;span class=&quot;org-string&quot;&gt;&amp;quot;1.3&amp;quot;&lt;/span&gt;,  git=&lt;span class=&quot;org-string&quot;&gt;&amp;quot;https://github.com/wez/finl_unicode.git&amp;quot;&lt;/span&gt;, branch=&lt;span class=&quot;org-string&quot;&gt;&amp;quot;no_std&amp;quot;&lt;/span&gt; , default-features=&lt;span class=&quot;org-keyword&quot;&gt;false&lt;/span&gt;, features=[&lt;span class=&quot;org-string&quot;&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;, &lt;span class=&quot;org-string&quot;&gt;&amp;quot;grapheme_clusters&amp;quot;&lt;/span&gt;]}
&lt;span class=&quot;org-variable-name&quot;&gt;fixed&lt;/span&gt; = &lt;span class=&quot;org-string&quot;&gt;&amp;quot;1.23&amp;quot;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
For dependencies like &lt;code&gt;fixed&lt;/code&gt;, there is no issue, the build system can match them with their imported package no-problem. However, for some reason that I still don't quite understand, if there is a &lt;code&gt;git&lt;/code&gt; field in the dependency list, then that completely breaks the proces, as Cargo will always try to download it from the internet, which then fails due to the build environment not having network access.
&lt;/p&gt;

&lt;p&gt;
The solution?
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-toml&quot;&gt;&lt;span class=&quot;org-variable-name&quot;&gt;finl_unicode&lt;/span&gt; = { version = &lt;span class=&quot;org-string&quot;&gt;&amp;quot;1.3&amp;quot;&lt;/span&gt;, default-features=&lt;span class=&quot;org-keyword&quot;&gt;false&lt;/span&gt;, features=[&lt;span class=&quot;org-string&quot;&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;, &lt;span class=&quot;org-string&quot;&gt;&amp;quot;grapheme_clusters&amp;quot;&lt;/span&gt;]}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Yep. That's it. We just have to instruct Guix to rewrite this one line (well, actually two, because there's two such dependencies in the project, but I'll not bore you with the exact same process) and everything else will magically work.
&lt;/p&gt;

&lt;p&gt;
To do this, we introduce a new section to our package definition, &lt;code&gt;arguments&lt;/code&gt;. The role of this field is to override certain aspects of the build system preset, such as the build flags or what so-called &amp;quot;build phases&amp;quot; we wish to execute on the code.
&lt;/p&gt;

&lt;p&gt;
A build phase can be practically anything from setting environment variables, to compiling code, to editing files. We would like to do the last one, so let's introduce a new build phase:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-scheme&quot;&gt;(arguments
 (list
  &lt;span class=&quot;org-builtin&quot;&gt;#:phases&lt;/span&gt;
  #~(modify-phases %standard-phases
      (add-after 'unpack 'use-guix-vendored-dependencies
        (&lt;span class=&quot;org-keyword&quot;&gt;lambda&lt;/span&gt; _
          (substitute* &lt;span class=&quot;org-string&quot;&gt;&amp;quot;Cargo.toml&amp;quot;&lt;/span&gt;
            ((&lt;span class=&quot;org-string&quot;&gt;&amp;quot;,  git.*default-features&amp;quot;&lt;/span&gt;)
             &lt;span class=&quot;org-string&quot;&gt;&amp;quot;, default-features&amp;quot;&lt;/span&gt;)
            ((&lt;span class=&quot;org-string&quot;&gt;&amp;quot;, git.*, rev.*}&amp;quot;&lt;/span&gt;)
             &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;This is added to ensure a different dependency
&lt;/span&gt;             &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;uses libraries provided by the inputs instead
&lt;/span&gt;             &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;of relying on bundled files.
&lt;/span&gt;             &lt;span class=&quot;org-string&quot;&gt;&amp;quot;, features=[\&amp;quot;use-system-lib\&amp;quot;]}&amp;quot;&lt;/span&gt;)))))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This code adds a new phase after &lt;code&gt;unpack&lt;/code&gt; (which is the phase responsible for extracting the actually usable files and folders from our &lt;code&gt;origin&lt;/code&gt;) named &lt;code&gt;use-guix-vendored-dependencies&lt;/code&gt;. The name of the phase that we're adding is arbitrary, but you're expected to pick something informative and truthful, so please don't try &lt;code&gt;do-stuff&lt;/code&gt; or &lt;code&gt;thing&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
The magic happens inside &lt;code&gt;substitute*&lt;/code&gt;, which takes a filename and a list of &lt;code&gt;regexp =&amp;gt; replacement&lt;/code&gt; pairs, and then executes the replacements. It's kind of like having a lispy &lt;code&gt;sed&lt;/code&gt; in your arsenal.
&lt;/p&gt;

&lt;p&gt;
&lt;a id=&quot;gexp&quot;&gt;&lt;/a&gt;Careful readers may have noticed the weird &lt;code&gt;#~&lt;/code&gt; symbol in front of the &lt;code&gt;modify-phases&lt;/code&gt; function call. This is a Guix-exclusive extension to Scheme's syntax, called &lt;a href=&quot;https://guix.gnu.org/manual/devel/en/html_node/G_002dExpressions.html&quot;&gt;&lt;i&gt;G-expressions&lt;/i&gt;&lt;/a&gt; (abbreviated as &amp;quot;gexp&amp;quot;).
&lt;/p&gt;

&lt;p&gt;
Their job is to facilitate working with files as data, therefore whenever we need to directly affect our source code (be that creation, deletion, or modification) or call out into the operating system, we need to do so inside a gexp. This isn't the only place where we'll need to do so, so I'll call into attention when it happens.
&lt;/p&gt;

&lt;p&gt;
In more important news, if we were to issue &lt;code&gt;./pre-inst-env guix build wezterm&lt;/code&gt; now, we'd find that the package actually builds… before failing again:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;error: crates-io is replaced with non-remote-registry source dir /tmp/guix-build-wezterm-05343b387085842b434d267f91b6b0ec157e4331.drv-0/source/guix-vendor;
include `--registry crates-io` to use crates.io
error: in phase 'package': uncaught exception:
%exception #&amp;lt;&amp;amp;invoke-error program: &amp;quot;cargo&amp;quot; arguments: (&amp;quot;package&amp;quot; &amp;quot;--offline&amp;quot; &amp;quot;--no-metadata&amp;quot; &amp;quot;--no-verify&amp;quot;) exit-status: 101 term-signal: #f stop-signal: #f&amp;gt; 
phase `package' failed after 0.1 seconds
command &amp;quot;cargo&amp;quot; &amp;quot;package&amp;quot; &amp;quot;--offline&amp;quot; &amp;quot;--no-metadata&amp;quot; &amp;quot;--no-verify&amp;quot; failed with status 101
build process 18 exited with status 256
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The actual error is a little vague, but the gist is that by default the &lt;code&gt;cargo-build-system&lt;/code&gt; &lt;a href=&quot;https://guix.gnu.org/manual/devel/en/html_node/Build-Systems.html#:~:text=Unless%20install%2Dsource%3F%20%23f%20is%20defined%20it%20will%20also%20install%20a%20source%20crate%20repository%20of%20itself%20and%20unpacked%20sources%2C%20to%20ease%20in%20future%20hacking%20on%20Rust%20packages%2E&quot;&gt;attempts&lt;/a&gt; to install the sources of all the dependencies we used. For us, this is neither desirable, nor does it actually allow the build to finish, so by adding the following to our arguments, we disable it (along with tests&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.9&quot; href=&quot;#fn.9&quot; class=&quot;footref&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;):
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-diff&quot;&gt;&lt;span class=&quot;org-diff-header&quot;&gt;diff --git a/gnu/packages/terminals.scm b/gnu/packages/terminals.scm
index 476e2b743e..84f52b24f8 100644
--- &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;a/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
+++ &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;b/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -1702,6 +1702,8 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public wezterm&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;       (build-system cargo-build-system)
       (arguments
        (list
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;        #:install-source? #f
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;        #:tests? #f
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;         #:phases
         #~(modify-phases %standard-phases
&lt;/span&gt;             (add-after 'unpack 'use-guix-vendored-dependencies
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Call build again, and…
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-we-have-liftoff-almost&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;we-have-liftoff-almost&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;4.4.&lt;/span&gt; We have liftoff… Almost&lt;/h3&gt;
&lt;div id=&quot;text-4-4&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
No errors. Our build was successful.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;successfully built /gnu/store/h8d2igkg4vlg687ixgj6hizyd4gbzl64-wezterm-05343b387085842b434d267f91b6b0ec157e4331.drv
/gnu/store/h99jlsdism6jxn3x02fv7jig61r6ydv2-wezterm-05343b387085842b434d267f91b6b0ec157e4331
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Let's celebrate by starting a shell that contains our newly built terminal in it and start it up:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;guix shell wezterm -- wezterm
guix shell: error: wezterm: command not found
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Sadly we're still no quite there, but there is only one step separating us from the same spot as we were with the original &amp;quot;ad-hoc&amp;quot; implementation. If we list the files in our built derivation, we would find that it doesn't contain anything useful:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;/gnu/store/h99jlsdism6jxn3x02fv7jig61r6ydv2-wezterm-05343b387085842b434d267f91b6b0ec157e4331
└── share
    └── doc
        └── wezterm-05343b387085842b434d267f91b6b0ec157e4331
            ├── ANGLE.md
            ├── LICENSE.md
            └── README.md
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
What happened? Simply put, we only told Guix to build our executables, not that we actually &lt;i&gt;need&lt;/i&gt; them. The built package contains several different binaries, of which we're primarily interested in one: &lt;code&gt;wezterm-gui&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
To access it, we will a phase called &lt;code&gt;install&lt;/code&gt; which is supposed to put everything in its rightful place. Let's modify &lt;code&gt;arguments&lt;/code&gt; and override it to install our file:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-diff&quot;&gt;&lt;span class=&quot;org-diff-header&quot;&gt;diff --git a/gnu/packages/terminals.scm b/gnu/packages/terminals.scm
index 84f52b24f8..8f54a79dbe 100644
--- &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;a/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
+++ &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;b/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -1712,7 +1712,16 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public wezterm&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;                   ((&amp;quot;,  git.*default-features&amp;quot;)
                    &amp;quot;, default-features&amp;quot;)
                   ((&amp;quot;, git.*, rev.*}&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-removed&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;                   &amp;quot;, features=[\&amp;quot;use-system-lib\&amp;quot;]}&amp;quot;)))))))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                   &amp;quot;, features=[\&amp;quot;use-system-lib\&amp;quot;]}&amp;quot;))))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;          (replace 'install
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;           (lambda* (#:key inputs native-inputs #:allow-other-keys)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;             ;; Binaries
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;             (with-directory-excursion &amp;quot;target/release&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;               (for-each
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                 (lambda (name) (install-file name (string-append #$output &amp;quot;/bin&amp;quot;)))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                '(&amp;quot;wezterm&amp;quot; &amp;quot;wezterm-gui&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                  &amp;quot;wezterm-mux-server&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                  &amp;quot;strip-ansi-escapes&amp;quot;))))))))
&lt;/span&gt;       (home-page &amp;quot;https://wezterm.org/&amp;quot;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Note from the future:&lt;/b&gt; Binaries aren't the only thing that need to be installed for a package like this. You also need to copy over stuff like icons, &lt;code&gt;.desktop&lt;/code&gt; files, and any other various assets a package might need.
&lt;/p&gt;

&lt;p&gt;
I abridged the package definition here quite a bit, because much of the process is just more of the same and I see very little educational value in repeating 95% the same code.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
This snippet of code enters &lt;code&gt;./target/release&lt;/code&gt;, which is the folder where Cargo places our compiled binaries, walks through our supplied list of executable names and copies each to &lt;code&gt;#$output/bin&lt;/code&gt;. In Bash terms, you could think of this as &lt;code&gt;cp ./target/release/wezterm $OUTPUT/bin/&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
You may wonder what &lt;code&gt;#$output&lt;/code&gt; even is. It's actually two separate things combined:
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;#$&lt;/code&gt; is a so-called &lt;a href=&quot;https://www.gnu.org/software/guile/manual/html_node/Reader-Extensions.html&quot;&gt;reader extension&lt;/a&gt; specific to Guix. Reader extensions are a user-defined extension to the usual syntax extensions, which are in turn special characters that affect the behavior of Lispy languages and are mostly used because spelling out the entire form they shorten would be quite unwieldy.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 4: &lt;/span&gt;Some examples of common syntax extensions to all Lispy languages.&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;(+ 1 2)         &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;=&amp;gt; 3        (Form is evaluated as usual)
&lt;/span&gt;'(+ 1 2)        &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;=&amp;gt; (+ 1 2)  (Form is &amp;quot;quoted&amp;quot; and returned as-is)
&lt;/span&gt;`(+ 1 2)        &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;=&amp;gt; (+ 1 2)  (Form is &amp;quot;quasiquoted&amp;quot; and returned as-is)
&lt;/span&gt;`(+ 1 ,(+ 1 1)) &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;=&amp;gt; (+ 1 2)  (Form is &amp;quot;quasiquoted&amp;quot;, the third element is &amp;quot;unquoted&amp;quot;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
In our case &lt;code&gt;#$&lt;/code&gt; is a lot like &lt;code&gt;,&lt;/code&gt; (also known as &lt;code&gt;unquote&lt;/code&gt;), which is used in conjunction with &lt;code&gt;`&lt;/code&gt; (&lt;code&gt;quasiquote&lt;/code&gt;). It allows us to &amp;quot;unquote&amp;quot; (i.e. execute) code inside a &lt;a href=&quot;#gexp&quot;&gt;gexp&lt;/a&gt;, which itself acts as sort of a quasiquote (i.e. a form that isn't evaluated by default, only the parts that are explicitly unquoted). For more information about the concept, Guile has an &lt;a href=&quot;https://www.gnu.org/software/guile/manual/html_node/Expression-Syntax.html&quot;&gt;entire page&lt;/a&gt; dedicated to it.
&lt;/p&gt;

&lt;p&gt;
Meanwhile, &lt;code&gt;output&lt;/code&gt; is simply a variable provided implicitly in our install phase, that points to the directory where our derivation will be built into. This folder acts similarly to a filesystem root folder (what you may know as &lt;code&gt;/&lt;/code&gt;), but it only contains files that either we explicitly placed into it or are automatically copied by the build phases.
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-02-01-guix_packaging/symlinks.avif&quot; alt=&quot;symlinks.avif&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
When we install a package, the files from its output are made available in our general environment. You can see this by calling &lt;code&gt;ls /run/current-system/profile/bin&lt;/code&gt;, which will list out most&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.10&quot; href=&quot;#fn.10&quot; class=&quot;footref&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; of the binaries available to you in the current environment. One thing of note is that none of these files here are the actual executables, but rather symlinks to binaries found in the Guix store, which is the organized collection of all built derivations.
&lt;/p&gt;

&lt;p&gt;
Circling back to our package, now that our executables are copied over to the output's bin folder, they will be available to use if we were to install the package. Let's do so:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;guix shell wezterm
wezterm
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This time the terminal runs, but then immediately crashes…
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-gpu-maladies&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;gpu-maladies&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;4.5.&lt;/span&gt; GPU maladies&lt;/h3&gt;
&lt;div id=&quot;text-4-5&quot; class=&quot;outline-text-3&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Note from the future:&lt;/b&gt; The method shown below might be a viable path for some particularly stubborn libraries, however, it is &lt;b&gt;not&lt;/b&gt; a good first approach. As it turns out in this case we can get rid of this entire build phase by simply instructing Cargo to link &lt;code&gt;libEGL&lt;/code&gt; using the &lt;code&gt;RUSTFLAGS&lt;/code&gt; environment variable.
&lt;/p&gt;

&lt;p&gt;
If you're thinking of making your own package, please be sure to check whether you can simply instruct your linker before you attempt to hack apart the code you're working on.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;21:59:33.533  ERROR  wezterm_gui::frontend &amp;gt; Failed to create window: with_egl_lib failed:
libEGL.so.1: libEGL.so.1: cannot open shared object file: No such file or directory,
libEGL.so: libEGL.so: cannot open shared object file: No such file or directory,
libEGL.so.1: libEGL.so.1: cannot open shared object file: No such file or directory,
libEGL.so: libEGL.so: cannot open shared object file: No such file or directory
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
You might remember, that we've &lt;a href=&quot;#egl-first&quot;&gt;seen&lt;/a&gt; this error already. This time, however, we'll do it right and instead of relying on a random build string extracted from a command, we'll rely on our build inputs.
&lt;/p&gt;

&lt;p&gt;
To figure out what we're supposed to do, let's &lt;code&gt;grep&lt;/code&gt; for &lt;code&gt;libEGL.so&lt;/code&gt; to find all the places the library is used. There are some irrelevant results, but there is some promising code under &lt;code&gt;./window/src/egl.rs&lt;/code&gt;. The definition of the function named &lt;code&gt;with_egl_lib&lt;/code&gt; reveals how exactly we're trying to load the library:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-rust&quot;&gt;&lt;span class=&quot;org-keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;with_egl_lib&lt;/span&gt;&amp;lt;&lt;span class=&quot;org-variable-name&quot;&gt;F&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;FnMut&lt;/span&gt;(&lt;span class=&quot;org-type&quot;&gt;EglWrapper&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;org-constant&quot;&gt;anyhow&lt;/span&gt;::&lt;span class=&quot;org-type&quot;&gt;Result&lt;/span&gt;&amp;lt;&lt;span class=&quot;org-type&quot;&gt;Self&lt;/span&gt;&amp;gt;&amp;gt;(
    &lt;span class=&quot;org-keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;func&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;F&lt;/span&gt;,
) -&amp;gt; &lt;span class=&quot;org-constant&quot;&gt;anyhow&lt;/span&gt;::&lt;span class=&quot;org-type&quot;&gt;Result&lt;/span&gt;&amp;lt;&lt;span class=&quot;org-type&quot;&gt;Self&lt;/span&gt;&amp;gt; {
    &lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;paths&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;Vec&lt;/span&gt;&amp;lt;&lt;span class=&quot;org-constant&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;org-constant&quot;&gt;path&lt;/span&gt;::&lt;span class=&quot;org-type&quot;&gt;PathBuf&lt;/span&gt;&amp;gt; = &lt;span class=&quot;org-preprocessor&quot;&gt;vec!&lt;/span&gt;[
        &lt;span class=&quot;org-preprocessor&quot;&gt;#[cfg(target_os = &lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;&amp;quot;windows&amp;quot;&lt;/span&gt;&lt;span class=&quot;org-preprocessor&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;libEGL.dll&amp;quot;&lt;/span&gt;.into(),
        &lt;span class=&quot;org-preprocessor&quot;&gt;#[cfg(target_os = &lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;&amp;quot;windows&amp;quot;&lt;/span&gt;&lt;span class=&quot;org-preprocessor&quot;&gt;)]&lt;/span&gt;
        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;atioglxx.dll&amp;quot;&lt;/span&gt;.into(),
        &lt;span class=&quot;org-preprocessor&quot;&gt;#[cfg(all(not(target_os = &lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;&amp;quot;macos&amp;quot;&lt;/span&gt;&lt;span class=&quot;org-preprocessor&quot;&gt;), not(target_os = &lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;&amp;quot;windows&amp;quot;&lt;/span&gt;&lt;span class=&quot;org-preprocessor&quot;&gt;)))]&lt;/span&gt;
        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;libEGL.so.1&amp;quot;&lt;/span&gt;.into(),
        &lt;span class=&quot;org-preprocessor&quot;&gt;#[cfg(all(not(target_os = &lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;&amp;quot;macos&amp;quot;&lt;/span&gt;&lt;span class=&quot;org-preprocessor&quot;&gt;), not(target_os = &lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;&amp;quot;windows&amp;quot;&lt;/span&gt;&lt;span class=&quot;org-preprocessor&quot;&gt;)))]&lt;/span&gt;
        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;libEGL.so&amp;quot;&lt;/span&gt;.into(),
    ];
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This piece of code tries to simply look for &lt;code&gt;libEGL.so&lt;/code&gt; without any path associated with it. Therefore the dynamic library loader will try all known search places. However, since Guix doesn't put libraries in the normal search paths, this will fail.
&lt;/p&gt;

&lt;p&gt;
So, what if we were to replace this string by an exact path to the library provided by one of our inputs? Turns out that's exactly what we need to do.
&lt;/p&gt;

&lt;p&gt;
As you might have guessed, we need yet another phase:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-diff&quot;&gt;&lt;span class=&quot;org-diff-header&quot;&gt;diff --git a/gnu/packages/terminals.scm b/gnu/packages/terminals.scm
index f7eb633624..34758f238e 100644
--- &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;a/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
+++ &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;b/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -1713,6 +1713,10 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public wezterm&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;                    &amp;quot;, default-features&amp;quot;)
                   ((&amp;quot;, git.*, rev.*}&amp;quot;)
                    &amp;quot;, features=[\&amp;quot;use-system-lib\&amp;quot;]}&amp;quot;))))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;            (add-after 'unpack 'fix-libegl-so
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;              (lambda _
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                (substitute* &amp;quot;./window/src/egl.rs&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                  ((&amp;quot;libEGL.so&amp;quot;) (string-append #$mesa &amp;quot;/lib/libEGL.so&amp;quot;)))))
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;             (replace 'install
              (lambda* (#:key inputs native-inputs #:allow-other-keys)
&lt;/span&gt;                ;; Binaries
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Let's rebuild and run our package again.
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-02-01-guix_packaging/working.avif&quot; alt=&quot;working.avif&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Success! Well, mostly. If we really wanted to, we could stop here. Our package technically &amp;quot;works&amp;quot; and can be installed. However, in its current state, it'd never be accepted into the registry for two main reasons (and a myriad of small ones, see the &lt;a href=&quot;#reject-reasons&quot;&gt;final part&lt;/a&gt; of this story).
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-finding-fonts&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;finding-fonts&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;4.6.&lt;/span&gt; Finding fonts&lt;/h3&gt;
&lt;div id=&quot;text-4-6&quot; class=&quot;outline-text-3&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Note from the future:&lt;/b&gt; WezTerm actually bundles &lt;a href=&quot;https://github.com/wezterm/wezterm/blob/main/README-DISTRO-MAINTAINER.md#un-bundling-vendored-fonts&quot;&gt;four different fonts&lt;/a&gt;. During the development of the package I only packaged JetBrains Mono and Roboto, because those two are considered vital for the terminal to function.
&lt;/p&gt;

&lt;p&gt;
However, by the time the package was merged, one of the contributors added the other two fonts too and replaced this entire phase with much more elegant code.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Of these two big issues, one is that we are still relying on fonts bundled by the source repository. This is a no-go, especially since most of the fonts we wish to use are already packaged in Guix.
&lt;/p&gt;

&lt;p&gt;
By looking into the source files, we find that WezTerm's font-loading code is found in &lt;code&gt;wezterm-font/src/parser.rs&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-rust&quot;&gt;&lt;span class=&quot;org-doc&quot;&gt;/// In case the user has a broken configuration, or no configuration,
/// we bundle JetBrains Mono and Noto Color Emoji to act as reasonably
/// sane fallback fonts.
/// This function loads those.
&lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;pub&lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;crate&lt;/span&gt;) &lt;span class=&quot;org-keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;load_built_in_fonts&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;font_info&lt;/span&gt;: &lt;span class=&quot;org-rust-ampersand&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vec&lt;/span&gt;&amp;lt;&lt;span class=&quot;org-type&quot;&gt;ParsedFont&lt;/span&gt;&amp;gt;) -&amp;gt; &lt;span class=&quot;org-constant&quot;&gt;anyhow&lt;/span&gt;::&lt;span class=&quot;org-type&quot;&gt;Result&lt;/span&gt;&amp;lt;()&amp;gt; {
    &lt;span class=&quot;org-preprocessor&quot;&gt;#[allow(unused_macros)]&lt;/span&gt;
    &lt;span class=&quot;org-preprocessor&quot;&gt;macro_rules!&lt;/span&gt; font {
       (&lt;span class=&quot;org-variable-name&quot;&gt;$font&lt;/span&gt;:literal) =&amp;gt; {
           (&lt;span class=&quot;org-preprocessor&quot;&gt;include_bytes!&lt;/span&gt;($font) &lt;span class=&quot;org-keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;org-rust-ampersand&quot;&gt;&amp;amp;&lt;/span&gt;'&lt;span class=&quot;org-keyword&quot;&gt;static&lt;/span&gt; [&lt;span class=&quot;org-type&quot;&gt;u8&lt;/span&gt;], $font)
       };
   }
   &lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;lib&lt;/span&gt; = &lt;span class=&quot;org-keyword&quot;&gt;crate&lt;/span&gt;::&lt;span class=&quot;org-constant&quot;&gt;ftwrap&lt;/span&gt;::&lt;span class=&quot;org-type&quot;&gt;Library&lt;/span&gt;::new()&lt;span class=&quot;org-rust-question-mark&quot;&gt;?&lt;/span&gt;;

   &lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;built_ins&lt;/span&gt;: &lt;span class=&quot;org-rust-ampersand&quot;&gt;&amp;amp;&lt;/span&gt;[&lt;span class=&quot;org-rust-ampersand&quot;&gt;&amp;amp;&lt;/span&gt;[(&lt;span class=&quot;org-rust-ampersand&quot;&gt;&amp;amp;&lt;/span&gt;[&lt;span class=&quot;org-type&quot;&gt;u8&lt;/span&gt;], &lt;span class=&quot;org-rust-ampersand&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;org-type&quot;&gt;str&lt;/span&gt;)]] = &lt;span class=&quot;org-rust-ampersand&quot;&gt;&amp;amp;&lt;/span&gt;[
       &lt;span class=&quot;org-preprocessor&quot;&gt;#[cfg(any(test, feature = &lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;&amp;quot;vendor-jetbrains&amp;quot;&lt;/span&gt;&lt;span class=&quot;org-preprocessor&quot;&gt;))]&lt;/span&gt;
       &lt;span class=&quot;org-rust-ampersand&quot;&gt;&amp;amp;&lt;/span&gt;[
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-BoldItalic.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-Bold.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-ExtraBoldItalic.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-ExtraBold.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-ExtraLightItalic.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-ExtraLight.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-Italic.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-LightItalic.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-Light.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-MediumItalic.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-Medium.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-Regular.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-SemiBoldItalic.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-SemiBold.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-ThinItalic.ttf&amp;quot;&lt;/span&gt;),
           &lt;span class=&quot;org-preprocessor&quot;&gt;font!&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;../../assets/fonts/JetBrainsMono-Thin.ttf&amp;quot;&lt;/span&gt;),
       ],
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
As you guessed, we need yet another phase, this time to replace all instances of &lt;code&gt;../../assets/fonts&lt;/code&gt; to the font package's own path:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-diff&quot;&gt;&lt;span class=&quot;org-diff-header&quot;&gt;diff --git a/gnu/packages/terminals.scm b/gnu/packages/terminals.scm
index 13e690e11f..a2a5a71a07 100644
--- &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;a/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
+++ &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;b/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -67,6 +67,7 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-module (gnu packages terminals)&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;   #:use-module (gnu packages sqlite)
   #:use-module (gnu packages elf)
   #:use-module (gnu packages vulkan)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;  #:use-module (gnu packages fonts)
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;   #:use-module ((guix licenses) #:prefix license:)
   #:use-module (guix build-system cargo)
   #:use-module (guix build-system cmake)
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -1702,7 +1703,9 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public wezterm&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;                             patchelf
                             vulkan-loader
                             `(,zstd &amp;quot;lib&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-removed&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;org-diff-removed&quot;&gt;                            mesa)))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                            mesa
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;+&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;                            font-jetbrains-mono
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;+&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;&lt;span class=&quot;org-diff-refine-added&quot;&gt;                            font-google-roboto&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;)))
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;       (build-system cargo-build-system)
       (arguments
        (list
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -1721,6 +1724,17 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public wezterm&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;               (lambda _
                 (substitute* &amp;quot;./window/src/egl.rs&amp;quot;
                   ((&amp;quot;libEGL.so&amp;quot;) (string-append #$mesa &amp;quot;/lib/libEGL.so&amp;quot;)))))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;            (add-after 'unpack 'fix-font-load-path
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;              (lambda* (#:key inputs #:allow-other-keys)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                (substitute* &amp;quot;wezterm-font/src/parser.rs&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                  ((&amp;quot;../../assets/fonts/JetBrains&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                   (string-append
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                    #$font-jetbrains-mono
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                    &amp;quot;/share/fonts/truetype/JetBrains&amp;quot;))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                  ((&amp;quot;../../assets/fonts/Roboto&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                   (string-append
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                    #$font-google-roboto
&lt;/span&gt;+                    &amp;quot;/share/fonts/truetype/Roboto&amp;quot;)))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
A rebuild confirms that the terminal still launches as before, only this time it's using fonts from Guix, not the ones bundled in the repository.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-it-s-vulkan-not-vulkan-t&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;it-s-vulkan-not-vulkan-t&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;4.7.&lt;/span&gt; It's Vulkan, not Vulkan't&lt;/h3&gt;
&lt;div id=&quot;text-4-7&quot; class=&quot;outline-text-3&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;
&lt;b&gt;Note from the future:&lt;/b&gt; Just like with &lt;code&gt;libEGL&lt;/code&gt;, this isn't the best way to ensure WezTerm (or any other Rust-based package) can see Vulkan and is only shown because this is what I stumbled upon while reading others' code and what any other complete beginner might reasonably reach out for without knowing better.
&lt;/p&gt;

&lt;p&gt;
In this case too, you can simply just instruct the linker, which both helps us get rid of &lt;code&gt;patchelf&lt;/code&gt; and any manual mucking with the load paths and is a lot more idiomatic.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
While the terminal is functional at this point, if we were to inspect the parent terminal where we started &lt;code&gt;wezterm&lt;/code&gt;, we might notice a suspicious error message:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;libEGL warning: pci id for fd 20: 10de:2f04, driver (null)

pci id for fd 21: 10de:2f04, driver (null)
kmsro: driver missing
libEGL warning: egl: failed to create dri2 screen
pci id for fd 21: 10de:2f04, driver (null)
kmsro: driver missing
libEGL warning: egl: failed to create dri2 screen
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This is in fact the other big issue foreshadowed two sections ago. While we have fixed the situation with &lt;code&gt;libEGL&lt;/code&gt;, there is another library we need to really get WezTerm working. This library is &lt;code&gt;libvulkan&lt;/code&gt;, also known as &lt;a href=&quot;https://en.wikipedia.org/wiki/Vulkan&quot;&gt;Vulkan&lt;/a&gt;, OpenGL's successor. WezTerm uses it to enable GPU hardware acceleration and, without it, we're left with an embarrassingly slow CPU-accelerated rendering pipeline, that even stuff as old as the original &lt;code&gt;xterm&lt;/code&gt; beat by magnitudes of speed.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.11&quot; href=&quot;#fn.11&quot; class=&quot;footref&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;p&gt;
However, this time around, unlike with EGL, there is no hard-coded filename to patch, no matter how hard we're looking. So the usual trick of rewriting won't work here. Instead, we are going to add &lt;code&gt;libvulkan.so&lt;/code&gt; to the library loader's search path in another way. Enter &lt;code&gt;patchelf&lt;/code&gt;, a handy tool that can manipulate the &lt;a href=&quot;https://en.wikipedia.org/wiki/Executable_and_Linkable_Format&quot;&gt;ELF header&lt;/a&gt; of a binary.
&lt;/p&gt;

&lt;p&gt;
ELF or &lt;i&gt;&amp;quot;Executable and Loadable Format&amp;quot;&lt;/i&gt; is the Unix solution to storing the metadata of executables. It is a small header that defines (among many other things, this is just a taste) what sort of machine this code is expected to run on (32-bit or 64-bit, x86 or ARM, etc.), what memory location our program code starts at, and most importantly for our purposes, the libraries linked to the executable.
&lt;/p&gt;

&lt;p&gt;
By changing this header table, we are able to slip in a reference to &lt;code&gt;libvulkan.so&lt;/code&gt;, without it ever being mentioned directly in the code:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-diff&quot;&gt;&lt;span class=&quot;org-diff-header&quot;&gt;diff --git a/gnu/packages/terminals.scm b/gnu/packages/terminals.scm
index 34758f238e..13e690e11f 100644
--- &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;a/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
+++ &lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;&lt;span class=&quot;org-diff-file-header&quot;&gt;b/gnu/packages/terminals.scm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;org-diff-header&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -65,6 +65,8 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-module (gnu packages terminals)&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;   #:use-module (gnu packages fcitx5)
   #:use-module (gnu packages version-control)
   #:use-module (gnu packages sqlite)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;  #:use-module (gnu packages elf)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;  #:use-module (gnu packages vulkan)
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;   #:use-module ((guix licenses) #:prefix license:)
   #:use-module (guix build-system cargo)
   #:use-module (guix build-system cmake)
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -1697,6 +1699,8 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public wezterm&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;                             libssh2
                             libgit2
                             sqlite
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                            patchelf
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                            vulkan-loader
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;                             `(,zstd &amp;quot;lib&amp;quot;)
                             mesa)))
       (build-system cargo-build-system)
&lt;/span&gt;&lt;span class=&quot;org-diff-hunk-header&quot;&gt;@@ -1717,6 +1721,12 @@&lt;/span&gt;&lt;span class=&quot;org-diff-function&quot;&gt; (define-public wezterm&lt;/span&gt;
&lt;span class=&quot;org-diff-context&quot;&gt;               (lambda _
                 (substitute* &amp;quot;./window/src/egl.rs&amp;quot;
                   ((&amp;quot;libEGL.so&amp;quot;) (string-append #$mesa &amp;quot;/lib/libEGL.so&amp;quot;)))))
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;            (add-before 'install 'patch-libvulkan-so
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;              (lambda* (#:key inputs #:allow-other-keys)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                (invoke &amp;quot;patchelf&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                        &amp;quot;--add-needed&amp;quot;
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                        (string-append #$vulkan-loader &amp;quot;/lib/libvulkan.so&amp;quot;)
&lt;/span&gt;&lt;span class=&quot;org-diff-indicator-added&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;org-diff-added&quot;&gt;                        &amp;quot;./target/release/wezterm-gui&amp;quot;)))
&lt;/span&gt;&lt;span class=&quot;org-diff-context&quot;&gt;             (replace 'install
              (lambda* (#:key inputs native-inputs #:allow-other-keys)
&lt;/span&gt;                ;; Binaries
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;invoke&lt;/code&gt;  as the name suggests is Guix's mechanism to call out into a different application during build time. Because we're inside a gexp, our code is ensured to only be actually executed when we're done with the building and just before the installation is attempted. Since we added &lt;code&gt;patchelf&lt;/code&gt; to our inputs, we don't have to worry about finding it, &lt;code&gt;invoke&lt;/code&gt; will sort it out for us.
&lt;/p&gt;

&lt;p&gt;
Another minor, yet important thing to note is that this time we used &lt;code&gt;add-before&lt;/code&gt; instead of &lt;code&gt;add-after&lt;/code&gt;. We don't really care when exactly &lt;code&gt;patchelf&lt;/code&gt; runs, only that it happens before the installation process is finished, as we'd first like to change the ELF header.
&lt;/p&gt;

&lt;p&gt;
With this step done, &lt;code&gt;libvulkan.so&lt;/code&gt; is finally visible to the library loader and the system can start using Vulkan to drive its graphics pipeline:
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-02-01-guix_packaging/vulkan.avif&quot; alt=&quot;vulkan.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 1: &lt;/span&gt;The different GPUs (real or emulated) WezTerm sees, now that Vulkan is enabled.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-a-short-aside-committing-is-hard&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;a-short-aside-committing-is-hard&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;4.8.&lt;/span&gt; A short aside: Committing is hard!&lt;/h3&gt;
&lt;div id=&quot;text-4-8&quot; class=&quot;outline-text-3&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 5: &lt;/span&gt;An example of the &amp;quot;ChangeLog&amp;quot; format.&lt;/label&gt;&lt;pre class=&quot;src src-change-log&quot;&gt;gnu: Add wezterm.

* gnu/packages/terminal.scm: (wezterm): New variable.
* gnu/packages/rust-crates.scm (lookup-cargo-inputs) [&lt;span class=&quot;org-change-log-conditionals&quot;&gt;wezterm&lt;/span&gt;]: New entry.
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This was a topic I wanted to mention, but couldn't really find a better place for, so I'll just stick it here before the epilogue.
&lt;/p&gt;

&lt;p&gt;
Guix, like most other GNU projects, follows the so-called &amp;quot;&lt;a href=&quot;https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html&quot;&gt;ChangeLog&lt;/a&gt;&amp;quot; style of commit messages. The idea is to have all contributors provide mostly consistent descriptions to their commits, based on a set of rules that makes understanding what exactly changed easier and in a sense &amp;quot;algorithmic&amp;quot;.
&lt;/p&gt;

&lt;p&gt;
You can basically go through a list of possible commit message formats and figure out what the current change fits most. Was it an addition, a modification, a deletion, a fix? After you nailed down the action, you can extract the exact file, section, and sometimes even subsection that was modified. And then there's still the &amp;quot;free form&amp;quot; part of the commit message, that elaborates on exactly what happened.
&lt;/p&gt;

&lt;p&gt;
Thing is, while this sounds excellent on paper, I found it really difficult to properly follow this style. With every commit I was double guessing myself whether what I'm writing is succinct enough and fits the style, or if I'm making subtle errors and just think it's correct because it looks &lt;i&gt;mostly&lt;/i&gt; fine.
&lt;/p&gt;

&lt;p&gt;
Unlike most other things in this article, I have no &amp;quot;and then it all clicked&amp;quot; moment to share here. I basically limped to the finish line, stealing turns of phrases from other contributors and then most of my commit messages disappeared when my submission was overhauled (see below). The rest that remained are such simple messages, that they don't really contain any of the actually iffy stuff, that confused me.
&lt;/p&gt;

&lt;p&gt;
Don't get me wrong, this is still better than total anarchy and I imagine with time you get a &amp;quot;feel&amp;quot; for how to do it right, but it sure as hell didn't inspire much confidence in me while I was working on this project.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-and-yet-some-stuff-is-still-better-left-to-the-professionals&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;and-yet-some-stuff-is-still-better-left-to-the-professionals&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;5.&lt;/span&gt; And yet, some stuff is still better left to the Professionals&lt;/h2&gt;
&lt;div id=&quot;text-5&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
So, at this point we have a pretty well-functioning package. In fact, what you're seeing above is 90% the same as what I've &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/6020&quot;&gt;submitted to guix/guix&lt;/a&gt;, with the high hopes, that the contributors there would accept it and I'd have a proper foot in the ecosystem.
&lt;/p&gt;

&lt;p&gt;
&lt;a id=&quot;reject-reasons&quot;&gt;&lt;/a&gt;The reality is both a little disappointing and, in a sense, very reassuring. As it turned out, my package definition was lacking in quite a few things:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Instead of handling all dependencies correctly, I relied on &lt;code&gt;(recurisve? #t)&lt;/code&gt;, which is considered an &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/6020#issuecomment-10315016&quot;&gt;anti-pattern&lt;/a&gt;. Instead, I should have unbundled all dependencies properly.&lt;/li&gt;
&lt;li&gt;I missed some bundled files that could've been deleted from the source folder, as they're completely unused during the compilation process.&lt;/li&gt;
&lt;li&gt;My &lt;code&gt;install&lt;/code&gt; phase didn't quite install all the files necessary. I accidentally left out stuff like shell completions and integrations.&lt;/li&gt;
&lt;li&gt;I didn't install the &lt;a href=&quot;https://en.wikipedia.org/wiki/Terminfo&quot;&gt;Terminfo&lt;/a&gt; files for WezTerm. This caused a lot of subtle errors with command line applications, that rely on these files to know how to manipulate the state of the terminal.&lt;/li&gt;
&lt;li&gt;The version string I came up with was incorrect both per the Guix rules and &lt;a href=&quot;https://github.com/wezterm/wezterm/blob/main/README-DISTRO-MAINTAINER.md&quot;&gt;WezTerm's own&lt;/a&gt;. The final version conforms to both.&lt;/li&gt;
&lt;li&gt;Turns out WezTerm reads a file called &lt;code&gt;.tag&lt;/code&gt; to figure its own version out. I didn't include this in my initial submission.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;
The way I patched &lt;code&gt;libEGL&lt;/code&gt; and &lt;code&gt;libvulkan&lt;/code&gt; are considered unidiomatic and can be replaced with a much simpler snippet, which just instructs Cargo to link these libraries as you'd usually do:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-scheme&quot;&gt;(setenv &lt;span class=&quot;org-string&quot;&gt;&amp;quot;RUSTFLAGS&amp;quot;&lt;/span&gt;
      (string-join
       '(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;-C&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;link-arg=-lEGL&amp;quot;&lt;/span&gt;
         &lt;span class=&quot;org-string&quot;&gt;&amp;quot;-C&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;link-arg=-lvulkan&amp;quot;&lt;/span&gt;)
       &lt;span class=&quot;org-string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;The description I came up with was not quite up to snuff either, due to some sensationalized words that I didn't manage to cut.&lt;/li&gt;
&lt;li&gt;And I had quite a few stylistic gaffes that weren't strictly wrong, but could be expressed much better. For a couple of examples:
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;I used &lt;code&gt;string-append&lt;/code&gt; to concatenate folders, even though Guile has a dedicated function for it called &lt;a href=&quot;https://www.gnu.org/software/guile/manual/html_node/File-System.html#:~:text=Scheme%20Procedure:-,in%2Dvicinity,-directory%20file&quot;&gt;in-vicinity&lt;/a&gt;.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.12&quot; href=&quot;#fn.12&quot; class=&quot;footref&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;I kept referencing packages directly in places where &lt;a href=&quot;https://codeberg.org/guix/guix/src/branch/master/guix/build/utils.scm#L686&quot;&gt;search-input-file&lt;/a&gt; would have sufficed.&lt;/li&gt;
&lt;li&gt;I destructured lists using &lt;code&gt;car&lt;/code&gt; and &lt;code&gt;cdr&lt;/code&gt;, when &lt;code&gt;match-lambda&lt;/code&gt; works much nicer for these purposes.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Yet, the story doesn't end on a sour note. I had three different contributors jump in and offer help and feedback, turning my package into one that truly deserves to be in the repository.
&lt;/p&gt;

&lt;p&gt;
Of these three people, I'd like to specifically call out and thank &lt;a href=&quot;https://codeberg.org/hako&quot;&gt;hako&lt;/a&gt;, who also happens to be the person running and developing &amp;quot;&lt;a href=&quot;https://guix.moe&quot;&gt;Guix Moe&lt;/a&gt;&amp;quot;, a project which includes a powerful build farm / mirror, and a hand-crafted, Nonguix-enabled &lt;a href=&quot;https://codeberg.org/hako/Testament&quot;&gt;LiveCD&lt;/a&gt;, which allows for much easier installation of Guix System.
&lt;/p&gt;

&lt;p&gt;
Hako jumped in fairly early after I opened the PR and practically revamped my code from the ground up. Obviously much of the code remains mine, but the remaining extensions and refactors made the whole thing a lot more readable and correct and, without their help, I don't think my package would have made it in or at least not nearly as easily. They also went the extra mile to include all of the recommended fonts for WezTerm, which further fixed some failing test-cases and issues.
&lt;/p&gt;

&lt;p&gt;
Being a software developer, I was already familiar with the experience of facing a code review, so having my PR thrown back for several days didn't feel too daunting. On the contrary, it was actually quite great to see the amount of care put into and attention given to my work. That's the beauty of free software and people acting out of genuine enthusiasm and willingness to help.
&lt;/p&gt;

&lt;p&gt;
I hope my experience might assuage some worries of potential packagers: &lt;b&gt;Your code doesn't have to be perfect!&lt;/b&gt; As long as you're willing to communicate and address change requests, you'll be fine and if things really are above your level, there will be others who will bear that load for you.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-conclusion&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;6.&lt;/span&gt; Conclusion&lt;/h2&gt;
&lt;div id=&quot;text-6&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
So ends about a week of coding around and finding out. As I happen to have &lt;i&gt;some&lt;/i&gt; Nix experience, the process wasn't nearly as harrowing as it might seem from a first glance, but Guix definitely has a couple of rough edges.
&lt;/p&gt;

&lt;p&gt;
One thing that I found a bit frustrating for example was how the manual can occasionally be outdated, causing people (like myself) who aren't &amp;quot;in the know&amp;quot; to commit mistakes that could've been easily avoided otherwise. For instance, &lt;a href=&quot;https://guix.gnu.org/manual/devel/en/html_node/Submitting-Patches.html#:~:text=Run%20guix%20style%20package%20to%20format%20the%20new%20package&quot;&gt;it stipulates&lt;/a&gt; that you should use &lt;code&gt;guix style&lt;/code&gt; to provide a consistent styling to your code. I've been following this advice and religiously formatting my code with each commit, only to be &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/6020#issuecomment-10344076&quot;&gt;told by hako&lt;/a&gt;, that I actually shouldn't use the formatter at all, because it cannot cope with complex code.
&lt;/p&gt;

&lt;p&gt;
Still, despite all this and even if Guix isn't quite as powerful from an infrastructure-perspective as, say, Nix, I believe the enthusiasm of its community still carries the experience hard and because of that, I came away from the experience with quite a positive impression while Guix came away with a working WezTerm package.
&lt;/p&gt;

&lt;p&gt;
If you're reading this, you're only a &lt;code&gt;guix pull&lt;/code&gt; away from being able to add &lt;code&gt;wezterm&lt;/code&gt; to your manifest or &lt;code&gt;guix install&lt;/code&gt; it and have it available on your system. If you happen to be a WezTerm user, I hope my package will serve you well.
&lt;/p&gt;

&lt;p&gt;
Thank you for sticking with me in this quite long post! Till next time!
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;footnotes&quot;&gt;
&lt;h2 class=&quot;footnotes&quot;&gt;Footnotes: &lt;/h2&gt;
&lt;div id=&quot;text-footnotes&quot;&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.1&quot; href=&quot;#fnr.1&quot; class=&quot;footnum&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
I was trying to play &lt;a href=&quot;https://store.steampowered.com/app/2012840/Portal_with_RTX/&quot;&gt;Portal RTX&lt;/a&gt; and I was a bit annoyed that even though both that and KDE support HDR, I still cannot make the two work due to Gamescope not being present on Guix System.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.2&quot; href=&quot;#fnr.2&quot; class=&quot;footnum&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
I'm being intentionally vague here. The actual trip graphical data takes to your monitor is convoluted and varies by several factors including driver, manufacturer, whether you're using Xorg or Wayland, etc. Because of that I'm not very comfortable talking about it in any authoritative way and so I won't. It doesn't really matter in the context of this blogpost anyway.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.3&quot; href=&quot;#fnr.3&quot; class=&quot;footnum&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Specifically &lt;code&gt;LD_PRELOAD&lt;/code&gt; adds libraries to the &lt;i&gt;front&lt;/i&gt; of the search path. This can be used to override libraries which can be used for things like injecting debug messages, using alternative memory allocation, etc.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
It's an extremely powerful tool and a great attack vector if you happen to load in a tainted library. Since we're working with Guix-packaged stuff, we're fine, but it's not something to use without care and good reason.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.4&quot; href=&quot;#fnr.4&quot; class=&quot;footnum&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
In case you're unused to declarative lingo, you can think of a &amp;quot;derivation&amp;quot; as a deterministic result from a build script. Usually this is a software package (like WezTerm's in this particular instance), but the system is flexible enough to be utilized for basically anything that results in files.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
For instance, the Guix home manager allows you to manage your dotfiles in a declarative way. The contents of the derivations built by Guix home are the files that are placed into your home folder including configuration files, scripts, and whatever else you may need.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.5&quot; href=&quot;#fnr.5&quot; class=&quot;footnum&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Specifically what this means is that by the time the build system is engaged, you're only allowed to access data you've specified either in your inputs or the &lt;code&gt;source&lt;/code&gt; of your package. The actual build itself happens inside a sealed off container without even network access. This is both for security reasons (a malicious package has a far smaller attack surface if it cannot talk to the cloud, nor even see your files) and to ensure reproducibility.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
While Guix is primarily known for its FSF/GNU affiliation, one of their major goals is to provide a system that can be bootstrapped from the ground up using nothing more than a tiny binary seed and a long-long chain of programs of increasing complexity. It is an utterly fascinating topic and if you find it as fascinating as I do, I really suggest giving &lt;a href=&quot;https://bootstrappable.org/&quot;&gt;this website&lt;/a&gt; a read.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.6&quot; href=&quot;#fnr.6&quot; class=&quot;footnum&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
It's completely unrelated to the packaging journey in the post, but I found it really interesting, that Guix had a &lt;a href=&quot;https://codeberg.org/guix/guix-consensus-documents/src/branch/main/003-rename-default-branch.md&quot;&gt;consensus vote&lt;/a&gt; about doing away with the &lt;a href=&quot;https://x.com/xpasky/status/1272280760280637441&quot;&gt;recently controversial&lt;/a&gt; &amp;quot;master&amp;quot; name for the base branch.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
It was nice to see the kind of pragmatism and forward thinking involved in this process: The maintainers weighed how much effort it'd take to update everyone from one branch to another, how difficult it'd be to undo, what other possible actions they could take, etc. Ultimately, the vote fell through in its infancy, due to not achieving majority support and master was kept.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
Whether or not you agree with the outcome, I hope you're similarly pleased to see that this project takes its democratic values seriously. If I happen to stick with Guix for a longer while, I hope to one day join the decision making myself, even if only as a minor voice in the choir.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.7&quot; href=&quot;#fnr.7&quot; class=&quot;footnum&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
If you happen to be following along or thinking of writing your own package, please don't forget about the &lt;code&gt;-j&lt;/code&gt; flag. It allows multiple parallel jobs to run and Guix's source is very well decoupled from each other. I have a 24-core Ryzen processor and it's a joy to see how fast &lt;code&gt;make -j24&lt;/code&gt; chews through the build process compared to the utter sluggishness of a single-core run.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.8&quot; href=&quot;#fnr.8&quot; class=&quot;footnum&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
It is worth noting that you have to get the package name right. When I first tried importing my crates, I didn't realize I had to use the final package's name and just entered &lt;code&gt;finl_unicode&lt;/code&gt;, since I assumed I have to provide the name of the crate I wanted to import.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
This ultimately sent me down a painful side-track that led nowhere until I finally read the docs better and realized I actually need to use &lt;code&gt;wezterm&lt;/code&gt;. Learn by my mistake!
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.9&quot; href=&quot;#fnr.9&quot; class=&quot;footnum&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
I'm actually not quite sure what's Guix's policy regarding tests. Some packages have them disabled, some enabled, some selectively disable some tests. In my variant of the package, I disabled them following other packages' example, but in the final variant, that other contributors helped out with, the tests are once again enabled.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.10&quot; href=&quot;#fnr.10&quot; class=&quot;footnum&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
There's also your personal profile's &lt;code&gt;bin&lt;/code&gt; and &lt;code&gt;sbin&lt;/code&gt; folders, &lt;code&gt;current-system&lt;/code&gt;'s &lt;code&gt;sbin&lt;/code&gt; folder, stuff managed by &lt;code&gt;guix home&lt;/code&gt;, etc.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.11&quot; href=&quot;#fnr.11&quot; class=&quot;footnum&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
I'm not 100% sure if Vulkan is needed on AMD hardware. I have a sneaking suspicion that the error is at least partly caused by the fact that on Guix System NVIDIA hardware requires a small &lt;a href=&quot;https://gitlab.com/nonguix/nonguix#:~:text=%25my%2Dos%29-,Application%20setup,-Application%20setup%20involves&quot;&gt;magic incantation&lt;/a&gt; to work with most packages. Still, whether the error is caused by the lack of Vulkan or not doesn't really matter, as the library is still a needed dependency of WezTerm, so adding it wasn't just for kicks.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.12&quot; href=&quot;#fnr.12&quot; class=&quot;footnum&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
In my defense &lt;code&gt;in-vicinity&lt;/code&gt; is a fairly horrible name for a function that joins paths. I mean, yeah, sure, it technically means what it does, but surely &lt;code&gt;path-append&lt;/code&gt; or &lt;code&gt;construct-path&lt;/code&gt; would've worked a lot better.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;/div&gt;</content></entry><entry><title>Guix System First Impressions as a Nix User</title><id>https://nemin.hu/guix.html</id><author><name>Nemin</name></author><updated>2026-01-26T18:09:00Z</updated><link href="https://nemin.hu/guix.html" rel="alternate" /><content type="html">&lt;div role=&quot;doc-toc&quot; id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div role=&quot;doc-toc&quot; id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#my-journey-to-guix-system&quot;&gt;1. My Journey to Guix System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#installer-impressions&quot;&gt;2. Installer Impressions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#i-can-t-find-my-way-land&quot;&gt;3. I Can't Find my Way-land&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sympathy-for-the-devil&quot;&gt;4. Sympathy for the Devil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#goals&quot;&gt;5. Goals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#results&quot;&gt;6. Results&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-good&quot;&gt;6.1. The Good&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-ambiguous&quot;&gt;6.2. The Ambiguous&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-bad&quot;&gt;6.3. The Bad&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#overall&quot;&gt;7. Overall&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#notes&quot;&gt;8. Notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-my-journey-to-guix-system&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;my-journey-to-guix-system&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;1.&lt;/span&gt; My Journey to Guix System&lt;/h2&gt;
&lt;div id=&quot;text-1&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
Feel free to skip this section if you don't really care about backstories. I just figured it makes sense to recap how and why one might start having an interest in declarative distros before tackling the main topic.
&lt;/p&gt;

&lt;p&gt;
I've been a Linux-only&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.1&quot; href=&quot;#fn.1&quot; class=&quot;footref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; user for about ten years now and, like many others, I too embarked on the arduous journey of distro-hopping. I started with &lt;a href=&quot;https://www.linuxmint.com/&quot;&gt;Mint&lt;/a&gt; and when that felt too slow, I switched to &lt;a href=&quot;https://ubuntu.com/&quot;&gt;Ubuntu&lt;/a&gt;. When Ubuntu felt too handholdy&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.2&quot; href=&quot;#fn.2&quot; class=&quot;footref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, I switched to &lt;a href=&quot;https://archlinux.org&quot;&gt;Arch&lt;/a&gt;, which proved to be my main driver for well over five or so years. And when I couldn't resist the Siren's call, I moved on to &lt;a href=&quot;https://gentoo.org&quot;&gt;Gentoo&lt;/a&gt;, thinking surely &amp;quot;harder is better&amp;quot;. Which resulted in severe burnout in a few months, so I capitulated and switched to &lt;a href=&quot;https://fedoraproject.org/&quot;&gt;Fedora&lt;/a&gt;, which was very stable and honestly an all around excellent system. But once more, my interest was piqued, and (before today's adventure) I finally switched to &lt;a href=&quot;https://nixos.org/&quot;&gt;NixOS&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
I've always had a passing interest towards Nix ever since I've first heard about it, but until fairly recently, I always dismissed it as a tool for DevOps guys. The syntax was &lt;i&gt;weird&lt;/i&gt;, the need for reproducible environments seemingly irrelevant, and stuff like the oft-recommended &lt;a href=&quot;https://nixos.org/guides/nix-pills/&quot;&gt;Nix Pills&lt;/a&gt; seemed anything but newbie-friendly.
&lt;/p&gt;

&lt;p&gt;
So then why would someone like me, who's so adamant about not needing Nix eventually choose to go all-in? I guess it was at first less about Nix being better and just the rest being worse.
&lt;/p&gt;

&lt;p&gt;
Of the two big reasons for the switch, one was that I realized that having per-directory environments for your projects is actually a very handy thing to do when you like to toy around with many technologies. I used to generate my &lt;a href=&quot;https://oddwords.hu&quot;&gt;other blog&lt;/a&gt; using &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; and, no matter which distro I used, it was always a pain in the neck to have a good Ruby environment set up. &lt;code&gt;bundler install&lt;/code&gt; didn't really want to work without privileges and I wasn't really a fan of unleashing &lt;code&gt;sudo&lt;/code&gt; on it, but usually that was the only way I could get things to work.
&lt;/p&gt;

&lt;p&gt;
With Nix, however, it was a matter of just describing a few packages in a shell and boom, Ruby in one folder, no Ruby (and thus no mess) everywhere else. &lt;b&gt;I was hooked!&lt;/b&gt; I started adding &lt;code&gt;shell.nix&lt;/code&gt; files to all my little projects, hell, I started planning projects by first adding a &lt;code&gt;shell.nix&lt;/code&gt; with all the dependencies I would reasonably need.
&lt;/p&gt;

&lt;p&gt;
The other reason, which ultimately cemented that I need to commit, was that I was getting tired of my installed packages slowly drifting out of control. Sure, every package manager has some method of listing what's installed, but these are usually cumbersome and completely ephemeral (in the sense that any listing becomes invalid the moment you change anything).
&lt;/p&gt;

&lt;p&gt;
With NixOS, the equation is flipped on its head: No longer did I query the system to tell me what's installed and what's not, it was now the system that worked based on files that I edit. The difference sounds small on paper, but for me it was an extremely liberating feeling to know that I could edit my system configuration in a versionable, explicit, and centralized way.
&lt;/p&gt;

&lt;p&gt;
But NixOS isn't the only declarative distro out there. In fact GNU forked Nix fairly early and made their own spin called &lt;a href=&quot;https://guix.gnu.org/&quot;&gt;Guix&lt;/a&gt;, whose big innovation is that, instead of using the unwieldy Nix-language, it uses Scheme. Specifically &lt;a href=&quot;https://www.gnu.org/software/guile/&quot;&gt;Guile Scheme&lt;/a&gt;, GNU's sanctioned configuration language. I've been following Guix for a bit, but it never felt quite ready to me with stuff like KDE being only barely supported and a lot of hardware not working out of the box.
&lt;/p&gt;

&lt;p&gt;
However, now that (after three years) Guix announced its &lt;a href=&quot;https://guix.gnu.org/en/blog/2026/gnu-guix-1.5.0-released/&quot;&gt;1.5.0 release&lt;/a&gt; with a lot of stuff stabilized and KDE finally a first-party citizen, I figured now is the best time to give it a fresh shot. This post captures my experiences from installation to the first 3-4 days.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-installer-impressions&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;installer-impressions&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;2.&lt;/span&gt; Installer Impressions&lt;/h2&gt;
&lt;div id=&quot;text-2&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
Plug your USB in, &lt;code&gt;dd&lt;/code&gt; the file onto the drive, reboot, nothing unusual. If you've ever installed a Linux system, it's more of the same.
&lt;/p&gt;

&lt;p&gt;
After selecting the pendrive in my BIOS settings, the monitor began to glow in a deep, radiant blue as the Guix System logo appeared on my screen… only to suddenly switch to a menacing red: My CPU's integrated GPU is not supported by free firmware. A helpful popup gave me a gentle nudge about picking free hardware next time (buddy, have you seen the PC part prices these days?) and off I went into the installer proper.
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-26-guix/installer_partitions.avif&quot; alt=&quot;installer_partitions.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 1: &lt;/span&gt;Picture of the installer graciously borrowed from the Guix installer manual.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
The installer itself is refreshingly barebones and I mean this in a positive way. It asks all the necessary questions and provides a nice basic configuration file, all done in a retro &lt;a href=&quot;https://guix.gnu.org/manual/1.5.0/en/html_node/Guided-Graphical-Installation.html&quot;&gt;Ncurses-based TUI&lt;/a&gt;. I was really happy to see that, unlike my last attempt at using Guix System in the early 2020-s, KDE Plasma is now a first-party choice during installation. I never really vibed too much with GNOME and the other options didn't appeal either, so the choice was obvious.
&lt;/p&gt;

&lt;p&gt;
Now, I'm not sure if I just picked the worst possible time or if the Guix servers were facing unusual load or whatever may have happened, but after such a breeze of a setup, the moment I pressed install, my PC became unusable for the next 2.5 &lt;b&gt;hours.&lt;/b&gt; Which is unacceptable for an installation process these days in my opinion. I am lucky enough to live in a household with fiber-optic internet, that merely shrugs at bandwidth of up to a gigabit per second and yet nearly all packages downloaded with a whopping 50 &lt;i&gt;kilobit&lt;/i&gt; per second, meaning even small-ish 5-10 megabyte packages took long minutes to download.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.3&quot; href=&quot;#fn.3&quot; class=&quot;footref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;p&gt;
A reboot later my issues only got worse.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-i-can-t-find-my-way-land&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;i-can-t-find-my-way-land&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;3.&lt;/span&gt; I Can't Find my Way-land&lt;/h2&gt;
&lt;div id=&quot;text-3&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
I was assuming I'd get SDDM after having chosen KDE Plasma, but (what a later, &lt;a href=&quot;https://guix.gnu.org/manual/devel/en/html_node/X-Window.html#:~:text=by%20default%20the%20GNOME%20Display%20Manager%20(GDM).&quot;&gt;closer read&lt;/a&gt; of the manual made me realize is the expected outcome for a default config) it was GDM that loaded in. I entered my name and password, and I was greeted with the familiar Plasma 6 spinner. The first hint that something might be off was that it loaded a bit longer than usual, but I was not going to get mad at waiting 10 seconds instead of 3. After all, I did just wait magnitudes longer to get here.
&lt;/p&gt;

&lt;p&gt;
With practically nothing installed beyond the very basics, I clicked on Konsole, hoping to start prodding around my config and add some of my day to day apps. To my horror, it opened in the top left corner, without a titlebar and without any borders. What's more, no matter what I did, I couldn't move it. It also didn't show up on the menu bar, despite the application launcher still being completely usable. At this point I was fairly exhausted by these antics, but I figured,
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Well, it's a brand new release, perhaps this just snuck in. Let's give updating a shot and see if that helps.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
So I issued &lt;code&gt;guix pull&lt;/code&gt;… The download whizzed by with speed quite unexpected after what I experienced with the installer… Only to crash into the brick wall that's indexing. Okay, whatever, another 10-12 minutes down the drain, at least now I have newest version.
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-26-guix/downloads.avif&quot; alt=&quot;downloads.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 2: &lt;/span&gt;Better than before download speeds&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Except I didn't. Because, unlike Nix, the &lt;code&gt;guix&lt;/code&gt; executable is not an omnipresent, unique thing that anyone and everyone uses on your PC. Not only does every user have their own instance, if you don't issue a certain set of commands, you won't start using the new version, despite updating it.
&lt;/p&gt;

&lt;p&gt;
To Guix's credit, the CLI does scream at you to update your environment or else you'll keep using the old version, but I still find this system very disorientating compared to Nix. I'm certain experienced Guixheads are long past being tripped up by this sort of stuff and might even struggle to remember that there was a time they had to do these special steps too, but as a new user it felt a bit rough, especially consdering this is Guix System, i.e. the system whose whole purpose is to be integrate Guix as much as it can.
&lt;/p&gt;

&lt;p&gt;
Back to our issue at hand. I issued &lt;code&gt;sudo -s&lt;/code&gt; and &lt;code&gt;guix pull&lt;/code&gt;-ed again. Once more 10-12 minutes passed indexing. But at least I could finally call &lt;code&gt;guix system reconfigure /etc/config.scm&lt;/code&gt;. Interestingly things are much faster this time around, I saw speeds up to 30-50 Mbps. Before long the system was updated to the newest commit and I rebooted with high hopes.
&lt;/p&gt;

&lt;p&gt;
High hopes, that were immediately dashed when Plasma loaded in the same messed up way. At this point I started to suspect this might be an issue with the GPU driver, so I enabled the LXQT desktop environment and rebooted once more. Thankfully that one worked like a charm and I was able to boot up both Emacs (editing Scheme with GNU Nano is a pain I do not wish on anyone) and &lt;a href=&quot;https://librewolf.net/&quot;&gt;LibreWolf&lt;/a&gt; (Firefox's de-Mozilla-d variant).
&lt;/p&gt;

&lt;p&gt;
Not having found anything too useful in the docs, I decided to make my problem someone else's so I fired up ERC&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.4&quot; href=&quot;#fn.4&quot; class=&quot;footref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; and connected to Libera.chat's &lt;code&gt;#guix&lt;/code&gt; channel. After around half an hour of wait, a user by the name of Rutherther stepped up and offered me some help. We were able to figure it out that Nouveau wasn't able to drive my GPU (an RTX 5070), so his recommendation was that I should try booting with &lt;code&gt;nomodeset&lt;/code&gt;. I did, but it sadly didn't help much either.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-sympathy-for-the-devil&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;sympathy-for-the-devil&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;4.&lt;/span&gt; Sympathy for the Devil&lt;/h2&gt;
&lt;div id=&quot;text-4&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
At this point I was out of ideas. Ideas of solving this using pure-Guix System, that is. There was still one option I wanted to avoid as long as I could, but alas, it seemed like the only option, that still had a realistic chance of working.
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-26-guix/logo-screen-light.svg&quot; class=&quot;org-svg&quot; alt=&quot;logo-screen-light.svg&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 3: &lt;/span&gt;Nonguix's official logo, self-described to be &lt;a href=&quot;https://gitlab.com/nonguix/nonguix/-/issues/317&quot;&gt;&amp;quot;dark and evil&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Enter &lt;a href=&quot;https://gitlab.com/nonguix/nonguix&quot;&gt;Nonguix&lt;/a&gt;, the Mr. Hyde to Guix's Dr. Jekyll, the shady guy who offers you a hit and first time's for free, the… Erm, in a nutshell, it's the repository for non-free applications and drivers packages for Guix System, basically. Interestingly enough, by Guix's own findings &lt;a href=&quot;https://guix.gnu.org/en/blog/2025/guix-user-and-contributor-survey-2024-the-results-part-2/&quot;&gt;about 64% of users&lt;/a&gt; utilize the Nonguix channel, which is perhaps not &amp;quot;literally everyone&amp;quot;, but it does paint a picture that there is still stuff out there that you simply cannot replace with FOSS software yet.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 1: &lt;/span&gt;At the time of writing, this is all one has to do to enable Nonguix.&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;&lt;span class=&quot;linenr&quot;&gt; 1: &lt;/span&gt;(cons* (channel
&lt;span class=&quot;linenr&quot;&gt; 2: &lt;/span&gt;      (name 'nonguix)
&lt;span class=&quot;linenr&quot;&gt; 3: &lt;/span&gt;      (url &lt;span class=&quot;org-string&quot;&gt;&amp;quot;https://gitlab.com/nonguix/nonguix&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;linenr&quot;&gt; 4: &lt;/span&gt;      &lt;span class=&quot;org-comment-delimiter&quot;&gt;;; &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;Enable signature verification:
&lt;span class=&quot;linenr&quot;&gt; 5: &lt;/span&gt;&lt;/span&gt;      (introduction
&lt;span class=&quot;linenr&quot;&gt; 6: &lt;/span&gt;       (make-channel-introduction
&lt;span class=&quot;linenr&quot;&gt; 7: &lt;/span&gt;        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;897c1a470da759236cc11798f4e0a5f7d4d59fbc&amp;quot;&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt; 8: &lt;/span&gt;        (openpgp-fingerprint
&lt;span class=&quot;linenr&quot;&gt; 9: &lt;/span&gt;         &lt;span class=&quot;org-string&quot;&gt;&amp;quot;2A39 3FFF 68F4 EF7A 3D29  12AF 6F51 20A0 22FB B2D5&amp;quot;&lt;/span&gt;))))
&lt;span class=&quot;linenr&quot;&gt;10: &lt;/span&gt;     %default-channels)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Enabling the repo wasn't exactly difficult. You just paste the short excerpt from above (also found in the README) into your &lt;code&gt;~/.config/guix/channels.scm&lt;/code&gt;, &lt;code&gt;guix pull&lt;/code&gt;, let it index to its heart's content again, and then you have access to all that is nasty (yet occasionally useful) in the world.
&lt;/p&gt;

&lt;p&gt;
I figured perhaps if &lt;a href=&quot;https://www.fsfla.org/ikiwiki/selibre/linux-libre/&quot;&gt;Linux-libre&lt;/a&gt; and its free firmware couldn't deal with my GPU, then surely Linux proper with its binary blobs could. Hell, for good measure I threw in the NVIDIA transform, which is supposed to automagically translate all dependencies to use the proprietary drivers.
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-26-guix/nvidia_first_shot_crash.avif&quot; alt=&quot;nvidia_first_shot_crash.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 4: &lt;/span&gt;What haste and half-reading manuals gets you…&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Turns out my eagerness was a mistake. Not only did the process take yet another half an hour (if not more, I stopped counting), upon reboot all I was met with was a kernel panic about the driver not being able to cope with the GPU it found and a massive spew of FSCK logs.
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-26-guix/fsck.avif&quot; alt=&quot;fsck.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 5: &lt;/span&gt;'FSCK' was indeed very close to the first words that came to my mind at this moment.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
With no better ideas in mind, I took out my pendrive again and burned Nonguix's own pre-built ISO on it using my partner's PC. While it ultimately did get me a working system, this version has three unfortunate hindrances:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;It was built in 2022, far before Guix's &lt;a href=&quot;https://guix.gnu.org/blog/2025/migrating-to-codeberg/&quot;&gt;migration to Codeberg&lt;/a&gt;, meaning it still attempts to pull content from the unfathomably slow &lt;a href=&quot;https://savannah.gnu.org/&quot;&gt;GNU Savannah&lt;/a&gt; mirror. I had to manually override my &lt;code&gt;channels.scm&lt;/code&gt; to point at the Codeberg repo instead, but with no easy means of finding its &amp;quot;&lt;a href=&quot;https://guix.gnu.org/manual/1.5.0/en/html_node/Channel-Authentication.html&quot;&gt;channel introduction&lt;/a&gt;&amp;quot;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.5&quot; href=&quot;#fn.5&quot; class=&quot;footref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, I had to pass in &lt;code&gt;--disable-authentication&lt;/code&gt; to Guix when updating my system. A bit scary, but I trust the Codeberg repo.&lt;/li&gt;
&lt;li&gt;Because of its age, I got a lot of somewhat intimidating errors about hardware not being recognized and other stuff I couldn't even decipher, but ultimately the system booted to the installer without issue.&lt;/li&gt;
&lt;li&gt;For some reason while the installer itself does include Nonguix stuff, it actually does not include the repo in the resulting channels files, nor the substitution server for the project. The README has a warning about this, but if you happen to miss it, you could accidentally install a non-Nonguix Guix System (say that three times fast).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
None of these were particularly hard to fix, however, and soon enough I was back where I started. That is to say, in a &lt;code&gt;nomodeset&lt;/code&gt; X11 session, except this time running &lt;a href=&quot;https://i3wm.org/&quot;&gt;i3&lt;/a&gt;, as LXQT wasn't an available option on an installer this old. There was certainly a bit of a hacker-ish vibe to messing with code files in an environment like that, but I was honestly much more looking forward to finally having a usable desktop.
&lt;/p&gt;

&lt;p&gt;
Having learned from my hastiness, this time I was smarter. I only enabled the full kernel and firmware blobs, without going anywhere near the NVIDIA transform. I issued another &lt;code&gt;guix system reconfigure&lt;/code&gt; and, after having time for another tea session, my update was finally finished.
&lt;/p&gt;

&lt;p&gt;
I rebooted with tentative nervousness and… Success? Huh.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-goals&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;goals&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;5.&lt;/span&gt; Goals&lt;/h2&gt;
&lt;div id=&quot;text-5&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
Obviously there is little point in throwing Guix System on my PC and declaring success. I wanted to be able to at least reproduce the kind of workflow I'm used to using NixOS. For that, I need the following:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;A browser:&lt;/b&gt; preferably Firefox, as I'm not a huge fan of Chrome / Chromium,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;An E-mail client:&lt;/b&gt; preferably Thunderbird,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;A basic office suite:&lt;/b&gt; preferably LibreOffice,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dev environments:&lt;/b&gt; for Rust, Zig, Scheme, and TypeScript (with the option for more, if possible),&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Emacs:&lt;/b&gt; I do almost all my text editing in it these days, falling back to Neovim for quick tasks,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Discord:&lt;/b&gt; for chatting with friends,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Telegram:&lt;/b&gt; for chatting with family,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Steam:&lt;/b&gt; for the very rare occasions I want to game,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NVIDIA drivers:&lt;/b&gt; I prefer to offload day-to-day usage to my CPU's integrated GPU, as it cuts my energy usage in half.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Of these it was obvious that two would be relatively hard and one &amp;quot;outright impossible&amp;quot;. The two being Steam and the drivers (as both are non-free and thus not in Guix's default repos) and the &amp;quot;impossible&amp;quot; one being Discord (which not even the non-free repo has packaged). But I was ready to compromise a little bit since I am requesting stuff that's explicitly against Guix's goals.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-results&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;results&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;6.&lt;/span&gt; Results&lt;/h2&gt;
&lt;div id=&quot;text-6&quot; class=&quot;outline-text-2&quot;&gt;

&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-26-guix/desktop.avif&quot; alt=&quot;desktop.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 6: &lt;/span&gt;My desktop running Wezterm packaged by me and Emacs.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
While there has been occasional bumps and hitches along the ride, I must say I'm very impressed with Guix System so far. Let's go through this list in order:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Browser:&lt;/b&gt; So far I'm really enjoying LibreWolf. It feels a lot snappier than Firefox and I'm really baffled how much speed I was apparently missing out on.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;E-mails:&lt;/b&gt; I installed Icedove, which is basically just Thunderbird without Mozilla branding. It works as expected.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Office suite:&lt;/b&gt; LibreOffice is available as expected. Not much to say about it. I guess it's interesting that Guix isn't following the usual &lt;code&gt;-stale&lt;/code&gt; / &lt;code&gt;-fresh&lt;/code&gt; packaging schema, but I don't really mind not having cutting edge versions of an office suite :)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dev environments:&lt;/b&gt; I've only briefly toyed with development environments so far, but to me it seems like for simple use-cases it might be even easier to use than &lt;code&gt;shell.nix&lt;/code&gt; (you don't need any sort of ceremony, just a &lt;code&gt;manifest.scm&lt;/code&gt; file with a &lt;code&gt;(specifications-&amp;gt;manifest &amp;lt;list of packages&amp;gt;)&lt;/code&gt; form inside and you have a dev env ready to go.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Emacs:&lt;/b&gt; Installed just fine. I had to install &lt;code&gt;emacs-vterm&lt;/code&gt; to make &lt;a href=&quot;https://github.com/akermu/emacs-libvterm&quot;&gt;Vterm&lt;/a&gt; work, but all that took was the very simple process of adding the library to my home configuration and then referencing it in my Emacs config as per this &lt;a href=&quot;https://reddit.com/r/GUIX/comments/11gzhyu/how_to_compile_the_vterm_module_from_emacs_and/&quot;&gt;Reddit post&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Discord:&lt;/b&gt; I decided to just use Discord's browser version, which works just as fine (if not better). It's trading a tiny bit of convenience in return for not having to figure out how to manually add a package for it from some random third-party source. From what I've read elsewhere Flatpak is also an option, but I prefer having just one package manager at a time.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Steam:&lt;/b&gt; Installed shockingly easily. I have to really give props to the Nonguix team. I tested Portal 2 with the Nouveau driver, it is a little disheartening to see a 15 years old game&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.6&quot; href=&quot;#fn.6&quot; class=&quot;footref&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; lag, but I understand the people's hands are tied when it comes to the free drivers. After I managed to install the proprietary drivers, I was able to play even Portal RTX, which is something I never managed to get to work using NixOS.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NVIDIA drivers:&lt;/b&gt; This time I actually read the docs properly and it didn't take long for me to realize the initial problem that caused my previous install to be unbootable was of course found between the chair and keyboard. This time, after making sure I enabled the open drivers and kernel mode-setting, I crossed my fingers, issued a reconfigure and it works beautifully!&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-the-good&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;the-good&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;6.1.&lt;/span&gt; The Good&lt;/h3&gt;
&lt;div id=&quot;text-6-1&quot; class=&quot;outline-text-3&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;p&gt;
&lt;b&gt;Helpful community:&lt;/b&gt; While I do feel like Guix's community could be much larger (see below), the one that exists is very helpful and nice from my limited experience. In all places I've looked so far (Libera's &lt;code&gt;#guix&lt;/code&gt;, /r/Guix, and the guix/guix Codeberg repository) I was met with genuinely kind and helpful people.
&lt;/p&gt;

&lt;p&gt;
That is not to say I haven't seen some bad eggs, especially in posts from years ago, but I don't think there is any community without those, so I'm not going to cite this as a negative.
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Home configuration:&lt;/b&gt; Having &lt;code&gt;guix home&lt;/code&gt; be a built-in, first class citizen, instead of a community made &amp;quot;extension&amp;quot; is excellent. Instead of needing to consult a third-party resource like Home Manager's &lt;a href=&quot;https://nix-community.github.io/home-manager/&quot;&gt;documentation&lt;/a&gt; you can simply use what you already know about Guix and, if you happen to hit a wall, you can just read the &lt;a href=&quot;https://guix.gnu.org/manual/1.5.0/en/html_node/Home-Configuration.html&quot;&gt;official handbook&lt;/a&gt; which is guaranteed to always stay up to date with the rest of the system.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Package availability:&lt;/b&gt; As long as you largely use FOSS stuff (which is much easier than one might think), the amount of choice is awesome. I could basically just copy over the list of packages from my Nix config and practically everything had an equivalent.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;
&lt;b&gt;Scheme:&lt;/b&gt; I'm not really a seasoned Schemer, but I have dabbled in the language previously and it feels so much better to me than Nix (the language) ever did. One great benefit of this is that it's a lot easier to start digging into package definitions to figure things out for yourself.
&lt;/p&gt;

&lt;p&gt;
This is &amp;quot;&lt;a href=&quot;https://www.gnu.org/philosophy/free-sw.html#make-changes&quot;&gt;Freedom 1&lt;/a&gt;&amp;quot; of GNU's Four Essential Freedoms in effect. Since the code is pretty much just Scheme and the different mechanisms available are fairly well documented (see caveat below), the barrier to entry is much lower than with Nix in my opinion.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.7&quot; href=&quot;#fn.7&quot; class=&quot;footref&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;p&gt;
Another nice benefit of this is that you can use Emacs' extensive Scheme support to help your configuration. Tools like &lt;a href=&quot;https://github.com/emacsmirror/geiser&quot;&gt;Geiser&lt;/a&gt; can plug right into Guix and help you find package and function names and, once you're experienced enough, debug your config/packages on the fly. I personally haven't yet achieved mastery of such level yet, but having the REPL confirm if I've entered names in correctly before running the code is already a boon.
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;
&lt;b&gt;Ease of hacking:&lt;/b&gt; In the &amp;quot;to tinker on&amp;quot; sense, rather than &amp;quot;being insecure&amp;quot;. With Nix, merely pulling in Nixpkgs is an effort, due to the repository being massive. My otherwise beefy machine struggled to switch between branches and make commits, which doesn't exactly inspire confidence in contributing, even though it was otherwise something I was excited to do. Meanwhile, with Guix I was able to get a fully functioning development environment in 15 minutes tops, which includes cloning the repo, authenticating all commits, generating bytecode for the entire repository, and getting Emacs set up to work nice with the codebase.
&lt;/p&gt;

&lt;p&gt;
Not to mention, at the time of writing &lt;a href=&quot;https://github.com/NixOS/nixpkgs/pull/453219&quot;&gt;my Nixpkgs PR of guile-colorized&lt;/a&gt; is still not accepted, despite being open since October, 2025. Which is kind of disheartening, when the package is really trivial and has a very low blast-radius. With Guix I &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/5962&quot;&gt;got an answer&lt;/a&gt; to an extremely noobish question on my first PR in mere hours.
&lt;/p&gt;

&lt;p&gt;
On a separate, but related note, I also found it a lot easier to test my package in a &amp;quot;live&amp;quot; environment as &lt;code&gt;guix pull&lt;/code&gt; supports a parameter called &lt;code&gt;--url&lt;/code&gt; which you can easily point to a folder on your own PC. So once I was confident my code should work, I could just &amp;quot;check out&amp;quot; my local repository clone and build it like I was an end user. This let me make sure it really does work.
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-the-ambiguous&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;the-ambiguous&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;6.2.&lt;/span&gt; The Ambiguous&lt;/h3&gt;
&lt;div id=&quot;text-6-2&quot; class=&quot;outline-text-3&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;p&gt;
&lt;b&gt;Search:&lt;/b&gt; &lt;code&gt;guix search&lt;/code&gt; not taking an extra parameter like &lt;code&gt;nix search&lt;/code&gt; is both very convenient and a bit of a bummer.
&lt;/p&gt;

&lt;p&gt;
Its absence is not a deal breaker, but I really loved how with Nix, you could search in &lt;i&gt;anything&lt;/i&gt;, that has a flake. Be that Nixpkgs, a repo you downloaded, a repo that's on a git forge, etc. I remember being awestruck that I could just do &lt;code&gt;nix search github:mozilla/nixpkgs-mozilla&lt;/code&gt; and search for their builds of Firefox without having to manually check out anything.
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;
&lt;b&gt;The documentation:&lt;/b&gt; Oof, this one is a bit hard to pass definite judgment on.
&lt;/p&gt;

&lt;p&gt;
On one hand I love the thoroughness of it all. You can get a fairly decent idea of what Guix, what it can do for your, how to use it, and how to extend it, just by reading the manual. It is evident that the Guix team and GNU in general takes its mission to educate using free software very seriously. Stuff like the &lt;a href=&quot;https://guix.gnu.org/cookbook/en/html_node/Packaging-Tutorial.html&quot;&gt;Packaging tutorial&lt;/a&gt; make it very easy for complete beginners to hack together package definitions without needing to consult any other resource.
&lt;/p&gt;

&lt;p&gt;
On the other hand, it really is just a manual, not a tutorial. What I mean by this is that concepts that could belong together aren't placed near each other. A simple example would be &lt;a href=&quot;https://guix.gnu.org/manual/1.5.0/en/html_node/Services.html&quot;&gt;services&lt;/a&gt; and &lt;a href=&quot;https://guix.gnu.org/manual/1.5.0/en/html_node/Service-Reference.html#:~:text=modify-services%20services&quot;&gt;customizing them&lt;/a&gt;. Assuming, you're in one of the sub-pages of Services and you suddenly realize you want to replace/modify one of the services, you are left completely clueless how that works. You have to go to a completely different chapter and find one particular function's description and then apply what you learn there. The &lt;a href=&quot;https://guix.gnu.org/cookbook/en/guix-cookbook.html&quot;&gt;Guix Cookbook&lt;/a&gt; has some examples, but you have to know about the cookbook in the first place.
&lt;/p&gt;

&lt;p&gt;
And before anyone misunderstands me, I'm fine with RTFM, but in my opinion one of the preconditions of mass-appeal is having &amp;quot;pre-chewed&amp;quot; solutions for common problems, that don't require perusing multiple chapters.
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-the-bad&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;the-bad&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;6.3.&lt;/span&gt; The Bad&lt;/h3&gt;
&lt;div id=&quot;text-6-3&quot; class=&quot;outline-text-3&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Substitute server stability:&lt;/b&gt; I imagine this is an issue that only a massive bag of money could fix, but the CI/CD servers could definitely use some more processing power. It's really annoying when you're trying to test something and you're suddenly forced to wait 10-15 minutes because the server can only spare 50-100 kbps for you.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;
&lt;b&gt;Content out there:&lt;/b&gt; Clearly this isn't the Guix team's fault (and it's something I'm trying to lessen with this post, even if just a tiny bit), but it's really hard to find good quality material when it comes to Guix.
&lt;/p&gt;

&lt;p&gt;
I mean, sure, there is the excellent &lt;a href=&quot;https://systemcrafters.net/craft-your-system-with-guix/&quot;&gt;System Crafters&lt;/a&gt; tutorial series, and the odd gems like &lt;a href=&quot;https://dthompson.us/posts/guix-for-development.html&quot;&gt;DThompson's dev env tutorial&lt;/a&gt;, but as a whole you're largely left to your own to trawl through the manual, IRC logs, Reddit threads, Codeberg and the previous issue tracker, etc. It's not an impossible task, especially if you're used to doing Linux things &amp;quot;the hard way&amp;quot;, but it's certainly a far cry from such one-stop shops as &lt;a href=&quot;https://nixos-and-flakes.thiscute.world/nixos-with-flakes/introduction-to-flakes&quot;&gt;the Nix Flakes book&lt;/a&gt; or &lt;a href=&quot;https://mhwombat.codeberg.page/nix-book/&quot;&gt;Wombat's Book of Nix&lt;/a&gt;.
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Guix's own build speed:&lt;/b&gt; Nix excels in speed, so I was hoping Guix would be the same. Yet stuff like &lt;code&gt;guix pull&lt;/code&gt; really bog things down. Doubly so, if you want to update not just your own &lt;code&gt;guix&lt;/code&gt; instance, but also root's.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Clarity of commands:&lt;/b&gt; The fact that all concerns are lumped together (unlike Nix's many utilities) means that to the new user the many commands such as &lt;code&gt;guix pull&lt;/code&gt;, &lt;code&gt;guix {system, home} reconfigure&lt;/code&gt;, &lt;code&gt;guix update&lt;/code&gt; can easily feel overwhelming and unclear what's updating/changing what. With time I'm sure you obtain a sort of mental muscle memory and you never think about it again, but starting out it's definitely a confusing part.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-overall&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;overall&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;7.&lt;/span&gt; Overall&lt;/h2&gt;
&lt;div id=&quot;text-7&quot; class=&quot;outline-text-2&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;label class=&quot;org-src-name&quot;&gt;&lt;span class=&quot;listing-number&quot;&gt;Listing 2: &lt;/span&gt;My current, fairly barebones Guix home config.&lt;/label&gt;&lt;pre class=&quot;src src-scheme&quot;&gt;&lt;span class=&quot;linenr&quot;&gt; 1: &lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;define-module&lt;/span&gt; (&lt;span class=&quot;org-type&quot;&gt;guix-home-config&lt;/span&gt;)
&lt;span class=&quot;linenr&quot;&gt; 2: &lt;/span&gt;  &lt;span class=&quot;org-builtin&quot;&gt;#:use-module&lt;/span&gt; (nongnu packages)
&lt;span class=&quot;linenr&quot;&gt; 3: &lt;/span&gt;  &lt;span class=&quot;org-builtin&quot;&gt;#:use-module&lt;/span&gt; (gnu packages)
&lt;span class=&quot;linenr&quot;&gt; 4: &lt;/span&gt;  &lt;span class=&quot;org-builtin&quot;&gt;#:use-module&lt;/span&gt; (gnu home)
&lt;span class=&quot;linenr&quot;&gt; 5: &lt;/span&gt;  &lt;span class=&quot;org-builtin&quot;&gt;#:use-module&lt;/span&gt; (gnu home services)
&lt;span class=&quot;linenr&quot;&gt; 6: &lt;/span&gt;  &lt;span class=&quot;org-builtin&quot;&gt;#:use-module&lt;/span&gt; (gnu home services shells)
&lt;span class=&quot;linenr&quot;&gt; 7: &lt;/span&gt;  &lt;span class=&quot;org-builtin&quot;&gt;#:use-module&lt;/span&gt; (gnu services)
&lt;span class=&quot;linenr&quot;&gt; 8: &lt;/span&gt;  &lt;span class=&quot;org-builtin&quot;&gt;#:use-module&lt;/span&gt; (gnu system shadow)
&lt;span class=&quot;linenr&quot;&gt; 9: &lt;/span&gt;  &lt;span class=&quot;org-builtin&quot;&gt;#:use-module&lt;/span&gt; (guix gexp))
&lt;span class=&quot;linenr&quot;&gt;10: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;11: &lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;%packages&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;12: &lt;/span&gt;  (list &lt;span class=&quot;org-string&quot;&gt;&amp;quot;git&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;openssh&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;librewolf&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;ripgrep&amp;quot;&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;13: &lt;/span&gt;        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;bat&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;eza&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;fd&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;zoxide&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;bc&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;gimp&amp;quot;&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;14: &lt;/span&gt;        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;libreoffice&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;jujutsu&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;starship&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;direnv&amp;quot;&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;15: &lt;/span&gt;        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;okular&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;gwenview&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;bitwarden-desktop&amp;quot;&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;16: &lt;/span&gt;        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;icedove-wayland&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;telegram-desktop&amp;quot;&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;17: &lt;/span&gt;        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;emacs-vterm&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;ispell&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;hunspell&amp;quot;&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;&amp;quot;wezterm&amp;quot;&lt;/span&gt;))
&lt;span class=&quot;linenr&quot;&gt;18: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;19: &lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;%nonfree-packages&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;20: &lt;/span&gt;  (list &lt;span class=&quot;org-string&quot;&gt;&amp;quot;steam-nvidia&amp;quot;&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;21: &lt;/span&gt;        &lt;span class=&quot;org-string&quot;&gt;&amp;quot;mpv-nvidia&amp;quot;&lt;/span&gt;))
&lt;span class=&quot;linenr&quot;&gt;22: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;23: &lt;/span&gt;(&lt;span class=&quot;org-keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;home-config&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;24: &lt;/span&gt;  (home-environment
&lt;span class=&quot;linenr&quot;&gt;25: &lt;/span&gt;   (packages (specifications-&amp;gt;packages (append %nonfree-packages %packages)))
&lt;span class=&quot;linenr&quot;&gt;26: &lt;/span&gt;   (services
&lt;span class=&quot;linenr&quot;&gt;27: &lt;/span&gt;    (append
&lt;span class=&quot;linenr&quot;&gt;28: &lt;/span&gt;     (list
&lt;span class=&quot;linenr&quot;&gt;29: &lt;/span&gt;      (service home-bash-service-type
&lt;span class=&quot;linenr&quot;&gt;30: &lt;/span&gt;               (home-bash-configuration
&lt;span class=&quot;linenr&quot;&gt;31: &lt;/span&gt;                 (aliases '((&lt;span class=&quot;org-string&quot;&gt;&amp;quot;ls&amp;quot;&lt;/span&gt; . &lt;span class=&quot;org-string&quot;&gt;&amp;quot;eza&amp;quot;&lt;/span&gt;)))
&lt;span class=&quot;linenr&quot;&gt;32: &lt;/span&gt;                 (bashrc (list (local-file &lt;span class=&quot;org-string&quot;&gt;&amp;quot;./bashrc.sh&amp;quot;&lt;/span&gt;)))))
&lt;span class=&quot;linenr&quot;&gt;33: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;34: &lt;/span&gt;      (service home-files-service-type
&lt;span class=&quot;linenr&quot;&gt;35: &lt;/span&gt;               `((&lt;span class=&quot;org-string&quot;&gt;&amp;quot;.guile&amp;quot;&lt;/span&gt; ,%default-dotguile)
&lt;span class=&quot;linenr&quot;&gt;36: &lt;/span&gt;                 (&lt;span class=&quot;org-string&quot;&gt;&amp;quot;.Xdefaults&amp;quot;&lt;/span&gt; ,%default-xdefaults)))
&lt;span class=&quot;linenr&quot;&gt;37: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;38: &lt;/span&gt;      (service home-xdg-configuration-files-service-type
&lt;span class=&quot;linenr&quot;&gt;39: &lt;/span&gt;               `((&lt;span class=&quot;org-string&quot;&gt;&amp;quot;gdb/gdbinit&amp;quot;&lt;/span&gt; ,%default-gdbinit)
&lt;span class=&quot;linenr&quot;&gt;40: &lt;/span&gt;                 (&lt;span class=&quot;org-string&quot;&gt;&amp;quot;nano/nanorc&amp;quot;&lt;/span&gt; ,%default-nanorc))))
&lt;span class=&quot;linenr&quot;&gt;41: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;42: &lt;/span&gt;     %base-home-services))))
&lt;span class=&quot;linenr&quot;&gt;43: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;44: &lt;/span&gt;home-config
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
In a nutshell I'm very positively surprised by Guix System. After struggling so much with it years ago, this time everything just clicked after a much shorter battle. So much so that I'm happy to make it my daily driver for the foreseeable future. Beyond the slightly slower execution speed, I'm getting a comparable experience to NixOS, with all the usual pros a declarative environment brings and without having to put up with Nixlang.
&lt;/p&gt;

&lt;p&gt;
My only recurring issues so far are the occasional slow download speeds and that I have to start my kernel in &lt;code&gt;nomodeset&lt;/code&gt; because otherwise the graphical environment crashes without me being able to switch to a TTY. It's a bummer, but honestly, I'm not too bothered by it so far. I'm trusting a driver update will fix it soon enough and, if not, it's not exactly difficult to &lt;a href=&quot;https://guix.gnu.org/manual/1.5.0/en/html_node/operating_002dsystem-Reference.html#:~:text=kernel-arguments&quot;&gt;throw in a kernel parameter&lt;/a&gt; into your config.
&lt;/p&gt;

&lt;p&gt;
I'm hoping to do a followup post about packaging in Guix, because I've been dipping my toes into it by &lt;a href=&quot;https://codeberg.org/guix/guix/pulls/6020&quot;&gt;trying to package&lt;/a&gt; Wezterm and the journey there was similarly arduous as installing the system itself.
&lt;/p&gt;

&lt;p&gt;
Till then, thank you for reading and see you next time!
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-notes&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;notes&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;8.&lt;/span&gt; Notes&lt;/h2&gt;
&lt;div id=&quot;text-8&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
The stuff you see below are all I managed to write down mid-process. Some of these I threw it into the file from Nano, some from half-broken X11 sessions. Because of this, it's not exactly well-edited, but I hope it might provide a glimpse into my mind at the time.
&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;The installer is decently simple
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;I appreciate the warning about incompatible hardware&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;2.5 hours at least to install (mirrors throttle connection to 50kbps)&lt;/li&gt;
&lt;li&gt;KDE is simply not working out of the box (titlebars are missing)
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;It seems to also default to X11, when I'm looking for Wayland&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;The first &lt;code&gt;guix pull&lt;/code&gt; is horrendously slow&lt;/li&gt;
&lt;li&gt;Wayland continues to elude me, seems to be an Nvidia issue
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;IRC recommends &lt;code&gt;nomodeset&lt;/code&gt;, doesn't help&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Try enabling Nonguix, system no longer boots&lt;/li&gt;
&lt;li&gt;Try installing using the Nonguix ISO
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Lots of errors, terribly old release&lt;/li&gt;
&lt;li&gt;Having to &lt;code&gt;guix pull&lt;/code&gt; myself to the present day again&lt;/li&gt;
&lt;li&gt;Also I'm missing the introduction, so I have to run it using &lt;code&gt;--disable-authentication&lt;/code&gt;, not great, but I trust the Codeberg repo&lt;/li&gt;
&lt;li&gt;At least the download speed seems to have normalized&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;It isn't entirely clear when you have to use &lt;code&gt;sudo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;i3&lt;/code&gt; on a shitty low-res has a certain vibe to it, but I'd prefer a system working out of the box&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;footnotes&quot;&gt;
&lt;h2 class=&quot;footnotes&quot;&gt;Footnotes: &lt;/h2&gt;
&lt;div id=&quot;text-footnotes&quot;&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.1&quot; href=&quot;#fnr.1&quot; class=&quot;footnum&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Well, if only life was so easy. What I mean here is that on my personal computer, I've not had Windows since about 2015. For work purposes my hands are currently chained to MacOS (though even there I use a Debian-based container).
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.2&quot; href=&quot;#fnr.2&quot; class=&quot;footnum&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
No disrespect to Ubuntu-users, past and present! My opinion at the time was quite ignorant and nowadays I far more appreciate an easy to maintain system as you'll see from the rest of this post.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.3&quot; href=&quot;#fnr.3&quot; class=&quot;footnum&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
It's merely a hunch, but it feels to me that the servers are far slower during the (Central-European) night. During midday, I get really good download speeds, but after around 8 PM, it slows to a crawl.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.4&quot; href=&quot;#fnr.4&quot; class=&quot;footnum&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Which, for the uninitiated, is an IRC client &lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_mono/erc.html&quot;&gt;built into Emacs&lt;/a&gt;. This editor continues to wow me every day.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.5&quot; href=&quot;#fnr.5&quot; class=&quot;footnum&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
I probably could have figured it out in time. But at this point I was a bit exasperated and I really didn't want to type in an 10x4 character hexadecimal code by hand.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.6&quot; href=&quot;#fnr.6&quot; class=&quot;footnum&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Goodness gracious, Portal 2 is almost 15 years old…
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.7&quot; href=&quot;#fnr.7&quot; class=&quot;footnum&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
That being said, my Nix experience was still very much helpful here. Understanding stuff such as build phases, why packages need to be patched and how this usually works, and what the different build flags mean is pretty much a must if you want to attain an understanding deeper than just &amp;quot;kinda getting it.&amp;quot;
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;/div&gt;</content></entry><entry><title>Implementing Perlin Noise in TypeScript</title><id>https://nemin.hu/perlin.html</id><author><name>Nemin</name></author><updated>2026-01-20T19:25:00Z</updated><link href="https://nemin.hu/perlin.html" rel="alternate" /><content type="html">&lt;div role=&quot;doc-toc&quot; id=&quot;table-of-contents&quot;&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div role=&quot;doc-toc&quot; id=&quot;text-table-of-contents&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;1. Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#basic-outline&quot;&gt;2. Basic outline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementation&quot;&gt;3. Implementation&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#vector-class&quot;&gt;3.1. Vector class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#interpolation-function&quot;&gt;3.2. Interpolation function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#noise-function&quot;&gt;3.3. Noise function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#main-loop&quot;&gt;3.4. Main loop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-introduction&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;introduction&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;1.&lt;/span&gt; Introduction&lt;/h2&gt;
&lt;div id=&quot;text-1&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
Ever since I first started programming, I was always fascinated by noise functions. The idea that you can take a relatively simple algorithm and generate realistic-looking textures of clouds, fire, cloth, etc. is really cool.
&lt;/p&gt;

&lt;p&gt;
However, as simple as the concept is, I still found myself struggling with putting it into code, until the idea one day clicked and I was able to finally implement it. To help others, who might wonder how this algorithm works, I'd like to share a small tutorial here how one might go about implementing a simple variant of &lt;a href=&quot;https://en.wikipedia.org/wiki/Perlin_noise&quot;&gt;Perlin noise&lt;/a&gt; in TypeScript.
&lt;/p&gt;

&lt;p&gt;
One caveat of the code below is that it focuses more on readability, without paying much attention towards performance and optimization, and thus isn't exactly fast, despite the algorithm being &lt;a href=&quot;https://en.wikipedia.org/wiki/Embarrassingly_parallel#:~:text=The%20Mandelbrot%20set%2C%20Perlin%20noise%20and%20similar%20images%2C%20where%20each%20point%20is%20calculated%20independently%2E&quot;&gt;Embarrasingly Parallel&lt;/a&gt;. For more serious implementations one should consider following the original algorithm's permutation lookup table-based approach, use &lt;a href=&quot;https://en.wikipedia.org/wiki/WebGL&quot;&gt;WebGL&lt;/a&gt; with a fragment shader that can easily compute every point's noise value at the same time, &lt;del&gt;or just use a better language than JS…&lt;/del&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-basic-outline&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;basic-outline&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;2.&lt;/span&gt; Basic outline&lt;/h2&gt;
&lt;div id=&quot;text-2&quot; class=&quot;outline-text-2&quot;&gt;
&lt;p&gt;
Given an arbitrarily sized rectangle, we need to do the following steps to fill it with noise:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;First we divide the rectangle into a grid of squares. The size of these squares is freely adjustable by us and it determines the general &amp;quot;roughness&amp;quot; of the result.&lt;/li&gt;
&lt;li&gt;For each of these grid points, we generate a so-called &lt;i&gt;gradient vector&lt;/i&gt;. In the context of Perlin noise, these are little more than vectors whose magnitude is 1 and which point in random directions.&lt;/li&gt;
&lt;li&gt;Then, for every pixel inside the original rectangle, we gather the four closest grid-points and their accompanying gradients.&lt;/li&gt;
&lt;li&gt;We calculate the &lt;a href=&quot;https://en.wikipedia.org/wiki/Dot_product&quot;&gt;dot product&lt;/a&gt; of the vector formed by the pixel's coordinates and each of the gradients.&lt;/li&gt;
&lt;li&gt;To smush these values into one, we take a proportional part of both the upper and lower corners' values based on the X coordinate of our pixel and then calculate a final value from that based on the Y coordinate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-implementation&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;implementation&quot;&gt;&lt;span class=&quot;section-number-2&quot;&gt;3.&lt;/span&gt; Implementation&lt;/h2&gt;
&lt;div id=&quot;text-3&quot; class=&quot;outline-text-2&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-vector-class&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;vector-class&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.1.&lt;/span&gt; Vector class&lt;/h3&gt;
&lt;div id=&quot;text-3-1&quot; class=&quot;outline-text-3&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt; 1: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt; 2: &lt;/span&gt;  x: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;;
&lt;span class=&quot;linenr&quot;&gt; 3: &lt;/span&gt;  y: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;;
&lt;span class=&quot;linenr&quot;&gt; 4: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt; 5: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;constructor&lt;/span&gt;(x: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;, y: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;) {
&lt;span class=&quot;linenr&quot;&gt; 6: &lt;/span&gt;    &lt;span class=&quot;org-typescript-this&quot;&gt;this&lt;/span&gt;.x = x;
&lt;span class=&quot;linenr&quot;&gt; 7: &lt;/span&gt;    &lt;span class=&quot;org-typescript-this&quot;&gt;this&lt;/span&gt;.y = y;
&lt;span class=&quot;linenr&quot;&gt; 8: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt; 9: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;10: &lt;/span&gt;  &lt;span class=&quot;org-typescript-access-modifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;random_gradient&lt;/span&gt;(): &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;11: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;angle&lt;/span&gt; = Math.&lt;span class=&quot;org-function-call&quot;&gt;random&lt;/span&gt;() * 2 * Math.PI;
&lt;span class=&quot;linenr&quot;&gt;12: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(Math.&lt;span class=&quot;org-function-call&quot;&gt;cos&lt;/span&gt;(angle), Math.&lt;span class=&quot;org-function-call&quot;&gt;sin&lt;/span&gt;(angle));
&lt;span class=&quot;linenr&quot;&gt;13: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt;14: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;15: &lt;/span&gt;  &lt;span class=&quot;org-function-name&quot;&gt;sub&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;other&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;): &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;16: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(&lt;span class=&quot;org-typescript-this&quot;&gt;this&lt;/span&gt;.x - other.x, &lt;span class=&quot;org-typescript-this&quot;&gt;this&lt;/span&gt;.y - other.y);
&lt;span class=&quot;linenr&quot;&gt;17: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt;18: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;19: &lt;/span&gt;  &lt;span class=&quot;org-function-name&quot;&gt;add&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;other&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;): &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;20: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(&lt;span class=&quot;org-typescript-this&quot;&gt;this&lt;/span&gt;.x + other.x, &lt;span class=&quot;org-typescript-this&quot;&gt;this&lt;/span&gt;.y + other.y);
&lt;span class=&quot;linenr&quot;&gt;21: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt;22: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;23: &lt;/span&gt;  &lt;span class=&quot;org-function-name&quot;&gt;dot_product&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;other&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;): &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;24: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;org-typescript-this&quot;&gt;this&lt;/span&gt;.x * other.x + &lt;span class=&quot;org-typescript-this&quot;&gt;this&lt;/span&gt;.y * other.y;
&lt;span class=&quot;linenr&quot;&gt;25: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt;26: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;27: &lt;/span&gt;  &lt;span class=&quot;org-function-name&quot;&gt;toString&lt;/span&gt;(): &lt;span class=&quot;org-typescript-primitive&quot;&gt;string&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;28: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;org-string&quot;&gt;`(&lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;org-default&quot;&gt;this.x&lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;org-default&quot;&gt;this.y&lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;org-string&quot;&gt;)`&lt;/span&gt;;
&lt;span class=&quot;linenr&quot;&gt;29: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt;30: &lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
We begin by defining a very rudimentary Vector class. Usually we'd define more operations, like multiplication, length, etc., but for our purposes this suffices.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.1&quot; href=&quot;#fn.1&quot; class=&quot;footref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;p&gt;
You may wonder why we bother to define &lt;code&gt;toString&lt;/code&gt;, when our end result will be graphical anyway. The reason is simple: We are going to associate data with our coordinates, using JavaScript's built-in &lt;code&gt;Map&lt;/code&gt;. However, because these don't support indexing by objects, we need to first convert our &lt;code&gt;Vector&lt;/code&gt; into a string, which does work as a key.&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.2&quot; href=&quot;#fn.2&quot; class=&quot;footref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-interpolation-function&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;interpolation-function&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.2.&lt;/span&gt; Interpolation function&lt;/h3&gt;
&lt;div id=&quot;text-3-2&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
In our algorithm, every pixel's value comes from four different values, but the end result needs to be only a single value. Therefore we need a function that, when given two numbers, allows us to pick a third, that is a mix of a given percentage of one of our numbers and a complementary percentage of the other. For this we utilize &lt;a href=&quot;https://en.wikipedia.org/wiki/Linear_interpolation#Programming_language_support&quot;&gt;Linear interpolation&lt;/a&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;31: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;lerp&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;percent&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;, &lt;span class=&quot;org-variable-name&quot;&gt;start&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;, &lt;span class=&quot;org-variable-name&quot;&gt;end&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;): &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;32: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; start + percent * (end - start);
&lt;span class=&quot;linenr&quot;&gt;33: &lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This function takes a &lt;code&gt;[start, end]&lt;/code&gt; range and a percentage, and has the following properties:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;lerp(0, start, end) = start + 0 * (end - start) = start&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lerp(1, start, end) = start + 1 * (end - start) = start + end - start = end&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Otherwise, &lt;code&gt;lerp(x, start, end) = ((1 - x) * start) + (x * end)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;b&gt;Example:&lt;/b&gt; Let's say our inputs are &lt;code&gt;0.25&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, and &lt;code&gt;5&lt;/code&gt;. We add to &lt;code&gt;1&lt;/code&gt; a fourth of the difference between &lt;code&gt;5&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;, thus get &lt;code&gt;1 + 0.25 * (5 - 1) = 1 + 0.25 * 4 = 1 + 1 = 2&lt;/code&gt;. If we were to pick &lt;code&gt;0.5&lt;/code&gt; as our &lt;code&gt;percent&lt;/code&gt; instead, our result would be &lt;code&gt;3&lt;/code&gt; which is the exact middle between &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;5&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
This already works, but it's less than ideal because the edges of the interpolation function are very sharp leading to very obvious artifacts on the edges of grid cells:
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-20-perlin/lerp.avif&quot; alt=&quot;lerp.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 1: &lt;/span&gt;Noise full of visual artifacts.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
It's easy to see why this happens if we take a look at how interpolating between some random values looks like:
&lt;/p&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-20-perlin/lerp_graph.avif&quot; alt=&quot;lerp_graph.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 2: &lt;/span&gt;Sharp peaks and valleys that show up as visible lines in our noise.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Thankfully fixing this is fairly simple. If the issue is that the endpoints' connections are too sharp, we simply have to smoothen them out. Enter the &amp;quot;&lt;a href=&quot;https://en.wikipedia.org/wiki/Smoothstep#Variations&quot;&gt;Smoothstep&lt;/a&gt;&amp;quot; function:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;34: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;smoothstep&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;x&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;): &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;35: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; 6*x**5 - 15*x**4 + 10*x**3;
&lt;span class=&quot;linenr&quot;&gt;36: &lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-20-perlin/smoothstep_func.avif&quot; alt=&quot;smoothstep_func.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 3: &lt;/span&gt;The Smoothstep function visualized in the range &lt;code&gt;[0, 1]&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Given a number between &lt;code&gt;[0, 1]&lt;/code&gt;, this function returns another value between &lt;code&gt;[0, 1]&lt;/code&gt;, but &amp;quot;smoothened out&amp;quot;. To provide a better visual explanation of what this means, let's see the previous interpolation, after we ran our percentages through Smoothstep&lt;a id=&quot;smoothstep&quot;&gt;&lt;/a&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;37: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;interpolate&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;percent&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;, &lt;span class=&quot;org-variable-name&quot;&gt;start&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;, &lt;span class=&quot;org-variable-name&quot;&gt;end&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;): &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;38: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;smooth_percent&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;smoothstep&lt;/span&gt;(percent);
&lt;span class=&quot;linenr&quot;&gt;39: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;org-function-call&quot;&gt;lerp&lt;/span&gt;(smooth_percent, start, end);
&lt;span class=&quot;linenr&quot;&gt;40: &lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-20-perlin/smootherstep.avif&quot; alt=&quot;smootherstep.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 4: &lt;/span&gt;The peaks and valleys are gone.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
As you can see, the end result looks a lot more &amp;quot;natural&amp;quot; as one curve flows nicely into the next, without any obvious edges.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-noise-function&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;noise-function&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.3.&lt;/span&gt; Noise function&lt;/h3&gt;
&lt;div id=&quot;text-3-3&quot; class=&quot;outline-text-3&quot;&gt;
&lt;p&gt;
With all the groundwork finally done, we reach the main algorithm. Let's take it in parts:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;41: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;gradients&lt;/span&gt; = &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Map&lt;/span&gt;&amp;lt;&lt;span class=&quot;org-typescript-primitive&quot;&gt;string&lt;/span&gt;, &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;&amp;gt;();
&lt;span class=&quot;linenr&quot;&gt;42: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;43: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;get_gradient&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;vec&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;): &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;44: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;key&lt;/span&gt; = vec.&lt;span class=&quot;org-function-call&quot;&gt;toString&lt;/span&gt;();
&lt;span class=&quot;linenr&quot;&gt;45: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;gradient&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt; | &lt;span class=&quot;org-constant&quot;&gt;undefined&lt;/span&gt; = gradients.&lt;span class=&quot;org-function-call&quot;&gt;get&lt;/span&gt;(key);
&lt;span class=&quot;linenr&quot;&gt;46: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;47: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;if&lt;/span&gt; (!gradient) {
&lt;span class=&quot;linenr&quot;&gt;48: &lt;/span&gt;    gradient = Vector.&lt;span class=&quot;org-function-call&quot;&gt;random_gradient&lt;/span&gt;();
&lt;span class=&quot;linenr&quot;&gt;49: &lt;/span&gt;    gradients.&lt;span class=&quot;org-function-call&quot;&gt;set&lt;/span&gt;(key, gradient);
&lt;span class=&quot;linenr&quot;&gt;50: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt;51: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;52: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; gradient
&lt;span class=&quot;linenr&quot;&gt;53: &lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
First we initialize a &lt;code&gt;Map&lt;/code&gt; to hold our calculated gradients. This both serves as an optimization (we only need to calculate a gradient for every grid cell corner once) and also ensures that every corner always gets the same gradient (our &lt;code&gt;random_gradient&lt;/code&gt; function has no knowledge about the outside state of our function, so it'll happily generate as many random vectors as we ask for).&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fnr.3&quot; href=&quot;#fn.3&quot; class=&quot;footref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;55: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;offset_vectors&lt;/span&gt;: &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;[] = [
&lt;span class=&quot;linenr&quot;&gt;56: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(0, 0),
&lt;span class=&quot;linenr&quot;&gt;57: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(1, 0),
&lt;span class=&quot;linenr&quot;&gt;58: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(0, 1),
&lt;span class=&quot;linenr&quot;&gt;59: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(1, 1),
&lt;span class=&quot;linenr&quot;&gt;60: &lt;/span&gt;];
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
We then create four offset vectors, which will help us address the grid cell's corners.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;62: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;noise&lt;/span&gt;(&lt;span class=&quot;org-variable-name&quot;&gt;x&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;, &lt;span class=&quot;org-variable-name&quot;&gt;y&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;): &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;63: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;coordinate_vector&lt;/span&gt; = &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(x, y);
&lt;span class=&quot;linenr&quot;&gt;64: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;floored_vector&lt;/span&gt; = &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Vector&lt;/span&gt;(Math.&lt;span class=&quot;org-function-call&quot;&gt;floor&lt;/span&gt;(x), Math.&lt;span class=&quot;org-function-call&quot;&gt;floor&lt;/span&gt;(y));
&lt;span class=&quot;linenr&quot;&gt;65: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;fraction_vector&lt;/span&gt; = coordinate_vector.&lt;span class=&quot;org-function-call&quot;&gt;sub&lt;/span&gt;(floored_vector);
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;
Our function takes in an X,Y pair representing the coordinates of a pixel. This pair is turned into three separate vectors:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;one for the raw coordinates (&lt;code&gt;coordinate_vector&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;one for the grid cell (&lt;code&gt;floored_vector&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;and finally the relative position of the coordinates inside the grid cell (&lt;code&gt;fraction_vector&lt;/code&gt;),&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;66: &lt;/span&gt;  &lt;span class=&quot;org-comment-delimiter&quot;&gt;// &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;top left, top right, bottom left, bottom right
&lt;span class=&quot;linenr&quot;&gt;67: &lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; [&lt;span class=&quot;org-variable-name&quot;&gt;tl&lt;/span&gt;, &lt;span class=&quot;org-variable-name&quot;&gt;tr&lt;/span&gt;, &lt;span class=&quot;org-variable-name&quot;&gt;bl&lt;/span&gt;, &lt;span class=&quot;org-variable-name&quot;&gt;br&lt;/span&gt;] = offset_vectors.&lt;span class=&quot;org-function-call&quot;&gt;map&lt;/span&gt;((&lt;span class=&quot;org-variable-name&quot;&gt;offset_vector&lt;/span&gt;) &lt;span class=&quot;org-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;68: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;corner_vector&lt;/span&gt; = floored_vector.&lt;span class=&quot;org-function-call&quot;&gt;add&lt;/span&gt;(offset_vector);
&lt;span class=&quot;linenr&quot;&gt;69: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;gradient&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;get_gradient&lt;/span&gt;(corner_vector);
&lt;span class=&quot;linenr&quot;&gt;70: &lt;/span&gt;    
&lt;span class=&quot;linenr&quot;&gt;71: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; coordinate_vector.&lt;span class=&quot;org-function-call&quot;&gt;sub&lt;/span&gt;(corner_vector).&lt;span class=&quot;org-function-call&quot;&gt;dot_product&lt;/span&gt;(gradient);
&lt;span class=&quot;linenr&quot;&gt;72: &lt;/span&gt;  });
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Next we calculate the dot products between the vector of our pixel's coordinates and each corner's gradient. &lt;code&gt;get_gradient&lt;/code&gt; ensures we always get the same gradient for a given corner.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;73: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;upper_interpolation&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;interpolate&lt;/span&gt;(fraction_vector.x, tl, tr);
&lt;span class=&quot;linenr&quot;&gt;74: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;lower_interpolation&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;interpolate&lt;/span&gt;(fraction_vector.x, bl, br);
&lt;span class=&quot;linenr&quot;&gt;75: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;76: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;noise_value&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;interpolate&lt;/span&gt;(
&lt;span class=&quot;linenr&quot;&gt;77: &lt;/span&gt;    fraction_vector.y,
&lt;span class=&quot;linenr&quot;&gt;78: &lt;/span&gt;    upper_interpolation,
&lt;span class=&quot;linenr&quot;&gt;79: &lt;/span&gt;    lower_interpolation,
&lt;span class=&quot;linenr&quot;&gt;80: &lt;/span&gt;  );
&lt;span class=&quot;linenr&quot;&gt;81: &lt;/span&gt;    
&lt;span class=&quot;linenr&quot;&gt;82: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; noise_value;
&lt;span class=&quot;linenr&quot;&gt;83: &lt;/span&gt;}
&lt;span class=&quot;linenr&quot;&gt;84: &lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Finally we do a 2D interpolation using the function we defined &lt;a href=&quot;#smoothstep&quot;&gt;earlier&lt;/a&gt;. We first interpolate between the ranges of &lt;code&gt;[top left, top right]&lt;/code&gt; and &lt;code&gt;[bottom left, bottom right]&lt;/code&gt; using the pixel coordinate's relative X position, and then, with one final interpolation using the coordinate's relative Y position, we get our noise value.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-main-loop&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;main-loop&quot;&gt;&lt;span class=&quot;section-number-3&quot;&gt;3.4.&lt;/span&gt; Main loop&lt;/h3&gt;
&lt;div id=&quot;text-3-4&quot; class=&quot;outline-text-3&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt; 85: &lt;/span&gt;&lt;span class=&quot;org-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;org-function-name&quot;&gt;generate_noise&lt;/span&gt;(
&lt;span class=&quot;linenr&quot;&gt; 86: &lt;/span&gt;  &lt;span class=&quot;org-variable-name&quot;&gt;width&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;,
&lt;span class=&quot;linenr&quot;&gt; 87: &lt;/span&gt;  &lt;span class=&quot;org-variable-name&quot;&gt;height&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;,
&lt;span class=&quot;linenr&quot;&gt; 88: &lt;/span&gt;  &lt;span class=&quot;org-variable-name&quot;&gt;resolution&lt;/span&gt;: &lt;span class=&quot;org-typescript-primitive&quot;&gt;number&lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt; 89: &lt;/span&gt;): &lt;span class=&quot;org-type&quot;&gt;Float32Array&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt; 90: &lt;/span&gt;  gradients.&lt;span class=&quot;org-function-call&quot;&gt;clear&lt;/span&gt;();
&lt;span class=&quot;linenr&quot;&gt; 91: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt; 92: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;values&lt;/span&gt; = &lt;span class=&quot;org-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;org-type&quot;&gt;Float32Array&lt;/span&gt;(width * height);
&lt;span class=&quot;linenr&quot;&gt; 93: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt; 94: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;y&lt;/span&gt; = 0; y &amp;lt; &lt;span class=&quot;org-type&quot;&gt;height&lt;/span&gt;; &lt;span class=&quot;org-type&quot;&gt;y&lt;/span&gt;++) {
&lt;span class=&quot;linenr&quot;&gt; 95: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;x&lt;/span&gt; = 0; x &amp;lt; &lt;span class=&quot;org-type&quot;&gt;width&lt;/span&gt;; &lt;span class=&quot;org-type&quot;&gt;x&lt;/span&gt;++) {
&lt;span class=&quot;linenr&quot;&gt; 96: &lt;/span&gt;      &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;x_percent&lt;/span&gt; = x / width;
&lt;span class=&quot;linenr&quot;&gt; 97: &lt;/span&gt;      &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;y_percent&lt;/span&gt; = y / height;
&lt;span class=&quot;linenr&quot;&gt; 98: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt; 99: &lt;/span&gt;      &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;value&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;noise&lt;/span&gt;(
&lt;span class=&quot;linenr&quot;&gt;100: &lt;/span&gt;        x_percent * resolution,
&lt;span class=&quot;linenr&quot;&gt;101: &lt;/span&gt;        y_percent * resolution,
&lt;span class=&quot;linenr&quot;&gt;102: &lt;/span&gt;      );
&lt;span class=&quot;linenr&quot;&gt;103: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;104: &lt;/span&gt;      &lt;span class=&quot;org-comment-delimiter&quot;&gt;// &lt;/span&gt;&lt;span class=&quot;org-comment&quot;&gt;Map [-1, 1] =&amp;gt; [0, 1]
&lt;span class=&quot;linenr&quot;&gt;105: &lt;/span&gt;&lt;/span&gt;      values[y * width + x] = (value + 1) / 2;
&lt;span class=&quot;linenr&quot;&gt;106: &lt;/span&gt;    }
&lt;span class=&quot;linenr&quot;&gt;107: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt;108: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;109: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;return&lt;/span&gt; values;
&lt;span class=&quot;linenr&quot;&gt;110: &lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
First we clear the gradients map, as we don't want any previous data to interfere in case this isn't the first time we run the function.
&lt;/p&gt;

&lt;p&gt;
Then we iterate through every pixel in our rectangle and calculate its coordinates, mapping &lt;code&gt;[0, width]&lt;/code&gt; and &lt;code&gt;[0, height]&lt;/code&gt; to &lt;code&gt;[0, 1]&lt;/code&gt;. We also take a parameter called &lt;code&gt;resolution&lt;/code&gt;, this determines how &amp;quot;zoomed in&amp;quot; our noise looks like:
&lt;/p&gt;

&lt;div class=&quot;sameline&quot;&gt;

&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-20-perlin/big_resolution.avif&quot; alt=&quot;big_resolution.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 5: &lt;/span&gt;Noise with high &lt;code&gt;resolution&lt;/code&gt; value.&lt;/p&gt;
&lt;/div&gt;


&lt;div class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./assets/imgs/2026-01-20-perlin/small_resolution.avif&quot; alt=&quot;small_resolution.avif&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;figure-number&quot;&gt;Figure 6: &lt;/span&gt;Noise with low &lt;code&gt;resolution&lt;/code&gt; value.&lt;/p&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;p&gt;
Finally, our noise function returns values in the range of &lt;code&gt;[-1, 1]&lt;/code&gt;, but we need &lt;code&gt;[0, 1]&lt;/code&gt;, so we add one to our result, mapping it to &lt;code&gt;[0, 2]&lt;/code&gt; and then divide it by 2, thus end up with &lt;code&gt;[0, 1]&lt;/code&gt;.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-typescript&quot;&gt;&lt;span class=&quot;linenr&quot;&gt;111: &lt;/span&gt;document.&lt;span class=&quot;org-function-call&quot;&gt;getElementById&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;button&amp;quot;&lt;/span&gt;).&lt;span class=&quot;org-function-call&quot;&gt;addEventListener&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;click&amp;quot;&lt;/span&gt;, () &lt;span class=&quot;org-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; {
&lt;span class=&quot;linenr&quot;&gt;112: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;canvas&lt;/span&gt; = document.&lt;span class=&quot;org-function-call&quot;&gt;getElementById&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;canvas&amp;quot;&lt;/span&gt;) &lt;span class=&quot;org-keyword&quot;&gt;as&lt;/span&gt; HTMLCanvasElement;
&lt;span class=&quot;linenr&quot;&gt;113: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;ctx&lt;/span&gt; = canvas.&lt;span class=&quot;org-function-call&quot;&gt;getContext&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;2d&amp;quot;&lt;/span&gt;);
&lt;span class=&quot;linenr&quot;&gt;114: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;115: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;maxFactor&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;Number&lt;/span&gt;(
&lt;span class=&quot;linenr&quot;&gt;116: &lt;/span&gt;    (document.&lt;span class=&quot;org-function-call&quot;&gt;getElementById&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;factor&amp;quot;&lt;/span&gt;) &lt;span class=&quot;org-keyword&quot;&gt;as&lt;/span&gt; HTMLInputElement).value
&lt;span class=&quot;linenr&quot;&gt;117: &lt;/span&gt;  );
&lt;span class=&quot;linenr&quot;&gt;118: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;119: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;baseFrequency&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;Number&lt;/span&gt;(
&lt;span class=&quot;linenr&quot;&gt;120: &lt;/span&gt;    (document.&lt;span class=&quot;org-function-call&quot;&gt;getElementById&lt;/span&gt;(&lt;span class=&quot;org-string&quot;&gt;&amp;quot;frequency&amp;quot;&lt;/span&gt;) &lt;span class=&quot;org-keyword&quot;&gt;as&lt;/span&gt; HTMLInputElement).value
&lt;span class=&quot;linenr&quot;&gt;121: &lt;/span&gt;  );
&lt;span class=&quot;linenr&quot;&gt;122: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;123: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;width&lt;/span&gt; = canvas.width;
&lt;span class=&quot;linenr&quot;&gt;124: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;height&lt;/span&gt; = canvas.height;
&lt;span class=&quot;linenr&quot;&gt;125: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;imageData&lt;/span&gt; = ctx.&lt;span class=&quot;org-function-call&quot;&gt;getImageData&lt;/span&gt;(0, 0, width, height);
&lt;span class=&quot;linenr&quot;&gt;126: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;127: &lt;/span&gt;  imageData.data.&lt;span class=&quot;org-function-call&quot;&gt;fill&lt;/span&gt;(0);
&lt;span class=&quot;linenr&quot;&gt;128: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;129: &lt;/span&gt;  &lt;span class=&quot;org-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;factor&lt;/span&gt; = 0; factor &amp;lt;= maxFactor; factor++) {
&lt;span class=&quot;linenr&quot;&gt;130: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;values&lt;/span&gt; = &lt;span class=&quot;org-function-call&quot;&gt;generate_noise&lt;/span&gt;(width, height, baseFrequency * (2 ** factor));
&lt;span class=&quot;linenr&quot;&gt;131: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;132: &lt;/span&gt;    &lt;span class=&quot;org-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;y&lt;/span&gt; = 0; y &amp;lt; &lt;span class=&quot;org-type&quot;&gt;height&lt;/span&gt;; &lt;span class=&quot;org-type&quot;&gt;y&lt;/span&gt;++) {
&lt;span class=&quot;linenr&quot;&gt;133: &lt;/span&gt;      &lt;span class=&quot;org-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;org-keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;x&lt;/span&gt; = 0; x &amp;lt; &lt;span class=&quot;org-type&quot;&gt;width&lt;/span&gt;; &lt;span class=&quot;org-type&quot;&gt;x&lt;/span&gt;++) {
&lt;span class=&quot;linenr&quot;&gt;134: &lt;/span&gt;        &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;idx&lt;/span&gt; = y * width + x;
&lt;span class=&quot;linenr&quot;&gt;135: &lt;/span&gt;        &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;value&lt;/span&gt; = values[idx];
&lt;span class=&quot;linenr&quot;&gt;136: &lt;/span&gt;        &lt;span class=&quot;org-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;org-variable-name&quot;&gt;color&lt;/span&gt; = Math.&lt;span class=&quot;org-function-call&quot;&gt;floor&lt;/span&gt;(Math.&lt;span class=&quot;org-function-call&quot;&gt;max&lt;/span&gt;(0, value) * 255 / (2 ** (factor + 1)));
&lt;span class=&quot;linenr&quot;&gt;137: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;138: &lt;/span&gt;        imageData.data[idx * 4 + 0] += color / 1.5;
&lt;span class=&quot;linenr&quot;&gt;139: &lt;/span&gt;        imageData.data[idx * 4 + 1] += color / 1.3;
&lt;span class=&quot;linenr&quot;&gt;140: &lt;/span&gt;        imageData.data[idx * 4 + 2] += color;
&lt;span class=&quot;linenr&quot;&gt;141: &lt;/span&gt;        imageData.data[idx * 4 + 3] = 255;
&lt;span class=&quot;linenr&quot;&gt;142: &lt;/span&gt;      }
&lt;span class=&quot;linenr&quot;&gt;143: &lt;/span&gt;    }
&lt;span class=&quot;linenr&quot;&gt;144: &lt;/span&gt;  }
&lt;span class=&quot;linenr&quot;&gt;145: &lt;/span&gt;
&lt;span class=&quot;linenr&quot;&gt;146: &lt;/span&gt;  ctx.&lt;span class=&quot;org-function-call&quot;&gt;putImageData&lt;/span&gt;(imageData, 0, 0)
&lt;span class=&quot;linenr&quot;&gt;147: &lt;/span&gt;});
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Since I don't want to cause lag immediately on page load, I bound the noise generation to a button that you can find below. We request the underlying image data from our canvas element and zero it out. This is done as a precaution in case the user presses the button multiple times. Without this reset, the &lt;code&gt;+=&lt;/code&gt; operations below would quickly oversaturate our output image.
&lt;/p&gt;

&lt;p&gt;
For this demo, I added two user-controlled variables, these being &lt;code&gt;factor&lt;/code&gt;, which determines how many layers of noise we generate and &lt;code&gt;baseFrequency&lt;/code&gt;, which determines the initial resolution (i.e. roughness) of the noise. For some examples, try a factor of 8 and frequency of 1 for a gentle fog and 4 and 16 for a fuzzy carpet.
&lt;/p&gt;

&lt;p&gt;
We generate a noise array with the given iteration's frequency and fill in the current pixel based on the value of the noise. I skewed the numbers a little to get a pleasant blue color, without any modification, we'd be left with a grayscale image.
&lt;/p&gt;

&lt;style&gt;

  #demo {
    margin: 1rem auto;
    width: fit-content;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  #controls {
    background-color: #aaa;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.25rem;
    border: 1px solid #888;
  }
&lt;/style&gt;

&lt;div id=&quot;demo&quot;&gt;
  &lt;canvas width=&quot;300px&quot; style=&quot;width: 300px; height: 300px; border: 1px solid #888; background-color: #aaa;&quot; id=&quot;canvas&quot; height=&quot;300px&quot;&gt;
  &lt;/canvas&gt;
  
  &lt;div id=&quot;controls&quot;&gt;
    Factor: &lt;input value=&quot;5&quot; type=&quot;range&quot; min=&quot;1&quot; max=&quot;8&quot; id=&quot;factor&quot; /&gt;
    Base frequency: &lt;input value=&quot;2&quot; type=&quot;range&quot; min=&quot;1&quot; max=&quot;64&quot; id=&quot;frequency&quot; /&gt;
    &lt;button id=&quot;button&quot;&gt;Start&lt;/button&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;script src=&quot;./assets/js/perlin.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;
Thank you for reading this post and I hope you may have learned something fun! If you'd like to read the code as a whole, you can access it &lt;a href=&quot;https://codeberg.org/nemin/blog/src/branch/main/assets/js/perlin.ts&quot;&gt;here&lt;/a&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;footnotes&quot;&gt;
&lt;h2 class=&quot;footnotes&quot;&gt;Footnotes: &lt;/h2&gt;
&lt;div id=&quot;text-footnotes&quot;&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.1&quot; href=&quot;#fnr.1&quot; class=&quot;footnum&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
In fact, we could easily get away without a class and just use inlined operations with either &lt;code&gt;{x: number, y: number}&lt;/code&gt; objects or &lt;code&gt;[x, y]&lt;/code&gt; arrays, but I believe the class-based version provides most pedagogical clarity, so that's the one I went with.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.2&quot; href=&quot;#fnr.2&quot; class=&quot;footnum&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Technically the &lt;code&gt;Map&lt;/code&gt; really doesn't care what we pass in as key. But since all keys are coerced into primitives, all of our &lt;code&gt;Vector&lt;/code&gt;-s would be turned into strings in the form of the dreaded &lt;code&gt;&amp;quot;[object Object]&amp;quot;&lt;/code&gt; and lose their identity, which would pretty much defeat the point of a hash-map. To avoid this, we override &lt;code&gt;toString&lt;/code&gt; and instead return a sensible and, more importantly, unique string representation, that our map can take with no issue.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footdef&quot;&gt;&lt;sup&gt;&lt;a role=&quot;doc-backlink&quot; id=&quot;fn.3&quot; href=&quot;#fnr.3&quot; class=&quot;footnum&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;div role=&quot;doc-footnote&quot; class=&quot;footpara&quot;&gt;&lt;p class=&quot;footpara&quot;&gt;
Those who are already familiar with the algorithm (or just read its Wikipedia page before continuing this article) might have realized that this is a vastly different way of generating gradients compared to the original version.
&lt;/p&gt;

&lt;p class=&quot;footpara&quot;&gt;
In the original we only have a handful (8 or 16) pre-generated gradient vectors and we pick one of them based on a somewhat involved process of hashing the current coordinate and then chopping off the lower 4 bits of the resulting number. While this most likely results in faster code, I find that it somewhat obscures of what we're really doing.
&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;/div&gt;</content></entry></feed>