1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE RecordWildCards #-}
4module Main ( main ) where
6import Distribution.Nixpkgs.Haskell.FromCabal ( fromGenericPackageDescription )
7import Distribution.Nixpkgs.Haskell.FromCabal.Flags
8import Distribution.Nixpkgs.Haskell.HackageGit ( readHackage, Hackage )
9import Distribution.Nixpkgs.Haskell.Constraint
10import Distribution.Nixpkgs.Haskell.FromCabal.Configuration.GHC7102
11import Distribution.Nixpkgs.Haskell.PackageSourceSpec
13import Control.Monad.Par.Combinator
14import Control.Monad.Par.IO
15import Control.Monad.Trans ( liftIO )
18import Data.Map.Strict ( Map )
19import qualified Data.Map.Strict as Map
22import Data.Set ( Set )
23import qualified Data.Set as Set
24import Distribution.Nixpkgs.Fetch
25import Distribution.Nixpkgs.Haskell
26import Distribution.Nixpkgs.Meta
27import Distribution.Nixpkgs.PackageMap
28import Distribution.PackageDescription hiding ( options, buildDepends, extraLibs, buildTools )
29import Distribution.Text
30import Distribution.Package
31import Distribution.Version
33import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
34import Paths_hackage2nix
36import Options.Applicative
39defaultConfiguration :: Configuration
40defaultConfiguration = ghc7102
42type Nixpkgs = PackageMap -- Map String (Set [String])
43type PackageSet = Map String Version
44type PackageMultiSet = Map String (Set Version)
46resolveConstraint :: Constraint -> Hackage -> Version
47resolveConstraint c = fromMaybe (error msg) . resolveConstraint' c
48 where msg = "constraint " ++ display c ++ " cannot be resolved in Hackage"
50resolveConstraint' :: Constraint -> Hackage -> Maybe Version
51resolveConstraint' (Dependency (PackageName name) vrange) hackage =
52 Set.findMax . Set.filter (`withinRange` vrange) . Map.keysSet <$> Map.lookup name hackage
55 { hackageRepository :: FilePath
56 , preferredVersionsFile :: Maybe FilePath
57 , nixpkgsRepository :: FilePath
61options :: Parser Options
63 <$> strOption (long "hackage" <> help "path to Hackage git repository" <> value "hackage" <> showDefault <> metavar "PATH")
64 <*> optional (strOption (long "preferred-versions" <> help "path to Hackage preferred-versions file" <> value "hackage/preferred-versions" <> showDefault <> metavar "PATH"))
65 <*> strOption (long "nixpkgs" <> help "path to Nixpkgs repository" <> value "<nixpkgs>" <> showDefault <> metavar "PATH")
67pinfo :: ParserInfo Options
70 <*> infoOption ("hackage2nix " ++ display version) (long "version" <> help "Show version number")
74 <> header "hackage2nix converts a Hackage database into a haskell-packages.nix file."
79 Options {..} <- execParser pinfo
81 hackage <- readHackage hackageRepository
82 nixpkgs <- readNixpkgPackageMap nixpkgsRepository Nothing
83 preferredVersions <- readPreferredVersions (fromMaybe (hackageRepository </> "preferred-versions") preferredVersionsFile)
84 let fixup = Map.delete "acme-everything" -- TODO: https://github.com/NixOS/cabal2nix/issues/164
85 . Map.delete "som" -- TODO: https://github.com/NixOS/cabal2nix/issues/164
86 . Map.delete "type" -- TODO: https://github.com/NixOS/cabal2nix/issues/163
87 . Map.delete "dictionary-sharing" -- TODO: https://github.com/NixOS/cabal2nix/issues/175
88 . Map.filter (/= Map.empty)
89 . Map.mapWithKey (enforcePreferredVersions preferredVersions)
90 runParIO $ generatePackageSet defaultConfiguration (fixup hackage) nixpkgs
92enforcePreferredVersions :: [Constraint] -> String -> Map Version GenericPackageDescription
93 -> Map Version GenericPackageDescription
94enforcePreferredVersions cs pkg = Map.filterWithKey (\v _ -> PackageIdentifier (PackageName pkg) v `satisfiesConstraints` cs)
96generatePackageSet :: Configuration -> Hackage -> Nixpkgs -> ParIO ()
97generatePackageSet config hackage nixpkgs = do
99 corePackageSet :: PackageSet
100 corePackageSet = Map.fromList [ (name, v) | PackageIdentifier (PackageName name) v <- corePackages config ++ hardCorePackages config ]
102 latestVersionSet :: PackageSet
103 latestVersionSet = Map.map (Set.findMax . Map.keysSet) hackage
105 defaultPackageOverridesSet :: PackageSet
106 defaultPackageOverridesSet = Map.fromList [ (name, resolveConstraint c hackage) | c@(Dependency (PackageName name) _) <- defaultPackageOverrides config ]
108 generatedDefaultPackageSet :: PackageSet
109 generatedDefaultPackageSet = (defaultPackageOverridesSet `Map.union` latestVersionSet) `Map.difference` corePackageSet
111 latestCorePackageSet :: PackageSet
112 latestCorePackageSet = latestVersionSet `Map.intersection` corePackageSet
114 latestOverridePackageSet :: PackageSet
115 latestOverridePackageSet = latestVersionSet `Map.intersection` defaultPackageOverridesSet
117 extraPackageSet :: PackageMultiSet
118 extraPackageSet = Map.unionsWith Set.union
119 [ Map.singleton name (Set.singleton (resolveConstraint c hackage)) | c@(Dependency (PackageName name) _) <- extraPackages config ]
121 db :: PackageMultiSet
122 db = Map.unionsWith Set.union [ Map.map Set.singleton generatedDefaultPackageSet
123 , Map.map Set.singleton latestCorePackageSet
124 , Map.map Set.singleton latestOverridePackageSet
128 haskellResolver :: Dependency -> Bool
129 haskellResolver (Dependency (PackageName name) vrange)
130 | Just v <- Map.lookup name corePackageSet = v `withinRange` vrange
131 | Just v <- Map.lookup name generatedDefaultPackageSet = v `withinRange` vrange
134 nixpkgsResolver :: Identifier -> Maybe Binding
135 nixpkgsResolver = resolve (Map.map (Set.map (over path ("pkgs":))) nixpkgs)
137 liftIO $ do putStrLn "/* hackage-packages.nix is an auto-generated file -- DO NOT EDIT! */"
139 putStrLn "{ pkgs, stdenv, callPackage }:"
143 pkgs <- flip parMapM (Map.toAscList db) $ \(name, vs) -> do
144 defs <- forM (Set.toAscList vs) $ \pkgversion -> do
145 let -- TODO: Include list of broken dependencies in the generated output.
146 descr = hackage Map.! name Map.! pkgversion
148 flagAssignment :: FlagAssignment
149 flagAssignment = configureCabalFlags (packageId descr)
152 drv' = fromGenericPackageDescription haskellResolver nixpkgsResolver (platform ghc7102) (compilerInfo ghc7102) flagAssignment [] descr
155 attr | Just v <- Map.lookup name generatedDefaultPackageSet, v == pkgversion = name
156 | otherwise = name ++ '_' : [ if c == '.' then '_' else c | c <- display pkgversion ]
159 sha256 | Just x <- lookup "X-Package-SHA256" (customFieldsPD (packageDescription descr)) = x
160 | otherwise = error $ display (packageId descr) ++ " has no hash"
162 srcSpec <- liftIO $ sourceFromHackage (Certain sha256) (name ++ "-" ++ display pkgversion)
164 let drv = drv' & src .~ srcSpec
165 & metaSection.hydraPlatforms %~ (`Set.difference` fromMaybe Set.empty (Map.lookup (PackageName name) (dontDistributePackages ghc7102)))
166 & metaSection.maintainers .~ fromMaybe Set.empty (Map.lookup (PackageName name) (packageMaintainers config))
168 isFromHackage :: Binding -> Bool
169 isFromHackage b = case view (reference . path) b of
174 overrides = fcat $ punctuate space
175 [ pPrint b | b <- Set.toList (view (dependencies . each) drv), not (isFromHackage b) ]
177 return $ nest 2 $ hang (doubleQuotes (text attr) <+> equals <+> text "callPackage") 2 (parens (pPrint drv)) <+> (braces overrides <> semi)
178 return (intercalate "\n\n" (map render defs))
180 liftIO $ mapM_ (\pkg -> putStrLn pkg >> putStrLn "") pkgs
181 liftIO $ putStrLn "}"
183readPreferredVersions :: FilePath -> IO [Constraint]
184readPreferredVersions p = mapMaybe parsePreferredVersionsLine . lines <$> readFile p
186parsePreferredVersionsLine :: String -> Maybe Constraint
187parsePreferredVersionsLine [] = Nothing
188parsePreferredVersionsLine ('-':'-':_) = Nothing
189parsePreferredVersionsLine l = simpleParse l `mplus` error ("invalid preferred-versions line: " ++ show l)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list