thoughtpolice / buck2-nix

Do not taunt happy fun ball
59 stars 4 forks source link

Use of upstream buck2-prelude #1

Open ndmitchell opened 1 year ago

ndmitchell commented 1 year ago

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.

thoughtpolice commented 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 and direnv 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:

There are probably others. Anyway, this is getting long! So I'll try to wrap up the next section quick.

Can the existing prelude work with Nix? ("Nix drives the tools")

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.

Summary

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.