Files
ghostty/src/benchmark/cli.zig
Chris Marchesi 048619a6bf global: take minimal instead of juicy main
The early-stage main Zig wrapper recognizes if main only needs the
minimal state (args and lower-level environment) and skips a bunch of
unneeded initialization (allocator, arena, threaded I/O, and the
higher-level environment map). Particularly, the fact that it does not
set up an I/O instance means that we won't have any unneeded signal
handlers set up for the unused threaded I/O implementation, which is
similar in spirit to the fixes we applied for the C VT implementation,
with the notable difference that we do actually set a threaded I/O up in
global state - hence, again, we don't want the duplicate unused one.
2026-07-21 22:05:33 -07:00

111 lines
3.6 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const cli = @import("../cli.zig");
const global = @import("../global.zig");
/// The available actions for the CLI. This is the list of available
/// benchmarks. View docs for each individual one in the predictably
/// named files.
pub const Action = enum {
@"apc-parser",
@"codepoint-width",
@"grapheme-break",
@"hyperlink-map",
@"page-compression",
@"scrollback-compression",
@"screen-clone",
@"terminal-parser",
@"terminal-stream",
@"is-symbol",
@"osc-parser",
/// Returns the struct associated with the action. The struct
/// should have a few decls:
///
/// - `const Options`: The CLI options for the action.
/// - `fn create`: Create a new instance of the action from options.
/// - `fn benchmark`: Returns a `Benchmark` instance for the action.
///
/// See TerminalStream for an example.
pub fn Struct(comptime action: Action) type {
return switch (action) {
.@"apc-parser" => @import("ApcParser.zig"),
.@"hyperlink-map" => @import("HyperlinkMap.zig"),
.@"screen-clone" => @import("ScreenClone.zig"),
.@"page-compression" => @import("PageCompression.zig"),
.@"scrollback-compression" => @import("ScrollbackCompression.zig"),
.@"terminal-stream" => @import("TerminalStream.zig"),
.@"codepoint-width" => @import("CodepointWidth.zig"),
.@"grapheme-break" => @import("GraphemeBreak.zig"),
.@"terminal-parser" => @import("TerminalParser.zig"),
.@"is-symbol" => @import("IsSymbol.zig"),
.@"osc-parser" => @import("OscParser.zig"),
};
}
};
/// An entrypoint for the benchmark CLI.
pub fn main(minimal: std.process.Init.Minimal) !void {
try global.init(.{ .tool = minimal });
const alloc = std.heap.c_allocator;
const action_ = try cli.action.detectArgs(Action, alloc, minimal.args);
const action = action_ orelse return error.NoAction;
try mainAction(alloc, action, .{ .cli = minimal.args });
}
/// Arguments that can be passed to the benchmark.
pub const Args = union(enum) {
/// The arguments passed to the CLI via argc/argv.
cli: std.process.Args,
/// Simple string arguments, parsed via ArgIteratorGeneral.
string: []const u8,
};
pub fn mainAction(
alloc: Allocator,
action: Action,
args: Args,
) !void {
switch (action) {
inline else => |comptime_action| {
const BenchmarkImpl = Action.Struct(comptime_action);
try mainActionImpl(BenchmarkImpl, alloc, args);
},
}
}
fn mainActionImpl(
comptime BenchmarkImpl: type,
alloc: Allocator,
args: Args,
) !void {
// First, parse our CLI options.
const Options = BenchmarkImpl.Options;
var opts: Options = .{};
defer if (@hasDecl(Options, "deinit")) opts.deinit();
switch (args) {
.cli => |process_args| {
var iter = try cli.args.argsIterator(alloc, process_args);
defer iter.deinit();
try cli.args.parse(Options, alloc, &opts, &iter);
},
.string => |str| {
var iter = try std.process.Args.IteratorGeneral(.{}).init(
alloc,
str,
);
defer iter.deinit();
try cli.args.parse(Options, alloc, &opts, &iter);
},
}
// Create our implementation
const impl = try BenchmarkImpl.create(alloc, opts);
defer impl.destroy(alloc);
// Initialize our benchmark
const b = impl.benchmark();
_ = try b.run(.once);
}