packages feed

stackage2nix (empty) → 0.3.0

raw patch · 24 files changed

+1492/−0 lines, 24 filesdep +Cabaldep +QuickCheckdep +aesonsetup-changed

Dependencies added: Cabal, QuickCheck, aeson, base, bytestring, cabal2nix, containers, deepseq, distribution-nixpkgs, exceptions, filepath, gitlib, gitlib-libgit2, hopenssl, hspec, inflections, language-nix, lens, network-uri, optparse-applicative, pretty, shakespeare, stackage-curator, stackage2nix, text, unordered-containers, yaml

Files

+ CHANGELOG.md view
@@ -0,0 +1,4 @@+# 0.3.0++- Initial Hackage release+- Add CHANGELOG.md
+ LICENSE view
@@ -0,0 +1,26 @@+Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,66 @@+# stackage2nix++[![Build Status](https://travis-ci.org/typeable/stackage2nix.svg?branch=master)](https://travis-ci.org/typeable/stackage2nix)++`stackage2nix` converts a Stack file into a Nix Haskell packages set.+It creates LTS Stackage packages set, and applies appropriate overrides on top of it.++```+stackage2nix \+  --lts-haskell "$LTS_HASKELL_REPO" \+  --all-cabal-hashes "$ALL_CABAL_HASHES_REPO" \+  .+```++`stackage2nix` has three required arguments:+- `--lts-haskell` - path to [fpco/lts-haskell](https://github.com/fpco/lts-haskell)+- `--all-cabal-hashes` - path to [commercialhaskell/all-cabal-hashes](https://github.com/commercialhaskell/all-cabal-hashes) checked out to `hackage` branch+- `.` - path to stack.yaml file or directory++Produced Nix derivation split on following files:+- packages.nix - Base Stackage packages set+- configuration-packages.nix - Compiler configuration+- default.nix - Final Haskell packages set with all overrides applied++A particular package from result Haskell packages set can be built with:++```+nix-build -A <package-name>+```++## Runtime dependencies++- `nix-env` is required to be on PATH by the+  [distribution-nixpkgs](https://hackage.haskell.org/package/distribution-nixpkgs)+  dependency+- `nix-prefetch-git` is required on PATH if you have git dependencies in+  `stack.yaml`++## Override result derivation++Complex projects may require some extra customization.+Snippet `override.nix` below shows a minimal example of how to apply additional+overrides on top of Haskell packages set produced by `stackage2nix`.++```+with import <nixpkgs> {};+with pkgs.haskell.lib;+let haskellPackages = import ./. {};+in haskellPackages.override {+  overrides = self: super: {+    stackage2nix = disableSharedExecutables super.stackage2nix;+  };+}+```++```+nix-build -A stackage2nix override.nix+```++For more complex overrides and detailed information on how to work with Haskell packages in Nix, see Nixpkgs manual [User’s Guide to the Haskell Infrastructure](http://nixos.org/nixpkgs/manual/#users-guide-to-the-haskell-infrastructure)+++## Examples++For other examples of `stackage2nix` usage, see [4e6/stackage2nix-examples](https://github.com/4e6/stackage2nix-examples) repository.+It verifies `stackage2nix` by running it on different public projects.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/AllCabalHashes.hs view
@@ -0,0 +1,82 @@+module AllCabalHashes where++import Control.Lens hiding ((<.>))+import Data.Aeson as A+import Data.ByteString as BS+import Data.ByteString.Lazy as BSL+import Data.Map as M+import Data.Text as T+import Data.Text.Encoding as T+import Distribution.Nixpkgs.Hashes as NH+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Parse+import Distribution.Text ( display )+import Git+import Git.Libgit2 as Libgit2+import OpenSSL.Digest as SSL ( digest, digestByName )+import System.FilePath+++type SHA1Hash = T.Text+type SHA256Hash = String++data Meta = Meta+  { _mHashes    :: !(Map String String)+  , _mLocations :: ![String]+  , _mPkgsize   :: !Int+  } deriving (Show)++makeLenses ''Meta++instance FromJSON Meta where+  parseJSON (Object v) = Meta+    <$> v .: "package-hashes"+    <*> v .: "package-locations"+    <*> v .: "package-size"+  parseJSON o          = fail ("invalid Cabal metadata: " ++ show o)++readPackageByHash :: FilePath -> SHA1Hash -> IO (GenericPackageDescription, SHA256Hash)+readPackageByHash repoDir sha1Hash = do+  let repoOpts = defaultRepositoryOptions { repoPath = repoDir }+  repo <- openLgRepository repoOpts+  buf <- runLgRepository repo $ do+    BlobObj (Blob _ contents) <- lookupObject =<< parseOid sha1Hash+    case contents of+      BlobString bs -> return bs+      BlobStringLazy bsl -> return $ BSL.toStrict bsl+      _ -> fail $ "Git SHA1 " ++ show sha1Hash ++ ": expected single Blob"+  cabal <- case parseGenericPackageDescription (T.unpack $ T.decodeUtf8 buf) of+    ParseOk _ a     -> return a+    ParseFailed err -> fail ("Git SHA1 " ++ show sha1Hash ++ ": " ++ show err)+  let+    hash = printSHA256 (digest (digestByName "sha256") buf)+    pkg  = setCabalFileHash hash cabal+  return (pkg, hash)++readPackageByName :: FilePath -> PackageIdentifier -> IO (GenericPackageDescription, SHA256Hash)+readPackageByName repoDir (PackageIdentifier name version) = do+  let cabalFile = repoDir </> unPackageName name </> display version </> unPackageName name <.> "cabal"+  buf <- BS.readFile cabalFile+  cabal <- case parseGenericPackageDescription (T.unpack $ T.decodeUtf8 buf) of+             ParseOk _ a  -> return a+             ParseFailed err -> fail (cabalFile ++ ": " ++ show err)+  let+    hash = NH.printSHA256 (SSL.digest (SSL.digestByName "sha256") buf)+    pkg  = setCabalFileHash hash cabal+  return (pkg, hash)++setCabalFileHash :: SHA256Hash -> GenericPackageDescription -> GenericPackageDescription+setCabalFileHash hash cabal = cabal+  { packageDescription = (packageDescription cabal)+    { customFieldsPD = ("X-Cabal-File-Hash", hash) : customFieldsPD (packageDescription cabal)+    }+  }++readPackageMeta :: FilePath -> PackageIdentifier -> IO Meta+readPackageMeta dirPrefix (PackageIdentifier name version) = do+  let metaFile = dirPrefix </> unPackageName name </> display version </> unPackageName name <.> "json"+  buf <- BS.readFile metaFile+  case eitherDecodeStrict buf of+    Left msg -> fail (metaFile ++ ": " ++ msg)+    Right x  -> return $ over (mHashes . ix "SHA256") (printSHA256 . packHex) x
+ src/Distribution/Nixpkgs/Haskell/FromStack.hs view
@@ -0,0 +1,93 @@+module Distribution.Nixpkgs.Haskell.FromStack where++import AllCabalHashes+import Control.Lens+import Data.Set.Lens+import Distribution.Compiler (CompilerInfo(..))+import Distribution.System (Platform(..))+import Distribution.Package (PackageName, PackageIdentifier(..), Dependency(..))+import Distribution.PackageDescription+import Distribution.Nixpkgs.Haskell.FromStack.Package+import Distribution.Nixpkgs.Haskell.PackageSourceSpec+import Distribution.Nixpkgs.Haskell.FromCabal+import Distribution.Nixpkgs.Haskell.Derivation+import Stackage.BuildPlan+import Stackage.Types (CabalFileInfo(..),PackageConstraints(..), DepInfo(..), SimpleDesc(..), TestState(..))+import qualified Distribution.Nixpkgs.Meta as Nix++import qualified Data.Map as Map+import qualified Data.Set as Set++data PackageSetConfig = PackageSetConfig+  { haskellResolver :: HaskellResolver+  , nixpkgsResolver :: NixpkgsResolver+  , packageLoader   :: Maybe SHA1Hash -> PackageIdentifier -> IO Package+  , targetPlatform  :: Platform+  , targetCompiler  :: CompilerInfo }++data PackageConfig = PackageConfig+  { enableCheck   :: Bool+  , enableHaddock :: Bool }++removeTests :: GenericPackageDescription -> GenericPackageDescription+removeTests gd = gd { condTestSuites = [] }++planDependencies :: PackagePlan -> [Dependency]+planDependencies = map makeDependency . Map.toList . sdPackages . ppDesc+ where+  makeDependency (name, depInfo) = Dependency name (diRange depInfo)++buildNodeM :: PackageSetConfig -> PackageConfig -> PackageName -> PackagePlan -> IO Node+buildNodeM conf pconf name plan = do+  let+    cabalHashes = maybe mempty cfiHashes $ ppCabalFileInfo plan+    mGitSha1 = Map.lookup "GitSHA1" cabalHashes+  pkg <- packageLoader conf mGitSha1 $ PackageIdentifier name (ppVersion plan)+  pure . mkNode $ fromPackage conf pconf plan pkg++fromPackage :: PackageSetConfig -> PackageConfig -> PackagePlan -> Package -> Derivation+fromPackage conf pconf plan pkg =+  let+    constraints = ppConstraints plan+    testsEnabled =+      enableCheck pconf &&+      pcTests constraints == ExpectSuccess+    haddocksEnabled =+      enableHaddock pconf &&+      pcHaddocks constraints == ExpectSuccess &&+      not (Set.null (sdModules (ppDesc plan)))+    configureTests+      | pcTests constraints == Don'tBuild = removeTests+      | otherwise = id+    flags = Map.toList (pcFlagOverrides constraints)+    (descr, missingDeps) = finalizeGenericPackageDescription+      (haskellResolver conf)+      (targetPlatform conf)+      (targetCompiler conf)+      flags+      (planDependencies plan)+      (configureTests (pkgCabal pkg))+    genericDrv = fromPackageDescription+      (haskellResolver conf)+      (nixpkgsResolver conf)+      missingDeps+      flags+      descr+    depName (Dependency name _) = name+    testDeps = setOf+      ( to testSuites . folded+        . to testBuildInfo . to targetBuildDepends+        . folded . to depName )+    hasIntersection a b = not . null $ Set.intersection a b+    brokenEnabled =+      testsEnabled &&+      not (null missingDeps) &&+      setOf (folded . to depName) missingDeps `hasIntersection` testDeps descr+  in+    genericDrv+      & src .~ pkgSource pkg+      & doCheck .~ testsEnabled+      & runHaddock .~ haddocksEnabled+      & metaSection . Nix.broken .~ brokenEnabled+      -- TODO: remove after Nixos/Nixpkgs #27196 released+      & enableSeparateDataOutput .~ False
+ src/Distribution/Nixpkgs/Haskell/FromStack/Package.hs view
@@ -0,0 +1,131 @@+module Distribution.Nixpkgs.Haskell.FromStack.Package where++import Control.Lens+import Data.Graph (Graph, Vertex)+import Data.Foldable as F+import Data.Maybe+import Data.Monoid+import Data.Ord as O+import Distribution.Text (display, disp)+import Distribution.Package (unPackageName, packageName)+import Distribution.Nixpkgs.Haskell.BuildInfo+import Distribution.Nixpkgs.Haskell.Derivation+import Language.Nix.PrettyPrinting (onlyIf)+import Language.Nix as Nix+import Stackage.Types (SystemInfo(..))+import Text.PrettyPrint.HughesPJClass hiding ((<>))+++import qualified Data.Graph as Graph+import qualified Data.Map as Map+import qualified Data.Set as Set++data Node = Node+  { _nodeDerivation   :: Derivation+  , _nodeTestDepends  :: Set.Set String+  , _nodeOtherDepends :: Set.Set String }++makeLenses ''Node++mkNode :: Derivation -> Node+mkNode _nodeDerivation = Node{..}+  where+    haskellDependencies s = Set.map (view (localName . ident))+      . Set.filter isFromHackage+      $ view (s . (haskell <> tool)) _nodeDerivation+    _nodeTestDepends = haskellDependencies testDepends+    _nodeOtherDepends = haskellDependencies (executableDepends <> libraryDepends)++nodeName :: Node -> String+nodeName = unPackageName . packageName . view (nodeDerivation . pkgid)++nodeDepends :: Node -> Set.Set String+nodeDepends = _nodeTestDepends <> _nodeOtherDepends++findCycles :: [Node] -> [[Node]]+findCycles nodes = mapMaybe cyclic $+  Graph.stronglyConnComp [(node, nodeName node, Set.toList $ nodeDepends node) | node <- nodes]+ where+  cyclic (Graph.AcyclicSCC _) = Nothing+  cyclic (Graph.CyclicSCC c)  = Just c++breakCycle :: [Node] -> [String]+breakCycle [] = []+breakCycle c = nodeName breaker : concatMap breakCycle (findCycles remaining)+ where+  breaker = F.maximumBy (O.comparing rate) c+  remaining = map updateNode c+  updateNode n+    | nodeName n == nodeName breaker = n & nodeTestDepends .~ mempty+    | otherwise = n+  names = Set.fromList $ map nodeName c+  rate node = - Set.size (view nodeOtherDepends node `Set.intersection` names)++type FromVertex = Vertex -> (Node, String, [String])+type FromKey = String -> Maybe Vertex++buildNodeGraph :: [Node] -> (Graph, FromVertex, FromKey)+buildNodeGraph nodes = Graph.graphFromEdges+  [(node, nodeName node, Set.toList $ nodeDepends node) | node <- nodes]++reachableDependencies :: [Node] -> [Node] -> [Node]+reachableDependencies keyNodes nodes = view _1 . fromVertex <$> reachableVertices+  where+    (graph, fromVertex, fromKey) = buildNodeGraph nodes+    keys = mapMaybe (fromKey . nodeName) keyNodes+    reachableVertices = concatMap F.toList $ Graph.dfs graph keys++-- pretty printing++isFromHackage :: Binding -> Bool+isFromHackage b = case view (reference . Nix.path) b of+  ["self",_] -> True+  _          -> False+++pPrintOutConfig :: SystemInfo -> [Node] -> Doc+pPrintOutConfig systemInfo nodes = vcat+  [ "{ pkgs }:"+  , ""+  , "with pkgs.haskell.lib; self: super: {"+  , ""+  , "  # core packages"+  , nest 2 $ vcat $+      Map.toList (siCorePackages systemInfo) <&> \(pkg, _version) ->+        onlyIf (pkg /= "ghc")+          $ hsep [doubleQuotes (text (display pkg)), equals, text "null"] <> semi+  , ""+  , nest 2 $ vcat $ pPrintBreakCycle <$> findCycles nodes+  , ""+  , "}"+  ]++pPrintBreakCycle :: [Node] -> Doc+pPrintBreakCycle c = vcat+  [ text $ "# break cycle: " ++ unwords (map nodeName c)+  , vcat $ breakCycle c <&> \breaker ->+      hsep [doubleQuotes (text breaker), equals, text "dontCheck", text "super." <> text breaker] <> semi+  ]++pPrintOutPackages :: [Derivation] -> Doc+pPrintOutPackages drvs = vcat+  [ "{ pkgs, stdenv, callPackage }:"+  , ""+  , "self: {"+  , ""+  , nest 2 $ vcat (pPrintPackageOverride <$> drvs)+  , ""+  , "}"+  ]++pPrintPackageOverride :: Derivation -> Doc+pPrintPackageOverride drv =+  let+    name = drv ^. pkgid . to packageName+    overrides = fsep+      [ disp bind <> semi+      | bind <- Set.toList $ view (dependencies . each <> extraFunctionArgs) drv+      , not $ isFromHackage bind ]+  in+    hang (hsep [doubleQuotes (text (display name)), equals, text "callPackage"]) 2+    $ parens (pPrint drv) <+> (braces overrides <> semi)
+ src/Distribution/Nixpkgs/Haskell/Packages/PrettyPrinting.hs view
@@ -0,0 +1,28 @@+module Distribution.Nixpkgs.Haskell.Packages.PrettyPrinting where++import Language.Nix.PrettyPrinting++compilerConfig :: Doc -> Doc+compilerConfig body =+  funargsCurried ["self", "super"] <+> body++packageSetConfig :: Doc -> Doc+packageSetConfig body = vcat+  [ funargs ["pkgs", "stdenv", "callPackage"]+  , ""+  , funargsCurried ["self"] <+> "{"+  , nest 2 body+  , "}"]++overrides :: Doc -> Doc+overrides body = funargsCurried ["self", "super"] <+> body++-- | Function argument+-- output 'arg:'+funarg :: Doc -> Doc+funarg = flip (<>) colon++-- | Function arguments curried+-- output 'arg1: arg2:'+funargsCurried :: [Doc] -> Doc+funargsCurried = hsep . fmap funarg
+ src/Distribution/Nixpkgs/Haskell/Stack.hs view
@@ -0,0 +1,102 @@+module Distribution.Nixpkgs.Haskell.Stack where++import Control.Lens+import Data.Text as T+import Distribution.Compiler as Compiler+import Distribution.Nixpkgs.Fetch+import Distribution.Nixpkgs.Haskell.Derivation+import Distribution.Nixpkgs.Haskell.FromCabal as FromCabal+import Distribution.Nixpkgs.Haskell.PackageSourceSpec as PackageSourceSpec+import Distribution.PackageDescription as PackageDescription+import Distribution.System as System+import Network.URI as URI+import Stack.Config+++newtype HackageDb = HackageDb { fromHackageDB :: T.Text }+  deriving (Eq, Ord, Show)++makePrisms ''HackageDb++unHackageDb :: HackageDb -> String+unHackageDb = T.unpack . fromHackageDB++data StackPackagesConfig = StackPackagesConfig+  { _spcHaskellResolver   :: !HaskellResolver+  , _spcNixpkgsResolver   :: !NixpkgsResolver+  , _spcTargetPlatform    :: !Platform+  , _spcTargetCompiler    :: !CompilerInfo+  , _spcFlagAssignment    :: !FlagAssignment+  , _spcDoCheckPackages   :: !Bool+  , _spcDoHaddockPackages :: !Bool+  , _spcDoCheckStackage   :: !Bool+  , _spcDoHaddockStackage :: !Bool+  }++makeLenses ''StackPackagesConfig++getStackPackageFromDb+  :: Maybe HackageDb+  -> StackPackage+  -> IO Package+getStackPackageFromDb optHackageDb stackPackage =+  PackageSourceSpec.getPackage+    (unHackageDb <$> optHackageDb)+    (stackLocationToSource $ stackPackage ^. spLocation)++stackLocationToSource :: PackageLocation -> Source+stackLocationToSource = \case+  HackagePackage p -> Source+    { sourceUrl      = "cabal://" ++ T.unpack p+    , sourceRevision = mempty+    , sourceHash     = UnknownHash+    , sourceCabalDir = mempty }+  StackFilePath p  -> Source+    { sourceUrl      = p+    , sourceRevision = mempty+    , sourceHash     = UnknownHash+    , sourceCabalDir = mempty }+  StackUri uri     -> Source+    { sourceUrl      = URI.uriToString id uri mempty+    , sourceRevision = mempty+    , sourceHash     = UnknownHash+    , sourceCabalDir = mempty }+  StackRepo r      -> Source+    { sourceUrl      = T.unpack $ r ^. rUri+    , sourceRevision = T.unpack $ r ^. rCommit+    , sourceHash     = UnknownHash+    , sourceCabalDir = mempty }++packageDerivation+  :: StackPackagesConfig+  -> Maybe HackageDb+  -> StackPackage+  -> IO Derivation+packageDerivation conf optHackageDb stackPackage = do+  pkg <- getStackPackageFromDb optHackageDb stackPackage+  let+    drv = genericPackageDerivation conf pkg+      & src .~ pkgSource pkg+      -- TODO: remove after Nixos/Nixpkgs #27196 released+      & enableSeparateDataOutput .~ False+  return $ if stackPackage ^. spExtraDep+    then drv+      & doCheck &&~ conf ^. spcDoCheckStackage+      & runHaddock &&~ conf ^. spcDoHaddockStackage+    else drv+      & doCheck &&~ conf ^. spcDoCheckPackages+      & runHaddock &&~ conf ^. spcDoHaddockPackages++genericPackageDerivation+  :: StackPackagesConfig+  -> Package+  -> Derivation+genericPackageDerivation conf pkg =+  FromCabal.fromGenericPackageDescription+    (conf ^. spcHaskellResolver)+    (conf ^. spcNixpkgsResolver)+    (conf ^. spcTargetPlatform)+    (conf ^. spcTargetCompiler)+    (conf ^. spcFlagAssignment)+    []+    (pkgCabal pkg)
+ src/Distribution/Nixpkgs/Haskell/Stack/PrettyPrinting.hs view
@@ -0,0 +1,93 @@+{-# 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+++data OverrideConfig = OverrideConfig+  { _ocGhc              :: !Version+  , _ocStackagePackages :: !FilePath+  , _ocStackageConfig   :: !FilePath+  , _ocNixpkgs          :: !FilePath+  }++makeLenses ''OverrideConfig++systemNixpkgs :: Doc+systemNixpkgs = "<nixpkgs>"++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+  [ funarg "self",  "import", disp (fromString path :: Nix.FilePath), "{"+  , "inherit pkgs stdenv;"+  , "inherit (self) callPackage;"+  , "}"+  ]++callStackageConfig :: FilePath -> Doc+callStackageConfig path = hsep+  [ "callPackage", disp (fromString path :: Nix.FilePath), "{}"]++overrideHaskellPackages :: OverrideConfig -> NonEmpty Derivation -> Doc+overrideHaskellPackages oc packages =+  let+    nixpkgs = if oc ^. ocNixpkgs . to fromString == systemNixpkgs+      then systemNixpkgs+      else (disp . (fromString :: FilePath -> Nix.FilePath)) (oc ^. ocNixpkgs)+  in vcat+  [ funargs+    [ "nixpkgs ? import " <> nixpkgs <> " {}"+    ]+  , ""+  , "with nixpkgs;"+  , "let"+  , nest 2 "inherit (stdenv.lib) extends;"+  , nest 2 $ vcat+    [ attr "stackagePackages" . importStackagePackages $ oc ^. ocStackagePackages+    , attr "stackageConfig" . callStackageConfig $ 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 (extends stackageConfig (stackagePackages self))"+    , attr "haskellLib" "callPackage (nixpkgs.path + \"/pkgs/development/haskell-modules/lib.nix\") {}"+    ]+  , "}"+  ]++toNixGhcVersion :: Version -> Doc+toNixGhcVersion =+  (<>) "ghc" . text . L.filter (/= '.') . show . disp
+ src/Language/Nix/FilePath.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE DeriveGeneric #-}++module Language.Nix.FilePath where++import           Control.DeepSeq+import           Control.Lens+import           Control.Monad+import           Data.Maybe+import           Data.String+import           Distribution.Compat.ReadP as ReadP+import           Distribution.Text+import           GHC.Generics (Generic)+import           Prelude hiding (FilePath)+import qualified System.FilePath.Posix as Posix+import           Test.QuickCheck as QC+import           Text.PrettyPrint as PP+++newtype FilePath = FilePath { unFilePath :: String }+  deriving (Show, Eq, Ord, Generic)++makePrisms ''FilePath++instance NFData FilePath where+  rnf = rnf . unFilePath++instance Arbitrary FilePath where+  arbitrary = FilePath <$> QC.suchThat arbitrary Posix.isValid+  shrink = fmap FilePath . shrink . unFilePath++instance Text FilePath where+  disp = text . renderFilePath+  parse = parseFilePath++instance IsString FilePath where+  fromString s = fromMaybe (error $ "invalid Nix file path:" ++ s) (simpleParse s)++parseFilePath :: ReadP r FilePath+parseFilePath = do+  path <- ReadP.munch (const True)+  if Posix.isValid path+    then pure (FilePath path)+    else pfail++renderFilePath :: FilePath -> String+renderFilePath = Posix.combine "."+               . Posix.normalise+               . Posix.dropTrailingPathSeparator+               . unFilePath
+ src/LtsHaskell.hs view
@@ -0,0 +1,96 @@+module LtsHaskell where++import AllCabalHashes+import Control.Lens+import Control.Monad.Catch+import Data.Maybe+import Data.Text as T+import Distribution.Text as Text (display)+import Control.Monad+import Distribution.Compiler (CompilerInfo(..), AbiTag(NoAbiTag), CompilerId(..), CompilerFlavor(GHC))+import Distribution.System (Platform(..))+import Distribution.Package (PackageName, PackageIdentifier(..), Dependency(..))+import Distribution.Nixpkgs.Haskell.PackageSourceSpec as PackageSourceSpec+import Distribution.Nixpkgs.Haskell.FromStack+import Distribution.Nixpkgs.Haskell.Stack+import Distribution.Nixpkgs.Fetch+import Distribution.Nixpkgs.PackageMap (readNixpkgPackageMap, resolve)+import Distribution.Version (Version, withinRange)+import Language.Haskell.Extension (Language(Haskell98, Haskell2010))+import Language.Nix+import Stack.Config+import Stackage.BuildPlan+import Stackage.Types (SystemInfo(..))+import System.FilePath as Path++import qualified Data.Yaml as Yaml+import qualified Data.Map as Map+import qualified Data.Set as Set+++loadBuildPlan :: FilePath -> IO BuildPlan+loadBuildPlan = Yaml.decodeFileEither >=> \case+  Left err -> fail $ "Failed to parse stackage build plan: " ++ show err+  Right bp -> return bp++getPackageFromRepo :: FilePath -> Maybe SHA1Hash -> PackageIdentifier -> IO Package+getPackageFromRepo allCabalHashesPath mSha1Hash pkgId = do+  (pkgDesc, _) <- case mSha1Hash of+    Just sha1 ->+      readPackageByHash allCabalHashesPath sha1+      `catchAll` const (readPackageByName allCabalHashesPath pkgId)+    Nothing   -> readPackageByName allCabalHashesPath pkgId+  meta <- readPackageMeta allCabalHashesPath pkgId+  let+    tarballSHA256 = fromMaybe+      (error (display pkgId ++ ": meta data has no SHA256 hash for the tarball"))+      (view (mHashes . at "SHA256") meta)+    source = DerivationSource "url" ("mirror://hackage/" ++ display pkgId ++ ".tar.gz") "" tarballSHA256+  return $ Package source pkgDesc++getPackageFromDb :: PackageIdentifier -> IO Package+getPackageFromDb pkgId =+  getStackPackageFromDb Nothing+  $ StackPackage (HackagePackage (T.pack $ Text.display pkgId)) True++loadPackage :: FilePath -> Maybe SHA1Hash -> PackageIdentifier -> IO Package+loadPackage allCabalHashesPath mSha1Hash pkgId =+  getPackageFromRepo allCabalHashesPath mSha1Hash pkgId+  `catchIOError` const (getPackageFromDb pkgId)++ghcCompilerInfo :: Version -> CompilerInfo+ghcCompilerInfo v = CompilerInfo+  { compilerInfoId = CompilerId GHC v+  , compilerInfoAbiTag = NoAbiTag+  , compilerInfoCompat = Just []+  , compilerInfoLanguages = Just [Haskell98, Haskell2010]+  , compilerInfoExtensions = Nothing+  }++buildPlanContainsDependency :: Map.Map PackageName Version -> Dependency -> Bool+buildPlanContainsDependency packageVersions (Dependency depName versionRange) =+  maybe False (`withinRange` versionRange) $ Map.lookup depName packageVersions++buildPackageSetConfig+  :: FilePath+  -> FilePath+  -> BuildPlan+  -> IO PackageSetConfig+buildPackageSetConfig optAllCabalHashes optNixpkgsRepository buildPlan = do+  nixpkgs <- readNixpkgPackageMap optNixpkgsRepository Nothing+  let+    systemInfo      = bpSystemInfo buildPlan+    packageVersions = fmap ppVersion (bpPackages buildPlan) `Map.union` siCorePackages systemInfo+  return PackageSetConfig+    { packageLoader   = loadPackage optAllCabalHashes+    , targetPlatform  = Platform (siArch systemInfo) (siOS systemInfo)+    , targetCompiler  = ghcCompilerInfo (siGhcVersion systemInfo)+    , nixpkgsResolver = resolve (Map.map (Set.map (over path ("pkgs":))) nixpkgs)+    , haskellResolver = buildPlanContainsDependency packageVersions }++-- Path to Yaml build definition file in lts-haskell repository+buildPlanFilePath :: FilePath -> StackResolver -> FilePath+buildPlanFilePath ltsRepoDir resolver =+  ltsRepoDir Path.</>+  (resolver ^. to unStackResolver) Path.<.>+  ".yaml"
+ src/Runner.hs view
@@ -0,0 +1,104 @@+module Runner ( run ) where++import Control.Lens+import Data.Foldable as F+import Data.Functor+import Distribution.Nixpkgs.Haskell.FromStack+import Distribution.Nixpkgs.Haskell.FromStack.Package+import Distribution.Nixpkgs.Haskell.Stack+import Distribution.Nixpkgs.Haskell.Stack.PrettyPrinting as PP+import Distribution.Version (Version)+import Distribution.Compiler (AbiTag(..), unknownCompilerInfo)+import Distribution.Package (mkPackageName, pkgName)+import Distribution.Text as Text (display)+import Language.Nix as Nix+import Options.Applicative+import Paths_stackage2nix ( version )+import Runner.Cli+import Stack.Config+import Stack.Types+import Stackage.Types+import System.IO (withFile, IOMode(..), hPutStrLn, hPrint)+import Text.PrettyPrint.HughesPJClass (render)++import qualified Data.Set as Set+import qualified Data.Map as Map+import qualified LtsHaskell as LH+++run :: IO ()+run = do+  opts <- execParser pinfo+  stackYaml <- envStackYaml >>= \case+    Just p  -> putStrLn "Getting project config file from STACK_YAML environment" $> p+    Nothing -> pure $ opts ^. optStackYaml+  stackConf <- either fail pure =<< readStackConfig stackYaml+  let buildPlanFile = LH.buildPlanFilePath (opts ^. optLtsHaskellRepo) (stackConf ^. scResolver)+  buildPlan <- LH.loadBuildPlan buildPlanFile+  packageSetConfig <- LH.buildPackageSetConfig+    (opts ^. optAllCabalHashesRepo)+    (opts ^. optNixpkgsRepository)+    buildPlan+  -- generate haskell packages override+  let+    overrideConfig = mkOverrideConfig opts (siGhcVersion $ bpSystemInfo buildPlan)+    stackPackagesConfig = mkStackPackagesConfig opts+  packages <- traverse (packageDerivation stackPackagesConfig (opts ^. optHackageDb))+    $ stackConf ^. scPackages+  let out = PP.overrideHaskellPackages overrideConfig packages+  withFile (opts ^. optOutDerivation) WriteMode $ \h -> do+    hPutStrLn h ("# Generated by stackage2nix " ++ Text.display version ++ " from " ++ stackYaml ^. syFilePath)+    hPrint h out+  let+    reachable = Set.map mkPackageName+      $ F.foldr1 Set.union+      $ nodeDepends . mkNode <$> packages+    s2nLoader mHash pkgId =+      if pkgName pkgId `Set.member` reachable+      then packageLoader packageSetConfig Nothing pkgId+      else packageLoader packageSetConfig mHash pkgId+    s2nPackageSetConfig = packageSetConfig { packageLoader = s2nLoader }+    s2nPackageConfig = PackageConfig+      { enableCheck   = opts ^. optDoCheckStackage+      , enableHaddock = opts ^. optDoHaddockStackage }+  allNodes <- traverse (uncurry (buildNodeM s2nPackageSetConfig s2nPackageConfig))+    $ Map.toList (bpPackages buildPlan)++  -- Find all reachable dependencies in stackage set to stick into+  -- stackage packages file. This is performed on the full stackage+  -- set rather than pruning stackage packages beforehand because+  -- stackage does not concern itself with build tools while cabal2nix+  -- does: pruning only after generating full set of packages allows+  -- us to make sure all those extra dependencies are explicitly+  -- listed as well.+  let nodes = case opts ^. optOutPackagesClosure of+        True -> flip reachableDependencies allNodes+                -- Originally reachable nodes are root nodes+                $ filter (\n -> mkPackageName (nodeName n) `Set.member` reachable) allNodes+        False -> allNodes++  withFile (opts ^. optOutStackagePackages) WriteMode $ \h -> do+    hPutStrLn h ("# Generated by stackage2nix " ++ Text.display version ++ " from " ++ buildPlanFile)+    hPutStrLn h $ render $ pPrintOutPackages (view nodeDerivation <$> nodes)+  withFile (opts ^. optOutStackageConfig) WriteMode $ \h -> do+    hPutStrLn h ("# Generated by stackage2nix " ++ Text.display version ++ " from " ++ buildPlanFile)+    hPutStrLn h $ render $ pPrintOutConfig (bpSystemInfo buildPlan) nodes++mkOverrideConfig :: Options -> Version -> OverrideConfig+mkOverrideConfig opts ghcVersion = OverrideConfig+  { _ocGhc              = ghcVersion+  , _ocStackagePackages = opts ^. optOutStackagePackages+  , _ocStackageConfig   = opts ^. optOutStackageConfig+  , _ocNixpkgs          = opts ^. optNixpkgsRepository }++mkStackPackagesConfig :: Options -> StackPackagesConfig+mkStackPackagesConfig opts = StackPackagesConfig+  { _spcHaskellResolver   = const True+  , _spcNixpkgsResolver   = \i -> Just (Nix.binding # (i, Nix.path # [i]))+  , _spcTargetPlatform    = opts ^. optPlatform+  , _spcTargetCompiler    = unknownCompilerInfo (opts ^. optCompilerId) NoAbiTag+  , _spcFlagAssignment    = []+  , _spcDoCheckPackages   = opts ^. optDoCheckPackages+  , _spcDoHaddockPackages = opts ^. optDoHaddockPackages+  , _spcDoCheckStackage   = opts ^. optDoCheckStackage+  , _spcDoHaddockStackage = opts ^. optDoHaddockStackage }
+ src/Runner/Cli.hs view
@@ -0,0 +1,190 @@+module Runner.Cli where++import           Control.Lens+import           Data.Monoid ( (<>) )+import           Data.Text as T+import qualified Distribution.Compat.ReadP as P+import           Distribution.Compiler as Compiler+import           Distribution.Nixpkgs.Haskell.Stack+import           Distribution.System as System+import qualified Distribution.Text as Text+import           Options.Applicative as Opts+import           Paths_stackage2nix ( version )+import           Stack.Types+import           System.Environment+import           System.FilePath+++data Options = Options+  { _optAllCabalHashesRepo  :: !FilePath+  , _optLtsHaskellRepo      :: !FilePath+  , _optOutStackagePackages :: !FilePath+  , _optOutStackageConfig   :: !FilePath+  , _optOutPackagesClosure  :: !Bool+  , _optOutDerivation       :: !FilePath+  , _optDoCheckPackages     :: !Bool+  , _optDoHaddockPackages   :: !Bool+  , _optDoCheckStackage     :: !Bool+  , _optDoHaddockStackage   :: !Bool+  , _optHackageDb           :: !(Maybe HackageDb)+  , _optNixpkgsRepository   :: !FilePath+  , _optCompilerId          :: !CompilerId+  , _optPlatform            :: !Platform+  , _optStackYamlArg        :: !FilePath+  } deriving (Show)++makeLenses ''Options++envStackYaml :: IO (Maybe StackYaml)+envStackYaml = fmap mkStackYaml <$> lookupEnv "STACK_YAML"++optStackYaml :: Getter Options StackYaml+optStackYaml = optStackYamlArg . to mkStackYaml++mkStackYaml :: FilePath -> StackYaml+mkStackYaml p = case splitFileName p of+  (dir, "")   -> StackYaml dir "stack.yaml"+  (dir, ".")  -> StackYaml dir "stack.yaml"+  (_  , "..") -> StackYaml p "stack.yaml"+  (dir, file) -> StackYaml dir file++options :: Parser Options+options = Options+  <$> allCabalHashesRepo+  <*> ltsHaskellRepo+  <*> outStackagePackages+  <*> outStackageConfig+  <*> outPackagesClosure+  <*> outDerivation+  <*> doCheckPackages+  <*> doHaddockPackages+  <*> doCheckStackage+  <*> doHaddockStackage+  <*> optional hackageDb+  <*> nixpkgsRepository+  <*> compilerId+  <*> platform+  <*> stackYamlArg++pinfo :: ParserInfo Options+pinfo = info+  ( helper+    <*> infoOption ("stackage2nix " ++ Text.display version) (long "version" <> help "Show version")+    <*> options )+  ( fullDesc+  <> header "stackage2nix converts Stack files into build instructions for Nix." )++nixpkgsRepository :: Parser FilePath+nixpkgsRepository = option str+  ( long "nixpkgs"+    <> metavar "NIXPKGS"+    <> help "path to Nixpkgs repository"+    <> value "<nixpkgs>"+    <> showDefaultWith id )++allCabalHashesRepo :: Parser FilePath+allCabalHashesRepo = option str+  ( long "all-cabal-hashes"+    <> metavar "REPO_DIR"+    <> help "path to commercialhaskell/all-cabal-hashes repository" )++ltsHaskellRepo :: Parser FilePath+ltsHaskellRepo = option str+  ( long "lts-haskell"+    <> metavar "REPO_DIR"+    <> help "path to fpco/lts-haskell repository" )++outStackagePackages :: Parser FilePath+outStackagePackages = option str+  ( long "out-packages"+    <> metavar "NIX_FILE"+    <> help "output file of the stackage packages set"+    <> value "packages.nix"+    <> showDefaultWith id)++outStackageConfig :: Parser FilePath+outStackageConfig = option str+  ( long "out-config"+    <> metavar "NIX_FILE"+    <> help "output file of the stackage packages compiler config"+    <> value "configuration-packages.nix"+    <> showDefaultWith id)++outPackagesClosure :: Parser Bool+outPackagesClosure = flag True False+  ( long "no-packages-closure"+    <> help "produce full set of stackage packages, not just dependencies" )++outDerivation :: Parser FilePath+outDerivation = option str+  ( long "out-derivation"+    <> metavar "NIX_FILE"+    <> help "path to output derivation"+    <> value "default.nix"+    <> showDefaultWith id )++doCheckPackages :: Parser Bool+doCheckPackages = flag True False+  ( long "no-check"+    <> help "disable tests for project packages")++doHaddockPackages :: Parser Bool+doHaddockPackages = flag True False+  ( long "no-haddock"+    <> help "disable haddock for project packages")++doCheckStackage :: Parser Bool+doCheckStackage = switch+  ( long "do-check-stackage"+    <> help "enable tests for Stackage packages" )++doHaddockStackage :: Parser Bool+doHaddockStackage = switch+  ( long "do-haddock-stackage"+    <> help "enable haddock for Stackage packages" )++stackYamlArg :: Parser FilePath+stackYamlArg = Opts.argument str+  ( metavar "STACK_YAML"+    <> help "path to stack.yaml file or directory" )++-- inherited from cabal2nix++hackageDb :: Parser HackageDb+hackageDb = HackageDb <$>+  option text+    ( long "hackage-db"+      <> metavar "HACKAGE_DB"+      <> help "path to the local hackage db in tar format" )++cabalFlag :: Parser CabalFlag+cabalFlag = CabalFlag <$>+  option text+    ( short 'f'+      <> long "flag"+      <> help "Cabal flag (may be specified multiple times)" )++compilerId :: Parser CompilerId+compilerId = option (readP Text.parse)+  ( long "compiler"+    <> help "compiler to use when evaluating the Cabal file"+    <> value buildCompilerId+    <> showDefaultWith Text.display )++platform :: Parser Platform+platform = option (readP Text.parse)+  ( long "system"+    <> help "target system to use when evaluating the Cabal file"+    <> value buildPlatform+    <> showDefaultWith Text.display )++-- utils++text :: ReadM Text+text = T.pack <$> str++readP :: P.ReadP a a -> ReadM a+readP p = eitherReader $ \s ->+  case [ r' | (r',"") <- P.readP_to_S p s ] of+    (r:_) -> Right r+    _     -> Left ("invalid value " ++ show s)
+ src/Stack/Config.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE ViewPatterns #-}++module Stack.Config where++import Control.Lens+import Data.Bifunctor+import Data.ByteString as BS+import Data.Coerce+import Data.Foldable as F+import Data.List.NonEmpty as NE+import Data.Maybe+import Data.Text as T+import Data.Yaml+import Network.URI+import Stack.Config.Yaml as Yaml+import Stack.Types+import System.FilePath+++newtype StackResolver = StackResolver { fromStackResolver :: Text }+  deriving (Eq, Ord, Show)++makePrisms ''StackResolver++unStackResolver :: StackResolver -> String+unStackResolver = T.unpack . fromStackResolver++newtype Vcs = Vcs { fromVcs :: Text }+  deriving (Eq, Ord, Show)++data Repo = Repo+  { _rVcs    :: !Vcs+  , _rUri    :: !Text+  , _rCommit :: !Text+  } deriving (Eq, Ord, Show)++makeLenses ''Repo++data PackageLocation+  = HackagePackage Text+  | StackFilePath FilePath+  | StackUri URI+  | StackRepo Repo+  deriving (Eq, Ord, Show)++makePrisms ''PackageLocation++data StackPackage = StackPackage+  { _spLocation :: !PackageLocation+  , _spExtraDep :: !Bool+  } deriving (Eq, Ord, Show)++makeLenses ''StackPackage++data StackConfig = StackConfig+  { _scResolver  :: !StackResolver+  , _scPackages  :: !(NonEmpty StackPackage)+  } deriving (Eq, Ord, Show)++makeLenses ''StackConfig++fromYamlConfig :: Yaml.Config -> StackConfig+fromYamlConfig c = StackConfig{..}+  where+    _scResolver    = coerce $ c ^. cResolver+    _scPackages    = F.foldr (NE.<|) neYamlPackages yamlExtraDeps+    neYamlPackages = fromMaybe (pure defaultPackage) $ NE.nonEmpty yamlPackages+    yamlPackages   = fromYamlPackage <$> fromMaybe mempty (c ^. cPackages)+    yamlExtraDeps  = fromYamlExtraDep <$> fromMaybe mempty (c ^. cExtraDeps)+    defaultPackage = StackPackage (StackFilePath ".") False++fromYamlPackage :: Yaml.Package -> StackPackage+fromYamlPackage = \case+  Yaml.Simple p                                  ->+    StackPackage (parseSimplePath p) False+  Yaml.LocationSimple (Yaml.Location p extraDep) ->+    StackPackage (parseSimplePath p) (fromMaybe False extraDep)+  Yaml.LocationGit (Location git extraDep)       ->+    StackPackage (StackRepo $ fromYamlGit git) (fromMaybe False extraDep)+  Yaml.LocationHg (Location hg extraDep)       ->+    StackPackage (StackRepo $ fromYamlHg hg) (fromMaybe False extraDep)+  where+    parseSimplePath (T.unpack -> p) = maybe (StackFilePath p) StackUri $ parseURI p++fromYamlExtraDep :: Text -> StackPackage+fromYamlExtraDep = flip StackPackage True . HackagePackage++fromYamlGit :: Yaml.Git -> Repo+fromYamlGit yg = Repo{..}+  where+    _rVcs = Vcs "git"+    _rUri = yg ^. gGit+    _rCommit = yg ^. gCommit++fromYamlHg :: Yaml.Hg -> Repo+fromYamlHg yh = Repo{..}+  where+    _rVcs = Vcs "hg"+    _rUri = yh ^. hHg+    _rCommit = yh ^. hCommit++readStackConfig :: StackYaml -> IO (Either String StackConfig)+readStackConfig stackYaml = do+  let+    relativeToStackYaml = \case+      StackFilePath p -> StackFilePath $ stackYaml ^. syDirName </> p+      packageLocation -> packageLocation+    mkStackConfig = over (scPackages . traversed . spLocation) relativeToStackYaml+      . fromYamlConfig+  second mkStackConfig . decodeEither <$> BS.readFile (stackYaml ^. syFilePath)
+ src/Stack/Config/TH.hs view
@@ -0,0 +1,23 @@+module Stack.Config.TH where++import Data.Aeson.TH+import Data.Char as C+import Data.List as L+import Data.Text as T+import Text.Inflections as TI++toDashedField :: String -> String+toDashedField = toDashed' . dropPrefix++toDashed' :: String -> String+toDashed' = either (const $ error "toDashed failed") T.unpack+  . TI.toDashed+  . T.pack++dropPrefix :: String -> String+dropPrefix = L.dropWhile (not . C.isUpper)++jsonOpts :: Options+jsonOpts = defaultOptions+  { fieldLabelModifier = toDashedField+  , omitNothingFields  = True }
+ src/Stack/Config/Yaml.hs view
@@ -0,0 +1,68 @@+module Stack.Config.Yaml where++import Control.Applicative+import Control.Lens+import Data.Aeson+import Data.Aeson.TH+import Data.Text as T+import Stack.Config.TH++data Location a = Location+  { _lLocation :: !a+  , _lExtraDep :: !(Maybe Bool)+  } deriving (Eq, Show)++makeLenses ''Location++deriveJSON jsonOpts ''Location++data Git = Git+  { _gGit    :: !Text+  , _gCommit :: !Text+  } deriving (Eq, Show)++makeLenses ''Git++deriveJSON jsonOpts ''Git++data Hg = Hg+  { _hHg     :: !Text+  , _hCommit :: !Text+  } deriving (Eq, Show)++makeLenses ''Hg++deriveJSON jsonOpts ''Hg++data Package+  = Simple Text+  | LocationSimple (Location Text)+  | LocationGit (Location Git)+  | LocationHg (Location Hg)+  deriving (Eq, Show)++makePrisms ''Package++instance FromJSON Package where+  parseJSON v =+    (Simple <$> parseJSON v) <|>+    (LocationSimple <$> parseJSON v) <|>+    (LocationGit <$> parseJSON v) <|>+    (LocationHg <$> parseJSON v)++instance ToJSON Package where+  toJSON = \case+    Simple t         -> toJSON t+    LocationSimple t -> toJSON t+    LocationGit t    -> toJSON t+    LocationHg t     -> toJSON t++data Config = Config+  { _cResolver  :: !Text+  , _cPackages  :: !(Maybe [Package])+  , _cExtraDeps :: !(Maybe [Text])+  } deriving (Eq, Show)++makeLenses ''Config++deriveJSON jsonOpts ''Config
+ src/Stack/Types.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE TemplateHaskell #-}++module Stack.Types where++import Control.Lens+import Data.Text as T+import System.FilePath+++data StackYaml = StackYaml+  { _syDirName  :: !FilePath+  , _syFileName :: !FilePath }++makeLenses ''StackYaml++syFilePath :: Getter StackYaml FilePath+syFilePath = to stackYamlPath+  where+    stackYamlPath sy = sy ^. syDirName </> sy ^. syFileName++newtype CabalFlag = CabalFlag { fromCabalFlag :: Text }+  deriving (Ord, Eq, Show)++makePrisms ''CabalFlag++unCabalFlag :: CabalFlag -> String+unCabalFlag = T.unpack . fromCabalFlag
+ stackage2nix.cabal view
@@ -0,0 +1,94 @@+name:                stackage2nix+version:             0.3.0+synopsis:            Convert Stack files into Nix build instructions.+homepage:            https://github.com/4e6/stackage2nix#readme+license:             BSD3+license-file:        LICENSE+author:              Dmitry Bushev+                   , Benno Fünfstück+maintainer:          bushevdv@gmail.com+category:            Distribution, Nix+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10+extra-source-files:  CHANGELOG.md++library+  hs-source-dirs:      src+  exposed-modules:     Distribution.Nixpkgs.Haskell.FromStack+                     , Distribution.Nixpkgs.Haskell.FromStack.Package+                     , Distribution.Nixpkgs.Haskell.Packages.PrettyPrinting+                     , Distribution.Nixpkgs.Haskell.Stack+                     , Distribution.Nixpkgs.Haskell.Stack.PrettyPrinting+                     , Language.Nix.FilePath+                     , Runner+                     , Runner.Cli+                     , Stack.Config+                     , Stack.Config.TH+                     , Stack.Config.Yaml+                     , Stack.Types+  other-modules:       AllCabalHashes+                     , LtsHaskell+                     , Paths_stackage2nix+  build-depends:       base > 4.7 && < 5+                     , Cabal > 1.24+                     , QuickCheck+                     , aeson+                     , bytestring+                     , cabal2nix >= 2.5+                     , containers+                     , deepseq+                     , distribution-nixpkgs >= 1.1+                     , exceptions > 0.8+                     , filepath+                     , gitlib > 3+                     , gitlib-libgit2 > 3+                     , hopenssl > 2.2+                     , inflections+                     , language-nix+                     , lens+                     , network-uri+                     , optparse-applicative+                     , pretty+                     , stackage-curator >= 0.15+                     , text+                     , unordered-containers+                     , yaml+  default-extensions:  LambdaCase+                     , OverloadedStrings+                     , RecordWildCards+                     , TemplateHaskell+  default-language:    Haskell2010+  ghc-options:         -Wall -funbox-strict-fields++executable stackage2nix+  hs-source-dirs:      stackage2nix+  main-is:             Main.hs+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N+  build-depends:       base+                     , stackage2nix+  default-language:    Haskell2010++test-suite spec+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , Cabal+                     , bytestring+                     , hspec+                     , pretty+                     , shakespeare+                     , stackage2nix+                     , text+                     , yaml+  default-extensions:  OverloadedStrings+  other-modules:       Language.Nix.FilePathSpec+                     , PrettyPrintingSpec+                     , Stack.Config.YamlSpec+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/4e6/stackage2nix
+ stackage2nix/Main.hs view
@@ -0,0 +1,6 @@+module Main where++import Runner++main :: IO ()+main = run
+ test/Language/Nix/FilePathSpec.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE OverloadedStrings #-}++module Language.Nix.FilePathSpec where++import           Data.String+import           Distribution.Text+import qualified Language.Nix.FilePath as Nix+import           Test.Hspec+import           Text.PrettyPrint as PP++displayPath :: String -> Doc -> SpecWith ()+displayPath path result = specify path+  $ disp (fromString path :: Nix.FilePath) `shouldBe` result++spec :: Spec+spec = describe "valid FilePath" $ do+  displayPath "foo" "./foo"+  displayPath "./foo" "./foo"+  displayPath "../foo" "./../foo"+  displayPath "/foo" "/foo"+  displayPath "." "./."
+ test/PrettyPrintingSpec.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE OverloadedStrings #-}++module PrettyPrintingSpec where++import Control.Monad+import Distribution.Nixpkgs.Haskell.Packages.PrettyPrinting+import Test.Hspec++-- test stuff+spec :: Spec+spec = specify "test" $+  when False $ print (overrides "body")
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/Stack/Config/YamlSpec.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++module Stack.Config.YamlSpec where++import Data.ByteString+import Data.Text.Encoding as T+import Data.Yaml as Y+import Stack.Config.Yaml+import Test.Hspec+import Text.Shakespeare.Text++configYamlMinimal :: ByteString+configYamlMinimal = T.encodeUtf8 [st|+resolver: lts-8.15+packages:+- '.'+|]++configYaml :: ByteString+configYaml = T.encodeUtf8 [st|+resolver: lts-3.7+packages:+  - .+  - location: dir1/dir2+  - location: https://example.com/foo/bar/baz-0.0.2.tar.gz+    extra-dep: true+  - location:+      git: git@github.com:commercialhaskell/stack.git+      commit: 6a86ee32e5b869a877151f74064572225e1a0398+# Comment+extra-deps:+- acme-missiles-0.3+|]++configMinimal :: Config+configMinimal = Config+  { _cResolver  = "lts-8.15"+  , _cPackages  = Just [ Simple "." ]+  , _cExtraDeps = Nothing+  }++config :: Config+config = Config+  { _cResolver = "lts-3.7"+  , _cPackages = Just+    [ Simple "."+    , LocationSimple (Location "dir1/dir2" Nothing)+    , LocationSimple (Location "https://example.com/foo/bar/baz-0.0.2.tar.gz" (Just True))+    , LocationGit $ Location+       (Git+        "git@github.com:commercialhaskell/stack.git"+        "6a86ee32e5b869a877151f74064572225e1a0398")+       Nothing]+  , _cExtraDeps = Just+    ["acme-missiles-0.3"]+  }++spec :: Spec+spec = describe "Parse" $ do+  specify "config-minimal" $+    (Y.decode configYamlMinimal :: Maybe Config) `shouldBe` Just configMinimal+  specify "config-full" $+    (Y.decode configYaml :: Maybe Config) `shouldBe` Just config