| ... | @@ -0,0 +1,375 @@ |
| 1 | const std = @import("std"); |
| 2 | const string = []const u8; |
| 3 | |
| 4 | const zigmod = @import("../lib.zig"); |
| 5 | const u = @import("./../util/index.zig"); |
| 6 | const common = @import("./../common.zig"); |
| 7 | |
| 8 | // |
| 9 | // |
| 10 | |
| 11 | pub fn execute(args: [][]u8) !void { |
| 12 | _ = args; |
| 13 | |
| 14 | // |
| 15 | const gpa = std.heap.c_allocator; |
| 16 | const cachepath = try std.fs.path.join(gpa, &.{ ".zigmod", "deps" }); |
| 17 | const dir = std.fs.cwd(); |
| 18 | |
| 19 | var options = common.CollectOptions{ |
| 20 | .log = false, |
| 21 | .update = false, |
| 22 | .alloc = gpa, |
| 23 | }; |
| 24 | const top_module = try common.collect_deps_deep(cachepath, dir, &options); |
| 25 | |
| 26 | var list = std.ArrayList(zigmod.Module).init(gpa); |
| 27 | try common.collect_pkgs(top_module, &list); |
| 28 | |
| 29 | std.sort.sort(zigmod.Module, list.items, {}, zigmod.Module.lessThan); |
| 30 | |
| 31 | try create_depszig(gpa, cachepath, dir, top_module, list.items); |
| 32 | } |
| 33 | |
| 34 | pub fn create_depszig(alloc: std.mem.Allocator, cachepath: string, dir: std.fs.Dir, top_module: zigmod.Module, list: []const zigmod.Module) !void { |
| 35 | const f = try dir.createFile("deps.zig", .{}); |
| 36 | defer f.close(); |
| 37 | |
| 38 | const w = f.writer(); |
| 39 | try w.writeAll("// zig fmt: off\n"); |
| 40 | try w.writeAll("const std = @import(\"std\");\n"); |
| 41 | try w.writeAll("const builtin = @import(\"builtin\");\n"); |
| 42 | try w.writeAll("const Pkg = std.build.Pkg;\n"); |
| 43 | try w.writeAll("const string = []const u8;\n"); |
| 44 | try w.writeAll("\n"); |
| 45 | try w.writeAll( |
| 46 | \\pub const GitExactStep = struct { |
| 47 | \\ step: std.build.Step, |
| 48 | \\ builder: *std.build.Builder, |
| 49 | \\ url: string, |
| 50 | \\ commit: string, |
| 51 | \\ |
| 52 | \\ pub fn create(b: *std.build.Builder, url: string, commit: string) *GitExactStep { |
| 53 | \\ var result = b.allocator.create(GitExactStep) catch @panic("memory"); |
| 54 | \\ result.* = GitExactStep{ |
| 55 | \\ .step = std.build.Step.init(.custom, b.fmt("git clone {s} @ {s}", .{ url, commit }), b.allocator, make), |
| 56 | \\ .builder = b, |
| 57 | \\ .url = url, |
| 58 | \\ .commit = commit, |
| 59 | \\ }; |
| 60 | \\ |
| 61 | \\ var urlpath = url; |
| 62 | \\ urlpath = trimPrefix(u8, urlpath, "https://"); |
| 63 | \\ urlpath = trimPrefix(u8, urlpath, "git://"); |
| 64 | \\ const repopath = b.fmt("{s}/zigmod/deps/git/{s}/{s}", .{ b.cache_root, urlpath, commit }); |
| 65 | \\ flip(std.fs.cwd().access(repopath, .{})) catch return result; |
| 66 | \\ |
| 67 | \\ var clonestep = std.build.RunStep.create(b, "clone"); |
| 68 | \\ clonestep.addArgs(&.{ "git", "clone", "-q", "--progress", url, repopath }); |
| 69 | \\ result.step.dependOn(&clonestep.step); |
| 70 | \\ |
| 71 | \\ var checkoutstep = std.build.RunStep.create(b, "checkout"); |
| 72 | \\ checkoutstep.addArgs(&.{ "git", "-C", repopath, "checkout", "-q", commit }); |
| 73 | \\ result.step.dependOn(&checkoutstep.step); |
| 74 | \\ |
| 75 | \\ return result; |
| 76 | \\ } |
| 77 | \\ |
| 78 | \\ fn make(step: *std.build.Step) !void { |
| 79 | \\ _ = step; |
| 80 | \\ } |
| 81 | \\}; |
| 82 | \\ |
| 83 | \\pub fn fetch(exe: *std.build.LibExeObjStep) void { |
| 84 | \\ const b = exe.builder; |
| 85 | \\ inline for (comptime std.meta.declarations(package_data)) |decl| { |
| 86 | \\ const path = &@field(package_data, decl.name).entry; |
| 87 | \\ const root = if (@field(package_data, decl.name).store) |_| b.cache_root else "."; |
| 88 | \\ if (path.* != null) path.* = b.fmt("{s}/zigmod/deps{s}", .{ root, path.*.? }); |
| 89 | \\ } |
| 90 | \\ |
| 91 | ); |
| 92 | for (list) |module| { |
| 93 | switch (module.type) { |
| 94 | .local => {}, |
| 95 | .system_lib => {}, |
| 96 | .framework => {}, |
| 97 | .git => try w.print(" exe.step.dependOn(&GitExactStep.create(b, \"{s}\", \"{s}\").step);\n", .{ module.dep.?.path, try module.pin(alloc, cachepath) }), |
| 98 | .hg => @panic("TODO"), |
| 99 | .http => @panic("TODO"), |
| 100 | } |
| 101 | } |
| 102 | try w.writeAll( |
| 103 | \\} |
| 104 | \\ |
| 105 | \\fn trimPrefix(comptime T: type, haystack: []const T, needle: []const T) []const T { |
| 106 | \\ if (std.mem.startsWith(T, haystack, needle)) { |
| 107 | \\ return haystack[needle.len .. haystack.len]; |
| 108 | \\ } |
| 109 | \\ return haystack; |
| 110 | \\} |
| 111 | \\ |
| 112 | \\fn flip(foo: anytype) !void { |
| 113 | \\ _ = foo catch return; |
| 114 | \\ return error.ExpectedError; |
| 115 | \\} |
| 116 | \\ |
| 117 | \\pub fn addAllTo(exe: *std.build.LibExeObjStep) void { |
| 118 | \\ checkMinZig(builtin.zig_version, exe); |
| 119 | \\ fetch(exe); |
| 120 | \\ const b = exe.builder; |
| 121 | \\ @setEvalBranchQuota(1_000_000); |
| 122 | \\ for (packages) |pkg| { |
| 123 | \\ exe.addPackage(pkg.zp(b)); |
| 124 | \\ } |
| 125 | \\ var llc = false; |
| 126 | \\ var vcpkg = false; |
| 127 | \\ inline for (comptime std.meta.declarations(package_data)) |decl| { |
| 128 | \\ const pkg = @as(Package, @field(package_data, decl.name)); |
| 129 | \\ const root = if (pkg.store) |st| b.fmt("{s}/zigmod/deps/{s}", .{ b.cache_root, st }) else "."; |
| 130 | \\ for (pkg.system_libs) |item| { |
| 131 | \\ exe.linkSystemLibrary(item); |
| 132 | \\ llc = true; |
| 133 | \\ } |
| 134 | \\ for (pkg.frameworks) |item| { |
| 135 | \\ if (!builtin.target.isDarwin()) @panic(exe.builder.fmt("a dependency is attempting to link to the framework {s}, which is only possible under Darwin", .{item})); |
| 136 | \\ exe.linkFramework(item); |
| 137 | \\ llc = true; |
| 138 | \\ } |
| 139 | \\ for (pkg.c_include_dirs) |item| { |
| 140 | \\ exe.addIncludeDir(b.fmt("{s}/{s}", .{ root, item })); |
| 141 | \\ llc = true; |
| 142 | \\ } |
| 143 | \\ for (pkg.c_source_files) |item| { |
| 144 | \\ exe.addCSourceFile(b.fmt("{s}/{s}", .{ root, item }), pkg.c_source_flags); |
| 145 | \\ llc = true; |
| 146 | \\ } |
| 147 | \\ vcpkg = vcpkg or pkg.vcpkg; |
| 148 | \\ } |
| 149 | \\ if (llc) exe.linkLibC(); |
| 150 | \\ if (builtin.os.tag == .windows and vcpkg) exe.addVcpkgPaths(.static) catch |err| @panic(@errorName(err)); |
| 151 | \\} |
| 152 | \\ |
| 153 | \\pub const Package = struct { |
| 154 | \\ name: string = "", |
| 155 | \\ entry: ?string = null, |
| 156 | \\ store: ?string = null, |
| 157 | \\ deps: []const *Package = &.{}, |
| 158 | \\ c_include_dirs: []const string = &.{}, |
| 159 | \\ c_source_files: []const string = &.{}, |
| 160 | \\ c_source_flags: []const string = &.{}, |
| 161 | \\ system_libs: []const string = &.{}, |
| 162 | \\ frameworks: []const string = &.{}, |
| 163 | \\ vcpkg: bool = false, |
| 164 | \\ |
| 165 | \\ pub fn zp(self: *const Package, b: *std.build.Builder) Pkg { |
| 166 | \\ var temp: [100]Pkg = undefined; |
| 167 | \\ for (self.deps) |item, i| { |
| 168 | \\ temp[i] = item.zp(b); |
| 169 | \\ } |
| 170 | \\ return .{ |
| 171 | \\ .name = self.name, |
| 172 | \\ .source = .{ .path = self.entry.? }, |
| 173 | \\ .dependencies = b.allocator.dupe(Pkg, temp[0..self.deps.len]) catch @panic("oom"), |
| 174 | \\ }; |
| 175 | \\ } |
| 176 | \\}; |
| 177 | \\ |
| 178 | \\ |
| 179 | ); |
| 180 | |
| 181 | try w.print( |
| 182 | \\fn checkMinZig(current: std.SemanticVersion, exe: *std.build.LibExeObjStep) void {{ |
| 183 | \\ const min = std.SemanticVersion.parse("{?}") catch return; |
| 184 | \\ if (current.order(min).compare(.lt)) @panic(exe.builder.fmt("Your Zig version v{{}} does not meet the minimum build requirement of v{{}}", .{{current, min}})); |
| 185 | \\}} |
| 186 | \\ |
| 187 | \\ |
| 188 | , .{top_module.minZigVersion()}); |
| 189 | |
| 190 | try w.writeAll("pub const package_data = struct {\n"); |
| 191 | var duped = std.ArrayList(zigmod.Module).init(alloc); |
| 192 | for (list) |mod| { |
| 193 | if (mod.type == .system_lib or mod.type == .framework) { |
| 194 | continue; |
| 195 | } |
| 196 | try duped.append(mod); |
| 197 | } |
| 198 | try print_pkg_data_to(w, alloc, cachepath, &duped, &std.ArrayList(zigmod.Module).init(alloc)); |
| 199 | try w.writeAll("};\n\n"); |
| 200 | |
| 201 | try w.writeAll("pub const packages = "); |
| 202 | try print_deps(w, top_module); |
| 203 | try w.writeAll(";\n\n"); |
| 204 | |
| 205 | try w.writeAll("pub const pkgs = "); |
| 206 | try print_pkgs(alloc, w, top_module); |
| 207 | try w.writeAll(";\n\n"); |
| 208 | |
| 209 | try w.writeAll("pub const imports = struct {\n"); |
| 210 | try print_imports(alloc, w, top_module, cachepath); |
| 211 | try w.writeAll("};\n"); |
| 212 | } |
| 213 | |
| 214 | fn print_dirs(w: std.fs.File.Writer, list: []const zigmod.Module) !void { |
| 215 | for (list) |mod| { |
| 216 | if (mod.type == .system_lib or mod.type == .framework) continue; |
| 217 | if (std.mem.eql(u8, mod.id, "root")) { |
| 218 | try w.writeAll(" pub const _root = \"\";\n"); |
| 219 | continue; |
| 220 | } |
| 221 | try w.print(" pub const _{s} = cache ++ \"/{}\";\n", .{ mod.short_id(), std.zig.fmtEscapes(mod.clean_path) }); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | fn print_deps(w: std.fs.File.Writer, m: zigmod.Module) !void { |
| 226 | try w.writeAll("[_]*const Package{\n"); |
| 227 | for (m.deps) |d| { |
| 228 | if (d.main.len == 0) { |
| 229 | continue; |
| 230 | } |
| 231 | if (d.for_build) { |
| 232 | continue; |
| 233 | } |
| 234 | try w.print(" &package_data._{s},\n", .{d.id[0..12]}); |
| 235 | } |
| 236 | try w.writeAll("}"); |
| 237 | } |
| 238 | |
| 239 | fn print_pkg_data_to(w: std.fs.File.Writer, alloc: std.mem.Allocator, cachepath: string, notdone: *std.ArrayList(zigmod.Module), done: *std.ArrayList(zigmod.Module)) !void { |
| 240 | var len: usize = notdone.items.len; |
| 241 | while (notdone.items.len > 0) { |
| 242 | for (notdone.items) |mod, i| { |
| 243 | if (contains_all(mod.deps, done.items)) { |
| 244 | try w.print( |
| 245 | \\ pub var _{s} = Package{{ |
| 246 | \\ |
| 247 | , .{ |
| 248 | mod.short_id(), |
| 249 | }); |
| 250 | var fixed_path = if (std.mem.startsWith(u8, mod.clean_path, "v/")) mod.clean_path[2..std.mem.lastIndexOfScalar(u8, mod.clean_path, '/').?] else mod.clean_path; |
| 251 | switch (mod.type) { |
| 252 | .system_lib, .framework => {}, |
| 253 | .local => {}, |
| 254 | .git => try w.print(" .store = \"/{}/{s}\",\n", .{ std.zig.fmtEscapes(fixed_path), try mod.pin(alloc, cachepath) }), |
| 255 | .hg => @panic("TODO"), |
| 256 | .http => @panic("TODO"), |
| 257 | } |
| 258 | if (mod.main.len > 0 and !std.mem.eql(u8, mod.id, "root")) { |
| 259 | try w.print(" .name = \"{s}\",\n", .{mod.name}); |
| 260 | try w.print(" .entry = \"/{}/{s}/{s}\",\n", .{ std.zig.fmtEscapes(fixed_path), try mod.pin(alloc, cachepath), mod.main }); |
| 261 | |
| 262 | if (!mod.has_no_zig_deps()) { |
| 263 | try w.writeAll(" .deps = &[_]*Package{"); |
| 264 | for (mod.deps) |moddep, j| { |
| 265 | if (moddep.main.len == 0) continue; |
| 266 | try w.print(" &_{s}", .{moddep.id[0..12]}); |
| 267 | if (j != mod.deps.len - 1) try w.writeAll(","); |
| 268 | } |
| 269 | try w.writeAll(" },\n"); |
| 270 | } |
| 271 | } |
| 272 | if (mod.c_include_dirs.len > 0) { |
| 273 | try w.writeAll(" .c_include_dirs = &.{"); |
| 274 | for (mod.c_include_dirs) |item, j| { |
| 275 | try w.print(" \"{}\"", .{std.zig.fmtEscapes(item)}); |
| 276 | if (j != mod.c_include_dirs.len - 1) try w.writeAll(","); |
| 277 | } |
| 278 | try w.writeAll(" },\n"); |
| 279 | } |
| 280 | if (mod.c_source_files.len > 0) { |
| 281 | try w.writeAll(" .c_source_files = &.{"); |
| 282 | for (mod.c_source_files) |item, j| { |
| 283 | try w.print(" \"{}\"", .{std.zig.fmtEscapes(item)}); |
| 284 | if (j != mod.c_source_files.len - 1) try w.writeAll(","); |
| 285 | } |
| 286 | try w.writeAll(" },\n"); |
| 287 | } |
| 288 | if (mod.c_source_flags.len > 0) { |
| 289 | try w.writeAll(" .c_source_flags = &.{"); |
| 290 | for (mod.c_source_flags) |item, j| { |
| 291 | try w.print(" \"{}\"", .{std.zig.fmtEscapes(item)}); |
| 292 | if (j != mod.c_source_flags.len - 1) try w.writeAll(","); |
| 293 | } |
| 294 | try w.writeAll(" },\n"); |
| 295 | } |
| 296 | if (mod.has_syslib_deps()) { |
| 297 | try w.writeAll(" .system_libs = &.{"); |
| 298 | for (mod.deps) |item, j| { |
| 299 | if (!(item.type == .system_lib)) continue; |
| 300 | try w.print(" \"{}\"", .{std.zig.fmtEscapes(item.name)}); |
| 301 | if (j != mod.deps.len - 1) try w.writeAll(","); |
| 302 | } |
| 303 | try w.writeAll(" },\n"); |
| 304 | } |
| 305 | if (mod.has_framework_deps()) { |
| 306 | try w.writeAll(" .frameworks = &.{"); |
| 307 | for (mod.deps) |item, j| { |
| 308 | if (!(item.type == .system_lib)) continue; |
| 309 | try w.print(" \"{}\"", .{std.zig.fmtEscapes(item.name)}); |
| 310 | if (j != mod.deps.len - 1) try w.writeAll(","); |
| 311 | } |
| 312 | try w.writeAll(" },\n"); |
| 313 | } |
| 314 | if (mod.vcpkg) { |
| 315 | try w.writeAll(" .vcpkg = true,\n"); |
| 316 | } |
| 317 | try w.writeAll(" };\n"); |
| 318 | |
| 319 | try done.append(mod); |
| 320 | _ = notdone.orderedRemove(i); |
| 321 | break; |
| 322 | } |
| 323 | } |
| 324 | if (notdone.items.len == len) { |
| 325 | u.fail("notdone still has {d} items", .{len}); |
| 326 | } |
| 327 | len = notdone.items.len; |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /// returns if all of the zig modules in needles are in haystack |
| 332 | fn contains_all(needles: []zigmod.Module, haystack: []const zigmod.Module) bool { |
| 333 | for (needles) |item| { |
| 334 | if (item.main.len > 0 and !u.list_contains_gen(zigmod.Module, haystack, item)) { |
| 335 | return false; |
| 336 | } |
| 337 | } |
| 338 | return true; |
| 339 | } |
| 340 | |
| 341 | fn print_pkgs(alloc: std.mem.Allocator, w: std.fs.File.Writer, m: zigmod.Module) !void { |
| 342 | try w.writeAll("struct {\n"); |
| 343 | for (m.deps) |d| { |
| 344 | if (d.main.len == 0) { |
| 345 | continue; |
| 346 | } |
| 347 | if (d.for_build) { |
| 348 | continue; |
| 349 | } |
| 350 | const ident = try zig_name_from_pkg_name(alloc, d.name); |
| 351 | try w.print(" pub const {s} = &package_data._{s};\n", .{ ident, d.id[0..12] }); |
| 352 | } |
| 353 | try w.writeAll("}"); |
| 354 | } |
| 355 | |
| 356 | fn print_imports(alloc: std.mem.Allocator, w: std.fs.File.Writer, m: zigmod.Module, path: string) !void { |
| 357 | for (m.deps) |d| { |
| 358 | if (d.main.len == 0) { |
| 359 | continue; |
| 360 | } |
| 361 | if (!d.for_build) { |
| 362 | continue; |
| 363 | } |
| 364 | const ident = try zig_name_from_pkg_name(alloc, d.name); |
| 365 | try w.print(" pub const {s} = @import(\"{}/{}/{s}\");\n", .{ ident, std.zig.fmtEscapes(path), std.zig.fmtEscapes(d.clean_path), d.main }); |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | fn zig_name_from_pkg_name(alloc: std.mem.Allocator, name: string) !string { |
| 370 | var legal = name; |
| 371 | legal = try std.mem.replaceOwned(u8, alloc, legal, "-", "_"); |
| 372 | legal = try std.mem.replaceOwned(u8, alloc, legal, "/", "_"); |
| 373 | legal = try std.mem.replaceOwned(u8, alloc, legal, ".", "_"); |
| 374 | return legal; |
| 375 | } |