-
Notifications
You must be signed in to change notification settings - Fork 698
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
Nix integration is not supported in new-build. #4646
Comments
It seems |
/cc @ttuegel |
I intentionally did not implement Nix integration for the |
Re |
@ttuegel What are the challenges involved in implementing this? Do you think it's a reasonable goal for someone like me who has never contributed to Cabal before? |
What is the goal exactly for nix integration with new-build? To translate into nix exprs? To build C deps using nix? To do the whole build using the cabal CLI but use nix under the hood to do the builds? |
In a |
So having all of a project's non-local dependencies installed via nix means translating the project plan into nix exprs and using nix to execute that. It's hardly any more work to also be able to make nix exprs for building the local deps too, which would be super-useful for CI. BTW, I don't think single-package nix exprs really make sense for us, a project corresponds to a particular set of packages with particular configuration for them all. Or to put it another way, cabal new-build's nix integration / cabal2nix should really be whole-project not single package. |
Not quite. The purpose of |
Ok, whereas for cabal new-build, it'd ignore |
No I don't think so. The point of using Nix with Haskell is to use Nix to define all your dependencies in a package set. Cabal should be doing no planning / solving, as that work is meant to be done by the nix expressions. I would expect it to work exactly how it does currently with |
Ok, so that's a completely different requirement and it's incompatible with cabal new-build and project files. We cannot use new-build to get nix to install all the deps when nix uses its own choice of configuration of those packages because then there is no guarantee that the configuration matches up with what we need for this project. The project file (+ solving & elaboration) determines the configuration of all the Haskell dependencies all the way down. It does not just pick versions but all details of the configuration (which it then hashes nix-style). We cannot arbitrarily substitute differently configured deps and expect it to work. The fact that the old build's nix integration code did this is no evidence that such a thing is possible. That approach was not deterministic and was easy to break for exactly these kinds of reasons. |
There are three levels of integration I can imagine:
|
To be totally clear, the point here isn't to use I also don't believe it should be incompatible. I can actually emulate the desired effect just by manually entering a nix-shell and running the Cabal commands in there myself. |
We absolutely do not want Cabal dictating Nix expressions. This completely defeats the purpose of nixpkgs. |
Ok, it's true that cabal can solve for "pinned" pre-installed packages such as the ones that are in the global package db. In principle one could try doing that with a very large pre-installed collection. It'll obviously completely change the solver results vs what you'd get normally, but if the solver finds a solution then it should indeed be ok. So I take it back, it is plausible by effectively changing the environment in which we consider the project. Instead of being a GHC + core libs + hackage source packages environment, it'd be a GHC + nix hs libs (+ hackage?) environment. What cabal would need in this case is the information for all the pre-installed packages, but presumably before they are actually installed since we'd want cabal to tell nix to install them. Basically we'd need the nixpkgs collection to provide a database in the style of the ghc-pkg format. This would then be an input to the solver, much like the ghc-pkg global db is now. So yes you can enter a suitable nix shell now and cabal will pick up those global packages, but if you want cabal to be able to pick/configure that shell then it needs the info up front about all those packages it can choose from. I don't know how the current cabal build code does that, I rather suspect that it does not. |
In this workflow, Cabal doesn't pick/configure that shell. You use Nix for this stuff to have that shell predefined via your Nix expressions. It's much like Stackage in that regard, in that your Nix shell pulls the packages you need from a predefined package set which is not solved by Cabal, but rather defined in a large Nix expression somewhere upstream (probably on GitHub in NixOS/nixpkgs). This should never ask Cabal for its build plan, and Cabal should never try to install anything. The Nix shell handles all of that. |
To clarify, |
Then I don't understand. If you're already in a nix shell pre-configured with the nixpkgs environment then everything already works, what is there to integrate? |
Not much =P In all the other commands, |
I'm sorry, I literally don't know what this means. What shell are you starting in? Is cabal starting a new shell instead of executing it's normal effect? Or is it executing its normal effect from within some new shell? How does it know what shell to start? Why do we need to be inside a new shell when you as a nix user can just enter the shell directly and run all cabal commands inside it? For context: it's worth noting that I have no idea what |
Apologies for explaining this so poorly. I'll try to start from the beginning to provide maximum context for those of us who don't use Nix. Nix is a programming language that you use to define build systems. Its main tenets are laziness and purity. Any given build (called a "derivation") is essentially two main components: the shell required to build it in, and the script to build it. This script is generally meant to be "pure," in that it is hopefully close enough to deterministic. Therefore, all the dependencies provided to a derivation's shell are also fairly deterministic. It's usually considered best practice with Nix to avoid global installation of things, and instead to rely on building nix shells that provide you with all the tools you need to do your build. This way merely modifying the nix expressions you use does not require any lengthy installation process. The next time a tool or person enters the nix-shell, they will be given the correct, updated environment without even thinking about it. With Haskell, you define a nix shell that depends on all the haskell dependencies you need. Then you enter the nix shell and use cabal from there. Now, there are some shorthands you can use to do this automatically: |
(BTW I'm quite familiar with the nix principles, I've read the papers and you may notice that cabal new-build is based on stealing ideas from nix: determinism, hashing to generate ids, nix-style store, multiple independent environments etc) Ok, so one thing I don't get here is why don't you just enter the nix shell first and then run cabal or other tools that invoke cabal? Why would you want to start in some other shell and run So your "nix support" is effectively just a shell script that does |
Unfortunately, that is not always up to me =P The most common example is editor tooling. Unless I want to always start my editor from inside a nix shell, and restart it after any change to any nix expression, most editor integrations will rely on calling It is also tedious to have to keep track of whether I need to exit the nix shell before running my next cabal-related command. |
I'm still trying to understand what we'd need to do beyond what can be accomplished by a shell-wrapper as has been pointed out that could be simply named #!/bin/sh
exec nix-shell . --run "cabal.real $@" |
Scripts like that have all sorts of edge cases. For example, when that script is run when the working directory is not the project root directory (or worse, when the working directory isn't the project root and has a different shell.nix file). |
Ok, let's assume (again, this is for me understanding the requirements) we already had |
I'm still not convinced that no edge cases remain, though I admit I can't think of any. Regardless, as Plus, I don't really think this is sufficient. I was saving this for another issue, but I think you should be able to do some Nix configuration in the
# shell.nix
{ myNixArg ? true }:
... It seems useful to be able to give slightly different parameters for Cabal shells. Also, I tend to think wrapper scripts are unfortunate hacks. Their prevalence in nixpkgs makes me queasy =P |
The other thing is consistency. If we're not going to support it in |
If there is a cabal.config.local file in the project root directory, readGlobalConfig reads it after ~/.cabal/config. This is intended to allow the user to specify site-specific global options on a per-project basis. Global options cannot be specified in cabal.project.local, which applies options only to packages in the project. See also: haskell#3883 haskell#4646
If there is a cabal.config.local file in the project root directory, readGlobalConfig reads it after ~/.cabal/config. This is intended to allow the user to specify site-specific global options on a per-project basis. Global options cannot be specified in cabal.project.local, which applies options only to packages in the project. See also: haskell#3883 haskell#4646
The global configuration in ~/.cabal/config or CABAL_CONFIG should be applied to all packages, even in the project (new-*) commands. Previously, package configuration fields applied only to packages in the actual project. A new field (projectConfigGlobalPackages) is added to ProjectConfig to track the global package configuration. See also: haskell#3883, haskell#4646
If there is a cabal.config.local file in the project root directory, readGlobalConfig reads it after ~/.cabal/config. This is intended to allow the user to specify site-specific global options on a per-project basis. Global options cannot be specified in cabal.project.local, which applies options only to packages in the project. See also: haskell#3883 haskell#4646
If there is a cabal.config.local file in the project root directory, readGlobalConfig reads it after ~/.cabal/config. This is intended to allow the user to specify site-specific global options on a per-project basis. Global options cannot be specified in cabal.project.local, which applies options only to packages in the project. See also: haskell#3883 haskell#4646
Nix integration is a joke, don't do it :) Stack failed with it. It is simply out of scope. After all, why not integrate with RPM or APT? P. S. As well as with Docker. |
Nix integration in Stack works well for me (on NixOS), I'd really like something similar in cabal. |
If people don't object I'll try to summarize the discussion so far as somebody who has used all three of
Feel free to correct me if I got anything wrong |
One further issue is that cabal v2 commands will always fetch and install packages that aren't in the db into the store, so you can't ensure you're only using the "closed universe" of packages made available into the custom pkgdb that nix builds. There are workarounds that can/should be integrated into nix scripts. In particular, setting a custom cabal_config env variable (as per here: #5322) can be used to prevent cabal from installing additional packages from hackage. |
Following on the comments of @Gabriel439 and @gbaz : When I enter a |
@gbaz, new in cabal-install 3.0, |
IMO https://github.com/input-output-hk/haskell.nix is the future, and a much better friend of Cabal and stack in particular, and any future work on nix-cabal integration should focus on that. One goal in that regard is that Nix and Cabal should be able to hash files in the same way. I think the best thing to do is teach them both git tree hashes. Once we reach the point where hackage builds, patched hackage builds, and local builds are all cached with comparable keys, things will start to move smoothly. |
Until this is solved in Cabal proper, you can use a workaround: nix-cabal. Installation: curl https://raw.githubusercontent.com/monadfix/nix-cabal/master/nix-cabal -o $HOME/.local/bin/nix-cabal
chmod u+x $HOME/.local/bin/nix-cabal Then whenever I want a project to use nix-cabal, I create a ;; .dir-locals.el
((haskell-mode
. ((haskell-process-type . cabal-new-repl)
(haskell-process-path-cabal . "nix-cabal")))) In the shell, you just use Another solution is lorri. It runs a background daemon that keeps nix-shell warm, and uses direnv to automatically enter nix-shell whenever you are in the project directory. It is somewhat finicky, though. |
I think facts has demonstrated nix integration in v2 build is at best a very low priority enhancement. |
Last I checked (years ago) Cabal's (and Stack's) Nix integration wasn't really what I wanted as a Nix user anyways, so I don't mind if they whither away and are replaced with something more proper later. |
Personally I've defined cabal = |
Mmm while that is better than nothing, it is so far from adequate Nix support it feels like false advertising to call that |
At least, the
cabal-install
one can compile with the current latest release tag (with GHC 8.2.1) does not support it.The text was updated successfully, but these errors were encountered: