| ... | ... | @@ -1249,8 +1249,13 @@ fn computeHash( |
| 1249 | 1249 | var all_files = std.ArrayList(*HashedFile).init(gpa); |
| 1250 | 1250 | defer all_files.deinit(); |
| 1251 | 1251 | |
| 1252 | | var all_deletions = std.ArrayList(*DeletedFile).init(gpa); |
| 1253 | | defer all_deletions.deinit(); |
| 1252 | var deleted_files = std.ArrayList(*DeletedFile).init(gpa); |
| 1253 | defer deleted_files.deinit(); |
| 1254 | |
| 1255 | // Track directories which had any files deleted from them so that empty directories |
| 1256 | // can be deleted. |
| 1257 | var sus_dirs: std.StringArrayHashMapUnmanaged(void) = .{}; |
| 1258 | defer sus_dirs.deinit(gpa); |
| 1254 | 1259 | |
| 1255 | 1260 | var walker = try @as(fs.IterableDir, .{ .dir = tmp_directory.handle }).walk(gpa); |
| 1256 | 1261 | defer walker.deinit(); |
| ... | ... | @@ -1274,16 +1279,22 @@ fn computeHash( |
| 1274 | 1279 | |
| 1275 | 1280 | if (!filter.includePath(entry.path)) { |
| 1276 | 1281 | // Delete instead of including in hash calculation. |
| 1282 | const fs_path = try arena.dupe(u8, entry.path); |
| 1283 | |
| 1284 | // Also track the parent directory in case it becomes empty. |
| 1285 | if (fs.path.dirname(fs_path)) |parent| |
| 1286 | try sus_dirs.put(gpa, parent, {}); |
| 1287 | |
| 1277 | 1288 | const deleted_file = try arena.create(DeletedFile); |
| 1278 | 1289 | deleted_file.* = .{ |
| 1279 | | .fs_path = try arena.dupe(u8, entry.path), |
| 1290 | .fs_path = fs_path, |
| 1280 | 1291 | .failure = undefined, // to be populated by the worker |
| 1281 | 1292 | }; |
| 1282 | 1293 | wait_group.start(); |
| 1283 | 1294 | try thread_pool.spawn(workerDeleteFile, .{ |
| 1284 | 1295 | tmp_directory.handle, deleted_file, &wait_group, |
| 1285 | 1296 | }); |
| 1286 | | try all_deletions.append(deleted_file); |
| 1297 | try deleted_files.append(deleted_file); |
| 1287 | 1298 | continue; |
| 1288 | 1299 | } |
| 1289 | 1300 | |
| ... | ... | @@ -1300,8 +1311,8 @@ fn computeHash( |
| 1300 | 1311 | if (std.mem.eql(u8, entry.path, Package.build_zig_basename)) |
| 1301 | 1312 | f.has_build_zig = true; |
| 1302 | 1313 | |
| 1303 | | const hashed_file = try arena.create(HashedFile); |
| 1304 | 1314 | const fs_path = try arena.dupe(u8, entry.path); |
| 1315 | const hashed_file = try arena.create(HashedFile); |
| 1305 | 1316 | hashed_file.* = .{ |
| 1306 | 1317 | .fs_path = fs_path, |
| 1307 | 1318 | .normalized_path = try normalizePath(arena, fs_path), |
| ... | ... | @@ -1317,6 +1328,36 @@ fn computeHash( |
| 1317 | 1328 | } |
| 1318 | 1329 | } |
| 1319 | 1330 | |
| 1331 | { |
| 1332 | // Sort by length, descending, so that child directories get removed first. |
| 1333 | sus_dirs.sortUnstable(@as(struct { |
| 1334 | keys: []const []const u8, |
| 1335 | pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { |
| 1336 | return ctx.keys[b_index].len < ctx.keys[a_index].len; |
| 1337 | } |
| 1338 | }, .{ .keys = sus_dirs.keys() })); |
| 1339 | |
| 1340 | // During this loop, more entries will be added, so we must loop by index. |
| 1341 | var i: usize = 0; |
| 1342 | while (i < sus_dirs.count()) : (i += 1) { |
| 1343 | const sus_dir = sus_dirs.keys()[i]; |
| 1344 | tmp_directory.handle.deleteDir(sus_dir) catch |err| switch (err) { |
| 1345 | error.DirNotEmpty => continue, |
| 1346 | error.FileNotFound => continue, |
| 1347 | else => |e| { |
| 1348 | try eb.addRootErrorMessage(.{ .msg = try eb.printString( |
| 1349 | "unable to delete empty directory '{s}': {s}", |
| 1350 | .{ sus_dir, @errorName(e) }, |
| 1351 | ) }); |
| 1352 | return error.FetchFailed; |
| 1353 | }, |
| 1354 | }; |
| 1355 | if (fs.path.dirname(sus_dir)) |parent| { |
| 1356 | try sus_dirs.put(gpa, parent, {}); |
| 1357 | } |
| 1358 | } |
| 1359 | } |
| 1360 | |
| 1320 | 1361 | std.mem.sortUnstable(*HashedFile, all_files.items, {}, HashedFile.lessThan); |
| 1321 | 1362 | |
| 1322 | 1363 | var hasher = Manifest.Hash.init(.{}); |
| ... | ... | @@ -1332,7 +1373,7 @@ fn computeHash( |
| 1332 | 1373 | }; |
| 1333 | 1374 | hasher.update(&hashed_file.hash); |
| 1334 | 1375 | } |
| 1335 | | for (all_deletions.items) |deleted_file| { |
| 1376 | for (deleted_files.items) |deleted_file| { |
| 1336 | 1377 | deleted_file.failure catch |err| { |
| 1337 | 1378 | any_failures = true; |
| 1338 | 1379 | try eb.addRootErrorMessage(.{ |
| ... | ... | @@ -1382,16 +1423,6 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void |
| 1382 | 1423 | |
| 1383 | 1424 | fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error!void { |
| 1384 | 1425 | try dir.deleteFile(deleted_file.fs_path); |
| 1385 | | // In case the file was the last remaining file in the parent directory, attempt to |
| 1386 | | // remove the parent directory. |
| 1387 | | var opt_parent = fs.path.dirname(deleted_file.fs_path); |
| 1388 | | while (opt_parent) |parent| : (opt_parent = fs.path.dirname(parent)) { |
| 1389 | | dir.deleteDir(parent) catch |err| switch (err) { |
| 1390 | | error.DirNotEmpty => return, |
| 1391 | | error.FileNotFound => return, |
| 1392 | | else => |e| return e, |
| 1393 | | }; |
| 1394 | | } |
| 1395 | 1426 | } |
| 1396 | 1427 | |
| 1397 | 1428 | fn isExecutable(file: fs.File) !bool { |