Is there any way to define flags for cabal dependencies? - haskell

I recently ran into a Cabal issue that I only managed to solve by manually installing transformers-compat with the -f transformers3 flag in my cabal sandbox before running cabal install for my project.
Is there any way to indicate in my application's .cabal file that I depend on a library so that it is built with the specific build flag?

Newer versions of Cabal let you specify constraints in your cabal.project.local or cabal.project file. For example:
constraints: hmatrix +openblas
Is there any way to indicate in my application's .cabal file that I depend on a library so that it is built with the specific build flag?
No, but in your case this is not actually a problem in the solver and is rather and uninformative error (caused by someone's less than judicious uses of flags).

Looks like it's not possible to specify such a dependency via the build-depends field in your .cabal file. buildDepends is defined as [Dependency], where data Dependency = Dependency PackageName VersionRange. You can use cabal install --constraint="transformers-compat +transformers3", though.
Looking at the transformers-compat.cabal file, I think that the solver should be able to figure out the correct flag assignment if you constrain your dependency on transformers appropriately. E.g. build-depends: transformers >= 0.3 && < 0.4 should force the solver to choose transformers-compat +transformers3. If this doesn't work, it may be a bug in the solver.

I also struggled for a long time to find a solution to this problem. I just found one! You have to modify the global cabal configuration file at ~/.cabal/config. Add a constraints line like this to the initial section of the file:
constraints: hmatrix +openblas
This enables the openblas flag for the hmatrix package. It will be used automatically the next time the package is installed. If there is a way to set such a flag locally for a sandbox, I could not find it.

You cannot do this with Cabal.
One way to do this is to use Stack. Edit your stack.yaml to include
flags:
transformers-compat:
transformers3: true
See also the section on flags.

cabal now supports an elegant way to do this similar to stack, through cabal.project configuration options.
package transformers-compat
flags: +transformers3
will add the flag transformers3 when building the package transformers-compat.

There are a couple of ways to constrain the version for installation.
Add lower and upper bounds to package versions in the cabal file like Mikhail mentioned above, example of such a file here
Additionally, you can override the settings in the .cabal file with the flag cabal install --constraint="bar-2.1"
To remove a specific version of a package:
In a sandbox you can unregister a version with cabal sandbox hc-pkg unregister bar-2.1
Global unregistering can be done with this command outside of sandbox ghc-pkg unregister bar-2.1

Related

How do you import a Haskell module that was installed using Cabal?

I installed the timezone-series Haskell module using cabal install timezone-series-0.1.5.1.
I then defined a module named Main.hs that starts with:
import Data.Time.LocalTime.TimeZone.Series -- from timezone-series-0.1.5.1
when I run ghc Main.hs, GHC throws the following error:
/home/ubuntu/Main.hs:2:1: error:
Failed to load interface for ‘Data.Time.LocalTime.TimeZone.Olson’
I tried explicitly including the cabal directory in GHC's search path using:
ghc -i/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/ Main.hs
This resulted in the correct path being searched, but GHC is only looking for files ending in the suffix ".hs":
Locations searched:
...
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Series.hs
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Series.lhs
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Series.hsig
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Series.lhsig
Cabal installed interface files instead however:
/home/ubuntu/.cabal/lib/x86_64-linux-ghc-8.0.2/timezone-olson-0.2.0-KqRNJj3zomR7zz2Yx6P5Oq/Data/Time/LocalTime/TimeZone/Olson.hi
From line 318 of GHC's source code it looks like GHC ignores "*.hi" files unless it is called in single-shot mode (with the -c flag). Is this correct? (See: https://github.com/ghc/ghc/blob/67a5a91ef5e61f3b3c84481d8a396ed48cd5d96e/compiler/GHC/Unit/Finder.hs)
How can I get GHC to import this module?
An help will be greatly appreciated!
My suggested ways of installing packages in order of my preference:
Make a cabal package and add timezone-series you want to install to the build-depends field as described in the cabal manual.
Use the experimental cabal-env tool to basically automate the process of point 3 below, but then with the global environment. This makes a new build-plan every time you install a new package, so it is like removing the package environment and building it again with all the old packages and the new package added to it. You can add specific constraints like this: cabal-env "timezone-series == 0.1.5.1".
Install a package into local package environment with cabal --package-env . --lib timezone-series. You can add as many packages as you want after the --lib option to install more than one package. If you later want to use a different set of packages simply remove the .ghc.environment.* file that is generated and rerun the installation with a new set of packages. GHC will automatically use these package environment files that are in the current or parent directories. You can specify specific constraints with the --constraint option like this: --constraint "timezone-series == 0.1.5.1".
Use cabal install --lib timezone-series to install it directly into the global environment (~/.ghc/x86_64-linux-8.0.2/environments/default), this will fail if a conflicting package was installed earlier. When you run into errors you can remove that package environment and try again.
Finally, I want to note that GHC 8.0.2 is quite old, so I would advise you to upgrade if you don't have a specific reason for using that version.

Cabal cannot find locally sourced (yet correctly installed) packages

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:
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.
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.
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.
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.
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...
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...
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...
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!
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?

Is there a tool for upgrading the dependecies' upper bounds to the latest versions of each dependency?

I'm trying to upgrade the dependencies of the pipes-files package, so that it can be included in the latest stack LTS. The pipes-files package does not contain a stack.yaml file, and the pipes-files.cabal file contains quite some upper and lower bounds on its dependencies, e.g.:
base >=4.7 && <4.10
, transformers >=0.3 && <0.6
, transformers-base >=0.3 && <0.6
-- and quite some more ...
Is there a tool that I can run to get the latest versions of each of these dependencies? The closest thing I could find was packdeps but it will require me to search for the dependencies one by one.
Preliminary notes:
hierarchy, a dependency of pipes-files, has the same issue you are trying to work around -- it is not in Stackage and has version bounds outdated with respect to the latest LTS. That means you'll first have to get it to build successfully, and then add your tweaked version of it to the extra-deps in the stack.yaml of pipes-files.
cabal gen-bounds, which "suggest[s] dependency version bounds that conform to Package Versioning Policy", is, in principle, the right tool for the job. However, getting cabal-install to behave according to the restrictions of a Stack-centric environment is not always straightforward. While this seems enough to run cabal gen-bounds from a bash shell in the absence of a Stack-independent GHC installation...
PATH=$PATH:$(stack path --compiler-bin) cabal gen-bounds
... I couldn't figure out how to get it to follow the version restrictions of the Stack(age) snapshot -- in particular, the --package-db option, which can be helpful with commands such as cabal configure, doesn't seem to be accepted by gen-bounds.
As I don't know how to make cabal gen-bounds and Stack cooperate, I will suggest a somewhat more convoluted method, but one that doesn't require using cabal-install directly. It uses Jenga, a tool that can retrieve the version information left implicit by your choice of Stackage snapshot. Jenga is not on Stackage; to install it with Stack, grab the sources from GitHub (or with stack unpack jenga) and then run stack init --solver followed by stack install.
Remove all version bounds from the hierarchy.cabal file (or whatever the relevant .cabal file is).
stack init --solver, to create a stack.yaml file with any extra-deps that might be necessary. (You can use --resolver to explicitly choose the snapshot to be used.)
stack build, as a sanity check that the package is buildable.
In the generated stack.yaml, check whether the extra-deps field is commented out; if so, uncomment it. This is necessary for step #6 to work.
jenga -i hierarchy.cabal, which will print the exact dependency versions Stack would use to build the package.
Paste the versions Jenga gave you into the build-depends of the .cabal file, adjusting then if desired (at a minimum, you'll probably want to relax the minor version bounds for PVP compliance -- e.g. changing base == 4.9.1.0 to base == 4.9.*).

At least the following dependencies are missing

I am starting a project. my cabal file specifies
build-depends: base <4.8, criterion >=1.0 && <1.1, memoize <=0.6
criterion is in the sandbox of current directory.
If I check with ghc-pkg list memoize it will show.
C:/Program Files/Haskell Platform/2014.2.0.0\lib\package.conf.d: (no packages)
C:\Users\Song\AppData\Roaming\ghc\x86_64-mingw32-7.8.3\package.conf.d: memoize-0.6
memoize is globally available.
However, when I run cabal repl, cabal complains memoize is missing. Do I have to re-install the package in the sandbox in this case?
Yes, you do. The purpose of sandboxes is to work independently of the user package database, not in addition to it. You can override this by adding back the user package database with the --package-db flag, but it isn't a recommended workflow.

How to check for haskell package versions in ./configure?

how can I tell configure to check for version >= x.y of a given Haskell package?
Thanks,
Use cabalvchk: http://hackage.haskell.org/package/cabalvchk-0.2
For example, to verify that the version of parsec is >= 0.4, you could issue:
$ cabalvchk parsec '>= 0.4'
The return code will be zero if the version constraint is satisfied and non-zero otherwise. The version constraint can be anything cabal understands. An optional third parameter can be non-blank to request verbose output.
I don't know much about configure; can you ask it to run a particular command? If so, then ghc-pkg latest should help you out. For example, here's a run on my machine for the zlib package:
% ghc-pkg latest zlib
zlib-0.5.3.1
% ghc-pkg latest --global zlib
zlib-0.5.3.1
% ghc-pkg latest --user zlib
ghc-pkg: cannot find package zlib
zsh: exit 1 ghc-pkg latest --user zlib
The --global should be used for system-wide installations, and no flag at all for user-specific installations. The --user flag should only be used when you want to check whether a user has a local installation of a package (that may override the global one).
Unless you have a reason not to, I recommend ditching configure in favor of cabal. For cabal, the solution here is to first cabal init in your project's directory, then check that you have a line like this in the .cabal file that's created:
build-depends: zlib >= 0.5
The cabal toolchain is the standard for Haskell projects (because it automates and simplifies many things, including dependency-chasing). You can also ask cabal to invoke configure if there are other dependencies. Open a separate question if you'd like more information about this.
Perhaps the better question is: should you? Checking for a specific version number is one of the great arguments in the autoconf world, and the general winner of the debate is the side which says you should never do it. What specific feature of Haskell do you need? Test for that. As a simple example (unrelated to haskell), suppose your program uses inotify so you want the configury to test if it is available. You could just test if the kernel version is > 2.6.13, but then when Joe tries to build your program on his 2.4.xx version in which he has patched in inotify capability, he's going to be really irritated that your program won't work.
You do not care if Haskell > x.y is available. Instead, there is some specific feature of Haskell that you want that was introduced in x.y; test for that feature.
Using ghc-pkg list, you can get a list of installed versions of a package in ascending order. You should hopefully be able to filter through this list looking for a match. (I don't know how to do this with configure, sorry).
$ ghc-pkg list yesod
/home/ahammar/.haskell/lib/ghc-7.0.2/package.conf.d
/home/ahammar/.ghc/x86_64-linux-7.0.2/package.conf.d
yesod-0.8.2.1
yesod-0.9.1
yesod-0.9.2.2
Try something like this:
# Find ghc-pkg, so we can do version checks
AC_ARG_VAR([GHC_PKG], [Path to ghc-pkg])
AC_PATH_PROG([GHC_PKG], [ghc-pkg])
AS_IF([test -z "$GHC_PKG"], [AC_MSG_ERROR([Cannot find ghc-pkg.])])
# Check that the package actually exists
AC_MSG_CHECKING([for Haskell package foo])
AS_IF([$GHC_PKG latest foo > /dev/null 2>&1],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
AC_MSG_ERROR([Cannot find foo])])
# Check its version
AC_MSG_CHECKING([if foo is new enough])
foo_ver=`$GHC_PKG latest foo | sed 's/^foo-//'`
# At this point you have the version of foo and the minimum version you want.
# The rest of the test is pretty easy to write, use cut and test to compare the
# version numbers. If it's new enough, AC_MSG_RESULT([yes]).
# If not, AC_MSG_RESULT([no]) and AC_MSG_ERROR([foo is not new enough.])

Resources