@@ -7,6 +7,7 @@ import Control.Lens
77import Control.Monad
88import Control.Monad.Par.Combinator
99import Control.Monad.Par.IO
10+import Control.Monad.Trans ( liftIO )
1011import Data.List
1112import Data.Map.Strict ( Map )
1213import qualified Data.Map.Strict as Map
@@ -35,23 +36,13 @@ import Paths_hackage2nix as Config
3536import Stackage
3637import System.FilePath
3738import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
39+import System.IO
3840
3941-- type Nixpkgs = PackageMap -- Map String (Set [String])
4042type PackageSet = Map String Version
4143type PackageMultiSet = Map String (Set Version)
4244
43−resolveConstraint :: Constraint -> Hackage -> Version
44−resolveConstraint c = fromMaybe (error msg) . resolveConstraint' c
45− where msg = "constraint " ++ display c ++ " cannot be resolved in Hackage"
46−
47−resolveConstraint' :: Constraint -> Hackage -> Maybe Version
48−resolveConstraint' (Dependency (PackageName name) vrange) hackage
49− | Just vset' <- Map.lookup name hackage
50− , vset <- Set.filter (`withinRange` vrange) (Map.keysSet vset')
51− , not (Set.null vset) = Just (Set.findMax vset)
52− | otherwise = Nothing
53−
54−data Options = Options
45+data CLI = CLI
5546 { hackageRepository :: FilePath
5647 , preferredVersionsFile :: Maybe FilePath
5748 , nixpkgsRepository :: FilePath
@@ -62,31 +53,30 @@ data Options = Options
6253 }
6354 deriving (Show)
6455
65−options :: Parser Options
66−options = Options
67− <$> strOption (long "hackage" <> help "path to Hackage git repository" <> value "hackage" <> showDefaultWith id <> metavar "PATH")
68− <*> optional (strOption (long "preferred-versions" <> help "path to Hackage preferred-versions file" <> value "hackage/preferred-versions" <> showDefault <> metavar "PATH"))
69− <*> strOption (long "nixpkgs" <> help "path to Nixpkgs repository" <> value "nixpkgs" <> showDefaultWith id <> metavar "PATH")
70− <*> strOption (long "lts-haskell" <> help "path to LTS Haskell repository" <> value "lts-haskell" <> showDefaultWith id <> metavar "PATH")
71− <*> strOption (long "stackage-nightly" <> help "path to Stackage Nightly repository" <> value "stackage-nightly" <> showDefaultWith id <> metavar "PATH")
72− <*> strOption (long "config" <> help "path to configuration file" <> value "nixpkgs/pkgs/development/haskell-modules/configuration-hackage2nix.yaml" <> showDefaultWith id <> metavar "PATH")
73− <*> option (fmap fromString str) (long "platform" <> help "target platform to generate package set for" <> value "x86_64-linux" <> showDefaultWith display <> metavar "PLATFORM")
74−
75−pinfo :: ParserInfo Options
76−pinfo = info
77− ( helper
78− <*> infoOption ("hackage2nix " ++ display Config.version) (long "version" <> help "Show version number")
79− <*> options
80− )
81− ( fullDesc
82− <> header "hackage2nix converts a Hackage database into a haskell-packages.nix file."
83− )
84−
8556main :: IO ()
8657main = do
87− Options {..} <- execParser pinfo
88−
89− config <- readConfiguration configFile
58+ let cliOptions :: Parser CLI
59+ cliOptions = CLI
60+ <$> strOption (long "hackage" <> help "path to Hackage git repository" <> value "hackage" <> showDefaultWith id <> metavar "PATH")
61+ <*> optional (strOption (long "preferred-versions" <> help "path to Hackage preferred-versions file" <> value "hackage/preferred-versions" <> showDefault <> metavar "PATH"))
62+ <*> strOption (long "nixpkgs" <> help "path to Nixpkgs repository" <> value "nixpkgs" <> showDefaultWith id <> metavar "PATH")
63+ <*> strOption (long "lts-haskell" <> help "path to LTS Haskell repository" <> value "lts-haskell" <> showDefaultWith id <> metavar "PATH")
64+ <*> strOption (long "stackage-nightly" <> help "path to Stackage Nightly repository" <> value "stackage-nightly" <> showDefaultWith id <> metavar "PATH")
65+ <*> strOption (long "config" <> help "path to configuration file inside of Nixpkgs" <> value "pkgs/development/haskell-modules/configuration-hackage2nix.yaml" <> showDefaultWith id <> metavar "PATH")
66+ <*> option (fmap fromString str) (long "platform" <> help "target platform to generate package set for" <> value "x86_64-linux" <> showDefaultWith display <> metavar "PLATFORM")
67+
68+ pinfo :: ParserInfo CLI
69+ pinfo = info
70+ ( helper
71+ <*> infoOption ("hackage2nix " ++ display Config.version) (long "version" <> help "Show version number")
72+ <*> cliOptions
73+ )
74+ ( fullDesc
75+ <> header "hackage2nix converts a Hackage database into a hackage-packages.nix file."
76+ )
77+ CLI {..} <- execParser pinfo
78+
79+ config <- readConfiguration (nixpkgsRepository </> configFile)
9080 nixpkgs <- readNixpkgPackageMap nixpkgsRepository Nothing
9181 preferredVersions <- readPreferredVersions (fromMaybe (hackageRepository </> "preferred-versions") preferredVersionsFile)
9282 let fixup = Map.delete "acme-everything" -- TODO: https://github.com/NixOS/cabal2nix/issues/164
@@ -96,6 +86,9 @@ main = do
9686 hackage <- fixup <$> readHackage hackageRepository
9787 snapshots <- runParIO (readStackage ltsHaskellRepository)
9888 let
89+ hackagePackagesFile :: FilePath
90+ hackagePackagesFile = nixpkgsRepository </> "pkgs/development/haskell-modules/hackage-packages.nix"
91+
9992 corePackageSet :: PackageSet
10093 corePackageSet = Map.fromList [ (name, v) | PackageIdentifier (PackageName name) v <- Set.toList (Config.corePackages config) ]
10194
@@ -141,12 +134,6 @@ main = do
141134 globalPackageMaintainers :: Map PackageName (Set Identifier)
142135 globalPackageMaintainers = Map.unionsWith Set.union [ Map.singleton p (Set.singleton m) | (m,ps) <- Map.toList (packageMaintainers config), p <- Set.toList ps ]
143136
144− do putStrLn "/* hackage-packages.nix is an auto-generated file -- DO NOT EDIT! */"
145− putStrLn ""
146− putStrLn "{ pkgs, stdenv, callPackage }:"
147− putStrLn ""
148− putStrLn "self: {"
149− putStrLn ""
150137 pkgs <- runParIO $ flip parMapM (Map.toAscList db) $ \(name, vs) -> do
151138 let defs = flip map (Set.toAscList vs) $ \v ->
152139 let
@@ -169,7 +156,7 @@ main = do
169156 sha256 = fromMaybe (abort "has no hash") (lookup "X-Package-SHA256" (customFieldsPD (packageDescription descr)))
170157
171158 attr :: String
172− attr = name ++ if isInDefaultPackageSet then [] else '_' : [ if c == '.' then '_' else c | c <- display v ]
159+ attr = if isInDefaultPackageSet then name else mangle pkgId
173160
174161 drv :: Derivation
175162 drv = fromGenericPackageDescription haskellResolver nixpkgsResolver targetPlatform (compilerInfo config) flagAssignment [] descr
@@ -184,8 +171,38 @@ main = do
184171 render $ nest 2 $ hang (doubleQuotes (text attr) <+> equals <+> text "callPackage") 2 (parens (pPrint drv)) <+> (braces overrides <> semi)
185172 return (intercalate "\n\n" defs)
186173
187− mapM_ (\pkg -> putStrLn pkg >> putStrLn "") pkgs
188− putStrLn "}"
174+ withFile hackagePackagesFile WriteMode $ \h -> do
175+ hPutStrLn h "/* hackage-packages.nix is an auto-generated file -- DO NOT EDIT! */"
176+ hPutStrLn h ""
177+ hPutStrLn h "{ pkgs, stdenv, callPackage }:"
178+ hPutStrLn h ""
179+ hPutStrLn h "self: {"
180+ hPutStrLn h ""
181+ mapM_ (\pkg -> hPutStrLn h pkg >> hPutStrLn h "") pkgs
182+ hPutStrLn h "}"
183+
184+ void $ runParIO $ flip parMapM snapshots $ \Snapshot {..} -> liftIO $ do
185+ let ltsConfigFile :: FilePath
186+ ltsConfigFile = nixpkgsRepository </> "pkgs/development/haskell-modules/configuration-" ++ show (pPrint snapshot) ++ ".nix"
187+ putStrLn ("writing " ++ ltsConfigFile)
188+ withFile ltsConfigFile WriteMode $ \h -> do
189+ hPutStrLn h "{ pkgs }:"
190+ hPutStrLn h ""
191+ hPutStrLn h ("self: super: assert super.ghc.name == " ++ show (display compiler) ++ "; {")
192+ hPutStrLn h ""
193+ hPutStrLn h " # core libraries provided by the compiler"
194+ forM_ (Map.keys (Map.insert (PackageName "rts") (Version [] []) (Map.insert (PackageName "Cabal") (Version [] []) corePackages))) $ \(PackageName n) -> do
195+ unless (n == "ghc") (hPutStrLn h (" " ++ n ++ " = null;"))
196+ hPutStrLn h ""
197+ hPutStrLn h (" # " ++ show (pPrint snapshot) ++ " packages")
198+ forM_ (Map.toList packages) $ \(PackageName name, spec) -> do
199+ let isInDefaultPackageSet :: Bool
200+ isInDefaultPackageSet = maybe False (== Stackage.version spec) (Map.lookup name generatedDefaultPackageSet)
201+ unless (isInDefaultPackageSet || name `elem` ["Cabal","rts"]) $
202+ hPutStrLn h (" " ++ show name ++ " = super." ++ show (mangle (PackageIdentifier (PackageName name) (Stackage.version spec))) ++ ";")
203+ hPutStrLn h ""
204+ hPutStrLn h "}"
205+
189206
190207isFromHackage :: Binding -> Bool
191208isFromHackage b = case view (reference . path) b of
@@ -203,3 +220,17 @@ parsePreferredVersionsLine l = simpleParse l `mplus` error ("invalid preferred-v
203220enforcePreferredVersions :: [Constraint] -> String -> Map Version GenericPackageDescription
204221 -> Map Version GenericPackageDescription
205222enforcePreferredVersions cs pkg = Map.filterWithKey (\v _ -> PackageIdentifier (PackageName pkg) v `satisfiesConstraints` cs)
223+
224+resolveConstraint :: Constraint -> Hackage -> Version
225+resolveConstraint c = fromMaybe (error msg) . resolveConstraint' c
226+ where msg = "constraint " ++ display c ++ " cannot be resolved in Hackage"
227+
228+resolveConstraint' :: Constraint -> Hackage -> Maybe Version
229+resolveConstraint' (Dependency (PackageName name) vrange) hackage
230+ | Just vset' <- Map.lookup name hackage
231+ , vset <- Set.filter (`withinRange` vrange) (Map.keysSet vset')
232+ , not (Set.null vset) = Just (Set.findMax vset)
233+ | otherwise = Nothing
234+
235+mangle :: PackageIdentifier -> String
236+mangle (PackageIdentifier (PackageName name) v) = name ++ '_' : [ if c == '.' then '_' else c | c <- display v ]