    //! Contains all the same data as `Target`, additionally introducing the concept of "the native target".
    //! The purpose of this abstraction is to provide meaningful and unsurprising defaults.
    //! This struct does reference any resources and it is copyable.

    const CrossTarget = @This();
    const std = @import("../std.zig");
    const builtin = @import("builtin");
    const assert = std.debug.assert;
    const Target = std.Target;
    const mem = std.mem;

    /// `null` means native.
    cpu_arch: ?Target.Cpu.Arch = null,

    cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,

    /// Sparse set of CPU features to add to the set from `cpu_model`.
    cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,

    /// Sparse set of CPU features to remove from the set from `cpu_model`.
    cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,

    /// `null` means native.
    os_tag: ?Target.Os.Tag = null,

    /// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)
    /// then `null` for this field means native.
    os_version_min: ?OsVersion = null,

    /// When cross compiling, `null` means default (latest known OS version).
    /// When `os_tag` is native, `null` means equal to the native OS version.
    os_version_max: ?OsVersion = null,

    /// `null` means default when cross compiling, or native when os_tag is native.
    /// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
    glibc_version: ?SemanticVersion = null,

    /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
    abi: ?Target.Abi = null,

    /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
    /// based on the `os_tag`.
    dynamic_linker: DynamicLinker = DynamicLinker{},

    /// `null` means default for the cpu/arch/os combo.
    ofmt: ?Target.ObjectFormat = null,

    pub const CpuModel = union(enum) {
        /// Always native
        native,

        /// Always baseline
        baseline,

        /// If CPU Architecture is native, then the CPU model will be native. Otherwise,
        /// it will be baseline.
        determined_by_cpu_arch,

        explicit: *const Target.Cpu.Model,
    };

    pub const OsVersion = union(enum) {
        none: void,
        semver: SemanticVersion,
        windows: Target.Os.WindowsVersion,
    };

    pub const SemanticVersion = std.SemanticVersion;

    pub const DynamicLinker = Target.DynamicLinker;

    pub fn fromTarget(target: Target) CrossTarget {
        var result: CrossTarget = .{
            .cpu_arch = target.cpu.arch,
            .cpu_model = .{ .explicit = target.cpu.model },
            .os_tag = target.os.tag,
            .os_version_min = undefined,
            .os_version_max = undefined,
            .abi = target.abi,
            .glibc_version = if (target.isGnuLibC())
                target.os.version_range.linux.glibc
            else
                null,
        };
        result.updateOsVersionRange(target.os);

        const all_features = target.cpu.arch.allFeaturesList();
        var cpu_model_set = target.cpu.model.features;
        cpu_model_set.populateDependencies(all_features);
        {
            // The "add" set is the full set with the CPU Model set removed.

            const add_set = &result.cpu_features_add;
            add_set.* = target.cpu.features;
            add_set.removeFeatureSet(cpu_model_set);
        }
        {
            // The "sub" set is the features that are on in CPU Model set and off in the full set.

            const sub_set = &result.cpu_features_sub;
            sub_set.* = cpu_model_set;
            sub_set.removeFeatureSet(target.cpu.features);
        }
        return result;
    }

    fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void {
        switch (os.tag) {
            .freestanding,
            .ananas,
            .cloudabi,
            .fuchsia,
            .kfreebsd,
            .lv2,
            .solaris,
            .zos,
            .haiku,
            .minix,
            .rtems,
            .nacl,
            .aix,
            .cuda,
            .nvcl,
            .amdhsa,
            .ps4,
            .ps5,
            .elfiamcu,
            .mesa3d,
            .contiki,
            .amdpal,
            .hermit,
            .hurd,
            .wasi,
            .emscripten,
            .driverkit,
            .shadermodel,
            .uefi,
            .opencl,
            .glsl450,
            .vulkan,
            .plan9,
            .other,
            => {
                self.os_version_min = .{ .none = {} };
                self.os_version_max = .{ .none = {} };
            },

            .freebsd,
            .macos,
            .ios,
            .tvos,
            .watchos,
            .netbsd,
            .openbsd,
            .dragonfly,
            => {
                self.os_version_min = .{ .semver = os.version_range.semver.min };
                self.os_version_max = .{ .semver = os.version_range.semver.max };
            },

            .linux => {
                self.os_version_min = .{ .semver = os.version_range.linux.range.min };
                self.os_version_max = .{ .semver = os.version_range.linux.range.max };
            },

            .windows => {
                self.os_version_min = .{ .windows = os.version_range.windows.min };
                self.os_version_max = .{ .windows = os.version_range.windows.max };
            },
        }
    }

    /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
    pub fn toTarget(self: CrossTarget) Target {
        return .{
            .cpu = self.getCpu(),
            .os = self.getOs(),
            .abi = self.getAbi(),
            .ofmt = self.getObjectFormat(),
        };
    }

    pub const ParseOptions = struct {
        /// This is sometimes called a "triple". It looks roughly like this:
        ///     riscv64-linux-musl
        /// The fields are, respectively:
        /// * CPU Architecture
        /// * Operating System (and optional version range)
        /// * C ABI (optional, with optional glibc version)
        /// The string "native" can be used for CPU architecture as well as Operating System.
        /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
        arch_os_abi: []const u8 = "native",

        /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
        /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
        /// to remove from the set.
        /// The following special strings are recognized for CPU Model name:
        /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set
        ///                of features that is expected to be supported on most available hardware.
        /// * "native"   - The native CPU model is to be detected when compiling.
        /// If this field is not provided (`null`), then the value will depend on the
        /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
        cpu_features: ?[]const u8 = null,

        /// Absolute path to dynamic linker, to override the default, which is either a natively
        /// detected path, or a standard path.
        dynamic_linker: ?[]const u8 = null,

        object_format: ?[]const u8 = null,

        /// If this is provided, the function will populate some information about parsing failures,
        /// so that user-friendly error messages can be delivered.
        diagnostics: ?*Diagnostics = null,

        pub const Diagnostics = struct {
            /// If the architecture was determined, this will be populated.
            arch: ?Target.Cpu.Arch = null,

            /// If the OS name was determined, this will be populated.
            os_name: ?[]const u8 = null,

            /// If the OS tag was determined, this will be populated.
            os_tag: ?Target.Os.Tag = null,

            /// If the ABI was determined, this will be populated.
            abi: ?Target.Abi = null,

            /// If the CPU name was determined, this will be populated.
            cpu_name: ?[]const u8 = null,

            /// If error.UnknownCpuFeature is returned, this will be populated.
            unknown_feature_name: ?[]const u8 = null,
        };
    };

    pub fn parse(args: ParseOptions) !CrossTarget {
        var dummy_diags: ParseOptions.Diagnostics = undefined;
        const diags = args.diagnostics orelse &dummy_diags;

        var result: CrossTarget = .{
            .dynamic_linker = DynamicLinker.init(args.dynamic_linker),
        };

        var it = mem.splitScalar(u8, args.arch_os_abi, '-');
        const arch_name = it.first();
        const arch_is_native = mem.eql(u8, arch_name, "native");
        if (!arch_is_native) {
            result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse
                return error.UnknownArchitecture;
        }
        const arch = result.getCpuArch();
        diags.arch = arch;

        if (it.next()) |os_text| {
            try parseOs(&result, diags, os_text);
        } else if (!arch_is_native) {
            return error.MissingOperatingSystem;
        }

        const opt_abi_text = it.next();
        if (opt_abi_text) |abi_text| {
            var abi_it = mem.splitScalar(u8, abi_text, '.');
            const abi = std.meta.stringToEnum(Target.Abi, abi_it.first()) orelse
                return error.UnknownApplicationBinaryInterface;
            result.abi = abi;
            diags.abi = abi;

            const abi_ver_text = abi_it.rest();
            if (abi_it.next() != null) {
                if (result.isGnuLibC()) {
                    result.glibc_version = parseVersion(abi_ver_text) catch |err| switch (err) {
                        error.Overflow => return error.InvalidAbiVersion,
                        error.InvalidVersion => return error.InvalidAbiVersion,
                    };
                } else {
                    return error.InvalidAbiVersion;
                }
            }
        }

        if (it.next() != null) return error.UnexpectedExtraField;

        if (args.cpu_features) |cpu_features| {
            const all_features = arch.allFeaturesList();
            var index: usize = 0;
            while (index < cpu_features.len and
                cpu_features[index] != '+' and
                cpu_features[index] != '-')
            {
                index += 1;
            }
            const cpu_name = cpu_features[0..index];
            diags.cpu_name = cpu_name;

            const add_set = &result.cpu_features_add;
            const sub_set = &result.cpu_features_sub;
            if (mem.eql(u8, cpu_name, "native")) {
                result.cpu_model = .native;
            } else if (mem.eql(u8, cpu_name, "baseline")) {
                result.cpu_model = .baseline;
            } else {
                result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
            }

            while (index < cpu_features.len) {
                const op = cpu_features[index];
                const set = switch (op) {
                    '+' => add_set,
                    '-' => sub_set,
                    else => unreachable,
                };
                index += 1;
                const start = index;
                while (index < cpu_features.len and
                    cpu_features[index] != '+' and
                    cpu_features[index] != '-')
                {
                    index += 1;
                }
                const feature_name = cpu_features[start..index];
                for (all_features, 0..) |feature, feat_index_usize| {
                    const feat_index = @as(Target.Cpu.Feature.Set.Index, @intCast(feat_index_usize));
                    if (mem.eql(u8, feature_name, feature.name)) {
                        set.addFeature(feat_index);
                        break;
                    }
                } else {
                    diags.unknown_feature_name = feature_name;
                    return error.UnknownCpuFeature;
                }
            }
        }

        if (args.object_format) |ofmt_name| {
            result.ofmt = std.meta.stringToEnum(Target.ObjectFormat, ofmt_name) orelse
                return error.UnknownObjectFormat;
        }

        return result;
    }

    /// Similar to `parse` except instead of fully parsing, it only determines the CPU
    /// architecture and returns it if it can be determined, and returns `null` otherwise.
    /// This is intended to be used if the API user of CrossTarget needs to learn the
    /// target CPU architecture in order to fully populate `ParseOptions`.
    pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch {
        var it = mem.splitScalar(u8, args.arch_os_abi, '-');
        const arch_name = it.first();
        const arch_is_native = mem.eql(u8, arch_name, "native");
        if (arch_is_native) {
            return builtin.cpu.arch;
        } else {
            return std.meta.stringToEnum(Target.Cpu.Arch, arch_name);
        }
    }

    /// Parses a version with an omitted patch component, such as "1.0",
    /// which SemanticVersion.parse is not capable of.
    fn parseVersion(ver: []const u8) !SemanticVersion {
        const parseVersionComponent = struct {
            fn parseVersionComponent(component: []const u8) !usize {
                return std.fmt.parseUnsigned(usize, component, 10) catch |err| {
                    switch (err) {
                        error.InvalidCharacter => return error.InvalidVersion,
                        error.Overflow => return error.Overflow,
                    }
                };
            }
        }.parseVersionComponent;
        var version_components = mem.split(u8, ver, ".");
        const major = version_components.first();
        const minor = version_components.next() orelse return error.InvalidVersion;
        const patch = version_components.next() orelse "0";
        if (version_components.next() != null) return error.InvalidVersion;
        return .{
            .major = try parseVersionComponent(major),
            .minor = try parseVersionComponent(minor),
            .patch = try parseVersionComponent(patch),
        };
    }

    /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
    pub fn getCpu(self: CrossTarget) Target.Cpu {
        switch (self.cpu_model) {
            .native => {
                // This works when doing `zig build` because Zig generates a build executable using

                // native CPU model & features. However this will not be accurate otherwise, and

                // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.

                return builtin.cpu;
            },
            .baseline => {
                var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
                self.updateCpuFeatures(&adjusted_baseline.features);
                return adjusted_baseline;
            },
            .determined_by_cpu_arch => if (self.cpu_arch == null) {
                // This works when doing `zig build` because Zig generates a build executable using

                // native CPU model & features. However this will not be accurate otherwise, and

                // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.

                return builtin.cpu;
            } else {
                var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
                self.updateCpuFeatures(&adjusted_baseline.features);
                return adjusted_baseline;
            },
            .explicit => |model| {
                var adjusted_model = model.toCpu(self.getCpuArch());
                self.updateCpuFeatures(&adjusted_model.features);
                return adjusted_model;
            },
        }
    }

    pub fn getCpuArch(self: CrossTarget) Target.Cpu.Arch {
        return self.cpu_arch orelse builtin.cpu.arch;
    }

    pub fn getCpuModel(self: CrossTarget) *const Target.Cpu.Model {
        return switch (self.cpu_model) {
            .explicit => |cpu_model| cpu_model,
            else => self.getCpu().model,
        };
    }

    pub fn getCpuFeatures(self: CrossTarget) Target.Cpu.Feature.Set {
        return self.getCpu().features;
    }

    /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
    pub fn getOs(self: CrossTarget) Target.Os {
        // `builtin.os` works when doing `zig build` because Zig generates a build executable using

        // native OS version range. However this will not be accurate otherwise, and

        // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.

        var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange(self.getCpuArch()) else builtin.os;

        if (self.os_version_min) |min| switch (min) {
            .none => {},
            .semver => |semver| switch (self.getOsTag()) {
                .linux => adjusted_os.version_range.linux.range.min = semver,
                else => adjusted_os.version_range.semver.min = semver,
            },
            .windows => |win_ver| adjusted_os.version_range.windows.min = win_ver,
        };

        if (self.os_version_max) |max| switch (max) {
            .none => {},
            .semver => |semver| switch (self.getOsTag()) {
                .linux => adjusted_os.version_range.linux.range.max = semver,
                else => adjusted_os.version_range.semver.max = semver,
            },
            .windows => |win_ver| adjusted_os.version_range.windows.max = win_ver,
        };

        if (self.glibc_version) |glibc| {
            assert(self.isGnuLibC());
            adjusted_os.version_range.linux.glibc = glibc;
        }

        return adjusted_os;
    }

    pub fn getOsTag(self: CrossTarget) Target.Os.Tag {
        return self.os_tag orelse builtin.os.tag;
    }

    /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
    pub fn getOsVersionMin(self: CrossTarget) OsVersion {
        if (self.os_version_min) |version_min| return version_min;
        var tmp: CrossTarget = undefined;
        tmp.updateOsVersionRange(self.getOs());
        return tmp.os_version_min.?;
    }

    /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
    pub fn getOsVersionMax(self: CrossTarget) OsVersion {
        if (self.os_version_max) |version_max| return version_max;
        var tmp: CrossTarget = undefined;
        tmp.updateOsVersionRange(self.getOs());
        return tmp.os_version_max.?;
    }

    /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
    pub fn getAbi(self: CrossTarget) Target.Abi {
        if (self.abi) |abi| return abi;

        if (self.os_tag == null) {
            // This works when doing `zig build` because Zig generates a build executable using

            // native CPU model & features. However this will not be accurate otherwise, and

            // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.

            return builtin.abi;
        }

        return Target.Abi.default(self.getCpuArch(), self.getOs());
    }

    pub fn isFreeBSD(self: CrossTarget) bool {
        return self.getOsTag() == .freebsd;
    }

    pub fn isDarwin(self: CrossTarget) bool {
        return self.getOsTag().isDarwin();
    }

    pub fn isNetBSD(self: CrossTarget) bool {
        return self.getOsTag() == .netbsd;
    }

    pub fn isOpenBSD(self: CrossTarget) bool {
        return self.getOsTag() == .openbsd;
    }

    pub fn isUefi(self: CrossTarget) bool {
        return self.getOsTag() == .uefi;
    }

    pub fn isDragonFlyBSD(self: CrossTarget) bool {
        return self.getOsTag() == .dragonfly;
    }

    pub fn isLinux(self: CrossTarget) bool {
        return self.getOsTag() == .linux;
    }

    pub fn isWindows(self: CrossTarget) bool {
        return self.getOsTag() == .windows;
    }

    pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
        return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
    }

    pub fn staticLibSuffix(self: CrossTarget) [:0]const u8 {
        return Target.staticLibSuffix_os_abi(self.getOsTag(), self.getAbi());
    }

    pub fn dynamicLibSuffix(self: CrossTarget) [:0]const u8 {
        return self.getOsTag().dynamicLibSuffix();
    }

    pub fn libPrefix(self: CrossTarget) [:0]const u8 {
        return Target.libPrefix_os_abi(self.getOsTag(), self.getAbi());
    }

    pub fn isNativeCpu(self: CrossTarget) bool {
        return self.cpu_arch == null and
            (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and
            self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty();
    }

    pub fn isNativeOs(self: CrossTarget) bool {
        return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
            self.dynamic_linker.get() == null and self.glibc_version == null;
    }

    pub fn isNativeAbi(self: CrossTarget) bool {
        return self.os_tag == null and self.abi == null;
    }

    pub fn isNative(self: CrossTarget) bool {
        return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi();
    }

    /// Formats a version with the patch component omitted if it is zero,
    /// unlike SemanticVersion.format which formats all its version components regardless.
    fn formatVersion(version: SemanticVersion, writer: anytype) !void {
        if (version.patch == 0) {
            try writer.print("{d}.{d}", .{ version.major, version.minor });
        } else {
            try writer.print("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
        }
    }

    pub fn zigTriple(self: CrossTarget, allocator: mem.Allocator) error{OutOfMemory}![]u8 {
        if (self.isNative()) {
            return allocator.dupe(u8, "native");
        }

        const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
        const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";

        var result = std.ArrayList(u8).init(allocator);
        defer result.deinit();

        try result.writer().print("{s}-{s}", .{ arch_name, os_name });

        // The zig target syntax does not allow specifying a max os version with no min, so

        // if either are present, we need the min.

        if (self.os_version_min != null or self.os_version_max != null) {
            switch (self.getOsVersionMin()) {
                .none => {},
                .semver => |v| {
                    try result.writer().writeAll(".");
                    try formatVersion(v, result.writer());
                },
                .windows => |v| try result.writer().print("{s}", .{v}),
            }
        }
        if (self.os_version_max) |max| {
            switch (max) {
                .none => {},
                .semver => |v| {
                    try result.writer().writeAll("...");
                    try formatVersion(v, result.writer());
                },
                .windows => |v| try result.writer().print("..{s}", .{v}),
            }
        }

        if (self.glibc_version) |v| {
            try result.writer().print("-{s}.", .{@tagName(self.getAbi())});
            try formatVersion(v, result.writer());
        } else if (self.abi) |abi| {
            try result.writer().print("-{s}", .{@tagName(abi)});
        }

        return result.toOwnedSlice();
    }

    pub fn allocDescription(self: CrossTarget, allocator: mem.Allocator) ![]u8 {
        // TODO is there anything else worthy of the description that is not

        // already captured in the triple?

        return self.zigTriple(allocator);
    }

    pub fn linuxTriple(self: CrossTarget, allocator: mem.Allocator) ![]u8 {
        return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi());
    }

    pub fn wantSharedLibSymLinks(self: CrossTarget) bool {
        return self.getOsTag() != .windows;
    }

    pub const VcpkgLinkage = std.builtin.LinkMode;

    /// Returned slice must be freed by the caller.
    pub fn vcpkgTriplet(self: CrossTarget, allocator: mem.Allocator, linkage: VcpkgLinkage) ![]u8 {
        const arch = switch (self.getCpuArch()) {
            .x86 => "x86",
            .x86_64 => "x64",

            .arm,
            .armeb,
            .thumb,
            .thumbeb,
            .aarch64_32,
            => "arm",

            .aarch64,
            .aarch64_be,
            => "arm64",

            else => return error.UnsupportedVcpkgArchitecture,
        };

        const os = switch (self.getOsTag()) {
            .windows => "windows",
            .linux => "linux",
            .macos => "macos",
            else => return error.UnsupportedVcpkgOperatingSystem,
        };

        const static_suffix = switch (linkage) {
            .Static => "-static",
            .Dynamic => "",
        };

        return std.fmt.allocPrint(allocator, "{s}-{s}{s}", .{ arch, os, static_suffix });
    }

    pub fn isGnuLibC(self: CrossTarget) bool {
        return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi());
    }

    pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void {
        assert(self.isGnuLibC());
        self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };
    }

    pub fn getObjectFormat(self: CrossTarget) Target.ObjectFormat {
        return self.ofmt orelse Target.ObjectFormat.default(self.getOsTag(), self.getCpuArch());
    }

    pub fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void {
        set.removeFeatureSet(self.cpu_features_sub);
        set.addFeatureSet(self.cpu_features_add);
        set.populateDependencies(self.getCpuArch().allFeaturesList());
        set.removeFeatureSet(self.cpu_features_sub);
    }

    fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void {
        var it = mem.splitScalar(u8, text, '.');
        const os_name = it.first();
        diags.os_name = os_name;
        const os_is_native = mem.eql(u8, os_name, "native");
        if (!os_is_native) {
            result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse
                return error.UnknownOperatingSystem;
        }
        const tag = result.getOsTag();
        diags.os_tag = tag;

        const version_text = it.rest();
        if (it.next() == null) return;

        switch (tag) {
            .freestanding,
            .ananas,
            .cloudabi,
            .fuchsia,
            .kfreebsd,
            .lv2,
            .solaris,
            .zos,
            .haiku,
            .minix,
            .rtems,
            .nacl,
            .aix,
            .cuda,
            .nvcl,
            .amdhsa,
            .ps4,
            .ps5,
            .elfiamcu,
            .mesa3d,
            .contiki,
            .amdpal,
            .hermit,
            .hurd,
            .wasi,
            .emscripten,
            .uefi,
            .opencl,
            .glsl450,
            .vulkan,
            .plan9,
            .driverkit,
            .shadermodel,
            .other,
            => return error.InvalidOperatingSystemVersion,

            .freebsd,
            .macos,
            .ios,
            .tvos,
            .watchos,
            .netbsd,
            .openbsd,
            .linux,
            .dragonfly,
            => {
                var range_it = mem.splitSequence(u8, version_text, "...");

                const min_text = range_it.next().?;
                const min_ver = parseVersion(min_text) catch |err| switch (err) {
                    error.Overflow => return error.InvalidOperatingSystemVersion,
                    error.InvalidVersion => return error.InvalidOperatingSystemVersion,
                };
                result.os_version_min = .{ .semver = min_ver };

                const max_text = range_it.next() orelse return;
                const max_ver = parseVersion(max_text) catch |err| switch (err) {
                    error.Overflow => return error.InvalidOperatingSystemVersion,
                    error.InvalidVersion => return error.InvalidOperatingSystemVersion,
                };
                result.os_version_max = .{ .semver = max_ver };
            },

            .windows => {
                var range_it = mem.splitSequence(u8, version_text, "...");

                const min_text = range_it.first();
                const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse
                    return error.InvalidOperatingSystemVersion;
                result.os_version_min = .{ .windows = min_ver };

                const max_text = range_it.next() orelse return;
                const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse
                    return error.InvalidOperatingSystemVersion;
                result.os_version_max = .{ .windows = max_ver };
            },
        }
    }

    test "CrossTarget.parse" {
        if (builtin.target.isGnuLibC()) {
            var cross_target = try CrossTarget.parse(.{});
            cross_target.setGnuLibCVersion(2, 1, 1);

            const text = try cross_target.zigTriple(std.testing.allocator);
            defer std.testing.allocator.free(text);

            var buf: [256]u8 = undefined;
            const triple = std.fmt.bufPrint(
                buf[0..],
                "native-native-{s}.2.1.1",
                .{@tagName(builtin.abi)},
            ) catch unreachable;

            try std.testing.expectEqualSlices(u8, triple, text);
        }
        {
            const cross_target = try CrossTarget.parse(.{
                .arch_os_abi = "aarch64-linux",
                .cpu_features = "native",
            });

            try std.testing.expect(cross_target.cpu_arch.? == .aarch64);
            try std.testing.expect(cross_target.cpu_model == .native);
        }
        {
            const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" });

            try std.testing.expect(cross_target.cpu_arch == null);
            try std.testing.expect(cross_target.isNative());

            const text = try cross_target.zigTriple(std.testing.allocator);
            defer std.testing.allocator.free(text);
            try std.testing.expectEqualSlices(u8, "native", text);
        }
        {
            const cross_target = try CrossTarget.parse(.{
                .arch_os_abi = "x86_64-linux-gnu",
                .cpu_features = "x86_64-sse-sse2-avx-cx8",
            });
            const target = cross_target.toTarget();

            try std.testing.expect(target.os.tag == .linux);
            try std.testing.expect(target.abi == .gnu);
            try std.testing.expect(target.cpu.arch == .x86_64);
            try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
            try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
            try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
            try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
            try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));

            try std.testing.expect(Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov }));
            try std.testing.expect(!Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx }));
            try std.testing.expect(Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87 }));
            try std.testing.expect(!Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87, .sse }));

            const text = try cross_target.zigTriple(std.testing.allocator);
            defer std.testing.allocator.free(text);
            try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text);
        }
        {
            const cross_target = try CrossTarget.parse(.{
                .arch_os_abi = "arm-linux-musleabihf",
                .cpu_features = "generic+v8a",
            });
            const target = cross_target.toTarget();

            try std.testing.expect(target.os.tag == .linux);
            try std.testing.expect(target.abi == .musleabihf);
            try std.testing.expect(target.cpu.arch == .arm);
            try std.testing.expect(target.cpu.model == &Target.arm.cpu.generic);
            try std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a));

            const text = try cross_target.zigTriple(std.testing.allocator);
            defer std.testing.allocator.free(text);
            try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text);
        }
        {
            const cross_target = try CrossTarget.parse(.{
                .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
                .cpu_features = "generic+v8a",
            });
            const target = cross_target.toTarget();

            try std.testing.expect(target.cpu.arch == .aarch64);
            try std.testing.expect(target.os.tag == .linux);
            try std.testing.expect(target.os.version_range.linux.range.min.major == 3);
            try std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
            try std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
            try std.testing.expect(target.os.version_range.linux.range.max.major == 4);
            try std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
            try std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
            try std.testing.expect(target.os.version_range.linux.glibc.major == 2);
            try std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
            try std.testing.expect(target.os.version_range.linux.glibc.patch == 0);
            try std.testing.expect(target.abi == .gnu);

            const text = try cross_target.zigTriple(std.testing.allocator);
            defer std.testing.allocator.free(text);
            try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text);
        }
    }