git.iko.soy/typeable/cabal2nix
on master help sign in
hackage2nix.hs: implement more sophisticated package set building logic

We distinguish between the "default package set" and the "extended package set". The default set contains exactly one version per package. Unless specified otherwise in 'defaultPackageOverrides', that would be the *latest* version. This package set is what Nixpkgs users get when accessing the unversioned attribute names, and that package set is what finalizePackageDescription uses to determine whether a package is broken because of missing dependencies or not. Furthermore, the extended package set contains additional packages (i.e. the latest versions of those packages that were overridden in the default set) which end up as versioned attributes. These play no part in the default dependency resolution, but users may use those attributes to fix builds manually by providing appropriate overrides on the Nixpkgs level.

commitd64370f13f86cee3fa5edbfd077547483636ade1 authorPeter Simons <simons@cryp.to> dateSun 4 Jan 2015, 18:26 (12 years ago) parent424a8fb
1 file changed+48−24
src/hackage2nix.hs+48−24
src/hackage2nix.hs+48−24
@@ -39,7 +39,22 @@ main = do
3939
4040generatePackageSet :: Hackage -> PackageMap -> ParIO ()
4141generatePackageSet hackage nixpkgs = do
42− let db = buildPackageSet hackage
42+ let defaultPackageSet = buildDefaultPackageSet hackage defaultPackageOverrides
43+ extendedPackageSet = buildExtendedPackageSet hackage extraPackages
44+ db = defaultPackageSet `mergePackageSet` extendedPackageSet
45+
46+ matches :: PackageIdentifier -> Dependency -> Bool
47+ matches (PackageIdentifier pn v) (Dependency dn vr) = pn == dn && v `withinRange` vr
48+
49+ resolver :: Dependency -> Bool
50+ resolver dep@(Dependency (PackageName pkg) vrange) =
51+ any (`matches` dep) corePackages ||
52+ maybe False (not . Map.null . Map.filterWithKey (\k _ -> k `withinRange` vrange)) (Map.lookup pkg defaultPackageSet)
53+
54+ forM_ (Map.assocs defaultPackageSet) $ \(name, vdb) ->
55+ when (Map.size vdb /= 1) $
56+ fail ("invalid defaut package set: " ++ show (name, Map.keys vdb))
57+
4358 liftIO $ do putStrLn "/* hackage-packages.nix is an auto-generated file -- DO NOT EDIT! */"
4459 putStrLn ""
4560 putStrLn "{ pkgs, stdenv, callPackage }:"
@@ -50,16 +65,16 @@ generatePackageSet hackage nixpkgs = do
5065 let multiVersionPackage :: Bool
5166 multiVersionPackage = Map.size versions > 1
5267
53− latestVersion :: Version
54− latestVersion = fst (Map.findMax versions)
68+ defaultVersion :: Version
69+ defaultVersion = fst $ Map.findMax $ (Map.!) defaultPackageSet name
5570
5671 pkg <- forM (Map.toList versions) $ \(version, descr) -> do
57− (drv, overrides) <- generatePackage db nixpkgs name version descr
72+ (drv, overrides) <- generatePackage db resolver nixpkgs name version descr
5873
5974 let nixAttr = name ++ if multiVersionPackage then "_" ++ [ if c == '.' then '_' else c | c <- display version ] else ""
6075
6176 let def = nest 2 $ hang ((string nixAttr) <+> equals <+> text "callPackage") 2 (parens (disp drv)) <+> (braces overrides <> semi)
62− let overr = if multiVersionPackage && version == latestVersion
77+ let overr = if multiVersionPackage && version == defaultVersion
6378 then nest 2 $ (string name <+> equals <+> text ("self." ++ show nixAttr)) <> semi
6479 else empty
6580
@@ -70,19 +85,11 @@ generatePackageSet hackage nixpkgs = do
7085 liftIO $ mapM_ putStrLn pkgs
7186 liftIO $ putStrLn "}"
7287
73−generatePackage :: Hackage -> PackageMap -> String -> Version -> GenericPackageDescription -> ParIO (Derivation, Doc)
74−generatePackage hackage nixpkgs name version descr = do
88+generatePackage :: Hackage -> (Dependency -> Bool) -> PackageMap -> String -> Version -> GenericPackageDescription -> ParIO (Derivation, Doc)
89+generatePackage hackage resolver nixpkgs name version descr = do
7590 srcSpec <- liftIO $ sourceFromHackage Nothing (name ++ "-" ++ display version)
7691 let Just cabalFileHash = lookup "x-cabal-file-hash" (customFieldsPD (packageDescription descr))
7792
78− matches :: PackageIdentifier -> Dependency -> Bool
79− matches (PackageIdentifier pn v) (Dependency dn vr) = pn == dn && v `withinRange` vr
80−
81− resolver :: Dependency -> Bool
82− resolver dep@(Dependency (PackageName pkg) vrange) =
83− any (`matches` dep) corePackages ||
84− maybe False (not . Map.null . Map.filterWithKey (\k _ -> k `withinRange` vrange)) (Map.lookup pkg hackage)
85−
8693 -- TODO: Include list of broken dependencies in the generated output.
8794 (missingDeps,drv') <- case cabal2nix resolver descr of
8895 Left ds -> do let Right x = cabal2nix (const True) descr
@@ -128,10 +135,32 @@ isKnownNixpkgAttribute nixpkgs hackage name
128135 goodScope :: Set [String] -> Bool
129136 goodScope = not . Set.null . Set.intersection (Set.fromList [[], ["xlibs"], ["gnome"]])
130137
131−buildPackageSet :: Hackage -> Hackage
132−buildPackageSet db = Map.unionsWith Map.union $
133− [ selectLatestMatchingPackage (Dependency (PackageName n) anyVersion) db | n <- Map.keys db ] ++
134− [ selectLatestMatchingPackage d db | d <- selectors ]
138+buildDefaultPackageSet :: Hackage -> [Dependency] -> Hackage
139+buildDefaultPackageSet hackage overrides = Map.unions $ -- unions is left-biased, i.e. overrides trump the latest version
140+ [ selectLatestMatchingPackage p hackage | p <- overrides ] ++
141+ [ selectLatestMatchingPackage (Dependency (PackageName n) anyVersion) hackage | n <- Map.keys hackage ]
142+
143+buildExtendedPackageSet :: Hackage -> [Dependency] -> Hackage
144+buildExtendedPackageSet hackage extras = Map.unionsWith Map.union
145+ [ selectLatestMatchingPackage p hackage | p <- extras ]
146+
147+mergePackageSet :: Hackage -> Hackage -> Hackage
148+mergePackageSet = Map.unionWith Map.union
149+
150+-- These packages replace the latest respective version during dependency resolution.
151+defaultPackageOverrides :: [Dependency]
152+defaultPackageOverrides = map (\s -> maybe (error (show s ++ " is not a valid override selector")) id (simpleParse s))
153+ [ "mtl == 2.1.*"
154+ , "monad-control == 0.3.*"
155+ ]
156+
157+-- These packages are added to the generated set, but the play no role during dependency resolution.
158+extraPackages :: [Dependency]
159+extraPackages =
160+ map (\(Dependency name _) -> Dependency name anyVersion) defaultPackageOverrides ++
161+ map (\s -> maybe (error (show s ++ " is not a valid extra package selector")) id (simpleParse s))
162+ [
163+ ]
135164
136165selectLatestMatchingPackage :: Dependency -> Hackage -> Hackage
137166selectLatestMatchingPackage (Dependency (PackageName name) vrange) db =
@@ -140,11 +169,6 @@ selectLatestMatchingPackage (Dependency (PackageName name) vrange) db =
140169 Just vdb -> let (key,val) = Map.findMax (Map.filterWithKey (\k _ -> k `withinRange` vrange) vdb)
141170 in Map.singleton name (Map.singleton key val)
142171
143−selectors :: [Dependency]
144−selectors = map (\s -> maybe (error (show s ++ " is not a valid package selector")) id (simpleParse s))
145− [ "mtl == 2.1.*"
146− , "monad-control == 0.3.*"
147− ]
148172
149173-- data Options = Options
150174-- { verbose :: Bool