3 ms·
Main differences with Nix: * Spack has a dependency solver (concretizer). nixpkgs is managed by humans who do the dependency solving for you over time. What
by tgamblin 4y ago
Main differences with Nix:
* Spack has a dependency solver (concretizer). nixpkgs is managed by humans who do the dependency solving for you over time. What this means practically is that you can install 5 versions of some package with different dependencies and flag combinations with a few commands in Spack, while with Nix you would probably need to check out different commits of the nixpkgs repo and maybe do some hacking on nix derivations to get the same thing. Spack is fundamentally designed to help with combinatorics.
* Nix builds all the way down to libc; Spack (currently) doesn't -- it's designed to live on an existing system.
* Nix build environments are isolated (but require root to run); Spack's aren't as completely isolated and don't require root (so you can run Spack in your home directory.
* Spack installation hashes are what Nix would call full configuration hashes -- they're configuration metadata hashes, not content hashes of the installation. So you could say we're not quite as committed to exact binary reproducibility, but you could also say that this allows us to support relocatable binaries (which Nix does not). Also, our metadata is pretty detailed.
* Spack's DSL is Python; Nix is, well, Nix.
Spack is very much inspired by Nix, and we talked about this a bit in the original paper here:
* https://tgamblin.github.io/pubs/spack-sc15.pdf https://tgamblin.github.io/pubs/spack-sc15.pdf
- pxc 4y ago> with Nix you would probably need to check out different commits of the nixpkgs repo and maybe do some hacking on nix derivations to get the same thing. Spack is fundamentally designed to help with combinatorics. Typically you'd use the overlay mechanism combined with package overrides for this, so you would have to write some Nix code, but you can do it out-of-tree (the most common location of choice would be inline in your configuration repo). One of the usability issues with this is that the APIs for overriding dependencies and flags in Nix packages are sometimes a bit language ecosystem-specific. The uniformity of Spack's approach strikes me as an even bigger win over Nixpkgs here than the fact that you can append those tweaks directly to CLI invocations without saving any code to a file. :) > Spack installation hashes are what Nix would call full configuration hashes -- they're configuration metadata hashes, not content hashes of the installation. So you could say we're not quite as committed to exact binary reproducibility, but you could also say that this allows us to support relocatable binaries (which Nix does not). Nix supports content-addressed store paths but it's still not very widely used yet, and it's opt-in. A normal system with the feature enabled will have a mix of configuration-addressed and content-addressed paths. Spack looks really, really cool. Thank you for advancing the state of the art. :D I hope Spack's package collection is seeing and will continue to see exponential growth like Nixpkgs and Guix are! It seems like the stronger design fundamentals in these newer package management ecosystems can really enable that.
- chriswarbo 4y ago> nixpkgs is managed by humans who do the dependency solving for you over time One nice feature of Nix is the "import from derivation" pattern: if some Nix definition happens to call the built-in functions 'import' or 'readFile' with a path to some other Nix derivation (AKA build product), Nix will first build that derivation, then import the resulting file to finish evaluating the original definition. This way, we can have one derivation which runs some arbitrary dependency-solving command; and import its result as part of our package's definition. We do this where I work: we run the `mvn2nix` command inside one derivation, to get a JSON file describing our Maven project dependencies (jar and pom files); this is imported, and used to define a self-contained folder of those jar and pom files; then the main project definition runs Maven pointing at that folder. The downside of this approach is that it slows down evaluation: Nix usually has an "evaluation phase", where we figure out what to build; then a "build phase", which runs those builders. When we "import from derivation", this distinction is lost: in order to figure out what to build (e.g. which Maven command to run, to build our project), we must first do some building (e.g. to figure out what should go in the folder that Maven will look in for dependencies). For this reason, the Nixpkgs repo doesn't allow definitions which use "import from derivation"; however, it's a very handy tool for our own personal or organisational projects :) (PS: Many years ago I tried to do something similar for building Haskell projects; but never really got it to work :( )
- soraminazuki 4y agoI'm not sure if I understand the first point. Doesn't Nix currently achieve the same thing feature-wise? For example, spack install packageX %libA@2.0 would translate to nix-build -E 'with import <nixpkgs> {}; packageX.override { libA = libAv2_0; }' For this to work, both Spack and Nix would have to package libA v2.0. If it isn't, the user would need to create their own. Assuming a different version of libA is already packaged in Nix, here's how it would look like without the "nix-build -E" part: let pkgs = import <nixpkgs> {}; libAv2_0 = pkgs.libA.overrideAttrs (old: { src = pkgs.fetchzip { url = "..."; sha256 = "..."; }; }); in pkgs.packageX.override { libA = libAv2_0; } I assume something similar would be required for Spack too. So aside from Spack having a nicer shorthand syntax for customization, I don't get what Spack can do that Nix can't in terms of features. Or to be more specific, how a dependency resolver can eliminate human work.
- tgamblin 4y agoSpack packages are parameterized, so there is one `package.py` file per package, not several, and you can have many versions and options declared. See, e.g., `zstd`: https://github.com/spack/spack/blob/develop/var/spack/repos/builtin/packages/zstd/package.py https://github.com/spack/spack/blob/develop/var/spack/repos/... This was a conscious decision we made to allow many versions to be built without requiring split implementations and without checking out different commits -- that saves some work already. It also forces the repo maintainers to consider the other use cases, which tends (IMO) to make the recipes more portable. But the real question is not just work saving but correctness. You cannot always just swap in libA@2.0 as in your example. For: packageX ^libA@2.0 Suppose that both packageX and libA depend on MPI (a versioned standard for which there are several implementations) and, further, libA requires MPI@3. Spack will ensure that: 1. packageX and libA use the same MPI implementation (e.g., openmpi, mpich, mvapich) 2. the MPI implementation chosen satisfies the provider requirement 3. any version/option constraints that packageX has on MPI are satisfied along with those of libA. You could also imagine that the two packages might have conflicts with certain implementations of MPI, e.g. say packageX conflicts with mpich and libA conflicts with mvapich. You'd have to choose mpich. This isn't just an overlay; it's a constraint solve. The choice of libA@2.0 can have effects on other nodes in the graph, and different choices may need to be made elsewhere; maybe even things like disabling options or choosing different dependencies.