1const std = @import("std");
2const string = []const u8;
3const builtin = @import("builtin");
4const yaml = @import("yaml");
5const extras = @import("extras");
6const nio = @import("nio");
7const nfs = @import("nfs");
8
9const zigmod = @import("../lib.zig");
10const u = @import("funcs.zig");
11const common = @import("./../common.zig");
12
13//
14//
15
16pub const Module = struct {
17 type: zigmod.Dep.Type,
18 id: [48]u8,
19 name: string,
20 main: string,
21 c_include_dirs: []const string = &.{},
22 c_source_flags: []const string = &.{},
23 c_source_files: []const string = &.{},
24 only_os: []const string = &.{},
25 except_os: []const string = &.{},
26 yaml: ?yaml.Mapping,
27 deps: []Module,
28 clean_path: [:0]const u8,
29 dep: ?zigmod.Dep,
30 for_build: bool = false,
31 min_zig_version: ?std.SemanticVersion,
32
33 pub const ROOT: [48]u8 = ("root" ++ (" " ** 44)).*;
34
35 pub fn from(alloc: std.mem.Allocator, dep: zigmod.Dep, cachepath: [:0]const u8, options: *common.CollectOptions) !Module {
36 var moddeps = std.array_list.Managed(Module).init(alloc);
37 errdefer moddeps.deinit();
38
39 for (dep.deps) |*d| {
40 if (try common.get_module_from_dep(d, cachepath, options)) |founddep| {
41 try moddeps.append(founddep);
42 }
43 }
44
45 var id = dep.id;
46 if (std.mem.eql(u8, &id, &zigmod.Dep.EMPTY)) id = u.random_string(48);
47
48 return Module{
49 .type = dep.type,
50 .id = id,
51 .name = dep.name,
52 .main = dep.main,
53 .c_include_dirs = dep.c_include_dirs,
54 .c_source_flags = dep.c_source_flags,
55 .c_source_files = dep.c_source_files,
56 .deps = try moddeps.toOwnedSlice(),
57 .clean_path = try dep.clean_path(alloc),
58 .only_os = dep.only_os,
59 .except_os = dep.except_os,
60 .yaml = dep.yaml,
61 .dep = dep,
62 .for_build = dep.for_build,
63 .min_zig_version = null,
64 };
65 }
66
67 pub fn eql(self: Module, another: Module) bool {
68 return std.mem.eql(u8, &self.id, &another.id);
69 }
70
71 pub fn get_hash(self: Module, alloc: std.mem.Allocator, cdpath: string) !string {
72 const file_list_1 = try u.file_list(alloc, try std.mem.concatWithSentinel(alloc, u8, &.{ cdpath, "/", self.clean_path }, 0));
73
74 var file_list_2 = std.array_list.Managed(string).init(alloc);
75 errdefer file_list_2.deinit();
76 for (file_list_1) |item| {
77 const _a = extras.trimPrefix(item, cdpath);
78 const _b = extras.trimPrefix(_a, self.clean_path);
79 if (_b[0] == '.') continue;
80 try file_list_2.append(_b);
81 }
82
83 std.mem.sort(string, file_list_2.items, void{}, struct {
84 pub fn lt(context: void, lhs: string, rhs: string) bool {
85 _ = context;
86 return std.mem.lessThan(u8, lhs, rhs);
87 }
88 }.lt);
89
90 var h = std.crypto.hash.Blake3.init(.{});
91 for (file_list_2.items) |item| {
92 const abs_path = try std.fs.path.joinZ(alloc, &.{ cdpath, self.clean_path, item });
93 const file = try nfs.cwd().openFile(abs_path, .{});
94 defer file.close();
95 const input = try file.readAllAlloc(alloc, u.mb * 100);
96 h.update(input);
97 }
98 var out: [32]u8 = undefined;
99 h.final(&out);
100 const hex = try nio.fmt.allocPrint(alloc, "blake3-{s}", .{&extras.to_hex(out)});
101 return hex;
102 }
103
104 pub fn is_for_this(self: Module) bool {
105 const os = @tagName(builtin.os.tag);
106 if (self.only_os.len > 0) {
107 return extras.containsString(self.only_os, os);
108 }
109 if (self.except_os.len > 0) {
110 return !extras.containsString(self.except_os, os);
111 }
112 return true;
113 }
114
115 pub fn has_no_zig_deps(self: Module) bool {
116 for (self.deps) |d| {
117 if (d.main.len > 0) {
118 return false;
119 }
120 }
121 return true;
122 }
123
124 pub fn has_syslib_deps(self: Module) bool {
125 for (self.deps) |d| {
126 if (d.type == .system_lib) {
127 return true;
128 }
129 }
130 return false;
131 }
132
133 pub fn has_framework_deps(self: Module) bool {
134 for (self.deps) |d| {
135 if (d.type == .framework) {
136 return true;
137 }
138 }
139 return false;
140 }
141
142 pub fn short_id(self: *const Module) string {
143 return u.slice(u8, &self.id, 0, @min(12, std.mem.indexOfScalar(u8, &self.id, ' ') orelse self.id.len));
144 }
145
146 pub fn minZigVersion(self: Module) ?std.SemanticVersion {
147 var res = self.min_zig_version;
148
149 for (self.deps) |dm| {
150 if (dm.minZigVersion()) |sv| {
151 if (res == null or sv.order(res.?).compare(.gt)) {
152 res = sv;
153 }
154 }
155 }
156 return res;
157 }
158
159 pub fn lessThan(_: void, lhs: Module, rhs: Module) bool {
160 for (lhs.clean_path, 0..) |_, i| {
161 if (i == rhs.clean_path.len) return false;
162 if (lhs.clean_path[i] < rhs.clean_path[i]) return true;
163 if (lhs.clean_path[i] > rhs.clean_path[i]) return false;
164 }
165 return false;
166 }
167
168 pub fn pin(self: Module, alloc: std.mem.Allocator, cachepath: [:0]const u8, options: *common.CollectOptions) !string {
169 return switch (self.type) {
170 .local => "",
171 .system_lib => "",
172 .framework => "",
173 else => |sub| {
174 var cdir = try nfs.cwd().openDir(cachepath, .{});
175 defer cdir.close();
176 var mdir = try cdir.openDir(self.clean_path, .{});
177 defer mdir.close();
178 return switch (sub) {
179 .local, .system_lib, .framework => unreachable,
180 .git => {
181 if (options.lock == null) {
182 return u.git_rev_HEAD(alloc, mdir) catch |err| switch (err) {
183 error.ENOENT, error.NotAGitRepo => {
184 const cpath = extras.trimPrefixEnsure(self.clean_path, "v/") orelse return err;
185 var iter = std.mem.splitScalar(u8, cpath, '/');
186 while (iter.next()) |segment| {
187 if (iter.peek() == null) return extras.trimPrefixEnsure(segment, "commit-") orelse return err;
188 }
189 unreachable;
190 },
191 else => |e| e,
192 };
193 }
194 for (options.lock.?) |item| {
195 if (!std.mem.eql(u8, item[1], "git")) continue;
196 if (!std.mem.eql(u8, item[2], self.dep.?.path)) continue;
197 return item[3][std.mem.indexOfScalar(u8, item[3], '-').? + 1 ..];
198 }
199 @panic("unreachable");
200 },
201 .hg => @panic("TODO"),
202 .http => @panic("TODO"),
203 };
204 },
205 };
206 }
207};