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.

Installation

WindowsSdk.Installation
pub const Installation = struct

File

lib/std/zig/WindowsSdk.zig:458

Code

pub const Installation = struct {
    path: []const u8,
    version: []const u8,

    /// Find path and version of Windows SDK.
    /// Caller owns the result's fields.
    fn find(
        gpa: Allocator,
        io: Io,
        registry: *Registry,
        roots_key: Registry.Key,
        roots_subkey: []const u16,
        prefix: []const u8,
        version_key_name: []const u16,
    ) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
        roots: {
            const installation = findFromRoot(gpa, io, roots_key, roots_subkey, prefix) catch
                break :roots;
            if (installation.isValidVersion(roots_key)) return installation;
            installation.free(gpa);
        }
        {
            const installation = try findFromInstallationFolder(gpa, registry, version_key_name);
            if (installation.isValidVersion(roots_key)) return installation;
            installation.free(gpa);
        }
        return error.InstallationNotFound;
    }

    fn findFromRoot(
        gpa: Allocator,
        io: Io,
        roots_key: Registry.Key,
        roots_subkey: []const u16,
        prefix: []const u8,
    ) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
        const path = path: {
            const path_w_maybe_with_trailing_slash = roots_key.getString(gpa, .{ .name = roots_subkey }, .wtf16) catch |err| switch (err) {
                error.NotAString,
                error.ValueNameNotFound,
                error.StringNotFound,
                => return error.InstallationNotFound,

                error.OutOfMemory => |e| return e,
            };
            defer gpa.free(path_w_maybe_with_trailing_slash);

            if (!std.fs.path.isAbsoluteWindowsWtf16(path_w_maybe_with_trailing_slash)) {
                return error.InstallationNotFound;
            }

            const path_w = std.mem.trimEnd(u16, path_w_maybe_with_trailing_slash, L("\\/"));
            break :path try std.unicode.wtf16LeToWtf8Alloc(gpa, path_w);
        };
        errdefer gpa.free(path);

        const version = version: {
            var buf: [Dir.max_path_bytes]u8 = undefined;
            const sdk_lib_dir_path = std.fmt.bufPrint(buf[0..], "{s}\\Lib\\", .{path}) catch |err| switch (err) {
                error.NoSpaceLeft => return error.PathTooLong,
            };
            if (!Dir.path.isAbsolute(sdk_lib_dir_path)) return error.InstallationNotFound;

            // enumerate files in sdk path looking for latest version
            var sdk_lib_dir = Dir.openDirAbsolute(io, sdk_lib_dir_path, .{
                .iterate = true,
            }) catch |err| switch (err) {
                error.NameTooLong => return error.PathTooLong,
                else => return error.InstallationNotFound,
            };
            defer sdk_lib_dir.close(io);

            var iterator = sdk_lib_dir.iterate();
            const versions = try iterateAndFilterByVersion(&iterator, gpa, io, prefix);
            if (versions.len == 0) return error.InstallationNotFound;
            defer {
                for (versions[1..]) |version| gpa.free(version);
                gpa.free(versions);
            }
            break :version versions[0];
        };
        errdefer gpa.free(version);

        return .{ .path = path, .version = version };
    }

    fn findFromInstallationFolder(
        gpa: Allocator,
        registry: *Registry,
        version_key_name: []const u16,
    ) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
        const key_name = try std.mem.concat(gpa, u16, &.{ L("Microsoft\\Microsoft SDKs\\Windows\\"), version_key_name });
        defer gpa.free(key_name);

        const key = registry.tryOpenSoftwareKeyWithPrecedence(switch (is_32_bit) {
            true => &.{
                .{ .root = .local_machine },
                .{ .root = .current_user },
            },
            false => &.{
                .{ .root = .local_machine, .wow64 = .wow64_32 },
                .{ .root = .current_user, .wow64 = .wow64_32 },
                .{ .root = .local_machine, .wow64 = .native },
                .{ .root = .current_user, .wow64 = .native },
            },
        }, key_name) catch {
            return error.InstallationNotFound;
        };
        defer key.close();

        const path: []const u8 = path: {
            const path_w_maybe_with_trailing_slash = key.getString(gpa, .{ .name = L("InstallationFolder") }, .wtf16) catch |err| switch (err) {
                error.NotAString,
                error.ValueNameNotFound,
                error.StringNotFound,
                => return error.InstallationNotFound,

                error.OutOfMemory => |e| return e,
            };
            defer gpa.free(path_w_maybe_with_trailing_slash);

            if (!std.fs.path.isAbsoluteWindowsWtf16(path_w_maybe_with_trailing_slash)) {
                return error.InstallationNotFound;
            }

            const path_w = std.mem.trimEnd(u16, path_w_maybe_with_trailing_slash, L("\\/"));
            break :path try std.unicode.wtf16LeToWtf8Alloc(gpa, path_w);
        };
        errdefer gpa.free(path);

        const version: []const u8 = version: {
            // Microsoft doesn't include the .0 in the ProductVersion key
            const version_without_0 = key.getString(gpa, .{ .name = L("ProductVersion") }, .wtf16) catch |err| switch (err) {
                error.NotAString,
                error.ValueNameNotFound,
                error.StringNotFound,
                => return error.InstallationNotFound,

                error.OutOfMemory => |e| return e,
            };
            defer gpa.free(version_without_0);

            if (version_without_0.len + ".0".len > product_version_max_length) {
                return error.VersionTooLong;
            }

            var version: std.array_list.Managed(u8) = try .initCapacity(gpa, version_without_0.len + 2);
            errdefer version.deinit();

            try std.unicode.wtf16LeToWtf8ArrayList(&version, version_without_0);
            try version.appendSlice(".0");

            break :version try version.toOwnedSlice();
        };
        errdefer gpa.free(version);

        return .{ .path = path, .version = version };
    }

    /// Check whether this version is enumerated in registry.
    fn isValidVersion(installation: Installation, roots_key: Registry.Key) bool {
        var version_buf: [product_version_max_length]u16 = undefined;
        const version_len = std.unicode.wtf8ToWtf16Le(&version_buf, installation.version) catch return false;
        const version = version_buf[0..version_len];
        const options_key_name = "Installed Options";
        const buf_len = product_version_max_length + options_key_name.len + 2;
        var buf: [buf_len]u16 = undefined;
        var query: std.ArrayList(u16) = .initBuffer(&buf);
        query.appendSliceAssumeCapacity(version);
        query.appendAssumeCapacity('\\');
        query.appendSliceAssumeCapacity(L(options_key_name));

        const options_key = roots_key.open(query.items) catch |err| switch (err) {
            error.KeyNotFound => return false,
        };
        defer options_key.close();

        const option_name = comptime switch (builtin.target.cpu.arch) {
            .thumb => "OptionId.DesktopCPParm",
            .aarch64 => "OptionId.DesktopCPParm64",
            .x86 => "OptionId.DesktopCPPx86",
            .x86_64 => "OptionId.DesktopCPPx64",
            else => |tag| @compileError("Windows SDK cannot be detected on architecture " ++ tag),
        };

        const reg_value = options_key.getDword(.{ .name = L(option_name) }) catch return false;
        return (reg_value == 1);
    }

    fn free(install: Installation, gpa: Allocator) void {
        gpa.free(install.path);
        gpa.free(install.version);
    }
}