diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cli.zig | 2 | ||||
| -rw-r--r-- | src/env.zig | 4 | ||||
| -rw-r--r-- | src/git.zig | 2 | ||||
| -rw-r--r-- | src/main.zig | 16 | ||||
| -rw-r--r-- | src/port.zig | 113 | ||||
| -rw-r--r-- | src/shell.zig | 42 | ||||
| -rw-r--r-- | src/template.zig | 5 |
7 files changed, 127 insertions, 57 deletions
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; |