Compare commits

..

No commits in common. "dev" and "main" have entirely different histories.
dev ... main

10 changed files with 160 additions and 357 deletions

4
build
View File

@ -1,12 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# generate this file from Nix config for now to avoid duplication?
cargo build --manifest-path ./rust/Cargo.toml cargo build --manifest-path ./rust/Cargo.toml
BUNDLED_LIB_DIR=$(cabal list-bin . | sed -e 's=x/garnet/build/garnet/garnet=build=g') BUNDLED_LIB_DIR=$(cabal list-bin . | sed -e 's=x/garnet/build/garnet/garnet=build=g')
mkdir -p $BUNDLED_LIB_DIR mkdir -p $BUNDLED_LIB_DIR
ln -sf $(pwd)/rust/target/debug/libgarnet_rs.a $BUNDLED_LIB_DIR/libCgarnet_rs.a ln -sf $(pwd)/rust/target/debug/libgarnet_rs.a $BUNDLED_LIB_DIR
cabal build cabal build

View File

@ -1,5 +1,20 @@
packages: . packages: .
-- not on Hackage yet: https://well-typed.com/blog/2026/02/hs-bindgen-alpha
-- we've modified the stanzas from that post in order to use a more recent version
-- because we want record-dot support: https://github.com/well-typed/hs-bindgen/issues/1829#issuecomment-4081875451
source-repository-package
type: git
location: https://github.com/well-typed/hs-bindgen
tag: 6ca94188abd756a1fb4dd8a4037de3fa7dca0765
subdir: c-expr-dsl c-expr-runtime hs-bindgen hs-bindgen-runtime
--sha256: M+8tEZA8gsEc6gXnNdSbRpMBQ5LkH7sphcV1aR0fclA=
source-repository-package
type: git
location: https://github.com/well-typed/libclang
tag: d6e482df49b88375cf3075928a78ee86c2a068d4
--sha256: 175kjba915bh50wf03gi88njz03jp0n3wp7yq73hznkaly6bpwal
allow-newer: allow-newer:
*:base, *:base,
*:containers, *:containers,

View File

@ -1,20 +1,13 @@
module Main (main) where module Main (main) where
import Data.Vector.Storable qualified as V
import GarnetRs.Wrapped import GarnetRs.Wrapped
import System.IO
main :: IO () main :: IO ()
main = do main = do
hello "Haskell" hello "Haskell"
helloStruct T{a = True, b = 42} helloStruct T{a = True, b = 42}
helloStruct T{a = False, b = maxBound} helloStruct T{a = False, b = maxBound}
helloEnum E1
helloEnum E3
helloShape $ Circle 3.14 helloShape $ Circle 3.14
helloShape $ Rectangle 10.0 5.0 helloShape $ Rectangle 10.0 5.0
putStrLn $ "3 + 4 = " <> show (add 3 4) putStrLn $ "3 + 4 = " <> show (add 3 4)
putStrLn $ "Tree sum: " <> show (sumTree (Fork (Fork (Leaf 1) (Fork (Leaf 2) (Leaf 3))) (Leaf 4))) putStrLn $ "Tree sum: " <> show (sumTree (Fork (Fork (Leaf 1) (Fork (Leaf 2) (Leaf 3))) (Leaf 4)))
putStrLn $ "Slice sum: " <> show (sumSlice $ V.fromList [0 .. 5])
putStrLn "Nothing." >> printOptional Nothing
putStr "Something: " >> hFlush stdout >> printOptional (Just 67)

189
flake.lock generated
View File

@ -85,11 +85,11 @@
}, },
"crane": { "crane": {
"locked": { "locked": {
"lastModified": 1774313767, "lastModified": 1771438068,
"narHash": "sha256-hy0XTQND6avzGEUFrJtYBBpFa/POiiaGBr2vpU6Y9tY=", "narHash": "sha256-nGBbXvEZVe/egCPVPFcu89RFtd8Rf6J+4RFoVCFec0A=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "3d9df76e29656c679c744968b17fbaf28f0e923d", "rev": "b5090e53e9d68c523a4bb9ad42b4737ee6747597",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -99,21 +99,6 @@
} }
}, },
"flake-compat": { "flake-compat": {
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1672831974, "lastModified": 1672831974,
@ -151,11 +136,11 @@
"hackage": { "hackage": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1770685064, "lastModified": 1771461386,
"narHash": "sha256-e2G/GFzIdTXosxq+3Z0OXbSUmXy9tDbZZ6wsmfy7Hf8=", "narHash": "sha256-93hCxhNOq9/HAggfTTurLhllI0IiEWZ35jk6i/X/qEo=",
"owner": "input-output-hk", "owner": "input-output-hk",
"repo": "hackage.nix", "repo": "hackage.nix",
"rev": "84628a8e4b5d580766d49d62a5e2e065cbb8604e", "rev": "b4f4537825a4db29c10541a50d7eb6c848bf0dca",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -167,11 +152,11 @@
"hackage-for-stackage": { "hackage-for-stackage": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1770683900, "lastModified": 1771461376,
"narHash": "sha256-03cshwLY80e5jBUgNBmoFoiH1e3VvhAX+1ME4NVt5g4=", "narHash": "sha256-aEuZkBpTaU0XBFiaIomicJDoWQaw84rYfKXI/r17V/w=",
"owner": "input-output-hk", "owner": "input-output-hk",
"repo": "hackage.nix", "repo": "hackage.nix",
"rev": "c7d9e737cd1e07136982e0df0f9aa12391e2c883", "rev": "ca611068e77f15b96ecf71fae827e491c0b2dc3b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -204,7 +189,7 @@
"cabal-34": "cabal-34", "cabal-34": "cabal-34",
"cabal-36": "cabal-36", "cabal-36": "cabal-36",
"cardano-shell": "cardano-shell", "cardano-shell": "cardano-shell",
"flake-compat": "flake-compat_2", "flake-compat": "flake-compat",
"hackage": "hackage", "hackage": "hackage",
"hackage-for-stackage": "hackage-for-stackage", "hackage-for-stackage": "hackage-for-stackage",
"hackage-internal": "hackage-internal", "hackage-internal": "hackage-internal",
@ -225,7 +210,6 @@
"hpc-coveralls": "hpc-coveralls", "hpc-coveralls": "hpc-coveralls",
"iserv-proxy": "iserv-proxy", "iserv-proxy": "iserv-proxy",
"nixpkgs": [ "nixpkgs": [
"nix-haskell",
"haskell-nix", "haskell-nix",
"nixpkgs-unstable" "nixpkgs-unstable"
], ],
@ -240,17 +224,16 @@
"stackage": "stackage" "stackage": "stackage"
}, },
"locked": { "locked": {
"lastModified": 1773942398, "lastModified": 1771462666,
"narHash": "sha256-VFUyPaWqXN357StToBRFbOCZAP4n8iCfEh2ZV1dNH88=", "narHash": "sha256-z+pJFzbYa1ulNDUZdb0qLVVAdE0dqpnE51jqh2chA7c=",
"owner": "ymeister", "owner": "input-output-hk",
"repo": "haskell.nix", "repo": "haskell.nix",
"rev": "9fa7c11202e16c8cd2743deed4d811da1cfb2e51", "rev": "857ba7b65d9c1a8765ad1599834a8fe71cb09981",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "ymeister", "owner": "input-output-hk",
"repo": "haskell.nix", "repo": "haskell.nix",
"rev": "9fa7c11202e16c8cd2743deed4d811da1cfb2e51",
"type": "github" "type": "github"
} }
}, },
@ -287,6 +270,23 @@
"type": "github" "type": "github"
} }
}, },
"hls-2-13": {
"flake": false,
"locked": {
"lastModified": 1768552477,
"narHash": "sha256-Y4XVuyJZnZ++u+wSHlz7xnN9+nk29csahnXDC0QMiGM=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "d744a66a853c287e671e06e62f2a58dd8e7a86d9",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.13.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.0": { "hls-2.0": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -491,23 +491,6 @@
"type": "github" "type": "github"
} }
}, },
"hls-src": {
"flake": false,
"locked": {
"lastModified": 1768552477,
"narHash": "sha256-Y4XVuyJZnZ++u+wSHlz7xnN9+nk29csahnXDC0QMiGM=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "d744a66a853c287e671e06e62f2a58dd8e7a86d9",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.13.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hpc-coveralls": { "hpc-coveralls": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -524,22 +507,6 @@
"type": "github" "type": "github"
} }
}, },
"hs-bindgen-src": {
"flake": false,
"locked": {
"lastModified": 1776078838,
"narHash": "sha256-f8SE6IuACy8bzIX+EOYTpekaAI/TOQ3zTwJKJmHl3LY=",
"owner": "well-typed",
"repo": "hs-bindgen",
"rev": "ca714aca1ed941271ac248488c9a469554c5b470",
"type": "github"
},
"original": {
"owner": "well-typed",
"repo": "hs-bindgen",
"type": "github"
}
},
"iserv-proxy": { "iserv-proxy": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -557,59 +524,6 @@
"type": "github" "type": "github"
} }
}, },
"libclang-src": {
"flake": false,
"locked": {
"lastModified": 1775645278,
"narHash": "sha256-JWARcbqH432vWcCvQ2bagTXBka8sCRNAnK1qOmnLWKw=",
"owner": "well-typed",
"repo": "libclang",
"rev": "bf574efcb492d2738d8a82b6ce556f840ff92778",
"type": "github"
},
"original": {
"owner": "well-typed",
"repo": "libclang",
"type": "github"
}
},
"nix-haskell": {
"inputs": {
"flake-compat": "flake-compat",
"haskell-nix": "haskell-nix",
"nixpkgs": "nixpkgs",
"reflex-platform": "reflex-platform"
},
"locked": {
"lastModified": 1774408168,
"narHash": "sha256-do26Ik50yH8NYSxuhwzBwNKsxIN88GoovwioWPvCpJU=",
"owner": "reflex-frp",
"repo": "nix-haskell",
"rev": "d6a3bb8f96ff35fd0ae2a0d64f40b584c92aa493",
"type": "github"
},
"original": {
"owner": "reflex-frp",
"repo": "nix-haskell",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1769089682,
"narHash": "sha256-9yA/LIuAVQq0lXelrZPjLuLVuZdm03p8tfmHhnDIkms=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "078d69f03934859a181e81ba987c2bb033eebfc5",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "078d69f03934859a181e81ba987c2bb033eebfc5",
"type": "github"
}
},
"nixpkgs-2305": { "nixpkgs-2305": {
"locked": { "locked": {
"lastModified": 1705033721, "lastModified": 1705033721,
@ -739,34 +653,15 @@
"type": "github" "type": "github"
} }
}, },
"reflex-platform": {
"flake": false,
"locked": {
"lastModified": 1760924715,
"narHash": "sha256-pUraGts8qa3R9jQqaVXevkC+nM07B8oF+LGF4xsyiJU=",
"owner": "reflex-frp",
"repo": "reflex-platform",
"rev": "4482ecb04c5939ac77c26d769d149dee12051a13",
"type": "github"
},
"original": {
"owner": "reflex-frp",
"repo": "reflex-platform",
"rev": "4482ecb04c5939ac77c26d769d149dee12051a13",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"crane": "crane", "crane": "crane",
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"hls-src": "hls-src", "haskell-nix": "haskell-nix",
"hs-bindgen-src": "hs-bindgen-src", "hls-2-13": "hls-2-13",
"libclang-src": "libclang-src",
"nix-haskell": "nix-haskell",
"nixpkgs": [ "nixpkgs": [
"nix-haskell", "haskell-nix",
"nixpkgs" "nixpkgs-2511"
], ],
"rust-overlay": "rust-overlay" "rust-overlay": "rust-overlay"
} }
@ -778,11 +673,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1774408260, "lastModified": 1771470520,
"narHash": "sha256-Jn9d9r85dmf3gTMnSRt6t+DP2nQ5uJns/MMXg2FpzfM=", "narHash": "sha256-PvytHcaYN5cPUll7FB70mXv1rRsIBRmu47fFfq3haxA=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "d6471ee5a8f470251e6e5b83a20a182eb6c46c9b", "rev": "a1d4cc1f264c45d3745af0d2ca5e59d460e58777",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -794,11 +689,11 @@
"stackage": { "stackage": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1770596307, "lastModified": 1771460325,
"narHash": "sha256-88b6YzeBaQr9ihSWAOxVGewCDfROKt2rOIHymadu3DI=", "narHash": "sha256-RRsGO7QVyuBtUTVqDiB7CYvFruj0Z/yxZK1JarkTqI4=",
"owner": "input-output-hk", "owner": "input-output-hk",
"repo": "stackage.nix", "repo": "stackage.nix",
"rev": "ff21e9b6a976faed56963ae5fa5d5396dc48ea63", "rev": "29c0dcc517e04be238023064412312ad4443ddb2",
"type": "github" "type": "github"
}, },
"original": { "original": {

119
flake.nix
View File

@ -1,10 +1,8 @@
{ {
inputs = { inputs = {
nix-haskell.url = "github:reflex-frp/nix-haskell"; haskell-nix.url = "github:input-output-hk/haskell.nix";
nixpkgs.follows = "nix-haskell/nixpkgs"; hls-2-13 = { url = "github:haskell/haskell-language-server/2.13.0.0"; flake = false; };
hls-src = { url = "github:haskell/haskell-language-server/2.13.0.0"; flake = false; }; nixpkgs.follows = "haskell-nix/nixpkgs-2511";
hs-bindgen-src = { url = "github:well-typed/hs-bindgen"; flake = false; };
libclang-src = { url = "github:well-typed/libclang"; flake = false; };
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
crane.url = "github:ipetkov/crane"; crane.url = "github:ipetkov/crane";
rust-overlay = { rust-overlay = {
@ -13,96 +11,51 @@
}; };
}; };
outputs = outputs =
inputs: inputs@{ nixpkgs, ... }:
inputs.flake-utils.lib.eachSystem [ "x86_64-linux" ] (system: inputs.flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
let let
pkgs = haskell.nixpkgs.extend (import inputs.rust-overlay); overlays = [
crane = (inputs.crane.mkLib pkgs).overrideToolchain (p: p.rust-bin.selectLatestNightlyWith ( inputs.haskell-nix.overlay
toolchain: toolchain.default.override { (final: _prev: {
extensions = [ "rust-src" "rust-analyzer" ]; hixProject =
targets = [ "x86_64-unknown-linux-gnu" ]; final.haskell-nix.hix.project {
} src = ./.;
)); evalSystem = "x86_64-linux";
addLibcIncludeDir = name = "garnet";
'' compiler-nix-name = "ghc9141";
export C_INCLUDE_PATH="${pkgs.stdenv.cc.libc.dev}/include''${C_INCLUDE_PATH:+:$C_INCLUDE_PATH}" shell.tools.cabal = "latest";
''; shell.withHoogle = false;
haskell = (import inputs.nix-haskell { inherit system inputs; } { shell.tools.haskell-language-server = {
name = "garnet"; src = inputs.hls-2-13;
src = ./.; sha256map = {
compiler-nix-name = "ghc914"; "https://github.com/snowleopard/alga"."d4e43fb42db05413459fb2df493361d5a666588a" = "0s1mlnl64wj7pkg3iipv5bb4syy3bhxwqzqv93zqlvkyfn64015i";
source-repository-packages = {
# not on Hackage yet: https://well-typed.com/blog/2026/02/hs-bindgen-alpha
# we're using more recent versions than in that post, because we want record-dot support:
# https://github.com/well-typed/hs-bindgen/issues/1829#issuecomment-4081875451
c-expr-dsl = inputs.hs-bindgen-src + "/c-expr-dsl";
c-expr-runtime = inputs.hs-bindgen-src + "/c-expr-runtime";
hs-bindgen = inputs.hs-bindgen-src + "/hs-bindgen";
hs-bindgen-runtime = inputs.hs-bindgen-src + "/hs-bindgen-runtime";
libclang-bindings = inputs.libclang-src;
};
overrides = [
({ pkgs, ... }: {
packages.libclang-bindings.components.library = {
build-tools = [ pkgs.llvmPackages.llvm ];
libs = [ pkgs.llvmPackages.libclang ];
};
packages.garnet.components.library = {
preBuild = addLibcIncludeDir;
# try removing?
preConfigure = ''
mkdir -p rust/target/debug
ln -s ${garnet-rs}/include/garnet_rs.h rust/target/debug/garnet_rs.h
'';
preInstall = ''
cp ${garnet-rs}/lib/libCgarnet_rs.a dist/build/libCgarnet_rs.a
'';
configureFlags = [ "--extra-lib-dirs=${garnet-rs}/lib" ];
};
})
];
shell = {
tools = {
cabal = "latest";
haskell-language-server = {
src = inputs.hls-src;
sha256map = {
"https://github.com/snowleopard/alga" = {
"d4e43fb42db05413459fb2df493361d5a666588a" = "0s1mlnl64wj7pkg3iipv5bb4syy3bhxwqzqv93zqlvkyfn64015i";
}; };
}; };
}; };
}; })
withHoogle = false; (import inputs.rust-overlay)
withHaddock = true; ];
shellHook = addLibcIncludeDir; pkgs = import nixpkgs { inherit system overlays; inherit (inputs.haskell-nix) config; };
}; haskell = pkgs.hixProject.flake { };
}); rust = (inputs.crane.mkLib pkgs).overrideToolchain (p: p.rust-bin.selectLatestNightlyWith (
garnet-rs = crane.buildPackage { toolchain: toolchain.default.override {
src = crane.cleanCargoSource ./rust; extensions = [ "rust-src" ];
doCheck = false; targets = [ "x86_64-unknown-linux-gnu" ];
installPhaseCommand = '' }
mkdir -p $out/lib $out/include ));
cp target/release/libgarnet_rs.a $out/lib/libCgarnet_rs.a
cp target/release/garnet_rs.h $out/include/
'';
};
in in
rec { {
defaultPackage = packages.garnet-exe;
packages = let garnet = haskell.project.haskell-nix.hsPkgs.garnet; in {
inherit garnet-rs;
garnet-exe = garnet.components.exes.garnet;
garnet-lib = garnet.components.library;
};
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
inputsFrom = [ inputsFrom = [
haskell.project.haskell-nix.shell haskell.devShells.default
(crane.devShell { }) (rust.devShell { })
]; ];
packages = with pkgs; [ packages = with pkgs; [
bacon bacon
ghcid ghcid
llvmPackages.libclang
llvmPackages.llvm
rust-analyzer
]; ];
}; };
}); });

View File

@ -6,21 +6,6 @@ author: Patrick Aldis
maintainer: maintainer:
george.thomas@obsidian.systems george.thomas@obsidian.systems
patrick.aldis@obsidian.systems patrick.aldis@obsidian.systems
-- aha, nice, this does fix recompilation checking
extra-source-files:
rust/target/debug/garnet_rs.h
-- that could be problematic given file is gitignored? unfortunately this doesn't work
-- tbf, I haven't even looked up the docs, just saw the autocompletion
-- extra-tmp-files:
-- rust/target/debug/garnet_rs.h
-- actually maybe this is pointless given we always need debug path anyway in `extra-source-files`
-- plus we have it in `Raw.hs`
flag dev
default:
False
manual:
True
common common common common
default-language: GHC2024 default-language: GHC2024
@ -37,20 +22,17 @@ common common
NoMonomorphismRestriction NoMonomorphismRestriction
OverloadedRecordDot OverloadedRecordDot
OverloadedStrings OverloadedStrings
PatternSynonyms
RecordWildCards RecordWildCards
ViewPatterns ViewPatterns
ghc-options: ghc-options:
-Wall -Wall
-fdefer-type-errors -fdefer-type-errors
build-depends: build-depends:
base,
bytestring, bytestring,
extra, extra,
mtl, mtl,
process, process,
text, text,
vector,
library library
import: common import: common
@ -58,16 +40,13 @@ library
GarnetRs.Raw GarnetRs.Raw
GarnetRs.Wrapped GarnetRs.Wrapped
hs-source-dirs: lib hs-source-dirs: lib
-- if flag (dev)
-- include-dirs: rust/target/debug
include-dirs: rust/target/debug include-dirs: rust/target/debug
-- HLS gives up entirely when the header is malformed if we do this extra-bundled-libraries: garnet_rs
-- and anyway, I don't think it gives us dependency tracking like `extra-source-files` does
-- includes: garnet_rs.h
extra-bundled-libraries: Cgarnet_rs
build-depends: build-depends:
base,
hs-bindgen, hs-bindgen,
hs-bindgen-runtime, hs-bindgen-runtime,
primitive,
template-haskell, template-haskell,
executable garnet executable garnet
@ -79,4 +58,5 @@ executable garnet
-rtsopts -rtsopts
-with-rtsopts=-N -with-rtsopts=-N
build-depends: build-depends:
base,
garnet, garnet,

View File

@ -1,5 +1,6 @@
{-# LANGUAGE CApiFFI #-} {-# LANGUAGE CApiFFI #-}
{-# LANGUAGE DerivingVia #-} {-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FieldSelectors #-}
{-# LANGUAGE MagicHash #-} {-# LANGUAGE MagicHash #-}
{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
@ -29,10 +30,19 @@ import HsBindgen.TH
import Language.Haskell.TH import Language.Haskell.TH
import System.Process import System.Process
withHsBindgen do
def systemDirs <- -- TODO bit of a hack
{ clang = def{extraIncludeDirs = [Pkg "rust/target/debug"]} map (Dir . T.unpack . T.strip)
, fieldNamingStrategy = OmitFieldPrefixes . concatMap (takeWhile (maybe False ((== ' ') . fst) . T.uncons) . dropWhile T.null . T.lines)
} . drop 1
def . T.splitOn "search starts here:"
$ hashInclude "garnet_rs.h" . T.pack
. thd3
<$> runIO (readProcessWithExitCode "cpp" ["-v"] "")
withHsBindgen
def
{ clang = def{extraIncludeDirs = Pkg "rust/target/debug" : systemDirs}
, fieldNamingStrategy = EnableRecordDot
}
def
$ hashInclude "garnet_rs.h"

View File

@ -2,29 +2,19 @@
-- or look at upstream plans: https://github.com/well-typed/hs-bindgen/issues?q=state%3Aopen%20label%3A%22highlevel%22 -- or look at upstream plans: https://github.com/well-typed/hs-bindgen/issues?q=state%3Aopen%20label%3A%22highlevel%22
module GarnetRs.Wrapped ( module GarnetRs.Wrapped (
T (..), T (..),
Raw.E (..),
-- TODO hmm, we don't really want to have to list all of these...
-- is there an option to make them not be patterns at all?
pattern Raw.E1,
pattern Raw.E2,
pattern Raw.E3,
Shape (..), Shape (..),
BTree (..), BTree (..),
hello, hello,
helloStruct, helloStruct,
helloEnum,
helloShape, helloShape,
add, add,
sumTree, sumTree,
sumSlice,
printOptional,
) where ) where
import Control.Monad.Cont import Control.Monad.Cont
import Control.Monad.Trans import Control.Monad.Trans
import Data.ByteString import Data.ByteString
import Data.Function import Data.Function
import Data.Vector.Storable qualified as V
import Data.Word import Data.Word
import Foreign import Foreign
import Foreign.C import Foreign.C
@ -44,8 +34,8 @@ data Shape
| Rectangle CDouble CDouble | Rectangle CDouble CDouble
convertShape :: Shape -> Raw.Shape convertShape :: Shape -> Raw.Shape
convertShape = \case convertShape = \case
Circle r -> Raw.Shape Raw.Circle $ Raw.set_shape_circle_circle $ Raw.Circle_Body r Circle r -> Raw.Shape Raw.Circle $ Raw.set_shape_body_circle $ Raw.Circle_Body r
Rectangle w h -> Raw.Shape Raw.Rectangle $ Raw.set_shape_circle_rectangle $ Raw.Rectangle_Body w h Rectangle w h -> Raw.Shape Raw.Rectangle $ Raw.set_shape_body_rectangle $ Raw.Rectangle_Body w h
data BTree a data BTree a
= Leaf a = Leaf a
@ -53,38 +43,27 @@ data BTree a
withBTree :: BTree Int64 -> (Raw.BTreeC -> IO a) -> IO a withBTree :: BTree Int64 -> (Raw.BTreeC -> IO a) -> IO a
withBTree = withBTree =
runContT . fix \f -> \case runContT . fix \f -> \case
Leaf v -> pure $ Raw.BTreeC Raw.Leaf $ Raw.set_bTreeC_leaf_leaf $ Raw.Leaf_Body v Leaf v -> pure $ Raw.BTreeC Raw.Leaf $ Raw.set_bTreeC_body_leaf $ Raw.Leaf_Body v
Fork l r -> do Fork l r -> do
lRaw <- f l lRaw <- f l
rRaw <- f r rRaw <- f r
lPtr <- ContT alloca lPtr <- ContT alloca
rPtr <- ContT alloca rPtr <- ContT alloca
lift $ poke lPtr lRaw >> poke rPtr rRaw lift $ poke lPtr lRaw >> poke rPtr rRaw
pure . Raw.BTreeC Raw.Fork . Raw.set_bTreeC_leaf_fork $ pure . Raw.BTreeC Raw.Fork . Raw.set_bTreeC_body_fork $
Raw.Fork_Body (unsafeFromPtr lPtr) (unsafeFromPtr rPtr) Raw.Fork_Body (unsafeFromPtr lPtr) (unsafeFromPtr rPtr)
hello :: ByteString -> IO () hello :: ByteString -> IO ()
hello bs = useAsCString bs \ptr -> Raw.hello $ unsafeFromPtr ptr hello s = useAsCString s $ Raw.hello . unsafeFromPtr
helloStruct :: T -> IO () helloStruct :: T -> IO ()
helloStruct t = with (convertT t) \ptr -> Raw.hello_struct $ unsafeFromPtr ptr helloStruct = Raw.hello_struct . convertT
helloEnum :: Raw.E -> IO ()
helloEnum e = with e \ptr -> Raw.hello_enum $ unsafeFromPtr ptr
helloShape :: Shape -> IO () helloShape :: Shape -> IO ()
helloShape s = with (convertShape s) \ptr -> Raw.hello_shape $ unsafeFromPtr ptr helloShape = Raw.hello_shape . convertShape
add :: Int64 -> Int64 -> Int64 add :: Int64 -> Int64 -> Int64
add = Raw.add add = Raw.add
sumTree :: BTree Int64 -> Int64 sumTree :: BTree Int64 -> Int64
sumTree t = unsafePerformIO $ withBTree t \tc -> with tc \ptr -> Raw.sum_tree $ unsafeFromPtr ptr sumTree = unsafePerformIO . flip withBTree Raw.sum_tree
sumSlice :: V.Vector Int64 -> Int64
sumSlice v = unsafePerformIO $ V.unsafeWith v \ptr -> Raw.sum_slice (unsafeFromPtr ptr) (fromIntegral $ V.length v)
printOptional :: Maybe Int8 -> IO ()
printOptional = \case
Nothing -> Raw.print_optional $ unsafeFromPtr nullPtr
Just t -> with t \ptr -> Raw.print_optional $ unsafeFromPtr ptr

View File

@ -1,36 +1,43 @@
use std::env; use std::env;
use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
fn main() { fn main() {
// this doesn't make _much_ difference really, since this is our only Rust source file
// but it seems it's probably better than not having it
// what we really want is to tell Rust to only regenerate the header file if the Rust code actually compiles
// but we don't have that flexibility
// and it's an issue because cbindgen tries to be fault-tolerant in some ways that don't even seem to make sense
//
// e.g. mis-spell "Option" as "Option" and you get
// void print_optional(Optio<const int8_t*> x);
// instead of
// void print_optional(const int8_t *x);
// and that's only an issue because in HLS TH dependent-file watching gives up after an error
// i.e. once the containing splice has thrown an exception once, the containing file needs a manual edit to kick it
// and that's really not helped by Rust Analyzer mostly only showing diagnostics on save
// P.S. strings to stdout?! what a terrible API
// don't get me started in the discoverability of actually then using the terminal for debugging:
// println!("cargo::warning={:?}", env::var("OUT_DIR"));
println!("cargo::rerun-if-changed=lib.rs");
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let profile = env::var("PROFILE").unwrap(); let profile = env::var("PROFILE").unwrap();
cbindgen::Builder::new()
let bindings = cbindgen::Builder::new()
.with_crate(&crate_dir) .with_crate(&crate_dir)
.with_language(cbindgen::Language::C) .with_language(cbindgen::Language::C)
.with_style(cbindgen::Style::Tag) .with_style(cbindgen::Style::Tag)
.generate() .generate()
.expect("Unable to generate bindings") .expect("Unable to generate bindings");
.write_to_file(
PathBuf::from(&crate_dir) // TODO vibe-coded workaround for hs-bindgen issue:
.join("target") // https://github.com/well-typed/hs-bindgen/issues/1649#issuecomment-3994425922
.join(&profile) let mut buf = Vec::new();
.join("garnet_rs.h"), bindings.write(&mut buf);
); let header = String::from_utf8(buf).unwrap();
let mut patched = String::new();
let mut saw_union = false;
for line in header.lines() {
if line == " };" && saw_union {
patched.push_str(" } body;\n");
saw_union = false;
continue;
}
if line == " union {" {
saw_union = true;
}
patched.push_str(line);
patched.push('\n');
}
fs::write(
PathBuf::from(&crate_dir)
.join("target")
.join(&profile)
.join("garnet_rs.h"),
patched,
)
.unwrap();
} }

View File

@ -3,7 +3,6 @@
use std::{ use std::{
ffi::{CStr, c_char}, ffi::{CStr, c_char},
ops::Add, ops::Add,
slice,
}; };
fn say_hello(name: &str) { fn say_hello(name: &str) {
@ -11,7 +10,7 @@ fn say_hello(name: &str) {
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn hello(c: *const c_char) { extern "C" fn hello(c: *const c_char) -> () {
say_hello(unsafe { CStr::from_ptr(c) }.to_str().unwrap()) say_hello(unsafe { CStr::from_ptr(c) }.to_str().unwrap())
} }
@ -23,23 +22,10 @@ struct T {
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn hello_struct(t: &T) { extern "C" fn hello_struct(t: T) -> () {
say_hello(&format!("{:?}", t)) say_hello(&format!("{:?}", t))
} }
#[repr(C)]
#[derive(Debug)]
enum E {
E1,
E2,
E3,
}
#[unsafe(no_mangle)]
extern "C" fn hello_enum(e: &E) {
say_hello(&format!("{:?}", e))
}
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
enum Shape { enum Shape {
@ -48,7 +34,7 @@ enum Shape {
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn hello_shape(s: &Shape) { extern "C" fn hello_shape(s: Shape) -> () {
say_hello(&format!("{:?}", s)) say_hello(&format!("{:?}", s))
} }
@ -92,19 +78,6 @@ enum BTreeC {
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn sum_tree(t: &BTreeC) -> i64 { extern "C" fn sum_tree(t: BTreeC) -> i64 {
(unsafe { std::mem::transmute::<_, &BTree<i64>>(t) }).sum() (unsafe { std::mem::transmute::<_, &BTree<i64>>(&t) }).sum()
}
#[unsafe(no_mangle)]
extern "C" fn sum_slice(v: *const i64, s: usize) -> i64 {
unsafe { slice::from_raw_parts(v, s) }.iter().sum()
}
#[unsafe(no_mangle)]
extern "C" fn print_optional(x: Option<&i8>) -> () {
match x {
Some(x) => println!("{}", x / 2),
None => {}
}
} }