git.iko.soy/typeable/stackage2nix
on master help sign in
Move stackage2nix in
commitce44008d2a78aad004832acc7dda2964bc7672e4 authorDmitry Bushev <bushevdv@gmail.com> dateMon 14 Aug 2017, 20:41 (9 years ago) parent35f8328
8 files changed+498−5
src/AllCabalHashes.hs+82 src/Distribution/Nixpkgs/Haskell/FromStack.hs+90 src/Distribution/Nixpkgs/Haskell/FromStack/Package.hs+131 src/Stackage2nix.hs+167 stack.yaml+2−2 stack2nix.cabal+20−3 app/Main.hs → stack2nix/Main.hs stackage2nix/Main.hs+6
src/AllCabalHashes.hsnew file+82
@@ -0,0 +1,82 @@
1+module AllCabalHashes where
2+
3+import Control.Lens hiding ((<.>))
4+import Data.Aeson as A
5+import Data.ByteString as BS
6+import Data.ByteString.Lazy as BSL
7+import Data.Map as M
8+import Data.Text as T
9+import Data.Text.Encoding as T
10+import Distribution.Nixpkgs.Hashes as NH
11+import Distribution.Package
12+import Distribution.PackageDescription
13+import Distribution.PackageDescription.Parse
14+import Distribution.Text ( display )
15+import Git
16+import Git.Libgit2 as Libgit2
17+import OpenSSL.Digest as SSL ( digest, digestByName )
18+import System.FilePath
19+
20+
21+type SHA1Hash = T.Text
22+type SHA256Hash = String
23+
24+data Meta = Meta
25+ { _mHashes :: !(Map String String)
26+ , _mLocations :: ![String]
27+ , _mPkgsize :: !Int
28+ } deriving (Show)
29+
30+makeLenses ''Meta
31+
32+instance FromJSON Meta where
33+ parseJSON (Object v) = Meta
34+ <$> v .: "package-hashes"
35+ <*> v .: "package-locations"
36+ <*> v .: "package-size"
37+ parseJSON o = fail ("invalid Cabal metadata: " ++ show o)
38+
39+readPackageByHash :: FilePath -> SHA1Hash -> IO (GenericPackageDescription, SHA256Hash)
40+readPackageByHash repoDir sha1Hash = do
41+ let repoOpts = defaultRepositoryOptions { repoPath = repoDir }
42+ repo <- openLgRepository repoOpts
43+ buf <- runLgRepository repo $ do
44+ BlobObj (Blob _ contents) <- lookupObject =<< parseOid sha1Hash
45+ case contents of
46+ BlobString bs -> return bs
47+ BlobStringLazy bsl -> return $ BSL.toStrict bsl
48+ _ -> fail $ "Git SHA1 " ++ show sha1Hash ++ ": expected single Blob"
49+ cabal <- case parseGenericPackageDescription (T.unpack $ T.decodeUtf8 buf) of
50+ ParseOk _ a -> return a
51+ ParseFailed err -> fail ("Git SHA1 " ++ show sha1Hash ++ ": " ++ show err)
52+ let
53+ hash = printSHA256 (digest (digestByName "sha256") buf)
54+ pkg = setCabalFileHash hash cabal
55+ return (pkg, hash)
56+
57+readPackageByName :: FilePath -> PackageIdentifier -> IO (GenericPackageDescription, SHA256Hash)
58+readPackageByName repoDir (PackageIdentifier name version) = do
59+ let cabalFile = repoDir </> unPackageName name </> display version </> unPackageName name <.> "cabal"
60+ buf <- BS.readFile cabalFile
61+ cabal <- case parseGenericPackageDescription (T.unpack $ T.decodeUtf8 buf) of
62+ ParseOk _ a -> return a
63+ ParseFailed err -> fail (cabalFile ++ ": " ++ show err)
64+ let
65+ hash = NH.printSHA256 (SSL.digest (SSL.digestByName "sha256") buf)
66+ pkg = setCabalFileHash hash cabal
67+ return (pkg, hash)
68+
69+setCabalFileHash :: SHA256Hash -> GenericPackageDescription -> GenericPackageDescription
70+setCabalFileHash hash cabal = cabal
71+ { packageDescription = (packageDescription cabal)
72+ { customFieldsPD = ("X-Cabal-File-Hash", hash) : customFieldsPD (packageDescription cabal)
73+ }
74+ }
75+
76+readPackageMeta :: FilePath -> PackageIdentifier -> IO Meta
77+readPackageMeta dirPrefix (PackageIdentifier name version) = do
78+ let metaFile = dirPrefix </> unPackageName name </> display version </> unPackageName name <.> "json"
79+ buf <- BS.readFile metaFile
80+ case eitherDecodeStrict buf of
81+ Left msg -> fail (metaFile ++ ": " ++ msg)
82+ Right x -> return $ over (mHashes . ix "SHA256") (printSHA256 . packHex) x
src/Distribution/Nixpkgs/Haskell/FromStack.hsnew file+90
@@ -0,0 +1,90 @@
1+module Distribution.Nixpkgs.Haskell.FromStack where
2+
3+import AllCabalHashes
4+import Control.Lens
5+import Data.Set.Lens
6+import Distribution.Compiler (CompilerInfo(..))
7+import Distribution.System (Platform(..))
8+import Distribution.Package (PackageName, PackageIdentifier(..), Dependency(..))
9+import Distribution.PackageDescription
10+import Distribution.Nixpkgs.Haskell.FromStack.Package
11+import Distribution.Nixpkgs.Haskell.PackageSourceSpec
12+import Distribution.Nixpkgs.Haskell.FromCabal
13+import Distribution.Nixpkgs.Haskell.Derivation
14+import Stackage.BuildPlan
15+import Stackage.Types (CabalFileInfo(..),PackageConstraints(..), DepInfo(..), SimpleDesc(..), TestState(..))
16+import qualified Distribution.Nixpkgs.Meta as Nix
17+
18+import qualified Data.Map as Map
19+import qualified Data.Set as Set
20+
21+data PackageSetConfig = PackageSetConfig
22+ { haskellResolver :: HaskellResolver
23+ , nixpkgsResolver :: NixpkgsResolver
24+ , packageLoader :: Maybe SHA1Hash -> PackageIdentifier -> IO Package
25+ , targetPlatform :: Platform
26+ , targetCompiler :: CompilerInfo
27+ , enableCheck :: Bool
28+ , enableHaddock :: Bool
29+ }
30+
31+removeTests :: GenericPackageDescription -> GenericPackageDescription
32+removeTests gd = gd { condTestSuites = [] }
33+
34+planDependencies :: PackagePlan -> [Dependency]
35+planDependencies = map makeDependency . Map.toList . sdPackages . ppDesc
36+ where
37+ makeDependency (name, depInfo) = Dependency name (diRange depInfo)
38+
39+buildNodeM :: PackageSetConfig -> PackageName -> PackagePlan -> IO Node
40+buildNodeM conf name plan = do
41+ let
42+ cabalHashes = maybe mempty cfiHashes $ ppCabalFileInfo plan
43+ mGitSha1 = Map.lookup "GitSHA1" cabalHashes
44+ pkg <- packageLoader conf mGitSha1 $ PackageIdentifier name (ppVersion plan)
45+ pure . mkNode $ fromPackage conf plan pkg
46+
47+fromPackage :: PackageSetConfig -> PackagePlan -> Package -> Derivation
48+fromPackage conf plan pkg =
49+ let
50+ constraints = ppConstraints plan
51+ testsEnabled =
52+ enableCheck conf &&
53+ pcTests constraints == ExpectSuccess
54+ haddocksEnabled =
55+ enableHaddock conf &&
56+ pcHaddocks constraints == ExpectSuccess &&
57+ not (Set.null (sdModules (ppDesc plan)))
58+ configureTests
59+ | pcTests constraints == Don'tBuild = removeTests
60+ | otherwise = id
61+ flags = Map.toList (pcFlagOverrides constraints)
62+ (descr, missingDeps) = finalizeGenericPackageDescription
63+ (haskellResolver conf)
64+ (targetPlatform conf)
65+ (targetCompiler conf)
66+ flags
67+ (planDependencies plan)
68+ (configureTests (pkgCabal pkg))
69+ genericDrv = fromPackageDescription
70+ (haskellResolver conf)
71+ (nixpkgsResolver conf)
72+ missingDeps
73+ flags
74+ descr
75+ depName (Dependency name _) = name
76+ testDeps = setOf
77+ ( to testSuites . folded
78+ . to testBuildInfo . to targetBuildDepends
79+ . folded . to depName )
80+ hasIntersection a b = not . null $ Set.intersection a b
81+ brokenEnabled =
82+ testsEnabled &&
83+ not (null missingDeps) &&
84+ setOf (folded . to depName) missingDeps `hasIntersection` testDeps descr
85+ in
86+ genericDrv
87+ & src .~ pkgSource pkg
88+ & doCheck .~ testsEnabled
89+ & runHaddock .~ haddocksEnabled
90+ & metaSection . Nix.broken .~ brokenEnabled
src/Distribution/Nixpkgs/Haskell/FromStack/Package.hsnew file+131
@@ -0,0 +1,131 @@
1+module Distribution.Nixpkgs.Haskell.FromStack.Package where
2+
3+import Control.Lens
4+import Data.Graph (Graph, Vertex)
5+import Data.Foldable as F
6+import Data.Maybe
7+import Data.Monoid
8+import Data.Ord as O
9+import Distribution.Text (display, disp)
10+import Distribution.Package (unPackageName, packageName)
11+import Distribution.Nixpkgs.Haskell.BuildInfo
12+import Distribution.Nixpkgs.Haskell.Derivation
13+import Language.Nix.PrettyPrinting (onlyIf)
14+import Language.Nix as Nix
15+import Stackage.Types (SystemInfo(..))
16+import Text.PrettyPrint.HughesPJClass hiding ((<>))
17+
18+
19+import qualified Data.Graph as Graph
20+import qualified Data.Map as Map
21+import qualified Data.Set as Set
22+
23+data Node = Node
24+ { _nodeDerivation :: Derivation
25+ , _nodeTestDepends :: Set.Set String
26+ , _nodeOtherDepends :: Set.Set String }
27+
28+makeLenses ''Node
29+
30+mkNode :: Derivation -> Node
31+mkNode _nodeDerivation = Node{..}
32+ where
33+ haskellDependencies s = Set.map (view (localName . ident))
34+ . Set.filter isFromHackage
35+ $ view (s . (haskell <> tool)) _nodeDerivation
36+ _nodeTestDepends = haskellDependencies testDepends
37+ _nodeOtherDepends = haskellDependencies (executableDepends <> libraryDepends)
38+
39+nodeName :: Node -> String
40+nodeName = unPackageName . packageName . view (nodeDerivation . pkgid)
41+
42+nodeDepends :: Node -> Set.Set String
43+nodeDepends = _nodeTestDepends <> _nodeOtherDepends
44+
45+findCycles :: [Node] -> [[Node]]
46+findCycles nodes = mapMaybe cyclic $
47+ Graph.stronglyConnComp [(node, nodeName node, Set.toList $ nodeDepends node) | node <- nodes]
48+ where
49+ cyclic (Graph.AcyclicSCC _) = Nothing
50+ cyclic (Graph.CyclicSCC c) = Just c
51+
52+breakCycle :: [Node] -> [String]
53+breakCycle [] = []
54+breakCycle c = nodeName breaker : concatMap breakCycle (findCycles remaining)
55+ where
56+ breaker = F.maximumBy (O.comparing rate) c
57+ remaining = map updateNode c
58+ updateNode n
59+ | nodeName n == nodeName breaker = n & nodeTestDepends .~ mempty
60+ | otherwise = n
61+ names = Set.fromList $ map nodeName c
62+ rate node = - Set.size (view nodeOtherDepends node `Set.intersection` names)
63+
64+type FromVertex = Vertex -> (Node, String, [String])
65+type FromKey = String -> Maybe Vertex
66+
67+buildNodeGraph :: [Node] -> (Graph, FromVertex, FromKey)
68+buildNodeGraph nodes = Graph.graphFromEdges
69+ [(node, nodeName node, Set.toList $ nodeDepends node) | node <- nodes]
70+
71+reachableDependencies :: [Node] -> [Node] -> [Node]
72+reachableDependencies keyNodes nodes = view _1 . fromVertex <$> reachableVertices
73+ where
74+ (graph, fromVertex, fromKey) = buildNodeGraph nodes
75+ keys = mapMaybe (fromKey . nodeName) keyNodes
76+ reachableVertices = concatMap F.toList $ Graph.dfs graph keys
77+
78+-- pretty printing
79+
80+isFromHackage :: Binding -> Bool
81+isFromHackage b = case view (reference . Nix.path) b of
82+ ["self",_] -> True
83+ _ -> False
84+
85+
86+pPrintOutConfig :: SystemInfo -> [Node] -> Doc
87+pPrintOutConfig systemInfo nodes = vcat
88+ [ "{ pkgs }:"
89+ , ""
90+ , "with pkgs.haskell.lib; self: super: {"
91+ , ""
92+ , " # core packages"
93+ , nest 2 $ vcat $
94+ Map.toList (siCorePackages systemInfo) <&> \(pkg, _version) ->
95+ onlyIf (pkg /= "ghc")
96+ $ hsep [doubleQuotes (text (display pkg)), equals, text "null"] <> semi
97+ , ""
98+ , nest 2 $ vcat $ pPrintBreakCycle <$> findCycles nodes
99+ , ""
100+ , "}"
101+ ]
102+
103+pPrintBreakCycle :: [Node] -> Doc
104+pPrintBreakCycle c = vcat
105+ [ text $ "# break cycle: " ++ unwords (map nodeName c)
106+ , vcat $ breakCycle c <&> \breaker ->
107+ hsep [doubleQuotes (text breaker), equals, text "dontCheck", text "super." <> text breaker] <> semi
108+ ]
109+
110+pPrintOutPackages :: [Derivation] -> Doc
111+pPrintOutPackages drvs = vcat
112+ [ "{ pkgs, stdenv, callPackage }:"
113+ , ""
114+ , "self: {"
115+ , ""
116+ , nest 2 $ vcat (pPrintPackageOverride <$> drvs)
117+ , ""
118+ , "}"
119+ ]
120+
121+pPrintPackageOverride :: Derivation -> Doc
122+pPrintPackageOverride drv =
123+ let
124+ name = drv ^. pkgid . to packageName
125+ overrides = fsep
126+ [ disp bind <> semi
127+ | bind <- Set.toList $ view (dependencies . each <> extraFunctionArgs) drv
128+ , not $ isFromHackage bind ]
129+ in
130+ hang (hsep [doubleQuotes (text (display name)), equals, text "callPackage"]) 2
131+ $ parens (pPrint drv) <+> (braces overrides <> semi)
src/Stackage2nix.hsnew file+167
@@ -0,0 +1,167 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE PartialTypeSignatures #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+
5+module Stackage2nix where
6+
7+import AllCabalHashes
8+import Data.Monoid
9+import Options.Applicative
10+import Stackage.BuildPlan
11+import Stackage.Types (SystemInfo(..))
12+import Paths_stack2nix as Main
13+import Distribution.Text (display)
14+import Control.Monad
15+import Text.PrettyPrint.HughesPJClass hiding ((<>))
16+import Distribution.Compiler (CompilerInfo(..), AbiTag(NoAbiTag), CompilerId(..), CompilerFlavor(GHC))
17+import Distribution.System (Platform(..))
18+import Distribution.Package (PackageName, PackageIdentifier(..), Dependency(..))
19+import Distribution.Nixpkgs.Haskell.PackageSourceSpec
20+import Distribution.Nixpkgs.Haskell.FromStack
21+import Distribution.Nixpkgs.Haskell.FromStack.Package
22+import Distribution.Nixpkgs.Fetch
23+import Distribution.Nixpkgs.PackageMap (readNixpkgPackageMap, resolve, PackageMap)
24+import Distribution.Version (Version, withinRange)
25+import Language.Haskell.Extension (Language(Haskell98, Haskell2010))
26+import Language.Nix
27+import Control.Lens
28+import Data.Maybe
29+import System.IO (withFile, IOMode(..), hPutStrLn)
30+
31+import qualified Data.Aeson as Aeson
32+import qualified Data.Yaml as Yaml
33+import qualified Data.Map as Map
34+import qualified Data.Set as Set
35+import qualified Data.ByteString as ByteString
36+import qualified Data.Text as Text
37+import qualified Data.HashMap.Strict as HashMap
38+
39+
40+data Options = Options
41+ { optBuildPlanFile :: FilePath
42+ , optAllCabalHashes :: FilePath
43+ , optDoCheck :: Bool
44+ , optDoHaddock :: Bool
45+ , optNixpkgsRepository :: FilePath
46+ , optNixpkgsMap :: Maybe FilePath
47+ , optOutPackages :: FilePath
48+ , optOutConfig :: FilePath
49+ }
50+
51+options :: Parser Options
52+options = Options
53+ <$> strArgument (metavar "PLAN" <> help "stackage build plan (YAML)")
54+ <*> strArgument (metavar "CABALFILES" <> help "path to checkout of all-cabal-hashes")
55+ <*> switch (long "do-check" <> help "enable tests for Stackage packages")
56+ <*> switch (long "do-haddock" <> help "enable haddock for Stackage packages")
57+ <*> strOption (long "nixpkgs" <> help "path to Nixpkgs repository" <> value "<nixpkgs>" <> showDefaultWith id <> metavar "PATH")
58+ <*> optional (strOption (long "package-map" <> help "path to a serialized nixpkgs package map" <> metavar "PATH"))
59+ <*> strOption (long "out-packages" <> help "name of the output file for the package set" <> value "packages.nix" <> metavar "PATH")
60+ <*> strOption (long "out-config" <> help "name of the output file for the package set configuration" <> value "configuration-packages.nix" <> metavar "PATH")
61+
62+pinfo :: ParserInfo Options
63+pinfo = info
64+ ( helper
65+ <*> infoOption ("stackage2nix " ++ display Main.version) (long "version" <> help "Show version number")
66+ <*> options
67+ )
68+ ( fullDesc
69+ <> header "stackage2nix converts stackage snapshots into a nix package set"
70+ )
71+
72+loadBuildPlan :: FilePath -> IO BuildPlan
73+loadBuildPlan = Yaml.decodeFileEither >=> \case
74+ Left err -> fail $ "Failed to parse stackage build plan: " ++ show err
75+ Right bp -> return bp
76+
77+loadPackage :: FilePath -> Maybe SHA1Hash -> PackageIdentifier -> IO Package
78+loadPackage allCabalHashesPath mSha1Hash pkgId = do
79+ (pkgDesc, _) <- case mSha1Hash of
80+ Just sha1 -> readPackageByHash allCabalHashesPath sha1
81+ Nothing -> readPackageByName allCabalHashesPath pkgId
82+ meta <- readPackageMeta allCabalHashesPath pkgId
83+ let
84+ tarballSHA256 = fromMaybe
85+ (error (display pkgId ++ ": meta data has no SHA256 hash for the tarball"))
86+ (view (mHashes . at "SHA256") meta)
87+ source = DerivationSource "url" ("mirror://hackage/" ++ display pkgId ++ ".tar.gz") "" tarballSHA256
88+ return $ Package source pkgDesc
89+
90+ghcCompilerInfo :: Version -> CompilerInfo
91+ghcCompilerInfo v = CompilerInfo
92+ { compilerInfoId = CompilerId GHC v
93+ , compilerInfoAbiTag = NoAbiTag
94+ , compilerInfoCompat = Just []
95+ , compilerInfoLanguages = Just [Haskell98, Haskell2010]
96+ , compilerInfoExtensions = Nothing
97+ }
98+
99+buildPlanContainsDependency :: Map.Map PackageName Version -> Dependency -> Bool
100+buildPlanContainsDependency packageVersions (Dependency depName versionRange) =
101+ maybe False (`withinRange` versionRange) $ Map.lookup depName packageVersions
102+
103+newtype SerializablePackageMap = SerializablePackageMap { getPackageMap :: PackageMap }
104+
105+instance Aeson.ToJSON SerializablePackageMap where
106+ toJSON (SerializablePackageMap m) = Aeson.object . map makeEntry $ Map.toList m
107+ where
108+ makeEntry (i, s) = (Text.pack (view ident i), Aeson.toJSON . map makePath $ Set.toList s)
109+ makePath = view (path . mapping ident)
110+
111+instance Aeson.FromJSON SerializablePackageMap where
112+ parseJSON = Aeson.withObject "package map object" $
113+ fmap (SerializablePackageMap . Map.fromList) . traverse makeEntry . HashMap.toList
114+ where
115+ makeEntry (i, s) = Aeson.parseJSON s <&> \s' -> (ident # Text.unpack i, Set.fromList (map makePath s'))
116+ makePath = review (path . mapping ident)
117+
118+run :: Options -> IO (BuildPlan, PackageSetConfig)
119+run Options{..} = do
120+ nixpkgs <- fromMaybe (readNixpkgPackageMap optNixpkgsRepository Nothing) $ do
121+ mapFile <- optNixpkgsMap
122+ return $ do
123+ contents <- ByteString.readFile mapFile
124+ case Aeson.eitherDecodeStrict contents of
125+ Left err -> fail $ "Failed to decode package map " ++ show mapFile ++ ": " ++ err
126+ Right v -> return $ getPackageMap v
127+
128+ buildPlan <- loadBuildPlan optBuildPlanFile
129+
130+ let
131+ systemInfo = bpSystemInfo buildPlan
132+ packageVersions = fmap ppVersion (bpPackages buildPlan) `Map.union` siCorePackages systemInfo
133+ conf = PackageSetConfig
134+ { packageLoader = loadPackage optAllCabalHashes
135+ , targetPlatform = Platform (siArch systemInfo) (siOS systemInfo)
136+ , targetCompiler = ghcCompilerInfo (siGhcVersion systemInfo)
137+ , nixpkgsResolver = resolve (Map.map (Set.map (over path ("pkgs":))) nixpkgs)
138+ , haskellResolver = buildPlanContainsDependency packageVersions
139+ , enableCheck = optDoCheck
140+ , enableHaddock = optDoHaddock
141+ }
142+ return (buildPlan, conf)
143+
144+writeOutPackages :: FilePath -> FilePath -> [Node] -> IO ()
145+writeOutPackages optBuildPlanFile optOutPackages nodes =
146+ withFile optOutPackages WriteMode $ \h -> do
147+ hPutStrLn h ("# Generated by stackage2nix from " ++ optBuildPlanFile)
148+ hPutStrLn h $ render $ pPrintOutPackages (view nodeDerivation <$> nodes)
149+
150+writeOutConfig :: FilePath -> FilePath -> SystemInfo -> [Node] -> IO ()
151+writeOutConfig optBuildPlanFile optOutConfig systemInfo nodes =
152+ withFile optOutConfig WriteMode $ \h -> do
153+ hPutStrLn h ("# Generated by stackage2nix from " ++ optBuildPlanFile)
154+ hPutStrLn h $ render $ pPrintOutConfig systemInfo nodes
155+
156+main :: IO ()
157+main = do
158+ opts@Options{..} <- execParser pinfo
159+ (buildPlan, conf) <- run opts
160+ let systemInfo = bpSystemInfo buildPlan
161+ nodes <- traverse (uncurry $ buildNodeM conf) $ Map.toList (bpPackages buildPlan)
162+ withFile optOutPackages WriteMode $ \h -> do
163+ hPutStrLn h ("# Generated by stackage2nix from " ++ optBuildPlanFile)
164+ hPutStrLn h $ render $ pPrintOutPackages (view nodeDerivation <$> nodes)
165+ withFile optOutConfig WriteMode $ \h -> do
166+ hPutStrLn h ("# Generated by stackage2nix from " ++ optBuildPlanFile)
167+ hPutStrLn h $ render $ pPrintOutConfig systemInfo nodes
stack.yaml+2−2
@@ -2,8 +2,8 @@ resolver: lts-9.0
22packages:
33- '.'
44- location:
5− git: https://github.com/4e6/cabal2nix.git
6− commit: 7993728ea70101036a3558acea58cc49a67ac569
5+ git: https://github.com/NixOS/cabal2nix.git
6+ commit: 53777ef45c45f397c1f56f9230ae494d2aefee86
77 extra-dep: true
88- location:
99 git: https://github.com/fpco/stackage-curator.git
stack2nix.cabal+20−3
@@ -1,5 +1,5 @@
11name: stack2nix
2−version: 0.1.0.0
2+version: 0.2.0
33synopsis: Convert Stack files into Nix build instructions.
44homepage: https://github.com/4e6/stack2nix#readme
55license: BSD3
@@ -13,7 +13,10 @@ cabal-version: >=1.10
1313
1414library
1515 hs-source-dirs: src
16− exposed-modules: Distribution.Nixpkgs.Haskell.Packages.PrettyPrinting
16+ exposed-modules: AllCabalHashes
17+ , Distribution.Nixpkgs.Haskell.FromStack
18+ , Distribution.Nixpkgs.Haskell.FromStack.Package
19+ , Distribution.Nixpkgs.Haskell.Packages.PrettyPrinting
1720 , Distribution.Nixpkgs.Haskell.Stack
1821 , Distribution.Nixpkgs.Haskell.Stack.PrettyPrinting
1922 , Language.Nix.FilePath
@@ -23,6 +26,8 @@ library
2326 , Stack.Config.TH
2427 , Stack.Config.Yaml
2528 , Stack.Types
29+ , Stackage2nix
30+ other-modules: Paths_stack2nix
2631 build-depends: base > 4.7 && < 5
2732 , Cabal > 1.24
2833 , QuickCheck
@@ -33,6 +38,9 @@ library
3338 , deepseq
3439 , distribution-nixpkgs >= 1.1
3540 , filepath
41+ , gitlib > 3
42+ , gitlib-libgit2 > 3
43+ , hopenssl > 2.2
3644 , inflections
3745 , language-nix
3846 , lens
@@ -41,6 +49,7 @@ library
4149 , pretty
4250 , stackage-curator
4351 , text
52+ , unordered-containers
4453 , yaml
4554 default-extensions: LambdaCase
4655 , OverloadedStrings
@@ -49,8 +58,16 @@ library
4958 default-language: Haskell2010
5059 ghc-options: -Wall -funbox-strict-fields
5160
61+executable stackage2nix
62+ hs-source-dirs: stackage2nix
63+ main-is: Main.hs
64+ ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
65+ build-depends: base
66+ , stack2nix
67+ default-language: Haskell2010
68+
5269executable stack2nix
53− hs-source-dirs: app
70+ hs-source-dirs: stack2nix
5471 main-is: Main.hs
5572 ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
5673 build-depends: base
app/Main.hs → stack2nix/Main.hsrenamed

The content is the same.

stackage2nix/Main.hsnew file+6
@@ -0,0 +1,6 @@
1+module Main ( main ) where
2+
3+import qualified Stackage2nix as S
4+
5+main :: IO ()
6+main = S.main