1
1
mirror of https://github.com/nmattia/snack.git synced 2024-09-11 11:55:36 +03:00

Add rudimentary HPack support

This commit is contained in:
Nicolas Mattia 2018-07-02 22:56:26 +02:00
parent a05c5655c8
commit 3c8ef4ec70
6 changed files with 226 additions and 36 deletions

View File

@ -32,13 +32,20 @@ import qualified Shelly as S
data Mode
= Standalone SnackNix -- Reads a snack.nix file
| HPack PackageYaml
-- | Like a FilePath, but Nix friendly
newtype SnackNix = SnackNix { unSnackNix :: FilePath }
newtype PackageYaml = PackageYaml { unPackageYaml :: FilePath }
mkSnackNix :: FilePath -> SnackNix
mkSnackNix = SnackNix -- XXX: this is not nix friendly, but it's ok, because
-- it'll be gone soon
--
mkPackageYaml :: FilePath -> PackageYaml
mkPackageYaml = PackageYaml -- XXX: this is not nix friendly, but it's ok, because
-- it'll be gone soon
data Command
= Build
@ -66,16 +73,26 @@ instance Aeson.FromJSON Project where
parseMode :: Opts.Parser Mode
parseMode =
Opts.flag Standalone Standalone
(Opts.long "--standalone")
-- Opts.flag Standalone Standalone
-- (Opts.long "standalone")
<*> (mkSnackNix <$>
((Standalone . mkSnackNix) <$>
Opts.strOption
(Opts.long "--snack-nix"
(Opts.long "snack-nix"
<> Opts.short 's'
<> Opts.value "./snack.nix"
<> Opts.metavar "PATH")
)
<|>
-- Opts.flag HPack HPack
-- (Opts.long "hpack")
((HPack . mkPackageYaml) <$>
Opts.strOption
(Opts.long "package-yaml"
<> Opts.value "./package.yaml"
<> Opts.metavar "PATH")
)
options :: Opts.Parser Options
options = Options <$> parseMode <*> parseCommand
@ -127,6 +144,49 @@ snackBuildGhci snackNix = do
let Just proj = Aeson.decodeStrict' json
pure proj
snackBuildHPack :: PackageYaml -> Sh Project
snackBuildHPack packageYaml = do
out <- runStdin1
(T.pack [i|
{ base, packageYaml, lib64, specJson }:
let
spec = builtins.fromJSON specJson;
pkgs = import (builtins.fetchTarball
{ url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz";
sha256 = spec.sha256;
}) {} ;
libDir =
let
b64 = pkgs.writeTextFile { name = "lib-b64"; text = lib64; };
in
pkgs.runCommand "snack-lib" {}
''
cat ${b64} | base64 --decode > out.tar.gz
mkdir -p $out
tar -C $out -xzf out.tar.gz
chmod +w $out
'';
snack = pkgs.callPackage libDir {};
proj = snack.packageYaml packageYaml;
in
{ build = proj.library.build;
ghci = proj.librayr.build;
}
|]
)
"nix-build"
[ "-"
, "--arg", "packageYaml", T.pack $ unPackageYaml packageYaml
, "--argstr", "lib64", libb64
, "--argstr", "specJson", specJson
, "--arg", "base", "./app"
, "--no-out-link"
, "-A", "build.json"
]
json <- liftIO $ BS.readFile (T.unpack out)
let Just proj = Aeson.decodeStrict' json
pure proj
snackBuild :: SnackNix -> Sh Project
snackBuild snackNix = do
out <- runStdin1
@ -178,6 +238,14 @@ runCommand (Standalone snackNix) = \case
snackRun build = do
fp <- S.shelly $ S.print_stdout False $ exePath <$> build snackNix
executeFile fp True [] Nothing
runCommand (HPack packageYaml) = \case
Build -> S.shelly $ void $ snackBuildHPack packageYaml
Run -> undefined -- snackRun snackBuild
Ghci -> undefined -- snackRun snackBuildGhci
-- where
-- snackRun build = do
-- fp <- S.shelly $ S.print_stdout False $ exePath <$> build packageYaml
-- executeFile fp True [] Nothing
parseCommand :: Opts.Parser Command
parseCommand =

View File

@ -1,15 +1,20 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash
#!nix-shell -I nixpkgs=./nix
#!nix-shell -p snack-exe
#!nix-shell -p shfmt
#!nix-shell -p git
#!nix-shell -p jq
#!nix-shell -p nix
#!nix-shell -p shfmt
#!nix-shell -p snack-exe
#!nix-shell -p glibcLocales
#!nix-shell --pure
# vim: ft=sh sw=2 et
set -euo pipefail
export LC_ALL="en_US.utf-8"
export LANG="en_US.utf-8"
## Functions
banner() {
@ -44,43 +49,48 @@ fail() {
cd "$(dirname "$0")/.."
banner "Test packages"
pushd tests/packages
./test
popd
#banner "Test packages"
#pushd tests/packages
#./test
#popd
banner "Test Template Haskell"
pushd tests/template-haskell
./test
popd
#banner "Test Template Haskell"
#pushd tests/template-haskell
#./test
#popd
banner "Test Template Haskell 2"
pushd tests/template-haskell-2
./test
popd
#banner "Test Template Haskell 2"
#pushd tests/template-haskell-2
#./test
#popd
banner "Test Template Haskell 3"
pushd tests/template-haskell-3
./test
popd
#banner "Test Template Haskell 3"
#pushd tests/template-haskell-3
#./test
#popd
banner "Test Template Haskell 4"
pushd tests/template-haskell-4
./test
popd
#banner "Test Template Haskell 4"
#pushd tests/template-haskell-4
#./test
#popd
banner "Test library"
pushd tests/library
./test
popd
#banner "Test library"
#pushd tests/library
#./test
#popd
banner "Test library-2"
pushd tests/library-2
./test
popd
#banner "Test library-2"
#pushd tests/library-2
#./test
#popd
banner "Test extensions"
pushd tests/extensions
#banner "Test extensions"
#pushd tests/extensions
#./test
#popd
banner "HPack"
pushd tests/hpack
./test
popd

17
snack-lib/YamlToJson.hs Normal file
View File

@ -0,0 +1,17 @@
module Main where
import qualified Data.Aeson as Aeson
import qualified Data.Yaml as Yaml
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BL8
import qualified Data.ByteString.Char8 as BS8
import qualified Data.Text.Encoding as T
import System.Environment (getArgs)
main :: IO ()
main = do
[file] <- getArgs
yaml <- BS8.readFile file
let Just value = Yaml.decode yaml :: Maybe Aeson.Value
BL8.putStrLn $ Aeson.encode value

View File

@ -18,6 +18,7 @@ with (callPackage ./files.nix {});
with (callPackage ./modules.nix { inherit singleOut; });
with (callPackage ./module-spec.nix { inherit singleOut; });
with (callPackage ./package-spec.nix { inherit singleOut; });
with (callPackage ./hpack.nix { inherit singleOut; });
with (callPackage ./lib.nix {});
let
@ -312,9 +313,17 @@ let
json = writeText "ghci_output" (builtins.toJSON json);
};
};
packageYaml = pyam:
let
project = snackNixFromHPack pyam;
in
{ library = executable project.library;
executables = lib.attrsets.mapAttrs (_: v: executable v) project.executables;
};
in
{
inherit
executable
packageYaml
;
}

73
snack-lib/hpack.nix Normal file
View File

@ -0,0 +1,73 @@
{ lib, glibcLocales, callPackage, singleOut, writeText, runCommand, haskellPackages }:
with (callPackage ./modules.nix { inherit singleOut; });
let
y2j = runCommand "yaml2json"
{ buildInputs =
[ (haskellPackages.ghcWithPackages (ps: [ ps.aeson ps.yaml ])) glibcLocales ];
}
"ghc ${./YamlToJson.hs} -o $out";
fromYAML = text:
let json =
builtins.readFile (runCommand "y2j"
{ buildInputs = [ glibcLocales ]; }
"${y2j} ${writeText "y2j" text} > $out"
);
in builtins.fromJSON json;
in
{
snackNixFromHPack = packageYaml:
let
package = fromYAML (builtins.readFile packageYaml);
topDeps =
# this drops the version bounds
map (x: lib.lists.head (lib.strings.splitString " " x))
package.dependencies;
extensions = package.default-extensions;
packageLib =
let component = package.library;
in
{ src =
let base = builtins.dirOf packageYaml;
in builtins.toPath "${builtins.toString base}/${component.source-dirs}";
dependencies = topDeps ++
(if builtins.hasAttr "dependencies" component
then component.dependencies
else []);
inherit extensions;
};
exes =
if builtins.hasAttr "executables" package
then lib.mapAttrs (k: v: mkExe v) package.executables
else {};
mkExe = component:
let
depOrPack =
lib.lists.partition
(x: x == package.name)
(if builtins.hasAttr "dependencies" component
then component.dependencies
else []);
packages = map (_: packageLib) depOrPack.right;
dependencies = topDeps ++ depOrPack.wrong;
in
{ main = fileToModule component.main;
src =
let
base = builtins.dirOf packageYaml;
in builtins.toPath "${builtins.toString base}/${component.source-dirs}";
inherit packages dependencies extensions;
};
in
{ library = packageLib;
executables = exes;
};
}

13
tests/hpack/test Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
# vim: ft=sh sw=2 et
set -euo pipefail
TMP_DIR=$(mktemp -d)
git clone http://github.com/2mol/pboy.git $TMP_DIR
git -C $TMP_DIR reset --hard a2458d6984930a33a3b1972cb6d5c167d2511b06
snack build --package-yaml $TMP_DIR/package.yaml
rm -rf $TMP_DIR