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 {
8888 .name = "check-case",
8989 .root_source_file = .{ .path = "test/src/Cases.zig" },
9090 .optimize = optimize,
91 .main_pkg_path = .{ .path = "." },
91 .main_mod_path = .{ .path = "." },
9292 });
9393 check_case_exe.stack_size = stack_size;
9494 check_case_exe.single_threaded = single_threaded;
lib/std/Build.zig+20-5
......@@ -634,6 +634,9 @@ pub const ExecutableOptions = struct {
634634 use_llvm: ?bool = null,
635635 use_lld: ?bool = null,
636636 zig_lib_dir: ?LazyPath = null,
637 main_mod_path: ?LazyPath = null,
638
639 /// Deprecated; use `main_mod_path`.
637640 main_pkg_path: ?LazyPath = null,
638641};
639642
......@@ -652,7 +655,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile {
652655 .use_llvm = options.use_llvm,
653656 .use_lld = options.use_lld,
654657 .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,
656659 });
657660}
658661
......@@ -667,6 +670,9 @@ pub const ObjectOptions = struct {
667670 use_llvm: ?bool = null,
668671 use_lld: ?bool = null,
669672 zig_lib_dir: ?LazyPath = null,
673 main_mod_path: ?LazyPath = null,
674
675 /// Deprecated; use `main_mod_path`.
670676 main_pkg_path: ?LazyPath = null,
671677};
672678
......@@ -683,7 +689,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *Step.Compile {
683689 .use_llvm = options.use_llvm,
684690 .use_lld = options.use_lld,
685691 .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,
687693 });
688694}
689695
......@@ -699,6 +705,9 @@ pub const SharedLibraryOptions = struct {
699705 use_llvm: ?bool = null,
700706 use_lld: ?bool = null,
701707 zig_lib_dir: ?LazyPath = null,
708 main_mod_path: ?LazyPath = null,
709
710 /// Deprecated; use `main_mod_path`.
702711 main_pkg_path: ?LazyPath = null,
703712};
704713
......@@ -717,7 +726,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile
717726 .use_llvm = options.use_llvm,
718727 .use_lld = options.use_lld,
719728 .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,
721730 });
722731}
723732
......@@ -733,6 +742,9 @@ pub const StaticLibraryOptions = struct {
733742 use_llvm: ?bool = null,
734743 use_lld: ?bool = null,
735744 zig_lib_dir: ?LazyPath = null,
745 main_mod_path: ?LazyPath = null,
746
747 /// Deprecated; use `main_mod_path`.
736748 main_pkg_path: ?LazyPath = null,
737749};
738750
......@@ -751,7 +763,7 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *Step.Compile
751763 .use_llvm = options.use_llvm,
752764 .use_lld = options.use_lld,
753765 .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,
755767 });
756768}
757769
......@@ -769,6 +781,9 @@ pub const TestOptions = struct {
769781 use_llvm: ?bool = null,
770782 use_lld: ?bool = null,
771783 zig_lib_dir: ?LazyPath = null,
784 main_mod_path: ?LazyPath = null,
785
786 /// Deprecated; use `main_mod_path`.
772787 main_pkg_path: ?LazyPath = null,
773788};
774789
......@@ -787,7 +802,7 @@ pub fn addTest(b: *Build, options: TestOptions) *Step.Compile {
787802 .use_llvm = options.use_llvm,
788803 .use_lld = options.use_lld,
789804 .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,
791806 });
792807}
793808
lib/std/Build/Cache.zig+11
......@@ -9,6 +9,13 @@ pub const Directory = struct {
99 path: ?[]const u8,
1010 handle: fs.Dir,
1111
12 pub fn cwd() Directory {
13 return .{
14 .path = null,
15 .handle = fs.cwd(),
16 };
17 }
18
1219 pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {
1320 if (self.path) |p| {
1421 // TODO clean way to do this with only 1 allocation
......@@ -53,6 +60,10 @@ pub const Directory = struct {
5360 try writer.writeAll(fs.path.sep_str);
5461 }
5562 }
63
64 pub fn eql(self: Directory, other: Directory) bool {
65 return self.handle.fd == other.handle.fd;
66 }
5667};
5768
5869gpa: Allocator,
lib/std/Build/Step/Compile.zig+9-6
......@@ -68,7 +68,7 @@ c_std: std.Build.CStd,
6868/// Set via options; intended to be read-only after that.
6969zig_lib_dir: ?LazyPath,
7070/// Set via options; intended to be read-only after that.
71main_pkg_path: ?LazyPath,
71main_mod_path: ?LazyPath,
7272exec_cmd_args: ?[]const ?[]const u8,
7373filter: ?[]const u8,
7474test_evented_io: bool = false,
......@@ -316,6 +316,9 @@ pub const Options = struct {
316316 use_llvm: ?bool = null,
317317 use_lld: ?bool = null,
318318 zig_lib_dir: ?LazyPath = null,
319 main_mod_path: ?LazyPath = null,
320
321 /// deprecated; use `main_mod_path`.
319322 main_pkg_path: ?LazyPath = null,
320323};
321324
......@@ -480,7 +483,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
480483 .installed_headers = ArrayList(*Step).init(owner.allocator),
481484 .c_std = std.Build.CStd.C99,
482485 .zig_lib_dir = null,
483 .main_pkg_path = null,
486 .main_mod_path = null,
484487 .exec_cmd_args = null,
485488 .filter = options.filter,
486489 .test_runner = options.test_runner,
......@@ -515,8 +518,8 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
515518 lp.addStepDependencies(&self.step);
516519 }
517520
518 if (options.main_pkg_path) |lp| {
519 self.main_pkg_path = lp.dupe(self.step.owner);
521 if (options.main_mod_path orelse options.main_pkg_path) |lp| {
522 self.main_mod_path = lp.dupe(self.step.owner);
520523 lp.addStepDependencies(&self.step);
521524 }
522525
......@@ -1998,8 +2001,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
19982001 try zig_args.append(dir.getPath(b));
19992002 }
20002003
2001 if (self.main_pkg_path) |dir| {
2002 try zig_args.append("--main-pkg-path");
2004 if (self.main_mod_path) |dir| {
2005 try zig_args.append("--main-mod-path");
20032006 try zig_args.append(dir.getPath(b));
20042007 }
20052008
src/Compilation.zig+84-86
......@@ -273,8 +273,8 @@ const Job = union(enum) {
273273 /// The source file containing the Decl has been updated, and so the
274274 /// Decl may need its line number information updated in the debug info.
275275 update_line_number: Module.Decl.Index,
276 /// The main source file for the package needs to be analyzed.
277 analyze_pkg: *Package,
276 /// The main source file for the module needs to be analyzed.
277 analyze_mod: *Package.Module,
278278
279279 /// one of the glibc static objects
280280 glibc_crt_file: glibc.CRTFile,
......@@ -414,7 +414,7 @@ pub const MiscTask = enum {
414414 compiler_rt,
415415 libssp,
416416 zig_libc,
417 analyze_pkg,
417 analyze_mod,
418418
419419 @"musl crti.o",
420420 @"musl crtn.o",
......@@ -544,7 +544,7 @@ pub const InitOptions = struct {
544544 global_cache_directory: Directory,
545545 target: Target,
546546 root_name: []const u8,
547 main_pkg: ?*Package,
547 main_mod: ?*Package.Module,
548548 output_mode: std.builtin.OutputMode,
549549 thread_pool: *ThreadPool,
550550 dynamic_linker: ?[]const u8 = null,
......@@ -736,53 +736,53 @@ pub const InitOptions = struct {
736736 pdb_out_path: ?[]const u8 = null,
737737};
738738
739fn addPackageTableToCacheHash(
739fn addModuleTableToCacheHash(
740740 hash: *Cache.HashHelper,
741741 arena: *std.heap.ArenaAllocator,
742 pkg_table: Package.Table,
743 seen_table: *std.AutoHashMap(*Package, void),
742 mod_table: Package.Module.Deps,
743 seen_table: *std.AutoHashMap(*Package.Module, void),
744744 hash_type: union(enum) { path_bytes, files: *Cache.Manifest },
745745) (error{OutOfMemory} || std.os.GetCwdError)!void {
746746 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());
749749 {
750750 // Copy over the hashmap entries to our slice
751 var table_it = pkg_table.iterator();
751 var table_it = mod_table.iterator();
752752 var idx: usize = 0;
753753 while (table_it.next()) |entry| : (idx += 1) {
754 packages[idx] = .{
754 modules[idx] = .{
755755 .key = entry.key_ptr.*,
756756 .value = entry.value_ptr.*,
757757 };
758758 }
759759 }
760760 // Sort the slice by package name
761 mem.sort(Package.Table.KV, packages, {}, struct {
762 fn lessThan(_: void, lhs: Package.Table.KV, rhs: Package.Table.KV) bool {
761 mem.sortUnstable(Package.Module.Deps.KV, modules, {}, struct {
762 fn lessThan(_: void, lhs: Package.Module.Deps.KV, rhs: Package.Module.Deps.KV) bool {
763763 return std.mem.lessThan(u8, lhs.key, rhs.key);
764764 }
765765 }.lessThan);
766766
767 for (packages) |pkg| {
768 if ((try seen_table.getOrPut(pkg.value)).found_existing) continue;
767 for (modules) |mod| {
768 if ((try seen_table.getOrPut(mod.value)).found_existing) continue;
769769
770770 // Finally insert the package name and path to the cache hash.
771 hash.addBytes(pkg.key);
771 hash.addBytes(mod.key);
772772 switch (hash_type) {
773773 .path_bytes => {
774 hash.addBytes(pkg.value.root_src_path);
775 hash.addOptionalBytes(pkg.value.root_src_directory.path);
774 hash.addBytes(mod.value.root_src_path);
775 hash.addOptionalBytes(mod.value.root_src_directory.path);
776776 },
777777 .files => |man| {
778 const pkg_zig_file = try pkg.value.root_src_directory.join(allocator, &[_][]const u8{
779 pkg.value.root_src_path,
778 const pkg_zig_file = try mod.value.root_src_directory.join(allocator, &[_][]const u8{
779 mod.value.root_src_path,
780780 });
781781 _ = try man.addFile(pkg_zig_file, null);
782782 },
783783 }
784 // Recurse to handle the package's dependencies
785 try addPackageTableToCacheHash(hash, arena, pkg.value.table, seen_table, hash_type);
784 // Recurse to handle the module's dependencies
785 try addModuleTableToCacheHash(hash, arena, mod.value.deps, seen_table, hash_type);
786786 }
787787}
788788
......@@ -839,7 +839,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
839839 break :blk true;
840840
841841 // If we have no zig code to compile, no need for LLVM.
842 if (options.main_pkg == null)
842 if (options.main_mod == null)
843843 break :blk false;
844844
845845 // 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 {
869869 // compiler state, the second clause here can be removed so that incremental
870870 // cache mode is used for LLVM backend too. We need some fuzz testing before
871871 // 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)
873873 CacheMode.whole
874874 else
875875 options.cache_mode;
......@@ -925,7 +925,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
925925 if (use_llvm) {
926926 // If stage1 generates an object file, self-hosted linker is not
927927 // yet sophisticated enough to handle that.
928 break :blk options.main_pkg != null;
928 break :blk options.main_mod != null;
929929 }
930930
931931 break :blk false;
......@@ -1210,7 +1210,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12101210 if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);
12111211 // 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: {
12141214 // Options that are specific to zig source files, that cannot be
12151215 // modified between incremental updates.
12161216 var hash = cache.hash;
......@@ -1223,11 +1223,12 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12231223 // do want to namespace different source file names because they are
12241224 // likely different compilations and therefore this would be likely to
12251225 // cause cache hits.
1226 hash.addBytes(main_pkg.root_src_path);
1227 hash.addOptionalBytes(main_pkg.root_src_directory.path);
1226 hash.addBytes(main_mod.root_src_path);
1227 hash.addOptionalBytes(main_mod.root.root_dir.path);
1228 hash.addBytes(main_mod.root.sub_path);
12281229 {
1229 var seen_table = std.AutoHashMap(*Package, void).init(arena);
1230 try addPackageTableToCacheHash(&hash, &arena_allocator, main_pkg.table, &seen_table, .path_bytes);
1230 var seen_table = std.AutoHashMap(*Package.Module, void).init(arena);
1231 try addModuleTableToCacheHash(&hash, &arena_allocator, main_mod.deps, &seen_table, .path_bytes);
12311232 }
12321233 },
12331234 .whole => {
......@@ -1283,34 +1284,31 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12831284 .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),
12841285 };
12851286
1286 const builtin_pkg = try Package.createWithDir(
1287 gpa,
1288 zig_cache_artifact_directory,
1289 null,
1290 "builtin.zig",
1291 );
1292 errdefer builtin_pkg.destroy(gpa);
1287 const builtin_mod = try Package.Module.create(arena, .{
1288 .root = .{ .root_dir = zig_cache_artifact_directory },
1289 .root_src_path = "builtin.zig",
1290 });
12931291
1294 // When you're testing std, the main module is std. In that case, we'll just set the std
1295 // module to the main one, since avoiding the errors caused by duplicating it is more
1296 // effort than it's worth.
1297 const main_pkg_is_std = m: {
1292 // When you're testing std, the main module is std. In that case,
1293 // we'll just set the std module to the main one, since avoiding
1294 // the errors caused by duplicating it is more effort than it's
1295 // worth.
1296 const main_mod_is_std = m: {
12981297 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
12991298 options.zig_lib_directory.path orelse ".",
13001299 "std",
13011300 "std.zig",
13021301 });
1303 defer arena.free(std_path);
13041302 const main_path = try std.fs.path.resolve(arena, &[_][]const u8{
1305 main_pkg.root_src_directory.path orelse ".",
1306 main_pkg.root_src_path,
1303 main_mod.root.root_dir.path orelse ".",
1304 main_mod.root.sub_path,
1305 main_mod.root_src_path,
13071306 });
1308 defer arena.free(main_path);
13091307 break :m mem.eql(u8, main_path, std_path);
13101308 };
13111309
1312 const std_pkg = if (main_pkg_is_std)
1313 main_pkg
1310 const std_mod = if (main_mod_is_std)
1311 main_mod
13141312 else
13151313 try Package.createWithDir(
13161314 gpa,
......@@ -1319,16 +1317,16 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
13191317 "std.zig",
13201318 );
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: {
13251323 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: {
13261324 const test_dir = std.fs.path.dirname(test_runner);
13271325 const basename = std.fs.path.basename(test_runner);
13281326 const pkg = try Package.create(gpa, test_dir, basename);
13291327
1330 // copy package table from main_pkg to root_pkg
1331 pkg.table = try main_pkg.table.clone(gpa);
1328 // copy module table from main_mod to root_mod
1329 pkg.deps = try main_mod.deps.clone(gpa);
13321330 break :test_pkg pkg;
13331331 } else try Package.createWithDir(
13341332 gpa,
......@@ -1338,26 +1336,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
13381336 );
13391337 errdefer test_pkg.destroy(gpa);
13401338
1341 break :root_pkg test_pkg;
1342 } else main_pkg;
1343 errdefer if (options.is_test) root_pkg.destroy(gpa);
1339 break :root_mod test_pkg;
1340 } else main_mod;
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: {
1346 break :compiler_rt_pkg try Package.createWithDir(
1343 const compiler_rt_mod = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_mod: {
1344 break :compiler_rt_mod try Package.createWithDir(
13471345 gpa,
13481346 options.zig_lib_directory,
13491347 null,
13501348 "compiler_rt.zig",
13511349 );
13521350 } 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);
1356 try main_pkg.add(gpa, "root", root_pkg);
1357 try main_pkg.add(gpa, "std", std_pkg);
1353 try main_mod.add(gpa, "builtin", builtin_mod);
1354 try main_mod.add(gpa, "root", root_mod);
1355 try main_mod.add(gpa, "std", std_mod);
13581356
1359 if (compiler_rt_pkg) |p| {
1360 try main_pkg.add(gpa, "compiler_rt", p);
1357 if (compiler_rt_mod) |p| {
1358 try main_mod.add(gpa, "compiler_rt", p);
13611359 }
13621360
13631361 // 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 {
13951393 module.* = .{
13961394 .gpa = gpa,
13971395 .comp = comp,
1398 .main_pkg = main_pkg,
1399 .root_pkg = root_pkg,
1396 .main_mod = main_mod,
1397 .root_mod = root_mod,
14001398 .zig_cache_artifact_directory = zig_cache_artifact_directory,
14011399 .global_zir_cache = global_zir_cache,
14021400 .local_zir_cache = local_zir_cache,
......@@ -2005,8 +2003,8 @@ fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Director
20052003 // This is only for cleanup purposes; Module.deinit calls close
20062004 // on the handle of zig_cache_artifact_directory.
20072005 if (comp.bin_file.options.module) |module| {
2008 const builtin_pkg = module.main_pkg.table.get("builtin").?;
2009 module.zig_cache_artifact_directory = builtin_pkg.root_src_directory;
2006 const builtin_mod = module.main_mod.deps.get("builtin").?;
2007 module.zig_cache_artifact_directory = builtin_mod.root_src_directory;
20102008 }
20112009}
20122010
......@@ -2148,8 +2146,8 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
21482146
21492147 // Make sure std.zig is inside the import_table. We unconditionally need
21502148 // it for start.zig.
2151 const std_pkg = module.main_pkg.table.get("std").?;
2152 _ = try module.importPkg(std_pkg);
2149 const std_mod = module.main_mod.deps.get("std").?;
2150 _ = try module.importPkg(std_mod);
21532151
21542152 // Normally we rely on importing std to in turn import the root source file
21552153 // 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
21582156 // Likewise, in the case of `zig test`, the test runner is the root source file,
21592157 // and so there is nothing to import the main file.
21602158 if (comp.bin_file.options.is_test) {
2161 _ = try module.importPkg(module.main_pkg);
2159 _ = try module.importPkg(module.main_mod);
21622160 }
21632161
2164 if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| {
2165 _ = try module.importPkg(compiler_rt_pkg);
2162 if (module.main_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2163 _ = try module.importPkg(compiler_rt_mod);
21662164 }
21672165
21682166 // 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
21852183 }
21862184 }
21872185
2188 try comp.work_queue.writeItem(.{ .analyze_pkg = std_pkg });
2186 try comp.work_queue.writeItem(.{ .analyze_mod = std_mod });
21892187 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 });
21912189 }
21922190
2193 if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| {
2194 try comp.work_queue.writeItem(.{ .analyze_pkg = compiler_rt_pkg });
2191 if (module.main_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2192 try comp.work_queue.writeItem(.{ .analyze_mod = compiler_rt_mod });
21952193 }
21962194 }
21972195
......@@ -2420,19 +2418,19 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
24202418 comptime assert(link_hash_implementation_version == 10);
24212419
24222420 if (comp.bin_file.options.module) |mod| {
2423 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
2424 mod.main_pkg.root_src_path,
2421 const main_zig_file = try mod.main_mod.root_src_directory.join(arena, &[_][]const u8{
2422 mod.main_mod.root_src_path,
24252423 });
24262424 _ = try man.addFile(main_zig_file, null);
24272425 {
2428 var seen_table = std.AutoHashMap(*Package, void).init(arena);
2426 var seen_table = std.AutoHashMap(*Package.Module, void).init(arena);
24292427
24302428 // Skip builtin.zig; it is useless as an input, and we don't want to have to
24312429 // write it before checking for a cache hit.
2432 const builtin_pkg = mod.main_pkg.table.get("builtin").?;
2433 try seen_table.put(builtin_pkg, {});
2430 const builtin_mod = mod.main_mod.deps.get("builtin").?;
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 });
24362434 }
24372435
24382436 // Synchronize with other matching comments: ZigOnlyHashStuff
......@@ -3564,8 +3562,8 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
35643562 decl.analysis = .codegen_failure_retryable;
35653563 };
35663564 },
3567 .analyze_pkg => |pkg| {
3568 const named_frame = tracy.namedFrame("analyze_pkg");
3565 .analyze_mod => |pkg| {
3566 const named_frame = tracy.namedFrame("analyze_mod");
35693567 defer named_frame.end();
35703568
35713569 const module = comp.bin_file.options.module.?;
......@@ -6379,11 +6377,11 @@ fn buildOutputFromZig(
63796377
63806378 std.debug.assert(output_mode != .Exe);
63816379
6382 var main_pkg: Package = .{
6380 var main_mod: Package = .{
63836381 .root_src_directory = comp.zig_lib_directory,
63846382 .root_src_path = src_basename,
63856383 };
6386 defer main_pkg.deinitTable(comp.gpa);
6384 defer main_mod.deinitTable(comp.gpa);
63876385 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
63886386 const target = comp.getTarget();
63896387 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
......@@ -6404,7 +6402,7 @@ fn buildOutputFromZig(
64046402 .cache_mode = .whole,
64056403 .target = target,
64066404 .root_name = root_name,
6407 .main_pkg = &main_pkg,
6405 .main_mod = &main_mod,
64086406 .output_mode = output_mode,
64096407 .thread_pool = comp.thread_pool,
64106408 .libc_installation = comp.bin_file.options.libc_installation,
......@@ -6481,7 +6479,7 @@ pub fn build_crt_file(
64816479 .cache_mode = .whole,
64826480 .target = target,
64836481 .root_name = root_name,
6484 .main_pkg = null,
6482 .main_mod = null,
64856483 .output_mode = output_mode,
64866484 .thread_pool = comp.thread_pool,
64876485 .libc_installation = comp.bin_file.options.libc_installation,
src/Manifest.zig+8-6
......@@ -1,6 +1,10 @@
11pub const max_bytes = 10 * 1024 * 1024;
22pub const basename = "build.zig.zon";
33pub 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
59pub const Dependency = struct {
610 location: union(enum) {
......@@ -46,7 +50,6 @@ comptime {
4650 assert(@intFromEnum(multihash_function) < 127);
4751 assert(Hash.digest_length < 127);
4852}
49pub const multihash_len = 1 + 1 + Hash.digest_length;
5053
5154name: []const u8,
5255version: std.SemanticVersion,
......@@ -122,8 +125,8 @@ test hex64 {
122125 try std.testing.expectEqualStrings("[00efcdab78563412]", s);
123126}
124127
125pub fn hexDigest(digest: [Hash.digest_length]u8) [multihash_len * 2]u8 {
126 var result: [multihash_len * 2]u8 = undefined;
128pub fn hexDigest(digest: Digest) MultiHashHexDigest {
129 var result: MultiHashHexDigest = undefined;
127130
128131 result[0] = hex_charset[@intFromEnum(multihash_function) >> 4];
129132 result[1] = hex_charset[@intFromEnum(multihash_function) & 15];
......@@ -339,10 +342,9 @@ const Parse = struct {
339342 }
340343 }
341344
342 const hex_multihash_len = 2 * Manifest.multihash_len;
343 if (h.len != hex_multihash_len) {
345 if (h.len != multihash_hex_digest_len) {
344346 return fail(p, tok, "wrong hash size. expected: {d}, found: {d}", .{
345 hex_multihash_len, h.len,
347 multihash_hex_digest_len, h.len,
346348 });
347349 }
348350
src/Module.zig+37-44
......@@ -55,10 +55,10 @@ comp: *Compilation,
5555/// Where build artifacts and incremental compilation metadata serialization go.
5656zig_cache_artifact_directory: Compilation.Directory,
5757/// Pointer to externally managed resource.
58root_pkg: *Package,
59/// Normally, `main_pkg` and `root_pkg` 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.
61main_pkg: *Package,
58root_mod: *Package.Module,
59/// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which
60/// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests.
61main_mod: *Package.Module,
6262sema_prog_node: std.Progress.Node = undefined,
6363
6464/// Used by AstGen worker to load and store ZIR cache.
......@@ -973,8 +973,8 @@ pub const File = struct {
973973 tree: Ast,
974974 /// Whether this is populated or not depends on `zir_loaded`.
975975 zir: Zir,
976 /// Package that this file is a part of, managed externally.
977 pkg: *Package,
976 /// Module that this file is a part of, managed externally.
977 mod: *Package.Module,
978978 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.
979979 multi_pkg: bool = false,
980980 /// List of references to this file, used for multi-package errors.
......@@ -1058,14 +1058,9 @@ pub const File = struct {
10581058 .stat = file.stat,
10591059 };
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
10661061 // Keep track of inode, file size, mtime, hash so we can detect which files
10671062 // 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, .{});
10691064 defer f.close();
10701065
10711066 const stat = try f.stat();
......@@ -1134,14 +1129,12 @@ pub const File = struct {
11341129 return ip.getOrPutTrailingString(mod.gpa, ip.string_bytes.items.len - start);
11351130 }
11361131
1137 /// Returns the full path to this file relative to its package.
11381132 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);
11401134 }
11411135
1142 /// Returns the full path to this file relative to its package.
11431136 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);
11451138 }
11461139
11471140 pub fn dumpSrc(file: *File, src: LazySrcLoc) void {
......@@ -2543,25 +2536,25 @@ pub fn deinit(mod: *Module) void {
25432536
25442537 mod.deletion_set.deinit(gpa);
25452538
2546 // The callsite of `Compilation.create` owns the `main_pkg`, however
2539 // The callsite of `Compilation.create` owns the `main_mod`, however
25472540 // 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| {
25492542 gpa.free(kv.key);
25502543 kv.value.destroy(gpa);
25512544 }
2552 if (mod.main_pkg.table.fetchRemove("std")) |kv| {
2545 if (mod.main_mod.table.fetchRemove("std")) |kv| {
25532546 gpa.free(kv.key);
2554 // It's possible for main_pkg to be std when running 'zig test'! In this case, we must not
2547 // It's possible for main_mod to be std when running 'zig test'! In this case, we must not
25552548 // destroy it, since it would lead to a double-free.
2556 if (kv.value != mod.main_pkg) {
2549 if (kv.value != mod.main_mod) {
25572550 kv.value.destroy(gpa);
25582551 }
25592552 }
2560 if (mod.main_pkg.table.fetchRemove("root")) |kv| {
2553 if (mod.main_mod.table.fetchRemove("root")) |kv| {
25612554 gpa.free(kv.key);
25622555 }
2563 if (mod.root_pkg != mod.main_pkg) {
2564 mod.root_pkg.destroy(gpa);
2556 if (mod.root_mod != mod.main_mod) {
2557 mod.root_mod.destroy(gpa);
25652558 }
25662559
25672560 mod.compile_log_text.deinit(gpa);
......@@ -2715,7 +2708,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
27152708
27162709 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;
27192712 const digest = hash: {
27202713 var path_hash: Cache.HashHelper = .{};
27212714 path_hash.addBytes(build_options.version);
......@@ -3158,23 +3151,23 @@ pub fn populateBuiltinFile(mod: *Module) !void {
31583151 comp.mutex.lock();
31593152 defer comp.mutex.unlock();
31603153
3161 const builtin_pkg = mod.main_pkg.table.get("builtin").?;
3162 const result = try mod.importPkg(builtin_pkg);
3154 const builtin_mod = mod.main_mod.table.get("builtin").?;
3155 const result = try mod.importPkg(builtin_mod);
31633156 break :blk .{
31643157 .file = result.file,
3165 .pkg = builtin_pkg,
3158 .pkg = builtin_mod,
31663159 };
31673160 };
31683161 const file = pkg_and_file.file;
3169 const builtin_pkg = pkg_and_file.pkg;
3162 const builtin_mod = pkg_and_file.pkg;
31703163 const gpa = mod.gpa;
31713164 file.source = try comp.generateBuiltinZigSource(gpa);
31723165 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| {
31753168 if (stat.size != file.source.len) {
3176 const full_path = try builtin_pkg.root_src_directory.join(gpa, &.{
3177 builtin_pkg.root_src_path,
3169 const full_path = try builtin_mod.root_src_directory.join(gpa, &.{
3170 builtin_mod.root_src_path,
31783171 });
31793172 defer gpa.free(full_path);
31803173
......@@ -3184,7 +3177,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {
31843177 .{ full_path, file.source.len, stat.size },
31853178 );
31863179
3187 try writeBuiltinFile(file, builtin_pkg);
3180 try writeBuiltinFile(file, builtin_mod);
31883181 } else {
31893182 file.stat = .{
31903183 .size = stat.size,
......@@ -3198,7 +3191,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {
31983191 error.PipeBusy => unreachable, // it's not a pipe
31993192 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
32033196 else => |e| return e,
32043197 }
......@@ -3212,8 +3205,8 @@ pub fn populateBuiltinFile(mod: *Module) !void {
32123205 file.status = .success_zir;
32133206}
32143207
3215fn writeBuiltinFile(file: *File, builtin_pkg: *Package) !void {
3216 var af = try builtin_pkg.root_src_directory.handle.atomicFile(builtin_pkg.root_src_path, .{});
3208fn writeBuiltinFile(file: *File, builtin_mod: *Package.Module) !void {
3209 var af = try builtin_mod.root_src_directory.handle.atomicFile(builtin_mod.root_src_path, .{});
32173210 defer af.deinit();
32183211 try af.file.writeAll(file.source);
32193212 try af.finish();
......@@ -3748,7 +3741,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
37483741
37493742 // TODO: figure out how this works under incremental changes to builtin.zig!
37503743 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").?;
37523745 if (decl.getFileScope(mod).pkg != std_mod) break :blk .none;
37533746 // We're in the std module.
37543747 const std_file = (try mod.importPkg(std_mod)).file;
......@@ -4100,13 +4093,13 @@ pub fn importFile(
41004093 import_string: []const u8,
41014094) !ImportFileResult {
41024095 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").?);
41044097 }
41054098 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").?);
41074100 }
41084101 if (std.mem.eql(u8, import_string, "root")) {
4109 return mod.importPkg(mod.root_pkg);
4102 return mod.importPkg(mod.root_mod);
41104103 }
41114104 if (cur_file.pkg.table.get(import_string)) |pkg| {
41124105 return mod.importPkg(pkg);
......@@ -4462,14 +4455,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
44624455 // test decl with no name. Skip the part where we check against
44634456 // the test name filter.
44644457 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;
44664459 try mod.test_functions.put(gpa, new_decl_index, {});
44674460 break :blk true;
44684461 },
44694462 else => blk: {
44704463 if (!is_named_test) break :blk false;
44714464 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;
44734466 if (comp.test_filter) |test_filter| {
44744467 if (mem.indexOf(u8, ip.stringToSlice(decl_name), test_filter) == null) {
44754468 break :blk false;
......@@ -5596,8 +5589,8 @@ pub fn populateTestFunctions(
55965589) !void {
55975590 const gpa = mod.gpa;
55985591 const ip = &mod.intern_pool;
5599 const builtin_pkg = mod.main_pkg.table.get("builtin").?;
5600 const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
5592 const builtin_mod = mod.main_mod.table.get("builtin").?;
5593 const builtin_file = (mod.importPkg(builtin_mod) catch unreachable).file;
56015594 const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?);
56025595 const builtin_namespace = mod.namespacePtr(root_decl.src_namespace);
56035596 const test_functions_str = try ip.getOrPutString(gpa, "test_functions");
src/Package.zig+69-233
......@@ -1,251 +1,87 @@
1const Package = @This();
2
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
1pub const Module = @import("Package/Module.zig");
2pub const Fetch = @import("Package/Fetch.zig");
203pub const build_zig_basename = "build.zig";
214pub 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.* = .{
52 .root_src_directory = .{
53 .path = owned_dir_path,
54 .handle = if (owned_dir_path) |p| try fs.cwd().openDir(p, .{}) else fs.cwd(),
55 },
56 .root_src_path = owned_src_path,
57 .root_src_directory_owned = true,
58 };
6pub const Path = struct {
7 root_dir: Cache.Directory,
8 /// The path, relative to the root dir, that this `Path` represents.
9 /// Empty string means the root_dir is the path.
10 sub_path: []const u8 = "",
5911
60 return ptr;
61}
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 };
12 pub fn cwd() Path {
13 return .{ .root_dir = Cache.Directory.cwd() };
9614 }
97 return ptr;
98}
9915
100/// Free all memory associated with this package. It does not destroy any packages
101/// inside its table; the caller is responsible for calling destroy() on them.
102pub fn destroy(pkg: *Package, gpa: Allocator) void {
103 gpa.free(pkg.root_src_path);
104
105 if (pkg.root_src_directory_owned) {
106 // If root_src_directory.path is null then the handle is the cwd()
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 }
16 pub fn join(p: Path, allocator: Allocator, sub_path: []const u8) Allocator.Error!Path {
17 const parts: []const []const u8 =
18 if (p.sub_path.len == 0) &.{sub_path} else &.{ p.sub_path, sub_path };
19 return .{
20 .root_dir = p.root_dir,
21 .sub_path = try fs.path.join(allocator, parts),
22 };
11223 }
11324
114 pkg.deinitTable(gpa);
115 gpa.destroy(pkg);
116}
117
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);
25 pub fn joinString(p: Path, allocator: Allocator, sub_path: []const u8) Allocator.Error![]u8 {
26 const parts: []const []const u8 =
27 if (p.sub_path.len == 0) &.{sub_path} else &.{ p.sub_path, sub_path };
28 return p.root_dir.join(allocator, parts);
15129 }
15230
153 if (mod.main_pkg != mod.root_pkg) {
154 const new = try node_arena.allocator().create(Queue.Node);
155 // TODO: once #12201 is resolved, we may want a way of indicating a different name for this
156 new.* = .{ .data = .{ .parent = null, .mod = mod.main_pkg } };
157 to_check.prepend(new);
31 pub fn joinStringZ(p: Path, allocator: Allocator, sub_path: []const u8) Allocator.Error![]u8 {
32 const parts: []const []const u8 =
33 if (p.sub_path.len == 0) &.{sub_path} else &.{ p.sub_path, sub_path };
34 return p.root_dir.joinZ(allocator, parts);
15835 }
15936
160 // set of modules we've already checked to prevent loops
161 var checked = std.AutoHashMap(*const Package, void).init(gpa);
162 defer checked.deinit();
163
164 const linked = while (to_check.pop()) |node| {
165 const check = &node.data;
166
167 if (checked.contains(check.mod)) continue;
168 try checked.put(check.mod, {});
169
170 if (check.mod == target) break check;
171
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);
37 pub fn openFile(
38 p: Path,
39 sub_path: []const u8,
40 flags: fs.File.OpenFlags,
41 ) fs.File.OpenError!fs.File {
42 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
43 const joined_path = if (p.sub_path.len == 0) sub_path else p: {
44 break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
45 p.sub_path, sub_path,
46 }) catch return error.NameTooLong;
47 };
48 return p.root_dir.handle.openFile(joined_path, flags);
20149 }
20250
203 // finally, print the names into a buffer!
204 var buf = std.ArrayList(u8).init(gpa);
205 defer buf.deinit();
206 try buf.writer().writeAll("root");
207 var i: usize = names.items.len;
208 while (i > 0) {
209 i -= 1;
210 try buf.writer().print(".{s}", .{names.items[i]});
51 pub fn makeOpenPath(p: Path, sub_path: []const u8, opts: fs.OpenDirOptions) !fs.Dir {
52 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
53 const joined_path = if (p.sub_path.len == 0) sub_path else p: {
54 break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
55 p.sub_path, sub_path,
56 }) catch return error.NameTooLong;
57 };
58 return p.root_dir.handle.makeOpenPath(joined_path, opts);
21159 }
21260
213 return buf.toOwnedSlice();
214}
215
216pub fn createFilePkg(
217 gpa: Allocator,
218 cache_directory: Compilation.Directory,
219 basename: []const u8,
220 contents: []const u8,
221) !*Package {
222 const rand_int = std.crypto.random.int(u64);
223 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ Manifest.hex64(rand_int);
224 {
225 var tmp_dir = try cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{});
226 defer tmp_dir.close();
227 try tmp_dir.writeFile(basename, contents);
61 pub fn format(
62 self: Path,
63 comptime fmt_string: []const u8,
64 options: std.fmt.FormatOptions,
65 writer: anytype,
66 ) !void {
67 _ = options;
68 if (fmt_string.len > 0)
69 std.fmt.invalidFmtError(fmt_string, self);
70 if (self.root_dir.path) |p| {
71 try writer.writeAll(p);
72 try writer.writeAll(fs.path.sep_str);
73 }
74 if (self.sub_path.len > 0) {
75 try writer.writeAll(self.sub_path);
76 try writer.writeAll(fs.path.sep_str);
77 }
22878 }
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,
24779};
248/// This is to avoid creating multiple modules for the same build.zig file.
249/// If the value is `null`, the package is a known dependency, but has not yet
250/// been fetched.
251pub const AllModules = std.AutoHashMapUnmanaged(MultiHashHexDigest, ?DependencyModule);
80
81const Package = @This();
82const builtin = @import("builtin");
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 @@
2727//! All of this must be done with only referring to the state inside this struct
2828//! 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,
3230arena: std.heap.ArenaAllocator,
3331location: Location,
3432location_tok: std.zig.Ast.TokenIndex,
3533hash_tok: std.zig.Ast.TokenIndex,
36global_cache: Cache.Directory,
37parent_package_root: Path,
34parent_package_root: Package.Path,
3835parent_manifest_ast: ?*const std.zig.Ast,
3936prog_node: *std.Progress.Node,
40http_client: *std.http.Client,
41thread_pool: *ThreadPool,
4237job_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
4542// Above this are fields provided as inputs to `run`.
4643// Below this are fields populated by `run`.
4744
4845/// This will either be relative to `global_cache`, or to the build root of
4946/// the root package.
50package_root: Path,
47package_root: Package.Path,
5148error_bundle: std.zig.ErrorBundle.Wip,
5249manifest: ?Manifest,
53manifest_ast: ?*std.zig.Ast,
54actual_hash: Digest,
50manifest_ast: std.zig.Ast,
51actual_hash: Manifest.Digest,
5552/// Fetch logic notices whether a package has a build.zig file and sets this flag.
5653has_build_zig: bool,
5754/// Indicates whether the task aborted due to an out-of-memory condition.
5855oom_flag: bool,
5956
57/// Contains shared state among all `Fetch` tasks.
6058pub const JobQueue = struct {
6159 mutex: std.Thread.Mutex = .{},
62};
63
64pub const Digest = [Manifest.Hash.digest_length]u8;
65pub const MultiHashHexDigest = [hex_multihash_len]u8;
66
67pub const Path = struct {
68 root_dir: Cache.Directory,
69 /// The path, relative to the root dir, that this `Path` represents.
70 /// Empty string means the root_dir is the path.
71 sub_path: []const u8 = "",
60 /// Protected by `mutex`.
61 table: Table = .{},
62 /// `table` may be missing some tasks such as ones that failed, so this
63 /// field contains references to all of them.
64 /// Protected by `mutex`.
65 all_fetches: std.ArrayListUnmanaged(*Fetch) = .{},
66
67 http_client: *std.http.Client,
68 thread_pool: *ThreadPool,
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 }
7288};
7389
7490pub const Location = union(enum) {
7591 remote: Remote,
92 /// A directory found inside the parent package.
7693 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
78103 pub const Remote = struct {
79104 url: []const u8,
80105 /// If this is null it means the user omitted the hash field from a dependency.
81106 /// 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,
83108 };
84109};
85110
......@@ -92,7 +117,11 @@ pub const RunError = error{
92117
93118pub fn run(f: *Fetch) RunError!void {
94119 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
97126 // Check the global zig package cache to see if the hash already exists. If
98127 // so, load, parse, and validate the build.zig.zon file therein, and skip
......@@ -111,43 +140,66 @@ pub fn run(f: *Fetch) RunError!void {
111140 );
112141 f.package_root = try f.parent_package_root.join(arena, sub_path);
113142 try loadManifest(f, f.package_root);
143 if (!f.job_queue.recursive) return;
114144 // Package hashes are used as unique identifiers for packages, so
115145 // we still need one for relative paths.
116 const hash = h: {
146 const digest = h: {
117147 var hasher = Manifest.Hash.init(.{});
118148 // This hash is a tuple of:
119149 // * whether it relative to the global cache directory or to the root package
120150 // * 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))
122152 &package_hash_prefix_cached
123153 else
124154 &package_hash_prefix_project);
125155 hasher.update(f.package_root.sub_path);
126156 break :h hasher.finalResult();
127157 };
128 return queueJobsForDeps(f, hash);
158 return queueJobsForDeps(f, Manifest.hexDigest(digest));
129159 },
130160 .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 },
131183 };
184
132185 const s = fs.path.sep_str;
133186 if (remote.hash) |expected_hash| {
134187 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, .{})) |_| {
136189 f.package_root = .{
137 .root_dir = f.global_cache,
190 .root_dir = cache_root,
138191 .sub_path = pkg_sub_path,
139192 };
140193 try loadManifest(f, f.package_root);
194 if (!f.job_queue.recursive) return;
141195 return queueJobsForDeps(f, expected_hash);
142196 } else |err| switch (err) {
143197 error.FileNotFound => {},
144198 else => |e| {
145199 try eb.addRootErrorMessage(.{
146200 .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),
148202 }),
149 .src_loc = .none,
150 .notes_len = 0,
151203 });
152204 return error.FetchFailed;
153205 },
......@@ -158,22 +210,50 @@ pub fn run(f: *Fetch) RunError!void {
158210
159211 const uri = std.Uri.parse(remote.url) catch |err| return f.fail(
160212 f.location_tok,
161 "invalid URI: {s}",
162 .{@errorName(err)},
213 try eb.printString("invalid URI: {s}", .{@errorName(err)}),
163214 );
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;
164236 const rand_int = std.crypto.random.int(u64);
165237 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});
167240 var tmp_directory: Cache.Directory = .{
168 .path = try f.global_cache.join(arena, &.{tmp_dir_sub_path}),
169 .handle = (try f.global_cache.handle.makeOpenPathIterable(tmp_dir_sub_path, .{})).dir,
241 .path = tmp_directory_path,
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 },
170253 };
171254 defer tmp_directory.handle.close();
172255
173 var resource = try f.initResource(uri);
174 defer resource.deinit(); // releases more than memory
175
176 try f.unpackResource(&resource, uri.path, tmp_directory);
256 try unpackResource(f, resource, uri_path, tmp_directory);
177257
178258 // Load, parse, and validate the unpacked build.zig.zon file. It is allowed
179259 // for the file to be missing, in which case this fetched package is
......@@ -194,15 +274,15 @@ pub fn run(f: *Fetch) RunError!void {
194274 // Compute the package hash based on the remaining files in the temporary
195275 // 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) {
198278 // https://github.com/ziglang/zig/issues/17095
199279 tmp_directory.handle.close();
200 const iterable_dir = f.global_cache.handle.makeOpenPathIterable(tmp_dir_sub_path, .{}) catch
280 const iterable_dir = cache_root.handle.makeOpenPathIterable(tmp_dir_sub_path, .{}) catch
201281 @panic("btrfs workaround failed");
202282 tmp_directory.handle = iterable_dir.dir;
203283 }
204284
205 f.actual_hash = try computeHash(f, .{ .dir = tmp_directory.handle }, filter);
285 f.actual_hash = try computeHash(f, tmp_directory, filter);
206286
207287 // Rename the temporary directory into the global zig package cache
208288 // directory. If the hash already exists, delete the temporary directory
......@@ -211,40 +291,54 @@ pub fn run(f: *Fetch) RunError!void {
211291 // package with the different hash is used in the future.
212292
213293 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
216304 // Validate the computed hash against the expected hash. If invalid, this
217305 // job is done.
218306
219307 const actual_hex = Manifest.hexDigest(f.actual_hash);
220 if (remote.hash) |declared_hash| {
221 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}", .{
223 declared_hash, actual_hex,
224 });
308 if (remote_hash) |declared_hash| {
309 if (!std.mem.eql(u8, &declared_hash, &actual_hex)) {
310 return f.fail(f.hash_tok, try eb.printString(
311 "hash mismatch: manifest declares {s} but the fetched package has {s}",
312 .{ declared_hash, actual_hex },
313 ));
225314 }
226 } else {
315 } else if (!f.omit_missing_hash_error) {
227316 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 });
229322 const notes_start = try eb.reserveNotes(notes_len);
230323 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{
231324 .msg = try eb.printString("expected .hash = \"{s}\",", .{&actual_hex}),
232325 }));
233 return error.PackageFetchFailed;
326 return error.FetchFailed;
234327 }
235328
236329 // Spawn a new fetch job for each dependency in the manifest file. Use
237330 // 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);
239333}
240334
241335/// 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 {
243337 const eb = &f.error_bundle;
244 const arena = f.arena_allocator.allocator();
245 const manifest_bytes = pkg_root.readFileAllocOptions(
338 const arena = f.arena.allocator();
339 const manifest_bytes = pkg_root.root_dir.handle.readFileAllocOptions(
246340 arena,
247 Manifest.basename,
341 try fs.path.join(arena, &.{ pkg_root.sub_path, Manifest.basename }),
248342 Manifest.max_bytes,
249343 null,
250344 1,
......@@ -252,39 +346,39 @@ fn loadManifest(f: *Fetch, pkg_root: Path) RunError!void {
252346 ) catch |err| switch (err) {
253347 error.FileNotFound => return,
254348 else => |e| {
255 const file_path = try pkg_root.join(arena, .{Manifest.basename});
349 const file_path = try pkg_root.join(arena, Manifest.basename);
256350 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}", .{
258352 file_path, @errorName(e),
259353 }),
260 .src_loc = .none,
261 .notes_len = 0,
262354 });
355 return error.FetchFailed;
263356 },
264357 };
265358
266 var ast = try std.zig.Ast.parse(arena, manifest_bytes, .zon);
267 f.manifest_ast = ast;
359 const ast = &f.manifest_ast;
360 ast.* = try std.zig.Ast.parse(arena, manifest_bytes, .zon);
268361
269362 if (ast.errors.len > 0) {
270 const file_path = try pkg_root.join(arena, .{Manifest.basename});
271 try main.putAstErrorsIntoBundle(arena, ast, file_path, eb);
272 return error.PackageFetchFailed;
363 const file_path = try std.fmt.allocPrint(arena, "{}" ++ Manifest.basename, .{pkg_root});
364 try main.putAstErrorsIntoBundle(arena, ast.*, file_path, eb);
365 return error.FetchFailed;
273366 }
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) {
278 const file_path = try pkg_root.join(arena, .{Manifest.basename});
371 if (manifest.errors.len > 0) {
372 const src_path = try eb.printString("{}{s}", .{ pkg_root, Manifest.basename });
279373 const token_starts = ast.tokens.items(.start);
280374
281 for (f.manifest.errors) |msg| {
375 for (manifest.errors) |msg| {
282376 const start_loc = ast.tokenLocation(0, msg.tok);
283377
284378 try eb.addRootErrorMessage(.{
285379 .msg = try eb.addString(msg.msg),
286380 .src_loc = try eb.addSourceLocation(.{
287 .src_path = try eb.addString(file_path),
381 .src_path = src_path,
288382 .span_start = token_starts[msg.tok],
289383 .span_end = @intCast(token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),
290384 .span_main = token_starts[msg.tok] + msg.off,
......@@ -292,71 +386,80 @@ fn loadManifest(f: *Fetch, pkg_root: Path) RunError!void {
292386 .column = @intCast(start_loc.column),
293387 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
294388 }),
295 .notes_len = 0,
296389 });
297390 }
298 return error.PackageFetchFailed;
391 return error.FetchFailed;
299392 }
300393}
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
303398 // If the package does not have a build.zig.zon file then there are no dependencies.
304399 const manifest = f.manifest orelse return;
305400
306401 const new_fetches = nf: {
402 const deps = manifest.dependencies.values();
403 const gpa = f.arena.child_allocator;
307404 // Grab the new tasks into a temporary buffer so we can unlock that mutex
308405 // as fast as possible.
309406 // This overallocates any fetches that get skipped by the `continue` in the
310407 // 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);
312409 var new_fetch_index: usize = 0;
313410
314 f.job_queue.lock();
315 defer f.job_queue.unlock();
411 f.job_queue.mutex.lock();
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
317417 // It is impossible for there to be a collision here. Consider all three cases:
318418 // * Correct hash is provided by manifest.
319419 // - Redundant jobs are skipped in the loop below.
320 // * Incorrect has is provided by manifest.
420 // * Incorrect hash is provided by manifest.
321421 // - Hash mismatch error emitted; `queueJobsForDeps` is not called.
322422 // * Hash is not provided by manifest.
323423 // - 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];
327428 const location: Location = switch (dep.location) {
328429 .url => |url| .{ .remote = .{
329430 .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 },
331440 } },
332441 .path => |path| .{ .relative_path = path },
333442 };
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;
337443 new_fetch_index += 1;
338
444 f.job_queue.all_fetches.appendAssumeCapacity(new_fetch);
339445 new_fetch.* = .{
340 .gpa = f.gpa,
341 .arena = std.heap.ArenaAllocator.init(f.gpa),
446 .arena = std.heap.ArenaAllocator.init(gpa),
342447 .location = location,
343448 .location_tok = dep.location_tok,
344449 .hash_tok = dep.hash_tok,
345 .global_cache = f.global_cache,
346450 .parent_package_root = f.package_root,
347 .parent_manifest_ast = f.manifest_ast.?,
451 .parent_manifest_ast = &f.manifest_ast,
348452 .prog_node = f.prog_node,
349 .http_client = f.http_client,
350 .thread_pool = f.thread_pool,
351453 .job_queue = f.job_queue,
352 .wait_group = f.wait_group,
454 .omit_missing_hash_error = false,
353455
354456 .package_root = undefined,
355 .error_bundle = .{},
457 .error_bundle = undefined,
356458 .manifest = null,
357 .manifest_ast = null,
459 .manifest_ast = undefined,
358460 .actual_hash = undefined,
359461 .has_build_zig = false,
462 .oom_flag = false,
360463 };
361464 }
362465
......@@ -364,12 +467,14 @@ fn queueJobsForDeps(f: *Fetch, hash: Digest) RunError!void {
364467 };
365468
366469 // Now it's time to give tasks to the thread pool.
367 for (new_fetches) |new_fetch| {
368 f.wait_group.start();
369 f.thread_pool.spawn(workerRun, .{f}) catch |err| switch (err) {
470 const thread_pool = f.job_queue.thread_pool;
471
472 for (new_fetches) |*new_fetch| {
473 f.job_queue.wait_group.start();
474 thread_pool.spawn(workerRun, .{new_fetch}) catch |err| switch (err) {
370475 error.OutOfMemory => {
371476 new_fetch.oom_flag = true;
372 f.wait_group.finish();
477 f.job_queue.wait_group.finish();
373478 continue;
374479 },
375480 };
......@@ -377,43 +482,83 @@ fn queueJobsForDeps(f: *Fetch, hash: Digest) RunError!void {
377482}
378483
379484fn workerRun(f: *Fetch) void {
380 defer f.wait_group.finish();
485 defer f.job_queue.wait_group.finish();
381486 run(f) catch |err| switch (err) {
382487 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 },
384492 };
385493}
386494
387fn fail(f: *Fetch, msg_tok: std.zig.Ast.TokenIndex, msg_str: u32) RunError!void {
388 const ast = f.parent_manifest_ast;
389 const token_starts = ast.tokens.items(.start);
390 const start_loc = ast.tokenLocation(0, msg_tok);
495fn srcLoc(
496 f: *Fetch,
497 tok: std.zig.Ast.TokenIndex,
498) Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex {
499 const ast = f.parent_manifest_ast orelse return .none;
391500 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});
393504 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;
395518 try eb.addRootErrorMessage(.{
396519 .msg = msg_str,
397 .src_loc = try eb.addSourceLocation(.{
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,
520 .src_loc = try f.srcLoc(msg_tok),
407521 });
408
409522 return error.FetchFailed;
410523}
411524
412525const Resource = union(enum) {
413526 file: fs.File,
414527 http_request: std.http.Client.Request,
415 git_fetch_stream: git.Session.FetchStream,
528 git: Git,
416529 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 }
417562};
418563
419564const FileType = enum {
......@@ -468,30 +613,52 @@ const FileType = enum {
468613};
469614
470615fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {
471 const gpa = f.gpa;
472 const arena = f.arena_allocator.allocator();
616 const gpa = f.arena.child_allocator;
617 const arena = f.arena.allocator();
473618 const eb = &f.error_bundle;
474619
475620 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 },
477626 };
478627
628 const http_client = f.job_queue.http_client;
629
479630 if (ascii.eqlIgnoreCase(uri.scheme, "http") or
480631 ascii.eqlIgnoreCase(uri.scheme, "https"))
481632 {
482633 var h = std.http.Headers{ .allocator = gpa };
483634 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 };
486642 errdefer req.deinit(); // releases more than memory
487643
488 try req.start(.{});
489 try req.wait();
644 req.start(.{}) catch |err| {
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
491657 if (req.response.status != .ok) {
492 return f.fail(f.location_tok, "expected response status '200 OK' got '{s} {s}'", .{
493 @intFromEnum(req.response.status), req.response.status.phrase() orelse "",
494 });
658 return f.fail(f.location_tok, try eb.printString(
659 "bad HTTP response code: '{d} {s}'",
660 .{ @intFromEnum(req.response.status), req.response.status.phrase() orelse "" },
661 ));
495662 }
496663
497664 return .{ .http_request = req };
......@@ -503,13 +670,21 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {
503670 var transport_uri = uri;
504671 transport_uri.scheme = uri.scheme["git+".len..];
505672 var redirect_uri: []u8 = undefined;
506 var session: git.Session = .{ .transport = f.http_client, .uri = transport_uri };
507 session.discoverCapabilities(gpa, &redirect_uri) catch |e| switch (e) {
673 var session: git.Session = .{ .transport = http_client, .uri = transport_uri };
674 session.discoverCapabilities(gpa, &redirect_uri) catch |err| switch (err) {
508675 error.Redirected => {
509676 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 ));
511687 },
512 else => |other| return other,
513688 };
514689
515690 const want_oid = want_oid: {
......@@ -519,12 +694,22 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {
519694 const want_ref_head = try std.fmt.allocPrint(arena, "refs/heads/{s}", .{want_ref});
520695 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, .{
523698 .ref_prefixes = &.{ want_ref, want_ref_head, want_ref_tag },
524699 .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 };
526706 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| {
528713 if (std.mem.eql(u8, ref.name, want_ref) or
529714 std.mem.eql(u8, ref.name, want_ref_head) or
530715 std.mem.eql(u8, ref.name, want_ref_tag))
......@@ -532,31 +717,46 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource {
532717 break :want_oid ref.peeled orelse ref.oid;
533718 }
534719 }
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}));
536721 };
537722 if (uri.fragment == null) {
538723 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 });
540729 const notes_start = try eb.reserveNotes(notes_len);
541730 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{
542731 .msg = try eb.printString("try .url = \"{+/}#{}\",", .{
543732 uri, std.fmt.fmtSliceHexLower(&want_oid),
544733 }),
545734 }));
546 return error.PackageFetchFailed;
735 return error.FetchFailed;
547736 }
548737
549738 var want_oid_buf: [git.fmt_oid_length]u8 = undefined;
550739 _ = std.fmt.bufPrint(&want_oid_buf, "{}", .{
551740 std.fmt.fmtSliceHexLower(&want_oid),
552741 }) 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 };
554748 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 } };
557754 }
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 ));
560760}
561761
562762fn unpackResource(
......@@ -565,52 +765,62 @@ fn unpackResource(
565765 uri_path: []const u8,
566766 tmp_directory: Cache.Directory,
567767) RunError!void {
768 const eb = &f.error_bundle;
568769 const file_type = switch (resource.*) {
569770 .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
572773 .http_request => |req| ft: {
573774 // Content-Type takes first precedence.
574775 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
577778 if (ascii.eqlIgnoreCase(content_type, "application/x-tar"))
578 return .tar;
779 break :ft .tar;
579780
580781 if (ascii.eqlIgnoreCase(content_type, "application/gzip") or
581782 ascii.eqlIgnoreCase(content_type, "application/x-gzip") or
582783 ascii.eqlIgnoreCase(content_type, "application/tar+gzip"))
583784 {
584 return .@"tar.gz";
785 break :ft .@"tar.gz";
585786 }
586787
587788 if (ascii.eqlIgnoreCase(content_type, "application/x-xz"))
588 return .@"tar.xz";
789 break :ft .@"tar.xz";
589790
590791 if (!ascii.eqlIgnoreCase(content_type, "application/octet-stream")) {
591 return f.fail(f.location_tok, "unrecognized 'Content-Type' header: '{s}'", .{
592 content_type,
593 });
792 return f.fail(f.location_tok, try eb.printString(
793 "unrecognized 'Content-Type' header: '{s}'",
794 .{content_type},
795 ));
594796 }
595797
596798 // Next, the filename from 'content-disposition: attachment' takes precedence.
597799 if (req.response.headers.getFirstValue("Content-Disposition")) |cd_header| {
598 break :ft FileType.fromContentDisposition(cd_header) orelse
599 return f.fail(
600 f.location_tok,
601 "unsupported Content-Disposition header value: '{s}' for Content-Type=application/octet-stream",
602 .{cd_header},
603 );
800 break :ft FileType.fromContentDisposition(cd_header) orelse {
801 return f.fail(f.location_tok, try eb.printString(
802 "unsupported Content-Disposition header value: '{s}' for Content-Type=application/octet-stream",
803 .{cd_header},
804 ));
805 };
604806 }
605807
606808 // Finally, the path from the URI is used.
607 break :ft FileType.fromPath(uri_path) orelse
608 return f.fail(f.location_tok, "unknown file type: '{s}'", .{uri_path});
809 break :ft FileType.fromPath(uri_path) orelse {
810 return f.fail(f.location_tok, try eb.printString(
811 "unknown file type: '{s}'",
812 .{uri_path},
813 ));
814 };
609815 },
610 .git_fetch_stream => return .git_pack,
611 .dir => |dir| {
612 try f.recursiveDirectoryCopy(dir, tmp_directory.handle);
613 return;
816
817 .git => .git_pack,
818
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 ));
614824 },
615825 };
616826
......@@ -618,7 +828,14 @@ fn unpackResource(
618828 .tar => try unpackTarball(f, tmp_directory.handle, resource.reader()),
619829 .@"tar.gz" => try unpackTarballCompressed(f, tmp_directory.handle, resource, std.compress.gzip),
620830 .@"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 },
622839 }
623840}
624841
......@@ -628,11 +845,17 @@ fn unpackTarballCompressed(
628845 resource: *Resource,
629846 comptime Compression: type,
630847) RunError!void {
631 const gpa = f.gpa;
848 const gpa = f.arena.child_allocator;
849 const eb = &f.error_bundle;
632850 const reader = resource.reader();
633851 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 };
636859 defer decompress.deinit();
637860
638861 return unpackTarball(f, out_dir, decompress.reader());
......@@ -640,11 +863,12 @@ fn unpackTarballCompressed(
640863
641864fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
642865 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 };
645869 defer diagnostics.deinit();
646870
647 try std.tar.pipeToFileSystem(out_dir, reader, .{
871 std.tar.pipeToFileSystem(out_dir, reader, .{
648872 .diagnostics = &diagnostics,
649873 .strip_components = 1,
650874 // 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 {
653877 // 2. the hashing algorithm here needs to support detecting the is_executable
654878 // bit on Windows from the ACLs (see the isExecutable function).
655879 .mode_mode = .ignore,
656 .filter = .{ .exclude_empty_directories = true },
657 });
880 .exclude_empty_directories = true,
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
659886 if (diagnostics.errors.items.len > 0) {
660887 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 });
662893 const notes_start = try eb.reserveNotes(notes_len);
663894 for (diagnostics.errors.items, notes_start..) |item, note_i| {
664895 switch (item) {
......@@ -678,19 +909,15 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
678909 },
679910 }
680911 }
681 return error.InvalidTarball;
912 return error.FetchFailed;
682913 }
683914}
684915
685fn unpackGitPack(
686 f: *Fetch,
687 out_dir: fs.Dir,
688 resource: *Resource,
689 want_oid: git.Oid,
690) !void {
916fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!void {
691917 const eb = &f.error_bundle;
692 const gpa = f.gpa;
693 const reader = resource.reader();
918 const gpa = f.arena.child_allocator;
919 const want_oid = resource.git.want_oid;
920 const reader = resource.git.fetch_stream.reader();
694921 // The .git directory is used to store the packfile and associated index, but
695922 // we do not attempt to replicate the exact structure of a real .git
696923 // directory, since that isn't relevant for fetching a package.
......@@ -700,13 +927,13 @@ fn unpackGitPack(
700927 var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true });
701928 defer pack_file.close();
702929 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());
704931 try pack_file.sync();
705932
706933 var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });
707934 defer index_file.close();
708935 {
709 var index_prog_node = reader.prog_node.start("Index pack", 0);
936 var index_prog_node = f.prog_node.start("Index pack", 0);
710937 defer index_prog_node.end();
711938 index_prog_node.activate();
712939 var index_buffered_writer = std.io.bufferedWriter(index_file.writer());
......@@ -716,7 +943,7 @@ fn unpackGitPack(
716943 }
717944
718945 {
719 var checkout_prog_node = reader.prog_node.start("Checkout", 0);
946 var checkout_prog_node = f.prog_node.start("Checkout", 0);
720947 defer checkout_prog_node.end();
721948 checkout_prog_node.activate();
722949 var repository = try git.Repository.init(gpa, pack_file, index_file);
......@@ -727,7 +954,11 @@ fn unpackGitPack(
727954
728955 if (diagnostics.errors.items.len > 0) {
729956 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 });
731962 const notes_start = try eb.reserveNotes(notes_len);
732963 for (diagnostics.errors.items, notes_start..) |item, note_i| {
733964 switch (item) {
......@@ -748,9 +979,10 @@ fn unpackGitPack(
748979 try out_dir.deleteTree(".git");
749980}
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;
752984 // Recursive directory copy.
753 var it = try dir.walk(f.gpa);
985 var it = try dir.walk(gpa);
754986 defer it.deinit();
755987 while (try it.next()) |entry| {
756988 switch (entry.kind) {
......@@ -816,16 +1048,22 @@ pub fn renameTmpIntoCache(
8161048/// the hash are not present on the file system. Empty directories are *not
8171049/// hashed* and must not be present on the file system when calling this
8181050/// 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 {
8201056 // All the path name strings need to be in memory for sorting.
821 const arena = f.arena_allocator.allocator();
822 const gpa = f.gpa;
1057 const arena = f.arena.allocator();
1058 const gpa = f.arena.child_allocator;
1059 const eb = &f.error_bundle;
1060 const thread_pool = f.job_queue.thread_pool;
8231061
8241062 // Collect all files, recursively, then sort.
8251063 var all_files = std.ArrayList(*HashedFile).init(gpa);
8261064 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);
8291067 defer walker.deinit();
8301068
8311069 {
......@@ -834,19 +1072,28 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige
8341072 var wait_group: WaitGroup = .{};
8351073 // `computeHash` is called from a worker thread so there must not be
8361074 // any waiting without working or a deadlock could occur.
837 defer wait_group.waitAndWork();
838
839 while (try walker.next()) |entry| {
1075 defer thread_pool.waitAndWork(&wait_group);
1076
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| {
8401084 _ = filter; // TODO: apply filter rules here
8411085
8421086 const kind: HashedFile.Kind = switch (entry.kind) {
8431087 .directory => continue,
8441088 .file => .file,
8451089 .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 )),
8471094 };
8481095
849 if (std.mem.eql(u8, entry.path, build_zig_basename))
1096 if (std.mem.eql(u8, entry.path, Package.build_zig_basename))
8501097 f.has_build_zig = true;
8511098
8521099 const hashed_file = try arena.create(HashedFile);
......@@ -859,7 +1106,9 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige
8591106 .failure = undefined, // to be populated by the worker
8601107 };
8611108 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
8641113 try all_files.append(hashed_file);
8651114 }
......@@ -869,19 +1118,13 @@ fn computeHash(f: *Fetch, pkg_dir: fs.IterableDir, filter: Filter) RunError!Dige
8691118
8701119 var hasher = Manifest.Hash.init(.{});
8711120 var any_failures = false;
872 const eb = &f.error_bundle;
8731121 for (all_files.items) |hashed_file| {
8741122 hashed_file.failure catch |err| {
8751123 any_failures = true;
8761124 try eb.addRootErrorMessage(.{
877 .msg = try eb.printString("unable to hash: {s}", .{@errorName(err)}),
878 .src_loc = try eb.addSourceLocation(.{
879 .src_path = try eb.addString(hashed_file.fs_path),
880 .span_start = 0,
881 .span_end = 0,
882 .span_main = 0,
1125 .msg = try eb.printString("unable to hash '{s}': {s}", .{
1126 hashed_file.fs_path, @errorName(err),
8831127 }),
884 .notes_len = 0,
8851128 });
8861129 };
8871130 hasher.update(&hashed_file.hash);
......@@ -934,7 +1177,7 @@ fn isExecutable(file: fs.File) !bool {
9341177const HashedFile = struct {
9351178 fs_path: []const u8,
9361179 normalized_path: []const u8,
937 hash: Digest,
1180 hash: Manifest.Digest,
9381181 failure: Error!void,
9391182 kind: Kind,
9401183
......@@ -970,7 +1213,7 @@ fn normalizePath(arena: Allocator, fs_path: []const u8) ![]const u8 {
9701213 return normalized;
9711214}
9721215
973pub const Filter = struct {
1216const Filter = struct {
9741217 include_paths: std.StringArrayHashMapUnmanaged(void) = .{},
9751218
9761219 /// sub_path is relative to the tarball root.
......@@ -990,12 +1233,9 @@ pub const Filter = struct {
9901233 }
9911234};
9921235
993const build_zig_basename = @import("../Package.zig").build_zig_basename;
994const hex_multihash_len = 2 * Manifest.multihash_len;
995
9961236// These are random bytes.
997const 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 };
1237const package_hash_prefix_cached = [8]u8{ 0x53, 0x7e, 0xfa, 0x94, 0x65, 0xe9, 0xf8, 0x73 };
1238const package_hash_prefix_project = [8]u8{ 0xe1, 0x25, 0xee, 0xfa, 0xa6, 0x17, 0x38, 0xcc };
9991239
10001240const builtin = @import("builtin");
10011241const std = @import("std");
......@@ -1010,3 +1250,4 @@ const Manifest = @import("../Manifest.zig");
10101250const Fetch = @This();
10111251const main = @import("../main.zig");
10121252const 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 {
139139
140140var crash_heap: [16 * 4096]u8 = undefined;
141141
142fn writeFilePath(file: *Module.File, stream: anytype) !void {
143 if (file.pkg.root_src_directory.path) |path| {
144 try stream.writeAll(path);
145 try stream.writeAll(std.fs.path.sep_str);
142fn writeFilePath(file: *Module.File, writer: anytype) !void {
143 if (file.mod.root.root_dir.path) |path| {
144 try writer.writeAll(path);
145 try writer.writeAll(std.fs.path.sep_str);
146146 }
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);
148152}
149153
150fn writeFullyQualifiedDeclWithFile(mod: *Module, decl: *Decl, stream: anytype) !void {
151 try writeFilePath(decl.getFileScope(mod), stream);
152 try stream.writeAll(": ");
153 try decl.renderFullyQualifiedDebugName(mod, stream);
154fn writeFullyQualifiedDeclWithFile(mod: *Module, decl: *Decl, writer: anytype) !void {
155 try writeFilePath(decl.getFileScope(mod), writer);
156 try writer.writeAll(": ");
157 try decl.renderFullyQualifiedDebugName(mod, writer);
154158}
155159
156160pub 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 =
416416 \\ dep: [[import=]name]
417417 \\ --deps [dep],[dep],... Set dependency names for the root package
418418 \\ dep: [[import=]name]
419 \\ --main-pkg-path Set the directory of the root package
419 \\ --main-mod-path Set the directory of the root module
420420 \\ -fPIC Force-enable Position Independent Code
421421 \\ -fno-PIC Force-disable Position Independent Code
422422 \\ -fPIE Force-enable Position Independent Executable
......@@ -765,17 +765,11 @@ const Framework = struct {
765765};
766766
767767const CliModule = struct {
768 mod: *Package,
768 mod: *Package.Module,
769769 /// still in CLI arg format
770770 deps_str: []const u8,
771771};
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
779773fn buildOutputType(
780774 gpa: Allocator,
781775 arena: Allocator,
......@@ -950,8 +944,7 @@ fn buildOutputType(
950944 // Contains every module specified via --mod. The dependencies are added
951945 // after argument parsing is completed. We use a StringArrayHashMap to make
952946 // error output consistent.
953 var modules = std.StringArrayHashMap(CliModule).init(gpa);
954 defer cleanupModules(&modules);
947 var modules = std.StringArrayHashMap(CliModule).init(arena);
955948
956949 // The dependency string for the root package
957950 var root_deps_str: ?[]const u8 = null;
......@@ -1023,32 +1016,37 @@ fn buildOutputType(
10231016
10241017 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
10251018 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 });
10271022 }
10281023 }
10291024
10301025 var mod_it = modules.iterator();
10311026 while (mod_it.next()) |kv| {
10321027 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 });
10341031 }
10351032 }
10361033
1037 try modules.ensureUnusedCapacity(1);
1038 modules.put(mod_name, .{
1039 .mod = try Package.create(
1040 gpa,
1041 fs.path.dirname(root_src),
1042 fs.path.basename(root_src),
1043 ),
1034 try modules.put(mod_name, .{
1035 .mod = try Package.Module.create(arena, .{
1036 .root = .{
1037 .root_dir = Cache.Directory.cwd(),
1038 .sub_path = fs.path.dirname(root_src) orelse "",
1039 },
1040 .root_src_path = fs.path.basename(root_src),
1041 }),
10441042 .deps_str = deps_str,
1045 }) catch unreachable;
1043 });
10461044 } else if (mem.eql(u8, arg, "--deps")) {
10471045 if (root_deps_str != null) {
10481046 fatal("only one --deps argument is allowed", .{});
10491047 }
10501048 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")) {
10521050 main_pkg_path = args_iter.nextOrFatal();
10531051 } else if (mem.eql(u8, arg, "-cflags")) {
10541052 extra_cflags.shrinkRetainingCapacity(0);
......@@ -2461,19 +2459,26 @@ fn buildOutputType(
24612459 var deps_it = ModuleDepIterator.init(deps_str);
24622460 while (deps_it.next()) |dep| {
24632461 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 });
24652465 }
24662466
24672467 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
24682468 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 });
24702472 }
24712473 }
24722474
2473 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 });
2475 const dep_mod = modules.get(dep.name) orelse {
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);
24772482 }
24782483 }
24792484 }
......@@ -3229,31 +3234,33 @@ fn buildOutputType(
32293234 };
32303235 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: {
32333238 const src_path = try introspect.resolvePath(arena, unresolved_src_path);
32343239 if (main_pkg_path) |unresolved_main_pkg_path| {
32353240 const p = try introspect.resolvePath(arena, unresolved_main_pkg_path);
3236 if (p.len == 0) {
3237 break :blk try Package.create(gpa, null, src_path);
3238 } else {
3239 const rel_src_path = try fs.path.relative(arena, p, src_path);
3240 break :blk try Package.create(gpa, p, rel_src_path);
3241 }
3241 break :blk try Package.Module.create(arena, .{
3242 .root = .{
3243 .root_dir = Cache.Directory.cwd(),
3244 .sub_path = p,
3245 },
3246 .root_src_path = if (p.len == 0)
3247 src_path
3248 else
3249 try fs.path.relative(arena, p, src_path),
3250 });
32423251 } else {
3243 const root_src_dir_path = fs.path.dirname(src_path);
3244 break :blk Package.create(gpa, root_src_dir_path, fs.path.basename(src_path)) catch |err| {
3245 if (root_src_dir_path) |p| {
3246 fatal("unable to open '{s}': {s}", .{ p, @errorName(err) });
3247 } else {
3248 return err;
3249 }
3250 };
3252 break :blk try Package.Module.create(arena, .{
3253 .root = .{
3254 .root_dir = Cache.Directory.cwd(),
3255 .sub_path = fs.path.dirname(src_path) orelse "",
3256 },
3257 .root_src_path = fs.path.basename(src_path),
3258 });
32513259 }
32523260 } else null;
3253 defer if (main_pkg) |p| p.destroy(gpa);
32543261
32553262 // Transfer packages added with --deps to the root package
3256 if (main_pkg) |mod| {
3263 if (main_mod) |mod| {
32573264 var it = ModuleDepIterator.init(root_deps_str orelse "");
32583265 while (it.next()) |dep| {
32593266 if (dep.expose.len == 0) {
......@@ -3269,7 +3276,7 @@ fn buildOutputType(
32693276 const dep_mod = modules.get(dep.name) orelse
32703277 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);
32733280 }
32743281 }
32753282
......@@ -3310,17 +3317,18 @@ fn buildOutputType(
33103317 if (arg_mode == .run) {
33113318 break :l global_cache_directory;
33123319 }
3313 if (main_pkg) |pkg| {
3320 if (main_mod != null) {
33143321 // search upwards from cwd until we find directory with build.zig
33153322 const cwd_path = try process.getCwdAlloc(arena);
3316 const build_zig = "build.zig";
33173323 const zig_cache = "zig-cache";
33183324 var dirname: []const u8 = cwd_path;
33193325 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 });
33213329 if (fs.cwd().access(joined_path, .{})) |_| {
3322 const cache_dir_path = try fs.path.join(arena, &[_][]const u8{ dirname, zig_cache });
3323 const dir = try pkg.root_src_directory.handle.makeOpenPath(cache_dir_path, .{});
3330 const cache_dir_path = try fs.path.join(arena, &.{ dirname, zig_cache });
3331 const dir = try fs.cwd().makeOpenPath(cache_dir_path, .{});
33243332 cleanup_local_cache_dir = dir;
33253333 break :l .{ .handle = dir, .path = cache_dir_path };
33263334 } else |err| switch (err) {
......@@ -3378,6 +3386,8 @@ fn buildOutputType(
33783386
33793387 gimmeMoreOfThoseSweetSweetFileDescriptors();
33803388
3389 if (true) @panic("TODO restore Compilation logic");
3390
33813391 const comp = Compilation.create(gpa, .{
33823392 .zig_lib_directory = zig_lib_directory,
33833393 .local_cache_directory = local_cache_directory,
......@@ -3389,7 +3399,7 @@ fn buildOutputType(
33893399 .dynamic_linker = target_info.dynamic_linker.get(),
33903400 .sysroot = sysroot,
33913401 .output_mode = output_mode,
3392 .main_pkg = main_pkg,
3402 .main_mod = main_mod,
33933403 .emit_bin = emit_bin_loc,
33943404 .emit_h = emit_h_resolved.data,
33953405 .emit_asm = emit_asm_resolved.data,
......@@ -4799,32 +4809,22 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
47994809 try thread_pool.init(.{ .allocator = gpa });
48004810 defer thread_pool.deinit();
48014811
4802 var cleanup_build_runner_dir: ?fs.Dir = null;
4803 defer if (cleanup_build_runner_dir) |*dir| dir.close();
4804
4805 var main_pkg: Package = if (override_build_runner) |build_runner_path|
4812 var main_mod: Package.Module = if (override_build_runner) |build_runner_path|
48064813 .{
4807 .root_src_directory = blk: {
4808 if (std.fs.path.dirname(build_runner_path)) |dirname| {
4809 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
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() };
4814 .root = .{
4815 .root_dir = Cache.Directory.cwd(),
4816 .sub_path = fs.path.dirname(build_runner_path) orelse "",
48174817 },
4818 .root_src_path = std.fs.path.basename(build_runner_path),
4818 .root_src_path = fs.path.basename(build_runner_path),
48194819 }
48204820 else
48214821 .{
4822 .root_src_directory = zig_lib_directory,
4822 .root = .{ .root_dir = zig_lib_directory },
48234823 .root_src_path = "build_runner.zig",
48244824 };
48254825
4826 var build_pkg: Package = .{
4827 .root_src_directory = build_directory,
4826 var build_mod: Package.Module = .{
4827 .root = .{ .root_dir = build_directory },
48284828 .root_src_path = build_zig_basename,
48294829 };
48304830 if (build_options.only_core_functionality) {
......@@ -4833,11 +4833,13 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
48334833 \\pub const root_deps: []const struct { []const u8, []const u8 } = &.{};
48344834 \\
48354835 );
4836 try main_pkg.add(gpa, "@dependencies", deps_pkg);
4836 try main_mod.deps.put(arena, "@dependencies", deps_pkg);
48374837 } else {
48384838 var http_client: std.http.Client = .{ .allocator = gpa };
48394839 defer http_client.deinit();
48404840
4841 if (true) @panic("TODO restore package fetching logic");
4842
48414843 // Here we provide an import to the build runner that allows using reflection to find
48424844 // all of the dependencies. Without this, there would be no way to use `@import` to
48434845 // 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
48574859
48584860 // Here we borrow main package's table and will replace it with a fresh
48594861 // one after this process completes.
4860 const fetch_result = build_pkg.fetchAndAddDependencies(
4861 &main_pkg,
4862 const fetch_result = build_mod.fetchAndAddDependencies(
4863 &main_mod,
48624864 arena,
48634865 &thread_pool,
48644866 &http_client,
......@@ -4886,10 +4888,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
48864888 dependencies_source.items,
48874889 );
48884890
4889 mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table);
4890 try main_pkg.add(gpa, "@dependencies", deps_pkg);
4891 mem.swap(Package.Table, &main_mod.table, &deps_pkg.table);
4892 try main_mod.add(gpa, "@dependencies", deps_pkg);
48914893 }
4892 try main_pkg.add(gpa, "@build", &build_pkg);
4894 try main_mod.add(gpa, "@build", &build_mod);
48934895
48944896 const comp = Compilation.create(gpa, .{
48954897 .zig_lib_directory = zig_lib_directory,
......@@ -4901,7 +4903,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
49014903 .is_native_abi = cross_target.isNativeAbi(),
49024904 .dynamic_linker = target_info.dynamic_linker.get(),
49034905 .output_mode = .Exe,
4904 .main_pkg = &main_pkg,
4906 .main_mod = &main_mod,
49054907 .emit_bin = emit_bin,
49064908 .emit_h = null,
49074909 .optimize_mode = .Debug,
......@@ -5115,12 +5117,14 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
51155117 .tree = tree,
51165118 .tree_loaded = true,
51175119 .zir = undefined,
5118 .pkg = undefined,
5120 .mod = undefined,
51195121 .root_decl = .none,
51205122 };
51215123
5122 file.pkg = try Package.create(gpa, null, file.sub_file_path);
5123 defer file.pkg.destroy(gpa);
5124 file.mod = try Package.Module.create(arena, .{
5125 .root = Package.Path.cwd(),
5126 .root_src_path = file.sub_file_path,
5127 });
51245128
51255129 file.zir = try AstGen.generate(gpa, file.tree);
51265130 file.zir_loaded = true;
......@@ -5321,12 +5325,14 @@ fn fmtPathFile(
53215325 .tree = tree,
53225326 .tree_loaded = true,
53235327 .zir = undefined,
5324 .pkg = undefined,
5328 .mod = undefined,
53255329 .root_decl = .none,
53265330 };
53275331
5328 file.pkg = try Package.create(gpa, null, file.sub_file_path);
5329 defer file.pkg.destroy(gpa);
5332 file.mod = try Package.Module.create(fmt.arena, .{
5333 .root = Package.Path.cwd(),
5334 .root_src_path = file.sub_file_path,
5335 });
53305336
53315337 if (stat.size > max_src_size)
53325338 return error.FileTooBig;
......@@ -5387,7 +5393,7 @@ pub fn putAstErrorsIntoBundle(
53875393 tree: Ast,
53885394 path: []const u8,
53895395 wip_errors: *std.zig.ErrorBundle.Wip,
5390) !void {
5396) Allocator.Error!void {
53915397 var file: Module.File = .{
53925398 .status = .never_loaded,
53935399 .source_loaded = true,
......@@ -5402,12 +5408,15 @@ pub fn putAstErrorsIntoBundle(
54025408 .tree = tree,
54035409 .tree_loaded = true,
54045410 .zir = undefined,
5405 .pkg = undefined,
5411 .mod = undefined,
54065412 .root_decl = .none,
54075413 };
54085414
5409 file.pkg = try Package.create(gpa, null, path);
5410 defer file.pkg.destroy(gpa);
5415 file.mod = try Package.Module.create(gpa, .{
5416 .root = Package.Path.cwd(),
5417 .root_src_path = file.sub_file_path,
5418 });
5419 defer gpa.destroy(file.mod);
54115420
54125421 file.zir = try AstGen.generate(gpa, file.tree);
54135422 file.zir_loaded = true;
......@@ -5933,7 +5942,7 @@ pub fn cmdAstCheck(
59335942 .stat = undefined,
59345943 .tree = undefined,
59355944 .zir = undefined,
5936 .pkg = undefined,
5945 .mod = undefined,
59375946 .root_decl = .none,
59385947 };
59395948 if (zig_source_file) |file_name| {
......@@ -5971,8 +5980,10 @@ pub fn cmdAstCheck(
59715980 file.stat.size = source.len;
59725981 }
59735982
5974 file.pkg = try Package.create(gpa, null, file.sub_file_path);
5975 defer file.pkg.destroy(gpa);
5983 file.mod = try Package.Module.create(arena, .{
5984 .root = Package.Path.cwd(),
5985 .root_src_path = file.sub_file_path,
5986 });
59765987
59775988 file.tree = try Ast.parse(gpa, file.source, .zig);
59785989 file.tree_loaded = true;
......@@ -6067,7 +6078,7 @@ pub fn cmdDumpZir(
60676078 .stat = undefined,
60686079 .tree = undefined,
60696080 .zir = try Module.loadZirCache(gpa, f),
6070 .pkg = undefined,
6081 .mod = undefined,
60716082 .root_decl = .none,
60726083 };
60736084
......@@ -6136,12 +6147,14 @@ pub fn cmdChangelist(
61366147 },
61376148 .tree = undefined,
61386149 .zir = undefined,
6139 .pkg = undefined,
6150 .mod = undefined,
61406151 .root_decl = .none,
61416152 };
61426153
6143 file.pkg = try Package.create(gpa, null, file.sub_file_path);
6144 defer file.pkg.destroy(gpa);
6154 file.mod = try Package.Module.create(arena, .{
6155 .root = Package.Path.cwd(),
6156 .root_src_path = file.sub_file_path,
6157 });
61456158
61466159 const source = try arena.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
61476160 const amt = try f.readAll(source);
......@@ -6623,8 +6636,11 @@ fn cmdFetch(
66236636 args: []const []const u8,
66246637) !void {
66256638 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;
66276642 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");
6643 var recursive = false;
66286644
66296645 {
66306646 var i: usize = 0;
......@@ -6640,18 +6656,21 @@ fn cmdFetch(
66406656 i += 1;
66416657 override_global_cache_dir = args[i];
66426658 continue;
6659 } else if (mem.eql(u8, arg, "--recursive")) {
6660 recursive = true;
6661 continue;
66436662 } else {
66446663 fatal("unrecognized parameter: '{s}'", .{arg});
66456664 }
6646 } else if (opt_url != null) {
6665 } else if (opt_path_or_url != null) {
66476666 fatal("unexpected extra parameter: '{s}'", .{arg});
66486667 } else {
6649 opt_url = arg;
6668 opt_path_or_url = arg;
66506669 }
66516670 }
66526671 }
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
66566675 var thread_pool: ThreadPool = undefined;
66576676 try thread_pool.init(.{ .allocator = gpa });
......@@ -6664,19 +6683,6 @@ fn cmdFetch(
66646683 const root_prog_node = progress.start("Fetch", 0);
66656684 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
66806686 var global_cache_directory: Compilation.Directory = l: {
66816687 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
66826688 break :l .{
......@@ -6686,56 +6692,48 @@ fn cmdFetch(
66866692 };
66876693 defer global_cache_directory.handle.close();
66886694
6689 var readable_resource: Package.ReadableResource = rr: {
6690 if (fs.cwd().openIterableDir(url, .{})) |dir| {
6691 break :rr .{
6692 .path = try gpa.dupe(u8, url),
6693 .resource = .{ .dir = dir },
6694 };
6695 } else |dir_err| {
6696 const file_err = if (dir_err == error.NotDir) e: {
6697 if (fs.cwd().openFile(url, .{})) |f| {
6698 break :rr .{
6699 .path = try gpa.dupe(u8, url),
6700 .resource = .{ .file = f },
6701 };
6702 } else |err| break :e err;
6703 } else dir_err;
6704
6705 const uri = std.Uri.parse(url) catch |uri_err| {
6706 fatal("'{s}' could not be recognized as a file path ({s}) or an URL ({s})", .{
6707 url, @errorName(file_err), @errorName(uri_err),
6708 });
6709 };
6710 const fetch_location = try Package.FetchLocation.initUri(uri, 0, report);
6711 const cwd: Cache.Directory = .{
6712 .handle = fs.cwd(),
6713 .path = null,
6714 };
6715 break :rr try fetch_location.fetch(gpa, cwd, &http_client, 0, report);
6716 }
6695 var job_queue: Package.Fetch.JobQueue = .{
6696 .http_client = &http_client,
6697 .thread_pool = &thread_pool,
6698 .global_cache = global_cache_directory,
6699 .recursive = recursive,
6700 .work_around_btrfs_bug = work_around_btrfs_bug,
6701 };
6702 defer job_queue.deinit();
6703
6704 var fetch: Package.Fetch = .{
6705 .arena = std.heap.ArenaAllocator.init(gpa),
6706 .location = .{ .path_or_url = path_or_url },
6707 .location_tok = 0,
6708 .hash_tok = 0,
6709 .parent_package_root = undefined,
6710 .parent_manifest_ast = null,
6711 .prog_node = root_prog_node,
6712 .job_queue = &job_queue,
6713 .omit_missing_hash_error = true,
6714
6715 .package_root = undefined,
6716 .error_bundle = undefined,
6717 .manifest = null,
6718 .manifest_ast = undefined,
6719 .actual_hash = undefined,
6720 .has_build_zig = false,
6721 .oom_flag = false,
67176722 };
6718 defer readable_resource.deinit(gpa);
6723 defer fetch.deinit();
67196724
6720 var package_location = readable_resource.unpack(
6721 gpa,
6722 &thread_pool,
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) });
6725 fetch.run() catch |err| switch (err) {
6726 error.OutOfMemory => fatal("out of memory", .{}),
6727 error.FetchFailed => {}, // error bundle checked below
67356728 };
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
67406738 progress.done = true;
67416739 progress.refresh();