Skip to content

Commit

Permalink
Merge pull request #164 from mlabs-haskell/szg251/rust-nix
Browse files Browse the repository at this point in the history
lbf-nix for Rust
  • Loading branch information
szg251 authored Dec 21, 2023
2 parents 59d3fb6 + 2730faf commit a3157c1
Show file tree
Hide file tree
Showing 39 changed files with 2,139 additions and 296 deletions.
43 changes: 32 additions & 11 deletions extras/flake-rust.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,36 @@ let
};
craneLib = crane.lib.${pkgs.system}.overrideToolchain rustWithTools;

cleanSrc = craneLib.cleanCargoSource (craneLib.path src);

# Library source code with extra dependencies attached
fullSrc = pkgs.stdenv.mkDerivation {
src = craneLib.cleanCargoSource (craneLib.path src);
name = "lbf-rust-build-env";
unpackPhase = ''
mkdir $out
cp -r $src/* $out
cd $out
${copyExtraSources}
${copyData}
'';
};
fullSrc =
pkgs.stdenv.mkDerivation
{
src = cleanSrc;
name = "${crateName}-build-env";
unpackPhase = ''
mkdir $out
cp -r $src/* $out
cd $out
${copyExtraSources}
${copyData}
'';
};

vendoredSrc =
pkgs.stdenv.mkDerivation
{
src = cleanSrc;
name = "${crateName}-vendored-src";
unpackPhase = ''
mkdir $out
cp -r $src/* $out
cd $out
sed -i 's/.extras/../g' Cargo.toml
'';
};

commonArgs = {
src = fullSrc;
pname = crateName;
Expand All @@ -28,6 +46,7 @@ let

# Extra sources
extra-sources = pkgs.linkFarm "extra-sources" extraSources;

hasExtraSources = builtins.length extraSources > 0;
linkExtraSources = pkgs.lib.optionalString hasExtraSources ''
echo "Linking extra sources"
Expand Down Expand Up @@ -68,6 +87,8 @@ in
doInstallCargoArtifacts = true;
});

packages."${crateName}-rust-src" = vendoredSrc;

checks."${crateName}-rust-test" = craneLib.cargoNextest (commonArgs // {
inherit cargoArtifacts;
nativeBuildInputs = testTools;
Expand Down
4 changes: 4 additions & 0 deletions extras/lbf-nix/build.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ _: {
lbg-haskell = "${config.packages.lbg-haskell}/bin/lbg-haskell";
lbg-plutarch = "${config.packages.lbg-plutarch}/bin/lbg-plutarch";
lbg-purescript = "${config.packages.lbg-purescript}/bin/lbg-purescript";
lbg-rust = "${config.packages.lbg-rust}/bin/lbg-rust";

in
{
Expand All @@ -18,6 +19,9 @@ _: {
lbfPurescript = import ./lbf-purescript.nix pkgs config.packages.lbf lbg-purescript;
lbfPreludePurescript = import ./lbf-prelude-purescript.nix pkgs config.packages.lbf lbg-purescript;
lbfPlutusPurescript = import ./lbf-plutus-purescript.nix pkgs config.packages.lbf lbg-purescript;
lbfRust = import ./lbf-rust.nix pkgs config.packages.lbf lbg-rust;
lbfPreludeRust = import ./lbf-prelude-rust.nix pkgs config.packages.lbf lbg-rust;
lbfPlutusRust = import ./lbf-plutus-rust.nix pkgs config.packages.lbf lbg-rust;
};

};
Expand Down
27 changes: 27 additions & 0 deletions extras/lbf-nix/lbf-plutus-rust.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Build .lbf schemas that use LB Plutus (and by extension LB Prelude) package and targets Rust's plutus-ledger-api library.
pkgs: lbf: lbg-rust: lbfRustOpts:
let
utils = import ./utils.nix pkgs;

lbfRust = import ./lbf-rust.nix pkgs lbf lbg-rust;
lbfRustOptsForPlutus = utils.overrideAttrs
{
imports = {
default = { };
override = libs: libs // {
lbf-prelude = ../../libs/lbf-prelude;
lbf-plutus = ../../libs/lbf-plutus;
};
};
classes = {
default = [ ];
override = cls: cls ++ [ "Prelude.Eq" "Plutus.V1.PlutusData" ];
};
configs = {
default = [ ];
override = _: [ ../../lambda-buffers-codegen/data/rust-prelude-base.json ../../lambda-buffers-codegen/data/rust-plutus-pla.json ];
};
}
lbfRustOpts;
in
lbfRust lbfRustOptsForPlutus
25 changes: 25 additions & 0 deletions extras/lbf-nix/lbf-prelude-rust.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Build .lbf schemas that use LB Prelude package and targets Rust's std (and friends) library.
pkgs: lbf: lbg-rust: lbfRustOpts:
let
utils = import ./utils.nix pkgs;

lbfRs = import ./lbf-rust.nix pkgs lbf lbg-rust;
lbfRustOptsForPrelude = utils.overrideAttrs
{
imports = {
default = { };
override = libs: libs // { lbf-prelude = ../../libs/lbf-prelude; };
};
classes = {
default = [ ];
override = cls: cls ++ [ "Prelude.Eq" "Prelude.Json" ];
};
configs = {
default = [ ];
override = cfgs: cfgs ++ [ ../../lambda-buffers-codegen/data/rust-prelude-base.json ];
};
}
lbfRustOpts;

in
lbfRs lbfRustOptsForPrelude
154 changes: 154 additions & 0 deletions extras/lbf-nix/lbf-rust.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Base API for constructing Rust packages given .lbf schemas

# Nixpkgs
pkgs:
# LambdaBuffers Frontend
lbf:
# LambdaBuffers Rust Codegen
lbg-rust:
let
lbfRustOpts =
{
# Source that is passed to `lbf` as the `--import-path` flag and used to find `files`.
# Examples: src = ./api
src
, # Additional sources that are passed to `lbf` as the `--import-path` flag.
# Examples: imports = { lbf-prelude = ./lbf-prelude; }
imports ? { }
, # .lbf files in `src` to compile and codegen.
# Examples: files = [ "Foo.lbf" "Foo/Bar.lbf" ]
files
# Classes for which to generate implementations for (default lbf-prelude classes).
, classes ? [ ]
, # Dependencies to include in the Cabal's `build-depends` stanza.
# examples: dependencies = [ "lbf-prelude" ]
dependencies ? [ ]
, configs ? [ ]
, # Name of the package and also the name of the Cabal package.
# Examples: name = "lbf-myproject"
name
, # Version of the package and also the version of the Cabal package.
# Examples: version = "0.1.0.0"
version ? "0.1.0"
}: { inherit src imports files classes dependencies configs name version; };

lbf-build = import ./lbf-build.nix pkgs lbf;

lbfBuild = opts: with (lbfRustOpts opts);
let
findModules = root: map
(path: builtins.replaceStrings [ "/" ] [ "." ]
(pkgs.lib.strings.removePrefix "./" (pkgs.lib.strings.removeSuffix ".lbf"
(pkgs.lib.path.removePrefix root path))))
(builtins.filter (pkgs.lib.hasSuffix ".lbf")
(pkgs.lib.filesystem.listFilesRecursive root));
packageSet =
pkgs.writeTextFile {
name = "lb-packages";
text =
builtins.toJSON
({ crate = findModules src; } // builtins.mapAttrs (_: findModules) imports);
};

in
lbf-build.build
{
inherit src;
opts = {
inherit files;
import-paths = pkgs.lib.attrsets.attrValues imports;
gen = lbg-rust;
gen-classes = classes;
gen-dir = "autogen";
gen-opts = [ "--packages=${packageSet}" ] ++ builtins.map (c: "--config=${c}") configs; # WARN(bladyjoker): If I put quotes here everything breaks.
work-dir = ".work";
};
};

cargoTemplate = opts: with (lbfRustOpts opts);
pkgs.writeTextFile {
name = "lambda-buffers-cabal-template";
text = ''
[package]
name = "${name}"
version = "${version}"
edition = "2021"
[dependencies]
'';
};

#
crateVersions = pkgs.writeTextFile {
name = "lambda-buffers-crate-versions";
text = ''
num-bigint = "0.4.4"
serde_json = { version = "1.0.107", features = ["arbitrary_precision"] }
plutus-ledger-api = { git = "https://github.com/mlabs-haskell/plutus-ledger-api-rust", features = [ "lbf", ], rev = "fb93fa590908580eb40368369bf6614d42ce9a95" }
'';
};

build = opts: with (lbfRustOpts opts);
let
lbfBuilt = lbfBuild opts;
in
pkgs.stdenv.mkDerivation {
inherit src version;
pname = name;
outputs = [ "out" "buildjson" ];
buildInputs = [
pkgs.jq
];
buildPhase = ''
ln -s ${lbfBuilt} autogen;
ln -s ${lbfBuilt.workdir} .work-dir;
ln -s ${lbfBuilt.buildjson} build.json;
# Generating Cargo manifest file
DEPS=$(echo ${builtins.concatStringsSep " " dependencies} $(cat build.json | jq -r ".[]" | sort -u));
echo "Gathered Cargo deps $DEPS";
cat ${cargoTemplate opts} > Cargo.toml;
for DEP in $DEPS; do
if [ $DEP != "std" ]; then
echo "$(cat ${crateVersions} | grep "$DEP" || echo "$DEP = { path = \"../$DEP\" }")" >> Cargo.toml
fi
done
'';

installPhase = ''
cp build.json $buildjson;
echo "Dependencies collected"
cat $buildjson;
mkdir -p $out/src;
cp -r autogen/* $out/src
cp Cargo.toml $out/Cargo.toml;
# Generating module files
chmod -R u+w $out/src
pushd $out/src
MODS=$(find . -type f -name "*.rs")
MODS+=" "
MODS+=$(find . -type d)
for MOD in $MODS; do
if [ "$MOD" != "." ]; then
if [ $(dirname $MOD) = "." ];
then MODFILE="lib.rs";
else MODFILE=$(dirname $MOD).rs;
fi
DOC="pub mod $(basename $MOD .rs);"
if [ ! $(grep "$DOC" $MODFILE) ]; then
echo $DOC >> $MODFILE;
fi
fi
done
echo "Files generated"
find $out/;
'';
};
in
build
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@
./testsuites/lbt-prelude/golden/build.nix
./testsuites/lbt-prelude/lbt-prelude-haskell/build.nix
./testsuites/lbt-prelude/lbt-prelude-purescript/build.nix
./testsuites/lbt-prelude/lbt-prelude-rust/build.nix
./testsuites/lbt-plutus/api/build.nix
./testsuites/lbt-plutus/golden/build.nix
./testsuites/lbt-plutus/lbt-plutus-haskell/build.nix
./testsuites/lbt-plutus/lbt-plutus-purescript/build.nix
./testsuites/lbt-plutus/lbt-plutus-plutarch/build.nix
./testsuites/lbt-plutus/lbt-plutus-rust/build.nix
./experimental/build.nix
];
debug = true;
Expand Down
24 changes: 23 additions & 1 deletion lambda-buffers-codegen/app/LambdaBuffers/Codegen/Cli/GenRust.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ module LambdaBuffers.Codegen.Cli.GenRust (GenOpts (..), gen) where
import Control.Lens (makeLenses, (^.))
import Control.Monad (unless)
import Data.Aeson (decodeFileStrict')
import Debug.Trace (trace)
import LambdaBuffers.Codegen.Cli.Gen (logError)
import LambdaBuffers.Codegen.Cli.Gen qualified as Gen
import LambdaBuffers.Codegen.Rust (runPrint)
import LambdaBuffers.Codegen.Rust.Config qualified as R
import LambdaBuffers.Codegen.Rust.Print.Syntax qualified as RS
import Paths_lambda_buffers_codegen qualified as Paths
import System.Directory (doesFileExist)
import System.Directory.Internal.Prelude (exitFailure)

data GenOpts = MkGenOpts
{ _config :: [FilePath]
, _packages :: FilePath
, _common :: Gen.GenOpts
}

Expand All @@ -28,9 +31,12 @@ gen opts = do
cfgs <- traverse readRustConfig fps
return (mconcat cfgs)

let pkgsCfg = opts ^. packages
pkgs <- readPackages pkgsCfg

Gen.gen
(opts ^. common)
(\ci -> fmap (\(fp, code, deps) -> Gen.Generated fp code deps) . runPrint cfg ci <$> (ci ^. #modules))
(\ci -> fmap (\(fp, code, deps) -> Gen.Generated fp code deps) . runPrint cfg pkgs ci <$> (ci ^. #modules))

readRustConfig :: FilePath -> IO R.Config
readRustConfig f = do
Expand All @@ -47,3 +53,19 @@ readRustConfig f = do
logError "" $ "Invalid Rust configuration file " <> f
exitFailure
Just cfg -> return cfg

readPackages :: FilePath -> IO RS.PkgMap
readPackages f = do
fExists <- doesFileExist f
unless
fExists
( do
logError "" $ "Provided Rust Codegen package manifest file doesn't exists: " <> f
exitFailure
)
mayPkgs <- decodeFileStrict' f
case mayPkgs of
Nothing -> do
logError "" $ "Invalid Rust package manifest file " <> f
exitFailure
Just pkgs -> trace (show pkgs) (R.mkPkgMap pkgs)
6 changes: 6 additions & 0 deletions lambda-buffers-codegen/app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ rustGenOptsP =
<> help "Configuration file for the Rust Codegen module (multiple `config`s are merged with left first merge conflict strategy)"
)
)
<*> strOption
( long "packages"
<> short 'g'
<> metavar "FILEPATH"
<> help "JSON file containing the package-set and all of its modules (including current package)"
)
<*> genOptsP

mkProgDesc :: forall {a}. String -> InfoMod a
Expand Down
2 changes: 1 addition & 1 deletion lambda-buffers-codegen/data/rust-plutus-pla.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"plutus-ledger-api",
"v1",
"interval",
"Interval"
"PlutusInterval"
],
"Plutus.V1.Extended": [
"plutus-ledger-api",
Expand Down
2 changes: 1 addition & 1 deletion lambda-buffers-codegen/data/rust-prelude-base.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"char"
],
"Prelude.Integer": [
"num_bigint",
"num-bigint",
"BigInt"
],
"Prelude.Bool": [
Expand Down
Loading

0 comments on commit a3157c1

Please sign in to comment.