Function option [src]

Creates a configuration option to be passed to the build.zig script. When a user directly runs zig build, they can set these options with -D arguments. When a project depends on a Zig package as a dependency, it programmatically sets these options when calling the dependency's build.zig script as a function. null is returned when an option is left to default.

Prototype

pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T

Parameters

b: *BuildT: typename_raw: []const u8description_raw: []const u8

Source

pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { const name = b.dupe(name_raw); const description = b.dupe(description_raw); const type_id = comptime typeToEnum(T); const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: { const EnumType = if (type_id == .enum_list) @typeInfo(T).pointer.child else T; const fields = comptime std.meta.fields(EnumType); var options = ArrayList([]const u8).initCapacity(b.allocator, fields.len) catch @panic("OOM"); inline for (fields) |field| { options.appendAssumeCapacity(field.name); } break :blk options.toOwnedSlice() catch @panic("OOM"); } else null; const available_option = AvailableOption{ .name = name, .type_id = type_id, .description = description, .enum_options = enum_options, }; if ((b.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) { panic("Option '{s}' declared twice", .{name}); } b.available_options_list.append(available_option) catch @panic("OOM"); const option_ptr = b.user_input_options.getPtr(name) orelse return null; option_ptr.used = true; switch (type_id) { .bool => switch (option_ptr.value) { .flag => return true, .scalar => |s| { if (mem.eql(u8, s, "true")) { return true; } else if (mem.eql(u8, s, "false")) { return false; } else { log.err("Expected -D{s} to be a boolean, but received '{s}'", .{ name, s }); b.markInvalidUserInput(); return null; } }, .list, .map, .lazy_path, .lazy_path_list => { log.err("Expected -D{s} to be a boolean, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, }, .int => switch (option_ptr.value) { .flag, .list, .map, .lazy_path, .lazy_path_list => { log.err("Expected -D{s} to be an integer, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, .scalar => |s| { const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { error.Overflow => { log.err("-D{s} value {s} cannot fit into type {s}.", .{ name, s, @typeName(T) }); b.markInvalidUserInput(); return null; }, else => { log.err("Expected -D{s} to be an integer of type {s}.", .{ name, @typeName(T) }); b.markInvalidUserInput(); return null; }, }; return n; }, }, .float => switch (option_ptr.value) { .flag, .map, .list, .lazy_path, .lazy_path_list => { log.err("Expected -D{s} to be a float, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, .scalar => |s| { const n = std.fmt.parseFloat(T, s) catch { log.err("Expected -D{s} to be a float of type {s}.", .{ name, @typeName(T) }); b.markInvalidUserInput(); return null; }; return n; }, }, .@"enum" => switch (option_ptr.value) { .flag, .map, .list, .lazy_path, .lazy_path_list => { log.err("Expected -D{s} to be an enum, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, .scalar => |s| { if (std.meta.stringToEnum(T, s)) |enum_lit| { return enum_lit; } else { log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(T) }); b.markInvalidUserInput(); return null; } }, }, .string => switch (option_ptr.value) { .flag, .list, .map, .lazy_path, .lazy_path_list => { log.err("Expected -D{s} to be a string, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, .scalar => |s| return s, }, .build_id => switch (option_ptr.value) { .flag, .map, .list, .lazy_path, .lazy_path_list => { log.err("Expected -D{s} to be an enum, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, .scalar => |s| { if (std.zig.BuildId.parse(s)) |build_id| { return build_id; } else |err| { log.err("unable to parse option '-D{s}': {s}", .{ name, @errorName(err) }); b.markInvalidUserInput(); return null; } }, }, .list => switch (option_ptr.value) { .flag, .map, .lazy_path, .lazy_path_list => { log.err("Expected -D{s} to be a list, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, .scalar => |s| { return b.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM"); }, .list => |lst| return lst.items, }, .enum_list => switch (option_ptr.value) { .flag, .map, .lazy_path, .lazy_path_list => { log.err("Expected -D{s} to be a list, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, .scalar => |s| { const Child = @typeInfo(T).pointer.child; const value = std.meta.stringToEnum(Child, s) orelse { log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(Child) }); b.markInvalidUserInput(); return null; }; return b.allocator.dupe(Child, &[_]Child{value}) catch @panic("OOM"); }, .list => |lst| { const Child = @typeInfo(T).pointer.child; const new_list = b.allocator.alloc(Child, lst.items.len) catch @panic("OOM"); for (new_list, lst.items) |*new_item, str| { new_item.* = std.meta.stringToEnum(Child, str) orelse { log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(Child) }); b.markInvalidUserInput(); b.allocator.free(new_list); return null; }; } return new_list; }, }, .lazy_path => switch (option_ptr.value) { .scalar => |s| return .{ .cwd_relative = s }, .lazy_path => |lp| return lp, .flag, .map, .list, .lazy_path_list => { log.err("Expected -D{s} to be a path, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, }, .lazy_path_list => switch (option_ptr.value) { .scalar => |s| return b.allocator.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"), .lazy_path => |lp| return b.allocator.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"), .list => |lst| { const new_list = b.allocator.alloc(LazyPath, lst.items.len) catch @panic("OOM"); for (new_list, lst.items) |*new_item, str| { new_item.* = .{ .cwd_relative = str }; } return new_list; }, .lazy_path_list => |lp_list| return lp_list.items, .flag, .map => { log.err("Expected -D{s} to be a path, but received a {s}.", .{ name, @tagName(option_ptr.value), }); b.markInvalidUserInput(); return null; }, }, } }