Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> But at the same time, I can patch the Python interpreter and then have some software running on my system actually use the patched version, since all of this stuff is configured through the same configuration system.

If I want to do that, then I have to rebuild the entire system so as to use my patched version. This is because the NixOS "dependencies" are basically expressed as hashes of binaries.

That is for me the single biggest deal-killer of NixOS, Guix, and while we're at it -- static linking.



This can be avoided my not overriding the package attribute, but instead creating a new one and then only using that as-needed.

What you describe as a deal-killer is just purity and safety. Updating a build input gives no guarantee that the build output will be the same or even work. Sure, as smart humans we can say “I know this change does not impact X, so it’s fine”, but NixOS is based partly on the claim that we do not do that reliably or consistently, and tries to save us from ourselves. Yes, it means more rebuilding, but it also means less debugging, confusion, etc., when things do go awry.


> What you describe as a deal-killer is just purity and safety.

It's not really "pure" since you are packaging software with side-effects to begin with. Even if you use the same text bit by bit it's also no guarantee that the output will be the same or that even it will work. You are still relying on some human claiming that it will do.

So the advantage is not really that big in practice, and the disadvantage means rebuilding the world on every low-level package change. Even Gentoo fares better in that regard.

And yes, I am aware that the deal-killer is at the core of these approaches, which is why I don't take them seriously.


I think you might be vastly understating the advantage here. I've been the guy who shipped software that "worked on my machine" but didn't work in production because on my machine there was a system installed dependency that got used but didn't exist in production and the only way to discover that was to ship to production. Nix makes that a far less likely scenario. It get's a lot of things wrong in how it achieves that but it fixes a real and very substantive problem.


Sure, if you have a program that outputs a random number, NixOS doesn’t make it suddenly always output the same number (would one even want that?). Nix aims to remove side-effects from the build system (and some aspects of the execution environment), not from within the program itself. Whether the program itself is pure is up to it, not NixOS. But the point is that NixOS eliminates whole classes of why the output may be different.


NixOS and Guix do _not_ require static linking. They go even one step further and allow many versions of a dynamic object to exist at the same time and each software on your system chooses, which version of the object to link against.


You cannot change the version used by a given binary unless you rebuilt that binary, effectively making it similar to static linking (you still get sharing of DSO files & pages, though). i.e. even if I have two versions of some low-level library installed, depending installed packages still hardcode which one of the two low-level libraries to use, and if I want to switch the system from one to the other, I have to rebuilt it (or at least all depending packages).

Suppose I make an overlay of libc to add some functionality that I am debugging which changes the libc binary, albeit not the ABI. Can I still reuse the same packages? Can I still reuse someone else's binary cache ? Basically, can I do without having to rebuild the entire system (save for libc) ?


It is correct to say that dynamic linking with rebuild-on-input-dependency-change (like NixOS does) is similar to static linking when it comes to rebuild behaviour.

However, also remember that changing dynamic libraries behind executables back is a concept that only makes sense in the presence of ABI compatibility. This is predominantly a C concept (or at least popular in the C world), and much less so for other linked programming languages like C++, Haskell, Go, and so on. Thus Nix being a general-purpose build system takes the general route here, and builds can also use their dependencies during the build step (e.g. a packaged program's autoconf suite might check the version or a symbol provided by a library), which requires full rebuilds for reproducibility.

(Nix is working on content-addressed instead of input-hash-haddressed builds, which might open the door for avoiding many rebuilds that do not affect build _output_.)

That said, it might still be quite easy to achieve what you want:

* If you want to iterate on a C library that's early in the dependency graph, in am ABI-compatible fashion, you coul duse LD_LIBRARY_PATH or LD_PRELOAD on it.

* If you want to override libc in an ABI-compatible way, you can use `patchelf` with its `--set-interpreter` and `--set-rpath` flags to replace the libc an executable is linked against after the fact. For example, you can make an `.override` of a nix derivation that just `cp -r`'s the existing files, and then calls `patchelf` on them.

I have used both these methods to work around glibc bugs that I patched out, avoiding even to have to recompile my own software at the top of the build dependency tree.

Some more relevant links about replacing glibc specifically:

* https://github.com/NixOS/nixpkgs/issues/50329

* https://github.com/NixOS/nixpkgs/issues/129595

If you want to replace things system-wide instead of for your own software, the `system.replaceRuntimeDependencies` mentioned by a sibling comment might be a good choice.


Yes, there is system.replaceRuntimeDependencies [1] that does what you ask: It replaces dependencies recursively in derivation outputs through binary patching, creating _new_ store paths (so immutability is preserved). It does so by replacing all occurrences of the store paths of the original dependency (e.g., libc) with the new ones [2].

[1] https://search.nixos.org/options?channel=21.11&show=system.r...

[2] https://github.com/NixOS/nixpkgs/blob/fad04722fc3d692e3511e5...


Grafts in Guix support this use case: you know you're providing an ABI-compatible package replacement and don't want to rebuild everything that depends on it:

https://guix.gnu.org/en/blog/2020/grafts-continued/

We use that for security updates, but also in other situations where we know we can take advantage of it such as the new `--tune` package transformation option, which tunes a package for a specific CPU:

https://hpc.guix.info/blog/2022/01/tuning-packages-for-a-cpu...

Similarly, as a user, you can "graft" a replacement straight from the command line using `--with-graft`:

https://guix.gnu.org/manual/devel/en/html_node/Package-Trans...


Guix has graft for something similar to this: https://guix.gnu.org/manual/devel/en/html_node/Security-Upda...

It's one of the points where it differs from Nix.


Is this different from Nix's `system.replaceRuntimeDependencies` posted in the sibling comment?


I was hoping to get an answer to your question as well, but I've only used Guix, so I can't really tell.

Guix grafts are used to distribute updates, and they're quite easy to use, that's all I can say.


If you are fine with sidestepping the system as intended then nothing prevents you from forcefully replacing the dynamically-linked, shared libc in the nix store.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: