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.

supported_targets

cvtres.supported_targets
pub const supported_targets = struct

File

Code

pub const supported_targets = struct {
    /// Enum containing a mixture of names that come from:
    /// - Machine Types constants in the PE format spec:
    ///   https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
    /// - cvtres.exe /machine options
    /// - Zig/LLVM arch names
    /// All field names are lowercase regardless of their casing used in the above origins.
    pub const Arch = enum {
        // cvtres.exe /machine names
        x64,
        x86,
        /// Note: Following cvtres.exe's lead, this corresponds to ARMNT, not ARM
        arm,
        arm64,
        arm64ec,
        arm64x,
        ia64,
        ebc,

        // PE/COFF MACHINE constant names not covered above
        amd64,
        i386,
        armnt,

        // Zig/LLVM names not already covered above
        x86_64,
        aarch64,

        pub fn toCoffMachineType(arch: Arch) std.coff.IMAGE.FILE.MACHINE {
            return switch (arch) {
                .x64, .amd64, .x86_64 => .AMD64,
                .x86, .i386 => .I386,
                .arm, .armnt => .ARMNT,
                .arm64, .aarch64 => .ARM64,
                .arm64ec => .ARM64EC,
                .arm64x => .ARM64X,
                .ia64 => .IA64,
                .ebc => .EBC,
            };
        }

        pub fn description(arch: Arch) []const u8 {
            return switch (arch) {
                .x64, .amd64, .x86_64 => "64-bit X86",
                .x86, .i386 => "32-bit X86",
                .arm, .armnt => "ARM Thumb-2 little endian",
                .arm64, .aarch64 => "ARM64/AArch64 little endian",
                .arm64ec => "ARM64 \"Emulation Compatible\"",
                .arm64x => "ARM64 and ARM64EC together",
                .ia64 => "64-bit Intel Itanium",
                .ebc => "EFI Byte Code",
            };
        }

        pub const ordered_for_display: []const Arch = &.{
            .x64,
            .x86_64,
            .amd64,
            .x86,
            .i386,
            .arm64,
            .aarch64,
            .arm,
            .armnt,
            .arm64ec,
            .arm64x,
            .ia64,
            .ebc,
        };
        comptime {
            const info = @typeInfo(Arch).@"enum";
            for (info.field_names, info.field_values) |field_name, field_value| {
                _ = std.mem.indexOfScalar(Arch, ordered_for_display, @fromBackingInt(@intCast(field_value))) orelse {
                    @compileError(std.fmt.comptimePrint("'{s}' missing from ordered_for_display", .{field_name}));
                };
            }
        }

        pub const longest_name = blk: {
            var len = 0;
            for (@typeInfo(Arch).@"enum".field_names) |field_name| {
                if (field_name.len > len) len = field_name.len;
            }
            break :blk len;
        };

        pub fn fromStringIgnoreCase(str: []const u8) ?Arch {
            if (str.len > longest_name) return null;
            var lower_buf: [longest_name]u8 = undefined;
            const lower = std.ascii.lowerString(&lower_buf, str);
            return std.meta.stringToEnum(Arch, lower);
        }

        test fromStringIgnoreCase {
            try std.testing.expectEqual(.x64, Arch.fromStringIgnoreCase("x64").?);
            try std.testing.expectEqual(.x64, Arch.fromStringIgnoreCase("X64").?);
            try std.testing.expectEqual(.aarch64, Arch.fromStringIgnoreCase("Aarch64").?);
            try std.testing.expectEqual(null, Arch.fromStringIgnoreCase("armzzz"));
            try std.testing.expectEqual(null, Arch.fromStringIgnoreCase("long string that is longer than any field"));
        }
    };

    // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#type-indicators
    pub fn rvaRelocationTypeIndicator(target: std.coff.IMAGE.FILE.MACHINE) ?u16 {
        return switch (target) {
            .AMD64 => @backingInt(std.coff.IMAGE.REL.AMD64.ADDR32NB),
            .I386 => @backingInt(std.coff.IMAGE.REL.I386.DIR32NB),
            .ARMNT => @backingInt(std.coff.IMAGE.REL.ARM.ADDR32NB),
            .ARM64, .ARM64EC, .ARM64X => @backingInt(std.coff.IMAGE.REL.ARM64.ADDR32NB),
            .IA64 => @backingInt(std.coff.IMAGE.REL.IA64.DIR32NB),
            .EBC => 0x1, // This is what cvtres.exe writes for this target, unsure where it comes from
            else => null,
        };
    }

    pub fn isSupported(target: std.coff.IMAGE.FILE.MACHINE) bool {
        return rvaRelocationTypeIndicator(target) != null;
    }

    comptime {
        // Enforce two things:
        // 1. Arch enum field names are all lowercase (necessary for how fromStringIgnoreCase is implemented)
        // 2. All enum fields in Arch have an associated RVA relocation type when converted to a coff.IMAGE.FILE.MACHINE
        for (@typeInfo(Arch).@"enum".field_names) |field_name| {
            const all_lower = all_lower: for (field_name) |c| {
                if (std.ascii.isUpper(c)) break :all_lower false;
            } else break :all_lower true;
            if (!all_lower) @compileError(std.fmt.comptimePrint("Arch field is not all lowercase: {s}", .{field_name}));
            const coff_machine = @field(Arch, field_name).toCoffMachineType();
            _ = rvaRelocationTypeIndicator(coff_machine) orelse {
                @compileError(std.fmt.comptimePrint("No RVA relocation for Arch: {s}", .{field_name}));
            };
        }
    }
}