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...@@ -58,6 +58,11 @@ Similar to `dependencies` but will only get added to the project if the current
58- Type: `string`58- Type: `string`
59Parsed 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.59Parsed 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
61----66----
6267
63### Dep Object68### Dep Object
src/cmd/aquila/add.zig+4-4
...@@ -12,11 +12,11 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {...@@ -12,11 +12,11 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
12 _ = self_name;12 _ = self_name;
1313
14 const pkg_id = args[0];14 const pkg_id = args[0];
15 _ = try do(std.fs.cwd(), pkg_id);15 _ = try do(std.fs.cwd(), ".", pkg_id);
16 std.log.info("Successfully added package {s}", .{pkg_id});16 std.log.info("Successfully added package {s}", .{pkg_id});
17}17}
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 {
20 const url = try std.mem.join(gpa, "/", &.{ aq.server_root, pkg_id });20 const url = try std.mem.join(gpa, "/", &.{ aq.server_root, pkg_id });
21 const doc = try aq.server_fetch(url);21 const doc = try aq.server_fetch(url);
22 doc.acquire();22 doc.acquire();
...@@ -27,7 +27,7 @@ pub fn do(dir: std.fs.Dir, pkg_id: string) !string {...@@ -27,7 +27,7 @@ pub fn do(dir: std.fs.Dir, pkg_id: string) !string {
27 doc.root.object().getO("package").?.getS("remote_name").?,27 doc.root.object().getO("package").?.getS("remote_name").?,
28 });28 });
2929
30 const m = try zigmod.ModFile.from_dir(gpa, dir);30 const m = try zigmod.ModFile.from_dir(gpa, dir, dir_path);
31 for (m.rootdeps) |d| {31 for (m.rootdeps) |d| {
32 if (std.mem.eql(u8, d.path, pkg_url)) {32 if (std.mem.eql(u8, d.path, pkg_url)) {
33 return pkg_url;33 return pkg_url;
...@@ -39,7 +39,7 @@ pub fn do(dir: std.fs.Dir, pkg_id: string) !string {...@@ -39,7 +39,7 @@ pub fn do(dir: std.fs.Dir, pkg_id: string) !string {
39 }39 }
40 }40 }
4141
42 var file = try zigmod.ModFile.openFile(dir, .{ .mode = .read_write });42 _, var file = try zigmod.ModFile.openFile(dir, .{ .mode = .read_write });
43 defer file.close();43 defer file.close();
44 try file.seekTo(try file.getEndPos());44 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 {...@@ -25,13 +25,13 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
2525
26 // add to ~/zigmod.yml for later26 // add to ~/zigmod.yml for later
27 const aqadd = @import("./add.zig");27 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) {
29 error.AquilaBadResponse => return,29 error.AquilaBadResponse => return,
30 else => |ee| return ee,30 else => |ee| return ee,
31 };31 };
3232
33 // get modfile and dep33 // 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);
35 var dep: zigmod.Dep = undefined;35 var dep: zigmod.Dep = undefined;
36 for (m.rootdeps) |d| {36 for (m.rootdeps) |d| {
37 if (std.mem.eql(u8, d.path, pkgurl)) {37 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 {...@@ -25,7 +25,7 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
25 u.assert(args.len == 0, "zigmod aq update accepts no parameters", .{});25 u.assert(args.len == 0, "zigmod aq update accepts no parameters", .{});
2626
27 // get modfile and dep27 // 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);
29 for (m.rootdeps) |dep| {29 for (m.rootdeps) |dep| {
30 //30 //
31 const cache = try knownfolders.getPath(gpa, .cache);31 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 {...@@ -57,7 +57,7 @@ pub fn execute(self_name: []const u8, args: [][:0]u8) !void {
57 break :blk @intFromEnum(_req.status) == 200;57 break :blk @intFromEnum(_req.status) == 200;
58 };58 };
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 });
61 defer file.close();61 defer file.close();
62 try file.seekTo(try file.getEndPos());62 try file.seekTo(try file.getEndPos());
6363
src/common.zig+5-5
...@@ -26,14 +26,14 @@ pub const CollectOptions = struct {...@@ -26,14 +26,14 @@ pub const CollectOptions = struct {
26pub fn collect_deps_deep(cachepath: string, mdir: std.fs.Dir, options: *CollectOptions) !zigmod.Module {26pub fn collect_deps_deep(cachepath: string, mdir: std.fs.Dir, options: *CollectOptions) !zigmod.Module {
27 try std.fs.cwd().makePath(cachepath);27 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, ".");
30 try options.init();30 try options.init();
31 var moduledeps = std.ArrayList(zigmod.Module).init(options.alloc);31 var moduledeps = std.ArrayList(zigmod.Module).init(options.alloc);
32 errdefer moduledeps.deinit();32 errdefer moduledeps.deinit();
33 if (m.root_files.len > 0) {33 if (m.root_files.len > 0) {
34 try gen_files_package(options.alloc, cachepath, mdir, m.root_files);34 try gen_files_package(options.alloc, cachepath, mdir, m.root_files);
35 }35 }
36 try moduledeps.append(try collect_deps(cachepath, mdir, .local, options));36 try moduledeps.append(try collect_deps(cachepath, mdir, ".", .local, options));
37 for (m.rootdeps) |*d| {37 for (m.rootdeps) |*d| {
38 if (try get_module_from_dep(d, cachepath, options)) |founddep| {38 if (try get_module_from_dep(d, cachepath, options)) |founddep| {
39 try moduledeps.append(founddep);39 try moduledeps.append(founddep);
...@@ -57,10 +57,10 @@ pub fn collect_deps_deep(cachepath: string, mdir: std.fs.Dir, options: *CollectO...@@ -57,10 +57,10 @@ pub fn collect_deps_deep(cachepath: string, mdir: std.fs.Dir, options: *CollectO
57 };57 };
58}58}
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 {
61 try std.fs.cwd().makePath(cachepath);61 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);
64 var moduledeps = std.ArrayList(zigmod.Module).init(options.alloc);64 var moduledeps = std.ArrayList(zigmod.Module).init(options.alloc);
65 errdefer moduledeps.deinit();65 errdefer moduledeps.deinit();
66 if (m.files.len > 0) {66 if (m.files.len > 0) {
...@@ -244,7 +244,7 @@ pub fn get_module_from_dep(d: *zigmod.Dep, cachepath: string, options: *CollectO...@@ -244,7 +244,7 @@ pub fn get_module_from_dep(d: *zigmod.Dep, cachepath: string, options: *CollectO
244 };244 };
245 },245 },
246 else => {246 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) {
248 error.ManifestNotFound => {248 error.ManifestNotFound => {
249 if (d.main.len > 0 or d.c_include_dirs.len > 0 or d.c_source_files.len > 0 or d.keep) {249 if (d.main.len > 0 or d.c_include_dirs.len > 0 or d.c_source_files.len > 0 or d.keep) {
250 var mod_from = try zigmod.Module.from(options.alloc, d.*, cachepath, options);250 var mod_from = try zigmod.Module.from(options.alloc, d.*, cachepath, options);
src/lib.zig+21
...@@ -1,4 +1,8 @@...@@ -1,4 +1,8 @@
1const std = @import("std");
1const zfetch = @import("zfetch");2const zfetch = @import("zfetch");
3const extras = @import("extras");
4const root = @import("root");
5const build_options = root.build_options;
26
3pub const commands = struct {7pub const commands = struct {
4 pub const version = @import("./cmd/version.zig");8 pub const version = @import("./cmd/version.zig");
...@@ -23,3 +27,20 @@ pub fn deinit() void {...@@ -23,3 +27,20 @@ pub fn deinit() void {
23pub const Dep = @import("./util/dep.zig").Dep;27pub const Dep = @import("./util/dep.zig").Dep;
24pub const ModFile = @import("./util/modfile.zig").ModFile;28pub const ModFile = @import("./util/modfile.zig").ModFile;
25pub const Module = @import("./util/module.zig").Module;29pub 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 @@...@@ -1,6 +1,9 @@
1const std = @import("std");1const std = @import("std");
2const string = []const u8;2const string = []const u8;
3const yaml = @import("yaml");3const yaml = @import("yaml");
4const extras = @import("extras");
5const root = @import("root");
6const build_options = root.build_options;
47
5const zigmod = @import("../lib.zig");8const zigmod = @import("../lib.zig");
6const u = @import("funcs.zig");9const u = @import("funcs.zig");
...@@ -29,29 +32,29 @@ pub const ModFile = struct {...@@ -29,29 +32,29 @@ pub const ModFile = struct {
29 builddeps: []zigmod.Dep,32 builddeps: []zigmod.Dep,
30 min_zig_version: ?std.SemanticVersion,33 min_zig_version: ?std.SemanticVersion,
3134
32 pub fn openFile(dir: std.fs.Dir, ops: std.fs.File.OpenFlags) !std.fs.File {35 pub fn openFile(dir: std.fs.Dir, ops: std.fs.File.OpenFlags) !struct { string, std.fs.File } {
33 return dir.openFile("zig.mod", ops) catch |err| switch (err) {36 return .{ "zig.mod", dir.openFile("zig.mod", ops) catch |err| switch (err) {
34 error.FileNotFound => dir.openFile("zigmod.yml", ops) catch |err2| switch (err2) {37 error.FileNotFound => return .{ "zigmod.yml", dir.openFile("zigmod.yml", ops) catch |err2| switch (err2) {
35 error.FileNotFound => return error.ManifestNotFound,38 error.FileNotFound => return error.ManifestNotFound,
36 else => |e2| return e2,39 else => |e2| return e2,
37 },40 } },
38 else => |e| return e,41 else => |e| return e,
39 };42 } };
40 }43 }
4144
42 pub fn init(alloc: std.mem.Allocator) !Self {45 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(), ".");
44 }47 }
4548
46 pub fn from_dir(alloc: std.mem.Allocator, dir: std.fs.Dir) !Self {49 pub fn from_dir(alloc: std.mem.Allocator, dir: std.fs.Dir, dir_path: string) !Self {
47 const file = try openFile(dir, .{});50 const file_path, const file = try openFile(dir, .{});
48 defer file.close();51 defer file.close();
49 const input = try file.reader().readAllAlloc(alloc, mb);52 const input = try file.reader().readAllAlloc(alloc, mb);
50 const doc = try yaml.parse(alloc, input);53 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);
52 }55 }
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 {
55 var id = zigmod.Dep.EMPTY;58 var id = zigmod.Dep.EMPTY;
56 std.mem.copyForwards(u8, &id, mapping.get_string("id") orelse &u.random_string(48));59 std.mem.copyForwards(u8, &id, mapping.get_string("id") orelse &u.random_string(48));
5760
...@@ -62,6 +65,16 @@ pub const ModFile = struct {...@@ -62,6 +65,16 @@ pub const ModFile = struct {
62 u.fail("name may not contain any '/'", .{});65 u.fail("name may not contain any '/'", .{});
63 }66 }
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
65 return Self{78 return Self{
66 .id = id,79 .id = id,
67 .name = name,80 .name = name,
zig.mod+1
...@@ -4,6 +4,7 @@ main: src/lib.zig...@@ -4,6 +4,7 @@ main: src/lib.zig
4license: MIT4license: MIT
5description: A package manager for the Zig programming language.5description: A package manager for the Zig programming language.
6min_zig_version: 0.14.06min_zig_version: 0.14.0
7min_zigmod_version: r96
7dependencies:8dependencies:
8 - src: git https://github.com/nektro/zig-yaml9 - src: git https://github.com/nektro/zig-yaml
9 - src: git https://github.com/nektro/zig-ansi10 - src: git https://github.com/nektro/zig-ansi