Is there a way to get version of Cabal package in source code of the package? - haskell

Say I have a console program that has an option to display its
version. Currently whenever I update version in .cabal file I need to go
to source code and update constant — string representation of current
version number as well. This feels against DRY principle and now I'm
wondering, is it possible to get version of my project as defined in
.cabal file from source code? Maybe Cabal defines some CPP macro or
something else?

Indeed Cabal allows to access information from .cabal file in your
program. According to Cabal documentation,
you can import special module that exists during building of your package,
like this:
import Paths_packagename (version)
import Data.Version (showVersion)
myVersion :: String
myVersion = showVersion version
The module Paths_packagename provides version of type
Version.

Related

Cabal update now can't load any modules from "hidden packages"

I've been working on a project and recently I did a cabal update.
I usually roll into ghci like:
$ ghci -package-db ~/.cabal/store/ghc-8.10.7/package.db
After the update loading module in my project results in even the basic Haskell modules like System.Random or MonadIO fails with the following errors when trying to load my own module called ProcessIO:
ProcessIO.hs:50:1: error:
Could not load module ‘Data.IORef.MonadIO’
It is a member of the hidden package ‘monadIO-0.11.1.0’.
You can run ‘:set -package monadIO’ to expose it.
(Note: this unloads all the modules in the current scope.)
Locations searched:
Data/IORef/MonadIO.hs
Data/IORef/MonadIO.lhs
Data/IORef/MonadIO.hsig
Data/IORef/MonadIO.lhsig
I checked that maybe the .cabal file build-depends versions might have been altered, but the cabal package.db directory contains all the right versions of the dependencies in the .cabal file. For example the error above complains abot monadIO-0.11.1.0 being hidden however: in package.db/ we see the right version exists:
monadIO-0.11.1.0-0aec75273f3fef94783e211a1933f8ac923485a963be3b6a61995d4a88dd1135.conf
I should say I haven't looked at the package.db files before because everything simple worked so there may be something telling about the .conf file name that signals something is wrong.
Either way, can't build anything and I need some help!
EDIT: posting my default environments file ~/.ghc/x86_64-linux-8.10.7/environments/default in case it matters:
clear-package-db
global-package-db
package-db /home/surya/.cabal/store/ghc-8.10.7/package.db
package-id ghc-8.10.7
package-id bytestring-0.10.12.0
...
(Let me know if I need to share more of it... or less)

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.

What is the default path of the source of Haskell modules (Prelude, etc.)?

I want to browse the source of the installed Haskell modules on my machine.
What is the default search path of Haskell's import?
So there's a bit of an impedance mismatch here, since packages are generally installed as binaries (though the most popular way to get the binaries is to build from source). Still, it's not too bad. The rough plan will be to map from module name to package/version pair, then ask cabal to unpack a copy of that version of the package.
Let's say I wanted to read the source for Data.Text. Then:
% ghc-pkg find-module Data.Text
/usr/local/lib/ghc-8.2.1/package.conf.d
(no packages)
/home/dmwit/.ghc/x86_64-linux-8.2.1/package.conf.d
text-1.2.2.2
% cabal unpack text-1.2.2.2
Unpacking to text-1.2.2.2/
You will now have a text-1.2.2.2 directory containing the source for the text package -- including a file in text-1.2.2.2/Data/Text.hs that contains the source for the Data.Text module.

How can I pin a version of a Haskell dependency to a version of an underlying native dependency with Cabal?

In my particuar case, I have a dependency in my Cabal file on the Haskell package bindings-libzip. In particular, I could accept several different versions of libzip, e.g. bindings-libzip-0.11 or bindings-libzip-0.10. These in turn have a dependency on the respective native C libzip libraries versions 0.11 and 0.10.
Therefore I have bindings-libzip >= 0.10 < 0.12 in my .cabal file.
The Haskell package bindings-libzip-x specifies with PkgConfig-Depends that libzip version x must be present on a client machine. Let's say a downstream user has version 0.10 of libzip installed. However, when pulling down my package, this user pulls down the latest dependencies possible and transitively pulls down version 0.11 of bindings-libzip. This causes the build process to error out with a message about an incorrect version of libzip installed.
Is there any way I can specify in my .cabal file to use bindings-libzip-0.11 if and only if pkg-config detects version 0.11 of libzip and to use bindings-libzip-0.10 if and only if pkg-config detects version 0.10 of libzip?
I'm submitting another answer because this uses another idea...
Using a custom Setup.hs with defaultMainWithHooksArgs allows you to inspect and modify the args to the cabal configure command.
This is a Setup.hs which does no modification:
import Distribution.Simple
import Distribution.Simple.Configure
import System.Environment
main = do
args <- getArgs
defaultMainWithHooksArgs simpleUserHooks args
If your .cabal file has a flag defined, e.g.:
Flag Foo
Default: False
then in the args you will see "--flags=-foo". So the idea is:
Define two flags in the .cabal file - use10 and use11 to select which version of bindings-libzip to use.
In your custom Setup.hs determine which version to use.
Find the "--flags=..." arg and modify it appropriately before passing it along to defaultMainWithHooksArgs.
I think the way to do this is with a custom Setup.hs file (specify build-type: Custom in the .cabal file.)
You can override specific stages of the build process by using a main like this:
main = defaultMainWithHooks $ simpleUserHooks { preConf = myPreConf }
myPreConf :: Args -> ConfigFlags -> IO HookedBuildInfo
myPreConf args configFlags = ...
It is also likely that overriding the confHook is what you want.
Some links:
all cabal hooks: data UserHooks
standard sets of hooks: (link)
Examples of custom Setup.hs files overriding confHook:
abcBridge
arb-fft
cabalmdvrpm
darkplaces-text
GLFW
happybara-webkit-server
haskeline
HDBC-postgresql
helics
hlbfgsb
hlibsass
hpqtypes
hruby
hsqml
hubris
illuminate
intel-aes
keera-posture
KiCS-debugger
libpq
llvm-general
morfeusz
postgresql-libpq
tamarin-prover
tamarin-prover-term
tamarin-prover-theory
tamarin-prover-utils
voyeur
wxc
wxcore
In particular, the hruby Setup.hs looks like it is doing something like what you want to do.

Haskell Hidden Packages: Data.HashSet

I'd like to use Data.HashSet in Haskell. So I put import Data.HashSet at the beginning of my program. GHCi complains: Could not find module Data.HashSet.
My questions are:
How can I get Data.HashSet to work?
I've read somewhere that this is part of a hidden module or package. Why is the module hidden? Does "hidden" mean that I should not use it?
If I should not use it, is there a better alternative for a haskell data structure with a near-constant lookup time?
Data.HashSet is a module in the unordered-containers package, and also in the hashmap package. If you have either package installed,
import Data.HashSet
should work out of the box, since it is an exposed module of both packages. To install it (using unordered-containers, since that is the commonly used one),
cabal update
cabal install unordered-containers --dry-run
-- check that it wouldn't reinstall anything, if all's fine
cabal install unordered-containers

Resources