From 13793e258c36d9a49f1c79db6e0188fe87a624de Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 5 Jul 2021 17:41:40 -0700 Subject: [PATCH] add the `ci` command. similar to `fetch` but only reads from `zigmod.lock` --- docs/commands/ci.md | 7 +++++ docs/commands/fetch.md | 1 + src/cmd/ci.zig | 25 +++++++++++++++++ src/common.zig | 61 +++++++++++++++++++++++++----------------- src/lib.zig | 2 ++ src/util/dep.zig | 12 ++++----- src/util/modfile.zig | 6 ++--- src/util/module.zig | 12 ++++----- 8 files changed, 87 insertions(+), 39 deletions(-) create mode 100644 docs/commands/ci.md create mode 100644 src/cmd/ci.zig diff --git a/docs/commands/ci.md b/docs/commands/ci.md new file mode 100644 index 0000000000000000000000000000000000000000..50fa9865d8d6ebcaa481271de61e85f7cc824c90 --- /dev/null +++ b/docs/commands/ci.md @@ -0,0 +1,7 @@ +## `ci` command +``` +zigmod ci +``` + +- This command takes no parameters and will do almost exactly the same thing as the [`fetch`](./fetch.md) command, except it will read version strings from your `zigmod.lock` file instead of from dependencies' `zig.mod` definitions. +- Inspired by the [`npm ci`](https://docs.npmjs.com/cli/ci.html) command. diff --git a/docs/commands/fetch.md b/docs/commands/fetch.md index 9d1b37b2f8fc1291dc172662909f118ee0a73363..3a3cf146abf5f1b70d0d669460c767eb4b9e67d3 100644 --- a/docs/commands/fetch.md +++ b/docs/commands/fetch.md @@ -5,6 +5,7 @@ zigmod fetch - This command takes no parameters and will generate a `deps.zig` in the root of your project. This is the file that you will then import into your `build.zig` to automatically add all the necessary packages and (any) C code that may be in your dependencies. - `deps.zig` is not typically checked into your source control. +- This command will also produce a `zigmod.lock` which you can use to easily generate [reproducible builds](https://reproducible-builds.org/) using the [`ci`](./ci.md) command. For a full reference on the fields available in `deps.zig` you can check [here](../deps.zig.md). diff --git a/src/cmd/ci.zig b/src/cmd/ci.zig new file mode 100644 index 0000000000000000000000000000000000000000..6e3323ddb77db5886f3c54603c24a05fcad97e8e --- /dev/null +++ b/src/cmd/ci.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const gpa = std.heap.c_allocator; + +const u = @import("./../util/index.zig"); +const common = @import("./../common.zig"); + +// Inspired by: +// https://docs.npmjs.com/cli/v7/commands/npm-ci + +pub fn execute(args: [][]u8) !void { + _ = args; + + const dir = try std.fs.path.join(gpa, &.{ ".zigmod", "deps" }); + + const top_module = try common.collect_deps_deep(dir, "zig.mod", .{ + .log = true, + .update = false, + .lock = try common.parse_lockfile("zigmod.lock"), + }); + + const list = &std.ArrayList(u.Module).init(gpa); + try common.collect_pkgs(top_module, list); + + try @import("./fetch.zig").create_depszig(dir, top_module, list); +} diff --git a/src/common.zig b/src/common.zig index 7a01d39c2f22e1ce2ef5aadab79174f583607390..f7b409987101acede4ade77f53b382a6a722cce1 100644 --- a/src/common.zig +++ b/src/common.zig @@ -10,6 +10,7 @@ const yaml = @import("./util/yaml.zig"); pub const CollectOptions = struct { log: bool, update: bool, + lock: ?[]const [4][]const u8 = null, }; pub fn collect_deps_deep(dir: []const u8, mpath: []const u8, options: CollectOptions) !u.Module { @@ -21,7 +22,7 @@ pub fn collect_deps_deep(dir: []const u8, mpath: []const u8, options: CollectOpt try moduledeps.append(try add_files_package("root", m.root_files, m.name)); } try moduledeps.append(try collect_deps(dir, mpath, options)); - for (m.devdeps) |d| { + for (m.devdeps) |*d| { if (try get_module_from_dep(d, dir, m.name, options)) |founddep| { try moduledeps.append(founddep); } @@ -31,13 +32,8 @@ pub fn collect_deps_deep(dir: []const u8, mpath: []const u8, options: CollectOpt .id = "root", .name = "root", .main = m.main, - .c_include_dirs = &.{}, - .c_source_flags = &.{}, - .c_source_files = &.{}, .deps = moduledeps.toOwnedSlice(), .clean_path = "", - .only_os = &.{}, - .except_os = &.{}, .yaml = m.yaml, .dep = null, }; @@ -50,7 +46,7 @@ pub fn collect_deps(dir: []const u8, mpath: []const u8, options: CollectOptions) if (m.files.len > 0) { try moduledeps.append(try add_files_package(m.id, m.files, m.name)); } - for (m.deps) |d| { + for (m.deps) |*d| { if (try get_module_from_dep(d, dir, m.name, options)) |founddep| { try moduledeps.append(founddep); } @@ -65,8 +61,6 @@ pub fn collect_deps(dir: []const u8, mpath: []const u8, options: CollectOptions) .c_source_files = m.c_source_files, .deps = moduledeps.toOwnedSlice(), .clean_path = "../..", - .only_os = &.{}, - .except_os = &.{}, .yaml = m.yaml, .dep = null, }; @@ -180,8 +174,18 @@ fn get_moddir(basedir: []const u8, d: u.Dep, parent_name: []const u8, options: C } } -pub fn get_module_from_dep(d: u.Dep, dir: []const u8, parent_name: []const u8, options: CollectOptions) anyerror!?u.Module { - const moddir = try get_moddir(dir, d, parent_name, options); +pub fn get_module_from_dep(d: *u.Dep, dir: []const u8, parent_name: []const u8, options: CollectOptions) anyerror!?u.Module { + if (options.lock) |lock| { + for (lock) |item| { + if (std.mem.eql(u8, item[0], try d.clean_path())) { + d.type = std.meta.stringToEnum(u.DepType, item[1]).?; + d.path = item[2]; + d.version = item[3]; + break; + } + } + } + const moddir = try get_moddir(dir, d.*, parent_name, options); switch (d.type) { .system_lib => { return u.Module{ @@ -191,20 +195,17 @@ pub fn get_module_from_dep(d: u.Dep, dir: []const u8, parent_name: []const u8, o .only_os = d.only_os, .except_os = d.except_os, .main = "", - .c_include_dirs = &.{}, - .c_source_flags = &.{}, - .c_source_files = &.{}, .deps = &[_]u.Module{}, .clean_path = d.path, .yaml = null, - .dep = d, + .dep = d.*, }; }, else => { var dd = try collect_deps(dir, try u.concat(&.{ moddir, "/zig.mod" }), options) catch |e| switch (e) { error.FileNotFound => { if (d.main.len > 0 or d.c_include_dirs.len > 0 or d.c_source_files.len > 0) { - var mod_from = try u.Module.from(d, dir, options); + var mod_from = try u.Module.from(d.*, dir, options); if (d.type != .local) mod_from.clean_path = u.trim_prefix(moddir, dir)[1..]; if (mod_from.is_for_this()) return mod_from; return null; @@ -213,7 +214,7 @@ pub fn get_module_from_dep(d: u.Dep, dir: []const u8, parent_name: []const u8, o }, else => e, }; - dd.dep = d; + dd.dep = d.*; const save = dd; if (d.type != .local) dd.clean_path = u.trim_prefix(moddir, dir)[1..]; if (dd.id.len == 0) dd.id = try u.random_string(48); @@ -274,23 +275,35 @@ fn add_files_package(pkg_name: []const u8, dirs: []const []const u8, parent_name \\ ); - const d: u.Dep = .{ + var d: u.Dep = .{ .type = .local, .path = "files", .id = "", .name = "self/files", .main = fname, .version = "absolute", - .c_include_dirs = &.{}, - .c_source_flags = &.{}, - .c_source_files = &.{}, - .only_os = &.{}, - .except_os = &.{}, .yaml = null, .deps = &.{}, }; - return (try get_module_from_dep(d, destination, parent_name, .{ + return (try get_module_from_dep(&d, destination, parent_name, .{ .log = false, .update = false, })).?; } + +pub fn parse_lockfile(path: []const u8) ![]const [4][]const u8 { + const list = &std.ArrayList([4][]const u8).init(gpa); + const max = std.math.maxInt(usize); + const f = try std.fs.cwd().openFile(path, .{}); + const r = f.reader(); + while (try r.readUntilDelimiterOrEofAlloc(gpa, '\n', max)) |line| { + var iter = std.mem.split(line, " "); + try list.append([4][]const u8{ + iter.next().?, + iter.next().?, + iter.next().?, + iter.next().?, + }); + } + return list.toOwnedSlice(); +} diff --git a/src/lib.zig b/src/lib.zig index e93cf8bc70a7bce6460766fbea3c37e775d12d95..3362f7dca1de35a0c376cdf311b72f0fecb1c865 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -1,10 +1,12 @@ pub const commands_to_bootstrap = struct { pub const fetch = @import("./cmd/fetch.zig"); + pub const ci = @import("./cmd/ci.zig"); }; pub const commands = struct { pub const init = @import("./cmd/init.zig"); pub const fetch = @import("./cmd/fetch.zig"); + pub const ci = @import("./cmd/ci.zig"); pub const sum = @import("./cmd/sum.zig"); pub const zpm = @import("./cmd/zpm.zig"); pub const license = @import("./cmd/license.zig"); diff --git a/src/util/dep.zig b/src/util/dep.zig index ba2259bb4a24e69fe0a1212a5d56baa9665b65d1..e3ab5ae8d306d74edbd19abdfdb5d6ebdbe1b09a 100644 --- a/src/util/dep.zig +++ b/src/util/dep.zig @@ -18,13 +18,13 @@ pub const Dep = struct { name: []const u8, main: []const u8, version: []const u8, - c_include_dirs: []const []const u8, - c_source_flags: []const []const u8, - c_source_files: []const []const u8, - only_os: []const []const u8, - except_os: []const []const u8, + c_include_dirs: []const []const u8 = &.{}, + c_source_flags: []const []const u8 = &.{}, + c_source_files: []const []const u8 = &.{}, + only_os: []const []const u8 = &.{}, + except_os: []const []const u8 = &.{}, yaml: ?yaml.Mapping, - deps: []const u.Dep, + deps: []u.Dep, pub fn clean_path(self: Dep) ![]const u8 { if (self.type == .local) { diff --git a/src/util/modfile.zig b/src/util/modfile.zig index 2c16a14f808fe1ad475674ede9a17345fca3a78b..43709bd8902446b983f6a38371fcfc85a41e4524 100644 --- a/src/util/modfile.zig +++ b/src/util/modfile.zig @@ -20,9 +20,9 @@ pub const ModFile = struct { c_include_dirs: []const []const u8, c_source_flags: []const []const u8, c_source_files: []const []const u8, - deps: []const u.Dep, + deps: []u.Dep, yaml: yaml.Mapping, - devdeps: []const u.Dep, + devdeps: []u.Dep, root_files: []const []const u8, files: []const []const u8, @@ -61,7 +61,7 @@ pub const ModFile = struct { }; } - fn dep_list_by_name(alloc: *std.mem.Allocator, mapping: yaml.Mapping, prop: []const u8) anyerror![]const u.Dep { + fn dep_list_by_name(alloc: *std.mem.Allocator, mapping: yaml.Mapping, prop: []const u8) anyerror![]u.Dep { const dep_list = &std.ArrayList(u.Dep).init(alloc); if (mapping.get(prop)) |dep_seq| { if (dep_seq == .sequence) { diff --git a/src/util/module.zig b/src/util/module.zig index a2da5f152806b76f5a748b2fc8474fcb6e3bd867..0e1e247a82f63b3be6bb89372d42d48b7eebac13 100644 --- a/src/util/module.zig +++ b/src/util/module.zig @@ -14,11 +14,11 @@ pub const Module = struct { id: []const u8, name: []const u8, main: []const u8, - c_include_dirs: []const []const u8, - c_source_flags: []const []const u8, - c_source_files: []const []const u8, - only_os: []const []const u8, - except_os: []const []const u8, + c_include_dirs: []const []const u8 = &.{}, + c_source_flags: []const []const u8 = &.{}, + c_source_files: []const []const u8 = &.{}, + only_os: []const []const u8 = &.{}, + except_os: []const []const u8 = &.{}, yaml: ?yaml.Mapping, deps: []Module, clean_path: []const u8, @@ -27,7 +27,7 @@ pub const Module = struct { pub fn from(dep: u.Dep, dir: []const u8, options: common.CollectOptions) !Module { const moddeps = &std.ArrayList(Module).init(gpa); defer moddeps.deinit(); - for (dep.deps) |d| { + for (dep.deps) |*d| { if (try common.get_module_from_dep(d, dir, dep.name, options)) |founddep| { try moddeps.append(founddep); } -- 2.54.0