packages feed

stackage2nix-0.6.1: src/Distribution/Nixpkgs/Haskell/Stack/PrettyPrinting.hs

{-# LANGUAGE Rank2Types #-}

module Distribution.Nixpkgs.Haskell.Stack.PrettyPrinting where

import           Control.Lens
import           Data.Foldable as F
import           Data.List as L
import           Data.List.NonEmpty as NE
import           Data.Maybe
import           Data.String
import           Distribution.Nixpkgs.Haskell.Derivation
import           Distribution.Nixpkgs.Haskell.Packages.PrettyPrinting as PP
import           Distribution.Package
import           Distribution.Text
import           Distribution.Version (Version)
import qualified Language.Nix.FilePath as Nix
import           Language.Nix.PrettyPrinting as PP
import           Stack.Config (StackResolver, unStackResolver)


data OverrideConfig = OverrideConfig
  { _ocGhc              :: Version
  , _ocStackagePackages :: FilePath
  , _ocStackageConfig   :: FilePath
  , _ocNixpkgs          :: FilePath
  }

makeLenses ''OverrideConfig

systemNixpkgs :: Doc
systemNixpkgs = "<nixpkgs>"

renderNixpkgs :: FilePath -> Doc
renderNixpkgs nixpkgsPath
  | fromString nixpkgsPath == systemNixpkgs = systemNixpkgs
  | otherwise = disp $ (fromString nixpkgsPath :: Nix.FilePath)

hasField :: Lens' a (Maybe b) -> a -> Bool
hasField p = views p isJust

overridePackages :: (Foldable t, Functor t) => t Derivation -> Doc
overridePackages = PP.packageSetConfig . PP.cat . F.toList . fmap callPackage
  where
    drvNameQuoted   = PP.doubleQuotes . disp . pkgName . view pkgid
    callPackage drv = hang
      (drvNameQuoted drv <> " = callPackage") 2
      (PP.parens (PP.pPrint drv) <+> "{};")

importStackagePackages :: FilePath -> Doc
importStackagePackages path = hsep
  ["import", disp (fromString path :: Nix.FilePath)]

importStackageConfig :: FilePath -> Doc
importStackageConfig path = hsep
  ["import ", disp (fromString path :: Nix.FilePath), "{ inherit pkgs haskellLib; }"]

overrideHaskellPackages :: OverrideConfig -> NonEmpty Derivation -> Doc
overrideHaskellPackages oc packages = vcat
  [ funargs
    [ "nixpkgs ? import " <> renderNixpkgs (oc ^. ocNixpkgs) <> " {}"
    ]
  , ""
  , "with nixpkgs;"
  , "let"
  , nest 2 "inherit (stdenv.lib) extends;"
  , nest 2 $ vcat
    [ attr "haskellLib" "callPackage (nixpkgs.path + \"/pkgs/development/haskell-modules/lib.nix\") {}"
    , attr "stackagePackages" . importStackagePackages $ oc ^. ocStackagePackages
    , attr "stackageConfig" . importStackageConfig $ oc ^. ocStackageConfig ]
  , nest 2 $ vcat
    [ "stackPackages ="
    , nest 2 $ overridePackages packages <> semi
    , ""
    , "pkgOverrides = self: stackPackages {"
    , nest 2 "inherit pkgs stdenv;"
    , nest 2 "inherit (self) callPackage;"
    , "};"
    , ""
    ]
  , "in callPackage (nixpkgs.path + \"/pkgs/development/haskell-modules\") {"
  , nest 2 $ vcat
    [ attr "ghc" ("pkgs.haskell.compiler." <> toNixGhcVersion (oc ^. ocGhc))
    , attr "compilerConfig" "self: extends pkgOverrides (stackageConfig self)"
    , attr "initialPackages" "stackagePackages"
    , attr "configurationCommon" "args: self: super: {}"
    , "inherit haskellLib;"
    ]
  , "}"
  ]

overrideStackage :: StackResolver -> FilePath -> NonEmpty Derivation -> Doc
overrideStackage stackResolver nixpkgsPath packages = vcat
  [ funargs
    [ "nixpkgs ? import " <> renderNixpkgs nixpkgsPath <> " {}"
    ]
  , ""
  , "let"
  , nest 2 $ vcat
    [ "stackPackages ="
    , nest 2 $ overridePackages packages <> semi
    ]
  , "in nixpkgs.haskell.packages.stackage." <> toNixStackResolver stackResolver <> ".override {"
  , nest 2
    $ attr "packageSetConfig" "self: super: stackPackages { inherit (nixpkgs) pkgs stdenv; inherit (self) callPackage; } super"
  , "}"
  ]

pPrintHaskellPackages :: OverrideConfig -> Doc
pPrintHaskellPackages oc = vcat
  [ funargs
    [ "callPackage"
    , "buildPackages"
    , "pkgs"
    , "stdenv"
    , "lib"
    , "overrides ? (self: super: {})"
    , "packageSetConfig ? (self: super: {})"
    ]
  , ""
  , "let"
  , nest 2 $ vcat
    [ "inherit (lib) extends makeExtensible;"
    , attr "haskellLib" "pkgs.haskell.lib"
    , "inherit (haskellLib) makePackageSet;"
    , ""
    , attr "haskellPackages" $ vcat
      [ "pkgs.callPackage makePackageSet {"
      , nest 2 $ vcat
        [ attr "ghc" ("buildPackages.haskell.compiler." <> toNixGhcVersion (oc ^. ocGhc))
        , attr "buildHaskellPackages" ("buildPackages.haskell.packages." <> toNixGhcVersion (oc ^. ocGhc))
        , attr "package-set" . importStackagePackages $ oc ^. ocStackagePackages
        , "inherit stdenv haskellLib extensible-self;"
        ]
      , "}"
      ]
    , ""
    , attr "compilerConfig" . importStackageConfig $ oc ^. ocStackageConfig
    , ""
    , attr "configurationCommon" "if builtins.pathExists ./configuration-common.nix then import ./configuration-common.nix { inherit pkgs haskellLib; } else self: super: {}"
    , attr "configurationNix" "import <nixpkgs/pkgs/development/haskell-modules/configuration-nix.nix> { inherit pkgs haskellLib; }"
    , ""
    , attr "extensible-self" "makeExtensible (extends overrides (extends configurationCommon (extends packageSetConfig (extends compilerConfig (extends configurationNix haskellPackages)))))"
    ]
  , ""
  , "in extensible-self"
  ]

toNixGhcVersion :: Version -> Doc
toNixGhcVersion = (<>) "ghc" . toNixVersion . show . disp

toNixStackResolver :: StackResolver -> Doc
toNixStackResolver = toNixVersion . unStackResolver

toNixVersion :: String -> Doc
toNixVersion = text . L.filter (/= '.')