authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-06 17:05:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-08 16:54:31-07:00
logd0bcc390e8f61ada470b524e3fd203c1af521a99
tree14503dd0b9cb5b3666b6c397f3449c7931bbcb08
parent88bbec8f9b2f8f023a0177c204f51b8ac0aee83a

get `zig fetch` working with the new system

* start renaming "package" to "module" (see #14307) - build system gains `main_mod_path` and `main_pkg_path` is still there but it is deprecated. * eliminate the object-oriented memory management style of what was previously `*Package`. Now it is `*Package.Module` and all pointers point to externally managed memory. * fixes to get the new Fetch.zig code working. The previous commit was work-in-progress. There are still two commented out code paths, the one that leads to `Compilation.create` and the one for `zig build` that fetches the entire dependency tree and creates the required modules for the build runner.

12 files changed, 883 insertions(+), 750 deletions(-)

build.zig+1-1
...@@ -88,7 +88,7 @@ pub fn build(b: *std.Build) !void {...@@ -88,7 +88,7 @@ pub fn build(b: *std.Build) !void {
88 .name = "check-case",88 .name = "check-case",
89 .root_source_file = .{ .path = "test/src/Cases.zig" },89 .root_source_file = .{ .path = "test/src/Cases.zig" },
90 .optimize = optimize,90 .optimize = optimize,
91 .main_pkg_path = .{ .path = "." },91 .main_mod_path = .{ .path = "." },
92 });92 });
93 check_case_exe.stack_size = stack_size;93 check_case_exe.stack_size = stack_size;
94 check_case_exe.single_threaded = single_threaded;94 check_case_exe.single_threaded = single_threaded;
lib/std/Build.zig+20-5
...@@ -634,6 +634,9 @@ pub const ExecutableOptions = struct {...@@ -634,6 +634,9 @@ pub const ExecutableOptions = struct {
634 use_llvm: ?bool = null,634 use_llvm: ?bool = null,
635 use_lld: ?bool = null,635 use_lld: ?bool = null,
636 zig_lib_dir: ?LazyPath = null,636 zig_lib_dir: ?LazyPath = null,
637 main_mod_path: ?LazyPath = null,
638
639 /// Deprecated; use `main_mod_path`.
637 main_pkg_path: ?LazyPath = null,640 main_pkg_path: ?LazyPath = null,
638};641};
639642
...@@ -652,7 +655,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile {...@@ -652,7 +655,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile {
652 .use_llvm = options.use_llvm,655 .use_llvm = options.use_llvm,
653 .use_lld = options.use_lld,656 .use_lld = options.use_lld,
654 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,657 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
655 .main_pkg_path = options.main_pkg_path,658 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,
656 });659 });
657}660}
658661
...@@ -667,6 +670,9 @@ pub const ObjectOptions = struct {...@@ -667,6 +670,9 @@ pub const ObjectOptions = struct {
667 use_llvm: ?bool = null,670 use_llvm: ?bool = null,
668 use_lld: ?bool = null,671 use_lld: ?bool = null,
669 zig_lib_dir: ?LazyPath = null,672 zig_lib_dir: ?LazyPath = null,
673 main_mod_path: ?LazyPath = null,
674
675 /// Deprecated; use `main_mod_path`.
670 main_pkg_path: ?LazyPath = null,676 main_pkg_path: ?LazyPath = null,
671};677};
672678
...@@ -683,7 +689,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *Step.Compile {...@@ -683,7 +689,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *Step.Compile {
683 .use_llvm = options.use_llvm,689 .use_llvm = options.use_llvm,
684 .use_lld = options.use_lld,690 .use_lld = options.use_lld,
685 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,691 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
686 .main_pkg_path = options.main_pkg_path,692 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,
687 });693 });
688}694}
689695
...@@ -699,6 +705,9 @@ pub const SharedLibraryOptions = struct {...@@ -699,6 +705,9 @@ pub const SharedLibraryOptions = struct {
699 use_llvm: ?bool = null,705 use_llvm: ?bool = null,
700 use_lld: ?bool = null,706 use_lld: ?bool = null,
701 zig_lib_dir: ?LazyPath = null,707 zig_lib_dir: ?LazyPath = null,
708 main_mod_path: ?LazyPath = null,
709
710 /// Deprecated; use `main_mod_path`.
702 main_pkg_path: ?LazyPath = null,711 main_pkg_path: ?LazyPath = null,
703};712};
704713
...@@ -717,7 +726,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile...@@ -717,7 +726,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile
717 .use_llvm = options.use_llvm,726 .use_llvm = options.use_llvm,
718 .use_lld = options.use_lld,727 .use_lld = options.use_lld,
719 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,728 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
720 .main_pkg_path = options.main_pkg_path,729 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,
721 });730 });
722}731}
723732
...@@ -733,6 +742,9 @@ pub const StaticLibraryOptions = struct {...@@ -733,6 +742,9 @@ pub const StaticLibraryOptions = struct {
733 use_llvm: ?bool = null,742 use_llvm: ?bool = null,
734 use_lld: ?bool = null,743 use_lld: ?bool = null,
735 zig_lib_dir: ?LazyPath = null,744 zig_lib_dir: ?LazyPath = null,
745 main_mod_path: ?LazyPath = null,
746
747 /// Deprecated; use `main_mod_path`.
736 main_pkg_path: ?LazyPath = null,748 main_pkg_path: ?LazyPath = null,
737};749};
738750
...@@ -751,7 +763,7 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *Step.Compile...@@ -751,7 +763,7 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *Step.Compile
751 .use_llvm = options.use_llvm,763 .use_llvm = options.use_llvm,
752 .use_lld = options.use_lld,764 .use_lld = options.use_lld,
753 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,765 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
754 .main_pkg_path = options.main_pkg_path,766 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,
755 });767 });
756}768}
757769
...@@ -769,6 +781,9 @@ pub const TestOptions = struct {...@@ -769,6 +781,9 @@ pub const TestOptions = struct {
769 use_llvm: ?bool = null,781 use_llvm: ?bool = null,
770 use_lld: ?bool = null,782 use_lld: ?bool = null,
771 zig_lib_dir: ?LazyPath = null,783 zig_lib_dir: ?LazyPath = null,
784 main_mod_path: ?LazyPath = null,
785
786 /// Deprecated; use `main_mod_path`.
772 main_pkg_path: ?LazyPath = null,787 main_pkg_path: ?LazyPath = null,
773};788};
774789
...@@ -787,7 +802,7 @@ pub fn addTest(b: *Build, options: TestOptions) *Step.Compile {...@@ -787,7 +802,7 @@ pub fn addTest(b: *Build, options: TestOptions) *Step.Compile {
787 .use_llvm = options.use_llvm,802 .use_llvm = options.use_llvm,
788 .use_lld = options.use_lld,803 .use_lld = options.use_lld,
789 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,804 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
790 .main_pkg_path = options.main_pkg_path,805 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,
791 });806 });
792}807}
793808
lib/std/Build/Cache.zig+11
...@@ -9,6 +9,13 @@ pub const Directory = struct {...@@ -9,6 +9,13 @@ pub const Directory = struct {
9 path: ?[]const u8,9 path: ?[]const u8,
10 handle: fs.Dir,10 handle: fs.Dir,
1111
12 pub fn cwd() Directory {
13 return .{
14 .path = null,
15 .handle = fs.cwd(),
16 };
17 }
18
12 pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {19 pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {
13 if (self.path) |p| {20 if (self.path) |p| {
14 // TODO clean way to do this with only 1 allocation21 // TODO clean way to do this with only 1 allocation
...@@ -53,6 +60,10 @@ pub const Directory = struct {...@@ -53,6 +60,10 @@ pub const Directory = struct {
53 try writer.writeAll(fs.path.sep_str);60 try writer.writeAll(fs.path.sep_str);
54 }61 }
55 }62 }
63
64 pub fn eql(self: Directory, other: Directory) bool {
65 return self.handle.fd == other.handle.fd;
66 }
56};67};
5768
58gpa: Allocator,69gpa: Allocator,
lib/std/Build/Step/Compile.zig+9-6
...@@ -68,7 +68,7 @@ c_std: std.Build.CStd,...@@ -68,7 +68,7 @@ c_std: std.Build.CStd,
68/// Set via options; intended to be read-only after that.68/// Set via options; intended to be read-only after that.
69zig_lib_dir: ?LazyPath,69zig_lib_dir: ?LazyPath,
70/// Set via options; intended to be read-only after that.70/// Set via options; intended to be read-only after that.
71main_pkg_path: ?LazyPath,71main_mod_path: ?LazyPath,
72exec_cmd_args: ?[]const ?[]const u8,72exec_cmd_args: ?[]const ?[]const u8,
73filter: ?[]const u8,73filter: ?[]const u8,
74test_evented_io: bool = false,74test_evented_io: bool = false,
...@@ -316,6 +316,9 @@ pub const Options = struct {...@@ -316,6 +316,9 @@ pub const Options = struct {
316 use_llvm: ?bool = null,316 use_llvm: ?bool = null,
317 use_lld: ?bool = null,317 use_lld: ?bool = null,
318 zig_lib_dir: ?LazyPath = null,318 zig_lib_dir: ?LazyPath = null,
319 main_mod_path: ?LazyPath = null,
320
321 /// deprecated; use `main_mod_path`.
319 main_pkg_path: ?LazyPath = null,322 main_pkg_path: ?LazyPath = null,
320};323};
321324
...@@ -480,7 +483,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -480,7 +483,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
480 .installed_headers = ArrayList(*Step).init(owner.allocator),483 .installed_headers = ArrayList(*Step).init(owner.allocator),
481 .c_std = std.Build.CStd.C99,484 .c_std = std.Build.CStd.C99,
482 .zig_lib_dir = null,485 .zig_lib_dir = null,
483 .main_pkg_path = null,486 .main_mod_path = null,
484 .exec_cmd_args = null,487 .exec_cmd_args = null,
485 .filter = options.filter,488 .filter = options.filter,
486 .test_runner = options.test_runner,489 .test_runner = options.test_runner,
...@@ -515,8 +518,8 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -515,8 +518,8 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
515 lp.addStepDependencies(&self.step);518 lp.addStepDependencies(&self.step);
516 }519 }
517520
518 if (options.main_pkg_path) |lp| {521 if (options.main_mod_path orelse options.main_pkg_path) |lp| {
519 self.main_pkg_path = lp.dupe(self.step.owner);522 self.main_mod_path = lp.dupe(self.step.owner);
520 lp.addStepDependencies(&self.step);523 lp.addStepDependencies(&self.step);
521 }524 }
522525
...@@ -1998,8 +2001,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1998,8 +2001,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1998 try zig_args.append(dir.getPath(b));2001 try zig_args.append(dir.getPath(b));
1999 }2002 }
20002003
2001 if (self.main_pkg_path) |dir| {2004 if (self.main_mod_path) |dir| {
2002 try zig_args.append("--main-pkg-path");2005 try zig_args.append("--main-mod-path");
2003 try zig_args.append(dir.getPath(b));2006 try zig_args.append(dir.getPath(b));
2004 }2007 }
20052008
src/Compilation.zig+84-86
...@@ -273,8 +273,8 @@ const Job = union(enum) {...@@ -273,8 +273,8 @@ const Job = union(enum) {
273 /// The source file containing the Decl has been updated, and so the273 /// The source file containing the Decl has been updated, and so the
274 /// Decl may need its line number information updated in the debug info.274 /// Decl may need its line number information updated in the debug info.
275 update_line_number: Module.Decl.Index,275 update_line_number: Module.Decl.Index,
276 /// The main source file for the package needs to be analyzed.276 /// The main source file for the module needs to be analyzed.
277 analyze_pkg: *Package,277 analyze_mod: *Package.Module,
278278
279 /// one of the glibc static objects279 /// one of the glibc static objects
280 glibc_crt_file: glibc.CRTFile,280 glibc_crt_file: glibc.CRTFile,
...@@ -414,7 +414,7 @@ pub const MiscTask = enum {...@@ -414,7 +414,7 @@ pub const MiscTask = enum {
414 compiler_rt,414 compiler_rt,
415 libssp,415 libssp,
416 zig_libc,416 zig_libc,
417 analyze_pkg,417 analyze_mod,
418418
419 @"musl crti.o",419 @"musl crti.o",
420 @"musl crtn.o",420 @"musl crtn.o",
...@@ -544,7 +544,7 @@ pub const InitOptions = struct {...@@ -544,7 +544,7 @@ pub const InitOptions = struct {
544 global_cache_directory: Directory,544 global_cache_directory: Directory,
545 target: Target,545 target: Target,
546 root_name: []const u8,546 root_name: []const u8,
547 main_pkg: ?*Package,547 main_mod: ?*Package.Module,
548 output_mode: std.builtin.OutputMode,548 output_mode: std.builtin.OutputMode,
549 thread_pool: *ThreadPool,549 thread_pool: *ThreadPool,
550 dynamic_linker: ?[]const u8 = null,550 dynamic_linker: ?[]const u8 = null,
...@@ -736,53 +736,53 @@ pub const InitOptions = struct {...@@ -736,53 +736,53 @@ pub const InitOptions = struct {
736 pdb_out_path: ?[]const u8 = null,736 pdb_out_path: ?[]const u8 = null,
737};737};
738738
739fn addPackageTableToCacheHash(739fn addModuleTableToCacheHash(
740 hash: *Cache.HashHelper,740 hash: *Cache.HashHelper,
741 arena: *std.heap.ArenaAllocator,741 arena: *std.heap.ArenaAllocator,
742 pkg_table: Package.Table,742 mod_table: Package.Module.Deps,
743 seen_table: *std.AutoHashMap(*Package, void),743 seen_table: *std.AutoHashMap(*Package.Module, void),
744 hash_type: union(enum) { path_bytes, files: *Cache.Manifest },744 hash_type: union(enum) { path_bytes, files: *Cache.Manifest },
745) (error{OutOfMemory} || std.os.GetCwdError)!void {745) (error{OutOfMemory} || std.os.GetCwdError)!void {
746 const allocator = arena.allocator();746 const allocator = arena.allocator();
747747
748 const packages = try allocator.alloc(Package.Table.KV, pkg_table.count());748 const modules = try allocator.alloc(Package.Module.Deps.KV, mod_table.count());
749 {749 {
750 // Copy over the hashmap entries to our slice750 // Copy over the hashmap entries to our slice
751 var table_it = pkg_table.iterator();751 var table_it = mod_table.iterator();
752 var idx: usize = 0;752 var idx: usize = 0;
753 while (table_it.next()) |entry| : (idx += 1) {753 while (table_it.next()) |entry| : (idx += 1) {
754 packages[idx] = .{754 modules[idx] = .{
755 .key = entry.key_ptr.*,755 .key = entry.key_ptr.*,
756 .value = entry.value_ptr.*,756 .value = entry.value_ptr.*,
757 };757 };
758 }758 }
759 }759 }
760 // Sort the slice by package name760 // Sort the slice by package name
761 mem.sort(Package.Table.KV, packages, {}, struct {761 mem.sortUnstable(Package.Module.Deps.KV, modules, {}, struct {
762 fn lessThan(_: void, lhs: Package.Table.KV, rhs: Package.Table.KV) bool {762 fn lessThan(_: void, lhs: Package.Module.Deps.KV, rhs: Package.Module.Deps.KV) bool {
763 return std.mem.lessThan(u8, lhs.key, rhs.key);763 return std.mem.lessThan(u8, lhs.key, rhs.key);
764 }764 }
765 }.lessThan);765 }.lessThan);
766766
767 for (packages) |pkg| {767 for (modules) |mod| {
768 if ((try seen_table.getOrPut(pkg.value)).found_existing) continue;768 if ((try seen_table.getOrPut(mod.value)).found_existing) continue;
769769
770 // Finally insert the package name and path to the cache hash.770 // Finally insert the package name and path to the cache hash.
771 hash.addBytes(pkg.key);771 hash.addBytes(mod.key);
772 switch (hash_type) {772 switch (hash_type) {
773 .path_bytes => {773 .path_bytes => {
774 hash.addBytes(pkg.value.root_src_path);774 hash.addBytes(mod.value.root_src_path);
775 hash.addOptionalBytes(pkg.value.root_src_directory.path);775 hash.addOptionalBytes(mod.value.root_src_directory.path);
776 },776 },
777 .files => |man| {777 .files => |man| {
778 const pkg_zig_file = try pkg.value.root_src_directory.join(allocator, &[_][]const u8{778 const pkg_zig_file = try mod.value.root_src_directory.join(allocator, &[_][]const u8{
779 pkg.value.root_src_path,779 mod.value.root_src_path,
780 });780 });
781 _ = try man.addFile(pkg_zig_file, null);781 _ = try man.addFile(pkg_zig_file, null);
782 },782 },
783 }783 }
784 // Recurse to handle the package's dependencies784 // Recurse to handle the module's dependencies
785 try addPackageTableToCacheHash(hash, arena, pkg.value.table, seen_table, hash_type);785 try addModuleTableToCacheHash(hash, arena, mod.value.deps, seen_table, hash_type);
786 }786 }
787}787}
788788
...@@ -839,7 +839,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -839,7 +839,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
839 break :blk true;839 break :blk true;
840840
841 // If we have no zig code to compile, no need for LLVM.841 // If we have no zig code to compile, no need for LLVM.
842 if (options.main_pkg == null)842 if (options.main_mod == null)
843 break :blk false;843 break :blk false;
844844
845 // If LLVM does not support the target, then we can't use it.845 // If LLVM does not support the target, then we can't use it.
...@@ -869,7 +869,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -869,7 +869,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
869 // compiler state, the second clause here can be removed so that incremental869 // compiler state, the second clause here can be removed so that incremental
870 // cache mode is used for LLVM backend too. We need some fuzz testing before870 // cache mode is used for LLVM backend too. We need some fuzz testing before
871 // that can be enabled.871 // that can be enabled.
872 const cache_mode = if ((use_llvm or options.main_pkg == null) and !options.disable_lld_caching)872 const cache_mode = if ((use_llvm or options.main_mod == null) and !options.disable_lld_caching)
873 CacheMode.whole873 CacheMode.whole
874 else874 else
875 options.cache_mode;875 options.cache_mode;
...@@ -925,7 +925,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -925,7 +925,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
925 if (use_llvm) {925 if (use_llvm) {
926 // If stage1 generates an object file, self-hosted linker is not926 // If stage1 generates an object file, self-hosted linker is not
927 // yet sophisticated enough to handle that.927 // yet sophisticated enough to handle that.
928 break :blk options.main_pkg != null;928 break :blk options.main_mod != null;
929 }929 }
930930
931 break :blk false;931 break :blk false;
...@@ -1210,7 +1210,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1210,7 +1210,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1210 if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);1210 if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);
1211 // TODO audit this and make sure everything is in it1211 // TODO audit this and make sure everything is in it
12121212
1213 const module: ?*Module = if (options.main_pkg) |main_pkg| blk: {1213 const module: ?*Module = if (options.main_mod) |main_mod| blk: {
1214 // Options that are specific to zig source files, that cannot be1214 // Options that are specific to zig source files, that cannot be
1215 // modified between incremental updates.1215 // modified between incremental updates.
1216 var hash = cache.hash;1216 var hash = cache.hash;
...@@ -1223,11 +1223,12 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1223,11 +1223,12 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1223 // do want to namespace different source file names because they are1223 // do want to namespace different source file names because they are
1224 // likely different compilations and therefore this would be likely to1224 // likely different compilations and therefore this would be likely to
1225 // cause cache hits.1225 // cause cache hits.
1226 hash.addBytes(main_pkg.root_src_path);1226 hash.addBytes(main_mod.root_src_path);
1227 hash.addOptionalBytes(main_pkg.root_src_directory.path);1227 hash.addOptionalBytes(main_mod.root.root_dir.path);
1228 hash.addBytes(main_mod.root.sub_path);
1228 {1229 {
1229 var seen_table = std.AutoHashMap(*Package, void).init(arena);1230 var seen_table = std.AutoHashMap(*Package.Module, void).init(arena);
1230 try addPackageTableToCacheHash(&hash, &arena_allocator, main_pkg.table, &seen_table, .path_bytes);1231 try addModuleTableToCacheHash(&hash, &arena_allocator, main_mod.deps, &seen_table, .path_bytes);
1231 }1232 }
1232 },1233 },
1233 .whole => {1234 .whole => {
...@@ -1283,34 +1284,31 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1283,34 +1284,31 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1283 .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),1284 .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),
1284 };1285 };
12851286
1286 const builtin_pkg = try Package.createWithDir(1287 const builtin_mod = try Package.Module.create(arena, .{
1287 gpa,1288 .root = .{ .root_dir = zig_cache_artifact_directory },
1288 zig_cache_artifact_directory,1289 .root_src_path = "builtin.zig",
1289 null,1290 });
1290 "builtin.zig",
1291 );
1292 errdefer builtin_pkg.destroy(gpa);
12931291
1294 // When you're testing std, the main module is std. In that case, we'll just set the std1292 // When you're testing std, the main module is std. In that case,
1295 // module to the main one, since avoiding the errors caused by duplicating it is more1293 // we'll just set the std module to the main one, since avoiding
1296 // effort than it's worth.1294 // the errors caused by duplicating it is more effort than it's
1297 const main_pkg_is_std = m: {1295 // worth.
1296 const main_mod_is_std = m: {
1298 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{1297 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
1299 options.zig_lib_directory.path orelse ".",1298 options.zig_lib_directory.path orelse ".",
1300 "std",1299 "std",
1301 "std.zig",1300 "std.zig",
1302 });1301 });
1303 defer arena.free(std_path);
1304 const main_path = try std.fs.path.resolve(arena, &[_][]const u8{1302 const main_path = try std.fs.path.resolve(arena, &[_][]const u8{
1305 main_pkg.root_src_directory.path orelse ".",1303 main_mod.root.root_dir.path orelse ".",
1306 main_pkg.root_src_path,1304 main_mod.root.sub_path,
1305 main_mod.root_src_path,
1307 });1306 });
1308 defer arena.free(main_path);
1309 break :m mem.eql(u8, main_path, std_path);1307 break :m mem.eql(u8, main_path, std_path);
1310 };1308 };
13111309
1312 const std_pkg = if (main_pkg_is_std)1310 const std_mod = if (main_mod_is_std)
1313 main_pkg1311 main_mod
1314 else1312 else
1315 try Package.createWithDir(1313 try Package.createWithDir(
1316 gpa,1314 gpa,
...@@ -1319,16 +1317,16 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1319,16 +1317,16 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1319 "std.zig",1317 "std.zig",
1320 );1318 );
13211319
1322 errdefer if (!main_pkg_is_std) std_pkg.destroy(gpa);1320 errdefer if (!main_mod_is_std) std_mod.destroy(gpa);
13231321
1324 const root_pkg = if (options.is_test) root_pkg: {1322 const root_mod = if (options.is_test) root_mod: {
1325 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: {1323 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: {
1326 const test_dir = std.fs.path.dirname(test_runner);1324 const test_dir = std.fs.path.dirname(test_runner);
1327 const basename = std.fs.path.basename(test_runner);1325 const basename = std.fs.path.basename(test_runner);
1328 const pkg = try Package.create(gpa, test_dir, basename);1326 const pkg = try Package.create(gpa, test_dir, basename);
13291327
1330 // copy package table from main_pkg to root_pkg1328 // copy module table from main_mod to root_mod
1331 pkg.table = try main_pkg.table.clone(gpa);1329 pkg.deps = try main_mod.deps.clone(gpa);
1332 break :test_pkg pkg;1330 break :test_pkg pkg;
1333 } else try Package.createWithDir(1331 } else try Package.createWithDir(
1334 gpa,1332 gpa,
...@@ -1338,26 +1336,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1338,26 +1336,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1338 );1336 );
1339 errdefer test_pkg.destroy(gpa);1337 errdefer test_pkg.destroy(gpa);
13401338
1341 break :root_pkg test_pkg;1339 break :root_mod test_pkg;
1342 } else main_pkg;1340 } else main_mod;
1343 errdefer if (options.is_test) root_pkg.destroy(gpa);1341 errdefer if (options.is_test) root_mod.destroy(gpa);
13441342
1345 const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: {1343 const compiler_rt_mod = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_mod: {
1346 break :compiler_rt_pkg try Package.createWithDir(1344 break :compiler_rt_mod try Package.createWithDir(
1347 gpa,1345 gpa,
1348 options.zig_lib_directory,1346 options.zig_lib_directory,
1349 null,1347 null,
1350 "compiler_rt.zig",1348 "compiler_rt.zig",
1351 );1349 );
1352 } else null;1350 } else null;
1353 errdefer if (compiler_rt_pkg) |p| p.destroy(gpa);1351 errdefer if (compiler_rt_mod) |p| p.destroy(gpa);
13541352
1355 try main_pkg.add(gpa, "builtin", builtin_pkg);1353 try main_mod.add(gpa, "builtin", builtin_mod);
1356 try main_pkg.add(gpa, "root", root_pkg);1354 try main_mod.add(gpa, "root", root_mod);
1357 try main_pkg.add(gpa, "std", std_pkg);1355 try main_mod.add(gpa, "std", std_mod);
13581356
1359 if (compiler_rt_pkg) |p| {1357 if (compiler_rt_mod) |p| {
1360 try main_pkg.add(gpa, "compiler_rt", p);1358 try main_mod.add(gpa, "compiler_rt", p);
1361 }1359 }
13621360
1363 // Pre-open the directory handles for cached ZIR code so that it does not need1361 // Pre-open the directory handles for cached ZIR code so that it does not need
...@@ -1395,8 +1393,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1395,8 +1393,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1395 module.* = .{1393 module.* = .{
1396 .gpa = gpa,1394 .gpa = gpa,
1397 .comp = comp,1395 .comp = comp,
1398 .main_pkg = main_pkg,1396 .main_mod = main_mod,
1399 .root_pkg = root_pkg,1397 .root_mod = root_mod,
1400 .zig_cache_artifact_directory = zig_cache_artifact_directory,1398 .zig_cache_artifact_directory = zig_cache_artifact_directory,
1401 .global_zir_cache = global_zir_cache,1399 .global_zir_cache = global_zir_cache,
1402 .local_zir_cache = local_zir_cache,1400 .local_zir_cache = local_zir_cache,
...@@ -2005,8 +2003,8 @@ fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Director...@@ -2005,8 +2003,8 @@ fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Director
2005 // This is only for cleanup purposes; Module.deinit calls close2003 // This is only for cleanup purposes; Module.deinit calls close
2006 // on the handle of zig_cache_artifact_directory.2004 // on the handle of zig_cache_artifact_directory.
2007 if (comp.bin_file.options.module) |module| {2005 if (comp.bin_file.options.module) |module| {
2008 const builtin_pkg = module.main_pkg.table.get("builtin").?;2006 const builtin_mod = module.main_mod.deps.get("builtin").?;
2009 module.zig_cache_artifact_directory = builtin_pkg.root_src_directory;2007 module.zig_cache_artifact_directory = builtin_mod.root_src_directory;
2010 }2008 }
2011}2009}
20122010
...@@ -2148,8 +2146,8 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void...@@ -2148,8 +2146,8 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
21482146
2149 // Make sure std.zig is inside the import_table. We unconditionally need2147 // Make sure std.zig is inside the import_table. We unconditionally need
2150 // it for start.zig.2148 // it for start.zig.
2151 const std_pkg = module.main_pkg.table.get("std").?;2149 const std_mod = module.main_mod.deps.get("std").?;
2152 _ = try module.importPkg(std_pkg);2150 _ = try module.importPkg(std_mod);
21532151
2154 // Normally we rely on importing std to in turn import the root source file2152 // Normally we rely on importing std to in turn import the root source file
2155 // in the start code, but when using the stage1 backend that won't happen,2153 // in the start code, but when using the stage1 backend that won't happen,
...@@ -2158,11 +2156,11 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void...@@ -2158,11 +2156,11 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
2158 // Likewise, in the case of `zig test`, the test runner is the root source file,2156 // Likewise, in the case of `zig test`, the test runner is the root source file,
2159 // and so there is nothing to import the main file.2157 // and so there is nothing to import the main file.
2160 if (comp.bin_file.options.is_test) {2158 if (comp.bin_file.options.is_test) {
2161 _ = try module.importPkg(module.main_pkg);2159 _ = try module.importPkg(module.main_mod);
2162 }2160 }
21632161
2164 if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| {2162 if (module.main_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2165 _ = try module.importPkg(compiler_rt_pkg);2163 _ = try module.importPkg(compiler_rt_mod);
2166 }2164 }
21672165
2168 // Put a work item in for every known source file to detect if2166 // Put a work item in for every known source file to detect if
...@@ -2185,13 +2183,13 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void...@@ -2185,13 +2183,13 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
2185 }2183 }
2186 }2184 }
21872185
2188 try comp.work_queue.writeItem(.{ .analyze_pkg = std_pkg });2186 try comp.work_queue.writeItem(.{ .analyze_mod = std_mod });
2189 if (comp.bin_file.options.is_test) {2187 if (comp.bin_file.options.is_test) {
2190 try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });2188 try comp.work_queue.writeItem(.{ .analyze_mod = module.main_mod });
2191 }2189 }
21922190
2193 if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| {2191 if (module.main_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2194 try comp.work_queue.writeItem(.{ .analyze_pkg = compiler_rt_pkg });2192 try comp.work_queue.writeItem(.{ .analyze_mod = compiler_rt_mod });
2195 }2193 }
2196 }2194 }
21972195
...@@ -2420,19 +2418,19 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2420,19 +2418,19 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2420 comptime assert(link_hash_implementation_version == 10);2418 comptime assert(link_hash_implementation_version == 10);
24212419
2422 if (comp.bin_file.options.module) |mod| {2420 if (comp.bin_file.options.module) |mod| {
2423 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{2421 const main_zig_file = try mod.main_mod.root_src_directory.join(arena, &[_][]const u8{
2424 mod.main_pkg.root_src_path,2422 mod.main_mod.root_src_path,
2425 });2423 });
2426 _ = try man.addFile(main_zig_file, null);2424 _ = try man.addFile(main_zig_file, null);
2427 {2425 {
2428 var seen_table = std.AutoHashMap(*Package, void).init(arena);2426 var seen_table = std.AutoHashMap(*Package.Module, void).init(arena);
24292427
2430 // Skip builtin.zig; it is useless as an input, and we don't want to have to2428 // Skip builtin.zig; it is useless as an input, and we don't want to have to
2431 // write it before checking for a cache hit.2429 // write it before checking for a cache hit.
2432 const builtin_pkg = mod.main_pkg.table.get("builtin").?;2430 const builtin_mod = mod.main_mod.deps.get("builtin").?;
2433 try seen_table.put(builtin_pkg, {});2431 try seen_table.put(builtin_mod, {});
24342432
2435 try addPackageTableToCacheHash(&man.hash, &arena_allocator, mod.main_pkg.table, &seen_table, .{ .files = man });2433 try addModuleTableToCacheHash(&man.hash, &arena_allocator, mod.main_mod.deps, &seen_table, .{ .files = man });
2436 }2434 }
24372435
2438 // Synchronize with other matching comments: ZigOnlyHashStuff2436 // Synchronize with other matching comments: ZigOnlyHashStuff
...@@ -3564,8 +3562,8 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v...@@ -3564,8 +3562,8 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
3564 decl.analysis = .codegen_failure_retryable;3562 decl.analysis = .codegen_failure_retryable;
3565 };3563 };
3566 },3564 },
3567 .analyze_pkg => |pkg| {3565 .analyze_mod => |pkg| {
3568 const named_frame = tracy.namedFrame("analyze_pkg");3566 const named_frame = tracy.namedFrame("analyze_mod");
3569 defer named_frame.end();3567 defer named_frame.end();
35703568
3571 const module = comp.bin_file.options.module.?;3569 const module = comp.bin_file.options.module.?;
...@@ -6379,11 +6377,11 @@ fn buildOutputFromZig(...@@ -6379,11 +6377,11 @@ fn buildOutputFromZig(
63796377
6380 std.debug.assert(output_mode != .Exe);6378 std.debug.assert(output_mode != .Exe);
63816379
6382 var main_pkg: Package = .{6380 var main_mod: Package = .{
6383 .root_src_directory = comp.zig_lib_directory,6381 .root_src_directory = comp.zig_lib_directory,
6384 .root_src_path = src_basename,6382 .root_src_path = src_basename,
6385 };6383 };
6386 defer main_pkg.deinitTable(comp.gpa);6384 defer main_mod.deinitTable(comp.gpa);
6387 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];6385 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
6388 const target = comp.getTarget();6386 const target = comp.getTarget();
6389 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{6387 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
...@@ -6404,7 +6402,7 @@ fn buildOutputFromZig(...@@ -6404,7 +6402,7 @@ fn buildOutputFromZig(
6404 .cache_mode = .whole,6402 .cache_mode = .whole,
6405 .target = target,6403 .target = target,
6406 .root_name = root_name,6404 .root_name = root_name,
6407 .main_pkg = &main_pkg,6405 .main_mod = &main_mod,
6408 .output_mode = output_mode,6406 .output_mode = output_mode,
6409 .thread_pool = comp.thread_pool,6407 .thread_pool = comp.thread_pool,
6410 .libc_installation = comp.bin_file.options.libc_installation,6408 .libc_installation = comp.bin_file.options.libc_installation,
...@@ -6481,7 +6479,7 @@ pub fn build_crt_file(...@@ -6481,7 +6479,7 @@ pub fn build_crt_file(
6481 .cache_mode = .whole,6479 .cache_mode = .whole,
6482 .target = target,6480 .target = target,
6483 .root_name = root_name,6481 .root_name = root_name,
6484 .main_pkg = null,6482 .main_mod = null,
6485 .output_mode = output_mode,6483 .output_mode = output_mode,
6486 .thread_pool = comp.thread_pool,6484 .thread_pool = comp.thread_pool,
6487 .libc_installation = comp.bin_file.options.libc_installation,6485 .libc_installation = comp.bin_file.options.libc_installation,
src/Manifest.zig+8-6
...@@ -1,6 +1,10 @@...@@ -1,6 +1,10 @@
1pub const max_bytes = 10 * 1024 * 1024;1pub const max_bytes = 10 * 1024 * 1024;
2pub const basename = "build.zig.zon";2pub const basename = "build.zig.zon";
3pub const Hash = std.crypto.hash.sha2.Sha256;3pub const Hash = std.crypto.hash.sha2.Sha256;
4pub const Digest = [Hash.digest_length]u8;
5pub const multihash_len = 1 + 1 + Hash.digest_length;
6pub const multihash_hex_digest_len = 2 * multihash_len;
7pub const MultiHashHexDigest = [multihash_hex_digest_len]u8;
48
5pub const Dependency = struct {9pub const Dependency = struct {
6 location: union(enum) {10 location: union(enum) {
...@@ -46,7 +50,6 @@ comptime {...@@ -46,7 +50,6 @@ comptime {
46 assert(@intFromEnum(multihash_function) < 127);50 assert(@intFromEnum(multihash_function) < 127);
47 assert(Hash.digest_length < 127);51 assert(Hash.digest_length < 127);
48}52}
49pub const multihash_len = 1 + 1 + Hash.digest_length;
5053
51name: []const u8,54name: []const u8,
52version: std.SemanticVersion,55version: std.SemanticVersion,
...@@ -122,8 +125,8 @@ test hex64 {...@@ -122,8 +125,8 @@ test hex64 {
122 try std.testing.expectEqualStrings("[00efcdab78563412]", s);125 try std.testing.expectEqualStrings("[00efcdab78563412]", s);
123}126}
124127
125pub fn hexDigest(digest: [Hash.digest_length]u8) [multihash_len * 2]u8 {128pub fn hexDigest(digest: Digest) MultiHashHexDigest {
126 var result: [multihash_len * 2]u8 = undefined;129 var result: MultiHashHexDigest = undefined;
127130
128 result[0] = hex_charset[@intFromEnum(multihash_function) >> 4];131 result[0] = hex_charset[@intFromEnum(multihash_function) >> 4];
129 result[1] = hex_charset[@intFromEnum(multihash_function) & 15];132 result[1] = hex_charset[@intFromEnum(multihash_function) & 15];
...@@ -339,10 +342,9 @@ const Parse = struct {...@@ -339,10 +342,9 @@ const Parse = struct {
339 }342 }
340 }343 }
341344
342 const hex_multihash_len = 2 * Manifest.multihash_len;345 if (h.len != multihash_hex_digest_len) {
343 if (h.len != hex_multihash_len) {
344 return fail(p, tok, "wrong hash size. expected: {d}, found: {d}", .{346 return fail(p, tok, "wrong hash size. expected: {d}, found: {d}", .{
345 hex_multihash_len, h.len,347 multihash_hex_digest_len, h.len,
346 });348 });
347 }349 }
348350
src/Module.zig+37-44
...@@ -55,10 +55,10 @@ comp: *Compilation,...@@ -55,10 +55,10 @@ comp: *Compilation,
55/// Where build artifacts and incremental compilation metadata serialization go.55/// Where build artifacts and incremental compilation metadata serialization go.
56zig_cache_artifact_directory: Compilation.Directory,56zig_cache_artifact_directory: Compilation.Directory,
57/// Pointer to externally managed resource.57/// Pointer to externally managed resource.
58root_pkg: *Package,58root_mod: *Package.Module,
59/// Normally, `main_pkg` and `root_pkg` are the same. The exception is `zig test`, in which59/// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which
60/// `root_pkg` is the test runner, and `main_pkg` is the user's source file which has the tests.60/// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests.
61main_pkg: *Package,61main_mod: *Package.Module,
62sema_prog_node: std.Progress.Node = undefined,62sema_prog_node: std.Progress.Node = undefined,
6363
64/// Used by AstGen worker to load and store ZIR cache.64/// Used by AstGen worker to load and store ZIR cache.
...@@ -973,8 +973,8 @@ pub const File = struct {...@@ -973,8 +973,8 @@ pub const File = struct {
973 tree: Ast,973 tree: Ast,
974 /// Whether this is populated or not depends on `zir_loaded`.974 /// Whether this is populated or not depends on `zir_loaded`.
975 zir: Zir,975 zir: Zir,
976 /// Package that this file is a part of, managed externally.976 /// Module that this file is a part of, managed externally.
977 pkg: *Package,977 mod: *Package.Module,
978 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.978 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.
979 multi_pkg: bool = false,979 multi_pkg: bool = false,
980 /// List of references to this file, used for multi-package errors.980 /// List of references to this file, used for multi-package errors.
...@@ -1058,14 +1058,9 @@ pub const File = struct {...@@ -1058,14 +1058,9 @@ pub const File = struct {
1058 .stat = file.stat,1058 .stat = file.stat,
1059 };1059 };
10601060
1061 const root_dir_path = file.pkg.root_src_directory.path orelse ".";
1062 log.debug("File.getSource, not cached. pkgdir={s} sub_file_path={s}", .{
1063 root_dir_path, file.sub_file_path,
1064 });
1065
1066 // Keep track of inode, file size, mtime, hash so we can detect which files1061 // Keep track of inode, file size, mtime, hash so we can detect which files
1067 // have been modified when an incremental update is requested.1062 // have been modified when an incremental update is requested.
1068 var f = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{});1063 var f = try file.mod.root.openFile(file.sub_file_path, .{});
1069 defer f.close();1064 defer f.close();
10701065
1071 const stat = try f.stat();1066 const stat = try f.stat();
...@@ -1134,14 +1129,12 @@ pub const File = struct {...@@ -1134,14 +1129,12 @@ pub const File = struct {
1134 return ip.getOrPutTrailingString(mod.gpa, ip.string_bytes.items.len - start);1129 return ip.getOrPutTrailingString(mod.gpa, ip.string_bytes.items.len - start);
1135 }1130 }
11361131
1137 /// Returns the full path to this file relative to its package.
1138 pub fn fullPath(file: File, ally: Allocator) ![]u8 {1132 pub fn fullPath(file: File, ally: Allocator) ![]u8 {
1139 return file.pkg.root_src_directory.join(ally, &[_][]const u8{file.sub_file_path});1133 return file.mod.root.joinString(ally, file.sub_file_path);
1140 }1134 }
11411135
1142 /// Returns the full path to this file relative to its package.
1143 pub fn fullPathZ(file: File, ally: Allocator) ![:0]u8 {1136 pub fn fullPathZ(file: File, ally: Allocator) ![:0]u8 {
1144 return file.pkg.root_src_directory.joinZ(ally, &[_][]const u8{file.sub_file_path});1137 return file.mod.root.joinStringZ(ally, file.sub_file_path);
1145 }1138 }
11461139
1147 pub fn dumpSrc(file: *File, src: LazySrcLoc) void {1140 pub fn dumpSrc(file: *File, src: LazySrcLoc) void {
...@@ -2543,25 +2536,25 @@ pub fn deinit(mod: *Module) void {...@@ -2543,25 +2536,25 @@ pub fn deinit(mod: *Module) void {
25432536
2544 mod.deletion_set.deinit(gpa);2537 mod.deletion_set.deinit(gpa);
25452538
2546 // The callsite of `Compilation.create` owns the `main_pkg`, however2539 // The callsite of `Compilation.create` owns the `main_mod`, however
2547 // Module owns the builtin and std packages that it adds.2540 // Module owns the builtin and std packages that it adds.
2548 if (mod.main_pkg.table.fetchRemove("builtin")) |kv| {2541 if (mod.main_mod.table.fetchRemove("builtin")) |kv| {
2549 gpa.free(kv.key);2542 gpa.free(kv.key);
2550 kv.value.destroy(gpa);2543 kv.value.destroy(gpa);
2551 }2544 }
2552 if (mod.main_pkg.table.fetchRemove("std")) |kv| {2545 if (mod.main_mod.table.fetchRemove("std")) |kv| {
2553 gpa.free(kv.key);2546 gpa.free(kv.key);
2554 // It's possible for main_pkg to be std when running 'zig test'! In this case, we must not2547 // It's possible for main_mod to be std when running 'zig test'! In this case, we must not
2555 // destroy it, since it would lead to a double-free.2548 // destroy it, since it would lead to a double-free.
2556 if (kv.value != mod.main_pkg) {2549 if (kv.value != mod.main_mod) {
2557 kv.value.destroy(gpa);2550 kv.value.destroy(gpa);
2558 }2551 }
2559 }2552 }
2560 if (mod.main_pkg.table.fetchRemove("root")) |kv| {2553 if (mod.main_mod.table.fetchRemove("root")) |kv| {
2561 gpa.free(kv.key);2554 gpa.free(kv.key);
2562 }2555 }
2563 if (mod.root_pkg != mod.main_pkg) {2556 if (mod.root_mod != mod.main_mod) {
2564 mod.root_pkg.destroy(gpa);2557 mod.root_mod.destroy(gpa);
2565 }2558 }
25662559
2567 mod.compile_log_text.deinit(gpa);2560 mod.compile_log_text.deinit(gpa);
...@@ -2715,7 +2708,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -2715,7 +2708,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
27152708
2716 const stat = try source_file.stat();2709 const stat = try source_file.stat();
27172710
2718 const want_local_cache = file.pkg == mod.main_pkg;2711 const want_local_cache = file.pkg == mod.main_mod;
2719 const digest = hash: {2712 const digest = hash: {
2720 var path_hash: Cache.HashHelper = .{};2713 var path_hash: Cache.HashHelper = .{};
2721 path_hash.addBytes(build_options.version);2714 path_hash.addBytes(build_options.version);
...@@ -3158,23 +3151,23 @@ pub fn populateBuiltinFile(mod: *Module) !void {...@@ -3158,23 +3151,23 @@ pub fn populateBuiltinFile(mod: *Module) !void {
3158 comp.mutex.lock();3151 comp.mutex.lock();
3159 defer comp.mutex.unlock();3152 defer comp.mutex.unlock();
31603153
3161 const builtin_pkg = mod.main_pkg.table.get("builtin").?;3154 const builtin_mod = mod.main_mod.table.get("builtin").?;
3162 const result = try mod.importPkg(builtin_pkg);3155 const result = try mod.importPkg(builtin_mod);
3163 break :blk .{3156 break :blk .{
3164 .file = result.file,3157 .file = result.file,
3165 .pkg = builtin_pkg,3158 .pkg = builtin_mod,
3166 };3159 };
3167 };3160 };
3168 const file = pkg_and_file.file;3161 const file = pkg_and_file.file;
3169 const builtin_pkg = pkg_and_file.pkg;3162 const builtin_mod = pkg_and_file.pkg;
3170 const gpa = mod.gpa;3163 const gpa = mod.gpa;
3171 file.source = try comp.generateBuiltinZigSource(gpa);3164 file.source = try comp.generateBuiltinZigSource(gpa);
3172 file.source_loaded = true;3165 file.source_loaded = true;
31733166
3174 if (builtin_pkg.root_src_directory.handle.statFile(builtin_pkg.root_src_path)) |stat| {3167 if (builtin_mod.root_src_directory.handle.statFile(builtin_mod.root_src_path)) |stat| {
3175 if (stat.size != file.source.len) {3168 if (stat.size != file.source.len) {
3176 const full_path = try builtin_pkg.root_src_directory.join(gpa, &.{3169 const full_path = try builtin_mod.root_src_directory.join(gpa, &.{
3177 builtin_pkg.root_src_path,3170 builtin_mod.root_src_path,
3178 });3171 });
3179 defer gpa.free(full_path);3172 defer gpa.free(full_path);
31803173
...@@ -3184,7 +3177,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {...@@ -3184,7 +3177,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {
3184 .{ full_path, file.source.len, stat.size },3177 .{ full_path, file.source.len, stat.size },
3185 );3178 );
31863179
3187 try writeBuiltinFile(file, builtin_pkg);3180 try writeBuiltinFile(file, builtin_mod);
3188 } else {3181 } else {
3189 file.stat = .{3182 file.stat = .{
3190 .size = stat.size,3183 .size = stat.size,
...@@ -3198,7 +3191,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {...@@ -3198,7 +3191,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {
3198 error.PipeBusy => unreachable, // it's not a pipe3191 error.PipeBusy => unreachable, // it's not a pipe
3199 error.WouldBlock => unreachable, // not asking for non-blocking I/O3192 error.WouldBlock => unreachable, // not asking for non-blocking I/O
32003193
3201 error.FileNotFound => try writeBuiltinFile(file, builtin_pkg),3194 error.FileNotFound => try writeBuiltinFile(file, builtin_mod),
32023195
3203 else => |e| return e,3196 else => |e| return e,
3204 }3197 }
...@@ -3212,8 +3205,8 @@ pub fn populateBuiltinFile(mod: *Module) !void {...@@ -3212,8 +3205,8 @@ pub fn populateBuiltinFile(mod: *Module) !void {
3212 file.status = .success_zir;3205 file.status = .success_zir;
3213}3206}
32143207
3215fn writeBuiltinFile(file: *File, builtin_pkg: *Package) !void {3208fn writeBuiltinFile(file: *File, builtin_mod: *Package.Module) !void {
3216 var af = try builtin_pkg.root_src_directory.handle.atomicFile(builtin_pkg.root_src_path, .{});3209 var af = try builtin_mod.root_src_directory.handle.atomicFile(builtin_mod.root_src_path, .{});
3217 defer af.deinit();3210 defer af.deinit();
3218 try af.file.writeAll(file.source);3211 try af.file.writeAll(file.source);
3219 try af.finish();3212 try af.finish();
...@@ -3748,7 +3741,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -3748,7 +3741,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
37483741
3749 // TODO: figure out how this works under incremental changes to builtin.zig!3742 // TODO: figure out how this works under incremental changes to builtin.zig!
3750 const builtin_type_target_index: InternPool.Index = blk: {3743 const builtin_type_target_index: InternPool.Index = blk: {
3751 const std_mod = mod.main_pkg.table.get("std").?;3744 const std_mod = mod.main_mod.table.get("std").?;
3752 if (decl.getFileScope(mod).pkg != std_mod) break :blk .none;3745 if (decl.getFileScope(mod).pkg != std_mod) break :blk .none;
3753 // We're in the std module.3746 // We're in the std module.
3754 const std_file = (try mod.importPkg(std_mod)).file;3747 const std_file = (try mod.importPkg(std_mod)).file;
...@@ -4100,13 +4093,13 @@ pub fn importFile(...@@ -4100,13 +4093,13 @@ pub fn importFile(
4100 import_string: []const u8,4093 import_string: []const u8,
4101) !ImportFileResult {4094) !ImportFileResult {
4102 if (std.mem.eql(u8, import_string, "std")) {4095 if (std.mem.eql(u8, import_string, "std")) {
4103 return mod.importPkg(mod.main_pkg.table.get("std").?);4096 return mod.importPkg(mod.main_mod.table.get("std").?);
4104 }4097 }
4105 if (std.mem.eql(u8, import_string, "builtin")) {4098 if (std.mem.eql(u8, import_string, "builtin")) {
4106 return mod.importPkg(mod.main_pkg.table.get("builtin").?);4099 return mod.importPkg(mod.main_mod.table.get("builtin").?);
4107 }4100 }
4108 if (std.mem.eql(u8, import_string, "root")) {4101 if (std.mem.eql(u8, import_string, "root")) {
4109 return mod.importPkg(mod.root_pkg);4102 return mod.importPkg(mod.root_mod);
4110 }4103 }
4111 if (cur_file.pkg.table.get(import_string)) |pkg| {4104 if (cur_file.pkg.table.get(import_string)) |pkg| {
4112 return mod.importPkg(pkg);4105 return mod.importPkg(pkg);
...@@ -4462,14 +4455,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err...@@ -4462,14 +4455,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
4462 // test decl with no name. Skip the part where we check against4455 // test decl with no name. Skip the part where we check against
4463 // the test name filter.4456 // the test name filter.
4464 if (!comp.bin_file.options.is_test) break :blk false;4457 if (!comp.bin_file.options.is_test) break :blk false;
4465 if (decl_pkg != mod.main_pkg) break :blk false;4458 if (decl_pkg != mod.main_mod) break :blk false;
4466 try mod.test_functions.put(gpa, new_decl_index, {});4459 try mod.test_functions.put(gpa, new_decl_index, {});
4467 break :blk true;4460 break :blk true;
4468 },4461 },
4469 else => blk: {4462 else => blk: {
4470 if (!is_named_test) break :blk false;4463 if (!is_named_test) break :blk false;
4471 if (!comp.bin_file.options.is_test) break :blk false;4464 if (!comp.bin_file.options.is_test) break :blk false;
4472 if (decl_pkg != mod.main_pkg) break :blk false;4465 if (decl_pkg != mod.main_mod) break :blk false;
4473 if (comp.test_filter) |test_filter| {4466 if (comp.test_filter) |test_filter| {
4474 if (mem.indexOf(u8, ip.stringToSlice(decl_name), test_filter) == null) {4467 if (mem.indexOf(u8, ip.stringToSlice(decl_name), test_filter) == null) {
4475 break :blk false;4468 break :blk false;
...@@ -5596,8 +5589,8 @@ pub fn populateTestFunctions(...@@ -5596,8 +5589,8 @@ pub fn populateTestFunctions(
5596) !void {5589) !void {
5597 const gpa = mod.gpa;5590 const gpa = mod.gpa;
5598 const ip = &mod.intern_pool;5591 const ip = &mod.intern_pool;
5599 const builtin_pkg = mod.main_pkg.table.get("builtin").?;5592 const builtin_mod = mod.main_mod.table.get("builtin").?;
5600 const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;5593 const builtin_file = (mod.importPkg(builtin_mod) catch unreachable).file;
5601 const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?);5594 const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?);
5602 const builtin_namespace = mod.namespacePtr(root_decl.src_namespace);5595 const builtin_namespace = mod.namespacePtr(root_decl.src_namespace);
5603 const test_functions_str = try ip.getOrPutString(gpa, "test_functions");5596 const test_functions_str = try ip.getOrPutString(gpa, "test_functions");
src/Package.zig+69-233
...@@ -1,251 +1,87 @@...@@ -1,251 +1,87 @@
1const Package = @This();1pub const Module = @import("Package/Module.zig");
22pub const Fetch = @import("Package/Fetch.zig");
3const builtin = @import("builtin");
4const std = @import("std");
5const fs = std.fs;
6const mem = std.mem;
7const Allocator = mem.Allocator;
8const ascii = std.ascii;
9const assert = std.debug.assert;
10const log = std.log.scoped(.package);
11const main = @import("main.zig");
12const ThreadPool = std.Thread.Pool;
13
14const Compilation = @import("Compilation.zig");
15const Module = @import("Module.zig");
16const Cache = std.Build.Cache;
17const build_options = @import("build_options");
18const Fetch = @import("Package/Fetch.zig");
19
20pub const build_zig_basename = "build.zig";3pub const build_zig_basename = "build.zig";
21pub const Manifest = @import("Manifest.zig");4pub const Manifest = @import("Manifest.zig");
22pub const Table = std.StringHashMapUnmanaged(*Package);
23
24root_src_directory: Compilation.Directory,
25/// Relative to `root_src_directory`. May contain path separators.
26root_src_path: []const u8,
27/// The dependency table of this module. Shared dependencies such as 'std', 'builtin', and 'root'
28/// are not specified in every dependency table, but instead only in the table of `main_pkg`.
29/// `Module.importFile` is responsible for detecting these names and using the correct package.
30table: Table = .{},
31/// Whether to free `root_src_directory` on `destroy`.
32root_src_directory_owned: bool = false,
33
34/// Allocate a Package. No references to the slices passed are kept.
35pub fn create(
36 gpa: Allocator,
37 /// Null indicates the current working directory
38 root_src_dir_path: ?[]const u8,
39 /// Relative to root_src_dir_path
40 root_src_path: []const u8,
41) !*Package {
42 const ptr = try gpa.create(Package);
43 errdefer gpa.destroy(ptr);
44
45 const owned_dir_path = if (root_src_dir_path) |p| try gpa.dupe(u8, p) else null;
46 errdefer if (owned_dir_path) |p| gpa.free(p);
47
48 const owned_src_path = try gpa.dupe(u8, root_src_path);
49 errdefer gpa.free(owned_src_path);
505
51 ptr.* = .{6pub const Path = struct {
52 .root_src_directory = .{7 root_dir: Cache.Directory,
53 .path = owned_dir_path,8 /// The path, relative to the root dir, that this `Path` represents.
54 .handle = if (owned_dir_path) |p| try fs.cwd().openDir(p, .{}) else fs.cwd(),9 /// Empty string means the root_dir is the path.
55 },10 sub_path: []const u8 = "",
56 .root_src_path = owned_src_path,
57 .root_src_directory_owned = true,
58 };
5911
60 return ptr;12 pub fn cwd() Path {
61}13 return .{ .root_dir = Cache.Directory.cwd() };
62
63pub fn createWithDir(
64 gpa: Allocator,
65 directory: Compilation.Directory,
66 /// Relative to `directory`. If null, means `directory` is the root src dir
67 /// and is owned externally.
68 root_src_dir_path: ?[]const u8,
69 /// Relative to root_src_dir_path
70 root_src_path: []const u8,
71) !*Package {
72 const ptr = try gpa.create(Package);
73 errdefer gpa.destroy(ptr);
74
75 const owned_src_path = try gpa.dupe(u8, root_src_path);
76 errdefer gpa.free(owned_src_path);
77
78 if (root_src_dir_path) |p| {
79 const owned_dir_path = try directory.join(gpa, &[1][]const u8{p});
80 errdefer gpa.free(owned_dir_path);
81
82 ptr.* = .{
83 .root_src_directory = .{
84 .path = owned_dir_path,
85 .handle = try directory.handle.openDir(p, .{}),
86 },
87 .root_src_directory_owned = true,
88 .root_src_path = owned_src_path,
89 };
90 } else {
91 ptr.* = .{
92 .root_src_directory = directory,
93 .root_src_directory_owned = false,
94 .root_src_path = owned_src_path,
95 };
96 }14 }
97 return ptr;
98}
9915
100/// Free all memory associated with this package. It does not destroy any packages16 pub fn join(p: Path, allocator: Allocator, sub_path: []const u8) Allocator.Error!Path {
101/// inside its table; the caller is responsible for calling destroy() on them.17 const parts: []const []const u8 =
102pub fn destroy(pkg: *Package, gpa: Allocator) void {18 if (p.sub_path.len == 0) &.{sub_path} else &.{ p.sub_path, sub_path };
103 gpa.free(pkg.root_src_path);19 return .{
10420 .root_dir = p.root_dir,
105 if (pkg.root_src_directory_owned) {21 .sub_path = try fs.path.join(allocator, parts),
106 // If root_src_directory.path is null then the handle is the cwd()22 };
107 // which shouldn't be closed.
108 if (pkg.root_src_directory.path) |p| {
109 gpa.free(p);
110 pkg.root_src_directory.handle.close();
111 }
112 }23 }
11324
114 pkg.deinitTable(gpa);25 pub fn joinString(p: Path, allocator: Allocator, sub_path: []const u8) Allocator.Error![]u8 {
115 gpa.destroy(pkg);26 const parts: []const []const u8 =
116}27 if (p.sub_path.len == 0) &.{sub_path} else &.{ p.sub_path, sub_path };
11728 return p.root_dir.join(allocator, parts);
118/// Only frees memory associated with the table.
119pub fn deinitTable(pkg: *Package, gpa: Allocator) void {
120 pkg.table.deinit(gpa);
121}
122
123pub fn add(pkg: *Package, gpa: Allocator, name: []const u8, package: *Package) !void {
124 try pkg.table.ensureUnusedCapacity(gpa, 1);
125 const name_dupe = try gpa.dupe(u8, name);
126 pkg.table.putAssumeCapacityNoClobber(name_dupe, package);
127}
128
129/// Compute a readable name for the package. The returned name should be freed from gpa. This
130/// function is very slow, as it traverses the whole package hierarchy to find a path to this
131/// package. It should only be used for error output.
132pub fn getName(target: *const Package, gpa: Allocator, mod: Module) ![]const u8 {
133 // we'll do a breadth-first search from the root module to try and find a short name for this
134 // module, using a DoublyLinkedList of module/parent pairs. note that the "parent" there is
135 // just the first-found shortest path - a module may be children of arbitrarily many other
136 // modules. This path may vary between executions due to hashmap iteration order, but that
137 // doesn't matter too much.
138 var node_arena = std.heap.ArenaAllocator.init(gpa);
139 defer node_arena.deinit();
140 const Parented = struct {
141 parent: ?*const @This(),
142 mod: *const Package,
143 };
144 const Queue = std.DoublyLinkedList(Parented);
145 var to_check: Queue = .{};
146
147 {
148 const new = try node_arena.allocator().create(Queue.Node);
149 new.* = .{ .data = .{ .parent = null, .mod = mod.root_pkg } };
150 to_check.prepend(new);
151 }29 }
15230
153 if (mod.main_pkg != mod.root_pkg) {31 pub fn joinStringZ(p: Path, allocator: Allocator, sub_path: []const u8) Allocator.Error![]u8 {
154 const new = try node_arena.allocator().create(Queue.Node);32 const parts: []const []const u8 =
155 // TODO: once #12201 is resolved, we may want a way of indicating a different name for this33 if (p.sub_path.len == 0) &.{sub_path} else &.{ p.sub_path, sub_path };
156 new.* = .{ .data = .{ .parent = null, .mod = mod.main_pkg } };34 return p.root_dir.joinZ(allocator, parts);
157 to_check.prepend(new);
158 }35 }
15936
160 // set of modules we've already checked to prevent loops37 pub fn openFile(
161 var checked = std.AutoHashMap(*const Package, void).init(gpa);38 p: Path,
162 defer checked.deinit();39 sub_path: []const u8,
16340 flags: fs.File.OpenFlags,
164 const linked = while (to_check.pop()) |node| {41 ) fs.File.OpenError!fs.File {
165 const check = &node.data;42 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
16643 const joined_path = if (p.sub_path.len == 0) sub_path else p: {
167 if (checked.contains(check.mod)) continue;44 break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
168 try checked.put(check.mod, {});45 p.sub_path, sub_path,
16946 }) catch return error.NameTooLong;
170 if (check.mod == target) break check;47 };
17148 return p.root_dir.handle.openFile(joined_path, flags);
172 var it = check.mod.table.iterator();
173 while (it.next()) |kv| {
174 var new = try node_arena.allocator().create(Queue.Node);
175 new.* = .{ .data = .{
176 .parent = check,
177 .mod = kv.value_ptr.*,
178 } };
179 to_check.prepend(new);
180 }
181 } else {
182 // this can happen for e.g. @cImport packages
183 return gpa.dupe(u8, "<unnamed>");
184 };
185
186 // we found a path to the module! unfortunately, we can only traverse *up* it, so we have to put
187 // all the names into a buffer so we can then print them in order.
188 var names = std.ArrayList([]const u8).init(gpa);
189 defer names.deinit();
190
191 var cur: *const Parented = linked;
192 while (cur.parent) |parent| : (cur = parent) {
193 // find cur's name in parent
194 var it = parent.mod.table.iterator();
195 const name = while (it.next()) |kv| {
196 if (kv.value_ptr.* == cur.mod) {
197 break kv.key_ptr.*;
198 }
199 } else unreachable;
200 try names.append(name);
201 }49 }
20250
203 // finally, print the names into a buffer!51 pub fn makeOpenPath(p: Path, sub_path: []const u8, opts: fs.OpenDirOptions) !fs.Dir {
204 var buf = std.ArrayList(u8).init(gpa);52 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
205 defer buf.deinit();53 const joined_path = if (p.sub_path.len == 0) sub_path else p: {
206 try buf.writer().writeAll("root");54 break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
207 var i: usize = names.items.len;55 p.sub_path, sub_path,
208 while (i > 0) {56 }) catch return error.NameTooLong;
209 i -= 1;57 };
210 try buf.writer().print(".{s}", .{names.items[i]});58 return p.root_dir.handle.makeOpenPath(joined_path, opts);
211 }59 }
21260
213 return buf.toOwnedSlice();61 pub fn format(
214}62 self: Path,
21563 comptime fmt_string: []const u8,
216pub fn createFilePkg(64 options: std.fmt.FormatOptions,
217 gpa: Allocator,65 writer: anytype,
218 cache_directory: Compilation.Directory,66 ) !void {
219 basename: []const u8,67 _ = options;
220 contents: []const u8,68 if (fmt_string.len > 0)
221) !*Package {69 std.fmt.invalidFmtError(fmt_string, self);
222 const rand_int = std.crypto.random.int(u64);70 if (self.root_dir.path) |p| {
223 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ Manifest.hex64(rand_int);71 try writer.writeAll(p);
224 {72 try writer.writeAll(fs.path.sep_str);
225 var tmp_dir = try cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{});73 }
226 defer tmp_dir.close();74 if (self.sub_path.len > 0) {
227 try tmp_dir.writeFile(basename, contents);75 try writer.writeAll(self.sub_path);
76 try writer.writeAll(fs.path.sep_str);
77 }
228 }78 }
229
230 var hh: Cache.HashHelper = .{};
231 hh.addBytes(build_options.version);
232 hh.addBytes(contents);
233 const hex_digest = hh.final();
234
235 const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
236 try Fetch.renameTmpIntoCache(cache_directory.handle, tmp_dir_sub_path, o_dir_sub_path);
237
238 return createWithDir(gpa, cache_directory, o_dir_sub_path, basename);
239}
240
241const hex_multihash_len = 2 * Manifest.multihash_len;
242const MultiHashHexDigest = [hex_multihash_len]u8;
243
244const DependencyModule = union(enum) {
245 zig_pkg: *Package,
246 non_zig_pkg: *Package,
247};79};
248/// This is to avoid creating multiple modules for the same build.zig file.80
249/// If the value is `null`, the package is a known dependency, but has not yet81const Package = @This();
250/// been fetched.82const builtin = @import("builtin");
251pub const AllModules = std.AutoHashMapUnmanaged(MultiHashHexDigest, ?DependencyModule);83const std = @import("std");
84const fs = std.fs;
85const Allocator = std.mem.Allocator;
86const assert = std.debug.assert;
87const Cache = std.Build.Cache;
src/Package/Fetch.zig+446-205
...@@ -27,59 +27,84 @@...@@ -27,59 +27,84 @@
27//! All of this must be done with only referring to the state inside this struct27//! All of this must be done with only referring to the state inside this struct
28//! because this work will be done in a dedicated thread.28//! because this work will be done in a dedicated thread.
2929
30/// Try to avoid this as much as possible since arena will have less contention.
31gpa: Allocator,
32arena: std.heap.ArenaAllocator,30arena: std.heap.ArenaAllocator,
33location: Location,31location: Location,
34location_tok: std.zig.Ast.TokenIndex,32location_tok: std.zig.Ast.TokenIndex,
35hash_tok: std.zig.Ast.TokenIndex,33hash_tok: std.zig.Ast.TokenIndex,
36global_cache: Cache.Directory,34parent_package_root: Package.Path,
37parent_package_root: Path,
38parent_manifest_ast: ?*const std.zig.Ast,35parent_manifest_ast: ?*const std.zig.Ast,
39prog_node: *std.Progress.Node,36prog_node: *std.Progress.Node,
40http_client: *std.http.Client,
41thread_pool: *ThreadPool,
42job_queue: *JobQueue,37job_queue: *JobQueue,
43wait_group: *WaitGroup,38/// If true, don't add an error for a missing hash. This flag is not passed
39/// down to recursive dependencies. It's intended to be used only be the CLI.
40omit_missing_hash_error: bool,
4441
45// Above this are fields provided as inputs to `run`.42// Above this are fields provided as inputs to `run`.
46// Below this are fields populated by `run`.43// Below this are fields populated by `run`.
4744
48/// This will either be relative to `global_cache`, or to the build root of45/// This will either be relative to `global_cache`, or to the build root of
49/// the root package.46/// the root package.
50package_root: Path,47package_root: Package.Path,
51error_bundle: std.zig.ErrorBundle.Wip,48error_bundle: std.zig.ErrorBundle.Wip,
52manifest: ?Manifest,49manifest: ?Manifest,
53manifest_ast: ?*std.zig.Ast,50manifest_ast: std.zig.Ast,
54actual_hash: Digest,51actual_hash: Manifest.Digest,
55/// Fetch logic notices whether a package has a build.zig file and sets this flag.52/// Fetch logic notices whether a package has a build.zig file and sets this flag.
56has_build_zig: bool,53has_build_zig: bool,
57/// Indicates whether the task aborted due to an out-of-memory condition.54/// Indicates whether the task aborted due to an out-of-memory condition.
58oom_flag: bool,55oom_flag: bool,
5956
57/// Contains shared state among all `Fetch` tasks.
60pub const JobQueue = struct {58pub const JobQueue = struct {
61 mutex: std.Thread.Mutex = .{},59 mutex: std.Thread.Mutex = .{},
62};60 /// Protected by `mutex`.
6361 table: Table = .{},
64pub const Digest = [Manifest.Hash.digest_length]u8;62 /// `table` may be missing some tasks such as ones that failed, so this
65pub const MultiHashHexDigest = [hex_multihash_len]u8;63 /// field contains references to all of them.
6664 /// Protected by `mutex`.
67pub const Path = struct {65 all_fetches: std.ArrayListUnmanaged(*Fetch) = .{},
68 root_dir: Cache.Directory,66
69 /// The path, relative to the root dir, that this `Path` represents.67 http_client: *std.http.Client,
70 /// Empty string means the root_dir is the path.68 thread_pool: *ThreadPool,
71 sub_path: []const u8 = "",69 wait_group: WaitGroup = .{},
70 global_cache: Cache.Directory,
71 recursive: bool,
72 work_around_btrfs_bug: bool,
73
74 pub const Table = std.AutoHashMapUnmanaged(Manifest.MultiHashHexDigest, *Fetch);
75
76 pub fn deinit(jq: *JobQueue) void {
77 if (jq.all_fetches.items.len == 0) return;
78 const gpa = jq.all_fetches.items[0].arena.child_allocator;
79 jq.table.deinit(gpa);
80 // These must be deinitialized in reverse order because subsequent
81 // `Fetch` instances are allocated in prior ones' arenas.
82 // Sorry, I know it's a bit weird, but it slightly simplifies the
83 // critical section.
84 while (jq.all_fetches.popOrNull()) |f| f.deinit();
85 jq.all_fetches.deinit(gpa);
86 jq.* = undefined;
87 }
72};88};
7389
74pub const Location = union(enum) {90pub const Location = union(enum) {
75 remote: Remote,91 remote: Remote,
92 /// A directory found inside the parent package.
76 relative_path: []const u8,93 relative_path: []const u8,
94 /// Recursive Fetch tasks will never use this Location, but it may be
95 /// passed in by the CLI. Indicates the file contents here should be copied
96 /// into the global package cache. It may be a file relative to the cwd or
97 /// absolute, in which case it should be treated exactly like a `file://`
98 /// URL, or a directory, in which case it should be treated as an
99 /// already-unpacked directory (but still needs to be copied into the
100 /// global package cache and have inclusion rules applied).
101 path_or_url: []const u8,
77102
78 pub const Remote = struct {103 pub const Remote = struct {
79 url: []const u8,104 url: []const u8,
80 /// If this is null it means the user omitted the hash field from a dependency.105 /// If this is null it means the user omitted the hash field from a dependency.
81 /// It will be an error but the logic should still fetch and print the discovered hash.106 /// It will be an error but the logic should still fetch and print the discovered hash.
82 hash: ?[hex_multihash_len]u8,107 hash: ?Manifest.MultiHashHexDigest,
83 };108 };
84};109};
85110
...@@ -92,7 +117,11 @@ pub const RunError = error{...@@ -92,7 +117,11 @@ pub const RunError = error{
92117
93pub fn run(f: *Fetch) RunError!void {118pub fn run(f: *Fetch) RunError!void {
94 const eb = &f.error_bundle;119 const eb = &f.error_bundle;
95 const arena = f.arena_allocator.allocator();120 const arena = f.arena.allocator();
121 const gpa = f.arena.child_allocator;
122 const cache_root = f.job_queue.global_cache;
123
124 try eb.init(gpa);
96125
97 // Check the global zig package cache to see if the hash already exists. If126 // Check the global zig package cache to see if the hash already exists. If
98 // so, load, parse, and validate the build.zig.zon file therein, and skip127 // so, load, parse, and validate the build.zig.zon file therein, and skip
...@@ -111,43 +140,66 @@ pub fn run(f: *Fetch) RunError!void {...@@ -111,43 +140,66 @@ pub fn run(f: *Fetch) RunError!void {
111 );140 );
112 f.package_root = try f.parent_package_root.join(arena, sub_path);141 f.package_root = try f.parent_package_root.join(arena, sub_path);
113 try loadManifest(f, f.package_root);142 try loadManifest(f, f.package_root);
143 if (!f.job_queue.recursive) return;
114 // Package hashes are used as unique identifiers for packages, so144 // Package hashes are used as unique identifiers for packages, so
115 // we still need one for relative paths.145 // we still need one for relative paths.
116 const hash = h: {146 const digest = h: {
117 var hasher = Manifest.Hash.init(.{});147 var hasher = Manifest.Hash.init(.{});
118 // This hash is a tuple of:148 // This hash is a tuple of:
119 // * whether it relative to the global cache directory or to the root package149 // * whether it relative to the global cache directory or to the root package
120 // * the relative file path from there to the build root of the package150 // * the relative file path from there to the build root of the package
121 hasher.update(if (f.package_root.root_dir.handle == f.global_cache.handle)151 hasher.update(if (f.package_root.root_dir.eql(cache_root))
122 &package_hash_prefix_cached152 &package_hash_prefix_cached
123 else153 else
124 &package_hash_prefix_project);154 &package_hash_prefix_project);
125 hasher.update(f.package_root.sub_path);155 hasher.update(f.package_root.sub_path);
126 break :h hasher.finalResult();156 break :h hasher.finalResult();
127 };157 };
128 return queueJobsForDeps(f, hash);158 return queueJobsForDeps(f, Manifest.hexDigest(digest));
129 },159 },
130 .remote => |remote| remote,160 .remote => |remote| remote,
161 .path_or_url => |path_or_url| {
162 if (fs.cwd().openIterableDir(path_or_url, .{})) |dir| {
163 var resource: Resource = .{ .dir = dir };
164 return runResource(f, path_or_url, &resource, null);
165 } else |dir_err| {
166 const file_err = if (dir_err == error.NotDir) e: {
167 if (fs.cwd().openFile(path_or_url, .{})) |file| {
168 var resource: Resource = .{ .file = file };
169 return runResource(f, path_or_url, &resource, null);
170 } else |err| break :e err;
171 } else dir_err;
172
173 const uri = std.Uri.parse(path_or_url) catch |uri_err| {
174 return f.fail(0, try eb.printString(
175 "'{s}' could not be recognized as a file path ({s}) or an URL ({s})",
176 .{ path_or_url, @errorName(file_err), @errorName(uri_err) },
177 ));
178 };
179 var resource = try f.initResource(uri);
180 return runResource(f, uri.path, &resource, null);
181 }
182 },
131 };183 };
184
132 const s = fs.path.sep_str;185 const s = fs.path.sep_str;
133 if (remote.hash) |expected_hash| {186 if (remote.hash) |expected_hash| {
134 const pkg_sub_path = "p" ++ s ++ expected_hash;187 const pkg_sub_path = "p" ++ s ++ expected_hash;
135 if (f.global_cache.handle.access(pkg_sub_path, .{})) |_| {188 if (cache_root.handle.access(pkg_sub_path, .{})) |_| {
136 f.package_root = .{189 f.package_root = .{
137 .root_dir = f.global_cache,190 .root_dir = cache_root,
138 .sub_path = pkg_sub_path,191 .sub_path = pkg_sub_path,
139 };192 };
140 try loadManifest(f, f.package_root);193 try loadManifest(f, f.package_root);
194 if (!f.job_queue.recursive) return;
141 return queueJobsForDeps(f, expected_hash);195 return queueJobsForDeps(f, expected_hash);
142 } else |err| switch (err) {196 } else |err| switch (err) {
143 error.FileNotFound => {},197 error.FileNotFound => {},
144 else => |e| {198 else => |e| {
145 try eb.addRootErrorMessage(.{199 try eb.addRootErrorMessage(.{
146 .msg = try eb.printString("unable to open global package cache directory '{s}': {s}", .{200 .msg = try eb.printString("unable to open global package cache directory '{s}': {s}", .{
147 try f.global_cache.join(arena, .{pkg_sub_path}), @errorName(e),201 try cache_root.join(arena, &.{pkg_sub_path}), @errorName(e),
148 }),202 }),
149 .src_loc = .none,
150 .notes_len = 0,
151 });203 });
152 return error.FetchFailed;204 return error.FetchFailed;
153 },205 },
...@@ -158,22 +210,50 @@ pub fn run(f: *Fetch) RunError!void {...@@ -158,22 +210,50 @@ pub fn run(f: *Fetch) RunError!void {
158210
159 const uri = std.Uri.parse(remote.url) catch |err| return f.fail(211 const uri = std.Uri.parse(remote.url) catch |err| return f.fail(
160 f.location_tok,212 f.location_tok,
161 "invalid URI: {s}",213 try eb.printString("invalid URI: {s}", .{@errorName(err)}),
162 .{@errorName(err)},
163 );214 );
215 var resource = try f.initResource(uri);
216 return runResource(f, uri.path, &resource, remote.hash);
217}
218
219pub fn deinit(f: *Fetch) void {
220 f.error_bundle.deinit();
221 f.arena.deinit();
222}
223
224/// Consumes `resource`, even if an error is returned.
225fn runResource(
226 f: *Fetch,
227 uri_path: []const u8,
228 resource: *Resource,
229 remote_hash: ?Manifest.MultiHashHexDigest,
230) RunError!void {
231 defer resource.deinit();
232 const arena = f.arena.allocator();
233 const eb = &f.error_bundle;
234 const s = fs.path.sep_str;
235 const cache_root = f.job_queue.global_cache;
164 const rand_int = std.crypto.random.int(u64);236 const rand_int = std.crypto.random.int(u64);
165 const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int);237 const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int);
166238
239 const tmp_directory_path = try cache_root.join(arena, &.{tmp_dir_sub_path});
167 var tmp_directory: Cache.Directory = .{240 var tmp_directory: Cache.Directory = .{
168 .path = try f.global_cache.join(arena, &.{tmp_dir_sub_path}),241 .path = tmp_directory_path,
169 .handle = (try f.global_cache.handle.makeOpenPathIterable(tmp_dir_sub_path, .{})).dir,242 .handle = handle: {
243 const dir = cache_root.handle.makeOpenPathIterable(tmp_dir_sub_path, .{}) catch |err| {
244 try eb.addRootErrorMessage(.{
245 .msg = try eb.printString("unable to create temporary directory '{s}': {s}", .{
246 tmp_directory_path, @errorName(err),
247 }),
248 });
249 return error.FetchFailed;
250 };
251 break :handle dir.dir;
252 },
170 };253 };
171 defer tmp_directory.handle.close();254 defer tmp_directory.handle.close();
172255
173 var resource = try f.initResource(uri);256 try unpackResource(f, resource, uri_path, tmp_directory);
174 defer resource.deinit(); // releases more than memory
175
176 try f.unpackResource(&resource, uri.path, tmp_directory);
177257
178 // Load, parse, and validate the unpacked build.zig.zon file. It is allowed258 // Load, parse, and validate the unpacked build.zig.zon file. It is allowed
179 // for the file to be missing, in which case this fetched package is259 // for the file to be missing, in which case this fetched package is
...@@ -194,15 +274,15 @@ pub fn run(f: *Fetch) RunError!void {...@@ -194,15 +274,15 @@ pub fn run(f: *Fetch) RunError!void {
194 // Compute the package hash based on the remaining files in the temporary274 // Compute the package hash based on the remaining files in the temporary
195 // directory.275 // directory.
196276
197 if (builtin.os.tag == .linux and f.work_around_btrfs_bug) {277 if (builtin.os.tag == .linux and f.job_queue.work_around_btrfs_bug) {
198 // https://github.com/ziglang/zig/issues/17095278 // https://github.com/ziglang/zig/issues/17095
199 tmp_directory.handle.close();279 tmp_directory.handle.close();
200 const iterable_dir = f.global_cache.handle.makeOpenPathIterable(tmp_dir_sub_path, .{}) catch280 const iterable_dir = cache_root.handle.makeOpenPathIterable(tmp_dir_sub_path, .{}) catch
201 @panic("btrfs workaround failed");281 @panic("btrfs workaround failed");
202 tmp_directory.handle = iterable_dir.dir;282 tmp_directory.handle = iterable_dir.dir;
203 }283 }
204284
205 f.actual_hash = try computeHash(f, .{ .dir = tmp_directory.handle }, filter);285 f.actual_hash = try computeHash(f, tmp_directory, filter);
206286
207 // Rename the temporary directory into the global zig package cache287 // Rename the temporary directory into the global zig package cache
208 // directory. If the hash already exists, delete the temporary directory288 // directory. If the hash already exists, delete the temporary directory
...@@ -211,40 +291,54 @@ pub fn run(f: *Fetch) RunError!void {...@@ -211,40 +291,54 @@ pub fn run(f: *Fetch) RunError!void {
211 // package with the different hash is used in the future.291 // package with the different hash is used in the future.
212292
213 const dest_pkg_sub_path = "p" ++ s ++ Manifest.hexDigest(f.actual_hash);293 const dest_pkg_sub_path = "p" ++ s ++ Manifest.hexDigest(f.actual_hash);
214 try renameTmpIntoCache(f.global_cache.handle, tmp_dir_sub_path, dest_pkg_sub_path);294 renameTmpIntoCache(cache_root.handle, tmp_dir_sub_path, dest_pkg_sub_path) catch |err| {
295 const src = try cache_root.join(arena, &.{tmp_dir_sub_path});
296 const dest = try cache_root.join(arena, &.{dest_pkg_sub_path});
297 try eb.addRootErrorMessage(.{ .msg = try eb.printString(
298 "unable to rename temporary directory '{s}' into package cache directory '{s}': {s}",
299 .{ src, dest, @errorName(err) },
300 ) });
301 return error.FetchFailed;
302 };
215303
216 // Validate the computed hash against the expected hash. If invalid, this304 // Validate the computed hash against the expected hash. If invalid, this
217 // job is done.305 // job is done.
218306
219 const actual_hex = Manifest.hexDigest(f.actual_hash);307 const actual_hex = Manifest.hexDigest(f.actual_hash);
220 if (remote.hash) |declared_hash| {308 if (remote_hash) |declared_hash| {
221 if (!std.mem.eql(u8, declared_hash, &actual_hex)) {309 if (!std.mem.eql(u8, &declared_hash, &actual_hex)) {
222 return f.fail(f.hash_tok, "hash mismatch: manifest declares {s} but the fetched package has {s}", .{310 return f.fail(f.hash_tok, try eb.printString(
223 declared_hash, actual_hex,311 "hash mismatch: manifest declares {s} but the fetched package has {s}",
224 });312 .{ declared_hash, actual_hex },
313 ));
225 }314 }
226 } else {315 } else if (!f.omit_missing_hash_error) {
227 const notes_len = 1;316 const notes_len = 1;
228 try f.addErrorWithNotes(notes_len, f.location_tok, "dependency is missing hash field");317 try eb.addRootErrorMessage(.{
318 .msg = try eb.addString("dependency is missing hash field"),
319 .src_loc = try f.srcLoc(f.location_tok),
320 .notes_len = notes_len,
321 });
229 const notes_start = try eb.reserveNotes(notes_len);322 const notes_start = try eb.reserveNotes(notes_len);
230 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{323 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{
231 .msg = try eb.printString("expected .hash = \"{s}\",", .{&actual_hex}),324 .msg = try eb.printString("expected .hash = \"{s}\",", .{&actual_hex}),
232 }));325 }));
233 return error.PackageFetchFailed;326 return error.FetchFailed;
234 }327 }
235328
236 // Spawn a new fetch job for each dependency in the manifest file. Use329 // Spawn a new fetch job for each dependency in the manifest file. Use
237 // a mutex and a hash map so that redundant jobs do not get queued up.330 // a mutex and a hash map so that redundant jobs do not get queued up.
238 return queueJobsForDeps(f, .{ .hash = f.actual_hash });331 if (!f.job_queue.recursive) return;
332 return queueJobsForDeps(f, actual_hex);
239}333}
240334
241/// This function populates `f.manifest` or leaves it `null`.335/// This function populates `f.manifest` or leaves it `null`.
242fn loadManifest(f: *Fetch, pkg_root: Path) RunError!void {336fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void {
243 const eb = &f.error_bundle;337 const eb = &f.error_bundle;
244 const arena = f.arena_allocator.allocator();338 const arena = f.arena.allocator();
245 const manifest_bytes = pkg_root.readFileAllocOptions(339 const manifest_bytes = pkg_root.root_dir.handle.readFileAllocOptions(
246 arena,340 arena,
247 Manifest.basename,341 try fs.path.join(arena, &.{ pkg_root.sub_path, Manifest.basename }),
248 Manifest.max_bytes,342 Manifest.max_bytes,
249 null,343 null,
250 1,344 1,
...@@ -252,39 +346,39 @@ fn loadManifest(f: *Fetch, pkg_root: Path) RunError!void {...@@ -252,39 +346,39 @@ fn loadManifest(f: *Fetch, pkg_root: Path) RunError!void {
252 ) catch |err| switch (err) {346 ) catch |err| switch (err) {
253 error.FileNotFound => return,347 error.FileNotFound => return,
254 else => |e| {348 else => |e| {
255 const file_path = try pkg_root.join(arena, .{Manifest.basename});349 const file_path = try pkg_root.join(arena, Manifest.basename);
256 try eb.addRootErrorMessage(.{350 try eb.addRootErrorMessage(.{
257 .msg = try eb.printString("unable to load package manifest '{s}': {s}", .{351 .msg = try eb.printString("unable to load package manifest '{}': {s}", .{
258 file_path, @errorName(e),352 file_path, @errorName(e),
259 }),353 }),
260 .src_loc = .none,
261 .notes_len = 0,
262 });354 });
355 return error.FetchFailed;
263 },356 },
264 };357 };
265358
266 var ast = try std.zig.Ast.parse(arena, manifest_bytes, .zon);359 const ast = &f.manifest_ast;
267 f.manifest_ast = ast;360 ast.* = try std.zig.Ast.parse(arena, manifest_bytes, .zon);
268361
269 if (ast.errors.len > 0) {362 if (ast.errors.len > 0) {
270 const file_path = try pkg_root.join(arena, .{Manifest.basename});363 const file_path = try std.fmt.allocPrint(arena, "{}" ++ Manifest.basename, .{pkg_root});
271 try main.putAstErrorsIntoBundle(arena, ast, file_path, eb);364 try main.putAstErrorsIntoBundle(arena, ast.*, file_path, eb);
272 return error.PackageFetchFailed;365 return error.FetchFailed;
273 }366 }
274367
275 f.manifest = try Manifest.parse(arena, ast);368 f.manifest = try Manifest.parse(arena, ast.*);
369 const manifest = &f.manifest.?;
276370
277 if (f.manifest.errors.len > 0) {371 if (manifest.errors.len > 0) {
278 const file_path = try pkg_root.join(arena, .{Manifest.basename});372 const src_path = try eb.printString("{}{s}", .{ pkg_root, Manifest.basename });
279 const token_starts = ast.tokens.items(.start);373 const token_starts = ast.tokens.items(.start);
280374
281 for (f.manifest.errors) |msg| {375 for (manifest.errors) |msg| {
282 const start_loc = ast.tokenLocation(0, msg.tok);376 const start_loc = ast.tokenLocation(0, msg.tok);
283377
284 try eb.addRootErrorMessage(.{378 try eb.addRootErrorMessage(.{
285 .msg = try eb.addString(msg.msg),379 .msg = try eb.addString(msg.msg),
286 .src_loc = try eb.addSourceLocation(.{380 .src_loc = try eb.addSourceLocation(.{
287 .src_path = try eb.addString(file_path),381 .src_path = src_path,
288 .span_start = token_starts[msg.tok],382 .span_start = token_starts[msg.tok],
289 .span_end = @intCast(token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),383 .span_end = @intCast(token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),
290 .span_main = token_starts[msg.tok] + msg.off,384 .span_main = token_starts[msg.tok] + msg.off,
...@@ -292,71 +386,80 @@ fn loadManifest(f: *Fetch, pkg_root: Path) RunError!void {...@@ -292,71 +386,80 @@ fn loadManifest(f: *Fetch, pkg_root: Path) RunError!void {
292 .column = @intCast(start_loc.column),386 .column = @intCast(start_loc.column),
293 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),387 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
294 }),388 }),
295 .notes_len = 0,
296 });389 });
297 }390 }
298 return error.PackageFetchFailed;391 return error.FetchFailed;
299 }392 }
300}393}
301394
302fn queueJobsForDeps(f: *Fetch, hash: Digest) RunError!void {395fn queueJobsForDeps(f: *Fetch, hash: Manifest.MultiHashHexDigest) RunError!void {
396 assert(f.job_queue.recursive);
397
303 // If the package does not have a build.zig.zon file then there are no dependencies.398 // If the package does not have a build.zig.zon file then there are no dependencies.
304 const manifest = f.manifest orelse return;399 const manifest = f.manifest orelse return;
305400
306 const new_fetches = nf: {401 const new_fetches = nf: {
402 const deps = manifest.dependencies.values();
403 const gpa = f.arena.child_allocator;
307 // Grab the new tasks into a temporary buffer so we can unlock that mutex404 // Grab the new tasks into a temporary buffer so we can unlock that mutex
308 // as fast as possible.405 // as fast as possible.
309 // This overallocates any fetches that get skipped by the `continue` in the406 // This overallocates any fetches that get skipped by the `continue` in the
310 // loop below.407 // loop below.
311 const new_fetches = try f.arena.alloc(Fetch, manifest.dependencies.count());408 const new_fetches = try f.arena.allocator().alloc(Fetch, deps.len);
312 var new_fetch_index: usize = 0;409 var new_fetch_index: usize = 0;
313410
314 f.job_queue.lock();411 f.job_queue.mutex.lock();
315 defer f.job_queue.unlock();412 defer f.job_queue.mutex.unlock();
413
414 try f.job_queue.all_fetches.ensureUnusedCapacity(gpa, new_fetches.len);
415 try f.job_queue.table.ensureUnusedCapacity(gpa, @intCast(new_fetches.len + 1));
316416
317 // It is impossible for there to be a collision here. Consider all three cases:417 // It is impossible for there to be a collision here. Consider all three cases:
318 // * Correct hash is provided by manifest.418 // * Correct hash is provided by manifest.
319 // - Redundant jobs are skipped in the loop below.419 // - Redundant jobs are skipped in the loop below.
320 // * Incorrect has is provided by manifest.420 // * Incorrect hash is provided by manifest.
321 // - Hash mismatch error emitted; `queueJobsForDeps` is not called.421 // - Hash mismatch error emitted; `queueJobsForDeps` is not called.
322 // * Hash is not provided by manifest.422 // * Hash is not provided by manifest.
323 // - Hash missing error emitted; `queueJobsForDeps` is not called.423 // - Hash missing error emitted; `queueJobsForDeps` is not called.
324 try f.job_queue.finish(hash, f, new_fetches.len);424 f.job_queue.table.putAssumeCapacityNoClobber(hash, f);
325425
326 for (manifest.dependencies.values()) |dep| {426 for (deps) |dep| {
427 const new_fetch = &new_fetches[new_fetch_index];
327 const location: Location = switch (dep.location) {428 const location: Location = switch (dep.location) {
328 .url => |url| .{ .remote = .{429 .url => |url| .{ .remote = .{
329 .url = url,430 .url = url,
330 .hash = if (dep.hash) |h| h[0..hex_multihash_len].* else null,431 .hash = h: {
432 const h = dep.hash orelse break :h null;
433 const digest_len = @typeInfo(Manifest.MultiHashHexDigest).Array.len;
434 const multihash_digest = h[0..digest_len].*;
435 const gop = f.job_queue.table.getOrPutAssumeCapacity(multihash_digest);
436 if (gop.found_existing) continue;
437 gop.value_ptr.* = new_fetch;
438 break :h multihash_digest;
439 },
331 } },440 } },
332 .path => |path| .{ .relative_path = path },441 .path => |path| .{ .relative_path = path },
333 };442 };
334 const new_fetch = &new_fetches[new_fetch_index];
335 const already_done = f.job_queue.add(location, new_fetch);
336 if (already_done) continue;
337 new_fetch_index += 1;443 new_fetch_index += 1;
338444 f.job_queue.all_fetches.appendAssumeCapacity(new_fetch);
339 new_fetch.* = .{445 new_fetch.* = .{
340 .gpa = f.gpa,446 .arena = std.heap.ArenaAllocator.init(gpa),
341 .arena = std.heap.ArenaAllocator.init(f.gpa),
342 .location = location,447 .location = location,
343 .location_tok = dep.location_tok,448 .location_tok = dep.location_tok,
344 .hash_tok = dep.hash_tok,449 .hash_tok = dep.hash_tok,
345 .global_cache = f.global_cache,
346 .parent_package_root = f.package_root,450 .parent_package_root = f.package_root,
347 .parent_manifest_ast = f.manifest_ast.?,451 .parent_manifest_ast = &f.manifest_ast,
348 .prog_node = f.prog_node,452 .prog_node = f.prog_node,
349 .http_client = f.http_client,
350 .thread_pool = f.thread_pool,
351 .job_queue = f.job_queue,453 .job_queue = f.job_queue,
352 .wait_group = f.wait_group,454 .omit_missing_hash_error = false,
353455
354 .package_root = undefined,456 .package_root = undefined,
355 .error_bundle = .{},457 .error_bundle = undefined,
356 .manifest = null,458 .manifest = null,
357 .manifest_ast = null,459 .manifest_ast = undefined,
358 .actual_hash = undefined,460 .actual_hash = undefined,
359 .has_build_zig = false,461 .has_build_zig = false,
462 .oom_flag = false,
360 };463 };
361 }464 }
362465
...@@ -364,12 +467,14 @@ fn queueJobsForDeps(f: *Fetch, hash: Digest) RunError!void {...@@ -364,12 +467,14 @@ fn queueJobsForDeps(f: *Fetch, hash: Digest) RunError!void {
364 };467 };
365468
366 // Now it's time to give tasks to the thread pool.469 // Now it's time to give tasks to the thread pool.
367 for (new_fetches) |new_fetch| {470 const thread_pool = f.job_queue.thread_pool;
368 f.wait_group.start();471
369 f.thread_pool.spawn(workerRun, .{f}) catch |err| switch (err) {472 for (new_fetches) |*new_fetch| {
473 f.job_queue.wait_group.start();
474 thread_pool.spawn(workerRun, .{new_fetch}) catch |err| switch (err) {
370 error.OutOfMemory => {475 error.OutOfMemory => {
371 new_fetch.oom_flag = true;476 new_fetch.oom_flag = true;
372 f.wait_group.finish();477 f.job_queue.wait_group.finish();
373 continue;478 continue;
374 },479 },
375 };480 };
...@@ -377,43 +482,83 @@ fn queueJobsForDeps(f: *Fetch, hash: Digest) RunError!void {...@@ -377,43 +482,83 @@ fn queueJobsForDeps(f: *Fetch, hash: Digest) RunError!void {
377}482}
378483
379fn workerRun(f: *Fetch) void {484fn workerRun(f: *Fetch) void {
380 defer f.wait_group.finish();485 defer f.job_queue.wait_group.finish();
381 run(f) catch |err| switch (err) {486 run(f) catch |err| switch (err) {
382 error.OutOfMemory => f.oom_flag = true,487 error.OutOfMemory => f.oom_flag = true,
383 error.FetchFailed => {}, // See `error_bundle`.488 error.FetchFailed => {
489 // Nothing to do because the errors are already reported in `error_bundle`,
490 // and a reference is kept to the `Fetch` task inside `all_fetches`.
491 },
384 };492 };
385}493}
386494
387fn fail(f: *Fetch, msg_tok: std.zig.Ast.TokenIndex, msg_str: u32) RunError!void {495fn srcLoc(
388 const ast = f.parent_manifest_ast;496 f: *Fetch,
389 const token_starts = ast.tokens.items(.start);497 tok: std.zig.Ast.TokenIndex,
390 const start_loc = ast.tokenLocation(0, msg_tok);498) Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex {
499 const ast = f.parent_manifest_ast orelse return .none;
391 const eb = &f.error_bundle;500 const eb = &f.error_bundle;
392 const file_path = try f.parent_package_root.join(f.arena, Manifest.basename);501 const token_starts = ast.tokens.items(.start);
502 const start_loc = ast.tokenLocation(0, tok);
503 const src_path = try eb.printString("{}" ++ Manifest.basename, .{f.parent_package_root});
393 const msg_off = 0;504 const msg_off = 0;
505 return eb.addSourceLocation(.{
506 .src_path = src_path,
507 .span_start = token_starts[tok],
508 .span_end = @intCast(token_starts[tok] + ast.tokenSlice(tok).len),
509 .span_main = token_starts[tok] + msg_off,
510 .line = @intCast(start_loc.line),
511 .column = @intCast(start_loc.column),
512 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
513 });
514}
394515
516fn fail(f: *Fetch, msg_tok: std.zig.Ast.TokenIndex, msg_str: u32) RunError {
517 const eb = &f.error_bundle;
395 try eb.addRootErrorMessage(.{518 try eb.addRootErrorMessage(.{
396 .msg = msg_str,519 .msg = msg_str,
397 .src_loc = try eb.addSourceLocation(.{520 .src_loc = try f.srcLoc(msg_tok),
398 .src_path = try eb.addString(file_path),
399 .span_start = token_starts[msg_tok],
400 .span_end = @intCast(token_starts[msg_tok] + ast.tokenSlice(msg_tok).len),
401 .span_main = token_starts[msg_tok] + msg_off,
402 .line = @intCast(start_loc.line),
403 .column = @intCast(start_loc.column),
404 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
405 }),
406 .notes_len = 0,
407 });521 });
408
409 return error.FetchFailed;522 return error.FetchFailed;
410}523}
411524
412const Resource = union(enum) {525const Resource = union(enum) {
413 file: fs.File,526 file: fs.File,
414 http_request: std.http.Client.Request,527 http_request: std.http.Client.Request,
415 git_fetch_stream: git.Session.FetchStream,528 git: Git,
416 dir: fs.IterableDir,529 dir: fs.IterableDir,
530
531 const Git = struct {
532 fetch_stream: git.Session.FetchStream,
533 want_oid: [git.oid_length]u8,
534 };
535
536 fn deinit(resource: *Resource) void {
537 switch (resource.*) {
538 .file => |*file| file.close(),
539 .http_request => |*req| req.deinit(),
540 .git => |*git_resource| git_resource.fetch_stream.deinit(),
541 .dir => |*dir| dir.close(),
542 }
543 resource.* = undefined;
544 }
545
546 fn reader(resource: *Resource) std.io.AnyReader {
547 return .{
548 .context = resource,
549 .readFn = read,
550 };
551 }
552
553 fn read(context: *const anyopaque, buffer: []u8) anyerror!usize {
554 const resource: *Resource = @constCast(@ptrCast(@alignCast(context)));
555 switch (resource.*) {
556 .file => |*f| return f.read(buffer),
557 .http_request => |*r| return r.read(buffer),
558 .git => |*g| return g.fetch_stream.read(buffer),
559 .dir => unreachable,
560 }
561 }
417};562};
418563
419const FileType = enum {564const FileType = enum {
...@@ -468,30 +613,52 @@ const FileType = enum {...@@ -468,30 +613,52 @@ const FileType = enum {
468};613};
469614
470fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {615fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {
471 const gpa = f.gpa;616 const gpa = f.arena.child_allocator;
472 const arena = f.arena_allocator.allocator();617 const arena = f.arena.allocator();
473 const eb = &f.error_bundle;618 const eb = &f.error_bundle;
474619
475 if (ascii.eqlIgnoreCase(uri.scheme, "file")) return .{620 if (ascii.eqlIgnoreCase(uri.scheme, "file")) return .{
476 .file = try f.parent_package_root.openFile(uri.path, .{}),621 .file = f.parent_package_root.openFile(uri.path, .{}) catch |err| {
622 return f.fail(f.location_tok, try eb.printString("unable to open '{}{s}': {s}", .{
623 f.parent_package_root, uri.path, @errorName(err),
624 }));
625 },
477 };626 };
478627
628 const http_client = f.job_queue.http_client;
629
479 if (ascii.eqlIgnoreCase(uri.scheme, "http") or630 if (ascii.eqlIgnoreCase(uri.scheme, "http") or
480 ascii.eqlIgnoreCase(uri.scheme, "https"))631 ascii.eqlIgnoreCase(uri.scheme, "https"))
481 {632 {
482 var h = std.http.Headers{ .allocator = gpa };633 var h = std.http.Headers{ .allocator = gpa };
483 defer h.deinit();634 defer h.deinit();
484635
485 var req = try f.http_client.request(.GET, uri, h, .{});636 var req = http_client.request(.GET, uri, h, .{}) catch |err| {
637 return f.fail(f.location_tok, try eb.printString(
638 "unable to connect to server: {s}",
639 .{@errorName(err)},
640 ));
641 };
486 errdefer req.deinit(); // releases more than memory642 errdefer req.deinit(); // releases more than memory
487643
488 try req.start(.{});644 req.start(.{}) catch |err| {
489 try req.wait();645 return f.fail(f.location_tok, try eb.printString(
646 "HTTP request failed: {s}",
647 .{@errorName(err)},
648 ));
649 };
650 req.wait() catch |err| {
651 return f.fail(f.location_tok, try eb.printString(
652 "invalid HTTP response: {s}",
653 .{@errorName(err)},
654 ));
655 };
490656
491 if (req.response.status != .ok) {657 if (req.response.status != .ok) {
492 return f.fail(f.location_tok, "expected response status '200 OK' got '{s} {s}'", .{658 return f.fail(f.location_tok, try eb.printString(
493 @intFromEnum(req.response.status), req.response.status.phrase() orelse "",659 "bad HTTP response code: '{d} {s}'",
494 });660 .{ @intFromEnum(req.response.status), req.response.status.phrase() orelse "" },
661 ));
495 }662 }
496663
497 return .{ .http_request = req };664 return .{ .http_request = req };
...@@ -503,13 +670,21 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {...@@ -503,13 +670,21 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {
503 var transport_uri = uri;670 var transport_uri = uri;
504 transport_uri.scheme = uri.scheme["git+".len..];671 transport_uri.scheme = uri.scheme["git+".len..];
505 var redirect_uri: []u8 = undefined;672 var redirect_uri: []u8 = undefined;
506 var session: git.Session = .{ .transport = f.http_client, .uri = transport_uri };673 var session: git.Session = .{ .transport = http_client, .uri = transport_uri };
507 session.discoverCapabilities(gpa, &redirect_uri) catch |e| switch (e) {674 session.discoverCapabilities(gpa, &redirect_uri) catch |err| switch (err) {
508 error.Redirected => {675 error.Redirected => {
509 defer gpa.free(redirect_uri);676 defer gpa.free(redirect_uri);
510 return f.fail(f.location_tok, "repository moved to {s}", .{redirect_uri});677 return f.fail(f.location_tok, try eb.printString(
678 "repository moved to {s}",
679 .{redirect_uri},
680 ));
681 },
682 else => |e| {
683 return f.fail(f.location_tok, try eb.printString(
684 "unable to discover remote git server capabilities: {s}",
685 .{@errorName(e)},
686 ));
511 },687 },
512 else => |other| return other,
513 };688 };
514689
515 const want_oid = want_oid: {690 const want_oid = want_oid: {
...@@ -519,12 +694,22 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {...@@ -519,12 +694,22 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {
519 const want_ref_head = try std.fmt.allocPrint(arena, "refs/heads/{s}", .{want_ref});694 const want_ref_head = try std.fmt.allocPrint(arena, "refs/heads/{s}", .{want_ref});
520 const want_ref_tag = try std.fmt.allocPrint(arena, "refs/tags/{s}", .{want_ref});695 const want_ref_tag = try std.fmt.allocPrint(arena, "refs/tags/{s}", .{want_ref});
521696
522 var ref_iterator = try session.listRefs(gpa, .{697 var ref_iterator = session.listRefs(gpa, .{
523 .ref_prefixes = &.{ want_ref, want_ref_head, want_ref_tag },698 .ref_prefixes = &.{ want_ref, want_ref_head, want_ref_tag },
524 .include_peeled = true,699 .include_peeled = true,
525 });700 }) catch |err| {
701 return f.fail(f.location_tok, try eb.printString(
702 "unable to list refs: {s}",
703 .{@errorName(err)},
704 ));
705 };
526 defer ref_iterator.deinit();706 defer ref_iterator.deinit();
527 while (try ref_iterator.next()) |ref| {707 while (ref_iterator.next() catch |err| {
708 return f.fail(f.location_tok, try eb.printString(
709 "unable to iterate refs: {s}",
710 .{@errorName(err)},
711 ));
712 }) |ref| {
528 if (std.mem.eql(u8, ref.name, want_ref) or713 if (std.mem.eql(u8, ref.name, want_ref) or
529 std.mem.eql(u8, ref.name, want_ref_head) or714 std.mem.eql(u8, ref.name, want_ref_head) or
530 std.mem.eql(u8, ref.name, want_ref_tag))715 std.mem.eql(u8, ref.name, want_ref_tag))
...@@ -532,31 +717,46 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {...@@ -532,31 +717,46 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {
532 break :want_oid ref.peeled orelse ref.oid;717 break :want_oid ref.peeled orelse ref.oid;
533 }718 }
534 }719 }
535 return f.fail(f.location_tok, "ref not found: {s}", .{want_ref});720 return f.fail(f.location_tok, try eb.printString("ref not found: {s}", .{want_ref}));
536 };721 };
537 if (uri.fragment == null) {722 if (uri.fragment == null) {
538 const notes_len = 1;723 const notes_len = 1;
539 try f.addErrorWithNotes(notes_len, f.location_tok, "url field is missing an explicit ref");724 try eb.addRootErrorMessage(.{
725 .msg = try eb.addString("url field is missing an explicit ref"),
726 .src_loc = try f.srcLoc(f.location_tok),
727 .notes_len = notes_len,
728 });
540 const notes_start = try eb.reserveNotes(notes_len);729 const notes_start = try eb.reserveNotes(notes_len);
541 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{730 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{
542 .msg = try eb.printString("try .url = \"{+/}#{}\",", .{731 .msg = try eb.printString("try .url = \"{+/}#{}\",", .{
543 uri, std.fmt.fmtSliceHexLower(&want_oid),732 uri, std.fmt.fmtSliceHexLower(&want_oid),
544 }),733 }),
545 }));734 }));
546 return error.PackageFetchFailed;735 return error.FetchFailed;
547 }736 }
548737
549 var want_oid_buf: [git.fmt_oid_length]u8 = undefined;738 var want_oid_buf: [git.fmt_oid_length]u8 = undefined;
550 _ = std.fmt.bufPrint(&want_oid_buf, "{}", .{739 _ = std.fmt.bufPrint(&want_oid_buf, "{}", .{
551 std.fmt.fmtSliceHexLower(&want_oid),740 std.fmt.fmtSliceHexLower(&want_oid),
552 }) catch unreachable;741 }) catch unreachable;
553 var fetch_stream = try session.fetch(gpa, &.{&want_oid_buf});742 var fetch_stream = session.fetch(gpa, &.{&want_oid_buf}) catch |err| {
743 return f.fail(f.location_tok, try eb.printString(
744 "unable to create fetch stream: {s}",
745 .{@errorName(err)},
746 ));
747 };
554 errdefer fetch_stream.deinit();748 errdefer fetch_stream.deinit();
555749
556 return .{ .git_fetch_stream = fetch_stream };750 return .{ .git = .{
751 .fetch_stream = fetch_stream,
752 .want_oid = want_oid,
753 } };
557 }754 }
558755
559 return f.fail(f.location_tok, "unsupported URL scheme: {s}", .{uri.scheme});756 return f.fail(f.location_tok, try eb.printString(
757 "unsupported URL scheme: {s}",
758 .{uri.scheme},
759 ));
560}760}
561761
562fn unpackResource(762fn unpackResource(
...@@ -565,52 +765,62 @@ fn unpackResource(...@@ -565,52 +765,62 @@ fn unpackResource(
565 uri_path: []const u8,765 uri_path: []const u8,
566 tmp_directory: Cache.Directory,766 tmp_directory: Cache.Directory,
567) RunError!void {767) RunError!void {
768 const eb = &f.error_bundle;
568 const file_type = switch (resource.*) {769 const file_type = switch (resource.*) {
569 .file => FileType.fromPath(uri_path) orelse770 .file => FileType.fromPath(uri_path) orelse
570 return f.fail(f.location_tok, "unknown file type: '{s}'", .{uri_path}),771 return f.fail(f.location_tok, try eb.printString("unknown file type: '{s}'", .{uri_path})),
571772
572 .http_request => |req| ft: {773 .http_request => |req| ft: {
573 // Content-Type takes first precedence.774 // Content-Type takes first precedence.
574 const content_type = req.response.headers.getFirstValue("Content-Type") orelse775 const content_type = req.response.headers.getFirstValue("Content-Type") orelse
575 return f.fail(f.location_tok, "missing 'Content-Type' header", .{});776 return f.fail(f.location_tok, try eb.addString("missing 'Content-Type' header"));
576777
577 if (ascii.eqlIgnoreCase(content_type, "application/x-tar"))778 if (ascii.eqlIgnoreCase(content_type, "application/x-tar"))
578 return .tar;779 break :ft .tar;
579780
580 if (ascii.eqlIgnoreCase(content_type, "application/gzip") or781 if (ascii.eqlIgnoreCase(content_type, "application/gzip") or
581 ascii.eqlIgnoreCase(content_type, "application/x-gzip") or782 ascii.eqlIgnoreCase(content_type, "application/x-gzip") or
582 ascii.eqlIgnoreCase(content_type, "application/tar+gzip"))783 ascii.eqlIgnoreCase(content_type, "application/tar+gzip"))
583 {784 {
584 return .@"tar.gz";785 break :ft .@"tar.gz";
585 }786 }
586787
587 if (ascii.eqlIgnoreCase(content_type, "application/x-xz"))788 if (ascii.eqlIgnoreCase(content_type, "application/x-xz"))
588 return .@"tar.xz";789 break :ft .@"tar.xz";
589790
590 if (!ascii.eqlIgnoreCase(content_type, "application/octet-stream")) {791 if (!ascii.eqlIgnoreCase(content_type, "application/octet-stream")) {
591 return f.fail(f.location_tok, "unrecognized 'Content-Type' header: '{s}'", .{792 return f.fail(f.location_tok, try eb.printString(
592 content_type,793 "unrecognized 'Content-Type' header: '{s}'",
593 });794 .{content_type},
795 ));
594 }796 }
595797
596 // Next, the filename from 'content-disposition: attachment' takes precedence.798 // Next, the filename from 'content-disposition: attachment' takes precedence.
597 if (req.response.headers.getFirstValue("Content-Disposition")) |cd_header| {799 if (req.response.headers.getFirstValue("Content-Disposition")) |cd_header| {
598 break :ft FileType.fromContentDisposition(cd_header) orelse800 break :ft FileType.fromContentDisposition(cd_header) orelse {
599 return f.fail(801 return f.fail(f.location_tok, try eb.printString(
600 f.location_tok,802 "unsupported Content-Disposition header value: '{s}' for Content-Type=application/octet-stream",
601 "unsupported Content-Disposition header value: '{s}' for Content-Type=application/octet-stream",803 .{cd_header},
602 .{cd_header},804 ));
603 );805 };
604 }806 }
605807
606 // Finally, the path from the URI is used.808 // Finally, the path from the URI is used.
607 break :ft FileType.fromPath(uri_path) orelse809 break :ft FileType.fromPath(uri_path) orelse {
608 return f.fail(f.location_tok, "unknown file type: '{s}'", .{uri_path});810 return f.fail(f.location_tok, try eb.printString(
811 "unknown file type: '{s}'",
812 .{uri_path},
813 ));
814 };
609 },815 },
610 .git_fetch_stream => return .git_pack,816
611 .dir => |dir| {817 .git => .git_pack,
612 try f.recursiveDirectoryCopy(dir, tmp_directory.handle);818
613 return;819 .dir => |dir| return f.recursiveDirectoryCopy(dir, tmp_directory.handle) catch |err| {
820 return f.fail(f.location_tok, try eb.printString(
821 "unable to copy directory '{s}': {s}",
822 .{ uri_path, @errorName(err) },
823 ));
614 },824 },
615 };825 };
616826
...@@ -618,7 +828,14 @@ fn unpackResource(...@@ -618,7 +828,14 @@ fn unpackResource(
618 .tar => try unpackTarball(f, tmp_directory.handle, resource.reader()),828 .tar => try unpackTarball(f, tmp_directory.handle, resource.reader()),
619 .@"tar.gz" => try unpackTarballCompressed(f, tmp_directory.handle, resource, std.compress.gzip),829 .@"tar.gz" => try unpackTarballCompressed(f, tmp_directory.handle, resource, std.compress.gzip),
620 .@"tar.xz" => try unpackTarballCompressed(f, tmp_directory.handle, resource, std.compress.xz),830 .@"tar.xz" => try unpackTarballCompressed(f, tmp_directory.handle, resource, std.compress.xz),
621 .git_pack => try unpackGitPack(f, tmp_directory.handle, resource),831 .git_pack => unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) {
832 error.FetchFailed => return error.FetchFailed,
833 error.OutOfMemory => return error.OutOfMemory,
834 else => |e| return f.fail(f.location_tok, try eb.printString(
835 "unable to unpack git files: {s}",
836 .{@errorName(e)},
837 )),
838 },
622 }839 }
623}840}
624841
...@@ -628,11 +845,17 @@ fn unpackTarballCompressed(...@@ -628,11 +845,17 @@ fn unpackTarballCompressed(
628 resource: *Resource,845 resource: *Resource,
629 comptime Compression: type,846 comptime Compression: type,
630) RunError!void {847) RunError!void {
631 const gpa = f.gpa;848 const gpa = f.arena.child_allocator;
849 const eb = &f.error_bundle;
632 const reader = resource.reader();850 const reader = resource.reader();
633 var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader);851 var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader);
634852
635 var decompress = try Compression.decompress(gpa, br.reader());853 var decompress = Compression.decompress(gpa, br.reader()) catch |err| {
854 return f.fail(f.location_tok, try eb.printString(
855 "unable to decompress tarball: {s}",
856 .{@errorName(err)},
857 ));
858 };
636 defer decompress.deinit();859 defer decompress.deinit();
637860
638 return unpackTarball(f, out_dir, decompress.reader());861 return unpackTarball(f, out_dir, decompress.reader());
...@@ -640,11 +863,12 @@ fn unpackTarballCompressed(...@@ -640,11 +863,12 @@ fn unpackTarballCompressed(
640863
641fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {864fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
642 const eb = &f.error_bundle;865 const eb = &f.error_bundle;
866 const gpa = f.arena.child_allocator;
643867
644 var diagnostics: std.tar.Options.Diagnostics = .{ .allocator = f.gpa };868 var diagnostics: std.tar.Options.Diagnostics = .{ .allocator = gpa };
645 defer diagnostics.deinit();869 defer diagnostics.deinit();
646870
647 try std.tar.pipeToFileSystem(out_dir, reader, .{871 std.tar.pipeToFileSystem(out_dir, reader, .{
648 .diagnostics = &diagnostics,872 .diagnostics = &diagnostics,
649 .strip_components = 1,873 .strip_components = 1,
650 // TODO: we would like to set this to executable_bit_only, but two874 // TODO: we would like to set this to executable_bit_only, but two
...@@ -653,12 +877,19 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {...@@ -653,12 +877,19 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
653 // 2. the hashing algorithm here needs to support detecting the is_executable877 // 2. the hashing algorithm here needs to support detecting the is_executable
654 // bit on Windows from the ACLs (see the isExecutable function).878 // bit on Windows from the ACLs (see the isExecutable function).
655 .mode_mode = .ignore,879 .mode_mode = .ignore,
656 .filter = .{ .exclude_empty_directories = true },880 .exclude_empty_directories = true,
657 });881 }) catch |err| return f.fail(f.location_tok, try eb.printString(
882 "unable to unpack tarball to temporary directory: {s}",
883 .{@errorName(err)},
884 ));
658885
659 if (diagnostics.errors.items.len > 0) {886 if (diagnostics.errors.items.len > 0) {
660 const notes_len: u32 = @intCast(diagnostics.errors.items.len);887 const notes_len: u32 = @intCast(diagnostics.errors.items.len);
661 try f.addErrorWithNotes(notes_len, f.location_tok, "unable to unpack tarball");888 try eb.addRootErrorMessage(.{
889 .msg = try eb.addString("unable to unpack tarball"),
890 .src_loc = try f.srcLoc(f.location_tok),
891 .notes_len = notes_len,
892 });
662 const notes_start = try eb.reserveNotes(notes_len);893 const notes_start = try eb.reserveNotes(notes_len);
663 for (diagnostics.errors.items, notes_start..) |item, note_i| {894 for (diagnostics.errors.items, notes_start..) |item, note_i| {
664 switch (item) {895 switch (item) {
...@@ -678,19 +909,15 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {...@@ -678,19 +909,15 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
678 },909 },
679 }910 }
680 }911 }
681 return error.InvalidTarball;912 return error.FetchFailed;
682 }913 }
683}914}
684915
685fn unpackGitPack(916fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!void {
686 f: *Fetch,
687 out_dir: fs.Dir,
688 resource: *Resource,
689 want_oid: git.Oid,
690) !void {
691 const eb = &f.error_bundle;917 const eb = &f.error_bundle;
692 const gpa = f.gpa;918 const gpa = f.arena.child_allocator;
693 const reader = resource.reader();919 const want_oid = resource.git.want_oid;
920 const reader = resource.git.fetch_stream.reader();
694 // The .git directory is used to store the packfile and associated index, but921 // The .git directory is used to store the packfile and associated index, but
695 // we do not attempt to replicate the exact structure of a real .git922 // we do not attempt to replicate the exact structure of a real .git
696 // directory, since that isn't relevant for fetching a package.923 // directory, since that isn't relevant for fetching a package.
...@@ -700,13 +927,13 @@ fn unpackGitPack(...@@ -700,13 +927,13 @@ fn unpackGitPack(
700 var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true });927 var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true });
701 defer pack_file.close();928 defer pack_file.close();
702 var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();929 var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
703 try fifo.pump(reader.reader(), pack_file.writer());930 try fifo.pump(reader, pack_file.writer());
704 try pack_file.sync();931 try pack_file.sync();
705932
706 var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });933 var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });
707 defer index_file.close();934 defer index_file.close();
708 {935 {
709 var index_prog_node = reader.prog_node.start("Index pack", 0);936 var index_prog_node = f.prog_node.start("Index pack", 0);
710 defer index_prog_node.end();937 defer index_prog_node.end();
711 index_prog_node.activate();938 index_prog_node.activate();
712 var index_buffered_writer = std.io.bufferedWriter(index_file.writer());939 var index_buffered_writer = std.io.bufferedWriter(index_file.writer());
...@@ -716,7 +943,7 @@ fn unpackGitPack(...@@ -716,7 +943,7 @@ fn unpackGitPack(
716 }943 }
717944
718 {945 {
719 var checkout_prog_node = reader.prog_node.start("Checkout", 0);946 var checkout_prog_node = f.prog_node.start("Checkout", 0);
720 defer checkout_prog_node.end();947 defer checkout_prog_node.end();
721 checkout_prog_node.activate();948 checkout_prog_node.activate();
722 var repository = try git.Repository.init(gpa, pack_file, index_file);949 var repository = try git.Repository.init(gpa, pack_file, index_file);
...@@ -727,7 +954,11 @@ fn unpackGitPack(...@@ -727,7 +954,11 @@ fn unpackGitPack(
727954
728 if (diagnostics.errors.items.len > 0) {955 if (diagnostics.errors.items.len > 0) {
729 const notes_len: u32 = @intCast(diagnostics.errors.items.len);956 const notes_len: u32 = @intCast(diagnostics.errors.items.len);
730 try f.addErrorWithNotes(notes_len, f.location_tok, "unable to unpack packfile");957 try eb.addRootErrorMessage(.{
958 .msg = try eb.addString("unable to unpack packfile"),
959 .src_loc = try f.srcLoc(f.location_tok),
960 .notes_len = notes_len,
961 });
731 const notes_start = try eb.reserveNotes(notes_len);962 const notes_start = try eb.reserveNotes(notes_len);
732 for (diagnostics.errors.items, notes_start..) |item, note_i| {963 for (diagnostics.errors.items, notes_start..) |item, note_i| {
733 switch (item) {964 switch (item) {
...@@ -748,9 +979,10 @@ fn unpackGitPack(...@@ -748,9 +979,10 @@ fn unpackGitPack(
748 try out_dir.deleteTree(".git");979 try out_dir.deleteTree(".git");
749}980}
750981
751fn recursiveDirectoryCopy(f: *Fetch, dir: fs.IterableDir, tmp_dir: fs.Dir) RunError!void {982fn recursiveDirectoryCopy(f: *Fetch, dir: fs.IterableDir, tmp_dir: fs.Dir) anyerror!void {
983 const gpa = f.arena.child_allocator;
752 // Recursive directory copy.984 // Recursive directory copy.
753 var it = try dir.walk(f.gpa);985 var it = try dir.walk(gpa);
754 defer it.deinit();986 defer it.deinit();
755 while (try it.next()) |entry| {987 while (try it.next()) |entry| {
756 switch (entry.kind) {988 switch (entry.kind) {
...@@ -816,16 +1048,22 @@ pub fn renameTmpIntoCache(...@@ -816,16 +1048,22 @@ pub fn renameTmpIntoCache(
816/// the hash are not present on the file system. Empty directories are *not1048/// the hash are not present on the file system. Empty directories are *not
817/// hashed* and must not be present on the file system when calling this1049/// hashed* and must not be present on the file system when calling this
818/// function.1050/// function.
819fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Digest {1051fn computeHash(
1052 f: *Fetch,
1053 tmp_directory: Cache.Directory,
1054 filter: Filter,
1055) RunError!Manifest.Digest {
820 // All the path name strings need to be in memory for sorting.1056 // All the path name strings need to be in memory for sorting.
821 const arena = f.arena_allocator.allocator();1057 const arena = f.arena.allocator();
822 const gpa = f.gpa;1058 const gpa = f.arena.child_allocator;
1059 const eb = &f.error_bundle;
1060 const thread_pool = f.job_queue.thread_pool;
8231061
824 // Collect all files, recursively, then sort.1062 // Collect all files, recursively, then sort.
825 var all_files = std.ArrayList(*HashedFile).init(gpa);1063 var all_files = std.ArrayList(*HashedFile).init(gpa);
826 defer all_files.deinit();1064 defer all_files.deinit();
8271065
828 var walker = try pkg_dir.walk(gpa);1066 var walker = try @as(fs.IterableDir, .{ .dir = tmp_directory.handle }).walk(gpa);
829 defer walker.deinit();1067 defer walker.deinit();
8301068
831 {1069 {
...@@ -834,19 +1072,28 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige...@@ -834,19 +1072,28 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige
834 var wait_group: WaitGroup = .{};1072 var wait_group: WaitGroup = .{};
835 // `computeHash` is called from a worker thread so there must not be1073 // `computeHash` is called from a worker thread so there must not be
836 // any waiting without working or a deadlock could occur.1074 // any waiting without working or a deadlock could occur.
837 defer wait_group.waitAndWork();1075 defer thread_pool.waitAndWork(&wait_group);
8381076
839 while (try walker.next()) |entry| {1077 while (walker.next() catch |err| {
1078 try eb.addRootErrorMessage(.{ .msg = try eb.printString(
1079 "unable to walk temporary directory '{}': {s}",
1080 .{ tmp_directory, @errorName(err) },
1081 ) });
1082 return error.FetchFailed;
1083 }) |entry| {
840 _ = filter; // TODO: apply filter rules here1084 _ = filter; // TODO: apply filter rules here
8411085
842 const kind: HashedFile.Kind = switch (entry.kind) {1086 const kind: HashedFile.Kind = switch (entry.kind) {
843 .directory => continue,1087 .directory => continue,
844 .file => .file,1088 .file => .file,
845 .sym_link => .sym_link,1089 .sym_link => .sym_link,
846 else => return error.IllegalFileTypeInPackage,1090 else => return f.fail(f.location_tok, try eb.printString(
1091 "package contains '{s}' which has illegal file type '{s}'",
1092 .{ entry.path, @tagName(entry.kind) },
1093 )),
847 };1094 };
8481095
849 if (std.mem.eql(u8, entry.path, build_zig_basename))1096 if (std.mem.eql(u8, entry.path, Package.build_zig_basename))
850 f.has_build_zig = true;1097 f.has_build_zig = true;
8511098
852 const hashed_file = try arena.create(HashedFile);1099 const hashed_file = try arena.create(HashedFile);
...@@ -859,7 +1106,9 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige...@@ -859,7 +1106,9 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige
859 .failure = undefined, // to be populated by the worker1106 .failure = undefined, // to be populated by the worker
860 };1107 };
861 wait_group.start();1108 wait_group.start();
862 try f.thread_pool.spawn(workerHashFile, .{ pkg_dir.dir, hashed_file, &wait_group });1109 try thread_pool.spawn(workerHashFile, .{
1110 tmp_directory.handle, hashed_file, &wait_group,
1111 });
8631112
864 try all_files.append(hashed_file);1113 try all_files.append(hashed_file);
865 }1114 }
...@@ -869,19 +1118,13 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige...@@ -869,19 +1118,13 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige
8691118
870 var hasher = Manifest.Hash.init(.{});1119 var hasher = Manifest.Hash.init(.{});
871 var any_failures = false;1120 var any_failures = false;
872 const eb = &f.error_bundle;
873 for (all_files.items) |hashed_file| {1121 for (all_files.items) |hashed_file| {
874 hashed_file.failure catch |err| {1122 hashed_file.failure catch |err| {
875 any_failures = true;1123 any_failures = true;
876 try eb.addRootErrorMessage(.{1124 try eb.addRootErrorMessage(.{
877 .msg = try eb.printString("unable to hash: {s}", .{@errorName(err)}),1125 .msg = try eb.printString("unable to hash '{s}': {s}", .{
878 .src_loc = try eb.addSourceLocation(.{1126 hashed_file.fs_path, @errorName(err),
879 .src_path = try eb.addString(hashed_file.fs_path),
880 .span_start = 0,
881 .span_end = 0,
882 .span_main = 0,
883 }),1127 }),
884 .notes_len = 0,
885 });1128 });
886 };1129 };
887 hasher.update(&hashed_file.hash);1130 hasher.update(&hashed_file.hash);
...@@ -934,7 +1177,7 @@ fn isExecutable(file: fs.File) !bool {...@@ -934,7 +1177,7 @@ fn isExecutable(file: fs.File) !bool {
934const HashedFile = struct {1177const HashedFile = struct {
935 fs_path: []const u8,1178 fs_path: []const u8,
936 normalized_path: []const u8,1179 normalized_path: []const u8,
937 hash: Digest,1180 hash: Manifest.Digest,
938 failure: Error!void,1181 failure: Error!void,
939 kind: Kind,1182 kind: Kind,
9401183
...@@ -970,7 +1213,7 @@ fn normalizePath(arena: Allocator, fs_path: []const u8) ![]const u8 {...@@ -970,7 +1213,7 @@ fn normalizePath(arena: Allocator, fs_path: []const u8) ![]const u8 {
970 return normalized;1213 return normalized;
971}1214}
9721215
973pub const Filter = struct {1216const Filter = struct {
974 include_paths: std.StringArrayHashMapUnmanaged(void) = .{},1217 include_paths: std.StringArrayHashMapUnmanaged(void) = .{},
9751218
976 /// sub_path is relative to the tarball root.1219 /// sub_path is relative to the tarball root.
...@@ -990,12 +1233,9 @@ pub const Filter = struct {...@@ -990,12 +1233,9 @@ pub const Filter = struct {
990 }1233 }
991};1234};
9921235
993const build_zig_basename = @import("../Package.zig").build_zig_basename;
994const hex_multihash_len = 2 * Manifest.multihash_len;
995
996// These are random bytes.1236// These are random bytes.
997const package_hash_prefix_cached: [8]u8 = &.{ 0x53, 0x7e, 0xfa, 0x94, 0x65, 0xe9, 0xf8, 0x73 };1237const package_hash_prefix_cached = [8]u8{ 0x53, 0x7e, 0xfa, 0x94, 0x65, 0xe9, 0xf8, 0x73 };
998const package_hash_prefix_project: [8]u8 = &.{ 0xe1, 0x25, 0xee, 0xfa, 0xa6, 0x17, 0x38, 0xcc };1238const package_hash_prefix_project = [8]u8{ 0xe1, 0x25, 0xee, 0xfa, 0xa6, 0x17, 0x38, 0xcc };
9991239
1000const builtin = @import("builtin");1240const builtin = @import("builtin");
1001const std = @import("std");1241const std = @import("std");
...@@ -1010,3 +1250,4 @@ const Manifest = @import("../Manifest.zig");...@@ -1010,3 +1250,4 @@ const Manifest = @import("../Manifest.zig");
1010const Fetch = @This();1250const Fetch = @This();
1011const main = @import("../main.zig");1251const main = @import("../main.zig");
1012const git = @import("../git.zig");1252const git = @import("../git.zig");
1253const Package = @import("../Package.zig");
src/Package/Module.zig created+32
...@@ -0,0 +1,32 @@
1//! Corresponds to something that Zig source code can `@import`.
2//! Not to be confused with src/Module.zig which should be renamed
3//! to something else. https://github.com/ziglang/zig/issues/14307
4
5/// Only files inside this directory can be imported.
6root: Package.Path,
7/// Relative to `root`. May contain path separators.
8root_src_path: []const u8,
9/// The dependency table of this module. Shared dependencies such as 'std',
10/// 'builtin', and 'root' are not specified in every dependency table, but
11/// instead only in the table of `main_pkg`. `Module.importFile` is
12/// responsible for detecting these names and using the correct package.
13deps: Deps = .{},
14
15pub const Deps = std.StringHashMapUnmanaged(*Module);
16
17pub const Tree = struct {
18 /// Each `Package` exposes a `Module` with build.zig as its root source file.
19 build_module_table: std.AutoArrayHashMapUnmanaged(MultiHashHexDigest, *Module),
20};
21
22pub fn create(allocator: Allocator, m: Module) Allocator.Error!*Module {
23 const new = try allocator.create(Module);
24 new.* = m;
25 return new;
26}
27
28const Module = @This();
29const Package = @import("../Package.zig");
30const std = @import("std");
31const Allocator = std.mem.Allocator;
32const MultiHashHexDigest = Package.Manifest.MultiHashHexDigest;
src/crash_report.zig+13-9
...@@ -139,18 +139,22 @@ fn dumpStatusReport() !void {...@@ -139,18 +139,22 @@ fn dumpStatusReport() !void {
139139
140var crash_heap: [16 * 4096]u8 = undefined;140var crash_heap: [16 * 4096]u8 = undefined;
141141
142fn writeFilePath(file: *Module.File, stream: anytype) !void {142fn writeFilePath(file: *Module.File, writer: anytype) !void {
143 if (file.pkg.root_src_directory.path) |path| {143 if (file.mod.root.root_dir.path) |path| {
144 try stream.writeAll(path);144 try writer.writeAll(path);
145 try stream.writeAll(std.fs.path.sep_str);145 try writer.writeAll(std.fs.path.sep_str);
146 }146 }
147 try stream.writeAll(file.sub_file_path);147 if (file.mod.root.sub_path.len > 0) {
148 try writer.writeAll(file.mod.root.sub_path);
149 try writer.writeAll(std.fs.path.sep_str);
150 }
151 try writer.writeAll(file.sub_file_path);
148}152}
149153
150fn writeFullyQualifiedDeclWithFile(mod: *Module, decl: *Decl, stream: anytype) !void {154fn writeFullyQualifiedDeclWithFile(mod: *Module, decl: *Decl, writer: anytype) !void {
151 try writeFilePath(decl.getFileScope(mod), stream);155 try writeFilePath(decl.getFileScope(mod), writer);
152 try stream.writeAll(": ");156 try writer.writeAll(": ");
153 try decl.renderFullyQualifiedDebugName(mod, stream);157 try decl.renderFullyQualifiedDebugName(mod, writer);
154}158}
155159
156pub fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, maybe_ret_addr: ?usize) noreturn {160pub fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, maybe_ret_addr: ?usize) noreturn {
src/main.zig+153-155
...@@ -416,7 +416,7 @@ const usage_build_generic =...@@ -416,7 +416,7 @@ const usage_build_generic =
416 \\ dep: [[import=]name]416 \\ dep: [[import=]name]
417 \\ --deps [dep],[dep],... Set dependency names for the root package417 \\ --deps [dep],[dep],... Set dependency names for the root package
418 \\ dep: [[import=]name]418 \\ dep: [[import=]name]
419 \\ --main-pkg-path Set the directory of the root package419 \\ --main-mod-path Set the directory of the root module
420 \\ -fPIC Force-enable Position Independent Code420 \\ -fPIC Force-enable Position Independent Code
421 \\ -fno-PIC Force-disable Position Independent Code421 \\ -fno-PIC Force-disable Position Independent Code
422 \\ -fPIE Force-enable Position Independent Executable422 \\ -fPIE Force-enable Position Independent Executable
...@@ -765,17 +765,11 @@ const Framework = struct {...@@ -765,17 +765,11 @@ const Framework = struct {
765};765};
766766
767const CliModule = struct {767const CliModule = struct {
768 mod: *Package,768 mod: *Package.Module,
769 /// still in CLI arg format769 /// still in CLI arg format
770 deps_str: []const u8,770 deps_str: []const u8,
771};771};
772772
773fn cleanupModules(modules: *std.StringArrayHashMap(CliModule)) void {
774 var it = modules.iterator();
775 while (it.next()) |kv| kv.value_ptr.mod.destroy(modules.allocator);
776 modules.deinit();
777}
778
779fn buildOutputType(773fn buildOutputType(
780 gpa: Allocator,774 gpa: Allocator,
781 arena: Allocator,775 arena: Allocator,
...@@ -950,8 +944,7 @@ fn buildOutputType(...@@ -950,8 +944,7 @@ fn buildOutputType(
950 // Contains every module specified via --mod. The dependencies are added944 // Contains every module specified via --mod. The dependencies are added
951 // after argument parsing is completed. We use a StringArrayHashMap to make945 // after argument parsing is completed. We use a StringArrayHashMap to make
952 // error output consistent.946 // error output consistent.
953 var modules = std.StringArrayHashMap(CliModule).init(gpa);947 var modules = std.StringArrayHashMap(CliModule).init(arena);
954 defer cleanupModules(&modules);
955948
956 // The dependency string for the root package949 // The dependency string for the root package
957 var root_deps_str: ?[]const u8 = null;950 var root_deps_str: ?[]const u8 = null;
...@@ -1023,32 +1016,37 @@ fn buildOutputType(...@@ -1023,32 +1016,37 @@ fn buildOutputType(
10231016
1024 for ([_][]const u8{ "std", "root", "builtin" }) |name| {1017 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
1025 if (mem.eql(u8, mod_name, name)) {1018 if (mem.eql(u8, mod_name, name)) {
1026 fatal("unable to add module '{s}' -> '{s}': conflicts with builtin module", .{ mod_name, root_src });1019 fatal("unable to add module '{s}' -> '{s}': conflicts with builtin module", .{
1020 mod_name, root_src,
1021 });
1027 }1022 }
1028 }1023 }
10291024
1030 var mod_it = modules.iterator();1025 var mod_it = modules.iterator();
1031 while (mod_it.next()) |kv| {1026 while (mod_it.next()) |kv| {
1032 if (std.mem.eql(u8, mod_name, kv.key_ptr.*)) {1027 if (std.mem.eql(u8, mod_name, kv.key_ptr.*)) {
1033 fatal("unable to add module '{s}' -> '{s}': already exists as '{s}'", .{ mod_name, root_src, kv.value_ptr.mod.root_src_path });1028 fatal("unable to add module '{s}' -> '{s}': already exists as '{s}'", .{
1029 mod_name, root_src, kv.value_ptr.mod.root_src_path,
1030 });
1034 }1031 }
1035 }1032 }
10361033
1037 try modules.ensureUnusedCapacity(1);1034 try modules.put(mod_name, .{
1038 modules.put(mod_name, .{1035 .mod = try Package.Module.create(arena, .{
1039 .mod = try Package.create(1036 .root = .{
1040 gpa,1037 .root_dir = Cache.Directory.cwd(),
1041 fs.path.dirname(root_src),1038 .sub_path = fs.path.dirname(root_src) orelse "",
1042 fs.path.basename(root_src),1039 },
1043 ),1040 .root_src_path = fs.path.basename(root_src),
1041 }),
1044 .deps_str = deps_str,1042 .deps_str = deps_str,
1045 }) catch unreachable;1043 });
1046 } else if (mem.eql(u8, arg, "--deps")) {1044 } else if (mem.eql(u8, arg, "--deps")) {
1047 if (root_deps_str != null) {1045 if (root_deps_str != null) {
1048 fatal("only one --deps argument is allowed", .{});1046 fatal("only one --deps argument is allowed", .{});
1049 }1047 }
1050 root_deps_str = args_iter.nextOrFatal();1048 root_deps_str = args_iter.nextOrFatal();
1051 } else if (mem.eql(u8, arg, "--main-pkg-path")) {1049 } else if (mem.eql(u8, arg, "--main-mod-path")) {
1052 main_pkg_path = args_iter.nextOrFatal();1050 main_pkg_path = args_iter.nextOrFatal();
1053 } else if (mem.eql(u8, arg, "-cflags")) {1051 } else if (mem.eql(u8, arg, "-cflags")) {
1054 extra_cflags.shrinkRetainingCapacity(0);1052 extra_cflags.shrinkRetainingCapacity(0);
...@@ -2461,19 +2459,26 @@ fn buildOutputType(...@@ -2461,19 +2459,26 @@ fn buildOutputType(
2461 var deps_it = ModuleDepIterator.init(deps_str);2459 var deps_it = ModuleDepIterator.init(deps_str);
2462 while (deps_it.next()) |dep| {2460 while (deps_it.next()) |dep| {
2463 if (dep.expose.len == 0) {2461 if (dep.expose.len == 0) {
2464 fatal("module '{s}' depends on '{s}' with a blank name", .{ kv.key_ptr.*, dep.name });2462 fatal("module '{s}' depends on '{s}' with a blank name", .{
2463 kv.key_ptr.*, dep.name,
2464 });
2465 }2465 }
24662466
2467 for ([_][]const u8{ "std", "root", "builtin" }) |name| {2467 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
2468 if (mem.eql(u8, dep.expose, name)) {2468 if (mem.eql(u8, dep.expose, name)) {
2469 fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose });2469 fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{
2470 dep.name, dep.expose,
2471 });
2470 }2472 }
2471 }2473 }
24722474
2473 const dep_mod = modules.get(dep.name) orelse2475 const dep_mod = modules.get(dep.name) orelse {
2474 fatal("module '{s}' depends on module '{s}' which does not exist", .{ kv.key_ptr.*, dep.name });2476 fatal("module '{s}' depends on module '{s}' which does not exist", .{
2477 kv.key_ptr.*, dep.name,
2478 });
2479 };
24752480
2476 try kv.value_ptr.mod.add(gpa, dep.expose, dep_mod.mod);2481 try kv.value_ptr.mod.deps.put(arena, dep.expose, dep_mod.mod);
2477 }2482 }
2478 }2483 }
2479 }2484 }
...@@ -3229,31 +3234,33 @@ fn buildOutputType(...@@ -3229,31 +3234,33 @@ fn buildOutputType(
3229 };3234 };
3230 defer emit_implib_resolved.deinit();3235 defer emit_implib_resolved.deinit();
32313236
3232 const main_pkg: ?*Package = if (root_src_file) |unresolved_src_path| blk: {3237 const main_mod: ?*Package.Module = if (root_src_file) |unresolved_src_path| blk: {
3233 const src_path = try introspect.resolvePath(arena, unresolved_src_path);3238 const src_path = try introspect.resolvePath(arena, unresolved_src_path);
3234 if (main_pkg_path) |unresolved_main_pkg_path| {3239 if (main_pkg_path) |unresolved_main_pkg_path| {
3235 const p = try introspect.resolvePath(arena, unresolved_main_pkg_path);3240 const p = try introspect.resolvePath(arena, unresolved_main_pkg_path);
3236 if (p.len == 0) {3241 break :blk try Package.Module.create(arena, .{
3237 break :blk try Package.create(gpa, null, src_path);3242 .root = .{
3238 } else {3243 .root_dir = Cache.Directory.cwd(),
3239 const rel_src_path = try fs.path.relative(arena, p, src_path);3244 .sub_path = p,
3240 break :blk try Package.create(gpa, p, rel_src_path);3245 },
3241 }3246 .root_src_path = if (p.len == 0)
3247 src_path
3248 else
3249 try fs.path.relative(arena, p, src_path),
3250 });
3242 } else {3251 } else {
3243 const root_src_dir_path = fs.path.dirname(src_path);3252 break :blk try Package.Module.create(arena, .{
3244 break :blk Package.create(gpa, root_src_dir_path, fs.path.basename(src_path)) catch |err| {3253 .root = .{
3245 if (root_src_dir_path) |p| {3254 .root_dir = Cache.Directory.cwd(),
3246 fatal("unable to open '{s}': {s}", .{ p, @errorName(err) });3255 .sub_path = fs.path.dirname(src_path) orelse "",
3247 } else {3256 },
3248 return err;3257 .root_src_path = fs.path.basename(src_path),
3249 }3258 });
3250 };
3251 }3259 }
3252 } else null;3260 } else null;
3253 defer if (main_pkg) |p| p.destroy(gpa);
32543261
3255 // Transfer packages added with --deps to the root package3262 // Transfer packages added with --deps to the root package
3256 if (main_pkg) |mod| {3263 if (main_mod) |mod| {
3257 var it = ModuleDepIterator.init(root_deps_str orelse "");3264 var it = ModuleDepIterator.init(root_deps_str orelse "");
3258 while (it.next()) |dep| {3265 while (it.next()) |dep| {
3259 if (dep.expose.len == 0) {3266 if (dep.expose.len == 0) {
...@@ -3269,7 +3276,7 @@ fn buildOutputType(...@@ -3269,7 +3276,7 @@ fn buildOutputType(
3269 const dep_mod = modules.get(dep.name) orelse3276 const dep_mod = modules.get(dep.name) orelse
3270 fatal("root module depends on module '{s}' which does not exist", .{dep.name});3277 fatal("root module depends on module '{s}' which does not exist", .{dep.name});
32713278
3272 try mod.add(gpa, dep.expose, dep_mod.mod);3279 try mod.deps.put(arena, dep.expose, dep_mod.mod);
3273 }3280 }
3274 }3281 }
32753282
...@@ -3310,17 +3317,18 @@ fn buildOutputType(...@@ -3310,17 +3317,18 @@ fn buildOutputType(
3310 if (arg_mode == .run) {3317 if (arg_mode == .run) {
3311 break :l global_cache_directory;3318 break :l global_cache_directory;
3312 }3319 }
3313 if (main_pkg) |pkg| {3320 if (main_mod != null) {
3314 // search upwards from cwd until we find directory with build.zig3321 // search upwards from cwd until we find directory with build.zig
3315 const cwd_path = try process.getCwdAlloc(arena);3322 const cwd_path = try process.getCwdAlloc(arena);
3316 const build_zig = "build.zig";
3317 const zig_cache = "zig-cache";3323 const zig_cache = "zig-cache";
3318 var dirname: []const u8 = cwd_path;3324 var dirname: []const u8 = cwd_path;
3319 while (true) {3325 while (true) {
3320 const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig });3326 const joined_path = try fs.path.join(arena, &.{
3327 dirname, Package.build_zig_basename,
3328 });
3321 if (fs.cwd().access(joined_path, .{})) |_| {3329 if (fs.cwd().access(joined_path, .{})) |_| {
3322 const cache_dir_path = try fs.path.join(arena, &[_][]const u8{ dirname, zig_cache });3330 const cache_dir_path = try fs.path.join(arena, &.{ dirname, zig_cache });
3323 const dir = try pkg.root_src_directory.handle.makeOpenPath(cache_dir_path, .{});3331 const dir = try fs.cwd().makeOpenPath(cache_dir_path, .{});
3324 cleanup_local_cache_dir = dir;3332 cleanup_local_cache_dir = dir;
3325 break :l .{ .handle = dir, .path = cache_dir_path };3333 break :l .{ .handle = dir, .path = cache_dir_path };
3326 } else |err| switch (err) {3334 } else |err| switch (err) {
...@@ -3378,6 +3386,8 @@ fn buildOutputType(...@@ -3378,6 +3386,8 @@ fn buildOutputType(
33783386
3379 gimmeMoreOfThoseSweetSweetFileDescriptors();3387 gimmeMoreOfThoseSweetSweetFileDescriptors();
33803388
3389 if (true) @panic("TODO restore Compilation logic");
3390
3381 const comp = Compilation.create(gpa, .{3391 const comp = Compilation.create(gpa, .{
3382 .zig_lib_directory = zig_lib_directory,3392 .zig_lib_directory = zig_lib_directory,
3383 .local_cache_directory = local_cache_directory,3393 .local_cache_directory = local_cache_directory,
...@@ -3389,7 +3399,7 @@ fn buildOutputType(...@@ -3389,7 +3399,7 @@ fn buildOutputType(
3389 .dynamic_linker = target_info.dynamic_linker.get(),3399 .dynamic_linker = target_info.dynamic_linker.get(),
3390 .sysroot = sysroot,3400 .sysroot = sysroot,
3391 .output_mode = output_mode,3401 .output_mode = output_mode,
3392 .main_pkg = main_pkg,3402 .main_mod = main_mod,
3393 .emit_bin = emit_bin_loc,3403 .emit_bin = emit_bin_loc,
3394 .emit_h = emit_h_resolved.data,3404 .emit_h = emit_h_resolved.data,
3395 .emit_asm = emit_asm_resolved.data,3405 .emit_asm = emit_asm_resolved.data,
...@@ -4799,32 +4809,22 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4799,32 +4809,22 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4799 try thread_pool.init(.{ .allocator = gpa });4809 try thread_pool.init(.{ .allocator = gpa });
4800 defer thread_pool.deinit();4810 defer thread_pool.deinit();
48014811
4802 var cleanup_build_runner_dir: ?fs.Dir = null;4812 var main_mod: Package.Module = if (override_build_runner) |build_runner_path|
4803 defer if (cleanup_build_runner_dir) |*dir| dir.close();
4804
4805 var main_pkg: Package = if (override_build_runner) |build_runner_path|
4806 .{4813 .{
4807 .root_src_directory = blk: {4814 .root = .{
4808 if (std.fs.path.dirname(build_runner_path)) |dirname| {4815 .root_dir = Cache.Directory.cwd(),
4809 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {4816 .sub_path = fs.path.dirname(build_runner_path) orelse "",
4810 fatal("unable to open directory to build runner from argument 'build-runner', '{s}': {s}", .{ dirname, @errorName(err) });
4811 };
4812 cleanup_build_runner_dir = dir;
4813 break :blk .{ .path = dirname, .handle = dir };
4814 }
4815
4816 break :blk .{ .path = null, .handle = fs.cwd() };
4817 },4817 },
4818 .root_src_path = std.fs.path.basename(build_runner_path),4818 .root_src_path = fs.path.basename(build_runner_path),
4819 }4819 }
4820 else4820 else
4821 .{4821 .{
4822 .root_src_directory = zig_lib_directory,4822 .root = .{ .root_dir = zig_lib_directory },
4823 .root_src_path = "build_runner.zig",4823 .root_src_path = "build_runner.zig",
4824 };4824 };
48254825
4826 var build_pkg: Package = .{4826 var build_mod: Package.Module = .{
4827 .root_src_directory = build_directory,4827 .root = .{ .root_dir = build_directory },
4828 .root_src_path = build_zig_basename,4828 .root_src_path = build_zig_basename,
4829 };4829 };
4830 if (build_options.only_core_functionality) {4830 if (build_options.only_core_functionality) {
...@@ -4833,11 +4833,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4833,11 +4833,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4833 \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{};4833 \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{};
4834 \\4834 \\
4835 );4835 );
4836 try main_pkg.add(gpa, "@dependencies", deps_pkg);4836 try main_mod.deps.put(arena, "@dependencies", deps_pkg);
4837 } else {4837 } else {
4838 var http_client: std.http.Client = .{ .allocator = gpa };4838 var http_client: std.http.Client = .{ .allocator = gpa };
4839 defer http_client.deinit();4839 defer http_client.deinit();
48404840
4841 if (true) @panic("TODO restore package fetching logic");
4842
4841 // Here we provide an import to the build runner that allows using reflection to find4843 // Here we provide an import to the build runner that allows using reflection to find
4842 // all of the dependencies. Without this, there would be no way to use `@import` to4844 // all of the dependencies. Without this, there would be no way to use `@import` to
4843 // access dependencies by name, since `@import` requires string literals.4845 // access dependencies by name, since `@import` requires string literals.
...@@ -4857,8 +4859,8 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4857,8 +4859,8 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
48574859
4858 // Here we borrow main package's table and will replace it with a fresh4860 // Here we borrow main package's table and will replace it with a fresh
4859 // one after this process completes.4861 // one after this process completes.
4860 const fetch_result = build_pkg.fetchAndAddDependencies(4862 const fetch_result = build_mod.fetchAndAddDependencies(
4861 &main_pkg,4863 &main_mod,
4862 arena,4864 arena,
4863 &thread_pool,4865 &thread_pool,
4864 &http_client,4866 &http_client,
...@@ -4886,10 +4888,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4886,10 +4888,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4886 dependencies_source.items,4888 dependencies_source.items,
4887 );4889 );
48884890
4889 mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table);4891 mem.swap(Package.Table, &main_mod.table, &deps_pkg.table);
4890 try main_pkg.add(gpa, "@dependencies", deps_pkg);4892 try main_mod.add(gpa, "@dependencies", deps_pkg);
4891 }4893 }
4892 try main_pkg.add(gpa, "@build", &build_pkg);4894 try main_mod.add(gpa, "@build", &build_mod);
48934895
4894 const comp = Compilation.create(gpa, .{4896 const comp = Compilation.create(gpa, .{
4895 .zig_lib_directory = zig_lib_directory,4897 .zig_lib_directory = zig_lib_directory,
...@@ -4901,7 +4903,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4901,7 +4903,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4901 .is_native_abi = cross_target.isNativeAbi(),4903 .is_native_abi = cross_target.isNativeAbi(),
4902 .dynamic_linker = target_info.dynamic_linker.get(),4904 .dynamic_linker = target_info.dynamic_linker.get(),
4903 .output_mode = .Exe,4905 .output_mode = .Exe,
4904 .main_pkg = &main_pkg,4906 .main_mod = &main_mod,
4905 .emit_bin = emit_bin,4907 .emit_bin = emit_bin,
4906 .emit_h = null,4908 .emit_h = null,
4907 .optimize_mode = .Debug,4909 .optimize_mode = .Debug,
...@@ -5115,12 +5117,14 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -5115,12 +5117,14 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
5115 .tree = tree,5117 .tree = tree,
5116 .tree_loaded = true,5118 .tree_loaded = true,
5117 .zir = undefined,5119 .zir = undefined,
5118 .pkg = undefined,5120 .mod = undefined,
5119 .root_decl = .none,5121 .root_decl = .none,
5120 };5122 };
51215123
5122 file.pkg = try Package.create(gpa, null, file.sub_file_path);5124 file.mod = try Package.Module.create(arena, .{
5123 defer file.pkg.destroy(gpa);5125 .root = Package.Path.cwd(),
5126 .root_src_path = file.sub_file_path,
5127 });
51245128
5125 file.zir = try AstGen.generate(gpa, file.tree);5129 file.zir = try AstGen.generate(gpa, file.tree);
5126 file.zir_loaded = true;5130 file.zir_loaded = true;
...@@ -5321,12 +5325,14 @@ fn fmtPathFile(...@@ -5321,12 +5325,14 @@ fn fmtPathFile(
5321 .tree = tree,5325 .tree = tree,
5322 .tree_loaded = true,5326 .tree_loaded = true,
5323 .zir = undefined,5327 .zir = undefined,
5324 .pkg = undefined,5328 .mod = undefined,
5325 .root_decl = .none,5329 .root_decl = .none,
5326 };5330 };
53275331
5328 file.pkg = try Package.create(gpa, null, file.sub_file_path);5332 file.mod = try Package.Module.create(fmt.arena, .{
5329 defer file.pkg.destroy(gpa);5333 .root = Package.Path.cwd(),
5334 .root_src_path = file.sub_file_path,
5335 });
53305336
5331 if (stat.size > max_src_size)5337 if (stat.size > max_src_size)
5332 return error.FileTooBig;5338 return error.FileTooBig;
...@@ -5387,7 +5393,7 @@ pub fn putAstErrorsIntoBundle(...@@ -5387,7 +5393,7 @@ pub fn putAstErrorsIntoBundle(
5387 tree: Ast,5393 tree: Ast,
5388 path: []const u8,5394 path: []const u8,
5389 wip_errors: *std.zig.ErrorBundle.Wip,5395 wip_errors: *std.zig.ErrorBundle.Wip,
5390) !void {5396) Allocator.Error!void {
5391 var file: Module.File = .{5397 var file: Module.File = .{
5392 .status = .never_loaded,5398 .status = .never_loaded,
5393 .source_loaded = true,5399 .source_loaded = true,
...@@ -5402,12 +5408,15 @@ pub fn putAstErrorsIntoBundle(...@@ -5402,12 +5408,15 @@ pub fn putAstErrorsIntoBundle(
5402 .tree = tree,5408 .tree = tree,
5403 .tree_loaded = true,5409 .tree_loaded = true,
5404 .zir = undefined,5410 .zir = undefined,
5405 .pkg = undefined,5411 .mod = undefined,
5406 .root_decl = .none,5412 .root_decl = .none,
5407 };5413 };
54085414
5409 file.pkg = try Package.create(gpa, null, path);5415 file.mod = try Package.Module.create(gpa, .{
5410 defer file.pkg.destroy(gpa);5416 .root = Package.Path.cwd(),
5417 .root_src_path = file.sub_file_path,
5418 });
5419 defer gpa.destroy(file.mod);
54115420
5412 file.zir = try AstGen.generate(gpa, file.tree);5421 file.zir = try AstGen.generate(gpa, file.tree);
5413 file.zir_loaded = true;5422 file.zir_loaded = true;
...@@ -5933,7 +5942,7 @@ pub fn cmdAstCheck(...@@ -5933,7 +5942,7 @@ pub fn cmdAstCheck(
5933 .stat = undefined,5942 .stat = undefined,
5934 .tree = undefined,5943 .tree = undefined,
5935 .zir = undefined,5944 .zir = undefined,
5936 .pkg = undefined,5945 .mod = undefined,
5937 .root_decl = .none,5946 .root_decl = .none,
5938 };5947 };
5939 if (zig_source_file) |file_name| {5948 if (zig_source_file) |file_name| {
...@@ -5971,8 +5980,10 @@ pub fn cmdAstCheck(...@@ -5971,8 +5980,10 @@ pub fn cmdAstCheck(
5971 file.stat.size = source.len;5980 file.stat.size = source.len;
5972 }5981 }
59735982
5974 file.pkg = try Package.create(gpa, null, file.sub_file_path);5983 file.mod = try Package.Module.create(arena, .{
5975 defer file.pkg.destroy(gpa);5984 .root = Package.Path.cwd(),
5985 .root_src_path = file.sub_file_path,
5986 });
59765987
5977 file.tree = try Ast.parse(gpa, file.source, .zig);5988 file.tree = try Ast.parse(gpa, file.source, .zig);
5978 file.tree_loaded = true;5989 file.tree_loaded = true;
...@@ -6067,7 +6078,7 @@ pub fn cmdDumpZir(...@@ -6067,7 +6078,7 @@ pub fn cmdDumpZir(
6067 .stat = undefined,6078 .stat = undefined,
6068 .tree = undefined,6079 .tree = undefined,
6069 .zir = try Module.loadZirCache(gpa, f),6080 .zir = try Module.loadZirCache(gpa, f),
6070 .pkg = undefined,6081 .mod = undefined,
6071 .root_decl = .none,6082 .root_decl = .none,
6072 };6083 };
60736084
...@@ -6136,12 +6147,14 @@ pub fn cmdChangelist(...@@ -6136,12 +6147,14 @@ pub fn cmdChangelist(
6136 },6147 },
6137 .tree = undefined,6148 .tree = undefined,
6138 .zir = undefined,6149 .zir = undefined,
6139 .pkg = undefined,6150 .mod = undefined,
6140 .root_decl = .none,6151 .root_decl = .none,
6141 };6152 };
61426153
6143 file.pkg = try Package.create(gpa, null, file.sub_file_path);6154 file.mod = try Package.Module.create(arena, .{
6144 defer file.pkg.destroy(gpa);6155 .root = Package.Path.cwd(),
6156 .root_src_path = file.sub_file_path,
6157 });
61456158
6146 const source = try arena.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);6159 const source = try arena.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
6147 const amt = try f.readAll(source);6160 const amt = try f.readAll(source);
...@@ -6623,8 +6636,11 @@ fn cmdFetch(...@@ -6623,8 +6636,11 @@ fn cmdFetch(
6623 args: []const []const u8,6636 args: []const []const u8,
6624) !void {6637) !void {
6625 const color: Color = .auto;6638 const color: Color = .auto;
6626 var opt_url: ?[]const u8 = null;6639 const work_around_btrfs_bug = builtin.os.tag == .linux and
6640 std.process.hasEnvVarConstant("ZIG_BTRFS_WORKAROUND");
6641 var opt_path_or_url: ?[]const u8 = null;
6627 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");6642 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");
6643 var recursive = false;
66286644
6629 {6645 {
6630 var i: usize = 0;6646 var i: usize = 0;
...@@ -6640,18 +6656,21 @@ fn cmdFetch(...@@ -6640,18 +6656,21 @@ fn cmdFetch(
6640 i += 1;6656 i += 1;
6641 override_global_cache_dir = args[i];6657 override_global_cache_dir = args[i];
6642 continue;6658 continue;
6659 } else if (mem.eql(u8, arg, "--recursive")) {
6660 recursive = true;
6661 continue;
6643 } else {6662 } else {
6644 fatal("unrecognized parameter: '{s}'", .{arg});6663 fatal("unrecognized parameter: '{s}'", .{arg});
6645 }6664 }
6646 } else if (opt_url != null) {6665 } else if (opt_path_or_url != null) {
6647 fatal("unexpected extra parameter: '{s}'", .{arg});6666 fatal("unexpected extra parameter: '{s}'", .{arg});
6648 } else {6667 } else {
6649 opt_url = arg;6668 opt_path_or_url = arg;
6650 }6669 }
6651 }6670 }
6652 }6671 }
66536672
6654 const url = opt_url orelse fatal("missing url or path parameter", .{});6673 const path_or_url = opt_path_or_url orelse fatal("missing url or path parameter", .{});
66556674
6656 var thread_pool: ThreadPool = undefined;6675 var thread_pool: ThreadPool = undefined;
6657 try thread_pool.init(.{ .allocator = gpa });6676 try thread_pool.init(.{ .allocator = gpa });
...@@ -6664,19 +6683,6 @@ fn cmdFetch(...@@ -6664,19 +6683,6 @@ fn cmdFetch(
6664 const root_prog_node = progress.start("Fetch", 0);6683 const root_prog_node = progress.start("Fetch", 0);
6665 defer root_prog_node.end();6684 defer root_prog_node.end();
66666685
6667 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6668 try wip_errors.init(gpa);
6669 defer wip_errors.deinit();
6670
6671 var report: Package.Report = .{
6672 .ast = null,
6673 .directory = .{
6674 .handle = fs.cwd(),
6675 .path = null,
6676 },
6677 .error_bundle = &wip_errors,
6678 };
6679
6680 var global_cache_directory: Compilation.Directory = l: {6686 var global_cache_directory: Compilation.Directory = l: {
6681 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);6687 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
6682 break :l .{6688 break :l .{
...@@ -6686,56 +6692,48 @@ fn cmdFetch(...@@ -6686,56 +6692,48 @@ fn cmdFetch(
6686 };6692 };
6687 defer global_cache_directory.handle.close();6693 defer global_cache_directory.handle.close();
66886694
6689 var readable_resource: Package.ReadableResource = rr: {6695 var job_queue: Package.Fetch.JobQueue = .{
6690 if (fs.cwd().openIterableDir(url, .{})) |dir| {6696 .http_client = &http_client,
6691 break :rr .{6697 .thread_pool = &thread_pool,
6692 .path = try gpa.dupe(u8, url),6698 .global_cache = global_cache_directory,
6693 .resource = .{ .dir = dir },6699 .recursive = recursive,
6694 };6700 .work_around_btrfs_bug = work_around_btrfs_bug,
6695 } else |dir_err| {6701 };
6696 const file_err = if (dir_err == error.NotDir) e: {6702 defer job_queue.deinit();
6697 if (fs.cwd().openFile(url, .{})) |f| {6703
6698 break :rr .{6704 var fetch: Package.Fetch = .{
6699 .path = try gpa.dupe(u8, url),6705 .arena = std.heap.ArenaAllocator.init(gpa),
6700 .resource = .{ .file = f },6706 .location = .{ .path_or_url = path_or_url },
6701 };6707 .location_tok = 0,
6702 } else |err| break :e err;6708 .hash_tok = 0,
6703 } else dir_err;6709 .parent_package_root = undefined,
67046710 .parent_manifest_ast = null,
6705 const uri = std.Uri.parse(url) catch |uri_err| {6711 .prog_node = root_prog_node,
6706 fatal("'{s}' could not be recognized as a file path ({s}) or an URL ({s})", .{6712 .job_queue = &job_queue,
6707 url, @errorName(file_err), @errorName(uri_err),6713 .omit_missing_hash_error = true,
6708 });6714
6709 };6715 .package_root = undefined,
6710 const fetch_location = try Package.FetchLocation.initUri(uri, 0, report);6716 .error_bundle = undefined,
6711 const cwd: Cache.Directory = .{6717 .manifest = null,
6712 .handle = fs.cwd(),6718 .manifest_ast = undefined,
6713 .path = null,6719 .actual_hash = undefined,
6714 };6720 .has_build_zig = false,
6715 break :rr try fetch_location.fetch(gpa, cwd, &http_client, 0, report);6721 .oom_flag = false,
6716 }
6717 };6722 };
6718 defer readable_resource.deinit(gpa);6723 defer fetch.deinit();
67196724
6720 var package_location = readable_resource.unpack(6725 fetch.run() catch |err| switch (err) {
6721 gpa,6726 error.OutOfMemory => fatal("out of memory", .{}),
6722 &thread_pool,6727 error.FetchFailed => {}, // error bundle checked below
6723 global_cache_directory,
6724 0,
6725 report,
6726 root_prog_node,
6727 ) catch |err| {
6728 if (wip_errors.root_list.items.len > 0) {
6729 var errors = try wip_errors.toOwnedBundle("");
6730 defer errors.deinit(gpa);
6731 errors.renderToStdErr(renderOptions(color));
6732 process.exit(1);
6733 }
6734 fatal("unable to unpack '{s}': {s}", .{ url, @errorName(err) });
6735 };6728 };
6736 defer package_location.deinit(gpa);
67376729
6738 const hex_digest = Package.Manifest.hexDigest(package_location.hash);6730 if (fetch.error_bundle.root_list.items.len > 0) {
6731 var errors = try fetch.error_bundle.toOwnedBundle("");
6732 errors.renderToStdErr(renderOptions(color));
6733 process.exit(1);
6734 }
6735
6736 const hex_digest = Package.Manifest.hexDigest(fetch.actual_hash);
67396737
6740 progress.done = true;6738 progress.done = true;
6741 progress.refresh();6739 progress.refresh();