Search code examples
haskellghchaskell-stackhaskell-ffi

Stack Build Cannot Find Local Library


I successfully replicated this example using GHC by itself.

https://wiki.haskell.org/Calling_Haskell_from_C

The end goal is to write 99% of my program in Haskell, and then call it from an event loop written in C:

#include <HsFFI.h>
#ifdef __GLASGOW_HASKELL__
#include "../.stack-work/dist/x86_64-linux/Cabal-2.4.0.1/build/Lib_stub.h"
extern void __stginit_Lib(void);
#endif
#include <stdio.h>
#include <time.h>
extern void hs_add_root (void (*init_root)(void));

int main(int argc, char *argv[])
{
int i;

hs_init(&argc, &argv);

#ifdef __GLASGOW_HASKELL__
    hs_add_root(__stginit_Lib);
#endif

for (int m = 0; m < 10; ++m) {
    i = fibonacci_hs(42);
    printf("Fibonacci: %d\n", i);
}

hs_exit();
return 0;
}

The motivation to run an event loop in C is that, from what I've read, forcing evaluation X times/second is difficult or impossible in Haskell.

Here is package.yaml:

name:                c-loop
version:             0.1.0.0
github:              "githubuser/c-loop"
license:             BSD3
author:              "Author name here"
maintainer:          "[email protected]"
copyright:           "2019 Author name here"

extra-source-files:
- README.md
- ChangeLog.md

# Metadata used when publishing your package
# synopsis:            Short description of your package
# category:            Web

# To avoid duplicated efforts in documentation and dealing with the
# complications of embedding Haddock markup inside cabal files, it is
# common to point users to the README.md file.
description:         Please see the README on GitHub at   <https://github.com/githubuser/c-loop#readme>

dependencies:
- base >= 4.7 && < 5
library:
  source-dirs: src

executables:
  c-loop-exe:
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    - -fobject-code
    #    - -no-hs-main
    - --make -no-hs-main -optc-O ./c/eventLoop.c Lib -o eventLoop
dependencies:
- c-loop

tests:
  c-loop-test:
    main:                Spec.hs
    source-dirs:         test
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - c-loop

When I run:

$ stack build

I get this:

<no location info>: error: module ‘Lib’ cannot be found locally

Does anyone know what is going on?


Solution

  • As an aside, your motivation seems misguided. I don't think there's any benefit to having a C event loop whose only purpose is to "force evaluation" on a regular schedule. You can do that in Haskell just fine.

    What's going wrong in your example above is probably the Lib in the ghc-options. However, there are other Cabal fields you should be using instead which will make things work more smoothly.

    Here's how to get your minimal example working with Stack. Create a fresh directory with the four files listed below and run stack build, then stack exec c-loop-exe.

    A few points:

    • You might be able to do this with a package.yaml file, but you'll have to convert the Cabal syntax over.
    • You don't need all that __stginit and hs_add_root garbage anymore, unless you're using GHC < 7.2.
    • You don't need to hard-code the path for the stub, if you set up the Cabal file correctly (i.e., using c-sources).
    • The -opt-O2 flag is unnecessary. It's the Stack default.

    The contents for the four files:

    -- stack.yaml
    resolver: lts-13.21
    packages:
    - .
    
    -- c-loop.cabal
    cabal-version: 1.12
    name:           c-loop
    version:        0.1.0.0
    build-type:     Simple
    
    executable c-loop-exe
      main-is: src/Lib.hs
      ghc-options: -no-hs-main
      c-sources: c/eventLoop.c
      build-depends:
          base >=4.7 && <5
      default-language: Haskell2010
    
    -- c/eventLoop.c
    #include <stdio.h>
    #include <time.h>
    #include "HsFFI.h"
    #include "Lib_stub.h"
    
    int main(int argc, char *argv[])
    {
        int i;
        hs_init(&argc, &argv);
        for (int m = 0; m < 10; ++m) {
            i = fibonacci_hs(42);
            printf("Fibonacci: %d\n", i);
        }
        hs_exit();
        return 0;
    }
    
    -- src/Lib.hs
    {-# LANGUAGE ForeignFunctionInterface #-}
    
    module Lib where
    
    import Foreign.C.Types
    
    fibonacci :: Int -> Int
    fibonacci n = fibs !! n
        where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
    
    fibonacci_hs :: CInt -> CInt
    fibonacci_hs = fromIntegral . fibonacci . fromIntegral
    
    foreign export ccall fibonacci_hs :: CInt -> CInt