Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

Options

cli.Options
pub const Options = struct

File

Code

pub const Options = struct {
    allocator: Allocator,
    input_source: IoSource = .{ .filename = &[_]u8{} },
    output_source: IoSource = .{ .filename = &[_]u8{} },
    extra_include_paths: std.ArrayList([]const u8) = .empty,
    ignore_include_env_var: bool = false,
    preprocess: Preprocess = .yes,
    default_language_id: ?u16 = null,
    default_code_page: ?SupportedCodePage = null,
    verbose: bool = false,
    symbols: std.array_hash_map.String(SymbolValue) = .empty,
    null_terminate_string_table_strings: bool = false,
    max_string_literal_codepoints: u15 = lex.default_max_string_literal_codepoints,
    silent_duplicate_control_ids: bool = false,
    warn_instead_of_error_on_invalid_code_page: bool = false,
    debug: bool = false,
    print_help_and_exit: bool = false,
    auto_includes: AutoIncludes = .any,
    depfile_path: ?[]const u8 = null,
    depfile_fmt: DepfileFormat = .json,
    input_format: InputFormat = .rc,
    output_format: OutputFormat = .res,
    coff_options: cvtres.CoffOptions = .{},

    pub const IoSource = union(enum) {
        stdio: Io.File,
        filename: []const u8,
    };
    pub const AutoIncludes = enum { any, msvc, gnu, none };
    pub const DepfileFormat = enum { json };
    pub const InputFormat = enum { rc, res, rcpp };
    pub const OutputFormat = enum {
        res,
        coff,
        rcpp,

        pub fn extension(format: OutputFormat) []const u8 {
            return switch (format) {
                .rcpp => ".rcpp",
                .coff => ".obj",
                .res => ".res",
            };
        }
    };
    pub const Preprocess = enum { no, yes, only };
    pub const SymbolAction = enum { define, undefine };
    pub const SymbolValue = union(SymbolAction) {
        define: []const u8,
        undefine: void,

        pub fn deinit(self: SymbolValue, allocator: Allocator) void {
            switch (self) {
                .define => |value| allocator.free(value),
                .undefine => {},
            }
        }
    };

    /// Does not check that identifier contains only valid characters
    pub fn define(self: *Options, identifier: []const u8, value: []const u8) !void {
        if (self.symbols.getPtr(identifier)) |val_ptr| {
            // If the symbol is undefined, then that always takes precedence so
            // we shouldn't change anything.
            if (val_ptr.* == .undefine) return;
            // Otherwise, the new value takes precedence.
            const duped_value = try self.allocator.dupe(u8, value);
            errdefer self.allocator.free(duped_value);
            val_ptr.deinit(self.allocator);
            val_ptr.* = .{ .define = duped_value };
            return;
        }
        const duped_key = try self.allocator.dupe(u8, identifier);
        errdefer self.allocator.free(duped_key);
        const duped_value = try self.allocator.dupe(u8, value);
        errdefer self.allocator.free(duped_value);
        try self.symbols.put(self.allocator, duped_key, .{ .define = duped_value });
    }

    /// Does not check that identifier contains only valid characters
    pub fn undefine(self: *Options, identifier: []const u8) !void {
        if (self.symbols.getPtr(identifier)) |action| {
            action.deinit(self.allocator);
            action.* = .{ .undefine = {} };
            return;
        }
        const duped_key = try self.allocator.dupe(u8, identifier);
        errdefer self.allocator.free(duped_key);
        try self.symbols.put(self.allocator, duped_key, .{ .undefine = {} });
    }

    /// If the current input filename:
    /// - does not have an extension, and
    /// - does not exist in the cwd, and
    /// - the input format is .rc
    /// then this function will append `.rc` to the input filename
    ///
    /// Note: This behavior is different from the Win32 compiler.
    ///       It always appends .RC if the filename does not have
    ///       a `.` in it and it does not even try the verbatim name
    ///       in that scenario.
    ///
    /// The approach taken here is meant to give us a 'best of both
    /// worlds' situation where we'll be compatible with most use-cases
    /// of the .rc extension being omitted from the CLI args, but still
    /// work fine if the file itself does not have an extension.
    pub fn maybeAppendRC(options: *Options, io: Io, cwd: Io.Dir) !void {
        switch (options.input_source) {
            .stdio => return,
            .filename => {},
        }
        if (options.input_format == .rc and std.fs.path.extension(options.input_source.filename).len == 0) {
            cwd.access(io, options.input_source.filename, .{}) catch |err| switch (err) {
                error.FileNotFound => {
                    var filename_bytes = try options.allocator.alloc(u8, options.input_source.filename.len + 3);
                    @memcpy(filename_bytes[0..options.input_source.filename.len], options.input_source.filename);
                    @memcpy(filename_bytes[filename_bytes.len - 3 ..], ".rc");
                    options.allocator.free(options.input_source.filename);
                    options.input_source = .{ .filename = filename_bytes };
                },
                else => {},
            };
        }
    }

    pub fn deinit(self: *Options) void {
        for (self.extra_include_paths.items) |extra_include_path| {
            self.allocator.free(extra_include_path);
        }
        self.extra_include_paths.deinit(self.allocator);
        switch (self.input_source) {
            .stdio => {},
            .filename => |filename| self.allocator.free(filename),
        }
        switch (self.output_source) {
            .stdio => {},
            .filename => |filename| self.allocator.free(filename),
        }
        var symbol_it = self.symbols.iterator();
        while (symbol_it.next()) |entry| {
            self.allocator.free(entry.key_ptr.*);
            entry.value_ptr.deinit(self.allocator);
        }
        self.symbols.deinit(self.allocator);
        if (self.depfile_path) |depfile_path| {
            self.allocator.free(depfile_path);
        }
        if (self.coff_options.define_external_symbol) |symbol_name| {
            self.allocator.free(symbol_name);
        }
    }

    pub fn dumpVerbose(self: *const Options, writer: *std.Io.Writer) !void {
        const input_source_name = switch (self.input_source) {
            .stdio => "<stdin>",
            .filename => |filename| filename,
        };
        const output_source_name = switch (self.output_source) {
            .stdio => "<stdout>",
            .filename => |filename| filename,
        };
        try writer.print("Input filename: {s} (format={s})\n", .{ input_source_name, @tagName(self.input_format) });
        try writer.print("Output filename: {s} (format={s})\n", .{ output_source_name, @tagName(self.output_format) });
        if (self.output_format == .coff) {
            try writer.print(" Target machine type for COFF: {s}\n", .{@tagName(self.coff_options.target)});
        }

        if (self.extra_include_paths.items.len > 0) {
            try writer.writeAll(" Extra include paths:\n");
            for (self.extra_include_paths.items) |extra_include_path| {
                try writer.print("  \"{s}\"\n", .{extra_include_path});
            }
        }
        if (self.ignore_include_env_var) {
            try writer.writeAll(" The INCLUDE environment variable will be ignored\n");
        }
        if (self.preprocess == .no) {
            try writer.writeAll(" The preprocessor will not be invoked\n");
        } else if (self.preprocess == .only) {
            try writer.writeAll(" Only the preprocessor will be invoked\n");
        }
        if (self.symbols.count() > 0) {
            try writer.writeAll(" Symbols:\n");
            var it = self.symbols.iterator();
            while (it.next()) |symbol| {
                try writer.print("  {s} {s}", .{ switch (symbol.value_ptr.*) {
                    .define => "#define",
                    .undefine => "#undef",
                }, symbol.key_ptr.* });
                if (symbol.value_ptr.* == .define) {
                    try writer.print(" {s}", .{symbol.value_ptr.define});
                }
                try writer.writeAll("\n");
            }
        }
        if (self.null_terminate_string_table_strings) {
            try writer.writeAll(" Strings in string tables will be null-terminated\n");
        }
        if (self.max_string_literal_codepoints != lex.default_max_string_literal_codepoints) {
            try writer.print(" Max string literal length: {}\n", .{self.max_string_literal_codepoints});
        }
        if (self.silent_duplicate_control_ids) {
            try writer.writeAll(" Duplicate control IDs will not emit warnings\n");
        }
        if (self.silent_duplicate_control_ids) {
            try writer.writeAll(" Invalid code page in .rc will produce a warning (instead of an error)\n");
        }

        const language_id = self.default_language_id orelse res.Language.default;
        const language_name = language_name: {
            if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| {
                break :language_name @tagName(lang_enum_val);
            }
            if (language_id == lang.LOCALE_CUSTOM_UNSPECIFIED) {
                break :language_name "LOCALE_CUSTOM_UNSPECIFIED";
            }
            break :language_name "<UNKNOWN>";
        };
        try writer.print("Default language: {s} (id=0x{x})\n", .{ language_name, language_id });

        const code_page = self.default_code_page orelse .windows1252;
        try writer.print("Default codepage: {s} (id={})\n", .{ @tagName(code_page), @backingInt(code_page) });
    }
}