aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortsne <tsne.dev@outlook.com>2026-06-19 13:09:03 +0200
committertsne <tsne.dev@outlook.com>2026-06-21 08:51:45 +0200
commit5ba038c3eb07fc94fc8d6f46b451f552bfc121a4 (patch)
tree4bc57b4616943f5ef014ae2081c3c680a506b6ef
parentupgrade to zig 0.16 (diff)
downloadporteur-5ba038c3eb07fc94fc8d6f46b451f552bfc121a4.tar.gz
minor bugfixes and output formattingHEADmain
-rw-r--r--porteur.conf.sample4
-rw-r--r--src/cli.zig2
-rw-r--r--src/env.zig4
-rw-r--r--src/git.zig2
-rw-r--r--src/main.zig16
-rw-r--r--src/port.zig113
-rw-r--r--src/shell.zig42
-rw-r--r--src/template.zig5
-rw-r--r--template.conf.sample2
9 files changed, 132 insertions, 58 deletions
diff --git a/porteur.conf.sample b/porteur.conf.sample
index 5baedcf..dde3802 100644
--- a/porteur.conf.sample
+++ b/porteur.conf.sample
@@ -31,6 +31,10 @@ category = tsne
# If none is configured, the `main` branch is used as default value.
git-branch = porteur
+# The location of the make.conf file.
+# If none is configured, `/etc/make.conf` will be used as default value.
+make.conf = /etc/make.conf
+
# When editing configuration files or variables, porteur needs to know which
# editor to use. Per default porteur tries to determine the editor from the
# environment. It reads the VISUAL or the EDITOR environment variable (in
diff --git a/src/cli.zig b/src/cli.zig
index 5b43e96..3525896 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -79,6 +79,8 @@ pub fn next_subcommand(comptime Command: type) ?Command.Subcommands {
}
/// Return the next positional argument or `null` if there are no more arguments.
+/// The returned slice is pointing to an internal buffer and is only guaranteed to
+/// be valid until the next call.
pub fn next_positional() ?[]const u8 {
if (current_cmd) |cmd| {
current_cmd = null;
diff --git a/src/env.zig b/src/env.zig
index bb1e8bc..389890f 100644
--- a/src/env.zig
+++ b/src/env.zig
@@ -18,6 +18,7 @@ distfiles_dir: []const u8,
distfiles_history: usize,
default_category: ?[]const u8,
default_repo_branch: []const u8,
+make_conf: ?[]const u8,
editor_cmd: ?[]const u8,
_proc_env: std.process.Environ,
@@ -41,6 +42,7 @@ pub fn init(allocator: std.mem.Allocator, env: std.process.Environ, etc: []const
.distfiles_history = 1,
.default_category = null,
.default_repo_branch = "main",
+ .make_conf = null,
.editor_cmd = null,
._proc_env = env,
@@ -73,6 +75,8 @@ pub fn init(allocator: std.mem.Allocator, env: std.process.Environ, etc: []const
self.default_category = v.val;
} else if (std.mem.eql(u8, v.key, "git-branch") and v.val.len > 0) {
self.default_repo_branch = v.val;
+ } else if (std.mem.eql(u8, v.key, "make.conf") and v.val.len > 0) {
+ self.make_conf = v.val;
} else if (std.mem.eql(u8, v.key, "editor") and v.val.len > 0) {
self.editor_cmd = v.val;
} else {
diff --git a/src/git.zig b/src/git.zig
index fc0d81b..1b49eed 100644
--- a/src/git.zig
+++ b/src/git.zig
@@ -41,7 +41,7 @@ pub const Repo = struct {
pub fn clone(self: Repo, env: *const Env) !void {
const dir = try fs.dir_info(&self.path);
if (!dir.exists or dir.empty) {
- var branch_buf: [std.fs.max_name_bytes]u8 = undefined;
+ var branch_buf: [std.Io.Dir.max_name_bytes]u8 = undefined;
const branch_param = branch_buf[0 .. 9 + self.branch.len];
@memcpy(branch_param[0..9], "--branch=");
@memcpy(branch_param[9..], self.branch);
diff --git a/src/main.zig b/src/main.zig
index 55e7604..e13cea0 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -110,8 +110,10 @@ const Main_Command = struct {
\\ .portversion - Current version of the port
\\ .category - Category of the port
\\ .distdir - Directory containing all distfiles
- \\ .distname - Name of the unnamed distribution file
- \\ .distname.⟨name⟩ - Name of a named distribution file
+ \\ .distname - Name of the unnamed distribution
+ \\ .distname.⟨name⟩ - Name of a named distribution
+ \\ .distfile - Name of the archive containing unnamed distribution
+ \\ .distfile.⟨name⟩ - Name of the archive containing named distribution
,
},
.{
@@ -121,7 +123,7 @@ const Main_Command = struct {
\\the port needs to be built. By default porteur creates a gzipped archive
\\of the whole Git repository and puts it in the configured `distfiles-dir`
\\(see `porteur.conf.sample`). The filename of this archive is provided
- \\in the `.distname` variable and its directory in `.distdir` (see
+ \\in the `.distfile` variable and its directory in `.distdir` (see
\\VARIABLES).
\\
\\This behaviour, however, can be configured. With the `dist.prepare-target`
@@ -996,16 +998,22 @@ const Port_Command = struct {
const opts: port.Update_Options = .{
.force = self.options.force.value_or_default(),
};
+
var count: usize = 0;
+ var need_newlines = false;
while (cli.next_positional()) |port_name| {
- count += 1;
+ if (need_newlines) cli.stderr.write("\n\n", .{});
try update_single_port(ctx, port_name, opts);
+ count += 1;
+ need_newlines = true;
}
if (count == 0) {
var iter = try port.iterate(ctx);
defer iter.deinit();
while (try iter.next()) |p| {
+ if (need_newlines) cli.stderr.write("\n\n", .{});
try update_single_port(ctx, p.name, opts);
+ need_newlines = true;
}
}
}
diff --git a/src/port.zig b/src/port.zig
index a7d53e4..b6e788b 100644
--- a/src/port.zig
+++ b/src/port.zig
@@ -21,7 +21,7 @@ const shell = @import("shell.zig");
const Time = time.Time;
const fatal = cli.fatal;
-const distname_ext = ".tar.gz";
+const distfile_ext = ".tar.gz";
pub const Ctx = struct {
env: *const Env,
@@ -388,7 +388,7 @@ pub fn create(ctx: Ctx, info: Info, opts: Create_Options) !void {
.branch = opts.repo_branch,
};
- ctx.env.info("===> cloning repository: {s}", .{repo.path.name()});
+ ctx.env.info("cloning repository: {s}", .{repo.path.name()});
try repo.clone(ctx.env);
errdefer fs.rm(&repo.path) catch {};
@@ -404,7 +404,7 @@ pub fn create(ctx: Ctx, info: Info, opts: Create_Options) !void {
const src_file = info.src_file();
try fs.mkdir_all(&port_root);
- ctx.env.info("===> writing port data: {s}", .{info.root.name()});
+ 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);
@@ -446,7 +446,6 @@ 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);
@@ -467,7 +466,6 @@ pub fn update_version(ctx: Ctx, port_name: []const u8, options: Update_Options)
source.commit = new_commit;
- 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);
@@ -619,41 +617,45 @@ fn render_port(ctx: Ctx, opts: Render_Options) !void {
try predefined_vars.add_var(".category", opts.info.category);
try predefined_vars.add_var(".distdir", dist_dir.name());
for (template_conf.archives[0..template_conf.archives_len]) |*archive| {
- var distname_buf: [std.fs.max_name_bytes]u8 = undefined;
+ var distname_buf: [std.Io.Dir.max_name_bytes]u8 = undefined;
+ var distfile_buf: [std.Io.Dir.max_name_bytes]u8 = undefined;
const distname = assemble_distname(&distname_buf, .{
.dist_id = archive.id,
.port_name = opts.info.name,
.version = version_str,
});
+ const distfile = concat(&distfile_buf, &.{ distname, distfile_ext });
if (archive.id.len > 0) {
var key_buf: [512]u8 = undefined;
try predefined_vars.add_var(concat(&key_buf, &.{ ".distname.", archive.id }), distname);
+ try predefined_vars.add_var(concat(&key_buf, &.{ ".distfile.", archive.id }), distfile);
} else {
try predefined_vars.add_var(".distname", distname);
+ try predefined_vars.add_var(".distfile", distfile);
}
}
+ var make_makeconf_buf: [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_makeconf = concat(&make_makeconf_buf, &.{ "__MAKE_CONF=", ctx.env.make_conf orelse "/etc/make.conf" });
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});
+ ctx.env.info("=======< {s}: writing port files >=======", .{opts.info.name});
+ ctx.env.info("using template `{s}`", .{template.name});
const vars = [_]conf.Config{ predefined_vars, port_vars };
try template.render(ctx.env, &vars, &wrk_port_dir);
try fs.mv_dir(&wrk_port_dir, &port_dir);
+ ctx.env.info("", .{}); // END OF TASK
var new_distfiles_created = false;
if (opts.source) |src| {
- var prepared_dist = false;
- defer {
- if (prepared_dist) src.repo.reset(ctx.env);
- }
-
+ var dist_prepared = false;
if (template_conf.prepare_target) |prepare_target| {
- ctx.env.info("===> {s}: prepare distribution", .{opts.info.name});
+ ctx.env.info("=======< {s}: prepare distribution >=======", .{opts.info.name});
- var make_target_buf: [std.fs.max_name_bytes]u8 = undefined;
+ var make_target_buf: [std.Io.Dir.max_name_bytes]u8 = undefined;
const res = shell.exec(.{
.env = ctx.env,
.argv = &.{ "make", "-V", concat(&make_target_buf, &.{ "${.ALLTARGETS:M", prepare_target, "}" }), make_portsdir },
@@ -672,36 +674,40 @@ fn render_port(ctx: Ctx, opts: Render_Options) !void {
},
};
if (has_target) {
+ ctx.env.info("executing target `{s}`", .{prepare_target});
var make_wrksrc_buf: [fs.max_path_bytes]u8 = undefined;
const make_wrksrc = concat(&make_wrksrc_buf, &.{ "WRKSRC=", src.repo.path.name() });
- shell.run(.{
+ const succeeded = shell.run(.{
.env = ctx.env,
- .argv = &.{ "make", make_portsdir, make_distdir, make_wrksrc, prepare_target },
+ .argv = &.{ "make", make_makeconf, make_portsdir, make_distdir, make_wrksrc, prepare_target },
.cwd = &port_dir,
});
- prepared_dist = true;
+ if (!succeeded) {
+ src.repo.reset(ctx.env);
+ std.process.exit(1);
+ }
+ dist_prepared = true;
} else {
- ctx.env.info("Target `{s}` not found (skipping)", .{prepare_target});
+ ctx.env.info("target `{s}` not found (skipping)", .{prepare_target});
}
+ ctx.env.info("", .{}); // END OF TASK
}
+ ctx.env.info("=======< {s}: creating distfiles >=======", .{opts.info.name});
for (template_conf.archives[0..template_conf.archives_len]) |*archive| {
- var distname_buf: [std.fs.max_name_bytes]u8 = undefined;
+ var distname_buf: [std.Io.Dir.max_name_bytes]u8 = undefined;
+ var distfile_buf: [std.Io.Dir.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 });
+ const distfile = concat(&distfile_buf, &.{ distname, distfile_ext });
+ const wrk_distfile: fs.Path = .join(.{ wrk_dist_dir.name(), distfile });
+ const tgt_distfile: fs.Path = .join(.{ dist_dir.name(), distfile });
- 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], "/," });
+ const tar_rename = concat(&tar_rename_buf, &.{ ",^,", distname, "/," });
var root = src.repo.path;
if (archive.root) |archive_root| root.append(.{archive_root});
@@ -710,7 +716,7 @@ fn render_port(ctx: Ctx, opts: Render_Options) !void {
var tar_cmd_len: usize = 8;
tar_cmd[0] = "tar";
tar_cmd[1] = "-czf";
- tar_cmd[2] = wrk_dist_file.name();
+ tar_cmd[2] = wrk_distfile.name();
tar_cmd[3] = "-C";
tar_cmd[4] = root.name();
tar_cmd[5] = "-s";
@@ -728,7 +734,7 @@ fn render_port(ctx: Ctx, opts: Render_Options) !void {
tar_cmd_len += 1;
existings_paths += 1;
} else {
- ctx.env.warn("cannot find path in archive root : {s}", .{path});
+ ctx.env.warn("cannot find path `{s}` in archive root: {s} ", .{ path, root.name() });
}
}
if (existings_paths == 0) {
@@ -739,31 +745,47 @@ fn render_port(ctx: Ctx, opts: Render_Options) !void {
}
}
- shell.run(.{
+ if (archive.id.len > 0) {
+ ctx.env.info("creating distfile [{s}]: {s}", .{ archive.id, tgt_distfile.name() });
+ } else {
+ ctx.env.info("creating distfile: {s}", .{tgt_distfile.name()});
+ }
+
+ const succeeded = shell.run(.{
.env = ctx.env,
.argv = tar_cmd[0..tar_cmd_len],
});
+ if (!succeeded) {
+ if (dist_prepared) src.repo.reset(ctx.env);
+ std.process.exit(1);
+ }
- try fs.mv_file(&wrk_dist_file, &dist_file);
- ctx.env.info("distfile: {s}", .{dist_file.name()});
+ try fs.mv_file(&wrk_distfile, &tgt_distfile);
new_distfiles_created = true;
}
+
+ if (dist_prepared) src.repo.reset(ctx.env);
+ ctx.env.info("", .{}); // END OF TASK
}
- ctx.env.info("===> {s}: creating port's distinfo", .{opts.info.name});
- shell.run(.{
+ ctx.env.info("=======< {s}: creating distinfo >=======", .{opts.info.name});
+ const succeeded = shell.run(.{
.env = ctx.env,
- .argv = &.{ "make", make_portsdir, make_distdir, "makesum" },
+ .argv = &.{ "make", make_makeconf, make_portsdir, make_distdir, "makesum" },
.cwd = &port_dir,
});
+ if (!succeeded) std.process.exit(1);
+ ctx.env.info("", .{}); // END OF TASK
if (new_distfiles_created) {
- ctx.env.info("===> {s}: cleaning up old distfiles", .{opts.info.name});
+ ctx.env.info("=======< {s}: cleaning up old distfiles >=======", .{opts.info.name});
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);
+
+ var removed_files = false;
if (history.items.len > 0) {
var version = history.items[0].version;
var visited: usize = 1;
@@ -773,12 +795,19 @@ fn render_port(ctx: Ctx, opts: Render_Options) !void {
version = distfile.version;
}
if (visited > ctx.env.distfiles_history) {
- fs.rm(&distfile.path) catch |err| {
+ if (fs.rm(&distfile.path)) |_| {
+ ctx.env.info("removed {s}", .{distfile.path.name()});
+ } else |err| {
ctx.env.warn("cannot remove {s} ({s})", .{ distfile.path.name(), @errorName(err) });
- };
+ }
+ removed_files = true;
}
}
}
+ if (!removed_files) {
+ ctx.env.info("nothing to do", .{});
+ }
+ ctx.env.info("", .{}); // END OF TASK
}
}
@@ -813,9 +842,9 @@ fn fetch_distfiles(allocator: std.mem.Allocator, dist_dir: *const fs.Path, filte
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)) {
+ if (std.mem.startsWith(u8, distfile, filter.port_name) and std.mem.endsWith(u8, distfile, distfile_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;
+ const version = Version.parse(distfile[last_dash + 1 .. distfile.len - distfile_ext.len]) catch continue;
if (filter.version) |expected_version| {
if (!Version.eq(expected_version, version)) {
continue;
@@ -839,9 +868,9 @@ const Distname_Parts = struct {
/// Build a distname that matches FreeBSD's default value for ${DISTNAME}.
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 })
+ concat(buf, &.{ parts.port_name, "-", parts.dist_id, "-", parts.version })
else
- return concat(buf, &.{ parts.port_name, "-", parts.version, distname_ext });
+ return concat(buf, &.{ parts.port_name, "-", parts.version });
}
fn parse_int(comptime Int: type, text: []const u8) ?Int {
diff --git a/src/shell.zig b/src/shell.zig
index f933777..be9fb0d 100644
--- a/src/shell.zig
+++ b/src/shell.zig
@@ -6,7 +6,7 @@ const fs = @import("fs.zig");
const conf = @import("conf.zig");
const time = @import("time.zig");
const Env = @import("env.zig");
-const fatal = @import("cli.zig").fatal;
+const fatal = cli.fatal;
pub const Exec_Args = struct {
env: *const Env,
@@ -19,12 +19,27 @@ pub const Exec_Result = union(enum) {
failure: ?[]const u8,
};
+/// Execute a binary and return its result. Both the success and the failure
+/// messages are the stdout and stderr output of the binary and the caller is
+/// responsible to deallocate this output.
pub fn exec(args: Exec_Args) Exec_Result {
return spawn_proc(args, true);
}
-pub fn run(args: Exec_Args) void {
- _ = spawn_proc(args, false);
+/// Run a binary and forward all its output to stdout/stderr directly. It
+/// returns if the execution was successful or not.
+pub fn run(args: Exec_Args) bool {
+ const res = spawn_proc(args, false);
+ switch (res) {
+ .success => |msg| {
+ assert(msg == null);
+ return true;
+ },
+ .failure => |msg| {
+ assert(msg == null);
+ return false;
+ },
+ }
}
pub fn edit_config_file(env: *const Env, file: *const fs.Path, comptime verify: fn (env: *const Env, vars: conf.Config) bool) !void {
@@ -113,12 +128,14 @@ fn exec_editor(env: *const Env, file: *const fs.Path) !void {
.argv = &.{ editor, file.name() },
};
const res = spawn_proc(args, false);
- if (res.failure) |failure| {
- if (failure.len == 0) {
+ switch (res) {
+ .success => |msg| {
+ assert(msg == null);
+ },
+ .failure => |msg| {
+ assert(msg == null);
fatal("failed to open editor", .{});
- } else {
- fatal("{s}", .{failure});
- }
+ },
}
}
@@ -129,7 +146,7 @@ fn tmp_edit_file(env: *const Env, file: *const fs.Path) fs.Path {
const etc = env.etc_path(.{});
const relname = file.relname(&etc).?;
- var tmp_filename_buf: [std.fs.max_name_bytes]u8 = undefined;
+ var tmp_filename_buf: [std.Io.Dir.max_name_bytes]u8 = undefined;
var tmp_filename_len: usize = 0;
tmp_filename_len += std.fmt.printInt(tmp_filename_buf[tmp_filename_len..], time.now(), 16, .lower, .{});
@@ -282,3 +299,10 @@ fn spawn_proc(args: Exec_Args, need_stdout: bool) Exec_Result {
},
}
}
+
+fn without_trailing_newline(s: []const u8) []const u8 {
+ return if (s.len > 0 and s[s.len - 1] == '\n')
+ s[0 .. s.len - 1]
+ else
+ s;
+}
diff --git a/src/template.zig b/src/template.zig
index 89521e3..3e7f530 100644
--- a/src/template.zig
+++ b/src/template.zig
@@ -147,7 +147,10 @@ pub const Template = struct {
try render_files_recursively(env, dest_root, src_root, entry_subpath.name(), tmpl_confs);
},
.file => {
- env.info("writing template file {s}", .{dest_path.name()[dest_root.len + 1 ..]});
+ const filename = dest_path.name()[dest_root.len + 1 ..];
+ if (std.mem.eql(u8, filename, config_filename)) return;
+
+ env.info("writing template file {s}", .{filename});
const input = try fs.read_file(env.allocator, &src_path);
var output_buf: [512]u8 = undefined;
diff --git a/template.conf.sample b/template.conf.sample
index 629b927..bb659a7 100644
--- a/template.conf.sample
+++ b/template.conf.sample
@@ -28,7 +28,7 @@
# tree. If no paths are defined, the whole source tree will be archived.
#
# It is also possible to define a distribution consisting of multiple archives,
-# where each archive needs to have a unique name. This name can be assigned
+# where each archive needs to have a unique name. This name can be achieved
# using the following format: `dist.archive[⟨name⟩].*`. The corresponding
# template variable that references a named archive is `.distname.⟨name⟩`.
# There can be up to 16 different archive files.