Getting Started ​
This guide will help you get started with args.zig in your Zig project.
Requirements ​
- Zig 0.16.0 or later
- A Zig project with
build.zigandbuild.zig.zon
Installation ​
Release Installation (Recommended) ​
Install the latest stable release (v0.0.7):
zig fetch --save https://github.com/muhammad-fiaz/args.zig/archive/refs/tags/0.0.7.tar.gzInstall the supported release for zig v0.15 (v0.0.4):
zig fetch --save https://github.com/muhammad-fiaz/args.zig/archive/refs/tags/0.0.4.tar.gzNightly Installation ​
Install the latest development version:
zig fetch --save git+https://github.com/muhammad-fiaz/args.zigConfigure build.zig (Release / Nightly) ​
After running zig fetch --save, your build.zig.zon will have a dependency entry like:
.dependencies = .{
.args = .{
.url = "https://github.com/muhammad-fiaz/args.zig/archive/refs/tags/0.0.7.tar.gz",
.hash = "...",
},
},Now configure your build.zig to import the args module into your executable:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// 1. Add args.zig as a dependency
const args_dep = b.dependency("args", .{
.target = target,
.optimize = optimize,
});
// 2. Create your executable
const exe = b.addExecutable(.{
.name = "my-cli-app",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
// 3. Import the args module into your executable
exe.root_module.addImport("args", args_dep.module("args"));
// 4. Install and optionally add a run step
b.installArtifact(exe);
const run_exe = b.addRunArtifact(exe);
run_exe.step.dependOn(&b.installArtifact(exe).step);
if (b.args) |args| run_exe.addArgs(args);
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_exe.step);
}IMPORTANT
- The dependency name in
b.dependency("args", ...)must match the key inbuild.zig.zon(.args = ...). - The module name passed to
addImport("args", ...)is what you use in your source code:const args = @import("args");. zig fetch --saveautomatically adds the dependency tobuild.zig.zon. You only need to write thebuild.zigcode manually.
Verifying the Installation ​
Create a minimal src/main.zig:
const std = @import("std");
const args = @import("args");
pub fn main(init: std.process.Init) !void {
const allocator = init.arena.allocator();
var parser = try args.ArgumentParser.init(allocator, .{
.name = "my-cli-app",
.version = "1.0.0",
.description = "My CLI application",
});
defer parser.deinit();
try parser.addFlag("verbose", .{ .short = 'v', .help = "Enable verbose output" });
var result = try parser.parseProcess(init);
defer result.deinit();
if (result.getBool("verbose") orelse false) {
std.debug.print("Verbose mode enabled\n", .{});
}
}Run it:
zig build run -- --help
zig build run -- -vConfiguration Presets ​
Choose a preset that matches your environment:
// Development - balanced defaults with colors (default)
var parser = try args.ArgumentParser.init(allocator, .{
.name = "myapp",
.config = args.Config.development(),
});
// CI - no colors, explicit exit on error
var parser2 = try args.ArgumentParser.init(allocator, .{
.name = "myapp",
.config = args.Config.ci(),
});
// Production - minimal overhead, no colors, no update checker
var parser3 = try args.ArgumentParser.init(allocator, .{
.name = "myapp",
.config = args.Config.production(),
});
// Testing - silent errors, permissive parsing
var parser4 = try args.ArgumentParser.init(allocator, .{
.name = "myapp",
.config = args.Config.testing(),
});Your First Parser ​
Create a simple command-line application with the basic API and process initialization context:
const std = @import("std");
const args = @import("args");
pub fn main(init: std.process.Init) !void {
const allocator = init.arena.allocator();
// Create parser
var parser = try args.ArgumentParser.init(allocator, .{
.name = "greet",
.version = "1.0.0",
.description = "A friendly greeting application",
});
defer parser.deinit();
// Add arguments
try parser.addOption("name", .{
.short = 'n',
.help = "Name to greet",
.default = "World",
});
try parser.addFlag("excited", .{
.short = 'e',
.help = "Add excitement to greeting",
});
// Parse arguments from the current process
var result = try parser.parseProcess(init);
defer result.deinit();
// Use the results
const name = result.getString("name") orelse "World";
const excited = result.getBool("excited") orelse false;
if (excited) {
std.debug.print("Hello, {s}!!!\n", .{name});
} else {
std.debug.print("Hello, {s}.\n", .{name});
}
}NOTE
In Zig 0.16, the main function signature changed to pub fn main(init: std.process.Init) !void.
std.process.Init provides access to:
init.arena.allocator()— A scoped arena allocator tied to the process lifetime. Use this as your primary allocator; it is freed automatically on exit.init.minimal.args— The process command-line arguments, exposed through Zig's minimal startup state.init.io— Astd.Ioinstance for the process, suitable for stdin, stdout, stderr, and other I/O work.init.environ_map— The process environment map, which lets args.zig resolve environment-backed options automatically.
Always pass init to parser.parseProcess(init) when you want the parser to use the process args, I/O context, and environment map directly. Use parser.parse(&[_][]const u8{...}) when you need a custom argument list in tests, tools, or examples.
Advanced: Using std.process.Init ​
For applications that need full control over process initialization:
const std = @import("std");
const args = @import("args");
pub fn main(init: std.process.Init) !void {
// Setup allocator (from process init)
const allocator = init.arena.allocator();
// Create parser
var parser = try args.ArgumentParser.init(allocator, .{
.name = "greet",
.version = "1.0.0",
.description = "A friendly greeting application",
});
defer parser.deinit();
// Add arguments
try parser.addOption("name", .{
.short = 'n',
.help = "Name to greet",
.default = "World",
});
try parser.addFlag("excited", .{
.short = 'e',
.help = "Add excitement to greeting",
});
// Parse arguments with full process context
var result = try parser.parseProcess(init);
defer result.deinit();
// Use the results
const name = result.getString("name") orelse "World";
const excited = result.getBool("excited") orelse false;
if (excited) {
std.debug.print("Hello, {s}!!!\n", .{name});
} else {
std.debug.print("Hello, {s}.\n", .{name});
}
}What You Get From std.process.Init ​
- Process lifetime allocation -
init.arena.allocator()is ideal for one-shot CLI programs - Startup arguments -
init.minimal.argsgivesargs.zigthe exact command-line input from Zig's runtime - Process I/O -
init.iokeeps parsing, help text, and related output aligned with the active process streams - Environment variables -
init.environ_mapenables automatic env-var fallback when you configureenv_prefixorenv_var - One-liner parsing -
parser.parseProcess(init)is the simplest and most future-proof path for Zig 0.16+
Running Your App ​
# Default greeting
zig build run
# Output: Hello, World.
# Custom name
zig build run -- --name Alice
# Output: Hello, Alice.
# With excitement
zig build run -- -n Bob -e
# Output: Hello, Bob!!!
# View help
zig build run -- --help
# The help output is formatted for better understanding 🎨Validation Commands ​
Use these commands while developing with args.zig:
# Run test suite for your current host target
zig build test
# Run all maintained examples
zig build run-all-examples
zig build run-update_check
# Cross-target compile checks
zig build -Dtarget=x86_64-windows-gnu
zig build -Dtarget=x86_64-linux-gnu
zig build -Dtarget=aarch64-macosFor full runtime test execution on Linux/macOS targets, run zig build test -Dtarget=... on a matching host or CI runner.
Version Information ​
You can access the library version in your code:
const args = @import("args");
std.debug.print("args.zig version: {s}\n", .{args.VERSION});Enabling Update Checker ​
By default, args.zig update checker is disabled. To enable it:
// Method 1: Global enable
args.enableUpdateCheck();
// Method 2: Per-parser config
var parser = try args.ArgumentParser.init(allocator, .{
.name = "myapp",
.config = .{ .check_for_updates = true },
});See Update Checker Configuration for more details.
Error Handling & Fallbacks ​
args.zig provides multiple levels of error handling to suit different needs:
Automatic Exit on Error (Default) ​
By default, when a required argument is missing or an unknown option is provided, the parser prints a helpful error message and exits gracefully:
var parser = try args.ArgumentParser.init(allocator, .{ .name = "myapp" });
try parser.addOption("input", .{ .required = true });
// If --input is missing, the library prints help and exits with code 1
var result = try parser.parseProcess(init); // never returns on errorThis behavior is controlled by exit_on_error in the config (enabled by default).
Parse-Level Fallback (parseOr / parseProcessOr) ​
Use parseOr or parseProcessOr when you want to handle errors yourself instead of exiting:
// Returns an empty ParseResult instead of propagating the error
var result = parser.parseProcessOr(init, null);
defer result.deinit();
// With a custom error handler
fn onError(err: anyerror, parser: *ArgumentParser) void {
std.debug.print("Parsing failed: {}\n", .{err});
}
var result2 = parser.parseProcessOr(init, onError);
defer result2.deinit();Value-Level Fallback (getOr* Methods) ​
For individual argument values, use the getOr* family of methods to provide inline defaults:
const verbose = result.getOrBool("verbose", false);
const count = result.getOrInt("count", 42);
const output = result.getOrString("output", "default.txt");
const threshold = result.getOrFloat("threshold", 0.5);
const workers = result.getOrUint("workers", 4);Environment Variable Fallback ​
Options can fall back to environment variables automatically:
try parser.addOption("token", .{
.help = "API token",
.env_var = "API_TOKEN", // Falls back to $API_TOKEN if not provided on CLI
});Custom Validator Error Handling ​
Validators return detailed error messages that are integrated with the parser's error output:
try parser.addIntOption("port", .{
.min = 1024,
.max = 65535,
.help = "Port number (1024-65535)",
.required = true,
});Next Steps ​
- Learn about Options and Flags
- Try Declarative Structs for rapid prototyping
- Explore Subcommands
- Configure Environment Variables
- Understand Error Handling & Fallbacks above
- Check the API Reference for full method documentation
