| ... | @@ -311,19 +311,34 @@ pub fn fetchAndAddDependencies( | ... | @@ -311,19 +311,34 @@ pub fn fetchAndAddDependencies( |
| 311 | } | 311 | } |
| 312 | | 312 | |
| 313 | for (manifest.dependencies.keys(), manifest.dependencies.values()) |name, *dep| { | 313 | for (manifest.dependencies.keys(), manifest.dependencies.values()) |name, *dep| { |
| | 314 | var fetch_location = try FetchLocation.init(gpa, dep.*, directory, report); |
| | 315 | defer fetch_location.deinit(gpa); |
| | 316 | |
| | 317 | // Directories do not provide a hash in build.zig.zon. |
| | 318 | // Hash the path to the module rather than its contents. |
| | 319 | if (fetch_location == .directory) { |
| | 320 | if (dep.hash != null) { |
| | 321 | return report.fail(dep.hash_tok, "hash not allowed for directory package", .{}); |
| | 322 | } |
| | 323 | const hex_digest = Manifest.hexDigest(try computePathHash(gpa, directory, fetch_location.directory)); |
| | 324 | dep.hash = try gpa.dupe(u8, &hex_digest); |
| | 325 | } |
| | 326 | |
| 314 | const sub_mod, const found_existing = try getCachedPackage( | 327 | const sub_mod, const found_existing = try getCachedPackage( |
| 315 | arena, | 328 | arena, |
| | 329 | fetch_location, |
| 316 | global_cache_directory, | 330 | global_cache_directory, |
| 317 | dep.*, | 331 | dep.*, |
| 318 | all_modules, | 332 | all_modules, |
| 319 | root_prog_node, | 333 | root_prog_node, |
| 320 | ) orelse .{ | 334 | ) orelse .{ |
| 321 | try fetchAndUnpack( | 335 | try fetchAndUnpack( |
| | 336 | fetch_location, |
| 322 | thread_pool, | 337 | thread_pool, |
| 323 | http_client, | 338 | http_client, |
| 324 | directory, | 339 | directory, |
| 325 | global_cache_directory, | 340 | global_cache_directory, |
| 326 | dep, | 341 | dep.*, |
| 327 | report, | 342 | report, |
| 328 | all_modules, | 343 | all_modules, |
| 329 | root_prog_node, | 344 | root_prog_node, |
| ... | @@ -503,9 +518,10 @@ const FetchLocation = union(enum) { | ... | @@ -503,9 +518,10 @@ const FetchLocation = union(enum) { |
| 503 | /// This may be a file that requires unpacking (such as a .tar.gz), | 518 | /// This may be a file that requires unpacking (such as a .tar.gz), |
| 504 | /// or the path to the root directory of a package. | 519 | /// or the path to the root directory of a package. |
| 505 | file: []const u8, | 520 | file: []const u8, |
| | 521 | directory: []const u8, |
| 506 | http_request: std.Uri, | 522 | http_request: std.Uri, |
| 507 | | 523 | |
| 508 | pub fn init(gpa: Allocator, dep: Manifest.Dependency, report: Report) !FetchLocation { | 524 | pub fn init(gpa: Allocator, dep: Manifest.Dependency, root_dir: Compilation.Directory, report: Report) !FetchLocation { |
| 509 | switch (dep.location) { | 525 | switch (dep.location) { |
| 510 | .url => |url| { | 526 | .url => |url| { |
| 511 | const uri = std.Uri.parse(url) catch |err| switch (err) { | 527 | const uri = std.Uri.parse(url) catch |err| switch (err) { |
| ... | @@ -522,14 +538,22 @@ const FetchLocation = union(enum) { | ... | @@ -522,14 +538,22 @@ const FetchLocation = union(enum) { |
| 522 | return report.fail(dep.location_tok, "Absolute paths are not allowed. Use a relative path instead", .{}); | 538 | return report.fail(dep.location_tok, "Absolute paths are not allowed. Use a relative path instead", .{}); |
| 523 | } | 539 | } |
| 524 | | 540 | |
| 525 | return .{ .file = try gpa.dupe(u8, path) }; | 541 | const is_dir = isDirectory(root_dir, path) catch |err| switch (err) { |
| | 542 | error.FileNotFound => return report.fail(dep.location_tok, "File not found: {s}", .{path}), |
| | 543 | else => return err, |
| | 544 | }; |
| | 545 | |
| | 546 | return if (is_dir) |
| | 547 | .{ .directory = try gpa.dupe(u8, path) } |
| | 548 | else |
| | 549 | .{ .file = try gpa.dupe(u8, path) }; |
| 526 | }, | 550 | }, |
| 527 | } | 551 | } |
| 528 | } | 552 | } |
| 529 | | 553 | |
| 530 | pub fn deinit(f: *FetchLocation, gpa: Allocator) void { | 554 | pub fn deinit(f: *FetchLocation, gpa: Allocator) void { |
| 531 | switch (f.*) { | 555 | switch (f.*) { |
| 532 | .file => |path| gpa.free(path), | 556 | inline .file, .directory => |path| gpa.free(path), |
| 533 | .http_request => {}, | 557 | .http_request => {}, |
| 534 | } | 558 | } |
| 535 | f.* = undefined; | 559 | f.* = undefined; |
| ... | @@ -545,20 +569,19 @@ const FetchLocation = union(enum) { | ... | @@ -545,20 +569,19 @@ const FetchLocation = union(enum) { |
| 545 | ) !ReadableResource { | 569 | ) !ReadableResource { |
| 546 | switch (f) { | 570 | switch (f) { |
| 547 | .file => |file| { | 571 | .file => |file| { |
| 548 | const is_dir = isDirectory(root_dir, file) catch |err| switch (err) { | | |
| 549 | error.FileNotFound => return report.fail(dep.location_tok, "File not found: {s}", .{file}), | | |
| 550 | else => return err, | | |
| 551 | }; | | |
| 552 | | | |
| 553 | const owned_path = try gpa.dupe(u8, file); | 572 | const owned_path = try gpa.dupe(u8, file); |
| 554 | errdefer gpa.free(owned_path); | 573 | errdefer gpa.free(owned_path); |
| 555 | | | |
| 556 | return .{ | 574 | return .{ |
| 557 | .path = owned_path, | 575 | .path = owned_path, |
| 558 | .resource = if (is_dir) | 576 | .resource = .{ .file = try root_dir.handle.openFile(file, .{}) }, |
| 559 | .{ .directory = try root_dir.handle.openIterableDir(file, .{}) } | 577 | }; |
| 560 | else | 578 | }, |
| 561 | .{ .file = try root_dir.handle.openFile(file, .{}) }, | 579 | .directory => |dir| { |
| | 580 | const owned_path = try gpa.dupe(u8, dir); |
| | 581 | errdefer gpa.free(owned_path); |
| | 582 | return .{ |
| | 583 | .path = owned_path, |
| | 584 | .resource = .{ .directory = try root_dir.handle.openIterableDir(dir, .{}) }, |
| 562 | }; | 585 | }; |
| 563 | }, | 586 | }, |
| 564 | .http_request => |uri| { | 587 | .http_request => |uri| { |
| ... | @@ -611,7 +634,7 @@ const ReadableResource = struct { | ... | @@ -611,7 +634,7 @@ const ReadableResource = struct { |
| 611 | switch (rr.resource) { | 634 | switch (rr.resource) { |
| 612 | .directory => { | 635 | .directory => { |
| 613 | return .{ | 636 | return .{ |
| 614 | .hash = computePathHash(rr.path), | 637 | .hash = try computePathHash(allocator, root_dir, rr.path), |
| 615 | .root_src_dir_path = try allocator.dupe(u8, rr.path), | 638 | .root_src_dir_path = try allocator.dupe(u8, rr.path), |
| 616 | .root_dir = root_dir, | 639 | .root_dir = root_dir, |
| 617 | }; | 640 | }; |
| ... | @@ -851,11 +874,19 @@ fn ProgressReader(comptime ReaderType: type) type { | ... | @@ -851,11 +874,19 @@ fn ProgressReader(comptime ReaderType: type) type { |
| 851 | /// (i.e. whether or not its transitive dependencies have been fetched). | 874 | /// (i.e. whether or not its transitive dependencies have been fetched). |
| 852 | fn getCachedPackage( | 875 | fn getCachedPackage( |
| 853 | gpa: Allocator, | 876 | gpa: Allocator, |
| | 877 | fetch_location: FetchLocation, |
| 854 | global_cache_directory: Compilation.Directory, | 878 | global_cache_directory: Compilation.Directory, |
| 855 | dep: Manifest.Dependency, | 879 | dep: Manifest.Dependency, |
| 856 | all_modules: *AllModules, | 880 | all_modules: *AllModules, |
| 857 | root_prog_node: *std.Progress.Node, | 881 | root_prog_node: *std.Progress.Node, |
| 858 | ) !?struct { DependencyModule, bool } { | 882 | ) !?struct { DependencyModule, bool } { |
| | 883 | // There is no fixed location to check for directory modules. |
| | 884 | // Instead, check whether it is already listed in all_modules. |
| | 885 | if (fetch_location == .directory) { |
| | 886 | const hex_digest = dep.hash.?[0..hex_multihash_len]; |
| | 887 | return if (all_modules.get(hex_digest.*)) |mod| .{ mod.?, true } else null; |
| | 888 | } |
| | 889 | |
| 859 | const s = fs.path.sep_str; | 890 | const s = fs.path.sep_str; |
| 860 | // Check if the expected_hash is already present in the global package | 891 | // Check if the expected_hash is already present in the global package |
| 861 | // cache, and thereby avoid both fetching and unpacking. | 892 | // cache, and thereby avoid both fetching and unpacking. |
| ... | @@ -912,11 +943,12 @@ fn getCachedPackage( | ... | @@ -912,11 +943,12 @@ fn getCachedPackage( |
| 912 | } | 943 | } |
| 913 | | 944 | |
| 914 | fn fetchAndUnpack( | 945 | fn fetchAndUnpack( |
| | 946 | fetch_location: FetchLocation, |
| 915 | thread_pool: *ThreadPool, | 947 | thread_pool: *ThreadPool, |
| 916 | http_client: *std.http.Client, | 948 | http_client: *std.http.Client, |
| 917 | directory: Compilation.Directory, | 949 | directory: Compilation.Directory, |
| 918 | global_cache_directory: Compilation.Directory, | 950 | global_cache_directory: Compilation.Directory, |
| 919 | dep: *Manifest.Dependency, | 951 | dep: Manifest.Dependency, |
| 920 | report: Report, | 952 | report: Report, |
| 921 | all_modules: *AllModules, | 953 | all_modules: *AllModules, |
| 922 | root_prog_node: *std.Progress.Node, | 954 | root_prog_node: *std.Progress.Node, |
| ... | @@ -931,13 +963,10 @@ fn fetchAndUnpack( | ... | @@ -931,13 +963,10 @@ fn fetchAndUnpack( |
| 931 | pkg_prog_node.activate(); | 963 | pkg_prog_node.activate(); |
| 932 | pkg_prog_node.context.refresh(); | 964 | pkg_prog_node.context.refresh(); |
| 933 | | 965 | |
| 934 | var fetch_location = try FetchLocation.init(gpa, dep.*, report); | 966 | var readable_resource = try fetch_location.fetch(gpa, directory, http_client, dep, report); |
| 935 | defer fetch_location.deinit(gpa); | | |
| 936 | | | |
| 937 | var readable_resource = try fetch_location.fetch(gpa, directory, http_client, dep.*, report); | | |
| 938 | defer readable_resource.deinit(gpa); | 967 | defer readable_resource.deinit(gpa); |
| 939 | | 968 | |
| 940 | var package_location = try readable_resource.unpack(gpa, thread_pool, directory, global_cache_directory, dep.*, report, &pkg_prog_node); | 969 | var package_location = try readable_resource.unpack(gpa, thread_pool, directory, global_cache_directory, dep, report, &pkg_prog_node); |
| 941 | defer package_location.deinit(gpa); | 970 | defer package_location.deinit(gpa); |
| 942 | | 971 | |
| 943 | const actual_hex = Manifest.hexDigest(package_location.hash); | 972 | const actual_hex = Manifest.hexDigest(package_location.hash); |
| ... | @@ -965,13 +994,6 @@ fn fetchAndUnpack( | ... | @@ -965,13 +994,6 @@ fn fetchAndUnpack( |
| 965 | })); | 994 | })); |
| 966 | return error.PackageFetchFailed; | 995 | return error.PackageFetchFailed; |
| 967 | } | 996 | } |
| 968 | } else { | | |
| 969 | if (dep.hash != null) { | | |
| 970 | return report.fail(dep.hash_tok, "hash not allowed for directory package", .{}); | | |
| 971 | } | | |
| 972 | // Since directory dependencies don't provide a hash in build.zig.zon, | | |
| 973 | // set the hash here to be the hash of the path to the dependency. | | |
| 974 | dep.hash = try gpa.dupe(u8, &actual_hex); | | |
| 975 | } | 997 | } |
| 976 | | 998 | |
| 977 | const build_zig_path = try std.fs.path.join(gpa, &.{ package_location.root_src_dir_path, build_zig_basename }); | 999 | const build_zig_path = try std.fs.path.join(gpa, &.{ package_location.root_src_dir_path, build_zig_basename }); |
| ... | @@ -1089,9 +1111,11 @@ fn computePackageHash( | ... | @@ -1089,9 +1111,11 @@ fn computePackageHash( |
| 1089 | } | 1111 | } |
| 1090 | | 1112 | |
| 1091 | /// Compute the hash of a file path. | 1113 | /// Compute the hash of a file path. |
| 1092 | fn computePathHash(path: []const u8) [Manifest.Hash.digest_length]u8 { | 1114 | fn computePathHash(gpa: Allocator, dir: Compilation.Directory, path: []const u8) ![Manifest.Hash.digest_length]u8 { |
| | 1115 | const resolved_path = try std.fs.path.resolve(gpa, &.{ dir.path.?, path }); |
| | 1116 | defer gpa.free(resolved_path); |
| 1093 | var hasher = Manifest.Hash.init(.{}); | 1117 | var hasher = Manifest.Hash.init(.{}); |
| 1094 | hasher.update(path); | 1118 | hasher.update(resolved_path); |
| 1095 | return hasher.finalResult(); | 1119 | return hasher.finalResult(); |
| 1096 | } | 1120 | } |
| 1097 | | 1121 | |