forked from numtide/nix-filter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
default.nix
168 lines (150 loc) · 4.61 KB
/
default.nix
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
# This is a pure and self-contained library
rec {
# Default to filter when calling this lib.
__functor = self: filter;
# A proper source filter
filter =
{
# Base path to include
root
, # Derivation name
name ? "source"
, # Only include the following path matches.
#
# Allows all files by default.
include ? [ (_:_:_: true) ]
, # Ignore the following matches
exclude ? [ ]
}:
assert _pathIsDirectory root;
let
callMatcher = args: _toMatcher ({ inherit root; } // args);
include_ = map (callMatcher { matchParents = true; }) include;
exclude_ = map (callMatcher { matchParents = false; }) exclude;
in
builtins.path {
inherit name;
path = root;
filter = path: type:
(builtins.any (f: f path type) include_) &&
(!builtins.any (f: f path type) exclude_);
};
# Match a directory and any path inside of it
inDirectory =
directory:
args:
let
# Convert `directory` to a path to clean user input.
directory_ = _toCleanPath args.root directory;
in
path: type:
directory_ == path
# Add / to the end to make sure we match a full directory prefix
|| _hasPrefix (directory_ + "/") path;
# Match any directory
isDirectory = _: _: type: type == "directory";
# Combines matchers
and = a: b: args:
let
toMatcher = _toMatcher args;
in
path: type:
(toMatcher a path type) && (toMatcher b path type);
# Combines matchers
or_ = a: b: args:
let
toMatcher = _toMatcher args;
in
path: type:
(toMatcher a path type) || (toMatcher b path type);
# Or is actually a keyword, but can also be used as a key in an attrset.
or = or_;
# Match paths with the given extension
matchExt = ext:
args: path: type:
_hasSuffix ".${ext}" path;
# Filter out files or folders with this exact name
matchName = name:
root: path: type:
builtins.baseNameOf path == name;
# Wrap a matcher with this to debug its results
debugMatch = label: fn:
args: path: type:
let
ret = fn args path type;
retStr = if ret then "true" else "false";
in
builtins.trace "label=${label} path=${path} type=${type} ret=${retStr}"
ret;
# Add this at the end of the include or exclude, to trace all the unmatched paths
traceUnmatched = args: path: type:
builtins.trace "unmatched path=${path} type=${type}" false;
# Lib stuff
# If an argument to include or exclude is a path, transform it to a matcher.
#
# This probably needs more work, I don't think that it works on
# sub-folders.
_toMatcher = args: f:
let
path_ = _toCleanPath args.root f;
pathIsDirectory = _pathIsDirectory path_;
in
if builtins.isFunction f then f args
else path: type:
(if pathIsDirectory then
inDirectory path_ args path type
else
path_ == path) || args.matchParents
&& type == "directory"
&& _hasPrefix "${path}/" path_;
# Makes sure a path is:
# * absolute
# * doesn't contain superfluous slashes or ..
#
# Returns a string so there is no risk of adding it to the store by mistake.
_toCleanPath = absPath: path:
assert _pathIsDirectory absPath;
if builtins.isPath path then
toString path
else if builtins.isString path then
if builtins.substring 0 1 path == "/" then
path
else
toString (absPath + ("/" + path))
else
throw "unsupported type ${builtins.typeOf path}, expected string or path";
_hasSuffix =
# Suffix to check for
suffix:
# Input string
content:
let
lenContent = builtins.stringLength content;
lenSuffix = builtins.stringLength suffix;
in
lenContent >= lenSuffix
&& builtins.substring (lenContent - lenSuffix) lenContent content == suffix;
_hasPrefix =
# Prefix to check for
prefix:
# Input string
content:
let
lenPrefix = builtins.stringLength prefix;
in
prefix == builtins.substring 0 lenPrefix content;
# Returns true if the path exists and is a directory and false otherwise
_pathIsDirectory = p:
let
parent = builtins.dirOf p;
base = builtins.unsafeDiscardStringContext (builtins.baseNameOf p);
inNixStore = builtins.storeDir == toString parent;
in
# If the parent folder is /nix/store, we assume p is a directory. Because
# reading /nix/store is very slow, and not allowed in every environments.
inNixStore ||
(
builtins.pathExists p &&
(builtins.readDir parent).${builtins.unsafeDiscardStringContext base} == "directory"
);
}