How to link to Haskell static runtime with cabal and stack without hard coding ghc version? - haskell

I have a project which exports a shared static library and I use the following part in my project.cabal file
executable libsxp.so
main-is: Somefile.hs
default-language: Haskell2010
ghc-options: -shared -dynamic -fPIC -lHSrts-ghc7.10.2
The version of GHC is controlled using Stack, so is there a way wherein I can either get and append the version to make -lHSrts-ghc{version} or is there some config for it? I tried setting
stack build --ghc-options='-O0 -lHSrts-ghc7.10.2'
but it doesn't seem to pick it.
Also to clarify, cabal install is called by Stack and not by me.

Does that cabal file work? If so, then it should be sufficient to do something like this:
executable libsxp.so
ghc-options: -shared -dynamic -fPIC
if impl (ghc >= 7.10.2 && < 7.10.3)
ghc-options: -lHSrts-ghc7.10.2
else if impl (ghc >= 7.10.3 && < 7.10.4)
ghc-options: -lHSrts-ghc7.10.3
else if ...
BTW, why does your executable end in .so? I've never seen that in an executable clause.
Are you sure you're using 7.10.2 and not 7.10.3? Try stack exec -- ghc --version

The general principle is described in this answer: https://stackoverflow.com/a/6034881/1663197
Using the configure style in Cabal, you can write a little configure
script that substitutes a variable for the output of the sdl-config
command. The values will then be replaced in a $foo.buildinfo.in file,
yielding a $foo.buildinfo file, that Cabal will include in the build
process.
First you need to switch your cabal build-type to Configure in project.cabal. Configure style is described in cabal users guide. For build type Configure the contents of Setup.hs must be:
import Distribution.Simple
main = defaultMainWithHooks autoconfUserHooks
In case of handling GHC runtime version you can have a variable #GHC_VERSION# corresponding to it in a project.buildinfo.in file:
ghc-options: -lHSrts-ghc#GHC_VERSION#
Finally you write a configure bash script that gets GHC version as mgsloan suggested and generates project.buildinfo file by substitution of #GHC_VERSION# varibale in project.buildinfo.in file:
GHC_VERSION=$(stack exec -- ghc-pkg field ghc version --simple-output)
sed 's,#GHC_VERSION#,'"$GHC_VERSION"',' project.buildinfo.in > project.buildinfo
This way when build is started it will first execute configure script, then read project.buildinfo file and merge with project.cabal.
Also it may be worth to populate extra-source-files with configure and
project.buildinfo.in; extra-tmp-files with project.buildinfo in project.cabal.
A more sophisticated solution may be inspired by this answer: https://stackoverflow.com/a/2940799/1663197

Related

How to set cabal flag from shell.nix for stack project?

By default I build my project without nix, and executable is linked statically. But when building with nix, I want to link it dynamically instead. So I added a switch to myproj.cabal:
flag dynamic
description: Build only dynamic binaries
default: False
executable RunMe
ghc-options: -O2 -Wall
if !flag(dynamic)
ghc-options: -optl-static -static
Now I can build the project with
stack --nix --nix-packages=zlib install --flag myproj:dynamic
To avoid passing command line options every time, I created shell.nix:
{ghc}:
with (import <nixpkgs> {});
haskell.lib.buildStackProject {
inherit ghc;
name = "myproj";
buildInputs = [ zlib ];
}
Now, I don't know how to pass flag to cabal from nix file. Based on buildStackProject definition, I tried setting buildPhase, e.g.
haskell.lib.buildStackProject {
...
buildPhase = "stack build --flag=myproj:dynamic";
}
but it doesn't seem to change anything. How can I pass the flag to cabal from the nix file?
Unfortunately, the stack nix integration is rather backwards. It doesn't use nix to build your package (despite the very misleadingbuildStackPackage), just to create an environment with GHC and native dependencies. You have to use stack itself to build the project, and since stack will run cabal, it should still work with the flag. The stack nix integration will not generate a .nix file with the dependencies resolved by stack, so you can't build your project with nix-build and there's no point in entering nix-shell. You would have to use a tool like stack2nix or stackage2nix for that.
So to recap. The shell.nix above looks fine. Add this to your stack.yaml:
nix:
shell-file: shell.nix
And build the project with stack:
stack --nix --flag myproj:dynamic
That should build the project with the flag as usual, but use GHC and zlib from nix.

Conditional Compilation in Haskell submodule

I'm trying to have a submodule in my program compile conditionally to switch small parts of the code between release builds and development builds.
Currently I'm attempting to use cpphs however when I change the flags passed into GHC to define a variable and change an ifdef statement stack won't recompile those files.
For example, I've got a port number that I want to switch based on which target I have built. The code I have defining this number looks like this.
#ifdef StableRelease
port = 12345
#else
port = 54321
#endif
the stable build has the following options in its cabal file
ghc-options: -threaded -rtsopts -with-rtsopts=-N -pgmP cpphs -optP "-DStableRelease"
When I run stack build though it doesn't seem to actually pre-process the code above.
Does anyone have experience with cpphs or another preprocessing solution?
I would solve this with the help of flags
cppstuf.cabal-file
name: cppstuf
version: 0.1.0.0
...
cabal-version: >=1.10
flag StableRelease {
Description: Stable release settings like port ...
Default: False
}
executable cppstuf
main-is: Main.hs
build-depends: base >=4.9 && <5.00
default-language: Haskell2010
extensions: CPP
if flag(StableRelease) {
GHC-Options: -DSTABLE
}
Main.hs
module Main where
main :: IO ()
main =
#if STABLE
putStrLn "Hello, Haskell!"
#else
putStrLn "Hello, Haskell?"
#endif
and compiling it
stack build
or
stack build --flag cppstuf:stablerelease
Personal Note aside from the answer
I would not use CPP to manage configuration options - either providing command-line options I like optparse-applicative but there is also cmdargs,and or a configuration file I have used configurator for that, but there are several options out there on hackage. One being configurator-ng as #Shersh said - the other one is not being developed anymore.
CPP on the other hand I tend to use for making libraries work across multiple GHC/library versions.
Update - w\ regards to the comment.
If I deliver a program, the operations team should be able to change ports, hostname or handle file location of input files etc. without knowing haskell let alone recompiling a project.
It makes error chasing a lot easier if your production source code does not differ from your development sources, say you would have a postgres db in dev mode, but an oracle in production - you'll never find the oracle specific bug.
For things like optimization levels - I don't mind too much.
But make sure you test multi-threaded stuff properly, I once ran out of file handles because I was opening a lot but not closing fast enough - If your dev-setting is single threaded you'll deliver a bug.

Integrating Haste into Stack tool chain

I am using Haskell Stack for a project and I want to include Haste it compile client side logic. I like the fact that Stack abstracts away the different build and install issues among environments and if it builds on my machine, it will build on someone else's.
How do I integrate Haste into the Stack tool chain? Working out one time setup is fine, but I don't want to have to recreate the whole tool chain every time the code moves to a new system.
This should work, but take that with a grain of salt as I'm having extra issues due to this known bug. Make sure your .cabal file has the right dependencies, especially the if impl(haste) .. part (see this). Seems like most of the dependencies for Haste (and since Haste uses GHC 7.10.3 as of today) work with lts-6.14, so I used that as resolver.
haste-project.cabal
name: haste-project
version: 0.1.0.0
category: Web
build-type: Simple
cabal-version: >=1.10
executable haste-project-exe
hs-source-dirs: app
main-is: Main.hs
build-depends: base >= 4.8 && < 4.9
if impl(haste)
build-depends: haste-lib >= 0.5 && < 0.6
else
build-depends: haste-compiler >= 0.5 && < 0.6
default-language: Haskell2010
stack.yaml
extra-deps:
- HTTP-4000.2.23
- ghc-simple-0.3
- haste-compiler-0.5.4.2
- shellmate-0.2.3
resolver: lts-6.14
Then, from the same directory, you can now proceed with the usual setup instructions for Haste, but with Stack complements of the Cabal commands:
$ stack build
$ stack install haste-compiler # installs haste-boot, haste-cat, haste-pkg, and hastec
$ stack exec haste-boot # setup Haste (where I get the bug I mentioned above)
Then, you should be able to run all the usual commands, but prefixed with stack exec --. For example
$ stack exec -- hastec -O2 -fglasgow-exts myprog.hs

Can I automatically embed pandoc's default templates in my application?

Pandoc comes with several default templates, which are distributed with the pandoc package. However, if I write an application that uses pandoc as a library, those default templates don't get included in the binary. I can still use them on my machine:
module Main where
import Text.Pandoc (getDefaultTemplate)
main = getDefaultTemplate Nothing "latex" >>= print
This will print the default.latex template. However, it's not portable, since it really refers to a file somewhere on my system:
$ cd path/to/example/project
$ stack build
$ scp path/to/binary remote:remote/path
$ ssh remote:remote/path/binary
example: Could not find data file /home/Zeta/.stack/snapshots/.../pandoc-1.16.0.2/data/templates/default.latex
Since pandoc's debian package does not include those files, it's somehow able to embed them. And indeed, there is a flag -f embed_data_files. I've tried to enable it in the local stack.yaml:
extra-deps: [pandoc-1.16]
flags:
pandoc:
embed_data_files: true
But that didn't change anything, the compiled binary still complains about missing data files.
So, is there any way to automatically include pandoc's template files?
It turns out that pandoc injects its data files during its build via hsb2hs. Somehow that step failed during stack build I missed the error message.
Neither hsb2hs nor its main dependency processing-tools are part of stack's LTS, they're only in the nightly stackage versions. The following additions to stack.yaml fixed the problem:
# part of stack.yaml:
extra-deps:
- preprocessor-tools-1.0.1
- hsb2hs-0.3.1
- pandoc-1.16
flags:
pandoc:
embed_data_files: true
For those using Cabal, this is somewhat equal to
cabal sandbox init
cabal update
cabal install hsb2hs-0.3.1 && cabal install pandoc-1.16 -f embed_data_files
cabal install --dependencies-only
cabal build
Here's how I verified that the templates are actually included:
$ stack build
$ grep "usepackage\{hyperref\}" .stack-work/install/*/bin/example -a
\usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry}
$endif$
\usepackage{hyperref}
$if(colorlinks)$
\PassOptionsToPackage{usenames,dvipsnames}{color} % color is loaded by hyperref
That snippet is part of default.latex, so it's really included in the binary.

Replace compiler when building Haskell project with Cabal

Is it possible somehow to configure cabal project to use different compiler than GHC? Additional is it possible to control this by some flags?
I want to compile my project with GHC or Haste (to JavaScript) based on some compilation flags.
It would be ideal if I could set my cabal configuration or my custom script to do something like:
-- target JS
cabal configure --target=js
cabal build
-- target Native
cabal configure --target=native
cabal build
To build a Cabal project with either GHC or Haste, use the cabal binary for the former, and haste-inst (comes with haste) for the latter.
To have conditional code in in your modules, add {-# LANGUAGE CPP #-} and use #ifdef __HASTE__, which will only be defined by haste, but not by GHC. Note that __GLASGOW_HASKELL__ is defined in both cases (which makes sense, as haste builds on GHC for large parts of the compilation). So you would use it like
{-# LANGUAGE CPP #-}
module Module where
compiler :: String
#ifdef __HASTE__
compiler = "haste"
#else
compiler = "GHC"
#endif
Theoretically, for conditional settings in the Cabal file something like this should work:
library
exposed-modules:
Module
if impl(ghc)
exposed-modules:
Module.GHC
if impl(haste)
exposed-modules:
Module.GHC
build-depends: base ==4.6.*
but it seems that even with haste-inst, impl(ghc) is true; bug report is filed.
While it's currently not possible to use impl(haste) in your cabal files, you can now check for flag(haste-inst) to see if your package is being built using haste-inst.

Resources