input-output-hk / stack2nix

Generate nix expressions for Haskell projects
MIT License
98 stars 32 forks source link

Allow changing nixpkgs to update Haskell package versions #113

Open hamishmack opened 5 years ago

hamishmack commented 5 years ago

Stack2nix is cool if you have to maintain a stack.yaml and want a reliable way to turn it into a nix file. However it does not work well as a migration tool yet.

For instance here is the haskell-ide-engine stack.yaml. Stack2nix outputs a 1.59MB nix file that pins all the haskell packages. This means you can't change nixpkgs to get different haskell packages versions (you will always get the ones stack would have chosen). This locks you into maintaining a stack.yaml file and running stack2nix (instead of bumping nixpkgs to a newer commit).

I think if stack2nix took a nixpkgs ref as an input it could get much closer to the hand crafted translation. It could compare the nixpkgs derivations to the ones it would have output and only output the differences.

Here is an example of a by hand translation and I think something like stack2nix --migrate --nixpkgs '<nixpkgs>' should be able to get pretty close to this:

  with import <nixpkgs> { };

  let
    rootName = name: builtins.elemAt (lib.splitString "/" name) 0;
    isValidFile = name: files: builtins.elem (rootName name) files;
    relative = path: name: lib.removePrefix (toString path + "/") name;
    onlyFiles = files: path: builtins.filterSource (name: type: isValidFile (relative path name) files) path;

    ghc = (pkgs.haskell.packages.ghc843.extend (pkgs.haskell.lib.packageSourceOverrides {
      haskell-ide-engine = onlyFiles ["LICENSE" "Setup.hs" "app" "haskell-ide-engine.cabal" "src" "test"] ./.;
      hie-plugin-api     = ./hie-plugin-api;
      HaRe               = ./submodules/HaRe;
      cabal-helper       = ./submodules/cabal-helper;
      ghc-mod            = ./submodules/ghc-mod;
      ghc-mod-core       = ./submodules/ghc-mod/core;
      haskell-lsp        = ./submodules/haskell-lsp;
      haskell-lsp-types  = ./submodules/haskell-lsp/haskell-lsp-types;
      haskell-lsp-test   = ./submodules/haskell-lsp-test;
      leksah-server      = ../leksah/vendor/leksah-server;
      base-compat        = "0.9.3";
      cabal-plan         = "0.3.0.0";
      haddock-api        = "2.20.0";
      haddock-library    = "1.6.0";
    })).extend(self: super: {
      hlint               = haskell.lib.dontCheck super.hlint;
      constrained-dynamic = haskell.lib.doJailbreak super.constrained-dynamic;
      haddock-api         = haskell.lib.dontCheck super.haddock-api;
      haddock-library     = haskell.lib.dontCheck (haskell.lib.dontHaddock super.haddock-library);
      ghc-exactprint      = haskell.lib.dontCheck super.ghc-exactprint;
      leksah-server       = haskell.lib.dontCheck super.leksah-server;
    });
  in {
    inherit ghc;

    shells = {
      ghc = ghc.shellFor {
        packages = p: [
          p.haskell-ide-engine
          p.hie-plugin-api
          p.HaRe
          p.cabal-helper
          p.ghc-mod
          p.ghc-mod-core
          p.haskell-lsp
          p.haskell-lsp-types
          p.haskell-lsp-test
          ];
      };
    };
  }

If ./ is included as package, the list of files passed to onlyFiles should be added to prevent non package files from triggering builds (I made that list by running cabal sdist and looking at the top level items in the .tar.gz file it produced).

domenkozar commented 5 years ago

The main issue with mixing cabal2nix and stack is that they have different build plans. For example, cabal2nix always includes tests and benchmarks dependencies, while stack does only for packages in locations section. There are many subtle differences how they use hackage as an api and findings show mixing the two is painful to get right. I'd actually like to use cabal2nix just as low-level library in #111 and stack do the whole build plan as it sees it. This would get rid of many issues that people face with stack2nix these days.

Of course, the above is doable if one wants to live with addressing those issues manually.

hamishmack commented 5 years ago

For example, cabal2nix always includes tests and benchmarks dependencies, while stack does only for packages in locations section.

Could a custom version of pkgs.haskell.lib.packageSourceOverrides disable tests and benchmarks for everything except the except the packages it is overriding with callCabal2nix?