| ... | ... | @@ -1249,6 +1249,9 @@ 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(); |
| 1254 | |
| 1252 | 1255 | var walker = try @as(fs.IterableDir, .{ .dir = tmp_directory.handle }).walk(gpa); |
| 1253 | 1256 | defer walker.deinit(); |
| 1254 | 1257 | |
| ... | ... | @@ -1267,10 +1270,25 @@ fn computeHash( |
| 1267 | 1270 | ) }); |
| 1268 | 1271 | return error.FetchFailed; |
| 1269 | 1272 | }) |entry| { |
| 1270 | | _ = filter; // TODO: apply filter rules here |
| 1273 | if (entry.kind == .directory) continue; |
| 1274 | |
| 1275 | if (!filter.includePath(entry.path)) { |
| 1276 | // Delete instead of including in hash calculation. |
| 1277 | const deleted_file = try arena.create(DeletedFile); |
| 1278 | deleted_file.* = .{ |
| 1279 | .fs_path = try arena.dupe(u8, entry.path), |
| 1280 | .failure = undefined, // to be populated by the worker |
| 1281 | }; |
| 1282 | wait_group.start(); |
| 1283 | try thread_pool.spawn(workerDeleteFile, .{ |
| 1284 | tmp_directory.handle, deleted_file, &wait_group, |
| 1285 | }); |
| 1286 | try all_deletions.append(deleted_file); |
| 1287 | continue; |
| 1288 | } |
| 1271 | 1289 | |
| 1272 | 1290 | const kind: HashedFile.Kind = switch (entry.kind) { |
| 1273 | | .directory => continue, |
| 1291 | .directory => unreachable, |
| 1274 | 1292 | .file => .file, |
| 1275 | 1293 | .sym_link => .sym_link, |
| 1276 | 1294 | else => return f.fail(f.location_tok, try eb.printString( |
| ... | ... | @@ -1295,7 +1313,6 @@ fn computeHash( |
| 1295 | 1313 | try thread_pool.spawn(workerHashFile, .{ |
| 1296 | 1314 | tmp_directory.handle, hashed_file, &wait_group, |
| 1297 | 1315 | }); |
| 1298 | | |
| 1299 | 1316 | try all_files.append(hashed_file); |
| 1300 | 1317 | } |
| 1301 | 1318 | } |
| ... | ... | @@ -1315,6 +1332,17 @@ fn computeHash( |
| 1315 | 1332 | }; |
| 1316 | 1333 | hasher.update(&hashed_file.hash); |
| 1317 | 1334 | } |
| 1335 | for (all_deletions.items) |deleted_file| { |
| 1336 | deleted_file.failure catch |err| { |
| 1337 | any_failures = true; |
| 1338 | try eb.addRootErrorMessage(.{ |
| 1339 | .msg = try eb.printString("failed to delete excluded path '{s}' from package: {s}", .{ |
| 1340 | deleted_file.fs_path, @errorName(err), |
| 1341 | }), |
| 1342 | }); |
| 1343 | }; |
| 1344 | } |
| 1345 | |
| 1318 | 1346 | if (any_failures) return error.FetchFailed; |
| 1319 | 1347 | return hasher.finalResult(); |
| 1320 | 1348 | } |
| ... | ... | @@ -1324,6 +1352,11 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { |
| 1324 | 1352 | hashed_file.failure = hashFileFallible(dir, hashed_file); |
| 1325 | 1353 | } |
| 1326 | 1354 | |
| 1355 | fn workerDeleteFile(dir: fs.Dir, deleted_file: *DeletedFile, wg: *WaitGroup) void { |
| 1356 | defer wg.finish(); |
| 1357 | deleted_file.failure = deleteFileFallible(dir, deleted_file); |
| 1358 | } |
| 1359 | |
| 1327 | 1360 | fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { |
| 1328 | 1361 | var buf: [8000]u8 = undefined; |
| 1329 | 1362 | var hasher = Manifest.Hash.init(.{}); |
| ... | ... | @@ -1347,6 +1380,20 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void |
| 1347 | 1380 | hasher.final(&hashed_file.hash); |
| 1348 | 1381 | } |
| 1349 | 1382 | |
| 1383 | fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error!void { |
| 1384 | 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 | } |
| 1396 | |
| 1350 | 1397 | fn isExecutable(file: fs.File) !bool { |
| 1351 | 1398 | if (builtin.os.tag == .windows) { |
| 1352 | 1399 | // TODO check the ACL on Windows. |
| ... | ... | @@ -1360,6 +1407,15 @@ fn isExecutable(file: fs.File) !bool { |
| 1360 | 1407 | } |
| 1361 | 1408 | } |
| 1362 | 1409 | |
| 1410 | const DeletedFile = struct { |
| 1411 | fs_path: []const u8, |
| 1412 | failure: Error!void, |
| 1413 | |
| 1414 | const Error = |
| 1415 | fs.Dir.DeleteFileError || |
| 1416 | fs.Dir.DeleteDirError; |
| 1417 | }; |
| 1418 | |
| 1363 | 1419 | const HashedFile = struct { |
| 1364 | 1420 | fs_path: []const u8, |
| 1365 | 1421 | normalized_path: []const u8, |
| ... | ... | @@ -1402,7 +1458,7 @@ fn normalizePath(arena: Allocator, fs_path: []const u8) ![]const u8 { |
| 1402 | 1458 | const Filter = struct { |
| 1403 | 1459 | include_paths: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 1404 | 1460 | |
| 1405 | | /// sub_path is relative to the tarball root. |
| 1461 | /// sub_path is relative to the package root. |
| 1406 | 1462 | pub fn includePath(self: Filter, sub_path: []const u8) bool { |
| 1407 | 1463 | if (self.include_paths.count() == 0) return true; |
| 1408 | 1464 | if (self.include_paths.contains("")) return true; |
| ... | ... | @@ -1410,7 +1466,7 @@ const Filter = struct { |
| 1410 | 1466 | |
| 1411 | 1467 | // Check if any included paths are parent directories of sub_path. |
| 1412 | 1468 | var dirname = sub_path; |
| 1413 | | while (std.fs.path.dirname(sub_path)) |next_dirname| { |
| 1469 | while (std.fs.path.dirname(dirname)) |next_dirname| { |
| 1414 | 1470 | if (self.include_paths.contains(sub_path)) return true; |
| 1415 | 1471 | dirname = next_dirname; |
| 1416 | 1472 | } |