C compiler selection in cabal package - haskell

I decided to add some flags to control the way that C source file is compiled (i.e. something like use-clang, use-intel etc.).
C-Sources: c_lib/tiger.c
Include-Dirs: c_lib
Install-Includes: tiger.h
if flag(debug)
GHC-Options: -debug -Wall -fno-warn-orphans
CPP-Options: -DDEBUG
CC-Options: -DDEBUG -g
else
GHC-Options: -Wall -fno-warn-orphans
Question is: which options in descritpion file need to be modified to change C compiler? I did found only CC-Options.

There is no straightforward way, but it is possible.
Assuming that you are using Distribution.Simple, you basically need to add a user hook to the build stage.
All of the following changes need to appear in Setup.hs:
Change main to use a build hook, something like:
main :: IO ()
main = defaultMainWithHooks simpleUserHooks { buildHook = myBuildHook }
Next you need a build hook. It will likely look something like the following:
myBuildHook pkg_descr local_bld_info user_hooks bld_flags =
do
let lib = fromJust (library pkg_descr)
lib_bi = libBuildInfo lib
custom_bi = customFieldsBI lib_bi
cpp_name = fromJust (lookup "x-cc-name" custom_bi)
c_srcs = cSources lib_bi
cc_opts = ccOptions lib_bi
inc_dirs = includeDirs lib_bi
lib_dirs = extraLibDirs lib_bi
bld_dir = buildDir local_bld_info
-- Compile C/C++ sources
putStrLn "invoking my compile phase"
objs <- mapM (compileCxx cpp_name cc_opts inc_dirs bld_dir) c_srcs
-- Remove C/C++ source code from the hooked build (don't change libs)
let lib_bi' = lib_bi { cSources = [] }
lib' = lib { libBuildInfo = lib_bi' }
pkg_descr' = pkg_descr { library = Just lib' }
-- The following line invokes the standard build behaviour
putStrLn "Invoke default build hook"
bh <- buildHook simpleUserHooks pkg_descr' local_bld_info user_hooks bld_flags
return bh
The code above probably needs unpacking a bit. The let clauses are basically about unpacking the required data fields from the structures passed to the build hook. Notice that you can create custom stanzas in your foo.cabal. I have provided the code to support a stanza something like:
x-cc-name: icc
As a means to specify your compiler. Having extracted all of the source files, you map over them using a function to compile a single file (NB: this is sub-optimal in some cases, e.g. those compilers which can efficiently compile multiple source files to produce a single object output and benefit from large scale optimizations, but we'll leave that aside for now).
Last of all, as we've now compiled the C/C++ code, remove it from the build structures before you pass everything on to the default build hook.
Sorry that this is more of a 'HOWTO' than a canned answer, but it should help you to get going.
I should mention that the code is untested. I have adapted it from some work I have been doing on the wxHaskell build system, so I know the idea works fine. The Cabal API is actually pretty well documented - it suffers mainly from being somewhat unstable around some of these areas.

There really doesn't seem to be any way to specify this in a .cabal file; the only thing we seem to have at the moment that would be even remotely useful here is --with-<prog>=path.
I suggest you try filing a ticket against Cabal on the trac.

4.10.1. Replacing the program for one or more phases
-pgmc cmd
        Use cmd as the C compiler.
This works for ghc --make, but I'm not sure how to get Cabal to apply this to the C file compilation.

Related

With Nix, how can I specify Haskell dependencies with profiling enabled?

I was trying like this initially:
nix-shell -p "haskell.packages.ghc821.ghcWithPackages (p: with p; [text hspec lens])" -j4 --run 'ghc Main.hs -prof
Then GHC told me
Main.hs:4:1: error:
Could not find module ‘Control.Lens’
Perhaps you haven't installed the profiling libraries for package ‘lens-4.15.4’?
Use -v to see a list of the files searched for.
Searching around the web I found this: https://github.com/NixOS/nixpkgs/issues/22340
So it seems I won't be able to download from the cache. But that's okay, if at least I can build the profiled variants locally.
Can I do that somehow simply by modifying the nix expression given to -p slightly?
Then at this point in writing this question, I remembered this resource: https://github.com/NixOS/nixpkgs/blob/bd6ba7/pkgs/development/haskell-modules/lib.nix
Where I found enableLibraryProfiling. So I tried:
nix-shell -p "haskell.packages.ghc821.ghcWithPackages (p: with p; [text hspec (haskell.lib.enableLibraryProfiling lens)])" -j4 --run 'ghc Main.hs -prof'
Which got me to a new error:
src/Control/Lens/Internal/Getter.hs:26:1: error:
Could not find module ‘Data.Functor.Contravariant’
Perhaps you haven't installed the profiling libraries for package ‘contravariant-1.4’?
Use -v to see a list of the files searched for.
|
26 | import Data.Functor.Contravariant
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So if I could map over all packages to enableLibraryProfiling on them, then I guess this could work. But my nix knowledge doesn't quite extend that far at the moment. How could I do that? And is this even the correct path to pursue?
With further snooping around in nix-repl and some helpful pointers from ElvishJerricco on #reflex-frp at FreeNode, I was able to construct this, which seems to work:
$ nix-shell -p "(haskell.packages.ghc821.extend (self: super: {mkDerivation = expr: super.mkDerivation (expr // { enableLibraryProfiling = true; });})).ghcWithPackages (p: with p; [text hspec lens])" -j4 --run 'ghc Main.hs -prof'
I figured out a simple approach that I'm working into a largest blog post on Haskell development using Nix. For now, here's the text of just the profiling section:
Nix makes this fairly easy. First, we add the following to a ~/.config/nixpkgs/config.nix:
{
packageOverrides = super: let self = super.pkgs; in
{
profiledHaskellPackages = self.haskellPackages.override {
overrides = self: super: {
mkDerivation = args: super.mkDerivation (args // {
enableLibraryProfiling = true;
});
};
};
};
}
Now in the project we want to profile, we create a new profiling-shell.nix:
let nixpkgs = import <nixpkgs> {};
orig = nixpkgs.pkgs.profiledHaskellPackages.callPackage ./default.nix {};
in (nixpkgs.pkgs.haskell.lib.doBenchmark orig).env
Almost identical to our normal shell.nix, except for the usage of profiledHaskellPackages, which we just defined globally. Now, an invocation of nix-shell profiling-shell.nix will rebuild every dependency in our project with profiling enabled. The first time this is done it will take quite a long time. Luckily this doesn't corrupt our Nix store - a vanilla nix-shell does seem to present us with our regular dependencies again, without redownloading or rebuilding.
WARNING: A nix-collect-garbage -d will wipe away all the custom-built libs from our Nix Store, and we'd have to build them again if they're needed.
If we're writing a library, the closest executable on hand that we could profile would be our benchmark suite. To do that:
Add -prof and -fprof-auto to our benchmark's GHC options
Regenerate default.nix
Enter our profiling shell: nix-shell profiling-shell.nix
cabal configure --enable-library-profiling --enable-benchmarks
cabal build
dist/build/projname/projname-bench +RTS -p
Look at the produced projname-bench.prof file
Based on the results, we can make code changes, remove the profiling options, regenerate default.nix, and benchmark as usual in our normal Nix Shell.

Error parsing haskell source file containing # (c preprocessor)

When parsing haskell source files that contain #define and #if, parseFileContentsWithMode gets confused and throws an error about the hash. I tried to include MagicHash and CPP in the extension list (extns, see below) but that didn't work. I can't really change the source files because there are a lot of them.
getModule extns filePath program = fromParseResult $ parseFileContentsWithMode mode program
where
bangPatternsExt = map parseExtension extns
mode = ParseMode filePath Haskell2010 bangPatternsExt False False
The parser failed at:
#if __GLASGOW_HASKELL__ >= 612
instance Lift ModName where
lift = lift . modString
Haskell-src-exts doesn't support CPP natively (there is no issue in the GitHub issue tracker for that, but an old one on trac at http://trac.haskell.org/haskell-src-exts/ticket/27).
You can look at solution or workarounds for haskell-src-exts parsing modules with CPP failing for workarounds (using cpphs).
There is also a package called hse-cpp (https://hackage.haskell.org/package/hse-cpp) that looks like it can run cpphs for you, but I haven't used it, and it doesn't seem to get updated a lot.

How to pass command-line options to Alex in Cabal

In my current project I have a file Tokens.x that needs to be compiled to Tokens.hs by Alex. In my .cabal file I have listed Tokens in the other-modules section and cabal build happily creates the file.
However it does so without the -g option that instructs Alex to create a GHC optimized version of the file. This option represents a 10x speed up in scanning when used with GHC and is also an order of magnitude faster to compile.
How do I tell cabal to included the -g option when compiling using GHC?
AFAIK, with Cabal you can currently only specify program options in the configuration file or via the command line, but not in a .cabal file.
There's an open issue about this: https://github.com/haskell/cabal/issues/1223
However, looking at the sources for Cabal, I find that your particular problem seems to be solved by default. In Distribution.Simple.PreProcess, there's:
ppAlex :: BuildInfo -> LocalBuildInfo -> PreProcessor
ppAlex _ lbi = pp { platformIndependent = True }
where pp = standardPP lbi alexProgram (hcFlags hc)
hc = compilerFlavor (compiler lbi)
hcFlags GHC = ["-g"]
hcFlags _ = []
This means that if Cabal is used with GHC, then -g is automatically passed to Alex when it's being used as a preprocessor.

How to Iterate Over Modules in Cabal Package

What's the best way to iterate over all the modules (files) in a given package? Concretely, suppose I have
an executable called "runThis"
a cabal package P with files F1.hs, F2.hs, ..., Fn.hs
Whats the easiest way to execute:
runThis F1.hs
runThis F2.hs
...
runThis Fn.hs
?
I thought I might try --with-compiler but that fails with
cabal: The program ghc version >=6.4 is required but the version of runThis
(The other option looks like tweaking the Setup.lhs -- but ideally I'd like to hijack the build process and use "runThis" instead of, say, ghc)
Thanks!
From Duncan Coutts' email:
Question: How to add the preprocessor? I have tried
main =
defaultMainWithHooks
simpleUserHooks{hookedPreProcessors=[("foo",transformation)]}
transformation :: BuildInfo -> LocalBuildInfo -> PreProcessor
That looks right. Here's how to complete it (example taken from the
Cabal haddock docs for the PreProcess module):
transformation _ _ =
PreProcessor {
platformIndependent = True,
runPreProcessor =
mkSimplePreProcessor $ \inFile outFile verbosity -> do
fail $ "transformation: " ++ inFile ++ " " ++ outFile
}
and it works fine:
runghc Setup.hs build
Preprocessing library foo-1.0...
Setup.hs: transformation: Abc.foo dist/build/Abc.hs
But under which circumstances will this function be called? Up to now
I have not succeeded in making cabal call this function.
It calls it when it goes and looks for the module Abc (ie Abc.hs
or .lhs), and since if does not find it, it'll check through the list of
preprocessors and go looking for the corresponding files, ie Abc.foo.

Haskell: How do I get the values of #define-d constants?

In a Haskell program, what's the best way to use constants defined in C headers?
For this task, hsc2hs is your friend.
For a simple example, let's get the value of INT_MAX from limits.h.
$ cat >IntMax.hsc
module Main where
#include <limits.h>
c_INT_MAX = #const INT_MAX
main = print c_INT_MAX
With hsc2hs, we can #include headers and use the values of constants with the #const directive.
Instead of building by hand, use Cabal:
$ cat >intmax.cabal
Name: intmax
Version: 0.0
Cabal-Version: >=1.2
Build-Type: Simple
Executable intmax
Main-Is: IntMax.hs
Build-Depends: base
Notice that even though the name of the main program is IntMax.hsc, the Main-Is line points to IntMax.hs. When Cabal looks for IntMax.hs but finds IntMax.hsc, it automatically feeds the latter through hsc2hs as part of the build.
$ cabal configure
Resolving dependencies...
Configuring intmax-0.0...
$ cabal build
Prerocessing executables for intmax-0.0...
Building intmax-0.0...
[1 of 1] Compiling Main ( dist\build\intmax\intmax-tmp\IntMax.hs, dist\build\intmax\intmax-tmp\Main.o )
Linking dist\build\intmax\intmax.exe ...
$ ./dist/build/intmax/intmax
2147483647
Note that you'll want to break up lines with multiple constants. Say you're assembling a bitfield to pass to FormatMessage. You'll want to write it as
flags = #const FORMAT_MESSAGE_FROM_SYSTEM
.|.
#const FORMAT_MESSAGE_IGNORE_INSERTS
Putting them all on one line will result in syntax errors.
GHC is moving away from -fvia-c and towards -fasm wherever possible.
One side effect is that your program may be compiled without using any C headers at all, even in -fvia-c mode, in order to ensure that the compilation results are functionally identical to GHC in -fasm mode.
Thus it is necessary to use hsc2hs, c2hs, or other preprocessors run before GHC compiles sources.
c2hs natively supports enum constants... it's been a while, but I think something like this is right.
#c
enum Foo = { Bar, Baz };
void something(enum Foo foo) {}
#endc
{#enum Foo#}
somethingBar = {#call pure something#} (cFromEnum Bar)
#define'd constants are a tick trickier. I've always just copied them inline, or used additional C to transform then into enums or const variables.

Resources