-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Combining efforts with dream2nix #1
Comments
Hi! There's definitely similarities in the goals, although it does seem like our implementation ideas are a bit different. I'm aiming to have one big (rust) program for coercing lockfiles -> nix expressions, and then individual nix APIs (with a shared core) for building each kind of library. I'm also focussing on "just do everything consistently (where possible)", where you're aiming to give the user more flexibility. From what I gather your approach is a lot more decomposed into modular pieces. I'm not sure how well that'll work in practice, but I haven't dug in too much yet. Implementation-wise I'm trying to put as much logic as I can in rust (at codegen time), to keep the build time dependencies and nix logic minimal. I'm a huge fan of types, and the build logic for my original project (opam2nix) got complex from doing too much at build time. One thing I'd love to reuse though is nix build logic. I'm using nixpkgs' The other thing that would be good to share is to have a collection of backend-specific tricks / techniques. e.g. how do you deal with cyclical dependencies in yarn? (I was thinking about this yesterday, and haven't found a satisfactory approach). I will take a look at https://github.com/DavHau/dream2nix/blob/main/src/builders/nodejs/granular/default.nix . On first blush it looks like you're using FYI you may be interested in https://github.com/timbertson/fetlock/blob/master/nix/core.nix - it's the base nix overlay underneath each backend, and it's the basis for the consistency in how you override things, regardless of backend (as well as shared code between all backends). |
I haven't digged into your code much, but from reading your description the way we both split the process into modules is already quite similar. You described the following stages:
Translated into dream2nix terms this is equivalent to:
... So to me it doesn't seem like there are a lot more modular pieces in dream2nix. Your rust program is translating arbitrary upstream lock files into you own lock format (lock.nix) which is shared across all languages/frameworks and in a third stage you read your lock.nix with a nix library that generates derivations. This is quite exactly the idea behind dream2nix. Though there are some differences:
I agree with that point of view, but let's explore for a moment how minimal
I do not consider cyclic dependencies a backend specific problem. It is a general problem in package management which is seen in multiple ecosystems. Therefore detecting cyclic dependencies is a generic feature in dream2nix which part of the core framework and doesn't need to be dealt with by the individual subsystem. Cyclic dependencies are an extra field in our lock file format. Of course the builder still needs to handle those correctly.
I actually just got rid of any usage of node2nix usage (it's not merged to master yet). Not sure what you mean by
Dream2nix has exactly the same goals and that granular appoach is already implemented in the current nodejs builder. The core idea of dream2nix is to make all those approaches re-usable. For example, we're putting a lot of effort currently into making the nodejs build backend work and writing overrides for it (follow our progress in matrix if you like: #dream2nix:nixos.org). I believe that our approaches are similar enough that we should combine our efforts. That doesn't necessarily mean that we have to contribute to the same repository, but if we agree on some common standards, for example a common lock file format, we can integrate each others work more easily. If you have any suggestions on how I'd have to change dream2nix' interfaces to improve collaboration, let me know. |
Mainly for sharing functionality. In practice, the majority of shared functionality so far revolves around sources: The other big shared part is the structure representing (output) nix syntax, although this is only necessary since I'm outputting nix, not JSON.
I don't understand how you'd ever get the correct sha256 digests without codegen? No lockfile I know of happens to contain nix-compatible digests.
This is true, and I've ended up doing this already for bundler & ocaml. Although in these cases it's encapsulated within fetlock - the rust code spawns the language-specific translator, which contains the bare minimum language-specific logic. The result is parsed by rust so that most of the logic remains in rust (and can be shared).
Not quite. The separation is really machine-generated vs handwritten. In particular, some backends do make use of expressions which you can't write in JSON, e.g referencing other derivations in build/install scripts, and including OS-specific logic: Lines 2397 to 2403 in 93fbed3
(it's ugly, but this expressiveness is required to support opam/esy) It also allows direct references things like You could represent all of this with JSON, but it'd be more cumbersome.
This is true, and I've wished nix were more machine-editable as well. But I'd rather use nix to support complex expressions (above) than the still-theoretical benefits from having other tools edit my already-machine-generated data.
The fact that the lock is in nix doesn't lead to more duplication, it's the same amount of data you'd put in JSON. It just lets you represent some things that JSON can't. The repetitive logic is still in (my equivalent of) the builder.
Yep, that's essentially the same as my nix backends. And I assume that your lock format is also extensible - e.g. a given transformer will add language-specific metadata which the builder then expects / handles. The difference is perhaps more in how much logic goes into the builders, vs how much is done by the transformers. I'm trying to do as much as possible in the transformer. As a specific example, I have now implemented cyclic dependencies for yarn. I do that detection in rust, and output a list of "dependencies that need to be added to NODE_PATH" in the root package: fetlock/test/yarn/recursive-deps/lock.nix Lines 560 to 562 in 93fbed3
(putting cyclic deps on NODE_PATH means they can be found when not present in node_modules directly) You could put much more of this logic in nix (and I believe you have), it's just a matter of where you want to put the complexity. I believe that sharing a common language for the transformers should result in more code reuse and less complexity in the builders, but it's too early to tell if that's true.
Interesting. I mostly use compiled languages these days, where it makes compilation impossible. So I've only seen it in node so far. Where else have you seen it?
Got a branch I can look at? I'm curious :)
Great! I think I saw you state that the user would be in control of things like whether you want many granular derivations or one big one, is that still a goal? It sounds hard to support both at the same time.
Yep, I'm definitely open to it. I wouldn't be rewriting any rust in python (or nix), but sharing ideas and maybe concrete builders could work. Do you think any of your builders would make sense to upstream into nixpkgs as standalone functionality, or would they only make sense as part of upstreaming the whole of dream2nix itself?
One immediate thing that strikes me is the use of "name + version" pairs. I've keyed implementations by an opaque backend-specific "key" which is typically name+version, but doesn't have to be. In some cases you may want multiple copies of a given name + version (in cargo you can have the same crate with different compile-time features enabled). Each derivation still contains a name & version, it's just not (necessarily) keyed that way. That's just a surface level thing though, I haven't tried it enough to get a good understanding of the internals yet. |
A few examples for lock file which contain nix compatible digests:
I'd say most lock files use hashes like sha1, sha256, sha512 and those are all nix compatible. An exception would be
It is quite common in python packages as well. So far I have mainly worked on python and nodejs packaging, therefore I'm not sure how it is with most other build systems.
My main branch is always the best version to look at (Just last time I pointed you to dev as some change was still being worked on). For the nodejs builder, see here: https://github.com/nix-community/dream2nix/blob/main/src/builders/nodejs/granular/default.nix
It is not a goal to have a generic abstraction over whether to use granular or aggregated building. I just want to have an abstract concept
First I'd like to clarify that none of the relevant core logic of the framework is implemented in python. All the important interfaces are in nix, so you can just call dream2nix from within a nix expression and have the full functionality. Python right now is only used as a CLI, wrapping the nix functionality in a user friendly and interactive way, but using that is optional. I also use python as a scripting language during build time for some things that are just super inefficient in bash, like creating thousands of symlinks. Regarding re-using builders in other projects like nixpkgs: Originally the To make our current builders re-usable, the I see two possible ways how the builders then could be used in other contexts:
If you have any ideas on that, please let me know. To give another example. I might be interested in re-using some of your parsers / translators, or whatever we call those. Since you are outputting nix logic directly, there isn't really any place where I can hook into and re-use the result, as long as I am not compatible with exactly that logic you are outputting. If you would instead output data only (JSON, TOML, ...), I could read that and use my own logic to build stuff. It would allow me to just treat your translator as a black box.
I think we don't need to express different variations of a software inside its key necessarily. We can have interfaces where there compile time options can be configured by the user. I don't see the need of expressing that inside the key. If we would, then the questions is, where do we start and where do we stop. There is an indefinite amount of different configurations a software can be built with. If users want to create package collections, with different keys for different variations, they can create that manually ontop of dream2nix without any issue. |
Hello, I'm the maintainer of dream2nix.
I just learned about that project via nix-community/dream2nix#49
It seems like we're working on very similar things. Should we combine our efforts?
Cheers, David.
The text was updated successfully, but these errors were encountered: