aboutsummaryrefslogtreecommitdiff
path: root/src/port.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/port.zig')
-rw-r--r--src/port.zig472
1 files changed, 283 insertions, 189 deletions
diff --git a/src/port.zig b/src/port.zig
index 81aa034..a7d53e4 100644
--- a/src/port.zig
+++ b/src/port.zig
@@ -1,5 +1,5 @@
//! A port's configuration is stored file-based with the following
-//! directory structure:
+//! files:
//!
//! * info - category, template
//! * src - repo, branch, commit hash, commit timestamp
@@ -24,7 +24,7 @@ const fatal = cli.fatal;
const distname_ext = ".tar.gz";
pub const Ctx = struct {
- env: Env,
+ env: *const Env,
tree: tree.Tree,
};
@@ -57,9 +57,9 @@ pub const Info = struct {
fn from_file(ctx: Ctx, port_name: []const u8) !?Info {
const port_root = ctx.tree.port_path(port_name);
- const info_file: fs.Path = .join(.{ port_root.name(), "info" });
+ const info_path: fs.Path = .join(.{ port_root.name(), "info" });
- const info_res = conf.Config.from_file(ctx.env.allocator, info_file.name()) catch {
+ const info_res = conf.Config.from_file(ctx.env.allocator, &info_path) catch {
port_corrupted(port_name, "invalid info file");
};
const info_conf = switch (info_res) {
@@ -88,13 +88,11 @@ pub const Info = struct {
};
}
- fn to_file(self: Info) !void {
- const path: fs.Path = .join(.{ self.root.name(), "info" });
- const file = try std.fs.createFileAbsoluteZ(path.name(), .{ .mode = 0o644 });
- defer file.close();
-
+ fn to_file(self: Info, path: *const fs.Path) !void {
var write_buf: [256]u8 = undefined;
- var writer = file.writer(&write_buf);
+ var writer = try fs.file_writer(path, &write_buf, .{ .create = true });
+ defer writer.deinit();
+
inline for (&[_]conf.Var{
.{ .key = "category", .val = self.category },
.{ .key = "template", .val = self.template },
@@ -105,6 +103,10 @@ pub const Info = struct {
try writer.interface.flush();
}
+ fn info_file(self: Info) fs.Path {
+ return .join(.{ self.root.name(), "info" });
+ }
+
fn version_file(self: Info) fs.Path {
return .join(.{ self.root.name(), "version" });
}
@@ -139,10 +141,10 @@ pub const Version = packed struct {
var major: []const u8 = text;
var minor: []const u8 = "";
var patch: []const u8 = "";
- if (std.mem.indexOfScalar(u8, major, '.')) |dot1| {
+ if (std.mem.findScalar(u8, major, '.')) |dot1| {
minor = major[dot1 + 1 ..];
major = major[0..dot1];
- if (std.mem.indexOfScalar(u8, minor, '.')) |dot2| {
+ if (std.mem.findScalar(u8, minor, '.')) |dot2| {
patch = minor[dot2 + 1 ..];
minor = minor[0..dot2];
}
@@ -155,63 +157,46 @@ pub const Version = packed struct {
};
}
- /// Parse the version as it is written to a file: 0.0.0[.d00000000[.r0]]
+ /// Parse the version as it is written to a file: 0.0.0[.00000000r0]
pub fn parse(text: []const u8) !Version {
- const dot1 = std.mem.indexOfScalar(u8, text, '.') orelse return error.invalid_version;
- const dot2 = std.mem.indexOfScalarPos(u8, text, dot1 + 1, '.') orelse return error.invalid_version;
- const dot3 = std.mem.indexOfScalarPos(u8, text, dot2 + 1, '.') orelse text.len;
+ const minor_pos = std.mem.findScalar(u8, text, '.') orelse return error.invalid_version;
+ const patch_pos = std.mem.findScalarPos(u8, text, minor_pos + 1, '.') orelse return error.invalid_version;
+ const date_pos = std.mem.findScalarPos(u8, text, patch_pos + 1, '.') orelse text.len;
- const major = text[0..dot1];
- const minor = text[dot1 + 1 .. dot2];
- const patch = text[dot2 + 1 .. dot3];
- var date: ?[]const u8 = null;
- var revision: ?[]const u8 = null;
- if (dot3 < text.len) {
- if (dot3 + 1 == text.len or text[dot3 + 1] != 'd') return error.invalid_version;
- date = text[dot3 + 2 ..];
- if (std.mem.indexOfScalarPos(u8, text, dot3 + 2, '.')) |dot4| {
- if (dot4 + 1 == text.len or text[dot4 + 1] != 'r') return error.invalid_version;
- date = text[dot3 + 2 .. dot4];
- revision = text[dot4 + 2 ..];
- }
+ const major = parse_int(u16, text[0..minor_pos]) orelse return error.invalid_version;
+ const minor = parse_int(u16, text[minor_pos + 1 .. patch_pos]) orelse return error.invalid_version;
+ const patch = parse_int(u32, text[patch_pos + 1 .. date_pos]) orelse return error.invalid_version;
+ var date: u32 = 0;
+ var revision: u32 = 0;
+ if (date_pos < text.len) {
+ const rev_pos = std.mem.findScalarPos(u8, text, date_pos + 1, 'r') orelse return error.invalid_version;
+ date = parse_int(u32, text[date_pos + 1 .. rev_pos]) orelse return error.invalid_version;
+ revision = parse_int(u32, text[rev_pos + 1 ..]) orelse return error.invalid_version;
}
return .{
- .major = parse_int(u16, major) orelse return error.invalid_version,
- .minor = parse_int(u16, minor) orelse return error.invalid_version,
- .patch = parse_int(u32, patch) orelse return error.invalid_version,
- .date = if (date) |d| parse_int(u32, d) orelse return error.invalid_version else 0,
- .revision = if (revision) |r| parse_int(u32, r) orelse return error.invalid_version else 0,
+ .major = major,
+ .minor = minor,
+ .patch = patch,
+ .date = date,
+ .revision = revision,
};
}
fn from_file(ctx: Ctx, info: Info) !Version {
- const text = try fs.read_file(ctx.env.allocator, info.version_file()) orelse return error.missing_version;
+ const version_file = info.version_file();
+ const text = fs.read_file(ctx.env.allocator, &version_file) catch |err| {
+ return if (err == error.FileNotFound) error.missing_version else err;
+ };
defer ctx.env.dealloc(text);
-
- const s = std.mem.trim(u8, text, " \t\n");
-
- var major: []const u8 = s;
- var minor: []const u8 = "";
- var patch: []const u8 = "";
- if (std.mem.indexOfScalar(u8, major, '.')) |dot1| {
- minor = major[dot1 + 1 ..];
- major = major[0..dot1];
- if (std.mem.indexOfScalar(u8, minor, '.')) |dot2| {
- patch = minor[dot2 + 1 ..];
- minor = minor[0..dot2];
- }
- }
-
return try parse(std.mem.trim(u8, text, " \t\n"));
}
- fn to_file(self: Version, path: fs.Path) !void {
- const file = try std.fs.createFileAbsoluteZ(path.name(), .{ .mode = 0o644 });
- defer file.close();
-
+ fn to_file(self: Version, path: *const fs.Path) !void {
var write_buf: [64]u8 = undefined;
- var writer = file.writer(&write_buf);
+ var writer = try fs.file_writer(path, &write_buf, .{ .create = true });
+ defer writer.deinit();
+
try self.format(&writer.interface);
try writer.interface.writeByte('\n');
try writer.interface.flush();
@@ -224,16 +209,17 @@ pub const Version = packed struct {
return str;
}
- pub fn format(self: Version, w: *std.io.Writer) std.io.Writer.Error!void {
+ pub fn format(self: Version, w: *std.Io.Writer) std.Io.Writer.Error!void {
try w.print("{}.{}.{}", .{ self.major, self.minor, self.patch });
if (self.date != 0) {
- try w.print(".d{}", .{self.date});
- if (self.revision != 0) {
- try w.print(".r{}", .{self.revision});
- }
+ try w.print(".{}r{}", .{ self.date, self.revision });
}
}
+ fn eq(left: Version, right: Version) bool {
+ return @as(u128, @bitCast(left)) == @as(u128, @bitCast(right));
+ }
+
fn less(left: Version, right: Version) bool {
return @as(u128, @bitCast(left)) < @as(u128, @bitCast(right));
}
@@ -255,7 +241,7 @@ pub const Source = struct {
fn from_file(ctx: Ctx, info: Info) !Source {
const src_file = info.src_file();
- const raw_res = try conf.Config.from_file(ctx.env.allocator, src_file.name());
+ const raw_res = try conf.Config.from_file(ctx.env.allocator, &src_file);
const raw = switch (raw_res) {
.conf => |c| c,
.err => port_corrupted(info.name, "invalid source file"),
@@ -289,16 +275,15 @@ pub const Source = struct {
};
}
- fn to_file(self: Source, path: fs.Path) !void {
+ fn to_file(self: Source, path: *const fs.Path) !void {
var commit_buf: [64]u8 = undefined;
- var commit_str: std.io.Writer = .fixed(&commit_buf);
+ var commit_str: std.Io.Writer = .fixed(&commit_buf);
try commit_str.print("{f}", .{self.commit});
- const file = try std.fs.createFileAbsoluteZ(path.name(), .{ .mode = 0o644 });
- defer file.close();
-
var write_buf: [256]u8 = undefined;
- var writer = file.writer(&write_buf);
+ var writer = try fs.file_writer(path, &write_buf, .{ .create = true });
+ defer writer.deinit();
+
inline for (&[_]conf.Var{
.{ .key = "repo", .val = self.repo.url },
.{ .key = "branch", .val = self.repo.branch },
@@ -317,12 +302,11 @@ pub const Source = struct {
pub const Iterator = struct {
ctx: Ctx,
- dir: ?std.fs.Dir = null,
- iter: ?std.fs.Dir.Iterator = null,
+ iter: ?fs.Iterator = null,
current: ?Info = null,
pub fn deinit(self: *Iterator) void {
- if (self.dir) |*dir| dir.close();
+ if (self.iter) |*it| it.deinit();
if (self.current) |p| p.deinit();
}
@@ -333,7 +317,7 @@ pub const Iterator = struct {
}
if (self.iter) |*iter| {
while (try iter.next()) |entry| {
- if (entry.kind == .directory) {
+ if (entry.type == .dir) {
self.current = try .from_file(self.ctx, entry.name);
return self.current;
}
@@ -344,17 +328,14 @@ pub const Iterator = struct {
};
pub fn iterate(ctx: Ctx) !Iterator {
- const dir = std.fs.openDirAbsoluteZ(ctx.tree.root.name(), .{ .iterate = true }) catch |err| {
- if (err == error.FileNotFound) {
- return .{ .ctx = ctx };
- }
+ const iter = fs.iterate(&ctx.tree.root) catch |err| {
+ if (err == error.FileNotFound) return .{ .ctx = ctx };
return err;
};
return .{
.ctx = ctx,
- .dir = dir,
- .iter = dir.iterate(),
+ .iter = iter,
};
}
@@ -384,7 +365,7 @@ pub fn read_source(ctx: Ctx, info: Info) !Source {
pub fn read_vars(ctx: Ctx, info: Info) !conf.Config {
const vars_file = info.vars_file();
- const conf_res = try conf.Config.from_file(ctx.env.allocator, vars_file.name());
+ const conf_res = try conf.Config.from_file(ctx.env.allocator, &vars_file);
return switch (conf_res) {
.conf => |c| c,
.err => |e| {
@@ -407,8 +388,9 @@ pub fn create(ctx: Ctx, info: Info, opts: Create_Options) !void {
.branch = opts.repo_branch,
};
+ ctx.env.info("===> cloning repository: {s}", .{repo.path.name()});
try repo.clone(ctx.env);
- errdefer fs.rm(repo.path) catch {};
+ errdefer fs.rm(&repo.path) catch {};
const source: Source = .{
.raw = undefined,
@@ -417,23 +399,30 @@ pub fn create(ctx: Ctx, info: Info, opts: Create_Options) !void {
};
const port_root = ctx.tree.port_path(info.name);
- try fs.mkdir_all(port_root);
- try info.to_file();
- try opts.initial_version.to_file(info.version_file());
- try source.to_file(info.src_file());
+ const info_file = info.info_file();
+ const version_file = info.version_file();
+ const src_file = info.src_file();
+
+ try fs.mkdir_all(&port_root);
+ ctx.env.info("===> writing port data: {s}", .{info.root.name()});
+ try info.to_file(&info_file);
+ try opts.initial_version.to_file(&version_file);
+ try source.to_file(&src_file);
}
pub fn edit_vars(ctx: Ctx, port_name: []const u8) !void {
const info = try must_get(ctx, port_name);
defer info.deinit();
- try shell.edit_config_file_noverify(ctx.env, info.vars_file());
+
+ const vars_file = info.vars_file();
+ try shell.edit_config_file_noverify(ctx.env, &vars_file);
}
-pub fn import_vars(ctx: Ctx, port_name: []const u8, new_vars_file: fs.Path) !void {
+pub fn import_vars(ctx: Ctx, port_name: []const u8, new_vars_file: *const fs.Path) !void {
const info = try must_get(ctx, port_name);
defer info.deinit();
- const parsed = try conf.Config.from_file(ctx.env.allocator, new_vars_file.name());
+ const parsed = try conf.Config.from_file(ctx.env.allocator, new_vars_file);
switch (parsed) {
.conf => |c| c.deinit(),
.err => |e| {
@@ -441,7 +430,8 @@ pub fn import_vars(ctx: Ctx, port_name: []const u8, new_vars_file: fs.Path) !voi
},
}
- try fs.mv_file(new_vars_file, info.vars_file());
+ const vars_file = info.vars_file();
+ try fs.mv_file(new_vars_file, &vars_file);
}
pub const Update_Options = struct {
@@ -456,6 +446,7 @@ pub fn update_version(ctx: Ctx, port_name: []const u8, options: Update_Options)
var source: Source = try .from_file(ctx, info);
defer source.deinit();
+ ctx.env.info("===> {s}: updating repository", .{info.name});
const old_version: Version = try .from_file(ctx, info);
const old_commit = source.commit;
const new_commit = try source.repo.update(ctx.env);
@@ -475,13 +466,17 @@ pub fn update_version(ctx: Ctx, port_name: []const u8, options: Update_Options)
}
source.commit = new_commit;
- try source.to_file(info.src_file());
- try new_version.to_file(info.version_file());
+
+ ctx.env.info("===> {s}: updating port data", .{info.name});
+ const src_file = info.src_file();
+ const version_file = info.version_file();
+ try source.to_file(&src_file);
+ try new_version.to_file(&version_file);
try render_port(ctx, .{
.info = info,
.version = new_version,
- .source = source,
+ .source = &source,
});
return new_version;
}
@@ -504,12 +499,13 @@ pub fn bump_version(ctx: Ctx, port_name: []const u8, options: Bump_Options) !Ver
.minor => .{ .major = version.major, .minor = version.minor + 1, .patch = 0 },
.patch => .{ .major = version.major, .minor = version.minor, .patch = version.patch + 1 },
};
- try new_version.to_file(info.version_file());
+ const version_file = info.version_file();
+ try new_version.to_file(&version_file);
try render_port(ctx, .{
.info = info,
.version = new_version,
- .source = source,
+ .source = &source,
});
return new_version;
}
@@ -520,23 +516,22 @@ pub const Distfile = struct {
};
pub fn list_distfiles(ctx: Ctx, port_name: []const u8) !std.ArrayList(Distfile) {
- const dist_dir = ctx.env.ports_dist_dir(ctx.tree.name, port_name);
- return try list_distfiles_sorted(ctx.env.allocator, port_name, dist_dir);
+ const distdir = ctx.env.ports_dist_dir(ctx.tree.name, port_name);
+ return try fetch_port_distfiles_sorted(ctx.env.allocator, port_name, &distdir);
}
pub fn rollback(ctx: Ctx, port_name: []const u8, old_version: Version) !void {
const info = try must_get(ctx, port_name);
defer info.deinit();
- var version_buf: [64]u8 = undefined;
- const version_str = std.fmt.bufPrint(&version_buf, "{f}", .{old_version}) catch unreachable;
-
- var distname_buf: [std.fs.max_name_bytes]u8 = undefined;
- const distname = assemble_distname(&distname_buf, info.name, version_str);
- var dist_file = ctx.env.ports_dist_dir(ctx.tree.name, info.name);
- dist_file.append(.{distname});
- if (!try fs.file_exists(dist_file)) {
- fatal("distfile {s} cannot be found", .{distname});
+ const distdir = ctx.env.ports_dist_dir(ctx.tree.name, port_name);
+ var distfiles = try fetch_distfiles(ctx.env.allocator, &distdir, .{
+ .port_name = port_name,
+ .version = old_version,
+ });
+ defer distfiles.deinit(ctx.env.allocator);
+ if (distfiles.items.len == 0) {
+ fatal("no distfiles found for port {s}", .{port_name});
}
try render_port(ctx, .{
@@ -553,19 +548,14 @@ pub fn remove(ctx: Ctx, port_name: []const u8) !bool {
}
pub inline fn remove_all(ctx: Ctx) !void {
- var dir = std.fs.openDirAbsoluteZ(ctx.tree.root.name(), .{ .iterate = true }) catch |err| {
- return switch (err) {
- error.FileNotFound => return,
- error.BadPathName, error.InvalidUtf8 => error.BadPathName,
- error.NameTooLong => unreachable,
- else => err,
- };
+ var iter = fs.iterate(&ctx.tree.root) catch |err| {
+ if (err == error.FileNotFound) return;
+ return err;
};
- defer dir.close();
+ defer iter.deinit();
- var iter = dir.iterate();
while (try iter.next()) |entry| {
- if (entry.kind == .directory) {
+ if (entry.type == .dir) {
if (try get(ctx, entry.name)) |p| {
try remove_existing_port(ctx, p);
}
@@ -577,16 +567,17 @@ fn remove_existing_port(ctx: Ctx, info: Info) !void {
const port_dir = ctx.env.ports_tree_path(ctx.tree.name, .{ info.category, info.name });
const dist_dir = ctx.env.ports_dist_dir(ctx.tree.name, info.name);
const src_dir = ctx.env.ports_src_path(ctx.tree.name, .{info.name});
- try fs.rm(port_dir);
- try fs.rm(dist_dir);
- try fs.rm(src_dir);
- try fs.rm(ctx.tree.port_path(info.name));
+ const port_path = ctx.tree.port_path(info.name);
+ try fs.rm(&port_dir);
+ try fs.rm(&dist_dir);
+ try fs.rm(&src_dir);
+ try fs.rm(&port_path);
}
const Render_Options = struct {
info: Info,
version: Version,
- source: ?Source, // null if it already exists
+ source: ?*const Source, // null if it already exists
};
fn render_port(ctx: Ctx, opts: Render_Options) !void {
@@ -600,23 +591,19 @@ fn render_port(ctx: Ctx, opts: Render_Options) !void {
var version_buf: [64]u8 = undefined;
const version_str = std.fmt.bufPrint(&version_buf, "{f}", .{opts.version}) catch unreachable;
- var distname_buf: [std.fs.max_name_bytes]u8 = undefined;
- const distname = assemble_distname(&distname_buf, opts.info.name, version_str);
-
// While generating the port files we write to the following working directories:
//
// * port files: <wrk>/<tree>/ports/<port>/*
- // * dist file: <wrk>/<tree>/dists/<distname>
+ // * dist files: <wrk>/<tree>/dists/*
//
const port_dir = ctx.env.ports_tree_path(ctx.tree.name, .{ opts.info.category, opts.info.name });
const dist_dir = ctx.env.ports_dist_dir(ctx.tree.name, opts.info.name);
- const dist_file: fs.Path = .join(.{ dist_dir.name(), distname });
const wrk_port_dir = ctx.env.work_path(.{ ctx.tree.name, "ports", opts.info.name });
- const wrk_dist_file = ctx.env.work_path(.{ ctx.tree.name, "dist", distname });
- try fs.rm(wrk_port_dir);
- try fs.rm(wrk_dist_file.dir().?);
- try fs.mkdir_all(wrk_port_dir);
- try fs.mkdir_all(wrk_dist_file.dir().?);
+ const wrk_dist_dir = ctx.env.work_path(.{ ctx.tree.name, "dist" });
+ try fs.rm(&wrk_port_dir);
+ try fs.rm(&wrk_dist_dir);
+ try fs.mkdir_all(&wrk_port_dir);
+ try fs.mkdir_all(&wrk_dist_dir);
const template: tmpl.Template = .init(ctx.env, opts.info.template);
const template_conf = try tmpl.read_config(ctx.env, template);
@@ -631,125 +618,230 @@ fn render_port(ctx: Ctx, opts: Render_Options) !void {
try predefined_vars.add_var(".portversion", version_str);
try predefined_vars.add_var(".category", opts.info.category);
try predefined_vars.add_var(".distdir", dist_dir.name());
- try predefined_vars.add_var(".distname", distname);
+ for (template_conf.archives[0..template_conf.archives_len]) |*archive| {
+ var distname_buf: [std.fs.max_name_bytes]u8 = undefined;
+ const distname = assemble_distname(&distname_buf, .{
+ .dist_id = archive.id,
+ .port_name = opts.info.name,
+ .version = version_str,
+ });
+ if (archive.id.len > 0) {
+ var key_buf: [512]u8 = undefined;
+ try predefined_vars.add_var(concat(&key_buf, &.{ ".distname.", archive.id }), distname);
+ } else {
+ try predefined_vars.add_var(".distname", distname);
+ }
+ }
- var make_portsdir_buf: [std.fs.max_path_bytes]u8 = undefined;
- var make_distdir_buf: [std.fs.max_path_bytes]u8 = undefined;
+ var make_portsdir_buf: [fs.max_path_bytes]u8 = undefined;
+ var make_distdir_buf: [fs.max_path_bytes]u8 = undefined;
const make_portsdir = concat(&make_portsdir_buf, &.{ "PORTSDIR=", ctx.env.ports_dir });
const make_distdir = concat(&make_distdir_buf, &.{ "DISTDIR=", dist_dir.name() });
ctx.env.info("===> {s}: writing port files", .{opts.info.name});
const vars = [_]conf.Config{ predefined_vars, port_vars };
- try template.render(ctx.env, &vars, wrk_port_dir);
- try fs.mv_dir(ctx.env.allocator, wrk_port_dir, port_dir);
+ try template.render(ctx.env, &vars, &wrk_port_dir);
+ try fs.mv_dir(&wrk_port_dir, &port_dir);
+ var new_distfiles_created = false;
if (opts.source) |src| {
var prepared_dist = false;
- if (template_conf.prepare_dist_target) |prepare_dist_target| {
+ defer {
+ if (prepared_dist) src.repo.reset(ctx.env);
+ }
+
+ if (template_conf.prepare_target) |prepare_target| {
ctx.env.info("===> {s}: prepare distribution", .{opts.info.name});
var make_target_buf: [std.fs.max_name_bytes]u8 = undefined;
const res = shell.exec(.{
.env = ctx.env,
- .argv = &.{ "make", "-V", concat(&make_target_buf, &.{ "${.ALLTARGETS:M", prepare_dist_target, "}" }), make_portsdir },
- .cwd = port_dir,
+ .argv = &.{ "make", "-V", concat(&make_target_buf, &.{ "${.ALLTARGETS:M", prepare_target, "}" }), make_portsdir },
+ .cwd = &port_dir,
});
const has_target = switch (res) {
.success => |out| has: {
- defer ctx.env.dealloc(out);
- const target_found = std.mem.trim(u8, out, " \t\n");
- break :has std.mem.eql(u8, target_found, prepare_dist_target);
+ const output = out orelse break :has false;
+ defer ctx.env.dealloc(output);
+ const target_found = std.mem.trim(u8, output, " \t\n");
+ break :has std.mem.eql(u8, target_found, prepare_target);
},
.failure => |out| {
- ctx.env.err("failed to check for makefile target `{s}`\n", .{prepare_dist_target});
- fatal("{s}", .{out});
+ ctx.env.err("failed to check for makefile target `{s}`\n", .{prepare_target});
+ fatal("{s}", .{out orelse ""});
},
};
if (has_target) {
- var make_wrksrc_buf: [std.fs.max_path_bytes]u8 = undefined;
+ var make_wrksrc_buf: [fs.max_path_bytes]u8 = undefined;
const make_wrksrc = concat(&make_wrksrc_buf, &.{ "WRKSRC=", src.repo.path.name() });
shell.run(.{
.env = ctx.env,
- .argv = &.{ "make", make_portsdir, make_distdir, make_wrksrc, prepare_dist_target },
- .cwd = port_dir,
+ .argv = &.{ "make", make_portsdir, make_distdir, make_wrksrc, prepare_target },
+ .cwd = &port_dir,
});
prepared_dist = true;
} else {
- ctx.env.info("Target `{s}` not found (skipping)", .{prepare_dist_target});
+ ctx.env.info("Target `{s}` not found (skipping)", .{prepare_target});
}
}
- ctx.env.info("===> {s}: creating distfiles", .{opts.info.name});
- var tar_rename_buf: [std.fs.max_path_bytes]u8 = undefined;
- const tar_rename = concat(&tar_rename_buf, &.{ ",^,", distname[0 .. distname.len - distname_ext.len], "/," });
- shell.run(.{
- .env = ctx.env,
- .argv = &.{ "tar", "-czf", wrk_dist_file.name(), "-C", src.repo.path.name(), "--exclude-vcs", "-s", tar_rename, "." },
- });
- if (prepared_dist) src.repo.reset(ctx.env);
- try fs.mv_file(wrk_dist_file, dist_file);
- ctx.env.info("distfile: {s}", .{dist_file.name()});
+ for (template_conf.archives[0..template_conf.archives_len]) |*archive| {
+ var distname_buf: [std.fs.max_name_bytes]u8 = undefined;
+ const distname = assemble_distname(&distname_buf, .{
+ .dist_id = archive.id,
+ .port_name = opts.info.name,
+ .version = version_str,
+ });
+ const wrk_dist_file: fs.Path = .join(.{ wrk_dist_dir.name(), distname });
+ const dist_file: fs.Path = .join(.{ dist_dir.name(), distname });
+
+ if (archive.id.len > 0) {
+ ctx.env.info("===> {s}: creating distfile [{s}]", .{ opts.info.name, archive.id });
+ } else {
+ ctx.env.info("===> {s}: creating distfile", .{opts.info.name});
+ }
+ var tar_rename_buf: [fs.max_path_bytes]u8 = undefined;
+ const tar_rename = concat(&tar_rename_buf, &.{ ",^,", distname[0 .. distname.len - distname_ext.len], "/," });
+
+ var root = src.repo.path;
+ if (archive.root) |archive_root| root.append(.{archive_root});
+
+ var tar_cmd: [8 + archive.paths.len][]const u8 = undefined;
+ var tar_cmd_len: usize = 8;
+ tar_cmd[0] = "tar";
+ tar_cmd[1] = "-czf";
+ tar_cmd[2] = wrk_dist_file.name();
+ tar_cmd[3] = "-C";
+ tar_cmd[4] = root.name();
+ tar_cmd[5] = "-s";
+ tar_cmd[6] = tar_rename;
+ tar_cmd[7] = "--exclude-vcs";
+ if (archive.paths_len == 0) {
+ tar_cmd[tar_cmd_len] = ".";
+ tar_cmd_len += 1;
+ } else {
+ var existings_paths: usize = 0;
+ for (archive.paths[0..archive.paths_len]) |path| {
+ const abs_path: fs.Path = .join(.{ root.name(), path });
+ if (fs.path_exists(&abs_path)) {
+ tar_cmd[tar_cmd_len] = path;
+ tar_cmd_len += 1;
+ existings_paths += 1;
+ } else {
+ ctx.env.warn("cannot find path in archive root : {s}", .{path});
+ }
+ }
+ if (existings_paths == 0) {
+ tar_cmd[tar_cmd_len] = "--files-from";
+ tar_cmd_len += 1;
+ tar_cmd[tar_cmd_len] = "/dev/null";
+ tar_cmd_len += 1;
+ }
+ }
+
+ shell.run(.{
+ .env = ctx.env,
+ .argv = tar_cmd[0..tar_cmd_len],
+ });
+
+ try fs.mv_file(&wrk_dist_file, &dist_file);
+ ctx.env.info("distfile: {s}", .{dist_file.name()});
+ new_distfiles_created = true;
+ }
}
- ctx.env.info("===> {s}: creating distinfo", .{opts.info.name});
+ ctx.env.info("===> {s}: creating port's distinfo", .{opts.info.name});
shell.run(.{
.env = ctx.env,
.argv = &.{ "make", make_portsdir, make_distdir, "makesum" },
- .cwd = port_dir,
+ .cwd = &port_dir,
});
- if (opts.source != null) {
+ if (new_distfiles_created) {
ctx.env.info("===> {s}: cleaning up old distfiles", .{opts.info.name});
- var history = list_distfiles_sorted(ctx.env.allocator, opts.info.name, dist_dir) catch |err| {
- ctx.env.warn("cannot remove old distfiles from {s} ({s})", .{ dist_dir.name(), @errorName(err) });
+ var history = fetch_port_distfiles_sorted(ctx.env.allocator, opts.info.name, &dist_dir) catch |err| {
+ ctx.env.warn("cannot fetch distfiles from {s} ({s})", .{ dist_dir.name(), @errorName(err) });
return;
};
defer history.deinit(ctx.env.allocator);
-
- if (history.items.len > ctx.env.distfiles_history) {
- var to_delete: usize = history.items.len - ctx.env.distfiles_history;
- var i: usize = history.items.len;
- while (i > 0 and to_delete > 0) {
- i -= 1;
- const dist: *const Distfile = &history.items[i];
- if (!std.mem.endsWith(u8, dist.path.name(), distname)) {
- fs.rm(dist.path) catch |err| {
- ctx.env.warn("cannot remove {s} ({s})", .{ dist.path.name(), @errorName(err) });
+ if (history.items.len > 0) {
+ var version = history.items[0].version;
+ var visited: usize = 1;
+ for (history.items[1..]) |*distfile| {
+ if (!Version.eq(distfile.version, version)) {
+ visited += 1;
+ version = distfile.version;
+ }
+ if (visited > ctx.env.distfiles_history) {
+ fs.rm(&distfile.path) catch |err| {
+ ctx.env.warn("cannot remove {s} ({s})", .{ distfile.path.name(), @errorName(err) });
};
- to_delete -= 1;
}
}
}
}
}
-/// Get a list of all distfiles sorted from new to old.
-fn list_distfiles_sorted(allocator: std.mem.Allocator, port_name: []const u8, dist_dir: fs.Path) !std.ArrayList(Distfile) {
- var iter = try fs.iterate_files(dist_dir) orelse return .empty;
+/// Get a list of all distfiles of a single port sorted from new to old.
+fn fetch_port_distfiles_sorted(allocator: std.mem.Allocator, port_name: []const u8, dist_dir: *const fs.Path) !std.ArrayList(Distfile) {
+ const distfiles = try fetch_distfiles(allocator, dist_dir, .{ .port_name = port_name });
+ std.mem.sortUnstable(Distfile, distfiles.items, {}, struct {
+ pub fn gt(_: void, left: Distfile, right: Distfile) bool {
+ return if (Version.eq(right.version, left.version))
+ std.mem.lessThan(u8, right.path.name(), left.path.name())
+ else
+ Version.less(right.version, left.version);
+ }
+ }.gt);
+ return distfiles;
+}
+
+const Distfile_Filter = struct {
+ port_name: []const u8,
+ version: ?Version = null,
+};
+
+fn fetch_distfiles(allocator: std.mem.Allocator, dist_dir: *const fs.Path, filter: Distfile_Filter) !std.ArrayList(Distfile) {
+ var iter = fs.iterate(dist_dir) catch |err| {
+ if (err == error.FileNotFound) return .empty;
+ return err;
+ };
defer iter.deinit();
var distfiles: std.ArrayList(Distfile) = .empty;
- while (iter.next()) |distfile| {
- if (std.mem.startsWith(u8, distfile, port_name) and std.mem.endsWith(u8, distfile, distname_ext)) {
- const version = Version.parse(distfile[port_name.len + 1 .. distfile.len - distname_ext.len]) catch continue;
+ while (try iter.next()) |entry| {
+ if (entry.type != .file) continue;
+
+ const distfile = entry.name;
+ if (std.mem.startsWith(u8, distfile, filter.port_name) and std.mem.endsWith(u8, distfile, distname_ext)) {
+ const last_dash = std.mem.lastIndexOfScalar(u8, distfile, '-') orelse continue;
+ const version = Version.parse(distfile[last_dash + 1 .. distfile.len - distname_ext.len]) catch continue;
+ if (filter.version) |expected_version| {
+ if (!Version.eq(expected_version, version)) {
+ continue;
+ }
+ }
try distfiles.append(allocator, .{
.version = version,
.path = .join(.{ dist_dir.name(), distfile }),
});
}
}
-
- std.mem.sortUnstable(Distfile, distfiles.items, {}, struct {
- pub fn gt(_: void, left: Distfile, right: Distfile) bool {
- return Version.less(right.version, left.version);
- }
- }.gt);
return distfiles;
}
+const Distname_Parts = struct {
+ dist_id: []const u8,
+ port_name: []const u8,
+ version: []const u8,
+};
+
/// Build a distname that matches FreeBSD's default value for ${DISTNAME}.
-fn assemble_distname(buf: []u8, port_name: []const u8, version: []const u8) []const u8 {
- return concat(buf, &.{ port_name, "-", version, distname_ext });
+fn assemble_distname(buf: []u8, parts: Distname_Parts) []const u8 {
+ return if (parts.dist_id.len > 0)
+ concat(buf, &.{ parts.port_name, "-", parts.dist_id, "-", parts.version, distname_ext })
+ else
+ return concat(buf, &.{ parts.port_name, "-", parts.version, distname_ext });
}
fn parse_int(comptime Int: type, text: []const u8) ?Int {
@@ -785,19 +877,21 @@ test "parse version" {
try std.testing.expectEqual(@as(u32, 0), version.date);
try std.testing.expectEqual(@as(u32, 0), version.revision);
- version = try .parse("3.2.1.d20180709");
+ version = try .parse("3.2.1.20250709r0");
try std.testing.expectEqual(@as(u16, 3), version.major);
try std.testing.expectEqual(@as(u16, 2), version.minor);
try std.testing.expectEqual(@as(u16, 1), version.patch);
- try std.testing.expectEqual(@as(u32, 20180709), version.date);
+ try std.testing.expectEqual(@as(u32, 20250709), version.date);
try std.testing.expectEqual(@as(u32, 0), version.revision);
- version = try .parse("2.1.3.d20210429.r7");
+ version = try .parse("2.1.3.20250429r7");
try std.testing.expectEqual(@as(u16, 2), version.major);
try std.testing.expectEqual(@as(u16, 1), version.minor);
try std.testing.expectEqual(@as(u16, 3), version.patch);
- try std.testing.expectEqual(@as(u32, 20210429), version.date);
+ try std.testing.expectEqual(@as(u32, 20250429), version.date);
try std.testing.expectEqual(@as(u32, 7), version.revision);
+
+ try std.testing.expectError(error.invalid_version, Version.parse("3.1.2.20250511"));
}
test "compare version" {