1const Package = @This(); 2 3const std = @import("std"); 4const fs = std.fs; 5const mem = std.mem; 6const Allocator = mem.Allocator; 7const assert = std.debug.assert; 8 9const Compilation = @import("Compilation.zig"); 10const Module = @import("Module.zig"); 11 12pub const Table = std.StringHashMapUnmanaged(*Package); 13 14root_src_directory: Compilation.Directory, 15/// Relative to `root_src_directory`. May contain path separators. 16root_src_path: []const u8, 17table: Table = .{}, 18parent: ?*Package = null, 19/// Whether to free `root_src_directory` on `destroy`. 20root_src_directory_owned: bool = false, 21 22/// Allocate a Package. No references to the slices passed are kept. 23pub fn create( 24 gpa: Allocator, 25 /// Null indicates the current working directory 26 root_src_dir_path: ?[]const u8, 27 /// Relative to root_src_dir_path 28 root_src_path: []const u8, 29) !*Package { 30 const ptr = try gpa.create(Package); 31 errdefer gpa.destroy(ptr); 32 33 const owned_dir_path = if (root_src_dir_path) |p| try gpa.dupe(u8, p) else null; 34 errdefer if (owned_dir_path) |p| gpa.free(p); 35 36 const owned_src_path = try gpa.dupe(u8, root_src_path); 37 errdefer gpa.free(owned_src_path); 38 39 ptr.* = .{ 40 .root_src_directory = .{ 41 .path = owned_dir_path, 42 .handle = if (owned_dir_path) |p| try fs.cwd().openDir(p, .{}) else fs.cwd(), 43 }, 44 .root_src_path = owned_src_path, 45 .root_src_directory_owned = true, 46 }; 47 48 return ptr; 49} 50 51pub fn createWithDir( 52 gpa: Allocator, 53 directory: Compilation.Directory, 54 /// Relative to `directory`. If null, means `directory` is the root src dir 55 /// and is owned externally. 56 root_src_dir_path: ?[]const u8, 57 /// Relative to root_src_dir_path 58 root_src_path: []const u8, 59) !*Package { 60 const ptr = try gpa.create(Package); 61 errdefer gpa.destroy(ptr); 62 63 const owned_src_path = try gpa.dupe(u8, root_src_path); 64 errdefer gpa.free(owned_src_path); 65 66 if (root_src_dir_path) |p| { 67 const owned_dir_path = try directory.join(gpa, &[1][]const u8{p}); 68 errdefer gpa.free(owned_dir_path); 69 70 ptr.* = .{ 71 .root_src_directory = .{ 72 .path = owned_dir_path, 73 .handle = try directory.handle.openDir(p, .{}), 74 }, 75 .root_src_directory_owned = true, 76 .root_src_path = owned_src_path, 77 }; 78 } else { 79 ptr.* = .{ 80 .root_src_directory = directory, 81 .root_src_directory_owned = false, 82 .root_src_path = owned_src_path, 83 }; 84 } 85 return ptr; 86} 87 88/// Free all memory associated with this package. It does not destroy any packages 89/// inside its table; the caller is responsible for calling destroy() on them. 90pub fn destroy(pkg: *Package, gpa: Allocator) void { 91 gpa.free(pkg.root_src_path); 92 93 if (pkg.root_src_directory_owned) { 94 // If root_src_directory.path is null then the handle is the cwd() 95 // which shouldn't be closed. 96 if (pkg.root_src_directory.path) |p| { 97 gpa.free(p); 98 pkg.root_src_directory.handle.close(); 99 } 100 } 101 102 pkg.deinitTable(gpa); 103 gpa.destroy(pkg); 104} 105 106/// Only frees memory associated with the table. 107pub fn deinitTable(pkg: *Package, gpa: Allocator) void { 108 var it = pkg.table.keyIterator(); 109 while (it.next()) |key| { 110 gpa.free(key.*); 111 } 112 113 pkg.table.deinit(gpa); 114} 115 116pub fn add(pkg: *Package, gpa: Allocator, name: []const u8, package: *Package) !void { 117 try pkg.table.ensureUnusedCapacity(gpa, 1); 118 const name_dupe = try gpa.dupe(u8, name); 119 pkg.table.putAssumeCapacityNoClobber(name_dupe, package); 120} 121 122pub fn addAndAdopt(parent: *Package, gpa: Allocator, name: []const u8, child: *Package) !void { 123 assert(child.parent == null); // make up your mind, who is the parent?? 124 child.parent = parent; 125 return parent.add(gpa, name, child); 126} 127