authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2025-08-11 03:33:58 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2025-08-11 03:33:58 -07:00
logb6168b6da8718f32e416c5256ad4839603a71b37
treeef803825468673adc134bb94a2d14e5dec3db063
parent1d66aac054ac54f39ead1cd87903744bee3b4b33

add min_zigmod_version manifest attribute


9 files changed, 63 insertions(+), 23 deletions(-)

docs/zig.mod.md+5
......@@ -58,6 +58,11 @@ Similar to `dependencies` but will only get added to the project if the current
5858- Type: `string`
5959Parsed as a `std.SemanticVersion`, this attribute refers to the minimum compatible Zig version for this package/application and will cause `zig build` to panic if violated.
6060
61### `min_zigmod_version`
62- Type: `string`
63This attribute refers to the minimum compatible Zigmod version for this package/application and will cause `zigmod fetch`, `zigmod ci`, and others to exit with an error.
64While rare, this is most useful to provide a nice error message to the user when your `zigmod.yml` uses a new feature that may not be available in previous versions of Zigmod.
65
6166----
6267
6368### Dep Object
src/cmd/aquila/add.zig+4-4
......@@ -12,11 +12,11 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
1212 _ = self_name;
1313
1414 const pkg_id = args[0];
15 _ = try do(std.fs.cwd(), pkg_id);
15 _ = try do(std.fs.cwd(), ".", pkg_id);
1616 std.log.info("Successfully added package {s}", .{pkg_id});
1717}
1818
19pub fn do(dir: std.fs.Dir, pkg_id: string) !string {
19pub fn do(dir: std.fs.Dir, dir_path: string, pkg_id: string) !string {
2020 const url = try std.mem.join(gpa, "/", &.{ aq.server_root, pkg_id });
2121 const doc = try aq.server_fetch(url);
2222 doc.acquire();
......@@ -27,7 +27,7 @@ pub fn do(dir: std.fs.Dir, pkg_id: string) !string {
2727 doc.root.object().getO("package").?.getS("remote_name").?,
2828 });
2929
30 const m = try zigmod.ModFile.from_dir(gpa, dir);
30 const m = try zigmod.ModFile.from_dir(gpa, dir, dir_path);
3131 for (m.rootdeps) |d| {
3232 if (std.mem.eql(u8, d.path, pkg_url)) {
3333 return pkg_url;
......@@ -39,7 +39,7 @@ pub fn do(dir: std.fs.Dir, pkg_id: string) !string {
3939 }
4040 }
4141
42 var file = try zigmod.ModFile.openFile(dir, .{ .mode = .read_write });
42 _, var file = try zigmod.ModFile.openFile(dir, .{ .mode = .read_write });
4343 defer file.close();
4444 try file.seekTo(try file.getEndPos());
4545
src/cmd/aquila/install.zig+2-2
......@@ -25,13 +25,13 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
2525
2626 // add to ~/zigmod.yml for later
2727 const aqadd = @import("./add.zig");
28 const pkgurl = aqadd.do(homedir, args[0]) catch |err| switch (err) {
28 const pkgurl = aqadd.do(homedir, homepath, args[0]) catch |err| switch (err) {
2929 error.AquilaBadResponse => return,
3030 else => |ee| return ee,
3131 };
3232
3333 // get modfile and dep
34 const m = try zigmod.ModFile.from_dir(gpa, homedir);
34 const m = try zigmod.ModFile.from_dir(gpa, homedir, homepath);
3535 var dep: zigmod.Dep = undefined;
3636 for (m.rootdeps) |d| {
3737 if (std.mem.eql(u8, d.path, pkgurl)) {
src/cmd/aquila/update.zig+1-1
......@@ -25,7 +25,7 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
2525 u.assert(args.len == 0, "zigmod aq update accepts no parameters", .{});
2626
2727 // get modfile and dep
28 const m = try zigmod.ModFile.from_dir(gpa, homedir);
28 const m = try zigmod.ModFile.from_dir(gpa, homedir, homepath);
2929 for (m.rootdeps) |dep| {
3030 //
3131 const cache = try knownfolders.getPath(gpa, .cache);
src/cmd/zpm/add.zig+1-1
......@@ -57,7 +57,7 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
5757 break :blk @intFromEnum(_req.status) == 200;
5858 };
5959
60 const file = try zigmod.ModFile.openFile(std.fs.cwd(), .{ .mode = .read_write });
60 _, const file = try zigmod.ModFile.openFile(std.fs.cwd(), .{ .mode = .read_write });
6161 defer file.close();
6262 try file.seekTo(try file.getEndPos());
6363
src/common.zig+5-5
......@@ -26,14 +26,14 @@ pub const CollectOptions = struct {
2626pub fn collect_deps_deep(cachepath: string, mdir: std.fs.Dir, options: *CollectOptions) !zigmod.Module {
2727 try std.fs.cwd().makePath(cachepath);
2828
29 const m = try zigmod.ModFile.from_dir(options.alloc, mdir);
29 const m = try zigmod.ModFile.from_dir(options.alloc, mdir, ".");
3030 try options.init();
3131 var moduledeps = std.ArrayList(zigmod.Module).init(options.alloc);
3232 errdefer moduledeps.deinit();
3333 if (m.root_files.len > 0) {
3434 try gen_files_package(options.alloc, cachepath, mdir, m.root_files);
3535 }
36 try moduledeps.append(try collect_deps(cachepath, mdir, .local, options));
36 try moduledeps.append(try collect_deps(cachepath, mdir, ".", .local, options));
3737 for (m.rootdeps) |*d| {
3838 if (try get_module_from_dep(d, cachepath, options)) |founddep| {
3939 try moduledeps.append(founddep);
......@@ -57,10 +57,10 @@ pub fn collect_deps_deep(cachepath: string, mdir: std.fs.Dir, options: *CollectO
5757 };
5858}
5959
60pub fn collect_deps(cachepath: string, mdir: std.fs.Dir, dtype: zigmod.Dep.Type, options: *CollectOptions) anyerror!zigmod.Module {
60pub fn collect_deps(cachepath: string, mdir: std.fs.Dir, mdir_path: string, dtype: zigmod.Dep.Type, options: *CollectOptions) anyerror!zigmod.Module {
6161 try std.fs.cwd().makePath(cachepath);
6262
63 const m = try zigmod.ModFile.from_dir(options.alloc, mdir);
63 const m = try zigmod.ModFile.from_dir(options.alloc, mdir, mdir_path);
6464 var moduledeps = std.ArrayList(zigmod.Module).init(options.alloc);
6565 errdefer moduledeps.deinit();
6666 if (m.files.len > 0) {
......@@ -244,7 +244,7 @@ pub fn get_module_from_dep(d: *zigmod.Dep, cachepath: string, options: *CollectO
244244 };
245245 },
246246 else => {
247 var dd = collect_deps(cachepath, moddir, d.type, options) catch |e| switch (e) {
247 var dd = collect_deps(cachepath, moddir, modpath, d.type, options) catch |e| switch (e) {
248248 error.ManifestNotFound => {
249249 if (d.main.len > 0 or d.c_include_dirs.len > 0 or d.c_source_files.len > 0 or d.keep) {
250250 var mod_from = try zigmod.Module.from(options.alloc, d.*, cachepath, options);
src/lib.zig+21
......@@ -1,4 +1,8 @@
1const std = @import("std");
12const zfetch = @import("zfetch");
3const extras = @import("extras");
4const root = @import("root");
5const build_options = root.build_options;
26
37pub const commands = struct {
48 pub const version = @import("./cmd/version.zig");
......@@ -23,3 +27,20 @@ pub fn deinit() void {
2327pub const Dep = @import("./util/dep.zig").Dep;
2428pub const ModFile = @import("./util/modfile.zig").ModFile;
2529pub const Module = @import("./util/module.zig").Module;
30
31pub const version: u16 = blk: {
32 var version_s = build_options.version;
33 version_s = extras.trimPrefixEnsure(version_s, "r").?;
34 version_s = version_s[0 .. std.mem.indexOfScalar(u8, version_s, '-') orelse version_s.len];
35 var version_i = std.fmt.parseInt(u16, version_s, 10) catch unreachable;
36 if (std.mem.indexOfScalar(u8, version_s, '-')) |_| version_i += 1;
37 break :blk version_i;
38};
39
40pub fn meetsMinimumVersion(min_zigmod_version_raw: []const u8) ?bool {
41 var min_zigmod_version_s = min_zigmod_version_raw;
42 min_zigmod_version_s = extras.trimPrefixEnsure(min_zigmod_version_s, "r") orelse return null;
43 min_zigmod_version_s = min_zigmod_version_s[0 .. std.mem.indexOfScalar(u8, min_zigmod_version_s, '-') orelse min_zigmod_version_s.len];
44 const min_zigmod_version = std.fmt.parseInt(u16, min_zigmod_version_s, 10) catch return null;
45 return min_zigmod_version <= version;
46}
src/util/modfile.zig+23-10
......@@ -1,6 +1,9 @@
11const std = @import("std");
22const string = []const u8;
33const yaml = @import("yaml");
4const extras = @import("extras");
5const root = @import("root");
6const build_options = root.build_options;
47
58const zigmod = @import("../lib.zig");
69const u = @import("funcs.zig");
......@@ -29,29 +32,29 @@ pub const ModFile = struct {
2932 builddeps: []zigmod.Dep,
3033 min_zig_version: ?std.SemanticVersion,
3134
32 pub fn openFile(dir: std.fs.Dir, ops: std.fs.File.OpenFlags) !std.fs.File {
33 return dir.openFile("zig.mod", ops) catch |err| switch (err) {
34 error.FileNotFound => dir.openFile("zigmod.yml", ops) catch |err2| switch (err2) {
35 pub fn openFile(dir: std.fs.Dir, ops: std.fs.File.OpenFlags) !struct { string, std.fs.File } {
36 return .{ "zig.mod", dir.openFile("zig.mod", ops) catch |err| switch (err) {
37 error.FileNotFound => return .{ "zigmod.yml", dir.openFile("zigmod.yml", ops) catch |err2| switch (err2) {
3538 error.FileNotFound => return error.ManifestNotFound,
3639 else => |e2| return e2,
37 },
40 } },
3841 else => |e| return e,
39 };
42 } };
4043 }
4144
4245 pub fn init(alloc: std.mem.Allocator) !Self {
43 return try from_dir(alloc, std.fs.cwd());
46 return try from_dir(alloc, std.fs.cwd(), ".");
4447 }
4548
46 pub fn from_dir(alloc: std.mem.Allocator, dir: std.fs.Dir) !Self {
47 const file = try openFile(dir, .{});
49 pub fn from_dir(alloc: std.mem.Allocator, dir: std.fs.Dir, dir_path: string) !Self {
50 const file_path, const file = try openFile(dir, .{});
4851 defer file.close();
4952 const input = try file.reader().readAllAlloc(alloc, mb);
5053 const doc = try yaml.parse(alloc, input);
51 return from_mapping(alloc, doc.mapping);
54 return from_mapping(alloc, doc.mapping, dir_path, file_path);
5255 }
5356
54 pub fn from_mapping(alloc: std.mem.Allocator, mapping: yaml.Mapping) !Self {
57 pub fn from_mapping(alloc: std.mem.Allocator, mapping: yaml.Mapping, dir_path: string, file_path: string) !Self {
5558 var id = zigmod.Dep.EMPTY;
5659 std.mem.copyForwards(u8, &id, mapping.get_string("id") orelse &u.random_string(48));
5760
......@@ -62,6 +65,16 @@ pub const ModFile = struct {
6265 u.fail("name may not contain any '/'", .{});
6366 }
6467
68 if (mapping.get_string("min_zigmod_version")) |min_zigmod_version_raw| {
69 const meets = zigmod.meetsMinimumVersion(min_zigmod_version_raw);
70 if (meets == null) {
71 u.fail("invalid min_zigmod_version: {s}", .{min_zigmod_version_raw});
72 }
73 if (meets.? == false) {
74 u.fail("Your Zigmod version {s} does not meet the minimum of {s} required by {s}{s}{s}", .{ build_options.version, min_zigmod_version_raw, dir_path, std.fs.path.sep_str, file_path });
75 }
76 }
77
6578 return Self{
6679 .id = id,
6780 .name = name,
zig.mod+1
......@@ -4,6 +4,7 @@ main: src/lib.zig
44license: MIT
55description: A package manager for the Zig programming language.
66min_zig_version: 0.14.0
7min_zigmod_version: r96
78dependencies:
89 - src: git https://github.com/nektro/zig-yaml
910 - src: git https://github.com/nektro/zig-ansi