const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const mem = std.mem;
const testing = std.testing;
const os = std.os;
const Target = std.Target;
pub fn detect(target_os: *Target.Os) !void {
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    const prefixSlash = "/System/Library/CoreServices/";
    const paths = [_][]const u8{
        prefixSlash ++ "SystemVersion.plist",
        prefixSlash ++ ".SystemVersionPlatform.plist",
    };
    for (paths) |path| {
        
        var buf: [2048]u8 = undefined;
        if (std.fs.cwd().readFile(path, &buf)) |bytes| {
            if (parseSystemVersion(bytes)) |ver| {
                
                if (!(ver.major == 10 and ver.minor >= 16)) {
                    target_os.version_range.semver.min = ver;
                    target_os.version_range.semver.max = ver;
                    return;
                }
                continue;
            } else |_| {
                return error.OSVersionDetectionFail;
            }
        } else |_| {
            return error.OSVersionDetectionFail;
        }
    }
    return error.OSVersionDetectionFail;
}
fn parseSystemVersion(buf: []const u8) !std.builtin.Version {
    var svt = SystemVersionTokenizer{ .bytes = buf };
    try svt.skipUntilTag(.start, "dict");
    while (true) {
        try svt.skipUntilTag(.start, "key");
        const content = try svt.expectContent();
        try svt.skipUntilTag(.end, "key");
        if (std.mem.eql(u8, content, "ProductVersion")) break;
    }
    try svt.skipUntilTag(.start, "string");
    const ver = try svt.expectContent();
    try svt.skipUntilTag(.end, "string");
    return std.builtin.Version.parse(ver);
}
const SystemVersionTokenizer = struct {
    bytes: []const u8,
    index: usize = 0,
    state: State = .begin,
    fn next(self: *@This()) !?Token {
        var mark: usize = self.index;
        var tag = Tag{};
        var content: []const u8 = "";
        while (self.index < self.bytes.len) {
            const char = self.bytes[self.index];
            switch (self.state) {
                .begin => switch (char) {
                    '<' => {
                        self.state = .tag0;
                        self.index += 1;
                        tag = Tag{};
                        mark = self.index;
                    },
                    '>' => {
                        return error.BadToken;
                    },
                    else => {
                        self.state = .content;
                        content = "";
                        mark = self.index;
                    },
                },
                .tag0 => switch (char) {
                    '<' => {
                        return error.BadToken;
                    },
                    '>' => {
                        self.state = .begin;
                        self.index += 1;
                        tag.name = self.bytes[mark..self.index];
                        return Token{ .tag = tag };
                    },
                    '"' => {
                        self.state = .tag_string;
                        self.index += 1;
                    },
                    '/' => {
                        self.state = .tag0_end_or_empty;
                        self.index += 1;
                    },
                    'A'...'Z', 'a'...'z' => {
                        self.state = .tagN;
                        tag.kind = .start;
                        self.index += 1;
                    },
                    else => {
                        self.state = .tagN;
                        self.index += 1;
                    },
                },
                .tag0_end_or_empty => switch (char) {
                    '<' => {
                        return error.BadToken;
                    },
                    '>' => {
                        self.state = .begin;
                        tag.kind = .empty;
                        tag.name = self.bytes[self.index..self.index];
                        self.index += 1;
                        return Token{ .tag = tag };
                    },
                    else => {
                        self.state = .tagN;
                        tag.kind = .end;
                        mark = self.index;
                        self.index += 1;
                    },
                },
                .tagN => switch (char) {
                    '<' => {
                        return error.BadToken;
                    },
                    '>' => {
                        self.state = .begin;
                        tag.name = self.bytes[mark..self.index];
                        self.index += 1;
                        return Token{ .tag = tag };
                    },
                    '"' => {
                        self.state = .tag_string;
                        self.index += 1;
                    },
                    '/' => {
                        self.state = .tagN_end;
                        tag.kind = .end;
                        self.index += 1;
                    },
                    else => {
                        self.index += 1;
                    },
                },
                .tagN_end => switch (char) {
                    '>' => {
                        self.state = .begin;
                        tag.name = self.bytes[mark..self.index];
                        self.index += 1;
                        return Token{ .tag = tag };
                    },
                    else => {
                        return error.BadToken;
                    },
                },
                .tag_string => switch (char) {
                    '"' => {
                        self.state = .tagN;
                        self.index += 1;
                    },
                    else => {
                        self.index += 1;
                    },
                },
                .content => switch (char) {
                    '<' => {
                        self.state = .tag0;
                        content = self.bytes[mark..self.index];
                        self.index += 1;
                        tag = Tag{};
                        mark = self.index;
                        return Token{ .content = content };
                    },
                    '>' => {
                        return error.BadToken;
                    },
                    else => {
                        self.index += 1;
                    },
                },
            }
        }
        return null;
    }
    fn expectContent(self: *@This()) ![]const u8 {
        if (try self.next()) |tok| {
            switch (tok) {
                .content => |content| {
                    return content;
                },
                else => {},
            }
        }
        return error.UnexpectedToken;
    }
    fn skipUntilTag(self: *@This(), kind: Tag.Kind, name: []const u8) !void {
        while (try self.next()) |tok| {
            switch (tok) {
                .tag => |tag| {
                    if (tag.kind == kind and std.mem.eql(u8, tag.name, name)) return;
                },
                else => {},
            }
        }
        return error.TagNotFound;
    }
    const State = enum {
        begin,
        tag0,
        tag0_end_or_empty,
        tagN,
        tagN_end,
        tag_string,
        content,
    };
    const Token = union(enum) {
        tag: Tag,
        content: []const u8,
    };
    const Tag = struct {
        kind: Kind = .unknown,
        name: []const u8 = "",
        const Kind = enum { unknown, start, end, empty };
    };
};
test "detect" {
    const cases = .{
        .{
            \\<?xml version="1.0" encoding="UTF-8"?>
            \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
            \\<plist version="1.0">
            \\<dict>
            \\    <key>ProductBuildVersion</key>
            \\    <string>7B85</string>
            \\    <key>ProductCopyright</key>
            \\    <string>Apple Computer, Inc. 1983-2003</string>
            \\    <key>ProductName</key>
            \\    <string>Mac OS X</string>
            \\    <key>ProductUserVisibleVersion</key>
            \\    <string>10.3</string>
            \\    <key>ProductVersion</key>
            \\    <string>10.3</string>
            \\</dict>
            \\</plist>
            ,
            .{ .major = 10, .minor = 3 },
        },
        .{
            \\<?xml version="1.0" encoding="UTF-8"?>
            \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
            \\<plist version="1.0">
            \\<dict>
            \\	<key>ProductBuildVersion</key>
            \\	<string>7W98</string>
            \\	<key>ProductCopyright</key>
            \\	<string>Apple Computer, Inc. 1983-2004</string>
            \\	<key>ProductName</key>
            \\	<string>Mac OS X</string>
            \\	<key>ProductUserVisibleVersion</key>
            \\	<string>10.3.9</string>
            \\	<key>ProductVersion</key>
            \\	<string>10.3.9</string>
            \\</dict>
            \\</plist>
            ,
            .{ .major = 10, .minor = 3, .patch = 9 },
        },
        .{
            \\<?xml version="1.0" encoding="UTF-8"?>
            \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
            \\<plist version="1.0">
            \\<dict>
            \\	<key>ProductBuildVersion</key>
            \\	<string>19G68</string>
            \\	<key>ProductCopyright</key>
            \\	<string>1983-2020 Apple Inc.</string>
            \\	<key>ProductName</key>
            \\	<string>Mac OS X</string>
            \\	<key>ProductUserVisibleVersion</key>
            \\	<string>10.15.6</string>
            \\	<key>ProductVersion</key>
            \\	<string>10.15.6</string>
            \\	<key>iOSSupportVersion</key>
            \\	<string>13.6</string>
            \\</dict>
            \\</plist>
            ,
            .{ .major = 10, .minor = 15, .patch = 6 },
        },
        .{
            \\<?xml version="1.0" encoding="UTF-8"?>
            \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
            \\<plist version="1.0">
            \\<dict>
            \\	<key>ProductBuildVersion</key>
            \\	<string>20A2408</string>
            \\	<key>ProductCopyright</key>
            \\	<string>1983-2020 Apple Inc.</string>
            \\	<key>ProductName</key>
            \\	<string>macOS</string>
            \\	<key>ProductUserVisibleVersion</key>
            \\	<string>11.0</string>
            \\	<key>ProductVersion</key>
            \\	<string>11.0</string>
            \\	<key>iOSSupportVersion</key>
            \\	<string>14.2</string>
            \\</dict>
            \\</plist>
            ,
            .{ .major = 11, .minor = 0 },
        },
        .{
            \\<?xml version="1.0" encoding="UTF-8"?>
            \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
            \\<plist version="1.0">
            \\<dict>
            \\	<key>ProductBuildVersion</key>
            \\	<string>20C63</string>
            \\	<key>ProductCopyright</key>
            \\	<string>1983-2020 Apple Inc.</string>
            \\	<key>ProductName</key>
            \\	<string>macOS</string>
            \\	<key>ProductUserVisibleVersion</key>
            \\	<string>11.1</string>
            \\	<key>ProductVersion</key>
            \\	<string>11.1</string>
            \\	<key>iOSSupportVersion</key>
            \\	<string>14.3</string>
            \\</dict>
            \\</plist>
            ,
            .{ .major = 11, .minor = 1 },
        },
    };
    inline for (cases) |case| {
        const ver0 = try parseSystemVersion(case[0]);
        const ver1: std.builtin.Version = case[1];
        try testVersionEquality(ver1, ver0);
    }
}
fn testVersionEquality(expected: std.builtin.Version, got: std.builtin.Version) !void {
    var b_expected: [64]u8 = undefined;
    const s_expected: []const u8 = try std.fmt.bufPrint(b_expected[0..], "{}", .{expected});
    var b_got: [64]u8 = undefined;
    const s_got: []const u8 = try std.fmt.bufPrint(b_got[0..], "{}", .{got});
    try testing.expectEqualStrings(s_expected, s_got);
}
pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
    var cpu_family: std.c.CPUFAMILY = undefined;
    var len: usize = @sizeOf(std.c.CPUFAMILY);
    os.sysctlbynameZ("hw.cpufamily", &cpu_family, &len, null, 0) catch |err| switch (err) {
        error.NameTooLong => unreachable, 
        error.PermissionDenied => unreachable, 
        error.SystemResources => unreachable, 
        error.UnknownName => unreachable, 
        error.Unexpected => unreachable, 
    };
    const current_arch = builtin.cpu.arch;
    switch (current_arch) {
        .aarch64, .aarch64_be, .aarch64_32 => {
            const model = switch (cpu_family) {
                .ARM_FIRESTORM_ICESTORM => &Target.aarch64.cpu.apple_a14,
                .ARM_LIGHTNING_THUNDER => &Target.aarch64.cpu.apple_a13,
                .ARM_VORTEX_TEMPEST => &Target.aarch64.cpu.apple_a12,
                .ARM_MONSOON_MISTRAL => &Target.aarch64.cpu.apple_a11,
                .ARM_HURRICANE => &Target.aarch64.cpu.apple_a10,
                .ARM_TWISTER => &Target.aarch64.cpu.apple_a9,
                .ARM_TYPHOON => &Target.aarch64.cpu.apple_a8,
                .ARM_CYCLONE => &Target.aarch64.cpu.cyclone,
                else => return null,
            };
            return Target.Cpu{
                .arch = current_arch,
                .model = model,
                .features = model.features,
            };
        },
        else => {},
    }
    return null;
}