1
+ { haskellLib , pkgs } :
2
+ { callProjectResults , selectedCompiler } :
3
+ let
4
+ # Read the plan.json file `plan-nix` derivation
5
+ plan-json = builtins . fromJSON (
6
+ builtins . unsafeDiscardStringContext (
7
+ builtins . readFile ( callProjectResults . projectNix + "/plan.json" ) ) ) ;
8
+ # All the units in the plan indexed by unit ID.
9
+ by-id = pkgs . lib . listToAttrs ( map ( x : { name = x . id ; value = x ; } ) plan-json . install-plan ) ;
10
+ # Find the names of all the pre-existing packages used by a list of dependencies
11
+ # (includes transitive dependencies)
12
+ lookupPreExisting = depends :
13
+ pkgs . lib . concatMap ( d : builtins . attrNames pre-existing-depends . ${ d } ) depends ;
14
+ pre-existing-depends =
15
+ pkgs . lib . listToAttrs ( map ( p : {
16
+ name = p . id ;
17
+ value = pkgs . lib . optionalAttrs ( p . type == "pre-existing" ) { ${ p . pkg-name } = null ; } //
18
+ pkgs . lib . listToAttrs (
19
+ map ( dname : { name = dname ; value = null ; } ) ( lookupPreExisting ( p . depends or p . components . lib . depends ) ) ) ;
20
+ } ) plan-json . install-plan ) ;
21
+ # Lookup a dependency in `hsPkgs`
22
+ lookupDependency = hsPkgs : d :
23
+ pkgs . lib . optional ( by-id . ${ d } . type != "pre-existing" ) (
24
+ if by-id . ${ d } . component-name or "lib" == "lib"
25
+ then hsPkgs . ${ d } or hsPkgs . ${ by-id . ${ d } . pkg-name }
26
+ else hsPkgs . ${ d } . components . sublibs . ${ pkgs . lib . removePrefix "lib:" by-id . ${ d } . component-name } ) ;
27
+ # Lookup an executable dependency in `hsPkgs.pkgsBuildBuild`
28
+ lookupExeDependency = hsPkgs : d :
29
+ # Try to lookup by ID, but if that fails use the name (currently a different plan is used by pkgsBuildBuild when cross compiling)
30
+ ( hsPkgs . pkgsBuildBuild . ${ d } or hsPkgs . pkgsBuildBuild . ${ by-id . ${ d } . pkg-name } ) . components . exes . ${ pkgs . lib . removePrefix "exe:" by-id . ${ d } . component-name } ;
31
+ # Populate `depends`, `pre-existing` and `build-tools`
32
+ lookupDependencies = hsPkgs : depends : exe-depends : {
33
+ depends = pkgs . lib . concatMap ( lookupDependency hsPkgs ) depends ;
34
+ pre-existing = lookupPreExisting depends ;
35
+ build-tools = map ( lookupExeDependency hsPkgs ) exe-depends ;
36
+ } ;
37
+ # Calculate the packages for a component
38
+ getComponents = cabal2nixComponents : hsPkgs : p :
39
+ let
40
+ components = p . components or { ${ p . component-name or "lib" } = { inherit ( p ) depends ; exe-depends = p . exe-depends or [ ] ; } ; } ;
41
+ # Other than the `lib` and `setup` components, component names
42
+ # have a prefix based on their type.
43
+ componentsWithPrefix = collectionName : prefix :
44
+ pkgs . lib . listToAttrs ( pkgs . lib . concatLists ( pkgs . lib . mapAttrsToList ( n : c :
45
+ pkgs . lib . optional ( pkgs . lib . hasPrefix "${ prefix } :" n ) (
46
+ let
47
+ name = pkgs . lib . removePrefix "${ prefix } :" n ;
48
+ value = ( if cabal2nixComponents == null then { } else cabal2nixComponents . ${ collectionName } . ${ name } ) // {
49
+ buildable = true ;
50
+ } // lookupDependencies hsPkgs c . depends c . exe-depends ;
51
+ in { inherit name value ; }
52
+ ) ) components ) ) ;
53
+ in
54
+ pkgs . lib . mapAttrs componentsWithPrefix haskellLib . componentPrefix
55
+ // pkgs . lib . optionalAttrs ( components ? lib ) {
56
+ library = ( if cabal2nixComponents == null then { } else cabal2nixComponents . library ) // {
57
+ buildable = true ;
58
+ } // lookupDependencies hsPkgs components . lib . depends components . lib . exe-depends ;
59
+ } // pkgs . lib . optionalAttrs ( components ? setup ) {
60
+ setup = {
61
+ buildable = true ;
62
+ } // lookupDependencies hsPkgs . pkgsBuildBuild ( components . setup . depends or [ ] ) ( components . setup . exe-depends or [ ] ) ;
63
+ } ;
64
+ nixFilesDir = callProjectResults . projectNix + callProjectResults . src . origSubDir or "" ;
65
+ in {
66
+ # This replaces the `plan-nix/default.nix`
67
+ pkgs = ( hackage : {
68
+ packages = pkgs . lib . listToAttrs (
69
+ # Include entries for the `pre-existing` packages, but leave them as `null`
70
+ pkgs . lib . concatMap ( p :
71
+ pkgs . lib . optional ( p . type == "pre-existing" ) {
72
+ name = p . id ;
73
+ value . revision = null ;
74
+ } ) plan-json . install-plan
75
+ # The other packages that are not part of the project itself.
76
+ ++ pkgs . lib . concatMap ( p :
77
+ pkgs . lib . optional ( p . type == "configured" && ( p . style == "global" || p . style == "inplace" ) ) {
78
+ name = p . id ;
79
+ value . revision =
80
+ { hsPkgs , ...} @args :
81
+ let
82
+ # Read the output of `Cabal2Nix.hs`. We need it for information not
83
+ # in the `plan.json` file.
84
+ cabal2nix = (
85
+ if builtins . pathExists ( nixFilesDir + "/cabal-files/${ p . pkg-name } .nix" )
86
+ then import ( nixFilesDir + "/cabal-files/${ p . pkg-name } .nix" )
87
+ else if builtins . pathExists ( nixFilesDir + "/.plan.nix/${ p . pkg-name } .nix" )
88
+ then import ( nixFilesDir + "/.plan.nix/${ p . pkg-name } .nix" )
89
+ else ( ( ( hackage . ${ p . pkg-name } ) . ${ p . pkg-version } ) . revisions ) . default ) ( args // { hsPkgs = { } ; } ) ;
90
+ in pkgs . lib . optionalAttrs ( p ? pkg-src-sha256 ) {
91
+ sha256 = p . pkg-src-sha256 ;
92
+ } // pkgs . lib . optionalAttrs ( p . pkg-src . type or "" == "source-repo" ) {
93
+ # Replace the source repository packages with versions created when
94
+ # parsing the `cabal.project` file.
95
+ src = pkgs . lib . lists . elemAt callProjectResults . sourceRepos ( pkgs . lib . strings . toInt p . pkg-src . source-repo . location ) + "/${ p . pkg-src . source-repo . subdir } " ;
96
+ } // pkgs . lib . optionalAttrs ( cabal2nix ? package-description-override && p . pkg-version == cabal2nix . package . identifier . version ) {
97
+ # Use the `.cabal` file from the `Cabal2Nix` if it for the matching
98
+ # version of the package (the one in the plan).
99
+ inherit ( cabal2nix ) package-description-override ;
100
+ } // {
101
+ flags = p . flags ; # Use the flags from `plan.json`
102
+ components = getComponents cabal2nix . components hsPkgs p ;
103
+ package = cabal2nix . package // {
104
+ identifier = { name = p . pkg-name ; version = p . pkg-version ; id = p . id ; } ;
105
+ isProject = false ;
106
+ setup-depends = [ ] ; # The correct setup depends will be in `components.setup.depends`
107
+ } ;
108
+ } ;
109
+ } ) plan-json . install-plan ) ;
110
+ compiler = {
111
+ inherit ( selectedCompiler ) version ;
112
+ } ;
113
+ } ) ;
114
+ # Packages in the project (those that are both configure and local)
115
+ extras = ( _hackage : {
116
+ packages = pkgs . lib . listToAttrs (
117
+ pkgs . lib . concatMap ( p :
118
+ pkgs . lib . optional ( p . type == "configured" && p . style == "local" ) {
119
+ name = p . id ;
120
+ value =
121
+ { hsPkgs , ...} @args :
122
+ let cabal2nix = import ( nixFilesDir + "/.plan.nix/${ p . pkg-name } .nix" ) ( args // { hsPkgs = { } ; } ) ;
123
+ in pkgs . lib . optionalAttrs ( p ? pkg-src-sha256 ) {
124
+ sha256 = p . pkg-src-sha256 ;
125
+ } // pkgs . lib . optionalAttrs ( p . pkg-src . type or "" == "local" && cabal2nix ? cabal-generator ) {
126
+ inherit ( cabal2nix ) cabal-generator ;
127
+ } // pkgs . lib . optionalAttrs ( p . pkg-src . type or "" == "local" ) {
128
+ # Find the `src` location based on `p.pkg-src.path`
129
+ src = if pkgs . lib . hasPrefix "/" p . pkg-src . path
130
+ then p . pkg-src . path # Absolute path
131
+ else haskellLib . appendSubDir {
132
+ # Relative to the project path
133
+ inherit ( callProjectResults ) src ;
134
+ subDir = pkgs . lib . removePrefix "./" ( pkgs . lib . removePrefix "/" ( pkgs . lib . removeSuffix "/." ( pkgs . lib . removeSuffix "/." (
135
+ if pkgs . lib . hasPrefix ".${ callProjectResults . src . origSubDir or "" } /" ( p . pkg-src . path + "/" )
136
+ then pkgs . lib . removePrefix ".${ callProjectResults . src . origSubDir or "" } " p . pkg-src . path
137
+ else throw "Unexpected path ${ p . pkg-src . path } expected it to start with .${ callProjectResults . src . origSubDir or "" } " ) ) ) ) ;
138
+ includeSiblings = true ; # Filtering sibling dirs of the package dir is done in the
139
+ # component builder so that relative paths can be used to
140
+ # reference project directories not in the package subDir.
141
+ } ;
142
+ } // {
143
+ flags = p . flags ; # Use the flags from `plan.json`
144
+ components = getComponents cabal2nix . components hsPkgs p ;
145
+ package = cabal2nix . package // {
146
+ identifier = { name = p . pkg-name ; version = p . pkg-version ; id = p . id ; } ;
147
+ isProject = true ;
148
+ setup-depends = [ ] ; # The correct setup depends will be in `components.setup.depends`
149
+ } ;
150
+ } ;
151
+ } ) plan-json . install-plan ) ;
152
+ } ) ;
153
+ modules = [
154
+ { inherit plan-json ; }
155
+ ( import ../modules/install-plan/non-reinstallable.nix )
156
+ ( import ../modules/install-plan/override-package-by-name.nix )
157
+ ( import ../modules/install-plan/planned.nix { inherit getComponents ; } )
158
+ ( import ../modules/install-plan/redirect.nix )
159
+ ] ;
160
+ }
0 commit comments