Cabal project seems to be importing multiple versions of Cabal Library breaking Setup.hs - haskell

Short:
I have a cabal project that depends on a library built using cabal sandbox add-source. This library exports a preprocessor function BuildInfo -> LocalBuildInfo -> PreProcessor. When I try to use that preprocessor in the Setup.hs for my main cabal project, I get the error:
Couldn't match type ‘Cabal-1.18.1.3:Distribution.PackageDescription.BuildInfo’
with ‘BuildInfo’
NB: ‘Cabal-1.18.1.3:Distribution.PackageDescription.BuildInfo’
is defined in ‘Distribution.PackageDescription’
in package ‘Cabal-1.18.1.3’
‘BuildInfo’
is defined in ‘Distribution.PackageDescription’
in package ‘Cabal-1.20.0.1’
Less Short:
I currently have the following directory structure in my cabal sandbox:
├── main.cabal
├── Setup.hs
├── Main.hs
├── lib
│   ├── myPP.cabal
│   ├── MyPP.hs
│   └── myPP
│   ├── MyPP.hs
│   ├── myPP.cabal
My main package has myPP has a build depends. myPP is a library with MyPP as the exposed module and Cabal and ghc as dependencies. the MyPP module exports a function
myPP :: BuildInfo -> LocalBuildInfo -> PreProcessor
I then run
cabal sandbox add-source ./lib/*
cabal install myPP
In my Setup.hs I have:
module Main (main) where
import Distribution.Simple
import Distribution.Simple.PreProcess
import Distribution.Simple.Utils
import Distribution.PackageDescription
import Distribution.Simple.LocalBuildInfo
import MyPP(myPP)
main :: IO ()
main = let hooks = simpleUserHooks
pp = ("pp",myPP)
in defaultMainWithHooks hooks {hookedPreProcessors = pp:knownSuffixHandlers}
I then attempt to actually cabal build only to get the error:
Couldn't match type ‘Cabal-1.18.1.3:Distribution.PackageDescription.BuildInfo’
with ‘BuildInfo’
NB: ‘Cabal-1.18.1.3:Distribution.PackageDescription.BuildInfo’
is defined in ‘Distribution.PackageDescription’
in package ‘Cabal-1.18.1.3’
‘BuildInfo’
is defined in ‘Distribution.PackageDescription’
in package ‘Cabal-1.20.0.1’
Attempt to diagnose the error:
My understanding is that when you have a package that depends on both ghc and Cabal, then you get the version of Cabal that is linked to when ghc was built. As a result, the myPP package is built with that old version of cabal. When the custom Setup.hs is run by cabal, it imports its version of cabal (which is the one that I have installed). This then causes the error. However, I am hoping that there is a better solution than to just downgrade my version of Cabal....

You won't like the other solution: build ghc from the current HEAD in order to get a newer Cabal (now at 1.21.1.0). Unfortunately, there's just no way to make two different versions of a package coexist in a single build. Downgrading Cabal is probably the least-painful option.

Related

How can I have GHCi reload include changes in the local dependent library?

Full reproducible project here: https://github.com/chrissound/215
I have the following simple cabal file which defines:
a library (source under src-lib)
executable (source under src) in the same project (which depends on the above local library)
cabal-version: 1.12
name: HaskellNixCabalStarter
version: 0.1.0.0
author: HaskellNixCabalStarter
maintainer: HaskellNixCabalStarter
license: MIT
build-type: Simple
library
exposed-modules:
Hello
other-modules:
Paths_HaskellNixCabalStarter
hs-source-dirs:
src-lib
build-depends:
base >=4.12 && <4.13
default-language: Haskell2010
executable app
main-is: Main.hs
other-modules:
Paths_HaskellNixCabalStarter
hs-source-dirs:
src
build-depends:
HaskellNixCabalStarter
, base >=4.12 && <4.13
default-language: Haskell2010
I can open a GHCi repl with:
cabal v2-repl app
However, upon GHCi reloading (:r), it will only reload changes in the app executable, and disregard any changes in the library.
This seems like very limiting / incorrect behavior. How can I fix this / workaround this?
There is a workaround, you either
run cabal repl and then :load src/Main.hs, or
with cabal repl app you'd need to :load src/Main.hs src-lib/Hello.hs.
Now :reload also reloads changes from dependencies.
In the first case it's the :load that somehow also starts loading/following the dependencies. (Not sure why cabal repl app isn't doing exactly the same.)
On the second case you need to explicitly name the modules you want to follow. Also, you need to have the module in who's namespace you want to be in, first. So :load src/Main.hs ..others...
See this on reddit. It appears that cabal can only have one "unit" loaded, but loading other sources with :load seems to subvert that.
I don't think it can be done (yet?). Evidence:
jeff#jbb-dell:cabal-experim$ tree
.
├── cabal.project
├── P1
│   ├── app
│   │   ├── Lib.hs
│   │   └── Main.hs
│   └── P1.cabal
└── P2
├── P2.cabal
└── src
└── MyLib.hs
jeff#jbb-dell:cabal-experim$ cabal repl P1 P2
cabal: Cannot open a repl for multiple components at once. The targets 'P1'
and 'P2' refer to different components.
The reason for this limitation is that current versions of ghci do not support
loading multiple components as source. Load just one component and when you
make changes to a dependent component then quit and reload.

Importing a haskell module from the parent directory

Given the following directory structure:
root
├── scripts
│   └── script1.hs
└── source
   ├── librarymodule.hs
   └── libraryconfig.txt
Where "librarymodule.hs" would be a library exporting multiple functions, where the output is influenced by the contents of the libraryconfig.txt file in his directory.
script1.hs is the file needing to use the functions declared in librarymodule.hs.
I can't find a solution on the internet for a structure as given above and hoped someone could help out.
GHC has a -i option. Under root/scripts/, this will add root/source/ to the search path:
ghc -i../source script1.hs
Also consider packaging your library using cabal so you can install it and use it anywhere without worrying about paths.
Here is a minimal example of a library with data-files:
source/
├── mylibrary.cabal
├── LibraryModule.hs
└── libraryconfig.txt
mylibrary.cabal
name: mylibrary
version: 0.0.1
build-type: Simple
cabal-version: >= 1.10
data-files: libraryconfig.txt
library
exposed-modules: LibraryModule
other-modules: Paths_mylibrary
build-depends: base
default-language: Haskell2010
LibraryModule.hs
module LibraryModule where
import Paths_mylibrary -- This module will be generated by cabal
-- Some function that uses the data-file
printConfig :: IO ()
printConfig = do
n <- getDataFileName "libraryconfig.txt"
-- Paths_mylibrary.getDataFileName resolves paths for files associated with mylibrary
c <- readFile n
print c
See this link for information about the Paths_* module: https://www.haskell.org/cabal/users-guide/developing-packages.html#accessing-data-files-from-package-code
Now running cabal install should install mylibrary.
Then, under scripts/script1.hs, you can just run ghc script1.hs, using the library you installed.

Could not find module ‘Test.HUnit’

I have a fresh installation of stack and ghci:
$ stack --version
Version 1.6.3, Git revision b27e629b8c4ce369e3b8273f04db193b060000db (5454 commits) x86_64 hpack-0.20.0
$ ghci --version
The Glorious Glasgow Haskell Compilation System, version 8.2.1
I make a new project:
$ stack new so-mve
Downloading template "new-template" to create project "so-mve" in so-mve/
... blah blah blah ...
Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- so-mve/
Selecting the best among 12 snapshots...
* Matches lts-10.3
Selected resolver: lts-10.3
Initialising configuration using resolver: lts-10.3
Total number of user packages considered: 1
Writing configuration to file: so-mve/stack.yaml
All done.
Looks pretty good:
$ tree so-mve
so-mve
├── ChangeLog.md
├── LICENSE
├── README.md
├── Setup.hs
├── app
│   └── Main.hs
├── package.yaml
├── so-mve.cabal
├── src
│   └── Lib.hs
├── stack.yaml
└── test
└── Spec.hs
It builds and runs:
$ cd so-mve
$ stack build
so-mve-0.1.0.0: build (lib + exe)
Preprocessing library for so-mve-0.1.0.0..
Building library for so-mve-0.1.0.0..
Preprocessing executable 'so-mve-exe' for so-mve-0.1.0.0..
Building executable 'so-mve-exe' for so-mve-0.1.0.0..
so-mve-0.1.0.0: copy/register
Installing library in /...blah-blah.../so-mve/.stack-work/install/x86_64-osx/lts-10.3/8.2.2/lib/x86_64-osx-ghc-8.2.2/so-mve-0.1.0.0-5kG2WnHWwo99IiYYGoxrcC
Installing executable so-mve-exe in /...blah-blah.../so-mve/.stack-work/install/x86_64-osx/lts-10.3/8.2.2/bin
Registering library for so-mve-0.1.0.0..
$ stack exec so-mve-exe
someFunc
Tests run:
$ stack test
blah blah blah
[2 of 2] Compiling Main ( test/Spec.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/so-mve-test/so-mve-test-tmp/Main.o )
...blah-blah-blah...
Progress: 1/2Test suite not yet implemented
so-mve-0.1.0.0: Test suite so-mve-test passed
Completed 2 action(s).
I triple check that HUnit is installed
$ stack install HUnit
Populated index cache.
I add one line to test/Spec.hs
$ cat test/Spec.hs
import Test.HUnit
main :: IO ()
main = putStrLn "Test suite not yet implemented"
Doesn't work:
$ stack test
so-mve-0.1.0.0: unregistering (components added: test:so-mve-test)
so-mve-0.1.0.0: build (lib + exe + test)
Preprocessing library for so-mve-0.1.0.0..
Building library for so-mve-0.1.0.0..
Preprocessing executable 'so-mve-exe' for so-mve-0.1.0.0..
Building executable 'so-mve-exe' for so-mve-0.1.0.0..
Preprocessing test suite 'so-mve-test' for so-mve-0.1.0.0..
Building test suite 'so-mve-test' for so-mve-0.1.0.0..
[2 of 2] Compiling Main ( test/Spec.hs, .stack-work/dist/x86_64-osx/Cabal-2.0.1.0/build/so-mve-test/so-mve-test-tmp/Main.o )
/...blah-blah.../so-mve/test/Spec.hs:1:1: error:
Could not find module ‘Test.HUnit’
Use -v to see a list of the files searched for.
|
1 | import Test.HUnit
| ^^^^^^^^^^^^^^^^^
Progress: 1/2
-- While building custom Setup.hs for package so-mve-0.1.0.0 using:
/Users/XXXXXXXX/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_mPHDZzAJ_2.0.1.0_ghc-8.2.2 --builddir=.stack-work/dist/x86_64-osx/Cabal-2.0.1.0 build lib:so-mve exe:so-mve-exe test:so-mve-test --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
I don't have trouble importing other libraries, like Text.Read and Text.Printf.
I googled around a bunch, but didn't find an answer. Any ideas for me?
You just need to add HUnit to the dependencies for your test project. When using stack, you should edit the package.yaml file to specify dependencies. In particular, your test configuration should look something like:
tests:
so-mve-test:
main: Spec.hs
source-dirs: test
ghc-options:
- ...
dependencies:
- HUnit
This is documented in the latest Stack Guide, under the section Adding Dependencies.
You installed HUnit globally with stack, but that doesn't mean it is specified for your project.
Your cabal file for the project needs to specify a dependency on HUnit:
--so-mve.cabal
...
test-suite so-mve
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Spec.hs
build-depends: base
, HUnit
...
Text.Read and Text.Printf are both included in base, so you don't need to specify an additional dependency.
As pointed out in the comments, since you're using a package.yaml (as opposed to a stack.yaml) configuration with the newer version of stack, you'll need to specify the dependency there instead of the .cabal file:
tests:
so-mve-test:
main: Spec.hs
source-dirs: test
ghc-options:
- -threaded
dependencies:
- HUnit

cabal haddock failing because function is defined in multiple files

I try to generate documentation for my executable using cabal haddock. My project structure looks like this:
~/.../project_name
project_name.cabal
Setup.hs
src/
Main.hs
Data/
...
test/
MainTestSuite
...
When I run cabal haddock --executable it fails with the following error message:
module ‘projects-0.1.0.0:Main’ is defined in multiple files: dist/build/tmp-8215/src/Main.hs
dist/build/tmp-8215/./Setup.hs
The Source.hs file has these contents:
import Distribution.Simple
main = defaultMain
The ghc Version is 7.8.3 and the haddock version is 2.14.3.

`cargo package`: error: main function not found

I'm trying to package a library using the cargo package manager for Rust. When I try to run cargo package per the documentation, I get the following output:
error: main function not found
error: aborting due to previous error
failed to verify package tarball
I'm confused. I'm trying to package a library (with useful external functions), so I expect that I don't need a main function. Here is my Cargo.toml:
[package]
name = "package-name"
version = "0.0.1"
authors = [ "Kevin Burke <kev#inburke.com>" ]
Here is my directory structure:
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
What am I missing?
Ah! If you are packaging a library for other programs to use (as I am trying to do), you need to name your file lib.rs.
Alternatively, if you are packaging a binary, name your file main.rs (this was my mistake).

Resources