mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-04 22:00:34 +08:00
381 lines
16 KiB
Markdown
381 lines
16 KiB
Markdown
[#]: subject: "Some notes on using nix"
|
||
[#]: via: "https://jvns.ca/blog/2023/02/28/some-notes-on-using-nix/"
|
||
[#]: author: "Julia Evans https://jvns.ca/"
|
||
[#]: collector: "lkxed"
|
||
[#]: translator: " "
|
||
[#]: reviewer: " "
|
||
[#]: publisher: " "
|
||
[#]: url: " "
|
||
|
||
Some notes on using nix
|
||
======
|
||
|
||
Recently I started using a Mac for the first time. The biggest downside I’ve
|
||
noticed so far is that the package management is much worse than on Linux.
|
||
At some point I got frustrated with homebrew because I felt like it was
|
||
spending too much time upgrading when I installed new packages, and so I
|
||
thought – maybe I’ll try the [nix][1] package manager!
|
||
|
||
nix has a reputation for being confusing (it has its whole
|
||
own programming language!), so I’ve been trying to figure out how to use nix in
|
||
a way that’s as simple as possible and does not involve managing any
|
||
configuration files or learning a new programming language. Here’s what I’ve
|
||
figured out so far! We’ll talk about how to:
|
||
|
||
- install packages with nix
|
||
- build a custom nix package for a C++ program called [paperjam][2]
|
||
- install a 5-year-old version of [hugo][3] with nix
|
||
|
||
As usual I’ve probably gotten some stuff wrong in this post since I’m still
|
||
pretty new to nix. I’m also still not sure how much I like nix – it’s very
|
||
confusing! But it’s helped me compile some software that I was struggling to
|
||
compile otherwise, and in general it seems to install things faster than
|
||
homebrew.
|
||
|
||
#### what’s interesting about nix?
|
||
|
||
People often describe nix as “declarative package management”. I don’t
|
||
care that much about declarative package management, so here are two things
|
||
that I appreciate about nix:
|
||
|
||
- It provides binary packages (hosted at [https://cache.nixos.org/][4]) that you can quickly download and install
|
||
- For packages which don’t have binary packages, it makes it easier to compile them
|
||
|
||
I think that the reason nix is good at compiling software is that:
|
||
|
||
- you can have multiple versions of the same library or program installed at a time (you could have 2 different versions of libc for instance). For example I have two versions of node on my computer right now, one at `/nix/store/4ykq0lpvmskdlhrvz1j3kwslgc6c7pnv-nodejs-16.17.1` and one at `/nix/store/5y4bd2r99zhdbir95w5pf51bwfg37bwa-nodejs-18.9.1`.
|
||
- when nix builds a package, it builds it in isolation, using only the
|
||
specific versions of its dependencies that you explicitly declared. So
|
||
there’s no risk that the package secretly depends on another package on your
|
||
system that you don’t know about. No more fighting with `LD_LIBRARY_PATH`!
|
||
- a lot of people have put a lot of work into writing down all of the
|
||
dependencies of packages
|
||
|
||
I’ll give a couple of examples later in this post of two times nix made it easier for me to compile software.
|
||
|
||
#### how I got started with nix
|
||
|
||
here’s how I got started with nix:
|
||
|
||
- Install nix. I forget exactly how I did this, but it looks like there’s an [official installer][5] and an [unofficial installer from zero-to-nix.com][6]. The [instructions for uninstalling nix on MacOS with the standard multi-user install][7] are a bit complicated, so it might be worth choosing an installation method with simpler uninstall instructions.
|
||
- Put `~/.nix-profile/bin` on my PATH
|
||
- Install packages with `nix-env -iA nixpkgs.NAME`
|
||
- That’s it.
|
||
|
||
Basically the idea is to treat `nix-env -iA` like `brew install` or `apt-get install`.
|
||
|
||
For example, if I want to install `fish`, I can do that like this:
|
||
|
||
```
|
||
nix-env -iA nixpkgs.fish
|
||
```
|
||
|
||
This seems to just download some binaries from [https://cache.nixos.org][8] – pretty simple.
|
||
|
||
Some people use nix to install their Node and Python and Ruby packages, but I haven’t
|
||
been doing that – I just use `npm install` and `pip install` the same way I
|
||
always have.
|
||
|
||
#### some nix features I’m not using
|
||
|
||
There are a bunch of nix features/tools that I’m not using, but that I’ll
|
||
mention. I originally thought that you _had_ to use these features to use nix,
|
||
because most of the nix tutorials I’ve read talk about them. But you don’t have to use them.
|
||
|
||
- NixOS (a Linux distribution)
|
||
- [nix-shell][9]
|
||
- [nix flakes][10]
|
||
- [home-manager][11]
|
||
- [devenv.sh][12]
|
||
|
||
I won’t go into these because I haven’t really used them and there are lots of
|
||
explanations out there.
|
||
|
||
#### where are nix packages defined?
|
||
|
||
I think packages in the main nix package repository are defined in [https://github.com/NixOS/nixpkgs/][13]
|
||
|
||
It looks like you can search for packages at [https://search.nixos.org/packages][14]. The two official ways to search packages seem to be:
|
||
|
||
- `nix-env -qaP NAME`, which is very extremely slow and which I haven’t been able to get to actually work
|
||
- `nix --extra-experimental-features 'nix-command flakes' search nixpkgs NAME`, which does seem to work but is kind of a mouthful. Also all of the packages it prints out start with `legacyPackages` for some reason
|
||
|
||
I found a way to search nix packages from the command line that I liked better:
|
||
|
||
- Run `nix-env -qa '*' > nix-packages.txt` to get a list of every package in the Nix repository
|
||
- Write a short `nix-search` script that just greps `packages.txt` (`cat ~/bin/nix-packages.txt | awk '{print $1}' | rg "$1"`)
|
||
|
||
#### everything is installed with symlinks
|
||
|
||
One of nix’s major design choices is that there isn’t one single `bin` with all
|
||
your packages, instead you use symlinks. There are a lot of layers of symlinks. A few examples of symlinks:
|
||
|
||
- `~/.nix-profile` on my machine is (indirectly) a symlink to `/nix/var/nix/profiles/per-user/bork/profile-111-link/`
|
||
- `~/.nix-profile/bin/fish` is a symlink to `/nix/store/afkwn6k8p8g97jiqgx9nd26503s35mgi-fish-3.5.1/bin/fish`
|
||
|
||
When I install something, it creates a new `profile-112-link` directory with new symlinks and updates my `~/.nix-profile` to point to that directory.
|
||
|
||
I think this means that if I install a new version of `fish` and I don’t like it, I can
|
||
easily go back just by running `nix-env --rollback` – it’ll move me to my previous profile directory.
|
||
|
||
#### uninstalling packages doesn’t delete them
|
||
|
||
If I uninstall a nix package like this, it doesn’t actually free any hard drive space, it just removes the symlinks.
|
||
|
||
```
|
||
$ nix-env --uninstall oil
|
||
```
|
||
|
||
I’m still not sure how to actually delete the package – I ran a garbage collection like this, which seemed to delete some things:
|
||
|
||
```
|
||
$ nix-collect-garbage
|
||
...
|
||
85 store paths deleted, 74.90 MiB freed
|
||
```
|
||
|
||
But I still have `oil` on my system at `/nix/store/8pjnk6jr54z77jiq5g2dbx8887dnxbda-oil-0.14.0`.
|
||
|
||
There’s a more aggressive version of `nix-collect-garbage` that also deletes old versions of your profiles (so that you can’t rollback)
|
||
|
||
```
|
||
$ nix-collect-garbage -d --delete-old
|
||
```
|
||
|
||
That doesn’t delete `/nix/store/8pjnk6jr54z77jiq5g2dbx8887dnxbda-oil-0.14.0` either though and I’m not sure why.
|
||
|
||
#### upgrading
|
||
|
||
It looks like you can upgrade nix packages like this:
|
||
|
||
```
|
||
nix-channel --update
|
||
nix-env --upgrade
|
||
```
|
||
|
||
(similar to `apt-get update && apt-get upgrade`)
|
||
|
||
I haven’t really upgraded anything yet. I think that if something goes wrong with an upgrade, you can roll back (because everything is immutable in nix!) with
|
||
|
||
```
|
||
nix-env --rollback
|
||
```
|
||
|
||
Someone linked me to [this post from Ian Henry][15] that
|
||
talks about some confusing problems with `nix-env --upgrade` – maybe it
|
||
doesn’t work the way you’d expect? I guess I’ll be wary around upgrades.
|
||
|
||
#### next goal: make a custom package of paperjam
|
||
|
||
After a few months of installing existing packages, I wanted to make a custom package with nix for a program called [paperjam][2] that wasn’t already packaged.
|
||
|
||
I was actually struggling to compile `paperjam` at all even without nix because the version I had
|
||
of `libiconv` I has on my system was wrong. I thought it might be easier to
|
||
compile it with nix even though I didn’t know how to make nix packages yet. And
|
||
it actually was!
|
||
|
||
But figuring out how to get there was VERY confusing, so here are some notes about how I did it.
|
||
|
||
#### how to build an example package
|
||
|
||
Before I started working on my `paperjam` package, I wanted to build an example existing package just to
|
||
make sure I understood the process for building a package. I was really
|
||
struggling to figure out how to do this, but I asked in Discord and someone
|
||
explained to me how I could get a working package from [https://github.com/NixOS/nixpkgs/][13] and build it. So here
|
||
are those instructions:
|
||
|
||
**step 1:** Download some arbitrary package from [nixpkgs][13] on github, for example the `dash` package:
|
||
|
||
```
|
||
wget https://raw.githubusercontent.com/NixOS/nixpkgs/47993510dcb7713a29591517cb6ce682cc40f0ca/pkgs/shells/dash/default.nix -O dash.nix
|
||
```
|
||
|
||
**step 2**: Replace the first statement (`{ lib , stdenv , buildPackages , autoreconfHook , pkg-config , fetchurl , fetchpatch , libedit , runCommand , dash }:` with `with import <nixpkgs> {};` I don’t know why you have to do this,
|
||
but it works.
|
||
|
||
**step 3**: Run `nix-build dash.nix`
|
||
|
||
This compiles the package
|
||
|
||
**step 4**: Run `nix-env -i -f dash.nix`
|
||
|
||
This installs the package into my `~/.nix-profile`
|
||
|
||
That’s all! Once I’d done that, I felt like I could modify the `dash` package and make my own package.
|
||
|
||
#### how I made my own package
|
||
|
||
`paperjam` has one dependency (`libpaper`) that also isn’t packaged yet, so I needed to build `libpaper` first.
|
||
|
||
Here’s `libpaper.nix`. I basically just wrote this by copying and pasting from
|
||
other packages in the [nixpkgs][13] repository.
|
||
My guess is what’s happening here is that nix has some default rules for
|
||
compiling C packages (like “run `make install`”), so the `make install` happens
|
||
default and I don’t need to configure it explicitly.
|
||
|
||
```
|
||
with import <nixpkgs> {};
|
||
|
||
stdenv.mkDerivation rec {
|
||
pname = "libpaper";
|
||
version = "0.1";
|
||
|
||
src = fetchFromGitHub {
|
||
owner = "naota";
|
||
repo = "libpaper";
|
||
rev = "51ca11ec543f2828672d15e4e77b92619b497ccd";
|
||
hash = "sha256-S1pzVQ/ceNsx0vGmzdDWw2TjPVLiRgzR4edFblWsekY=";
|
||
};
|
||
|
||
buildInputs = [ ];
|
||
|
||
meta = with lib; {
|
||
homepage = "https://github.com/naota/libpaper";
|
||
description = "libpaper";
|
||
platforms = platforms.unix;
|
||
license = with licenses; [ bsd3 gpl2 ];
|
||
};
|
||
}
|
||
```
|
||
|
||
Basically this just tells nix how to download the source from GitHub.
|
||
|
||
I built this by running `nix-build libpaper.nix`
|
||
|
||
Next, I needed to compile `paperjam`. Here’s a link to the [nix package I wrote][16]. The main things I needed to do other than telling it where to download the source were:
|
||
|
||
- add some extra build dependencies (like `asciidoc`)
|
||
- set some environment variables for the install (`installFlags = [ "PREFIX=$(out)" ];`) so that it installed in the correct directory instead of `/usr/local/bin`.
|
||
|
||
I set the hashes by first leaving the hash empty, then running `nix-build` to get an error message complaining about a mismatched hash. Then I copied the correct hash out of the error message.
|
||
|
||
I figured out how to set `installFlags` just by running `rg PREFIX`
|
||
in the nixpkgs repository – I figured that needing to set a `PREFIX` was
|
||
pretty common and someone had probably done it before, and I was right. So I
|
||
just copied and pasted that line from another package.
|
||
|
||
Then I ran:
|
||
|
||
```
|
||
nix-build paperjam.nix
|
||
nix-env -i -f paperjam.nix
|
||
```
|
||
|
||
and then everything worked and I had `paperjam` installed! Hooray!
|
||
|
||
#### next goal: install a 5-year-old version of hugo
|
||
|
||
Right now I build this blog using Hugo 0.40, from 2018. I don’t need any new
|
||
features so I haven’t felt a need to upgrade. On Linux this is easy: Hugo’s
|
||
releases are a static binary, so I can just download the 5-year-old binary from
|
||
the [releases page][17] and
|
||
run it. Easy!
|
||
|
||
But on this Mac I ran into some complications. Mac hardware has changed in the
|
||
last 5 years, so the Mac Hugo binary I downloaded crashed. And when I tried to
|
||
build it from source with `go build`, that didn’t work either because Go build
|
||
norms have changed in the last 5 years as well.
|
||
|
||
I was working around this by running Hugo in a Linux docker container, but I
|
||
didn’t love that: it was kind of slow and it felt silly. It shouldn’t be that
|
||
hard to compile one Go program!
|
||
|
||
Nix to the rescue! Here’s what I did to install the old version of Hugo with
|
||
nix.
|
||
|
||
#### installing Hugo 0.40 with nix
|
||
|
||
I wanted to install Hugo 0.40 and put it in my PATH as `hugo-0.40`. Here’s how
|
||
I did it. I did this in a kind of weird way, but it worked ([Searching and installing old versions of Nix packages][18]
|
||
describes a probably more normal method).
|
||
|
||
**step 1**: Search through the nixpkgs repo to find Hugo 0.40
|
||
|
||
I found the `.nix` file here [https://github.com/NixOS/nixpkgs/blob/17b2ef2/pkgs/applications/misc/hugo/default.nix][19]
|
||
|
||
**step 2**: Download that file and build it
|
||
|
||
I downloaded that file (and another file called `deps.nix` in the same directory), replaced the first line with `with import <nixpkgs> {};`, and built it with `nix-build hugo.nix`.
|
||
|
||
That almost worked without any changes, but I had to make two changes:
|
||
|
||
- replace `with stdenv.lib` to `with lib` for some reason.
|
||
- rename the package to `hugo040` so that it wouldn’t conflict with the other version of `hugo` that I had installed
|
||
|
||
**step 3**: Rename `hugo` to `hugo-0.40`
|
||
|
||
I write a little post install script to rename the Hugo binary.
|
||
|
||
```
|
||
postInstall = ''
|
||
mv $out/bin/hugo $out/bin/hugo-0.40
|
||
'';
|
||
```
|
||
|
||
I figured out how to run this by running `rg 'mv '` in the nixpkgs repository and just copying and modifying something that seemed related.
|
||
|
||
**step 4**: Install it
|
||
|
||
I installed into my `~/.nix-profile/bin` by running `nix-env -i -f hugo.nix`.
|
||
|
||
And it all works! I put the final `.nix` file into my own personal [nixpkgs repo][20] so that I can use it again later if I
|
||
want.
|
||
|
||
#### reproducible builds aren’t magic, they’re really hard
|
||
|
||
I think it’s worth noting here that this `hugo.nix` file isn’t magic – the
|
||
reason I can easily compile Hugo 0.40 today is that many people worked for a long time to make it possible to
|
||
package that version of Hugo in a reproducible way.
|
||
|
||
#### that’s all!
|
||
|
||
Installing `paperjam` and this 5-year-old version of Hugo were both
|
||
surprisingly painless and actually much easier than compiling it without nix,
|
||
because nix made it much easier for me to compile the `paperjam` package with
|
||
the right version of `libiconv`, and because someone 5 years ago had already
|
||
gone to the trouble of listing out the exact dependencies for Hugo.
|
||
|
||
I don’t have any plans to get much more complicated with nix (and it’s still
|
||
very possible I’ll get frustrated with it and go back to homebrew!), but we’ll
|
||
see what happens! I’ve found it much easier to start in a simple way and then
|
||
start using more features if I feel the need instead of adopting a whole bunch
|
||
of complicated stuff all at once.
|
||
|
||
I probably won’t use nix on Linux – I’ve always been happy enough with `apt`
|
||
(on Debian-based distros) and `pacman` (on Arch-based distros), and they’re
|
||
much less confusing. But on a Mac it seems like it might be worth it. We’ll
|
||
see! It’s very possible in 3 months I’ll get frustrated with nix and just go back to homebrew.
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
via: https://jvns.ca/blog/2023/02/28/some-notes-on-using-nix/
|
||
|
||
作者:[Julia Evans][a]
|
||
选题:[lkxed][b]
|
||
译者:[译者ID](https://github.com/译者ID)
|
||
校对:[校对者ID](https://github.com/校对者ID)
|
||
|
||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||
|
||
[a]: https://jvns.ca/
|
||
[b]: https://github.com/lkxed/
|
||
[1]: https://nixos.org/
|
||
[2]: https://mj.ucw.cz/sw/paperjam/
|
||
[3]: https://github.com/gohugoio/hugo/
|
||
[4]: https://cache.nixos.org/
|
||
[5]: https://nixos.org/download
|
||
[6]: https://zero-to-nix.com/concepts/nix-installer
|
||
[7]: https://nixos.org/manual/nix/stable/installation/installing-binary.html#macos
|
||
[8]: https://cache.nixos.org
|
||
[9]: https://nixos.org/guides/nix-pills/developing-with-nix-shell.html
|
||
[10]: https://nixos.wiki/wiki/Flakes
|
||
[11]: https://github.com/nix-community/home-manager
|
||
[12]: https://devenv.sh/
|
||
[13]: https://github.com/NixOS/nixpkgs/
|
||
[14]: https://search.nixos.org/packages
|
||
[15]: https://ianthehenry.com/posts/how-to-learn-nix/my-first-package-upgrade/
|
||
[16]: https://github.com/jvns/nixpkgs/blob/22b70a48a797538c76b04261b3043165896d8f69/paperjam.nix
|
||
[17]: https://github.com/gohugoio/hugo/releases/tag/v0.40
|
||
[18]: https://lazamar.github.io/download-specific-package-version-with-nix/
|
||
[19]: https://github.com/NixOS/nixpkgs/blob/17b2ef2/pkgs/applications/misc/hugo/default.nix
|
||
[20]: https://github.com/jvns/nixpkgs/ |