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