Packaging a Rust FFI

Hello,

I am developing a Haskell library that uses a FFI to Rust. The library works but I’m trying to simplify its use.

In its current form, a configure and a Makefile compile the Rust portion and set up the paths so pkg-config can find the library.

Is there a better way to package this library, so that a different module that wants to use it can just declare the dependency and the compile/configure can happen without manual intervention?

6 Likes

Once you have polyglot build system requirements, I’d recommend just using Nix and insisting your users use Nix or maintain their misc installation methods themselves. Yeah, there’s a learning curve etc but it keeps things sane.

I’ve been looking into Nix/Flake, as it seems it would do what I expect. But I have not been able to set it up on my library and have not been able to find an example of how to do it.

You can use a custom setup as documented here: Build a Haskell project alongside its Rust dependency | Light Thoughts

There’s currently a plan to rearchitect custom setups to make things more reusable and modular, but for now its still a pretty good approach.

8 Likes

I’m working through this approach and compiling the Rust part worked but now I’m stuck with a c2hs file that’s not been processed.

This is my first dive into using cabal directly, compiling this library was straight-forward using stack. Cabal is currently producing this error:

<no location info>: error: module ‘C.MyFfi’ cannot be found locally

In my .cabal file:

library
  exposed-modules:
    C.MyFfi
  ...
  build-tool-depends:
    c2hs:c2hs

Update

I managed to fix it and it compiles now. I needed to specify the pre-processor explicity in Setup.hs and then there were some file paths that needed to be corrected to ensure header files are found.

The library works with this, which is great.

If I use it as a dependency from another Haskell project, it compiles as expected along the rest of the dependencies but the final executable fails during the Linker, unable to find the Rust bindings.

I’ve set the library as static and shared, any ideas on what could help get the linker find the library?

I’m trying to wrap my head around Nix but I’m brand new to it and I haven’t been able to get it to work yet.

1 Like

It’s actually not working. The library compiles but trying to run the test suite fails, saying that it can’t find the lib*.so file that the Rust compiler generated.

What is the proper way to tell Cabal where to find custom *.so files that are part of the current library, not something else installed system-wide?

1 Like

hmm I think that’s what pkg-config can do. Although I’ve only written it by cribbing from other files in nixpkgs :sweat_smile:

After more testing, it seems to be that my lib*.so file needs to be in LD_LIBRARY_PATH for this to work. I’m now tweaking the Setup.hs file to add it :smiley:

2 Likes

Turns out that you can’t change the path environment variables from Setup.hs.

you can set extra-lib-dirs or the like?

I’ve set those and it successfully compiles. The problem is that during execution, the lib*.so created by the Rust compiler can’t be found unless I use pkgconfig-depends in the cabal file.

Which leads me back to having a bash script that sets a PATH to find this file.

1 Like

oh that makes sense – i wonder if installing that so into a standard shared location is the way to go instead?

I think one option is to go back to a Makefile, and make a target that needs to be run with sudo so it can place the files in /usr/....

My hope was to be able to package this as a self-contained library like some of the C FFI libs so you can just put it on your build-depends and everything would get compiled on its own.

But even some of those C FFI libs also need you to go and install some lib*-dev on your box through the package manager anyway.

Is anyone aware of a guide to make this work for stack (and hpack)?
I was able to get cargo-cabal to work on cabal, but have had zero luck making it work with stack. (Including the option of manually copying the built .dylib files to the system folders (/usr/local/lib)

I get the cryptic error when I compile:

ghc-9.8.1: panic! (the 'impossible' happened)
  GHC version 9.8.1:
        Don't understand library name rust_interop
  CallStack (from HasCallStack):
    panic, called at compiler/GHC/Unit/Info.hs:226:15 in ghc-9.8.1-a76c:GHC.Unit.Info

I have this working using only cabal, a custom Setup.hs file and a configure script that sets the paths correctly.

I do remember seeing this error during my many, many trial-and-error cycles, if I remember correctly this went away when I removed the Rust library from all the ...-libs fields in the Cabal file, and use it only on the pkgconfig-depends field.