git.iko.soy/typeable/stackage2nix
on master help sign in
197 lines 9.7 kB history raw
1module Runner ( run ) where
3import Control.Lens
4import Data.Foldable as F
5import Data.List as L
6import Data.Maybe (fromMaybe)
7import Data.Semigroup ((<>))
8import Distribution.Nixpkgs.Haskell.FromStack
9import Distribution.Nixpkgs.Haskell.FromStack.Package
10import Distribution.Nixpkgs.Haskell.Hackage as DB
11import Distribution.Nixpkgs.Haskell.PackageSourceSpec (loadHackageDB)
12import Distribution.Nixpkgs.Haskell.Stack
13import Distribution.Nixpkgs.Haskell.Stack.PrettyPrinting as PP
14import Distribution.Version (Version)
15import Distribution.Compiler (AbiTag(..), unknownCompilerInfo)
16import Distribution.Package (PackageName, mkPackageName, pkgName)
17import Distribution.Text as Text (display)
18import Language.Nix as Nix
19import Options.Applicative
20import Paths_stackage2nix ( version )
21import Runner.Cli
22import Stack.Config
23import Stack.Types
24import Stackage.Types
25import System.IO (withFile, IOMode(..), hPutStrLn)
26import Text.PrettyPrint.HughesPJClass (Doc, render)
28import qualified Data.List.NonEmpty as NE
29import qualified Data.Map as Map
30import qualified Data.Set as Set
31import qualified LtsHaskell as LH
33run :: IO ()
34run = do
35 opts <- execParser pinfo
36 hackageDb <- loadHackageDB (opts ^. optHackageDb) Nothing
37 let
38 -- With optparse-applicative it seems there's no way to make
39 -- 'StackageOptions' optional for Stackage override and required in
40 -- other cases
41 missingErr = error . ("Missing required parameter: " <>)
42 sopts = StackageOptions
43 (fromMaybe (missingErr "--all-cabal-hashes") (opts ^. optAllCabalHashesRepo) )
44 (fromMaybe (missingErr "--lts-haskell") (opts ^. optLtsHaskellRepo))
46 case opts ^. optConfigOrigin of
47 -- Generate Stackage packages from stack.yaml resolver
48 OriginStackYaml stackYaml buildTarget -> do
49 stackConf <- either fail pure =<< readStackConfig stackYaml
50 let
51 stackResolver = stackConf ^. scResolver
52 stackPackagesConfig = mkStackPackagesConfig opts
53 stackExtraDepsConfig = stackPackagesConfig
54 & spcDoCheckPackages .~ False
55 & spcDoHaddockPackages .~ False
56 stackConfPackages <- traverse (fmap mkNode . packageDerivation stackPackagesConfig hackageDb)
57 $ stackConf ^. scPackages
58 stackConfExtraDeps <- traverse (fmap mkNode . packageDerivation stackExtraDepsConfig hackageDb)
59 $ stackConf ^. scExtraDeps
60 let
61 stackConfAllPackages = maybe stackConfPackages ((<>) stackConfPackages)
62 $ NE.nonEmpty stackConfExtraDeps
64 case buildTarget of
65 TargetStackageClosure -> do
66 (buildPlanFile, buildPlan, packageSetConfig) <- loadStackageBuildPlan hackageDb stackResolver opts sopts
67 let
68 overrideConfig = mkOverrideConfig opts (siGhcVersion $ bpSystemInfo buildPlan)
69 stackageSetConfig = if opts ^. optTweaks . tExtraDepsRevisionLatest
70 then extraDepsLatestConfig stackConfAllPackages packageSetConfig
71 else packageSetConfig
72 stackagePackages <- buildStackagePackages stackageSetConfig buildPlan
73 let
74 reachable = reachableDeps stackConfAllPackages
75 -- Nixpkgs generic-builder puts hscolour on path for all libraries
76 withHscolour pkgs =
77 let hscolour = F.find ((== "hscolour") . nodeName) stackagePackages
78 in maybe pkgs (`Set.insert` pkgs) hscolour
79 -- Find all reachable dependencies in stackage set to stick into
80 -- stackage packages file. This is performed on the full stackage
81 -- set rather than pruning stackage packages beforehand because
82 -- stackage does not concern itself with build tools while cabal2nix
83 -- does: pruning only after generating full set of packages allows
84 -- us to make sure all those extra dependencies are explicitly
85 -- listed as well.
86 nodes = Set.toAscList
87 $ withHscolour
88 $ flip reachableDependencies stackagePackages
89 -- Originally reachable nodes are root nodes
90 $ L.filter (\n -> mkPackageName (nodeName n) `Set.member` reachable) stackagePackages
91 writeOutFile buildPlanFile (opts ^. optOutStackagePackages)
92 $ pPrintOutPackages (view nodeDerivation <$> nodes)
93 writeOutFile buildPlanFile (opts ^. optOutStackageConfig)
94 $ pPrintOutConfig (bpSystemInfo buildPlan) nodes
95 writeOutFile (stackYaml ^. syFilePath) (opts ^. optOutDerivation)
96 $ PP.overrideHaskellPackages overrideConfig (view nodeDerivation <$> stackConfAllPackages)
98 TargetStackagePackages -> do
99 (buildPlanFile, buildPlan, packageSetConfig) <- loadStackageBuildPlan hackageDb stackResolver opts sopts
100 let
101 overrideConfig = mkOverrideConfig opts (siGhcVersion $ bpSystemInfo buildPlan)
102 stackageSetConfig = if opts ^. optTweaks . tExtraDepsRevisionLatest
103 then extraDepsLatestConfig stackConfAllPackages packageSetConfig
104 else packageSetConfig
105 -- stackageLoader mHash pkgId =
106 -- if pkgName pkgId `Set.member` reachable
107 -- then packageLoader packageSetConfig Nothing pkgId
108 -- else packageLoader packageSetConfig mHash pkgId
109 -- stackageSetConfig = packageSetConfig { packageLoader = stackageLoader }
110 nodes <- buildStackagePackages stackageSetConfig buildPlan
111 writeOutFile buildPlanFile (opts ^. optOutStackagePackages)
112 $ pPrintOutPackages (view nodeDerivation <$> nodes)
113 writeOutFile buildPlanFile (opts ^. optOutStackageConfig)
114 $ pPrintOutConfig (bpSystemInfo buildPlan) nodes
115 writeOutFile (stackYaml ^. syFilePath) (opts ^. optOutDerivation)
116 $ PP.overrideHaskellPackages overrideConfig (view nodeDerivation <$> stackConfAllPackages)
118 TargetStackageOverride -> do
119 writeOutFile (stackYaml ^. syFilePath) (opts ^. optOutDerivation)
120 $ PP.overrideStackage
121 (stackConf ^. scResolver)
122 (opts ^. optNixpkgsRepository)
123 (view nodeDerivation <$> stackConfAllPackages)
124 writeOutFile (stackYaml ^. syFilePath) (opts ^. optOutStackYamlPackages)
125 $ PP.stackYamlPackages (view nodeDerivation <$> stackConfPackages)
127 -- Generate Stackage packages from resolver
128 OriginResolver stackResolver -> do
129 (buildPlanFile, buildPlan, packageSetConfig) <- loadStackageBuildPlan hackageDb stackResolver opts sopts
130 nodes <- buildStackagePackages packageSetConfig buildPlan
131 let overrideConfig = mkOverrideConfig opts (siGhcVersion $ bpSystemInfo buildPlan)
132 writeOutFile buildPlanFile (opts ^. optOutStackagePackages)
133 $ pPrintOutPackages (view nodeDerivation <$> nodes)
134 writeOutFile buildPlanFile (opts ^. optOutStackageConfig)
135 $ pPrintOutConfig (bpSystemInfo buildPlan) nodes
136 writeOutFile buildPlanFile (opts ^. optOutDerivation)
137 $ PP.pPrintHaskellPackages overrideConfig
139loadStackageBuildPlan :: DB.HackageDB -> StackResolver -> Options -> StackageOptions -> IO (FilePath, BuildPlan, PackageSetConfig)
140loadStackageBuildPlan hackageDb stackResolver opts sopts = do
141 let buildPlanFile = LH.buildPlanFilePath (sopts ^. soptLtsHaskellRepo) stackResolver
142 buildPlan <- LH.loadBuildPlan buildPlanFile
143 packageSetConfig <- LH.buildPackageSetConfig
144 hackageDb
145 (sopts ^. soptAllCabalHashesRepo)
146 (opts ^. optNixpkgsRepository)
147 buildPlan
148 return (buildPlanFile, buildPlan, packageSetConfig)
150buildStackagePackages :: PackageSetConfig -> BuildPlan -> IO [Node]
151buildStackagePackages packageSetConfig buildPlan = do
152 let
153 stackagePackageConfig = PackageConfig
154 { enableCheck = True
155 , enableHaddock = True }
156 traverse (uncurry (buildNode packageSetConfig stackagePackageConfig))
157 $ Map.toAscList (bpPackages buildPlan)
159reachableDeps :: Traversable t => t Node -> Set.Set PackageName
160reachableDeps = Set.map mkPackageName
161 . F.foldr1 Set.union
162 . fmap nodeDepends
164-- Return 'PackageSetConfig' with package loader that loads latest Hackage
165-- revison for direct (reachable) dependencies of input packages
166extraDepsLatestConfig :: Traversable t => t Node -> PackageSetConfig -> PackageSetConfig
167extraDepsLatestConfig packages packageSetConfig =
168 let
169 reachable = reachableDeps packages
170 stackageLoader mHash pkgId =
171 if pkgName pkgId `Set.member` reachable
172 then packageLoader packageSetConfig Nothing pkgId
173 else packageLoader packageSetConfig mHash pkgId
174 in packageSetConfig { packageLoader = stackageLoader }
176writeOutFile :: Show source => source -> FilePath -> Doc -> IO ()
177writeOutFile source filePath contents =
178 withFile filePath WriteMode $ \h -> do
179 hPutStrLn h ("# Generated by stackage2nix " ++ Text.display version ++ " from " ++ show source)
180 hPutStrLn h $ render contents
182mkOverrideConfig :: Options -> Version -> OverrideConfig
183mkOverrideConfig opts ghcVersion = OverrideConfig
184 { _ocGhc = ghcVersion
185 , _ocStackagePackages = opts ^. optOutStackagePackages
186 , _ocStackageConfig = opts ^. optOutStackageConfig
187 , _ocNixpkgs = opts ^. optNixpkgsRepository }
189mkStackPackagesConfig :: Options -> StackPackagesConfig
190mkStackPackagesConfig opts = StackPackagesConfig
191 { _spcHaskellResolver = const True
192 , _spcNixpkgsResolver = \i -> Just (Nix.binding # (i, Nix.path # [i]))
193 , _spcTargetPlatform = opts ^. optPlatform
194 , _spcTargetCompiler = unknownCompilerInfo (opts ^. optCompilerId) NoAbiTag
195 , _spcFlagAssignment = mempty
196 , _spcDoCheckPackages = opts ^. optDoCheckPackages
197 , _spcDoHaddockPackages = opts ^. optDoHaddockPackages }