on e94de40 help sign in
haskell.nix/lib/clean-source-with.nix
123 lines 5.5 kB history raw
1# This is a replacement for the cleanSourceWith function in nixpkgs
2# https://github.com/NixOS/nixpkgs/blob/1d9d31a0eb8e8358830528538a391df52f6a075a/lib/sources.nix#L41
3# It adds a subDir argument in a way that allows descending into a subdirectory
4# to compose with cleaning the source with a filter.
5{ lib }: rec {
7 # Like `builtins.filterSource`, except it will compose with itself,
8 # allowing you to chain multiple calls together without any
9 # intermediate copies being put in the nix store.
10 #
11 # lib.cleanSourceWith {
12 # filter = f;
13 # src = lib.cleanSourceWith {
14 # filter = g;
15 # src = ./.;
16 # };
17 # }
18 # # Succeeds!
19 #
20 # builtins.filterSource f (builtins.filterSource g ./.)
21 # # Fails!
22 #
23 # Parameters:
24 #
25 # src: A path or cleanSourceWith result to filter and/or rename.
26 #
27 # filter: A function (path -> type -> bool)
28 # Optional with default value: constant true (include everything)
29 # The function will be combined with the && operator such
30 # that src.filter is called lazily.
31 # For implementing a filter, see
32 # https://nixos.org/nix/manual/#builtin-filterSource
33 #
34 # subDir: Descend into a subdirectory in a way that will compose.
35 # It will be as if `src = src + "/${subDir}` and filters
36 # already applied to `src` will be respected.
37 #
38 # includeSiblings: By default the siblings trees of `subDir` are excluded.
39 # In some cases it is useful to include these so that
40 # relative references to those siblings will work.
41 #
42 # name: Optional name to use as part of the store path.
43 # If you do not provide a `name` it will be derived
44 # from the `subDir`. You should provide `name` or
45 # `subDir`. If you do not a warning will be displayed
46 # and the name used will be `source`.
47 #
48 # caller: Name of the function used in warning message.
49 # Functions that are implemented using `cleanSourceWith`
50 # (and forward a `name` argument) can use this to make
51 # the message to the use more meaningful.
52 #
53 cleanSourceWith = { filter ? _path: _type: true, src, subDir ? "", name ? null
54 , caller ? "cleanSourceWith", includeSiblings ? false }:
55 let
56 subDir' = if subDir == "" then "" else "/" + subDir;
57 subDirName = __replaceStrings ["/"] ["-"] subDir;
58 # In case this is mixed with older versions of cleanSourceWith
59 isFiltered = src ? _isLibCleanSourceWith;
60 isFilteredEx = src ? _isLibCleanSourceWithEx;
61 origSrc = if isFiltered || isFilteredEx then src.origSrc else src;
62 origSubDir = if isFilteredEx then src.origSubDir + subDir' else subDir';
63 origSrcSubDir = toString origSrc + origSubDir;
64 parentFilter = if isFiltered || isFilteredEx
65 then path: type: src.filter path type
66 else path: type: true;
67 filter' = path: type:
68 # Respect the parent filter
69 parentFilter path type && (
70 # Must include parent paths of the subdir.
71 (lib.strings.hasPrefix (path + "/") (origSrcSubDir + "/"))
72 ||
73 # Everything else is either the child tree or sibling tree.
74 ((includeSiblings || lib.strings.hasPrefix (origSrcSubDir + "/") path)
75 && filter path type # Use the filter function to decide if we need it
76 )
77 );
78 name' = if name != null
79 then name
80 else
81 if subDirName != ""
82 then if src ? name
83 then src.name + "-" + subDirName
84 else "source-" + subDirName
85 else if src ? name
86 then src.name
87 else
88 # No name was provided and one could not be constructed from
89 # the `subDirName`.
91 # We used to use `baseNameOf src` as a default here.
92 # This was cute, but it lead to cache misses. For instance if
93 # `x = cleanSourceWith { src = ./.; }` then `baseName src`
94 # will be different when `src` resolves to "/nix/store/X"
95 # than when it is in a local directory. If people use
96 # git worktrees they also may wind up with different
97 # values for `name`. Anything that depends on `x.name` will
98 # propagate the issue.
100 # Encourage adding a suitable `name` with:
101 # * A warning message.
102 # * A default name that gives a hint as to why there is no name.
103 __trace (
104 "WARNING: `${caller}` called on ${toString src} without a `name`. "
105 + "Consider adding `name = \"${baseNameOf src}\";`") "source";
106 in {
107 inherit origSrc origSubDir origSrcSubDir;
108 filter = filter';
109 outPath = (builtins.path { filter = filter'; path = origSrc; name = name'; }) + origSubDir;
110 _isLibCleanSourceWithEx = true;
111 # It is only safe for older cleanSourceWith to filter this one
112 # if the we are still looking at the root of origSrc
113 _isLibCleanSourceWith = origSubDir == "";
114 name = name';
115 };
117 pathHasContext = builtins.hasContext or (lib.hasPrefix builtins.storeDir);
119 canCleanSource = src:
120 src ? _isLibCleanSourceWithEx
121 || src ? _isLibCleanSourceWith
122 || !(pathHasContext (toString src));