Open ndmitchell opened 1 year ago
I don't think it would be impossible, but it might be a bit difficult. There's a lot of surface area to cover. But before anything else, I think the biggest motivating reason I started from scratch here is:
Nix is actually kind of an accessory, in this sense. I know Nix very well, so the "overhead" of me adapting it to my needs is very minimal. And if I want to drive something like Buck from scratch, I need a set of tools. And there's no better place to download my set of tools from. It's like: it just so happens that peanut butter and jelly go well together, but nobody set out to make that happen a priori.
So with that said, I think adapting the upstream toolchain definitions to Nixpkgs is possible. But first we need to understand the prior work and design space here a little.
This toolchain is an experiment in the sense it inverts some of my prior design choices in this space. For example, I had many private projects that used Shake. But they did a kind of thing where nix would populate the environment, and shake executes in that environment. So you'd go into a directory, type nix shell
, and then you could run the commands like shakebuild all
and build the all
target. That was the interactive loop. And when the CI system built things, or you wanted to produce packages, you ran nix build
, which built the whole thing from scratch, under the nix daemon, and all that jazz. Nix provides the hermetic environment. The tagline here is "Nix drives the build system", and Nix is what the user interacts with from this perspective.
This different design is informed by one I saw at a previous employer and some other prior art. In short, we reverse the tagline, instead, "The build system drives Nix". So the idea here is that instead, we use buck to invoke the Nix tools, and download things, and run toolchains provided by Nix. These toolchains can describe what environment variables they need set, how to do stuff, etc. But ultimately Buck now wraps Nix and drives it to build the system to completion.
Nix actually does two things in this repository: it compiles buck2 and some other tools, and puts them in
$PATH
. When you shell into it anddirenv
kicks in, that happens. This "shim layer" provides the development environment. But I also uses Nix to describe the toolchains, which are driven purely by Buck on-demand. This is the "toolchain" layer. So, conceptually these are different pieces of code for different use cases. Nix is just so useful for provisioning tools in a development environment, it makes sense to use it to do things like provide buck2 executables (the "shim"), rather than rely on the user to install a compatible copy themselves. It's important to distinguish these.
This inversion has a number of consequences:
nix build
when nix is at the forefront. But this "whole sale" build really requires granularity in the derivation structure to achieve good performance, and without content-addressable derivations, features like early cut-off don't exist.buck2
nix expression in this repository causes a 20min CI build time spike, because it recompiles all Cargo packages every time. You need to have every Cargo package become an individual Nix derivation, but that requires a lot of magic and there still isn't a good solution to it today. nixpkgs
probably wastes ungodly amounts of compute on this.PATH
or anything before trying to invoke something. Why would it? That's up to the rules. So it basically fails this test immediately, because any rule can use the ambient filesystem. It's simply too easy to get implicit assumptions on this wrong.gcc
accidentally link a shared library, one from /usr/lib
and one /nix/store
into the same binary, only for it to fantastically explode later.default.nix
to run buck build ...
inside a Nix sandbox, and then run this in your CI system like, once a day or whatever. This isn't what most people should use, it completely kills incrementalism at the build level but full rebuilds like this are often required to truly suss out things like reproducibility issues or hidden dependencies on the ambient filesystem (oh, I was accidentally relying on the "which" command to be installed, which came from /usr/bin
, stuff like that).shakeVersion
can probably be built completely in Starlark, I think?)There are probably others. Anyway, this is getting long! So I'll try to wrap up the next section quick.
Honestly? I think so, probably. If you simply populate a nix-shell
with the tools you want, the prelude might work today! I haven't actually tried it. This is the "Nix drives the tools" approach. This can probably work because Nix environments for "a language" generally try to emulate the semantics of non-Nix environments as closely as possible. So they'll set up PYTHONPATH
etc for you.
But it's tricky, because things like PYTHONPATH
require special care in Nix. It's not enough to have a rule like python_binary(name = ..., deps = [ "//python:click" ])
, because you also need to set up your environment with a Nix Python interpreter, combined with Nix Python libraries, which ensures sure their PYTHONPATH
is configured properly. So you have to specify both of these things, once for Nix and once for Buck.
This actually isn't too bad of a deal, and it's basically what everyone else already does. For example many Python packages inside Nixpkgs have to specify their dependencies at the Nix level and at the Python level in a poetry file or whatever. It's just the way it is. The inverted design of this repository might seek to overcome this burden, but it will come with more complexity in the Starlark layer.
But can it work? I think so. The problem of course is in the details, and having to align the surface area of buck-prelude with the surface area of nixpkgs, which is where all the abstractions lie.
Too much to summarize and I wrote enough already. If you'd like to have a video chat sometime, maybe with a vscode session, that'd be great, and I can go over some of my thoughts! Please let me know and maybe we can squeeze it in sometime, though it's busy for me.
Very cool project :)
I wondered if you'd considered whether it would be possible to reuse the open source upstream Buck2 prelude as well? It seems that Nix is a great source for toolchain definitions, but reimplementing things like Rust/Haskell/OCaml/C++ etc on top separately from the upstream prelude would be a lot of work.
Happy to chat further over video conference or similar if that's easier, and happy to answer any questions you have about Buck2. It's still early days, but integration with Nix is super cool.