Cabal cannot find locally sourced (yet correctly installed) packages
Asked Answered
O

1

6

I recently upgraded to Cabal 3.2 (and GHC 8.10) and I am running into some major issues that make some of my project non-buildable anymore...

Thorough description of the problem

Here is a minimal (not) working configuration that fails every time:

  1. I start off with a clean Cabal configuration (by deleting ~/.cabal); the reason for that will appear later in the post. I run cabal update to recreate the .cabal directory and to ensure Cabal is working.

  2. I create a project (let's call it test1) using cabal init. This is a library project with one exposed module (conveniently named Test1) that exports some dummy function foo. I run cabal build, then cabal install --lib; everything is running smooth, so far so good.

  3. Just to be sure, I leave the project directory and fire up GHCi. I type in :m Test1 to load the module I created earlier, and it works! I can type in foo ... and see my function executed. Also, I list the content of ~/.cabal/store/ghc-8.10.xxx and see that the test1-xxx directory is there.

  4. I then create a new project, test2, still using cabal init. This time, I configure it to be an executable, and I add test1 as a dependency (using the build-depends field). But this time when I run cabal build, I run into some issue:

~/projects/haskell/test2> cabal build
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: test2-0.1.0.0 (user goal)
[__1] unknown package: test1 (dependency of test2)
[__1] fail (backjumping, conflict set: test1, test2)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test2, test1

It seems to me like package test1 cannot be found, however I can access it from GHCi (and GHC for that matters) and it is present in ~/.cabal/store...

But unfortunately there is more.

  1. I create a third project, test3. This is a library, and it depends on nothing else than base (so in particular it does not depend on test1). The lib exposes one module, Test3, with one function exported, bar. I run cabal build, no problem here. But when I want to install test3 with cabal install --lib I run into some errors:
~/projects/haskell/test3> cabal install --lib
Wrote tarball sdist to
/home/<user>/projects/haskell/test3/dist-newstyle/sdist/test3-0.1.0.0.tar.gz
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] unknown package: test1 (user goal)
[__0] fail (backjumping, conflict set: test1)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test1

It seems that it cannot find test1, although it has been installed correctly; may be this is a remnant of the failed build of test2 though...

  1. Just to be sure, I fire up GHCi and type in :m Test3, but GHCi tells me that it cannot find module Test3 (and even suggests this is a typo and I was meaning Test1), showing that test3 indeed did not get installed, although it got successfully built...

  2. Okay there is one more quirk to this whole situation: I create once again a new project with cabal init, called test4, which is an executable that (again) depends on nothing else than base. I keep the default Main.hs (that just prints "Hello, Haskell!"). I run cabal build: no problem. Then I run cabal install and... No problem either? I run test4 in a random location and it fires up the executable, printing "Hello, Haskell!" in the terminal...

  3. And there is one last thing: I go to some random location and I run cabal install xxx --lib where xxx is a library package available on Hackage (for example xml) and:

~> cabal install xml --lib
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] unknown package: test1 (user goal)
[__0] fail (backjumping, conflict set: test1)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test1

This is the reason why I need to nuke .cabal regularly... Right now I seem to be in some kind of stale state where I cannot install any library anymore.

Technical configuration and notes

I am running Cabal 3.2.0.0 and GHC 8.10.0.20200123. I installed them from the hvr/ghc PPA, and I made sure there are no other versions of those tools anywhere on my computer.

Just as a note, I am running Ubuntu 18.04.4 LTS (with XFCE so XUbuntu to be exact). Everything else (seem to be like it) is up to date.

Last thing, regarding the *.cabal files I use for building, they are pretty much the ones generated by cabal init, except I switch executable xxx for library in the case of libraries, and I simply add a exposed-modules field for exposing modules for the libraries (so Test1 for test1 and Test3 for test3 respectively). I also use build-depends in test2 to make the project depend on test1. Apart from that, they are pretty much left untouched.

Notes and thoughts

I must confess that I am new to Cabal 3; until last week I was using Cabal 1 (because I never bothered to update it; yes I know this is bad). With Cabal 1 I did not have any problem whatsoever, and I was perfectly able to install a package from local sources and depend on it in other projects...

I feel like I am doing something wrong; maybe am I not using the correct Cabal commands? I saw somewhere something about cabal new-build and cabal new-install but it does not seem to do anything more than cabal build and cabal install, at least in my case. I also wanted to investigate sandboxes but it seems that has disappeared since version 2 of Cabal.

There is also a slight possibility this is a Cabal bug, but I don't find any relevant issue on the bug tracker that may be related to my problem...

What do you think about this? What am I doing wrong? Do you see any alternative or possible fix?

Thanks a lot!

Ornithine answered 12/3, 2020 at 9:20 Comment(4)
cabal 3 significantly changes how things work. Notably, for multi package projects you need a cabal.project file. I'm not sure what step exactly went wrong, but have you tried following the documentation? haskell.org/cabal/users-guide/nix-local-build.html#quickstart (anything in the section "Nix-style local build" is relevant, because that's how cabal 3 works now, it was just new in cabal 2)Lemuroid
@Li-yaoXia I am not entirely sure what is the cabal.project file about, but it seems to me it is related to project architecture in general... I would expect that you can actually install a package from local sources and be able to access it the exact same way as if it had been installed from Hackage, right? Here the doc is more about splitting your project into multiple sub-packages (if I understand correctly).Ornithine
Can you glue all these steps in a docker file and reproduce the issue in a portable manner?Trainbearer
For cabal 3.0, the new-* and v2-* subcommand prefixes have the same behavior as the unprefixed commands, they're there because they were used in the migration period between versions 2.x and 3.Sos
S
6

GHC environment files

A GHC installation comes with a certain number of packages out-of-the box. base is one of them but there are others, for example text. If you install GHC alone (no cabal or stack) and open ghci, it should let you import Data.Text without problems.

What if you want GHC or ghci to be aware of other compiled packages present in your filesystem? You can point GHC to additional package databases using command-line flags, but there's also the concept of package environment files.

Environments are plain text files that contain a list of package-related GHC flags. There might be a global environment at ~/.ghc/$ARCH-$OS-$GHCVER/environments/default, and there might also exist local environments which only affect GHC and ghci commands invoked inside the same folder. The exact rules for search are described in the GHC User Guide.

What does cabal install --lib actually do?

By default, it modifies the global environment file, so that GHC and ghci can now find that library. That's why point 3) worked. The actual compiled binaries of the library still reside in the cabal store though.

We can also create local environment files. For example cabal install sop-core --lib --package-env . will create the environment file .ghc.environment.xxx in the current folder, and the library will be available to ghc and ghci when they are invoked there.

Why isn't test1 available for test2?

Modern cabal makes a distinction between local packages and external packages.

  • local packages is the set of packages you are developing together in a project, being edited, recompiled and changed repeatedly. They are built "inplace" and not seen outside the project. They can depend on each other.
  • external packages are dependencies from build-depends: whose source code is downloaded from a package repository and which, when compiled, are put in the cabal store so that other Cabal projects might make use of them without re-compiling.

The list of local packages and other project-level configuration details are specified in a cabal.project file. But you don't need one if you work on a single isolated package; the default list of packages is simply ./*.cabal.

cabal wants to completely control the build environment of local packages, and will ignore the global environment file. In your case, you'll have to make test1 and test2 local packages in the same project (likely the best option) or publish test1 and treat it as an external package.

Note that "cabal project" is a concept relevant only during development. Packages are published independently, there are no "projects" in Hackage or other repositories, just packages.

What if I want to treat test1 as external without publishing it to Hackage?

You will have to set up a local package repository, basically a non-public Hackage.

You can tell Cabal about additional package repositories in the Cabal configuration file, that is, the file that configures cabal itself. Its location is given in the last line of cabal --help.

But how to set up the repository? The hackage-repo-tool can help with that.

Why did test3 fail? Why did further library installs fail?

That's weird, I have no idea why that happens. Did you by perchance delete the ~/.cabal folder between steps 3) and 5) ? What happens if you delete the global GHC environment file and try again?

Sos answered 12/3, 2020 at 18:18 Comment(4)
Thank you very much! I now understand that the difference between local and external is crucial; tbh I was a bit confused as to why it is called "Nix-style". I thought it was something else. Thanks for clearing that out.Ornithine
Regarding last point, I did not delete the ~/.cabal between points 3 and 5 (in fact I did not take any extra step from what I described). Upon deleting ~/.ghc I can install libraries again... That's even weirder, uh?Ornithine
With cabal 3.2 there's now a simpler alternative to hackage-repo-tool for local repositories: github.com/haskell/cabal/issues/6359Rebane
Jeez, in the name of everyone, I would like to point out that that is really messed-up. cabal install should install to /usr/local and there should be a tool to turn it into a package of your distribution’s package manager, like normal. Not cabal thinking it’s OK to completely do its own thing just because that’s all that’s available on Windows/MacOS joke OSes.Adao

© 2022 - 2024 — McMap. All rights reserved.