{ nixpkgs }: with nixpkgs; let # provided by .envrc root = builtins.getEnv "BIZ_ROOT"; # general functions to put in a lib lines = s: lib.strings.splitString "\n" s; removeNull = ls: builtins.filter (x: x != null) ls; depsToPackageSet = packageSet: deps: lib.attrsets.attrVals deps packageSet; # returns true if a is a subset of b, where a and b are attrsets subset = a: b: builtins.all (x: builtins.elem x b) a; allDeps = import ./haskell-deps.nix; # gather data needed for compiling by analyzing the main module analyze = main: rec { # path to the module relative to the git root relpath = builtins.replaceStrings ["${root}/"] [""] (builtins.toString main); # Haskell-appropriate name of the module module = builtins.replaceStrings ["/" ".hs"] ["." ""] relpath; # file contents content = builtins.readFile main; # search for the ': exe' declaration exe = builtins.head (lib.lists.flatten (removeNull (map (builtins.match "^-- : exe ([[:alnum:]._-]*)$") (lines content)))); # collect all of the ': dep' declarations deps = lib.lists.flatten (removeNull (map (builtins.match "^-- : dep ([[:alnum:]._-]*)$") (lines content))); }; mkGhc = compiler: (deps: compiler (hp: if (subset deps allDeps) then depsToPackageSet hp deps else throw '' missing from nix/haskell-deps.nix: ${toString (lib.lists.subtractLists allDeps deps)} '')); ghc_ = mkGhc pkgs.haskell.packages.ghc865.ghcWithHoogle; ghcjs_ = mkGhc pkgs.haskell.packages.ghcjs.ghcWithPackages; in { ghc = main: let data = analyze main; ghc = ghc_ data.deps; in stdenv.mkDerivation { name = data.module; src = ../.; nativeBuildInputs = [ ghc ]; strictDeps = true; buildPhase = '' mkdir -p $out/bin # compile with ghc ${ghc}/bin/ghc -Werror -i. \ --make ${main} \ -main-is ${data.module} \ -o $out/bin/${data.exe} ''; # the install process was handled above installPhase = "exit 0"; } // { env = ghc; }; ghcjs = main: let data = analyze main; ghcjs = ghcjs_ data.deps; in stdenv.mkDerivation { name = data.module; src = ../.; nativeBuildInputs = [ ghcjs ]; strictDeps = true; buildPhase = '' mkdir -p $out/static # compile with ghcjs ${ghcjs}/bin/ghcjs -Werror -i. \ --make ${main} \ -main-is ${data.module} \ -o ${data.exe} # optimize js output ${pkgs.closurecompiler}/bin/closure-compiler \ ${data.exe}/all.js > $out/static/${data.exe} ''; installPhase = "exit 0"; } // { env = ghcjs; }; env = mkShell { name = "bizdev"; buildInputs = [ (ghc_ allDeps) # this says something about missing haskelline? #(ghcjs_ allDeps) nixpkgs.niv.niv nixpkgs.hlint nixpkgs.ormolu nixpkgs.python37Packages.black nixpkgs.python37Packages.pylint nixpkgs.wemux ]; EXAMPLE = "hi"; shellHook = '' function help() { echo "" echo "bizdev" | ${nixpkgs.figlet}/bin/figlet | ${nixpkgs.lolcat}/bin/lolcat echo "" echo " bild compile code" echo " deps manage dependencies with niv" echo " ghci start ghci with correct options" echo " help show this message" echo " hero compile and start a dev server for herocomics.app" echo " lint auto-lint all changed files" echo " ./push TODO: convert to haskell" echo " ./ship TODO: convert to haskell" } function bild() { runghc Biz.Bild $@ } function deps() { niv --sources-file $BIZ_ROOT/nix/sources.json $@ } function ghci() { ghci -i$BIZ_ROOT -ghci-script "$BIZ_ROOT/.ghci" } function hero() { out="_bild/nix" export HERO_PORT=3000 export HERO_NODE=$BIZ_ROOT/$out/Hero.Node/static export HERO_KEEP=$BIZ_ROOT/_keep export HERO_SKEY=$BIZ_ROOT/_skey b="runghc Biz.Bild" rg --files \ | entr -rcs \ "$b Hero.Host && $b Hero.Node && $out/Hero.Host/bin/mmc" } function lint() { alias lint=$BIZ_ROOT/Biz/lint.py } help ''; }; os = cfg: (nixos (args: lib.attrsets.recursiveUpdate cfg { boot.cleanTmpDir = true; networking.firewall.allowPing = true; nix.binaryCaches = [ "https://cache.nixos.org" ]; nix.gc.automatic = true; nix.gc.dates = "Sunday 02:15"; nix.optimise.automatic = true; nix.optimise.dates = [ "Sunday 02:30" ]; nixpkgs.overlays = overlays; programs.mosh.enable = true; programs.mosh.withUtempter = true; security.acme.email = "ben@bsima.me"; security.acme.acceptTerms = true; security.sudo.wheelNeedsPassword = false; services.clamav.daemon.enable = true; # security services.clamav.updater.enable = true; # security services.fail2ban.enable = true; # security services.openssh.enable = true; services.openssh.openFirewall = true; services.openssh.forwardX11 = true; services.openssh.passwordAuthentication = false; system.autoUpgrade.enable = false; # 'true' breaks our nixpkgs pin })).toplevel; }