kylewlacy
Joined 14 years ago
Joined 14 years ago
YoWASP has also had an LLVM toolchain working in Wasm for a while too[1], although it seems like this version solves the subprocess problem by providing an implementation of `posix_spawn` whereas the YoWASP one uses some patches to avoid subprocesses altogether
My biggest question marks around this version are about runtime/platform support. As I understand it, this toolchain uses WASIX, which (AFAICT) works with Wasmer's own runtime and with a browser shim, but with none of the other runtimes. Are there plans to get WASIX more widely adopted across more runtimes, or to get WASIX caught up to the latest WASI standard (preview2)? Or maybe even better, bring the missing features from WASIX to mainline WASI like `posix_spawn`[2]? I'd love to be able to adopt this toolchain, but it doesn't seem like WASIX support has really caught on across the other runtimes
[1]: https://discourse.llvm.org/t/rfc-building-llvm-for-webassemb... [2]: https://github.com/WebAssembly/WASI/issues/414
Building something on Nix obviously has a ton of benefits: well-proven tooling, lots of mindshare already, TONS of packages. Everyone knows that Nix-the-language is fairly unintuitive for beginners, and tools like garn, Devenv, Devbox, etc. all seem to be trying to let folks use Nix without exposing them to Nix-the-language. I think this is a good goal to have and helps make Nix more accessible
But I really thought it was worth exploring the Nix-free design space too. For example, I think derivations in Nix are... kinda complicated and weird! Having a fixed set of phases specifically designed for autotools builds always seemed a little weird to me. By ditching Nix, I was able to see what the world looks like when you ditch the idea of phases completely, for example
Another interesting one was ditching the absolute path for the store path. Getting everything working by resolving relative paths was surprisingly tricky, but the end result is that you don't need to be root to install Brioche, and you can also take a Brioche-built binary (along with the resource dir it outputs) and ship it to another Linux machine and it'll just work. That's only something you can do if you see what happens if you explore outside the Nix space
So I'm hoping Brioche can _at least_ be an interesting case study in how you can take a lot of Nix's ideas and get a lot of the same value without having to take Nix's approach directly (or who knows-- maybe some of those ideas end up back in Nix someday!)
In other words, the eager part is basically constructing the build graph. Maybe I'm wrong but I don't that this would necessarily be slower than the lazy version. In practice the most complex build graph I've made is basically the full chain of Linux From Scratch builds (that's the basis for my toolchain currently), and I think that takes about 400-500ms to evaluate. It's about 160 build steps, so it's not _simple_ but I know build graphs can also get a lot more complex, so I'll just have to keep an eye on performance as I start to get into more and more complex builds
Maybe I'm missing something but intuitively I'd expect this approach to be fairly efficient-- as long as build scripts only call these functions when they're used as part of the build graph
One neat thing about Brioche is that Bash isn't really baked into it the same way as the Nix ecosystem. In a lot of the docs and examples, I used the `std.runBash` Brioche function for running scripts, but that's a fairly simple 11-line function[^1]. I wanted to make it just as easy to add `runZsh`, `runFish`, `runNushell`, etc. functions that work just as well as Bash (and they could either be put in the std library or in their own packages). So hopefully, there will be a Brioche `runNushell` function in the near future :)
[^1]: https://github.com/brioche-dev/brioche-packages/blob/9fd5109...
And yep, I think having a "Brioche for Nix users" guide makes a lot of sense, although it's not the first time that question so I'll probably stand up a first-pass version of it sooner rather than later (my Nix skills are also pretty rusty-- I'll need to brush up a bit first before I write it!)
Definitely agreed about the lockfile ideas! I went with a fairly similar design, although I haven't really escaped the "rebuild the world" situation yet and I've rebuilt a _lot_ of copies of gcc from source by now! (that's also partially because I set up the packages repo as a workspace[1], which I felt was easier to iterate on in the early days... I might eventually split each package into their own projects or separate repos so they can keep lockfiles independent of each other)
and yeah, I definitely felt the temptation to write my own DSL but I stayed strong! I just knew that it'd be a huge uphill battle, especially because I wanted to provide good editor support (IMO implementing a language is (relatively) easy, but implementing a language with good error messages and LSP support is crazy hard)
So in general, I don't think I'm going to have anything quite as powerful as Nix's overrides. But I'm hoping most of the use-cases for it will be covered by some simpler options:
- Since build definitions are functions, package authors can just take arguments for the things they want downstream users to be able to customize (e.g. `configure` flags, optional features and plugins, etc.)
- I haven't built it yet, but I think adding support for dependency overrides would be fairly easy, a la Cargo. Basically, you'd just fork or clone the package you want to tweak, make your tweaks, then set an "overrides" option to use it instead. I know that's not a super satisfying answer, but that should help cover a lot of use cases
- For toolchains specifically, I have an idea in mind for this as well (also not implemented at all). At a high level, the idea is that packages could use "dynamic bindings", which you can then override for downstream recipes (this would require some new runtime features in Brioche itself). The toolchain itself would effectively be a dynamic binding, letting you pick a different recipe (so you could swap glibc for musl, or gcc for clang, etc). Cross-compilation would also be built on this same feature
I've generally stayed away from using the term "reproducible build" when talking about Brioche, because I don't feel like it fits the reproducible-builds.org definition (though I don't think Nix does either). But, if a build is cached locally or in the registry, then you're guaranteed to get the same result, since it'll just re-use the cache
The sandboxing also gives pretty strong guarantees around hermetic builds[1]. So I think you could do reproducible builds _within_ Brioche (and I'd like to add tools to make this even easier), but I'd say Brioche itself doesn't give you reproducible builds out of the box
Outside Nix, there's Brioche (my project!), and there's also Vorpal and Tangram, all of which are "Nix-like" but are from-scratch projects instead of using any Nix tooling
So I definitely agree that Nix as an _idea_ is right, and I definitely also agree that NixLang is a blocker for mass adoption. Personally though, I think if you dig deeper, there are other places that could use improvement beyond just the language itself (but nothing that would necessarily be a show-stopper for adoption IMO). But that's why my project is a "Nix-like"-- there are things that I think Nix could / should do, but which aren't really compatible with the way Nix itself works. One example: Brioche works without having a global store path-- it's per-user instead so that it can be installed rootlessly-- which doesn't really map well to how Nix itself handles store paths and dependencies
So point is, I agree Nix is the right model, but I think the _real_ gold lies outside Nix itself (...with the side note that I'm obviously biased!)