A tool that helps you to turn in one command a Rust crate into a Haskell Cabal library!

Overview

cabal-pack

A tool that helps you to turn in one command a Rust crate into a Haskell Cabal library!

To generate bindings, you need to annotate the Rust function you want to expose with hs-bindgen macro.

Getting started

Here a little screencast demonstrating how it works (commands walkthrough are just pasted below):

asciinema

N.B. You need in your $PATH a working Rust and Haskell environment, if you use Nix you can just enter: nix-shell -p cabal-install ghc cargo rustc


Welcome in this little cabal-pack / hs-bindgen demo 🙂

Let's start by creating a dumb Rust library!

$ cargo new --lib greetings
     Created library `greetings` package

$ tree greetings
greetings
├── Cargo.toml
└── src
    └── lib.rs

1 directory, 2 files

$ cd greetings

Add hs-bindgen to the dependencies list:

$ cargo add hs-bindgen
    Updating crates.io index
      Adding hs-bindgen v0.6.0 to dependencies.
             Features:
             + antlion

And use it to decorate the function we want to expose:

  • src/lib.rs:
use hs_bindgen::*;

#[hs_bindgen]
fn hello(name: &str) {
    println!("Hello, {name}!");
}
$ cargo build   Compiling proc-macro2 v1.0.47
   Compiling quote v1.0.21
   Compiling unicode-ident v1.0.5
   Compiling syn v1.0.103
   Compiling serde_derive v1.0.147
   Compiling thiserror v1.0.37
   Compiling serde v1.0.147
   Compiling semver v1.0.14
   Compiling antlion v0.3.0
   Compiling lazy_static v1.4.0
   Compiling thiserror-impl v1.0.37
   Compiling displaydoc v0.2.3
   Compiling hs-bindgen-traits v0.6.1
   Compiling toml v0.5.9
   Compiling hs-bindgen-derive v0.6.1
   Compiling hs-bindgen v0.6.1
   Compiling greetings v0.1.0 (/Users/yvan/demo/greetings)
error: custom attribute panicked
 --> src/lib.rs:3:1
  |
3 | #[hs_bindgen]
  | ^^^^^^^^^^^^^
  |
  = help: message: fail to read content of `.hsbindgen` configuration file
          n.b. you have to run the command `cabal-pack` to generate it: Os { code: 2, kind: NotFound, message: "No such file or directory" }

error: could not compile `greetings` due to previous error

So, we will use cabal-pack to check our setup and generate Cabal files:

$ cargo install cabal-pack
    Updating crates.io index
     Ignored package `cabal-pack v0.6.0` is already installed, use --force to override

$ cabal-pack
Error: Your `Cargo.toml` file should contain a [lib] section with a `crate-type` field
that contains either `staticlib` or `cdylib` value, e.g.:

[lib]
crate-type = ["staticlib"]

N.B. if you're a Nix user, rather than rely on impure cargo install fell free to just nix run github:yvan-sraka/cabal-pack

Right, we edit the Cargo.toml accordingly:

  • Cargo.toml:
[package]
name = "greetings"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hs-bindgen = "0.6"

[lib]
crate-type = ["staticlib"]
$ cabal-pack
Cabal files generated!
**********************
You should now be able to compile your library with `cabal build` and should
add `hs-bindgen` to your crate dependencies list and decorate the Rust function
you want to expose with `#[hs_bindgen]` attribute macro.

$ ls
Cargo.lock  Cargo.toml  Setup.lhs  greetings.cabal  src  target
$ cargo build
   Compiling hs-bindgen v0.6.1
   Compiling greetings v0.1.0 (/Users/yvan/demo/greetings)
    Finished dev [unoptimized + debuginfo] target(s) in 0.55s

$ cabal build
Build profile: -w ghc-9.0.2 -O1
In order, the following will be built (use -v for more details):
 - greetings-0.1.0 (lib:greetings) (first run)
[1 of 1] Compiling Main             ( omitted ... )
Linking /Users/yvan/demo/dist-newstyle/build/aarch64-osx/ghc-9.0.2/greetings-0.1.0/setup/setup ...
Configuring greetings-0.1.0...
Preprocessing library for greetings-0.1.0..
Building library for greetings-0.1.0..
[1 of 1] Compiling Greetings        ( src/Greetings.hs, omitted ... )

It works! And so cargo build too if you just want to use the library in a Rust project!


Now let's try to use our freshly generated library in an Haskell app 😉

$ cd ..
$ cabal init --non-interactive test
[Log] Guessing dependencies...
[Log] Using cabal specification: 3.8
[Warning] unknown license type, you must put a copy in LICENSE yourself.
[Log] Creating fresh file CHANGELOG.md...
[Log] Creating fresh directory ./app...
[Log] Creating fresh file app/Main.hs...
[Log] Creating fresh file test.cabal...
[Warning] No synopsis given. You should edit the .cabal file and add one.
[Info] You may want to edit the .cabal file and add a Description field.

$ tree test
test
├── app
│   └── Main.hs
├── CHANGELOG.md
└── test.cabal

1 directory, 3 files

We create a cabal.project (equivalent to cargo workspace) to perform a local test without having to upload greetings on hackage:

  • cabal.project:
packages: ./greetings ./test

We edit test.cabal to make it depends on greetings library:

  • test/test.cabal (content partially omitted):
executable test
    -- Other library packages from which modules are imported.
    build-depends:    base, greetings

We write a minimalist main function that will make call hello from Greetings module

  • test/app/Main.hs:
module Main where

import Foreign.C.String
import Greetings

main :: IO ()
main = withCString "Rust 🦀" hello

Let's check if everything works as expected:

$ cabal run test
Build profile: -w ghc-9.0.2 -O1
In order, the following will be built (use -v for more details):
 - test-0.1.0.0 (exe:test) (first run)
Configuring executable 'test' for test-0.1.0.0..
Preprocessing executable 'test' for test-0.1.0.0..
Building executable 'test' for test-0.1.0.0..
[1 of 1] Compiling Main             ( app/Main.hs, omitted ... )
Linking /Users/yvan/demo/dist-newstyle/build/aarch64-osx/ghc-9.0.2/test-0.1.0.0/x/test/build/test/test ...
Hello, Rust 🦀!

That's all folks! Happy hacking 🙂

Nix support

The --enable-nix CLI arg makes cabal-pack generate a haskell.nix / naersk based flake.nix rather than the Setup.lhs.

N.B. when first working with hs-bindgen and Nix flakes, checking if Cargo.lock isn't in .gitignore and running cargo build and git add --all before nix build, will save you a lot of pain 😉

Acknowledgments

⚠️ This is still a working experiment, not yet production ready.

cabal-pack was heavily inspired by other interoperability initiatives, as wasm-pack and Maturin.

This project was part of a work assignment as an IOG contractor.

You might also like...
Unify your game sources in one place and aquire more of them, using modules made by the community.

Project Black Pearl Unify your game sources in one place by using modules made by the community. What is Project Black Pearl? Project Black Pearl (or

A tool to deserialize data from an input encoding, transform it and serialize it back into an output encoding.

dts A simple tool to deserialize data from an input encoding, transform it and serialize it back into an output encoding. Requires rust = 1.56.0. Ins

Goodname is a tool to assist you with cool naming of your methods and software

Goodname is a tool to assist you with cool naming of your methods and software. Given a brief description of your method or software, this tool enumerates name candidates forming subsequences of the description (i.e., abbreviation).

Rust Imaging Library: A high-level Rust imaging crate.

ril Rust Imaging Library: A performant and high-level Rust imaging crate. Documentation • Crates.io • Discord What's this? This is a Rust crate design

Rust command-line tool to encrypt and decrypt files or directories with age

Bottle A Rust command-line tool that can compress and encrypt (and decrypt and extract) files or directories using age, gzip, and tar. Bottle has no c

c-library wrapper around the rust pdb crate

pdbcrust: pdbcrust is a c-library wrapper around the rust pdb crate. The API only exports a minimum subset of the pdb crate functionality. The project

Omeglib, a portmanteau of "omegle" and "library", is a crate for interacting with omegle, simply and asynchronously

Omeglib, a portmanteau of "omegle" and "library", is a crate for interacting with omegle, simply and asynchronously. It is intended to suit one's every requirement regarding chat on omegle.

Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.
Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.

Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.

A comprehensive and FREE Online Rust hacking tutorial utilizing the x64, ARM64 and ARM32 architectures going step-by-step into the world of reverse engineering Rust from scratch.
A comprehensive and FREE Online Rust hacking tutorial utilizing the x64, ARM64 and ARM32 architectures going step-by-step into the world of reverse engineering Rust from scratch.

FREE Reverse Engineering Self-Study Course HERE Hacking Rust A comprehensive and FREE Online Rust hacking tutorial utilizing the x64, ARM64 and ARM32

Comments
  • Replace '_' by '-' in package name, and viceversa for lib name

    Replace '_' by '-' in package name, and viceversa for lib name

    If '-' is used in the crate name, when compiled to a static lib, rustc replaces it with '', and cabal does not find the library. Similarly, if we use '' in the crate name, cabal build complains when using underscore as the package name.

    sha3_bindings.cabal:13:25: error:
    unexpected '_'
    expecting white space or end of input
    
    opened by iquerejeta 0
Typing the technical interview, translated from Haskell to Rust

Typing the technical interview, translated from Haskell to Rust % cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.02s Running

Zac Kologlu 111 Dec 28, 2022
Haskell-style monads in Rust.

Monads, Functors, & More in Stable Rust Haskell-style monads with macro-free Rust syntax. Types work with zero annotations A fancy example (that compi

Will Sturgeon 10 May 29, 2023
Salty and Sweet one-line Rust Runtime Optimization Library

SAS SAS (Salty-And-Sweet) is an one-line Rust runtime optimization library. Features NUMA-aware rayon: numa feature should be enabled If you have 1 NU

UlagBulag 3 Feb 21, 2024
cargo-add command to make dependencies into dylibs

cargo add-dynamic This cargo command allows to wrap dependencies as dylibs. For why you might want this see Speeding up incremental Rust compilation w

Robert Krahn 62 Dec 27, 2022
A tool & library to help you with the compiler course.

Compiler Course Helper Support: eliminate left recursion (require grammar with no cycles or ϵ-production) calculate nullable, first sets, follow, sets

水夕 4 May 2, 2022
A webapp that reads your articles to you while you're on the subway

ReadToMyShoe Video Demo A website that reads articles to you, even when you're offline. Still in early development. This is a full-stack Rust webapp,

Michael Rosenberg 20 Dec 10, 2022
A tray application for Windows that gives you push notifications and instant downloads of new posts, messages and stories posted by models you subscribe to on Onlyfans.

OF-notifier A tray application for Windows that gives you push notifications and instant downloads of new posts, messages and stories posted by models

Gentlemen Mercenary 10 Dec 20, 2022
An open source WCH-Link library/command line tool written in Rust.

wlink - WCH-Link command line tool NOTE: This tool is still in development and not ready for production use. Known Issue: Only support binary firmware

WCH MCU for Rust 22 Mar 7, 2023
A pretty simple VM implemented with C++. Just one of my practices.

Bailan VM Overview Bailan VM is a simple VM implemented in C++. It just one of my little practices, but my friend, Zihao Qu encouraged me to open its

27Onion Nebell 3 Oct 6, 2022