1{ pkgs, stdenv, lib, haskellLib, recurseIntoAttrs, srcOnly }:
7 # Why `final.evalPackages.buildPackages.gitMinimal`?
8 # Why not just final.evalPackages.gitMinimal?
10 # A problem arises when `evalPackages` is `buildPackages`.i
11 # As may be the case in a flake.
13 # It turns out `git` depends on `gdb` in a round about way:
14 # git -> openssh -> libfido2 -> systemd -> python libxml -> Cython -> gdb
15 # Somewhere in that chain there should perhaps be a `buildPackages` so
16 # that the `gdb` that is used is not the one for debugging code in
17 # the `final` (but instead the one for debugging code in
18 # `final.buildPackages`).
20 # Using `final.buildPackages.git` causes two problems:
22 # * Multiple versions of `git` (and that dependency chain
23 # to `gdb` are needed when cross compiling).
24 # * When `gdb` does not exist for `js`, so when cross
25 # compiling with ghcjs `final.buildPackages.git` fails
27 inherit (pkgs.evalPackages.buildPackages) gitMinimal;
30 # Within the package components, these are the attribute names of
40 foldrAttrVals = f: z: attrs:
41 lib.foldr (g: acc: g acc) z (lib.mapAttrsToList (_name: f) attrs);
43 foldComponents = tys: f: z: conf:
45 comps = conf.components or {};
46 # ensure that comps.library exists and is not null.
47 libComp = acc: if (comps.library or null) != null then f comps.library acc else acc;
50 (ty: acc': foldrAttrVals f acc' (comps.${ty} or {}))
53 in libComp (subComps z);
55 getAllComponents = foldComponents subComponentTypes (c: acc: [c] ++ acc) [];
65 applyComponents = f: config:
67 comps = config.components;
68 applyLibrary = cname: f { cname = config.package.identifier.name; ctype = "lib"; };
69 applySubComp = ctype: cname: f { inherit cname; ctype = componentPrefix.${ctype} or (throw "Missing component mapping for ${ctype}."); };
70 buildableAttrs = lib.filterAttrs (n: comp: comp.buildable or true);
71 libComp = if comps.library == null || !(comps.library.buildable or true)
73 else lib.mapAttrs applyLibrary (removeAttrs comps (subComponentTypes ++ [ "setup" ]));
74 subComps = lib.mapAttrs
75 (ctype: attrs: lib.mapAttrs (applySubComp ctype) (buildableAttrs attrs))
76 (builtins.intersectAttrs (lib.genAttrs subComponentTypes (_: null)) comps);
77 in subComps // libComp;
79 isLibrary = componentId: componentId.ctype == "lib";
80 isExe = componentId: componentId.ctype == "exe";
81 isTest = componentId: componentId.ctype == "test";
82 isBenchmark = componentId: componentId.ctype == "bench";
83 isExecutableType = componentId:
86 || isBenchmark componentId;
87 mayHaveExecutable = componentId:
88 isExecutableType componentId;
90 # Was there a reference to the package source in the `cabal.project` or `stack.yaml` file.
91 # This is used to make the default `packages` list for `shellFor`.
92 isLocalPackage = p: p.isLocal or false;
93 selectLocalPackages = ps: lib.filterAttrs (n: p: p != null && isLocalPackage p) ps;
95 # if it's a project package it has a src attribute set with an origSubDir attribute.
96 # project packages are a subset of localPackages
97 isProjectPackage = p: p ? src && p.src ? origSubDir;
98 selectProjectPackages = ps: lib.filterAttrs (n: p: p != null && isLocalPackage p && isProjectPackage p) ps;
100 # Format a componentId as it should appear as a target on the
101 # command line of the setup script.
102 componentTarget = componentId:"${componentId.ctype}:${componentId.cname}";
104 # Remove null or empty values from an attrset.
105 optionalHooks = lib.filterAttrs (_: hook: hook != null && hook != "");
107 # Avoid pkgs.callPackage for now. It does a lot of nonsense with OOP
108 # style programming that we should avoid until we know we want it.
110 # weakCallPackage: call a function or (importable expression)
113 # weakCallPackage scope f args
114 # will call f (scope // args)
116 # weakCallpackage scope ./path args
117 # will call the expression at ./path with (scope // args)
119 weakCallPackage = scope: f: args:
120 let f' = if lib.isFunction f then f else import f;
121 args' = (builtins.intersectAttrs (builtins.functionArgs f') scope) // args;
124 # Collect all (transitive) Haskell library dependencies of a
126 ## flatLibDepends :: Component -> [Package]
127 flatLibDepends = component:
129 # this is a minor improvement over the "cannot coerce set to string"
130 # error. It will now say:
132 # > The option `packages.Win32.package.identifier.name' is used but not defined.
134 # which indicates that the package.Win32 is missing and not defined.
135 getKey = x: if x ? "outPath" then "${x}" else (throw x.identifier.name);
136 makePairs = map (p: rec { key=getKey val; val=(p.components.library or p); });
137 closure = builtins.genericClosure {
138 startSet = makePairs component.depends;
139 operator = {val,...}: makePairs val.config.depends;
141 in map ({val,...}: val) closure;
144 # Extracts a selection of components from a Haskell package set.
146 # This can be used to filter out all test suites or benchmarks of
147 # your project, so that they can be built in Hydra.
151 # tests = collectComponents "tests" (package: package.identifier.name == "mypackage") hsPkgs;
153 # Will result in moving:
154 # from: hsPkgs.mypackage.components.tests.unit-tests
155 # to: tests.mypackage.unit-tests
157 collectComponents = group: packageSel: haskellPackages:
158 let packageToComponents = name: package:
159 # look for the components with this group if there are any
160 let components = package.components.${group} or {};
161 # set recurseForDerivations unless it's a derivation itself (e.g. the "library" component) or an empty set
162 in if lib.isDerivation components || components == {}
164 else recurseIntoAttrs components;
165 packageFilter = name: package: (package.isHaskell or false) && packageSel package;
166 filteredPkgs = lib.filterAttrs packageFilter haskellPackages;
167 # at this point we can filter out packages that don't have any of the given kind of component
168 packagesByComponent = lib.filterAttrs (_: components: components != {}) (lib.mapAttrs packageToComponents filteredPkgs);
169 in recurseIntoAttrs packagesByComponent;
171 # Equivalent to collectComponents with (_: true) as selection function.
172 # Useful for pre-filtered package-set.
176 # myHaskellPackages = selectProjectPackages hsPkgs;
177 # myTests = collectComponents' "tests" myHaskellPackages;
178 collectComponents' = group: collectComponents group (_: true);
180 # Extracts a selection of 'checks' from a Haskell package set.
182 # This can be used to collect all the test runs in your project, so that can be run in CI.
183 collectChecks = packageSel: haskellPackages:
184 let packageFilter = name: package: (package.isHaskell or false) && packageSel package;
185 in recurseIntoAttrs (lib.mapAttrs (_: p: p.checks) (lib.filterAttrs packageFilter haskellPackages));
187 # Equivalent to collectChecks with (_: true) as selection function.
188 # Useful for pre-filtered package-set.
189 collectChecks' = collectChecks (_: true);
191 # Replacement for lib.cleanSourceWith that has a subDir argument.
192 inherit (import ./clean-source-with.nix { inherit lib; }) cleanSourceWith canCleanSource;
194 # Use cleanSourceWith to filter just the files needed for a particular
195 # component of a package
196 cleanCabalComponent = import ./clean-cabal-component.nix { inherit lib cleanSourceWith; };
198 # Clean git directory based on `git ls-files --recurse-submodules`
199 cleanGit = import ./clean-git.nix {
200 inherit lib cleanSourceWith;
202 inherit (pkgs.evalPackages) runCommand;
205 # Some times it is handy to temporarily use a relative path between git
206 # repos. If the repos are individually cleaned this is not possible
207 # (since the cleaned version of one repo will never include the files
210 # `cleanGits` allows us to specify a root directory and any number of
211 # sub directories containing git repos.
213 # See docs/user-guide/clean-git.md for details of how to use this
214 # with `cabalProject`.
215 cleanGits = { src, gitDirs, name ? null, caller ? "cleanGits" }@args:
217 # List of filters, one for each git directory.
218 filters = builtins.map (subDir:
219 (pkgs.haskell-nix.haskellLib.cleanGit {
220 src = pkgs.haskell-nix.haskellLib.cleanSourceWith {
224 in pkgs.haskell-nix.haskellLib.cleanSourceWith {
225 inherit src name caller;
226 # Keep files that match any of the filters
227 filter = path: type: pkgs.lib.any (f: f path type) filters;
230 # Check a test component
231 check = import ./check.nix {
232 inherit stdenv lib haskellLib srcOnly;
235 # Do coverage of a package
236 coverageReport = import ./cover.nix {
237 inherit stdenv lib haskellLib pkgs;
240 # Do coverage of a project
241 projectCoverageReport = import ./cover-project.nix {
242 inherit stdenv lib haskellLib pkgs;
245 # Use `isCrossHost` to identify when we are cross compiling and
246 # the code we are producing will not run on the build system
247 # without an emulator.
248 # In most cases we do not want to treat musl as a cross compiler.
249 # For instance when building ghc we want to include ghci.
250 isCrossHost = stdenv.hostPlatform != stdenv.buildPlatform
251 && !(stdenv.buildPlatform.isLinux && stdenv.hostPlatform.isMusl && stdenv.buildPlatform.isx86 && stdenv.hostPlatform.isx86);
252 # This is the same as isCrossHost but for use when building ghc itself
253 isCrossTarget = stdenv.targetPlatform != stdenv.hostPlatform
254 && !(stdenv.hostPlatform.isLinux && stdenv.targetPlatform.isMusl && stdenv.hostPlatform.isx86 && stdenv.targetPlatform.isx86);
255 # Native musl build-host-target combo
256 isNativeMusl = stdenv.hostPlatform.isMusl
257 && stdenv.buildPlatform == stdenv.hostPlatform
258 && stdenv.hostPlatform == stdenv.targetPlatform;
260 # Takes a version number or attr set of arguments (for cabalProject)
261 # and converts it to an attr set of arguments. This allows
262 # the use of "1.0.0.0" or { version = "1.0.0.0"; ... }
263 versionOrArgsToArgs = versionOrArgs:
264 if lib.isAttrs versionOrArgs
266 else { version = versionOrArgs; };
268 # Find the resolver in the stack.yaml file and fetch it if a sha256 value is provided
269 fetchResolver = import ./fetch-resolver.nix {
270 inherit (pkgs.evalPackages) pkgs;
273 inherit (import ./cabal-project-parser.nix {
275 }) parseIndexState parseBlock;
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