authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-18 20:34:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:35-07:00
logdb7ceada15b007747bfd433e6635324edcec918e
tree9f435ee5831fbe1ccefc760e5bf6d20599180bc6
parent398ea7e492b7016dcb6cebfe6656876002f5bf73

Maker: implement Step.WriteFile


3 files changed, 235 insertions(+), 151 deletions(-)

lib/compiler/Maker/Step.zig+14-21
......@@ -26,6 +26,7 @@ pub const ObjCopy = @import("Step/ObjCopy.zig");
2626pub const Options = @import("Step/Options.zig");
2727pub const Run = @import("Step/Run.zig");
2828pub const UpdateSourceFiles = @import("Step/UpdateSourceFiles.zig");
29pub const WriteFile = @import("Step/WriteFile.zig");
2930
3031/// Avoid false sharing.
3132_: void align(std.atomic.cache_line) = {},
......@@ -87,7 +88,7 @@ pub const Extended = union(enum) {
8788 top_level: TopLevel,
8889 translate_c: Todo,
8990 update_source_files: UpdateSourceFiles,
90 write_file: Todo,
91 write_file: WriteFile,
9192
9293 pub fn init(tag: Configuration.Step.Tag) Extended {
9394 return switch (tag) {
......@@ -119,9 +120,10 @@ pub const Extended = union(enum) {
119120 progress_node: std.Progress.Node,
120121 ) Step.ExtendedMakeError!void {
121122 _ = todo;
122 _ = maker;
123123 _ = progress_node;
124 std.debug.panic("TODO implement another step type (index {d})", .{step_index});
124 const conf = &maker.scanned_config.configuration;
125 const conf_step = step_index.ptr(conf);
126 std.debug.panic("TODO implement another step type: {s}", .{conf_step.name.slice(conf)});
125127 }
126128 };
127129
......@@ -807,19 +809,17 @@ pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: La
807809/// Paths derived from this directory should also be manually added via
808810/// `addDirectoryWatchInputFromPath` if and only if this function returns
809811/// `true`.
810pub fn addDirectoryWatchInput(step: *Step, lazy_directory: LazyPath) Allocator.Error!bool {
812pub fn addDirectoryWatchInput(step: *Step, maker: *Maker, lazy_directory: LazyPath) Allocator.Error!bool {
811813 switch (lazy_directory) {
812 .src_path => |src_path| try addDirectoryWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),
813 .dependency => |d| try addDirectoryWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),
814 .cwd_relative => |path_string| {
815 try addDirectoryWatchInputFromPath(step, .{
816 .root_dir = .{
817 .path = null,
818 .handle = .cwd(),
819 },
820 .sub_path = path_string,
821 });
814 .source_path => |source_path| {
815 const conf = &maker.scanned_config.configuration;
816 const graph = maker.graph;
817 const arena = graph.arena; // TODO don't leak into the process arena
818 const sub_path = source_path.sub_path.slice(conf);
819 const pkg_path = try maker.packagePath(arena, source_path.owner, sub_path);
820 try addDirectoryWatchInputFromPath(step, maker, pkg_path);
822821 },
822 .relative => |relative| try addDirectoryWatchInputFromPath(step, maker, maker.relativePath(relative)),
823823 // Nothing to watch because this dependency edge is modeled instead via `dependants`.
824824 .generated => return false,
825825 }
......@@ -838,13 +838,6 @@ pub fn addDirectoryWatchInputFromPath(step: *Step, maker: *Maker, path: Path) !v
838838 return addWatchInputFromPath(step, maker, path, ".");
839839}
840840
841fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {
842 return addDirectoryWatchInputFromPath(step, .{
843 .root_dir = package.build_root,
844 .sub_path = sub_path,
845 });
846}
847
848841fn addWatchInputPath(step: *Step, maker: *Maker, path: Path) Allocator.Error!void {
849842 return addWatchInputFromPath(step, maker, .{
850843 .root_dir = path.root_dir,
lib/compiler/Maker/Step/UpdateSourceFiles.zig+7-5
......@@ -32,6 +32,8 @@ pub fn make(
3232
3333 progress_node.setEstimatedTotalItems(conf_usf.embeds.slice.len + conf_usf.copies.slice.len);
3434
35 step.clearWatchInputs(maker);
36
3537 for (conf_usf.embeds.slice) |*embed| {
3638 const dest_path: Path = .{
3739 .root_dir = build_root,
......@@ -43,12 +45,12 @@ pub fn make(
4345 .sub_path = dirname,
4446 };
4547 dirname_path.root_dir.handle.createDirPath(io, dirname_path.sub_path) catch |err|
46 return step.fail(maker, "failed to create path {f}: {t}", .{ dirname_path, err });
48 return step.fail(maker, "failed creating path {f}: {t}", .{ dirname_path, err });
4749 }
4850 dest_path.root_dir.handle.writeFile(io, .{
4951 .sub_path = dest_path.sub_path,
5052 .data = embed.contents.slice(conf),
51 }) catch |err| return step.fail(maker, "failed to write file {f}: {t}", .{ dest_path, err });
53 }) catch |err| return step.fail(maker, "failed writing file {f}: {t}", .{ dest_path, err });
5254 any_miss = true;
5355 progress_node.completeOne();
5456 }
......@@ -64,11 +66,11 @@ pub fn make(
6466 .sub_path = dirname,
6567 };
6668 dirname_path.root_dir.handle.createDirPath(io, dirname_path.sub_path) catch |err|
67 return step.fail(maker, "failed to create path {f}: {t}", .{ dirname_path, err });
69 return step.fail(maker, "failed creating path {f}: {t}", .{ dirname_path, err });
6870 }
6971 const src_lazy_path = copy.src_file.get(conf);
7072 const source_path = try maker.resolveLazyPath(arena, src_lazy_path, step_index);
71 if (!step.inputs.populated()) try step.addWatchInput(maker, arena, src_lazy_path);
73 try step.addWatchInput(maker, arena, src_lazy_path);
7274
7375 const prev_status = source_path.root_dir.handle.updateFile(
7476 io,
......@@ -76,7 +78,7 @@ pub fn make(
7678 dest_path.root_dir.handle,
7779 dest_path.sub_path,
7880 .{},
79 ) catch |err| return step.fail(maker, "unable to update file from {f} to {f}: {t}", .{
81 ) catch |err| return step.fail(maker, "failed updating file from {f} to {f}: {t}", .{
8082 source_path, dest_path, err,
8183 });
8284 any_miss = any_miss or prev_status == .stale;
lib/compiler/Maker/Step/WriteFile.zig+214-125
......@@ -1,205 +1,294 @@
1fn make(step: *Step, options: Step.MakeOptions) !void {
2 _ = options;
3 const b = step.owner;
4 const graph = b.graph;
1const WriteFile = @This();
2
3const std = @import("std");
4const Io = std.Io;
5const assert = std.debug.assert;
6const Path = std.Build.Cache.Path;
7const allocPrint = std.fmt.allocPrint;
8const Configuration = std.Build.Configuration;
9
10const Step = @import("../Step.zig");
11const Maker = @import("../../Maker.zig");
12
13pub fn make(
14 wf: *WriteFile,
15 step_index: Configuration.Step.Index,
16 maker: *Maker,
17 progress_node: std.Progress.Node,
18) Step.ExtendedMakeError!void {
19 _ = wf;
20 const graph = maker.graph;
21 const gpa = maker.gpa;
22 const arena = maker.graph.arena; // TODO don't leak into process arena
523 const io = graph.io;
6 const arena = b.allocator;
7 const gpa = graph.cache.gpa;
8 const write_file: *WriteFile = @fieldParentPtr("step", step);
24 const step = maker.stepByIndex(step_index);
25 const conf = &maker.scanned_config.configuration;
26 const conf_step = step_index.ptr(conf);
27 const conf_wf = conf_step.extended.get(conf.extra).write_file;
28 const cache_root = graph.local_cache_root;
29 const directories = conf_wf.directories.slice;
930
10 const open_dir_cache = try arena.alloc(Io.Dir, write_file.directories.items.len);
11 var open_dirs_count: usize = 0;
31 const open_dir_cache = try arena.alloc(Io.Dir, directories.len);
32 var open_dirs_count: u32 = 0;
1233 defer Io.Dir.closeMany(io, open_dir_cache[0..open_dirs_count]);
1334
14 switch (write_file.mode) {
15 .whole_cached => {
16 step.clearWatchInputs();
35 // Doesn't yet include contents of directories.
36 var total_items: usize = conf_wf.embeds.slice.len + conf_wf.copies.slice.len + conf_wf.directories.slice.len;
37 progress_node.setEstimatedTotalItems(total_items);
1738
18 // The cache is used here not really as a way to speed things up - because writing
19 // the data to a file would probably be very fast - but as a way to find a canonical
20 // location to put build artifacts.
39 switch (conf_wf.flags.mode) {
40 .whole_cached => {
41 step.clearWatchInputs(maker);
2142
22 // If, for example, a hard-coded path was used as the location to put WriteFile
23 // files, then two WriteFiles executing in parallel might clobber each other.
43 // The cache is used here primarily as a way to find a canonical
44 // location to put build artifacts without parallel step execution
45 // clobbering each other.
2446
25 var man = b.graph.cache.obtain();
47 var man = graph.cache.obtain();
2648 defer man.deinit();
2749
28 for (write_file.files.items) |file| {
29 man.hash.addBytes(file.sub_path);
30
31 switch (file.contents) {
32 .bytes => |bytes| {
33 man.hash.addBytes(bytes);
34 },
35 .copy => |lazy_path| {
36 const path = lazy_path.getPath3(b, step);
37 _ = try man.addFilePath(path, null);
38 try step.addWatchInput(lazy_path);
39 },
40 }
50 for (conf_wf.embeds.slice) |*embed| {
51 man.hash.addBytes(embed.sub_path.slice(conf));
52 man.hash.addBytes(embed.contents.slice(conf));
53 }
54
55 for (conf_wf.copies.slice) |*copy| {
56 man.hash.addBytes(copy.sub_path.slice(conf));
57 const src_lazy_path = copy.src_file.get(conf);
58 const source_path = try maker.resolveLazyPath(arena, src_lazy_path, step_index);
59 _ = try man.addFilePath(source_path, null);
60 try step.addWatchInput(maker, arena, src_lazy_path);
4161 }
4262
43 for (write_file.directories.items, open_dir_cache) |dir, *open_dir_cache_elem| {
44 man.hash.addBytes(dir.sub_path);
45 for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext);
46 if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc);
63 for (directories, open_dir_cache) |conf_dir, *opened_dir| {
64 const exclude_extensions = conf_dir.exclude_extensions.slice(conf) orelse &.{};
65 const include_extensions = conf_dir.include_extensions.slice(conf);
66
67 man.hash.addBytes(conf_dir.sub_path.slice(conf));
68 for (exclude_extensions) |ext| man.hash.addBytes(ext.slice(conf));
69 if (include_extensions) |includes| for (includes) |inc| {
70 man.hash.addBytes(inc.slice(conf));
71 };
4772
48 const need_derived_inputs = try step.addDirectoryWatchInput(dir.source);
49 const src_dir_path = dir.source.getPath3(b, step);
73 const src_lazy_path = conf_dir.src_path.get(conf);
74 const need_derived_inputs = try step.addDirectoryWatchInput(maker, src_lazy_path);
75 const src_dir_path = try maker.resolveLazyPath(arena, src_lazy_path, step_index);
5076
5177 var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
52 return step.fail("unable to open source directory '{f}': {s}", .{
53 src_dir_path, @errorName(err),
54 });
78 return step.fail(maker, "failed opening source directory {f}: {t}", .{ src_dir_path, err });
5579 };
56 open_dir_cache_elem.* = src_dir;
80 opened_dir.* = src_dir;
5781 open_dirs_count += 1;
5882
5983 var it = try src_dir.walk(gpa);
6084 defer it.deinit();
61 while (try it.next(io)) |entry| {
62 if (!dir.options.pathIncluded(entry.path)) continue;
85 while (it.next(io) catch |err| switch (err) {
86 error.Canceled, error.OutOfMemory => |e| return e,
87 else => |e| return step.fail(maker, "failed iterating dir {f}: {t}", .{ src_dir_path, e }),
88 }) |entry| {
89 if (!pathIncluded(conf, exclude_extensions, include_extensions, entry.path)) continue;
6390
6491 switch (entry.kind) {
6592 .directory => {
6693 if (need_derived_inputs) {
6794 const entry_path = try src_dir_path.join(arena, entry.path);
68 try step.addDirectoryWatchInputFromPath(entry_path);
95 try step.addDirectoryWatchInputFromPath(maker, entry_path);
6996 }
7097 },
7198 .file => {
7299 const entry_path = try src_dir_path.join(arena, entry.path);
73100 _ = try man.addFilePath(entry_path, null);
101 total_items += 1;
74102 },
75103 else => continue,
76104 }
77105 }
78106 }
79107
80 if (try step.cacheHit(&man)) {
108 if (try step.cacheHit(maker, &man)) {
81109 const digest = man.final();
82 write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest });
110 maker.generatedPath(conf_wf.generated_directory).* = .{
111 .root_dir = cache_root,
112 .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest }),
113 };
83114 assert(step.result_cached);
84115 return;
85116 }
86117
87118 const digest = man.final();
88 const cache_path = "o" ++ Dir.path.sep_str ++ digest;
89
90 write_file.generated_directory.path = try b.cache_root.join(arena, &.{cache_path});
119 const out_path: Path = .{
120 .root_dir = cache_root,
121 .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest }),
122 };
91123
92 try operate(write_file, open_dir_cache, .{
93 .root_dir = b.cache_root,
94 .sub_path = cache_path,
95 });
124 progress_node.setEstimatedTotalItems(total_items);
125 try operate(maker, step_index, open_dir_cache, out_path, progress_node);
126 try step.writeManifest(maker, &man);
96127
97 try step.writeManifest(&man);
128 maker.generatedPath(conf_wf.generated_directory).* = out_path;
98129 },
99130 .tmp => {
100131 step.result_cached = false;
101132
102133 var rand_int: u64 = undefined;
103134 io.random(@ptrCast(&rand_int));
104 const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
135 const hex_digest = std.fmt.hex(rand_int);
105136
106 write_file.generated_directory.path = try b.cache_root.join(arena, &.{tmp_dir_sub_path});
137 const out_path: Path = .{
138 .root_dir = cache_root,
139 .sub_path = try Io.Dir.path.join(arena, &.{ "tmp", &hex_digest }),
140 };
107141
108 try operate(write_file, open_dir_cache, .{
109 .root_dir = b.cache_root,
110 .sub_path = tmp_dir_sub_path,
111 });
142 try operate(maker, step_index, open_dir_cache, out_path, progress_node);
143
144 maker.generatedPath(conf_wf.generated_directory).* = out_path;
112145 },
113 .mutate => |lp| {
146 .mutate => {
114147 step.result_cached = false;
115 const root_path = try lp.getPath4(b, step);
116 write_file.generated_directory.path = try root_path.toString(arena);
117 try operate(write_file, open_dir_cache, root_path);
148 const root_path = try maker.resolveLazyPathIndex(arena, conf_wf.mutate_path.value.?, step_index);
149 try operate(maker, step_index, open_dir_cache, root_path, progress_node);
150 maker.generatedPath(conf_wf.generated_directory).* = root_path;
118151 },
119152 }
120153}
121154
122fn operate(write_file: *WriteFile, open_dir_cache: []const Io.Dir, root_path: std.Build.Cache.Path) !void {
123 const step = &write_file.step;
124 const b = step.owner;
125 const io = b.graph.io;
126 const gpa = b.graph.cache.gpa;
127 const arena = b.allocator;
128
129 var cache_dir = root_path.root_dir.handle.createDirPathOpen(io, root_path.sub_path, .{}) catch |err|
130 return step.fail("unable to make path {f}: {t}", .{ root_path, err });
131 defer cache_dir.close(io);
132
133 for (write_file.files.items) |file| {
134 if (Dir.path.dirname(file.sub_path)) |dirname| {
135 cache_dir.createDirPath(io, dirname) catch |err| {
136 return step.fail("unable to make path '{f}{c}{s}': {t}", .{
137 root_path, Dir.path.sep, dirname, err,
138 });
155fn operate(
156 maker: *Maker,
157 step_index: Configuration.Step.Index,
158 open_dir_cache: []const Io.Dir,
159 root_path: std.Build.Cache.Path,
160 progress_node: std.Progress.Node,
161) !void {
162 const graph = maker.graph;
163 const gpa = maker.gpa;
164 const arena = maker.graph.arena; // TODO don't leak into process arena
165 const io = graph.io;
166 const step = maker.stepByIndex(step_index);
167 const conf = &maker.scanned_config.configuration;
168 const conf_step = step_index.ptr(conf);
169 const conf_wf = conf_step.extended.get(conf.extra).write_file;
170
171 const root_directory: std.Build.Cache.Directory = .{
172 .handle = root_path.root_dir.handle.createDirPathOpen(io, root_path.sub_path, .{}) catch |err|
173 return step.fail(maker, "failed creating path {f}: {t}", .{ root_path, err }),
174 .path = try root_path.toString(arena),
175 };
176 defer root_directory.handle.close(io);
177
178 for (conf_wf.embeds.slice) |*embed| {
179 const dest_path: Path = .{
180 .root_dir = root_directory,
181 .sub_path = embed.sub_path.slice(conf),
182 };
183 if (Io.Dir.path.dirname(dest_path.sub_path)) |dirname| {
184 const dirname_path: Path = .{
185 .root_dir = root_directory,
186 .sub_path = dirname,
139187 };
188 dirname_path.root_dir.handle.createDirPath(io, dirname_path.sub_path) catch |err|
189 return step.fail(maker, "failed creating path {f}: {t}", .{ dirname_path, err });
140190 }
141 switch (file.contents) {
142 .bytes => |bytes| {
143 cache_dir.writeFile(io, .{ .sub_path = file.sub_path, .data = bytes }) catch |err| {
144 return step.fail("unable to write file '{f}{c}{s}': {t}", .{
145 root_path, Dir.path.sep, file.sub_path, err,
146 });
147 };
148 },
149 .copy => |file_source| {
150 const source_path = file_source.getPath2(b, step);
151 const prev_status = Io.Dir.updateFile(.cwd(), io, source_path, cache_dir, file.sub_path, .{}) catch |err| {
152 return step.fail("unable to update file from '{s}' to '{f}{c}{s}': {t}", .{
153 source_path, root_path, Dir.path.sep, file.sub_path, err,
154 });
155 };
156 // At this point we already will mark the step as a cache miss.
157 // But this is kind of a partial cache hit since individual
158 // file copies may be avoided. Oh well, this information is
159 // discarded.
160 _ = prev_status;
161 },
191 dest_path.root_dir.handle.writeFile(io, .{
192 .sub_path = dest_path.sub_path,
193 .data = embed.contents.slice(conf),
194 }) catch |err| return step.fail(maker, "failed writing contents to file {f}: {t}", .{ dest_path, err });
195 progress_node.completeOne();
196 }
197
198 for (conf_wf.copies.slice) |*copy| {
199 const dest_path: Path = .{
200 .root_dir = root_directory,
201 .sub_path = copy.sub_path.slice(conf),
202 };
203 // Rather than passing make_path = true below, this optimizes for the
204 // more common case where the directory does not exist.
205 if (Io.Dir.path.dirname(dest_path.sub_path)) |dirname| {
206 const dirname_path: Path = .{
207 .root_dir = root_directory,
208 .sub_path = dirname,
209 };
210 dirname_path.root_dir.handle.createDirPath(io, dirname_path.sub_path) catch |err|
211 return step.fail(maker, "failed creating path {f}: {t}", .{ dirname_path, err });
162212 }
213 const source_path = try maker.resolveLazyPathIndex(arena, copy.src_file, step_index);
214 Io.Dir.copyFile(
215 source_path.root_dir.handle,
216 source_path.sub_path,
217 dest_path.root_dir.handle,
218 dest_path.sub_path,
219 io,
220 .{},
221 ) catch |err| return step.fail(maker, "failed copying file from {f} to {f}: {t}", .{
222 source_path, dest_path, err,
223 });
224 progress_node.completeOne();
163225 }
164226
165 for (write_file.directories.items, open_dir_cache) |dir, already_open_dir| {
166 const src_dir_path = dir.source.getPath3(b, step);
167 const dest_dirname = dir.sub_path;
227 for (conf_wf.directories.slice, open_dir_cache) |conf_dir, already_open_dir| {
228 const exclude_extensions = conf_dir.exclude_extensions.slice(conf) orelse &.{};
229 const include_extensions = conf_dir.include_extensions.slice(conf);
168230
169 if (dest_dirname.len != 0) {
170 cache_dir.createDirPath(io, dest_dirname) catch |err| {
171 return step.fail("unable to make path '{f}{c}{s}': {t}", .{
172 root_path, Dir.path.sep, dest_dirname, err,
173 });
174 };
231 const src_dir_path = try maker.resolveLazyPathIndex(arena, conf_dir.src_path, step_index);
232 const dest_dir_path: Path = .{
233 .root_dir = root_directory,
234 .sub_path = conf_dir.sub_path.slice(conf),
235 };
236
237 if (dest_dir_path.sub_path.len != 0) {
238 dest_dir_path.root_dir.handle.createDirPath(io, dest_dir_path.sub_path) catch |err|
239 return step.fail(maker, "failed creating path {f}: {t}", .{ dest_dir_path, err });
175240 }
176241
177242 var it = try already_open_dir.walk(gpa);
178243 defer it.deinit();
179 while (try it.next(io)) |entry| {
180 if (!dir.options.pathIncluded(entry.path)) continue;
244 while (it.next(io) catch |err| switch (err) {
245 error.Canceled, error.OutOfMemory => |e| return e,
246 else => |e| return step.fail(maker, "failed iterating dir {f}: {t}", .{ src_dir_path, e }),
247 }) |entry| {
248 if (!pathIncluded(conf, exclude_extensions, include_extensions, entry.path)) continue;
181249
182250 const src_entry_path = try src_dir_path.join(arena, entry.path);
183 const dest_path = b.pathJoin(&.{ dest_dirname, entry.path });
251 const dest_path = try dest_dir_path.join(arena, entry.path);
184252 switch (entry.kind) {
185 .directory => try cache_dir.createDirPath(io, dest_path),
253 .directory => dest_path.root_dir.handle.createDirPath(io, dest_path.sub_path) catch |err| {
254 return step.fail(maker, "failed creating path {f}: {t}", .{ dest_path, err });
255 },
186256 .file => {
187 const prev_status = Io.Dir.updateFile(
257 Io.Dir.copyFile(
188258 src_entry_path.root_dir.handle,
189 io,
190259 src_entry_path.sub_path,
191 cache_dir,
192 dest_path,
260 dest_path.root_dir.handle,
261 dest_path.sub_path,
262 io,
193263 .{},
194 ) catch |err| {
195 return step.fail("unable to update file from '{f}' to '{f}{c}{s}': {t}", .{
196 src_entry_path, root_path, Dir.path.sep, dest_path, err,
197 });
198 };
199 _ = prev_status;
264 ) catch |err| return step.fail(maker, "failed copying file from {f} to {f}: {t}", .{
265 src_entry_path, dest_path, err,
266 });
267 progress_node.completeOne();
200268 },
201269 else => continue,
202270 }
203271 }
204272 }
205273}
274
275fn pathIncluded(
276 conf: *const Configuration,
277 exclude_extensions: []const Configuration.String,
278 include_extensions: ?[]const Configuration.String,
279 path: []const u8,
280) bool {
281 for (exclude_extensions) |ext| {
282 if (std.mem.endsWith(u8, path, ext.slice(conf)))
283 return false;
284 }
285 if (include_extensions) |incs| {
286 for (incs) |inc| {
287 if (std.mem.endsWith(u8, path, inc.slice(conf)))
288 return true;
289 } else {
290 return false;
291 }
292 }
293 return true;
294}