1
1
mirror of https://github.com/nmattia/niv.git synced 2024-11-09 23:04:38 +03:00
niv/default.nix
Nicolas Mattia 461905a70c Only build static executable in top-level niv attribute
This reduces the closure size by about 2G.
2020-01-11 20:01:18 +01:00

287 lines
8.4 KiB
Nix

{ sources ? import ./nix/sources.nix
, pkgs ? import ./nix { inherit sources; }
}:
with rec
{
files = pkgs.callPackage ./nix/files.nix {};
sourceByRegex = name: src: regexes:
builtins.path {
filter = (
path: type:
let
relPath = pkgs.lib.removePrefix (toString src + "/") (toString path);
accept = pkgs.lib.any (re: builtins.match re relPath != null) regexes;
in
accept
);
inherit name;
path = src;
};
niv-source = sourceByRegex "niv" ./. [
"^package.yaml$"
"^README.md$"
"^LICENSE$"
"^app$"
"^app.*.hs$"
"^src$"
"^src/Data$"
"^src/Data/Aeson$"
"^src/Data/HashMap$"
"^src/Data/HashMap/Strict$"
"^src/Data/Text$"
"^src/Niv$"
"^src/Niv/Git$"
"^src/Niv/GitHub$"
"^src/Niv/Sources$"
"^src/Niv/Update$"
"^src.*.hs$"
"^README.md$"
"^nix$"
"^nix.sources.nix$"
];
haskellPackages = pkgs.haskellPackages.override {
overrides = _: haskellPackages: {
niv =
pkgs.haskell.lib.justStaticExecutables (
pkgs.haskell.lib.failOnAllWarnings (
pkgs.haskell.lib.disableExecutableProfiling (
pkgs.haskell.lib.disableLibraryProfiling (
pkgs.haskell.lib.generateOptparseApplicativeCompletion "niv" (
haskellPackages.callCabal2nix "niv" niv-source {}
)
)
)
)
);
};
};
niv = haskellPackages.niv;
niv-sdist = pkgs.haskell.lib.sdistTarball niv;
niv-cabal-upload =
let
niv-version = niv.version;
in
pkgs.writeScript "cabal-upload"
''
#!${pkgs.stdenv.shell}
cabal upload "$@" "${niv-sdist}/niv-${niv-version}.tar.gz"
'';
# WARNING: extremely disgusting hack below.
#
# <rant>
# I was trying to fix this issue: https://github.com/nmattia/niv/issues/109
# Basically, --help should also show niv's version. As usual when trying to
# lookup the documentation for `Paths_` I google "cabal Paths_ module", end
# up here:
# https://stackoverflow.com/questions/21588500/haskell-cabal-package-cant-find-paths-module,
# click this link:
# https://downloads.haskell.org/~ghc/7.0.3/docs/html/Cabal/authors.html#paths-module,
# and then try to find my way to the latest doc.
#
# (╯°□°)╯︵ ┻━┻
#
# But now that we're using cabal Paths_, the `ghci` `repl` function won't
# work! So I wonder: stack or cabal? Well, stack, it's been a while! But
# _of course_ there's no way to easily tell stack to use the system GHC!
# Even if there's no `stack.yaml`, stack will infer "some" LTS and
# `--system-ghc` will be discarded because the GHC versions don't match!!
#
# (╯°□°)╯︵ ┻━┻
#
# So I give good old `cabal repl` a try and, (not much of a) surprise, it
# complains that it can't start because niv contains more than one cabal
# component.
#
# (╯°□°)╯︵ ┻━┻
#
# WTF, there's now repl, new-repl, v1-repl, v2-repl. WHAT?
#
# (˚Õ˚)ر ~~~~╚╩╩╝
#
# So I try `cabal new-repl`, and it doesn't load _any_ main function
# (without having to actually write `main = Niv.Test.test` or doing weird
# stuff to load a `Main` module).
#
# (╯°□°)╯︵ ┻━┻
#
# And there's no `cabal new-repl -main-is ...`
#
# (╯°□°)╯︵ ┻━┻
#
# The workaround here:
# https://github.com/haskell/cabal/issues/5374#issuecomment-410431619
# suggests using -ghci-script=foo where foo specifies `main = bar`, but, as
# pointed out, the ghci script runs too early (and that's after having
# tried --ghci-options which doesn't work; must use --repl-options).
#
# (╯°□°)╯︵ ┻━┻ (˚Õ˚)ر ~~~~╚╩╩╝
#
# But there's hope! The `cabal new-repl` doc says you can load a
# "component". So I try with `cabal new-repl niv-test`. FIRST it builds
# everything (actually generates `.o`s and stuff) and then loads the
# _wrong_ module. At this point I can't help thinking that a monkey
# (bonobo, baboon, pick any) will still manage to pick the correct main 50%
# of the time. I don't want to jump to conclusion: I only gave cabal one
# chance, so I'm not sure how exactly it scores on the "pick the correct
# main" game.
#
# (゜-゜)
#
# Well,
# => rm -rf ~/.stack
# => rm -rf ~/.cabal
# => rm -rf dist
# => rm -rf dist-newstyle
#
# </rant>
#
# In order to make `Paths_niv(version)` available in `ghci`, we parse the
# version from `package.yaml` and create a dummy module that we inject in the
# `ghci` command.
niv-devshell = haskellPackages.shellFor {
buildInputs = [ pkgs.nixpkgs-fmt ];
packages = ps: [ ps.niv ];
shellHook = ''
repl_for() {
haskell_version=$(cat ./package.yaml \
| grep -oP 'version: \K\d+.\d+.\d+' \
| sed 's/\./,/g' )
paths_niv=$(mktemp -d)/Paths_niv.hs
echo "module Paths_niv where" >> $paths_niv
echo "import qualified Data.Version" >> $paths_niv
echo "version :: Data.Version.Version" >> $paths_niv
echo "version = Data.Version.Version [$haskell_version] []" >> $paths_niv
niv_main=""
shopt -s globstar
ghci -clear-package-db -global-package-db -Wall app/$1.hs src/**/*.hs $paths_niv
}
repl() {
repl_for NivTest
}
repl_niv() {
repl_for Niv
}
echo "To start a REPL for the test suite, run:"
echo " > repl"
echo " > :main"
echo " (tests run)"
echo
echo "To start a REPL session emulating the niv executable, run:"
echo " > repl_niv"
echo " > :main --help ..."
echo " NIV - Version manager for Nix projects"
echo " ..."
'';
};
};
rec
{
inherit niv niv-sdist niv-source niv-devshell niv-cabal-upload;
tests-github = pkgs.callPackage ./tests/github { inherit niv; };
tests-git = pkgs.callPackage ./tests/git { inherit niv; };
niv-test = pkgs.runCommand "niv-test" { buildInputs = [ niv ]; }
"niv-test && touch $out";
readme = pkgs.writeText "README.md" (
let
template = builtins.readFile ./README.tpl.md;
niv_help = builtins.readFile (
pkgs.runCommand "niv_help" { buildInputs = [ niv ]; }
"niv --help > $out"
);
niv_cmd_help = cmd: builtins.readFile (
pkgs.runCommand "niv_${cmd}_help" { buildInputs = [ niv ]; }
"niv ${cmd} --help > $out"
);
cmds = [ "add" "update" "drop" "init" "show" ];
in
pkgs.lib.replaceStrings
([ "replace_niv_help" ] ++ (map (cmd: "replace_niv_${cmd}_help") cmds))
([ niv_help ] ++ (map niv_cmd_help cmds))
template
);
readme-test = pkgs.runCommand "README-test" {}
''
err() {
echo
echo -e "\e[31mERR\e[0m: README.md out of date"
echo -e "please run \e[1m./script/gen\e[0m"
echo
exit 1
}
diff ${./README.md} ${readme} && echo dummy > $out || err ;
'';
niv-svg-test = pkgs.runCommand "niv-svg-test" {}
''
# XXX: This test means that the svg needs to be regenerated
# by hand on (virtually) every commit.
# TODO: figure out a nicer way
touch $out
exit 0
err() {
echo
echo -e "\e[31mERR\e[0m: niv.svg out of date"
echo -e "please run \e[1m./script/gen\e[0m"
echo
exit 1
}
expected_hash=$(${pkgs.nix}/bin/nix-hash ${niv-svg-gen})
actual_hash=$(grep -oP 'id="\K[^"]+' ${./site/niv.svg} -m 1)
echo "expected $expected_hash"
echo "actual $actual_hash"
[ $expected_hash == $actual_hash ] && echo dymmy > $out || err
'';
# TODO: use nivForTest for this one
niv-svg-cmds = pkgs.writeScript "niv-svg-cmds"
''
#!${pkgs.stdenv.shell}
set -euo pipefail
echo '$ niv init'
niv init
echo
echo '$ niv add stedolan/jq'
niv add stedolan/jq
'';
niv-svg-gen = pkgs.writeScript "niv-svg-gen"
''
#!${pkgs.stdenv.shell}
set -euo pipefail
export PATH=${haskellPackages.niv}/bin:${pkgs.nix}/bin:$PATH
site=$PWD/site
pushd $(mktemp -d)
${pkgs.termtosvg}/bin/termtosvg \
-g 82x26 -M 2000 -m 2000 -t gjm8 \
-c '${niv-svg-cmds}' $site/niv.svg
echo done rendering
popd
'';
}