@@ -5,28 +5,24 @@ module Main ( main ) where
5import Cabal2Nix.Flags ( configureCabalFlags )
5import Cabal2Nix.Flags ( configureCabalFlags )
6import Cabal2Nix.Generate ( cabal2nix' )
6import Cabal2Nix.Generate ( cabal2nix' )
7import Cabal2Nix.Hackage ( readHashedHackage, Hackage )
7import Cabal2Nix.Hackage ( readHashedHackage, Hackage )
8−-- import Cabal2Nix.Name
9import Cabal2Nix.Package
8import Cabal2Nix.Package
10import Control.Monad
10import Control.Monad
11−import Control.Applicative hiding ( empty )
12import Control.Monad.Par.Combinator
11import Control.Monad.Par.Combinator
13import Control.Monad.Par.IO
12import Control.Monad.Par.IO
14−-- import Control.Monad.RWS
15−import Control.Monad.Trans
13+import Control.Monad.Trans ( liftIO )
16−import Data.Map ( Map )
14+import Data.Map.Strict ( Map )
17−import qualified Data.Map as Map
15+import qualified Data.Map.Strict as Map
18import Data.Maybe
16import Data.Maybe
19import Data.Monoid
17import Data.Monoid
20import Data.Set ( Set )
18import Data.Set ( Set )
21import qualified Data.Set as Set
19import qualified Data.Set as Set
22import Distribution.Compiler
20import Distribution.Compiler
23−-- import Distribution.Compiler
24import Distribution.NixOS.Derivation.Cabal
21import Distribution.NixOS.Derivation.Cabal
25import Distribution.NixOS.PackageMap ( PackageMap, readNixpkgPackageMap )
22import Distribution.NixOS.PackageMap ( PackageMap, readNixpkgPackageMap )
26−import Distribution.NixOS.PrettyPrinting hiding ( (<>) )
23+import Distribution.NixOS.PrettyPrinting hiding ( attr, (<>) )
27import Distribution.Package
24import Distribution.Package
28import Distribution.PackageDescription hiding ( buildDepends, extraLibs, buildTools )
25import Distribution.PackageDescription hiding ( buildDepends, extraLibs, buildTools )
29−-- import Options.Applicative hiding ( empty )
30import Distribution.PackageDescription.Configuration
26import Distribution.PackageDescription.Configuration
31import Distribution.System
27import Distribution.System
32import Distribution.Text
28import Distribution.Text
@@ -38,9 +34,6 @@ type PackageMultiSet = Map String (Set Version)
39type Constraint = Dependency
35type Constraint = Dependency
41−(!) :: Hackage -> String -> Map Version GenericPackageDescription
42−(!) hackage pkg = fromMaybe (error (show pkg ++ " is not a valid hackage package")) (Map.lookup pkg hackage)
44resolveConstraint :: Constraint -> Hackage -> Version
37resolveConstraint :: Constraint -> Hackage -> Version
45resolveConstraint c = fromMaybe (error ("constraint " ++ display c ++ " cannot be resolved in Hackage")) .
38resolveConstraint c = fromMaybe (error ("constraint " ++ display c ++ " cannot be resolved in Hackage")) .
46 resolveConstraint' c
39 resolveConstraint' c
@@ -48,18 +41,17 @@ resolveConstraint c = fromMaybe (error ("constraint " ++ display c ++ " cannot b
48resolveConstraint' :: Constraint -> Hackage -> Maybe Version
41resolveConstraint' :: Constraint -> Hackage -> Maybe Version
49resolveConstraint' (Dependency (PackageName name) vrange) hackage | Set.null vs = Nothing
42resolveConstraint' (Dependency (PackageName name) vrange) hackage | Set.null vs = Nothing
50 | otherwise = Just (Set.findMax vs)
43 | otherwise = Just (Set.findMax vs)
51− where vs = Set.filter (`withinRange` vrange) (Map.keysSet (hackage ! name))
44+ where vs = Set.filter (`withinRange` vrange) (Map.keysSet (hackage Map.! name))
53main :: IO ()
46main :: IO ()
55 hackage <- readHashedHackage
48 hackage <- readHashedHackage
56− nixpkgs <- readNixpkgPackageMap
49+ nixpkgs <- readNixpkgPackageMap -- TODO: nix barfs at the generated "type" attribute for some reason.
57 runParIO (generatePackageSet (Map.delete "type" hackage) nixpkgs)
50 runParIO (generatePackageSet (Map.delete "type" hackage) nixpkgs)
59generatePackageSet :: Hackage -> Nixpkgs -> ParIO ()
52generatePackageSet :: Hackage -> Nixpkgs -> ParIO ()
60generatePackageSet hackage nixpkgs = do
53generatePackageSet hackage nixpkgs = do
63 corePackageSet :: PackageSet
55 corePackageSet :: PackageSet
64 corePackageSet = Map.fromList [ (name, v) | PackageIdentifier (PackageName name) v <- corePackages ++ hardCorePackages ]
56 corePackageSet = Map.fromList [ (name, v) | PackageIdentifier (PackageName name) v <- corePackages ++ hardCorePackages ]
@@ -79,36 +71,24 @@ generatePackageSet hackage nixpkgs = do
79 latestOverridePackageSet = latestVersionSet `Map.intersection` defaultPackageOverridesSet
71 latestOverridePackageSet = latestVersionSet `Map.intersection` defaultPackageOverridesSet
81 extraPackageSet :: PackageMultiSet
73 extraPackageSet :: PackageMultiSet
82− extraPackageSet = Map.unionsWith Set.union $
74+ extraPackageSet = Map.unionsWith Set.union
83− [ Map.singleton name (Set.singleton (resolveConstraint c hackage)) | c@(Dependency (PackageName name) _) <- extraPackages ] ++
75+ [ Map.singleton name (Set.singleton (resolveConstraint c hackage)) | c@(Dependency (PackageName name) _) <- extraPackages ]
84− [ Map.map Set.singleton latestCorePackageSet ] ++
85− [ Map.map Set.singleton latestOverridePackageSet ]
87− defaultPkgs :: Hackage
88− defaultPkgs = Map.mapWithKey (\k v -> Map.singleton v (hackage ! k Map.! v)) generatedDefaultPackageSet
90− extraPkgs :: Hackage
77+ db :: PackageMultiSet
91− extraPkgs = Map.mapWithKey (\k -> Set.foldr (\v -> Map.insert v (hackage ! k Map.! v)) Map.empty) extraPackageSet
78+ db = Map.unionsWith Set.union [ Map.map Set.singleton generatedDefaultPackageSet
79+ , Map.map Set.singleton latestCorePackageSet
80+ , Map.map Set.singleton latestOverridePackageSet
93− db :: Hackage
84+ knownAttributesSet :: Set String
94− db = Map.unionWith Map.union defaultPkgs extraPkgs
85+ knownAttributesSet = Set.union (Map.keysSet hackage) (Map.keysSet corePackageSet)
97− defaultPackageSet = buildDefaultPackageSet hackage defaultPackageOverrides
98− extendedPackageSet = buildExtendedPackageSet hackage extraPackages
99− db = defaultPackageSet `mergePackageSet` extendedPackageSet
101− matches :: PackageIdentifier -> Dependency -> Bool
102− matches (PackageIdentifier pn v) (Dependency dn vr) = pn == dn && v `withinRange` vr
104 resolver :: Dependency -> Bool
87 resolver :: Dependency -> Bool
105− resolver dep@(Dependency (PackageName pkg) vrange) =
88+ resolver (Dependency (PackageName name) vrange)
106− any (`matches` dep) (corePackages ++ hardCorePackages) ||
89+ | Just v <- Map.lookup name corePackageSet = v `withinRange` vrange
107− maybe False (not . Map.null . Map.filterWithKey (\k _ -> k `withinRange` vrange)) (Map.lookup pkg defaultPkgs)
90+ | Just v <- Map.lookup name generatedDefaultPackageSet = v `withinRange` vrange
108−
91+ | otherwise = False
109− forM_ (Map.assocs defaultPkgs) $ \(name, vdb) ->
110− when (Map.size vdb /= 1) $
111− fail ("invalid defaut package set: " ++ show (name, Map.keys vdb))
113 liftIO $ do putStrLn "/* hackage-packages.nix is an auto-generated file -- DO NOT EDIT! */"
93 liftIO $ do putStrLn "/* hackage-packages.nix is an auto-generated file -- DO NOT EDIT! */"
114 putStrLn ""
94 putStrLn ""
@@ -116,166 +96,67 @@ generatePackageSet hackage nixpkgs = do
116 putStrLn ""
96 putStrLn ""
117 putStrLn "self: {"
97 putStrLn "self: {"
118 putStrLn ""
98 putStrLn ""
119− pkgs <- flip parMapM (Map.toList db) $ \(name, versions) -> do
99+ pkgs <- flip parMapM (Map.toAscList db) $ \(name, vs) -> do
120− pkg <- forM (Map.toList versions) $ \(version, descr) -> do
100+ defs <- forM (Set.toAscList vs) $ \version -> do
121− let isDefaultVersion :: Bool
101+ srcSpec <- liftIO $ sourceFromHackage Nothing (name ++ "-" ++ display version)
122− isDefaultVersion
102+ let Just cabalFileHash = lookup "x-cabal-file-hash" (customFieldsPD (packageDescription descr))
123− | Just _ <- Map.lookup name corePackageSet = False
124− | Just v <- Map.lookup name generatedDefaultPackageSet = v == version
127− (drv, overrides) <- generatePackage db resolver nixpkgs name version descr
129− let nixAttr = name ++ if isDefaultVersion then "" else "_" ++ [ if c == '.' then '_' else c | c <- display version ]
104+ -- TODO: Include list of broken dependencies in the generated output.
130−
105+ descr = hackage Map.! name Map.! version
131− let def = nest 2 $ hang (string nixAttr <+> equals <+> text "callPackage") 2 (parens (disp drv)) <+> (braces overrides <> semi)
106+ (_, _, drv') = cabal2nix resolver descr
132− -- let overr = if not isDefaultVersion && isJust (Map.lookup name corePackageSet)
107+ drv = drv' { src = srcSpec, editedCabalFile = if revision drv == 0 then "" else cabalFileHash }
133− -- then nest 2 $ (string name <+> equals <+> text "null") <> semi
136− return def -- $+$ overr
138− return (render (vcat pkg $+$ text ""))
140− liftIO $ mapM_ putStrLn pkgs
141− liftIO $ putStrLn "}"
143−generatePackage :: Hackage -> (Dependency -> Bool) -> Nixpkgs -> String -> Version -> GenericPackageDescription -> ParIO (Derivation, Doc)
109+ missing :: Set String
144−generatePackage hackage resolver nixpkgs name version descr = do
110+ missing = Set.fromList $
145− srcSpec <- liftIO $ sourceFromHackage Nothing (name ++ "-" ++ display version)
111+ filter (not . isKnownNixpkgAttribute nixpkgs hackage) (extraLibs drv ++ pkgConfDeps drv ++ buildTools drv) ++
146− let Just cabalFileHash = lookup "x-cabal-file-hash" (customFieldsPD (packageDescription descr))
112+ filter (flip Set.notMember knownAttributesSet) (buildDepends drv ++ testDepends drv)
148− -- TODO: Include list of broken dependencies in the generated output.
114+ missingOverrides :: Doc
149− -- Currently, we add overrides that set "pkgname = null", but this is
115+ missingOverrides | Set.null missing = empty
150− -- unsatisfactory because these dependencies may very well work when building
116+ | otherwise = fcat [ text (' ':dep++" = null;") | dep <- Set.toAscList missing ] <> space
151− -- the same package set with a different GHC version.
152− (_, _, drv') = cabal2nix resolver descr
154− let drv = drv' { src = srcSpec
118+ conflicts :: Set String
155− , editedCabalFile = if revision drv == 0 then "" else cabalFileHash
119+ conflicts = Set.intersection knownAttributesSet (Set.fromList (extraLibs drv ++ pkgConfDeps drv))
158− selectHackageNames :: Set String -> Set String
121+ conflictOverrides :: Doc
159− selectHackageNames = Set.intersection (Map.keysSet hackage `Set.union` Set.fromList [ n | PackageIdentifier (PackageName n) _ <- corePackages ++ hardCorePackages ])
122+ conflictOverrides | Set.null conflicts = empty
123+ | otherwise = text " inherit (pkgs) " <> hsep (map text (Set.toAscList conflicts)) <> text "; "
161− selectMissingHackageNames :: Set String -> Set String
125+ overrides :: Doc
162− selectMissingHackageNames = flip Set.difference (Map.keysSet hackage `Set.union` Set.fromList [ n | PackageIdentifier (PackageName n) _ <- corePackages ++ hardCorePackages ])
126+ overrides = conflictOverrides $+$ missingOverrides
164− conflicts :: Set String
128+ attr | Just v <- Map.lookup name generatedDefaultPackageSet, v == version = name
165− conflicts = Set.difference (selectHackageNames $ Set.fromList (extraLibs drv ++ pkgConfDeps drv)) missing
129+ | otherwise = name ++ '_' : [ if c == '.' then '_' else c | c <- display version ]
167− conflictOverrides :: Doc
131+ return $ nest 2 $ hang (string attr <+> equals <+> text "callPackage") 2 (parens (disp drv)) <+> (braces overrides <> semi)
168− conflictOverrides | Set.null conflicts = empty
132+ return (intercalate "\n\n" (map render defs))
169− | otherwise = text " inherit (pkgs) " <> hsep (map text (Set.toAscList conflicts)) <> text "; "
171− missing :: Set String
134+ liftIO $ mapM_ (\pkg -> putStrLn pkg >> putStrLn "") pkgs
172− missing = Set.unions
135+ liftIO $ putStrLn "}"
173− [ Set.fromList (filter (not . isKnownNixpkgAttribute nixpkgs hackage) (extraLibs drv ++ pkgConfDeps drv ++ buildTools drv))
174− , selectMissingHackageNames (Set.fromList (buildDepends drv ++ testDepends drv))
175− -- , Set.fromList [ n | Dependency (PackageName n) _ <- missingDeps, n /= name ]
178− missingOverrides :: Doc
179− missingOverrides | Set.null missing = empty
180− | otherwise = fcat [ text (' ':dep++" = null;") | dep <- Set.toAscList missing ] <> space
183− overrides = conflictOverrides $+$ missingOverrides
185− return (drv, overrides)
187isKnownNixpkgAttribute :: Nixpkgs -> Hackage -> String -> Bool
137isKnownNixpkgAttribute :: Nixpkgs -> Hackage -> String -> Bool
188isKnownNixpkgAttribute nixpkgs hackage name
138isKnownNixpkgAttribute nixpkgs hackage name
189− | '.' `elem` name = True
139+ | '.' `elem` name = True -- TODO: remove this crap once the cuda expression has a proper post-process hook.
190 | Just _ <- Map.lookup name hackage = True
140 | Just _ <- Map.lookup name hackage = True
191 | otherwise = maybe False goodScope (Map.lookup name nixpkgs)
141 | otherwise = maybe False goodScope (Map.lookup name nixpkgs)
193 goodScope :: Set [String] -> Bool
143 goodScope :: Set [String] -> Bool
194 goodScope = not . Set.null . Set.intersection (Set.fromList [[], ["xlibs"], ["gnome"]])
144 goodScope = not . Set.null . Set.intersection (Set.fromList [[], ["xlibs"], ["gnome"]])
196−buildDefaultPackageSet :: Hackage -> [Dependency] -> Hackage
197−buildDefaultPackageSet hackage overrides = Map.unions $ -- unions is left-biased, i.e. overrides trump the latest version
198− [ selectLatestMatchingPackage p hackage | p <- overrides ] ++
199− [ selectLatestMatchingPackage (Dependency (PackageName n) anyVersion) hackage | n <- Map.keys hackage ]
201−buildExtendedPackageSet :: Hackage -> [Dependency] -> Hackage
202−buildExtendedPackageSet hackage extras = Map.unionsWith Map.union
203− [ selectLatestMatchingPackage p hackage | p <- extras ]
205−mergePackageSet :: Hackage -> Hackage -> Hackage
206−mergePackageSet = Map.unionWith Map.union
208-- These packages replace the latest respective version during dependency resolution.
146-- These packages replace the latest respective version during dependency resolution.
209−defaultPackageOverrides :: [Dependency]
147+defaultPackageOverrides :: [Constraint]
210defaultPackageOverrides = map (\s -> fromMaybe (error (show s ++ " is not a valid override selector")) (simpleParse s))
148defaultPackageOverrides = map (\s -> fromMaybe (error (show s ++ " is not a valid override selector")) (simpleParse s))
211 [ "mtl == 2.1.*"
149 [ "mtl == 2.1.*"
212 , "monad-control == 0.3.*"
150 , "monad-control == 0.3.*"
214− [ Dependency n (thisVersion v) | PackageIdentifier n v <- corePackages ]
215− -- TODO: This is necessary because otherwise we may end up having a
216− -- version that's "too new" in the default package set. The problem
217− -- is that we don't want those packages generated, really, so maybe
218− -- we should remove them from the final package set after the
219− -- resover has been constructed?
222-- These packages are added to the generated set, but the play no role during dependency resolution.
153-- These packages are added to the generated set, but the play no role during dependency resolution.
223−extraPackages :: [Dependency]
154+extraPackages :: [Constraint]
224extraPackages =
155extraPackages =
225− -- map (\(Dependency name _) -> Dependency name anyVersion) defaultPackageOverrides ++
226 map (\s -> fromMaybe (error (show s ++ " is not a valid extra package selector")) (simpleParse s))
156 map (\s -> fromMaybe (error (show s ++ " is not a valid extra package selector")) (simpleParse s))
227 [ "Cabal < 1.22"
157 [ "Cabal < 1.22"
230−selectLatestMatchingPackage :: Dependency -> Hackage -> Hackage
231−selectLatestMatchingPackage (Dependency (PackageName name) vrange) db = Map.singleton name (Map.singleton key val)
234− (key,val) = Map.findMax (Map.filterWithKey (\k _ -> k `withinRange` vrange) vdb)
236−-- data Options = Options
238−-- , compiler :: CompilerId
242−-- data Config = Config
243−-- { _verbose :: Bool
244−-- , _hackage :: Hackage
245−-- , _nixpkgs :: Nixpkgs
249−-- type Compile a = RWST Config () (Set PackageId) ParIO a
251−-- run :: Compile a -> IO a
253−-- (a, st, ws) <- runParIO (runRWST f (Config True Map.empty Map.empty) Set.empty)
256−-- parseCommandLine :: IO Options
257−-- parseCommandLine = execParser mainOptions
259−-- parseCompilerId :: Parser CompilerId
260−-- parseCompilerId = option (eitherReader (\s -> maybe (Left (show s ++ " is no valid compiler id")) Right (simpleParse s)))
262−-- <> help "identifier of the compiler"
263−-- <> metavar "COMPILER-ID"
264−-- <> value (fromJust (simpleParse "ghc-7.8.3"))
265−-- <> showDefaultWith display
268−-- parseOptions :: Parser Options
269−-- parseOptions = Options
270−-- <$> switch (long "verbose" <> help "enable detailed progress diagnostics")
271−-- <*> parseCompilerId
273−-- mainOptions :: ParserInfo Options
274−-- mainOptions = info (helper <*> parseOptions)
276−-- <> header "hackage2nix -- convert the Hackage database into Nix build instructions"
279cabal2nix :: (Dependency -> Bool) -> GenericPackageDescription -> ([Dependency], FlagAssignment, Derivation)
160cabal2nix :: (Dependency -> Bool) -> GenericPackageDescription -> ([Dependency], FlagAssignment, Derivation)
280cabal2nix resolver cabal = (missingDeps, flags, drv)
161cabal2nix resolver cabal = (missingDeps, flags, drv)