| ... | @@ -0,0 +1,421 @@ |
| 1 | // Adapted from https://github.com/nektro/zigmod/blob/r95/src/cmd/fetch.zig |
| 2 | |
| 3 | const std = @import("std"); |
| 4 | const string = []const u8; |
| 5 | const ansi = @import("ansi"); |
| 6 | const extras = @import("extras"); |
| 7 | const nfs = @import("nfs"); |
| 8 | |
| 9 | const zigmod = @import("../lib.zig"); |
| 10 | const u = @import("./../util/funcs.zig"); |
| 11 | const common = @import("./../common.zig"); |
| 12 | const license = @import("./license.zig"); |
| 13 | |
| 14 | pub fn create_depszig(alloc: std.mem.Allocator, cachepath: string, dir: nfs.Dir, top_module: zigmod.Module, list: *std.array_list.Managed(zigmod.Module)) !void { |
| 15 | const f = try dir.createFile("deps.zig", .{}); |
| 16 | defer f.close(); |
| 17 | |
| 18 | const w = f; |
| 19 | try w.writeAll("// zig fmt: off\n"); |
| 20 | try w.writeAll("const std = @import(\"std\");\n"); |
| 21 | try w.writeAll("const builtin = @import(\"builtin\");\n"); |
| 22 | try w.writeAll("const string = []const u8;\n"); |
| 23 | try w.writeAll("\n"); |
| 24 | try w.print("pub const cache = \"{}\";\n", .{u.altStringEscape(cachepath)}); |
| 25 | try w.writeAll("\n"); |
| 26 | try w.writeAll( |
| 27 | \\pub fn addAllTo(exe: *std.Build.Step.Compile) void { |
| 28 | \\ checkMinZig(builtin.zig_version, exe); |
| 29 | \\ @setEvalBranchQuota(1_000_000); |
| 30 | \\ for (packages) |pkg| { |
| 31 | \\ const module = pkg.module(exe); |
| 32 | \\ exe.root_module.addImport(pkg.import.?[0], module); |
| 33 | \\ } |
| 34 | \\ for (package_data._root.system_libs) |libname| { |
| 35 | \\ exe.linkSystemLibrary(libname); |
| 36 | \\ exe.linkLibC(); |
| 37 | \\ } |
| 38 | \\ // clear module memo cache so addAllTo can be called more than once in the same build.zig |
| 39 | \\ inline for (comptime std.meta.declarations(package_data)) |decl| @field(package_data, decl.name).module_memo = null; |
| 40 | \\} |
| 41 | \\ |
| 42 | \\var link_lib_c = false; |
| 43 | \\pub const Package = struct { |
| 44 | \\ directory: string, |
| 45 | \\ import: ?struct { string, std.Build.LazyPath } = null, |
| 46 | \\ dependencies: []const *Package, |
| 47 | \\ c_include_dirs: []const string = &.{}, |
| 48 | \\ c_source_files: []const string = &.{}, |
| 49 | \\ c_source_flags: []const string = &.{}, |
| 50 | \\ system_libs: []const string = &.{}, |
| 51 | \\ frameworks: []const string = &.{}, |
| 52 | \\ module_memo: ?*std.Build.Module = null, |
| 53 | \\ |
| 54 | \\ pub fn module(self: *Package, exe: *std.Build.Step.Compile) *std.Build.Module { |
| 55 | \\ if (self.module_memo) |cached| { |
| 56 | \\ return cached; |
| 57 | \\ } |
| 58 | \\ const b = exe.step.owner; |
| 59 | \\ const result = b.createModule(.{ |
| 60 | \\ .target = exe.root_module.resolved_target orelse b.host, |
| 61 | \\ }); |
| 62 | \\ if (self.import) |capture| { |
| 63 | \\ result.root_source_file = capture[1]; |
| 64 | \\ } |
| 65 | \\ for (self.dependencies) |item| { |
| 66 | \\ const module_dep = item.module(exe); |
| 67 | \\ if (module_dep.root_source_file != null) { |
| 68 | \\ result.addImport(item.import.?[0], module_dep); |
| 69 | \\ } |
| 70 | \\ for (module_dep.include_dirs.items) |jtem| { |
| 71 | \\ switch (jtem) { |
| 72 | \\ .path => result.addIncludePath(jtem.path), |
| 73 | \\ .path_system, .path_after, .framework_path, .framework_path_system, .other_step, .config_header_step => {}, |
| 74 | \\ } |
| 75 | \\ } |
| 76 | \\ } |
| 77 | \\ for (self.c_include_dirs) |item| { |
| 78 | \\ result.addIncludePath(.{ .cwd_relative = (b.fmt("{s}/{s}", .{ self.directory, item })) }); |
| 79 | \\ exe.addIncludePath(.{ .cwd_relative = (b.fmt("{s}/{s}", .{ self.directory, item })) }); |
| 80 | \\ link_lib_c = true; |
| 81 | \\ } |
| 82 | \\ for (self.c_source_files) |item| { |
| 83 | \\ exe.addCSourceFile(.{ .file = .{ .cwd_relative = (b.fmt("{s}/{s}", .{ self.directory, item })) }, .flags = self.c_source_flags }); |
| 84 | \\ link_lib_c = true; |
| 85 | \\ } |
| 86 | \\ for (self.system_libs) |item| { |
| 87 | \\ result.linkSystemLibrary(item, .{}); |
| 88 | \\ exe.linkSystemLibrary(item); |
| 89 | \\ link_lib_c = true; |
| 90 | \\ } |
| 91 | \\ for (self.frameworks) |item| { |
| 92 | \\ result.linkFramework(item, .{}); |
| 93 | \\ exe.linkFramework(item); |
| 94 | \\ link_lib_c = true; |
| 95 | \\ } |
| 96 | \\ if (link_lib_c) { |
| 97 | \\ result.link_libc = true; |
| 98 | \\ exe.linkLibC(); |
| 99 | \\ } |
| 100 | \\ self.module_memo = result; |
| 101 | \\ return result; |
| 102 | \\ } |
| 103 | \\}; |
| 104 | \\ |
| 105 | \\ |
| 106 | ); |
| 107 | |
| 108 | try w.print( |
| 109 | \\fn checkMinZig(current: std.SemanticVersion, exe: *std.Build.Step.Compile) void {{ |
| 110 | \\ const min = std.SemanticVersion.parse("{?}") catch return; |
| 111 | \\ if (current.order(min).compare(.lt)) @panic(exe.step.owner.fmt("Your Zig version v{{}} does not meet the minimum build requirement of v{{}}", .{{current, min}})); |
| 112 | \\}} |
| 113 | \\ |
| 114 | \\ |
| 115 | , .{if (top_module.minZigVersion()) |sv| u.altSemanticVersion(sv) else null}); |
| 116 | |
| 117 | try w.writeAll("pub const dirs = struct {\n"); |
| 118 | try print_dirs(w, list.items, alloc); |
| 119 | try w.writeAll("};\n\n"); |
| 120 | |
| 121 | try w.writeAll("pub const package_data = struct {\n"); |
| 122 | var duped = std.array_list.Managed(zigmod.Module).init(alloc); |
| 123 | var done = std.array_list.Managed(zigmod.Module).init(alloc); |
| 124 | for (list.items) |mod| { |
| 125 | if (mod.type == .system_lib or mod.type == .framework) { |
| 126 | continue; |
| 127 | } |
| 128 | try duped.append(mod); |
| 129 | } |
| 130 | try print_pkg_data_to(w, &duped, &done); |
| 131 | try w.writeAll("};\n\n"); |
| 132 | |
| 133 | try w.writeAll("pub const packages = "); |
| 134 | try print_deps(w, top_module); |
| 135 | try w.writeAll(";\n\n"); |
| 136 | |
| 137 | try w.writeAll("pub const pkgs = "); |
| 138 | try print_pkgs(alloc, w, top_module); |
| 139 | try w.writeAll(";\n\n"); |
| 140 | |
| 141 | try w.writeAll("pub const imports = struct {\n"); |
| 142 | try print_imports(alloc, w, top_module, cachepath); |
| 143 | try w.writeAll("};\n"); |
| 144 | } |
| 145 | |
| 146 | fn create_lockfile(alloc: std.mem.Allocator, list: *std.array_list.Managed(zigmod.Module), path: string, dir: nfs.Dir) !void { |
| 147 | const fl = try dir.createFile("zigmod.lock", .{}); |
| 148 | defer fl.close(); |
| 149 | |
| 150 | std.mem.sort(zigmod.Module, list.items, {}, zigmod.Module.lessThan); |
| 151 | |
| 152 | const wl = fl.writer(); |
| 153 | try wl.writeAll("2\n"); |
| 154 | for (list.items) |m| { |
| 155 | if (m.dep) |md| { |
| 156 | if (md.type.isLocal()) continue; |
| 157 | const mpath = try std.fs.path.join(alloc, &.{ path, m.clean_path }); |
| 158 | const version = try md.exact_version(alloc, mpath); |
| 159 | try wl.print("{s} {s} {s}\n", .{ @tagName(md.type), md.path, version }); |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | const DiffChange = struct { |
| 165 | from: string, |
| 166 | to: string, |
| 167 | }; |
| 168 | |
| 169 | fn diff_lockfile(alloc: std.mem.Allocator) !void { |
| 170 | const max = std.math.maxInt(usize); |
| 171 | |
| 172 | if (try extras.doesFolderExist(null, ".git")) { |
| 173 | const result = try u.run_cmd_raw(alloc, null, &.{ "git", "diff", "zigmod.lock" }); |
| 174 | var stdout = std.io.fixedBufferStream(result.stdout); |
| 175 | const r = stdout.reader(); |
| 176 | while (try r.readUntilDelimiterOrEofAlloc(alloc, '\n', max)) |line| { |
| 177 | if (std.mem.startsWith(u8, line, "@@")) break; |
| 178 | } |
| 179 | |
| 180 | var rems = std.array_list.Managed(string).init(alloc); |
| 181 | var adds = std.array_list.Managed(string).init(alloc); |
| 182 | while (try r.readUntilDelimiterOrEofAlloc(alloc, '\n', max)) |line| { |
| 183 | if (line[0] == ' ') continue; |
| 184 | if (line[0] == '-') try rems.append(line[1..]); |
| 185 | if (line[0] == '+') if (line[1] == '2') break else try adds.append(line[1..]); |
| 186 | } |
| 187 | |
| 188 | var changes = std.StringHashMap(DiffChange).init(alloc); |
| 189 | |
| 190 | var didbreak = false; |
| 191 | var i: usize = 0; |
| 192 | while (i < rems.items.len) { |
| 193 | const it = rems.items[i]; |
| 194 | const sni = u.indexOfN(it, ' ', 2).?; |
| 195 | |
| 196 | var j: usize = 0; |
| 197 | while (j < adds.items.len) { |
| 198 | const jt = adds.items[j]; |
| 199 | const snj = u.indexOfN(jt, ' ', 2).?; |
| 200 | |
| 201 | if (std.mem.eql(u8, it[0..sni], jt[0..snj])) { |
| 202 | try changes.put(it[0..sni], .{ |
| 203 | .from = it[u.indexOfAfter(it, '-', sni).? + 1 .. it.len], |
| 204 | .to = jt[u.indexOfAfter(jt, '-', snj).? + 1 .. jt.len], |
| 205 | }); |
| 206 | _ = rems.orderedRemove(i); |
| 207 | _ = adds.orderedRemove(j); |
| 208 | didbreak = true; |
| 209 | break; |
| 210 | } |
| 211 | if (!didbreak) j += 1; |
| 212 | } |
| 213 | if (!didbreak) i += 1; |
| 214 | if (didbreak) didbreak = false; |
| 215 | } |
| 216 | |
| 217 | if (adds.items.len > 0) { |
| 218 | std.debug.print(comptime ansi.color.Faint("Newly added packages:\n"), .{}); |
| 219 | defer std.debug.print("\n", .{}); |
| 220 | |
| 221 | for (adds.items) |it| { |
| 222 | std.debug.print("- {s}\n", .{it}); |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | if (rems.items.len > 0) { |
| 227 | std.debug.print(comptime ansi.color.Faint("Removed packages:\n"), .{}); |
| 228 | defer std.debug.print("\n", .{}); |
| 229 | |
| 230 | for (rems.items) |it| { |
| 231 | std.debug.print("- {s}\n", .{it}); |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | if (changes.unmanaged.size > 0) std.debug.print(comptime ansi.color.Faint("Updated packages:\n"), .{}); |
| 236 | var iter = changes.iterator(); |
| 237 | while (iter.next()) |it| { |
| 238 | if (diff_printchange("git https://github.com", "- {s}/compare/{s}...{s}\n", it)) continue; |
| 239 | if (diff_printchange("git https://gitlab.com", "- {s}/-/compare/{s}...{s}\n", it)) continue; |
| 240 | if (diff_printchange("git https://gitea.com", "- {s}/compare/{s}...{s}\n", it)) continue; |
| 241 | |
| 242 | std.debug.print("- {s}\n", .{it.key_ptr.*}); |
| 243 | std.debug.print(" - {s} ... {s}\n", .{ it.value_ptr.from, it.value_ptr.to }); |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | fn diff_printchange(comptime testt: string, comptime replacement: string, item: std.StringHashMap(DiffChange).Entry) bool { |
| 249 | if (std.mem.startsWith(u8, item.key_ptr.*, testt)) { |
| 250 | if (std.mem.eql(u8, item.value_ptr.from, item.value_ptr.to)) return true; |
| 251 | std.debug.print(replacement, .{ item.key_ptr.*[4..], item.value_ptr.from, item.value_ptr.to }); |
| 252 | return true; |
| 253 | } |
| 254 | return false; |
| 255 | } |
| 256 | |
| 257 | fn print_dirs(w: nfs.File, list: []const zigmod.Module, alloc: std.mem.Allocator) !void { |
| 258 | for (list) |mod| { |
| 259 | if (mod.type == .system_lib or mod.type == .framework) continue; |
| 260 | if (std.mem.eql(u8, &mod.id, &zigmod.Module.ROOT)) { |
| 261 | try w.writeAll(" pub const _root = \"\";\n"); |
| 262 | continue; |
| 263 | } |
| 264 | if (std.mem.eql(u8, mod.clean_path, "../..")) { |
| 265 | const cwd_realpath = try std.fs.cwd().realpathAlloc(alloc, "."); |
| 266 | try w.print(" pub const _{s} = \"{}\";\n", .{ mod.short_id(), u.altStringEscape(cwd_realpath) }); |
| 267 | continue; |
| 268 | } |
| 269 | try w.print(" pub const _{s} = cache ++ \"/{}\";\n", .{ mod.short_id(), u.altStringEscape(mod.clean_path) }); |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | fn print_deps(w: nfs.File, m: zigmod.Module) !void { |
| 274 | try w.writeAll("&[_]*Package{\n"); |
| 275 | for (m.deps) |d| { |
| 276 | if (d.main.len == 0) { |
| 277 | continue; |
| 278 | } |
| 279 | if (d.for_build) { |
| 280 | continue; |
| 281 | } |
| 282 | try w.print(" &package_data._{s},\n", .{d.id[0..12]}); |
| 283 | } |
| 284 | try w.writeAll("}"); |
| 285 | } |
| 286 | |
| 287 | fn print_pkg_data_to(w: nfs.File, notdone: *std.array_list.Managed(zigmod.Module), done: *std.array_list.Managed(zigmod.Module)) !void { |
| 288 | var len: usize = notdone.items.len; |
| 289 | while (notdone.items.len > 0) { |
| 290 | for (notdone.items, 0..) |mod, i| { |
| 291 | if (contains_all(mod.deps, done.items)) { |
| 292 | try w.print( |
| 293 | \\ pub var _{s} = Package{{ |
| 294 | \\ .directory = dirs._{s}, |
| 295 | \\ |
| 296 | , .{ |
| 297 | mod.short_id(), |
| 298 | mod.short_id(), |
| 299 | }); |
| 300 | if (mod.main.len > 0 and !std.mem.eql(u8, &mod.id, &zigmod.Module.ROOT)) { |
| 301 | try w.print( |
| 302 | \\ .import = .{{ "{s}", .{{ .cwd_relative = dirs._{s} ++ "/{s}" }} }}, |
| 303 | \\ |
| 304 | , .{ |
| 305 | mod.name, |
| 306 | mod.short_id(), |
| 307 | mod.main, |
| 308 | }); |
| 309 | } |
| 310 | { |
| 311 | try w.writeAll(" .dependencies ="); |
| 312 | try w.writeAll(" &.{"); |
| 313 | for (mod.deps, 0..) |moddep, j| { |
| 314 | if (moddep.type == .system_lib) continue; |
| 315 | if (moddep.type == .framework) continue; |
| 316 | try w.print(" &_{s}", .{moddep.id[0..12]}); |
| 317 | if (j != mod.deps.len - 1) try w.writeAll(","); |
| 318 | } |
| 319 | try w.writeAll(" },\n"); |
| 320 | } |
| 321 | if (mod.c_include_dirs.len > 0) { |
| 322 | try w.writeAll(" .c_include_dirs = &.{"); |
| 323 | for (mod.c_include_dirs, 0..) |item, j| { |
| 324 | try w.print(" \"{}\"", .{u.altStringEscape(item)}); |
| 325 | if (j != mod.c_include_dirs.len - 1) try w.writeAll(","); |
| 326 | } |
| 327 | try w.writeAll(" },\n"); |
| 328 | } |
| 329 | if (mod.c_source_files.len > 0) { |
| 330 | try w.writeAll(" .c_source_files = &.{"); |
| 331 | for (mod.c_source_files, 0..) |item, j| { |
| 332 | try w.print(" \"{}\"", .{u.altStringEscape(item)}); |
| 333 | if (j != mod.c_source_files.len - 1) try w.writeAll(","); |
| 334 | } |
| 335 | try w.writeAll(" },\n"); |
| 336 | } |
| 337 | if (mod.c_source_flags.len > 0) { |
| 338 | try w.writeAll(" .c_source_flags = &.{"); |
| 339 | for (mod.c_source_flags, 0..) |item, j| { |
| 340 | try w.print(" \"{}\"", .{u.altStringEscape(item)}); |
| 341 | if (j != mod.c_source_flags.len - 1) try w.writeAll(","); |
| 342 | } |
| 343 | try w.writeAll(" },\n"); |
| 344 | } |
| 345 | if (mod.has_syslib_deps()) { |
| 346 | try w.writeAll(" .system_libs = &.{"); |
| 347 | for (mod.deps, 0..) |item, j| { |
| 348 | if (!(item.type == .system_lib)) continue; |
| 349 | try w.print(" \"{}\"", .{u.altStringEscape(item.name)}); |
| 350 | if (j != mod.deps.len - 1) try w.writeAll(","); |
| 351 | } |
| 352 | try w.writeAll(" },\n"); |
| 353 | } |
| 354 | if (mod.has_framework_deps()) { |
| 355 | try w.writeAll(" .frameworks = &.{"); |
| 356 | for (mod.deps, 0..) |item, j| { |
| 357 | if (!(item.type == .system_lib)) continue; |
| 358 | try w.print(" \"{}\"", .{u.altStringEscape(item.name)}); |
| 359 | if (j != mod.deps.len - 1) try w.writeAll(","); |
| 360 | } |
| 361 | try w.writeAll(" },\n"); |
| 362 | } |
| 363 | try w.writeAll(" };\n"); |
| 364 | |
| 365 | try done.append(mod); |
| 366 | _ = notdone.orderedRemove(i); |
| 367 | break; |
| 368 | } |
| 369 | } |
| 370 | if (notdone.items.len == len) { |
| 371 | u.fail("notdone still has {d} items", .{len}); |
| 372 | } |
| 373 | len = notdone.items.len; |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | /// returns if all of the zig modules in needles are in haystack |
| 378 | fn contains_all(needles: []zigmod.Module, haystack: []const zigmod.Module) bool { |
| 379 | for (needles) |item| { |
| 380 | if (item.main.len > 0 and !extras.containsAggregate(zigmod.Module, haystack, item)) { |
| 381 | return false; |
| 382 | } |
| 383 | } |
| 384 | return true; |
| 385 | } |
| 386 | |
| 387 | fn print_pkgs(alloc: std.mem.Allocator, w: nfs.File, m: zigmod.Module) !void { |
| 388 | try w.writeAll("struct {\n"); |
| 389 | for (m.deps) |d| { |
| 390 | if (d.main.len == 0) { |
| 391 | continue; |
| 392 | } |
| 393 | if (d.for_build) { |
| 394 | continue; |
| 395 | } |
| 396 | const ident = try zig_name_from_pkg_name(alloc, d.name); |
| 397 | try w.print(" pub const {s} = &package_data._{s};\n", .{ ident, d.id[0..12] }); |
| 398 | } |
| 399 | try w.writeAll("}"); |
| 400 | } |
| 401 | |
| 402 | fn print_imports(alloc: std.mem.Allocator, w: nfs.File, m: zigmod.Module, path: string) !void { |
| 403 | for (m.deps) |d| { |
| 404 | if (d.main.len == 0) { |
| 405 | continue; |
| 406 | } |
| 407 | if (!d.for_build) { |
| 408 | continue; |
| 409 | } |
| 410 | const ident = try zig_name_from_pkg_name(alloc, d.name); |
| 411 | try w.print(" pub const {s} = @import(\"{}/{}/{s}\");\n", .{ ident, u.altStringEscape(path), u.altStringEscape(d.clean_path), d.main }); |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | fn zig_name_from_pkg_name(alloc: std.mem.Allocator, name: string) !string { |
| 416 | var legal = name; |
| 417 | legal = try std.mem.replaceOwned(u8, alloc, legal, "-", "_"); |
| 418 | legal = try std.mem.replaceOwned(u8, alloc, legal, "/", "_"); |
| 419 | legal = try std.mem.replaceOwned(u8, alloc, legal, ".", "_"); |
| 420 | return legal; |
| 421 | } |