-
-
Notifications
You must be signed in to change notification settings - Fork 152
/
FromCabal.hs
172 lines (151 loc) · 9.16 KB
/
FromCabal.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Distribution.Nixpkgs.Haskell.FromCabal
( HaskellResolver, NixpkgsResolver
, fromGenericPackageDescription , finalizeGenericPackageDescription , fromPackageDescription
) where
import Control.Lens
import Data.Maybe
import Data.Set ( Set )
import qualified Data.Set as Set
import Distribution.Compiler
import Distribution.Nixpkgs.Haskell
import qualified Distribution.Nixpkgs.Haskell as Nix
import Distribution.Nixpkgs.Haskell.Constraint
import Distribution.Nixpkgs.Haskell.FromCabal.License
import Distribution.Nixpkgs.Haskell.FromCabal.Name
import Distribution.Nixpkgs.Haskell.FromCabal.Normalize
import Distribution.Nixpkgs.Haskell.FromCabal.PostProcess (postProcess)
import qualified Distribution.Nixpkgs.License as Nix
import qualified Distribution.Nixpkgs.Meta as Nix
import Distribution.Package
import Distribution.PackageDescription
import qualified Distribution.PackageDescription as Cabal
import Distribution.PackageDescription.Configuration as Cabal
import Distribution.System
import Distribution.Text ( display )
import Distribution.Types.ComponentRequestedSpec as Cabal
import Distribution.Types.ExeDependency as Cabal
import Distribution.Types.LegacyExeDependency as Cabal
import Distribution.Types.PkgconfigDependency as Cabal
import Distribution.Types.UnqualComponentName as Cabal
import Distribution.Version
import Language.Nix
type HaskellResolver = Dependency -> Bool
type NixpkgsResolver = Identifier -> Maybe Binding
fromGenericPackageDescription :: HaskellResolver -> NixpkgsResolver -> Platform -> CompilerInfo -> FlagAssignment -> [Constraint] -> GenericPackageDescription -> Derivation
fromGenericPackageDescription haskellResolver nixpkgsResolver arch compiler flags constraints genDesc =
fromPackageDescription haskellResolver nixpkgsResolver missingDeps flags descr
where
(descr, missingDeps) = finalizeGenericPackageDescription haskellResolver arch compiler flags constraints genDesc
finalizeGenericPackageDescription :: HaskellResolver -> Platform -> CompilerInfo -> FlagAssignment -> [Constraint] -> GenericPackageDescription -> (PackageDescription, [Dependency])
finalizeGenericPackageDescription haskellResolver arch compiler flags constraints genDesc =
let
-- We have to call the Cabal finalizer several times with different resolver
-- functions, and this convenience function makes our code shorter.
finalize :: HaskellResolver -> Either [Dependency] (PackageDescription,FlagAssignment)
finalize resolver = finalizePD flags requestedComponents resolver arch compiler constraints genDesc
requestedComponents :: ComponentRequestedSpec
requestedComponents = ComponentRequestedSpec
{ testsRequested = True
, benchmarksRequested = True
}
jailbroken :: HaskellResolver -> HaskellResolver
jailbroken resolver (Dependency pkg _ _) = resolver (Dependency pkg anyVersion mempty)
withInternalLibs :: HaskellResolver -> HaskellResolver
withInternalLibs resolver d = depPkgName d `elem` internalNames || resolver d
internalNames :: [PackageName]
internalNames = [ unqualComponentNameToPackageName n | (n,_) <- condSubLibraries genDesc ]
++ [ unqualComponentNameToPackageName n | LSubLibName n <- libName <$> subLibraries (packageDescription genDesc) ]
in case finalize (jailbroken (withInternalLibs haskellResolver)) of
Left m -> case finalize (const True) of
Left _ -> error ("Cabal cannot finalize " ++ display (packageId genDesc))
Right (d,_) -> (d,m)
Right (d,_) -> (d,[])
fromPackageDescription :: HaskellResolver -> NixpkgsResolver -> [Dependency] -> FlagAssignment -> PackageDescription -> Derivation
fromPackageDescription haskellResolver nixpkgsResolver missingDeps flags PackageDescription {..} = normalize $ postProcess $ nullDerivation
& isLibrary .~ isJust library
& pkgid .~ package
& revision .~ xrev
& isLibrary .~ isJust library
& isExecutable .~ not (null executables)
& extraFunctionArgs .~ mempty
& libraryDepends .~ foldMap (convertBuildInfo . libBuildInfo) (maybeToList library ++ subLibraries)
& executableDepends .~ mconcat (map (convertBuildInfo . buildInfo) executables)
& testDepends .~ mconcat (map (convertBuildInfo . testBuildInfo) testSuites)
& benchmarkDepends .~ mconcat (map (convertBuildInfo . benchmarkBuildInfo) benchmarks)
& Nix.setupDepends .~ maybe mempty convertSetupBuildInfo setupBuildInfo
& configureFlags .~ mempty
& cabalFlags .~ flags
& runHaddock .~ doHaddockPhase
& jailbreak .~ False
& doCheck .~ True
& doBenchmark .~ False
& testTarget .~ mempty
& hyperlinkSource .~ True
& enableSplitObjs .~ True
& enableLibraryProfiling .~ False
& enableExecutableProfiling .~ False
& enableSeparateDataOutput .~ not (null dataFiles)
& subpath .~ "."
& phaseOverrides .~ mempty
& editedCabalFile .~ (if xrev > 0
then fromMaybe (error (display package ++ ": X-Cabal-File-Hash field is missing")) (lookup "X-Cabal-File-Hash" customFieldsPD)
else "")
& metaSection .~ ( Nix.nullMeta
& Nix.homepage .~ homepage
& Nix.description .~ synopsis
& Nix.license .~ nixLicense
& Nix.platforms .~ Nix.allKnownPlatforms
& Nix.hydraPlatforms .~ (if isFreeLicense nixLicense then Nix.allKnownPlatforms else Set.empty)
& Nix.maintainers .~ mempty
& Nix.broken .~ not (null missingDeps)
)
where
xrev = maybe 0 read (lookup "x-revision" customFieldsPD)
nixLicense :: Nix.License
nixLicense = either fromSPDXLicense fromCabalLicense licenseRaw
resolveInHackage :: Identifier -> Binding
resolveInHackage i | (i^.ident) `elem` [ unPackageName n | (Dependency n _ _) <- missingDeps ] = bindNull i
| otherwise = binding # (i, path # ["self",i]) -- TODO: "self" shouldn't be hardcoded.
-- TODO: This is all very confusing. Haskell packages refer to the Nixpkgs
-- derivation 'foo' as 'pkgs.foo', because they live in the 'haskellPackages'
-- name space -- not on the top level. Therefore, we built our Nixpkgs lookup
-- function so that top level names are returned as 'pkgs.foo'. As a result, we
-- end up pre-pending that path to all kinds of names all over the place. I
-- suppose the correct approach would be to assume that the lookup function
-- returns names that live in the top-level and to adapt the code in
-- PostProcess.hs et all to that fact.
goodScopes :: Set [Identifier]
goodScopes = Set.fromList (map ("pkgs":) [[], ["xorg"], ["xlibs"], ["gnome2"], ["gnome"], ["gnome3"], ["kde4"]])
resolveInNixpkgs :: Identifier -> Binding
resolveInNixpkgs i
| i `elem` ["clang","lldb","llvm"] = binding # (i, path # ["self","llvmPackages",i]) -- TODO: evil!
| i == "gtk2" = binding # (i, path # ["pkgs","gtk2"]) -- TODO: these cases should not be necessary
| i == "gtk3" = binding # (i, path # ["pkgs","gtk3"])
| i == "gtksourceview3" = binding # (i, path # ["pkgs","gtksourceview3"])
| i == "vte_291" = binding # (i, path # ["pkgs","vte"])
| Just p <- nixpkgsResolver i, init (view (reference . path) p) `Set.member` goodScopes = p
| otherwise = bindNull i
resolveInHackageThenNixpkgs :: Identifier -> Binding
resolveInHackageThenNixpkgs i | haskellResolver (Dependency (mkPackageName (i^.ident)) anyVersion mempty) = resolveInHackage i
| otherwise = resolveInNixpkgs i
internalLibNames :: [PackageName]
internalLibNames = [ unqualComponentNameToPackageName n | LSubLibName n <- libName <$> subLibraries ]
doHaddockPhase :: Bool
doHaddockPhase | not (null internalLibNames) = False
| Just l <- library = not (null (exposedModules l))
| otherwise = True
convertBuildInfo :: Cabal.BuildInfo -> Nix.BuildInfo
convertBuildInfo Cabal.BuildInfo {..} | not buildable = mempty
convertBuildInfo Cabal.BuildInfo {..} = mempty
& haskell .~ Set.fromList [ resolveInHackage (toNixName x) | (Dependency x _ _) <- targetBuildDepends, x `notElem` internalLibNames ]
& system .~ Set.fromList [ resolveInNixpkgs y | x <- extraLibs, y <- libNixName x ]
& pkgconfig .~ Set.fromList [ resolveInNixpkgs y | PkgconfigDependency x _ <- pkgconfigDepends, y <- libNixName (unPkgconfigName x) ]
& tool .~ Set.fromList (map resolveInHackageThenNixpkgs . concatMap buildToolNixName
$ [ unPackageName x | ExeDependency x _ _ <- buildToolDepends ] ++ [ x | LegacyExeDependency x _ <- buildTools ])
convertSetupBuildInfo :: Cabal.SetupBuildInfo -> Nix.BuildInfo
convertSetupBuildInfo bi = mempty
& haskell .~ Set.fromList [ resolveInHackage (toNixName x) | (Dependency x _ _) <- Cabal.setupDepends bi ]
bindNull :: Identifier -> Binding
bindNull i = binding # (i, path # ["null"])