1-- Run: cabal build -j hackage2nix && dist/build/hackage2nix/hackage2nix >hackage-packages.nix && mv hackage-packages.nix ~/.nix-defexpr/pkgs/development/haskell-modules/hackage-packages.nix && nix-env -qaP | tail -1
3module Main ( main ) where
5import Cabal2Nix.Flags ( configureCabalFlags )
6import Cabal2Nix.Generate ( cabal2nix' )
7import Cabal2Nix.Hackage ( readHashedHackage, Hackage )
8import Cabal2Nix.Package
11import Control.Monad.Par.Combinator
12import Control.Monad.Par.IO
13import Control.Monad.Trans ( liftIO )
14import Data.Map.Strict ( Map )
15import qualified Data.Map.Strict as Map
18import Data.Set ( Set )
19import qualified Data.Set as Set
20import Distribution.Compiler
21import Distribution.NixOS.Derivation.Cabal
22import Distribution.NixOS.PackageMap ( PackageMap, readNixpkgPackageMap )
23import Distribution.NixOS.PrettyPrinting hiding ( attr, (<>) )
24import Distribution.Package
25import Distribution.PackageDescription hiding ( buildDepends, extraLibs, buildTools )
26import Distribution.PackageDescription.Configuration
27import Distribution.System
28import Distribution.Text
29import Distribution.Version
31type Nixpkgs = PackageMap -- Map String (Set [String])
32type PackageSet = Map String Version
33type PackageMultiSet = Map String (Set Version)
35type Constraint = Dependency
37resolveConstraint :: Constraint -> Hackage -> Version
38resolveConstraint c = fromMaybe (error ("constraint " ++ display c ++ " cannot be resolved in Hackage")) .
41resolveConstraint' :: Constraint -> Hackage -> Maybe Version
42resolveConstraint' (Dependency (PackageName name) vrange) hackage | Set.null vs = Nothing
43 | otherwise = Just (Set.findMax vs)
44 where vs = Set.filter (`withinRange` vrange) (Map.keysSet (hackage Map.! name))
48 hackage <- readHashedHackage
49 nixpkgs <- readNixpkgPackageMap -- TODO: nix barfs at the generated "type" attribute for some reason.
50 runParIO (generatePackageSet (Map.delete "type" hackage) nixpkgs)
52generatePackageSet :: Hackage -> Nixpkgs -> ParIO ()
53generatePackageSet hackage nixpkgs = do
55 corePackageSet :: PackageSet
56 corePackageSet = Map.fromList [ (name, v) | PackageIdentifier (PackageName name) v <- corePackages ++ hardCorePackages ]
58 latestVersionSet :: PackageSet
59 latestVersionSet = Map.map (Set.findMax . Map.keysSet) hackage
61 defaultPackageOverridesSet :: PackageSet
62 defaultPackageOverridesSet = Map.fromList [ (name, resolveConstraint c hackage) | c@(Dependency (PackageName name) _) <- defaultPackageOverrides ]
64 generatedDefaultPackageSet :: PackageSet
65 generatedDefaultPackageSet = (defaultPackageOverridesSet `Map.union` latestVersionSet) `Map.difference` corePackageSet
67 latestCorePackageSet :: PackageSet
68 latestCorePackageSet = latestVersionSet `Map.intersection` corePackageSet
70 latestOverridePackageSet :: PackageSet
71 latestOverridePackageSet = latestVersionSet `Map.intersection` defaultPackageOverridesSet
73 extraPackageSet :: PackageMultiSet
74 extraPackageSet = Map.unionsWith Set.union
75 [ Map.singleton name (Set.singleton (resolveConstraint c hackage)) | c@(Dependency (PackageName name) _) <- extraPackages ]
78 db = Map.unionsWith Set.union [ Map.map Set.singleton generatedDefaultPackageSet
79 , Map.map Set.singleton latestCorePackageSet
80 , Map.map Set.singleton latestOverridePackageSet
84 knownAttributesSet :: Set String
85 knownAttributesSet = Set.union (Map.keysSet hackage) (Map.keysSet corePackageSet)
87 resolver :: Dependency -> Bool
88 resolver (Dependency (PackageName name) vrange)
89 | Just v <- Map.lookup name corePackageSet = v `withinRange` vrange
90 | Just v <- Map.lookup name generatedDefaultPackageSet = v `withinRange` vrange
93 liftIO $ do putStrLn "/* hackage-packages.nix is an auto-generated file -- DO NOT EDIT! */"
95 putStrLn "{ pkgs, stdenv, callPackage }:"
99 pkgs <- flip parMapM (Map.toAscList db) $ \(name, vs) -> do
100 defs <- forM (Set.toAscList vs) $ \version -> do
101 srcSpec <- liftIO $ sourceFromHackage Nothing (name ++ "-" ++ display version)
102 let Just cabalFileHash = lookup "x-cabal-file-hash" (customFieldsPD (packageDescription descr))
104 -- TODO: Include list of broken dependencies in the generated output.
105 descr = hackage Map.! name Map.! version
106 (_, _, drv') = cabal2nix resolver descr
107 drv = drv' { src = srcSpec, editedCabalFile = if revision drv == 0 then "" else cabalFileHash }
109 missing :: Set String
110 missing = Set.fromList $
111 filter (not . isKnownNixpkgAttribute nixpkgs hackage) (extraLibs drv ++ pkgConfDeps drv ++ buildTools drv) ++
112 filter (flip Set.notMember knownAttributesSet) (buildDepends drv ++ testDepends drv)
114 missingOverrides :: Doc
115 missingOverrides | Set.null missing = empty
116 | otherwise = fcat [ text (' ':dep++" = null;") | dep <- Set.toAscList missing ] <> space
118 conflicts :: Set String
119 conflicts = Set.intersection knownAttributesSet (Set.fromList (extraLibs drv ++ pkgConfDeps drv))
121 conflictOverrides :: Doc
122 conflictOverrides | Set.null conflicts = empty
123 | otherwise = text " inherit (pkgs) " <> hsep (map text (Set.toAscList conflicts)) <> text "; "
126 overrides = conflictOverrides $+$ missingOverrides
128 attr | Just v <- Map.lookup name generatedDefaultPackageSet, v == version = name
129 | otherwise = name ++ '_' : [ if c == '.' then '_' else c | c <- display version ]
131 return $ nest 2 $ hang (string attr <+> equals <+> text "callPackage") 2 (parens (disp drv)) <+> (braces overrides <> semi)
132 return (intercalate "\n\n" (map render defs))
134 liftIO $ mapM_ (\pkg -> putStrLn pkg >> putStrLn "") pkgs
135 liftIO $ putStrLn "}"
137isKnownNixpkgAttribute :: Nixpkgs -> Hackage -> String -> Bool
138isKnownNixpkgAttribute nixpkgs hackage name
139 | '.' `elem` name = True -- TODO: remove this crap once the cuda expression has a proper post-process hook.
140 | Just _ <- Map.lookup name hackage = True
141 | otherwise = maybe False goodScope (Map.lookup name nixpkgs)
143 goodScope :: Set [String] -> Bool
144 goodScope = not . Set.null . Set.intersection (Set.fromList [[], ["xlibs"], ["gnome"]])
146-- These packages replace the latest respective version during dependency resolution.
147defaultPackageOverrides :: [Constraint]
148defaultPackageOverrides = map (\s -> fromMaybe (error (show s ++ " is not a valid override selector")) (simpleParse s))
150 , "monad-control == 0.3.*"
153-- These packages are added to the generated set, but the play no role during dependency resolution.
154extraPackages :: [Constraint]
156 map (\s -> fromMaybe (error (show s ++ " is not a valid extra package selector")) (simpleParse s))
160cabal2nix :: (Dependency -> Bool) -> GenericPackageDescription -> ([Dependency], FlagAssignment, Derivation)
161cabal2nix resolver cabal = (missingDeps, flags, drv)
163 drv = (cabal2nix' descr) { configureFlags = [ "-f" ++ (if b then "" else "-") ++ n | (FlagName n, b) <- flags ] }
165 Right (descr, flags) = finalize (if null missingDeps then resolver else const True) cabal
167 missingDeps :: [Dependency]
168 missingDeps = either id (const []) (finalize resolver cabal')
170 finalize :: (Dependency -> Bool) -> GenericPackageDescription -> Either [Dependency] (PackageDescription, FlagAssignment)
171 finalize resolver' = finalizePackageDescription
172 (configureCabalFlags (package (packageDescription cabal)))
174 (Platform X86_64 Linux) -- shouldn't be hardcoded
175 (CompilerId GHC (Version [7,8,4] [])) -- ditto
176 [] -- no additional constraints
178 -- A variant of the cabal file that has all test suites enabled to ensure
179 -- that their dependencies are recognized by finalizePackageDescription.
180 cabal' :: GenericPackageDescription
181 cabal' = cabal { condTestSuites = flaggedTests }
183 flaggedTests :: [(String, CondTree ConfVar [Dependency] TestSuite)]
184 flaggedTests = map (\(n, t) -> (n, mapTreeData enableTest t)) (condTestSuites cabal)
186 enableTest :: TestSuite -> TestSuite
187 enableTest t = t { testEnabled = True }
189corePackages :: [PackageIdentifier] -- Core packages found on Hackageg
190corePackages = map (\s -> fromMaybe (error (show s ++ " is not a valid core package")) (simpleParse s))
195 , "bytestring-0.10.4.0"
196 , "containers-0.5.5.1"
198 , "directory-1.2.1.0"
201 , "haskeline-0.7.1.2"
202 , "haskell2010-1.1.2.0"
203 , "haskell98-2.0.0.3"
206 , "integer-gmp-0.5.1.0"
207 , "old-locale-1.0.0.6"
211 , "template-haskell-2.9.0.0"
214 , "transformers-0.3.0.0"
219hardCorePackages :: [PackageIdentifier] -- Core packages not found on Hackage.
220hardCorePackages = map (\s -> fromMaybe (error (show s ++ " is not a valid core package")) (simpleParse s))
221 [ "bin-package-db-0.0.0.0"
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