1//! Contains all the same data as `Target`, additionally introducing the concept of "the native target". 2//! The purpose of this abstraction is to provide meaningful and unsurprising defaults. 3//! This struct does reference any resources and it is copyable. 4 5const CrossTarget = @This(); 6const std = @import("../std.zig"); 7const builtin = @import("builtin"); 8const assert = std.debug.assert; 9const Target = std.Target; 10const mem = std.mem; 11 12/// `null` means native. 13cpu_arch: ?Target.Cpu.Arch = null, 14 15cpu_model: CpuModel = CpuModel.determined_by_cpu_arch, 16 17/// Sparse set of CPU features to add to the set from `cpu_model`. 18cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, 19 20/// Sparse set of CPU features to remove from the set from `cpu_model`. 21cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty, 22 23/// `null` means native. 24os_tag: ?Target.Os.Tag = null, 25 26/// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native) 27/// then `null` for this field means native. 28os_version_min: ?OsVersion = null, 29 30/// When cross compiling, `null` means default (latest known OS version). 31/// When `os_tag` is native, `null` means equal to the native OS version. 32os_version_max: ?OsVersion = null, 33 34/// `null` means default when cross compiling, or native when os_tag is native. 35/// If `isGnuLibC()` is `false`, this must be `null` and is ignored. 36glibc_version: ?SemVer = null, 37 38/// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI. 39abi: ?Target.Abi = null, 40 41/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path 42/// based on the `os_tag`. 43dynamic_linker: DynamicLinker = DynamicLinker{}, 44 45pub const CpuModel = union(enum) { 46 /// Always native 47 native, 48 49 /// Always baseline 50 baseline, 51 52 /// If CPU Architecture is native, then the CPU model will be native. Otherwise, 53 /// it will be baseline. 54 determined_by_cpu_arch, 55 56 explicit: *const Target.Cpu.Model, 57}; 58 59pub const OsVersion = union(enum) { 60 none: void, 61 semver: SemVer, 62 windows: Target.Os.WindowsVersion, 63}; 64 65pub const SemVer = std.builtin.Version; 66 67pub const DynamicLinker = Target.DynamicLinker; 68 69pub fn fromTarget(target: Target) CrossTarget { 70 var result: CrossTarget = .{ 71 .cpu_arch = target.cpu.arch, 72 .cpu_model = .{ .explicit = target.cpu.model }, 73 .os_tag = target.os.tag, 74 .os_version_min = undefined, 75 .os_version_max = undefined, 76 .abi = target.abi, 77 .glibc_version = if (target.isGnuLibC()) 78 target.os.version_range.linux.glibc 79 else 80 null, 81 }; 82 result.updateOsVersionRange(target.os); 83 84 const all_features = target.cpu.arch.allFeaturesList(); 85 var cpu_model_set = target.cpu.model.features; 86 cpu_model_set.populateDependencies(all_features); 87 { 88 // The "add" set is the full set with the CPU Model set removed. 89 const add_set = &result.cpu_features_add; 90 add_set.* = target.cpu.features; 91 add_set.removeFeatureSet(cpu_model_set); 92 } 93 { 94 // The "sub" set is the features that are on in CPU Model set and off in the full set. 95 const sub_set = &result.cpu_features_sub; 96 sub_set.* = cpu_model_set; 97 sub_set.removeFeatureSet(target.cpu.features); 98 } 99 return result; 100} 101 102fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void { 103 switch (os.tag) { 104 .freestanding, 105 .ananas, 106 .cloudabi, 107 .fuchsia, 108 .kfreebsd, 109 .lv2, 110 .solaris, 111 .zos, 112 .haiku, 113 .minix, 114 .rtems, 115 .nacl, 116 .aix, 117 .cuda, 118 .nvcl, 119 .amdhsa, 120 .ps4, 121 .elfiamcu, 122 .mesa3d, 123 .contiki, 124 .amdpal, 125 .hermit, 126 .hurd, 127 .wasi, 128 .emscripten, 129 .uefi, 130 .opencl, 131 .glsl450, 132 .vulkan, 133 .plan9, 134 .other, 135 => { 136 self.os_version_min = .{ .none = {} }; 137 self.os_version_max = .{ .none = {} }; 138 }, 139 140 .freebsd, 141 .macos, 142 .ios, 143 .tvos, 144 .watchos, 145 .netbsd, 146 .openbsd, 147 .dragonfly, 148 => { 149 self.os_version_min = .{ .semver = os.version_range.semver.min }; 150 self.os_version_max = .{ .semver = os.version_range.semver.max }; 151 }, 152 153 .linux => { 154 self.os_version_min = .{ .semver = os.version_range.linux.range.min }; 155 self.os_version_max = .{ .semver = os.version_range.linux.range.max }; 156 }, 157 158 .windows => { 159 self.os_version_min = .{ .windows = os.version_range.windows.min }; 160 self.os_version_max = .{ .windows = os.version_range.windows.max }; 161 }, 162 } 163} 164 165/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. 166pub fn toTarget(self: CrossTarget) Target { 167 return .{ 168 .cpu = self.getCpu(), 169 .os = self.getOs(), 170 .abi = self.getAbi(), 171 }; 172} 173 174pub const ParseOptions = struct { 175 /// This is sometimes called a "triple". It looks roughly like this: 176 /// riscv64-linux-musl 177 /// The fields are, respectively: 178 /// * CPU Architecture 179 /// * Operating System (and optional version range) 180 /// * C ABI (optional, with optional glibc version) 181 /// The string "native" can be used for CPU architecture as well as Operating System. 182 /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted. 183 arch_os_abi: []const u8 = "native", 184 185 /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e" 186 /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features 187 /// to remove from the set. 188 /// The following special strings are recognized for CPU Model name: 189 /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set 190 /// of features that is expected to be supported on most available hardware. 191 /// * "native" - The native CPU model is to be detected when compiling. 192 /// If this field is not provided (`null`), then the value will depend on the 193 /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline". 194 cpu_features: ?[]const u8 = null, 195 196 /// Absolute path to dynamic linker, to override the default, which is either a natively 197 /// detected path, or a standard path. 198 dynamic_linker: ?[]const u8 = null, 199 200 /// If this is provided, the function will populate some information about parsing failures, 201 /// so that user-friendly error messages can be delivered. 202 diagnostics: ?*Diagnostics = null, 203 204 pub const Diagnostics = struct { 205 /// If the architecture was determined, this will be populated. 206 arch: ?Target.Cpu.Arch = null, 207 208 /// If the OS name was determined, this will be populated. 209 os_name: ?[]const u8 = null, 210 211 /// If the OS tag was determined, this will be populated. 212 os_tag: ?Target.Os.Tag = null, 213 214 /// If the ABI was determined, this will be populated. 215 abi: ?Target.Abi = null, 216 217 /// If the CPU name was determined, this will be populated. 218 cpu_name: ?[]const u8 = null, 219 220 /// If error.UnknownCpuFeature is returned, this will be populated. 221 unknown_feature_name: ?[]const u8 = null, 222 }; 223}; 224 225pub fn parse(args: ParseOptions) !CrossTarget { 226 var dummy_diags: ParseOptions.Diagnostics = undefined; 227 const diags = args.diagnostics orelse &dummy_diags; 228 229 var result: CrossTarget = .{ 230 .dynamic_linker = DynamicLinker.init(args.dynamic_linker), 231 }; 232 233 var it = mem.split(u8, args.arch_os_abi, "-"); 234 const arch_name = it.next().?; 235 const arch_is_native = mem.eql(u8, arch_name, "native"); 236 if (!arch_is_native) { 237 result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse 238 return error.UnknownArchitecture; 239 } 240 const arch = result.getCpuArch(); 241 diags.arch = arch; 242 243 if (it.next()) |os_text| { 244 try parseOs(&result, diags, os_text); 245 } else if (!arch_is_native) { 246 return error.MissingOperatingSystem; 247 } 248 249 const opt_abi_text = it.next(); 250 if (opt_abi_text) |abi_text| { 251 var abi_it = mem.split(u8, abi_text, "."); 252 const abi = std.meta.stringToEnum(Target.Abi, abi_it.next().?) orelse 253 return error.UnknownApplicationBinaryInterface; 254 result.abi = abi; 255 diags.abi = abi; 256 257 const abi_ver_text = abi_it.rest(); 258 if (abi_it.next() != null) { 259 if (result.isGnuLibC()) { 260 result.glibc_version = SemVer.parse(abi_ver_text) catch |err| switch (err) { 261 error.Overflow => return error.InvalidAbiVersion, 262 error.InvalidCharacter => return error.InvalidAbiVersion, 263 error.InvalidVersion => return error.InvalidAbiVersion, 264 }; 265 } else { 266 return error.InvalidAbiVersion; 267 } 268 } 269 } 270 271 if (it.next() != null) return error.UnexpectedExtraField; 272 273 if (args.cpu_features) |cpu_features| { 274 const all_features = arch.allFeaturesList(); 275 var index: usize = 0; 276 while (index < cpu_features.len and 277 cpu_features[index] != '+' and 278 cpu_features[index] != '-') 279 { 280 index += 1; 281 } 282 const cpu_name = cpu_features[0..index]; 283 diags.cpu_name = cpu_name; 284 285 const add_set = &result.cpu_features_add; 286 const sub_set = &result.cpu_features_sub; 287 if (mem.eql(u8, cpu_name, "native")) { 288 result.cpu_model = .native; 289 } else if (mem.eql(u8, cpu_name, "baseline")) { 290 result.cpu_model = .baseline; 291 } else { 292 result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) }; 293 } 294 295 while (index < cpu_features.len) { 296 const op = cpu_features[index]; 297 const set = switch (op) { 298 '+' => add_set, 299 '-' => sub_set, 300 else => unreachable, 301 }; 302 index += 1; 303 const start = index; 304 while (index < cpu_features.len and 305 cpu_features[index] != '+' and 306 cpu_features[index] != '-') 307 { 308 index += 1; 309 } 310 const feature_name = cpu_features[start..index]; 311 for (all_features) |feature, feat_index_usize| { 312 const feat_index = @intCast(Target.Cpu.Feature.Set.Index, feat_index_usize); 313 if (mem.eql(u8, feature_name, feature.name)) { 314 set.addFeature(feat_index); 315 break; 316 } 317 } else { 318 diags.unknown_feature_name = feature_name; 319 return error.UnknownCpuFeature; 320 } 321 } 322 } 323 324 return result; 325} 326 327/// Similar to `parse` except instead of fully parsing, it only determines the CPU 328/// architecture and returns it if it can be determined, and returns `null` otherwise. 329/// This is intended to be used if the API user of CrossTarget needs to learn the 330/// target CPU architecture in order to fully populate `ParseOptions`. 331pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch { 332 var it = mem.split(u8, args.arch_os_abi, "-"); 333 const arch_name = it.next().?; 334 const arch_is_native = mem.eql(u8, arch_name, "native"); 335 if (arch_is_native) { 336 return builtin.cpu.arch; 337 } else { 338 return std.meta.stringToEnum(Target.Cpu.Arch, arch_name); 339 } 340} 341 342/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. 343pub fn getCpu(self: CrossTarget) Target.Cpu { 344 switch (self.cpu_model) { 345 .native => { 346 // This works when doing `zig build` because Zig generates a build executable using 347 // native CPU model & features. However this will not be accurate otherwise, and 348 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. 349 return builtin.cpu; 350 }, 351 .baseline => { 352 var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch()); 353 self.updateCpuFeatures(&adjusted_baseline.features); 354 return adjusted_baseline; 355 }, 356 .determined_by_cpu_arch => if (self.cpu_arch == null) { 357 // This works when doing `zig build` because Zig generates a build executable using 358 // native CPU model & features. However this will not be accurate otherwise, and 359 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. 360 return builtin.cpu; 361 } else { 362 var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch()); 363 self.updateCpuFeatures(&adjusted_baseline.features); 364 return adjusted_baseline; 365 }, 366 .explicit => |model| { 367 var adjusted_model = model.toCpu(self.getCpuArch()); 368 self.updateCpuFeatures(&adjusted_model.features); 369 return adjusted_model; 370 }, 371 } 372} 373 374pub fn getCpuArch(self: CrossTarget) Target.Cpu.Arch { 375 return self.cpu_arch orelse builtin.cpu.arch; 376} 377 378pub fn getCpuModel(self: CrossTarget) *const Target.Cpu.Model { 379 return switch (self.cpu_model) { 380 .explicit => |cpu_model| cpu_model, 381 else => self.getCpu().model, 382 }; 383} 384 385pub fn getCpuFeatures(self: CrossTarget) Target.Cpu.Feature.Set { 386 return self.getCpu().features; 387} 388 389/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. 390pub fn getOs(self: CrossTarget) Target.Os { 391 // `builtin.os` works when doing `zig build` because Zig generates a build executable using 392 // native OS version range. However this will not be accurate otherwise, and 393 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. 394 var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange(self.getCpuArch()) else builtin.os; 395 396 if (self.os_version_min) |min| switch (min) { 397 .none => {}, 398 .semver => |semver| switch (self.getOsTag()) { 399 .linux => adjusted_os.version_range.linux.range.min = semver, 400 else => adjusted_os.version_range.semver.min = semver, 401 }, 402 .windows => |win_ver| adjusted_os.version_range.windows.min = win_ver, 403 }; 404 405 if (self.os_version_max) |max| switch (max) { 406 .none => {}, 407 .semver => |semver| switch (self.getOsTag()) { 408 .linux => adjusted_os.version_range.linux.range.max = semver, 409 else => adjusted_os.version_range.semver.max = semver, 410 }, 411 .windows => |win_ver| adjusted_os.version_range.windows.max = win_ver, 412 }; 413 414 if (self.glibc_version) |glibc| { 415 assert(self.isGnuLibC()); 416 adjusted_os.version_range.linux.glibc = glibc; 417 } 418 419 return adjusted_os; 420} 421 422pub fn getOsTag(self: CrossTarget) Target.Os.Tag { 423 return self.os_tag orelse builtin.os.tag; 424} 425 426/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. 427pub fn getOsVersionMin(self: CrossTarget) OsVersion { 428 if (self.os_version_min) |version_min| return version_min; 429 var tmp: CrossTarget = undefined; 430 tmp.updateOsVersionRange(self.getOs()); 431 return tmp.os_version_min.?; 432} 433 434/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. 435pub fn getOsVersionMax(self: CrossTarget) OsVersion { 436 if (self.os_version_max) |version_max| return version_max; 437 var tmp: CrossTarget = undefined; 438 tmp.updateOsVersionRange(self.getOs()); 439 return tmp.os_version_max.?; 440} 441 442/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. 443pub fn getAbi(self: CrossTarget) Target.Abi { 444 if (self.abi) |abi| return abi; 445 446 if (self.os_tag == null) { 447 // This works when doing `zig build` because Zig generates a build executable using 448 // native CPU model & features. However this will not be accurate otherwise, and 449 // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. 450 return builtin.abi; 451 } 452 453 return Target.Abi.default(self.getCpuArch(), self.getOs()); 454} 455 456pub fn isFreeBSD(self: CrossTarget) bool { 457 return self.getOsTag() == .freebsd; 458} 459 460pub fn isDarwin(self: CrossTarget) bool { 461 return self.getOsTag().isDarwin(); 462} 463 464pub fn isNetBSD(self: CrossTarget) bool { 465 return self.getOsTag() == .netbsd; 466} 467 468pub fn isOpenBSD(self: CrossTarget) bool { 469 return self.getOsTag() == .openbsd; 470} 471 472pub fn isUefi(self: CrossTarget) bool { 473 return self.getOsTag() == .uefi; 474} 475 476pub fn isDragonFlyBSD(self: CrossTarget) bool { 477 return self.getOsTag() == .dragonfly; 478} 479 480pub fn isLinux(self: CrossTarget) bool { 481 return self.getOsTag() == .linux; 482} 483 484pub fn isWindows(self: CrossTarget) bool { 485 return self.getOsTag() == .windows; 486} 487 488pub fn exeFileExt(self: CrossTarget) [:0]const u8 { 489 return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag()); 490} 491 492pub fn staticLibSuffix(self: CrossTarget) [:0]const u8 { 493 return Target.staticLibSuffix_os_abi(self.getOsTag(), self.getAbi()); 494} 495 496pub fn dynamicLibSuffix(self: CrossTarget) [:0]const u8 { 497 return self.getOsTag().dynamicLibSuffix(); 498} 499 500pub fn libPrefix(self: CrossTarget) [:0]const u8 { 501 return Target.libPrefix_os_abi(self.getOsTag(), self.getAbi()); 502} 503 504pub fn isNativeCpu(self: CrossTarget) bool { 505 return self.cpu_arch == null and 506 (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and 507 self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty(); 508} 509 510pub fn isNativeOs(self: CrossTarget) bool { 511 return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and 512 self.dynamic_linker.get() == null and self.glibc_version == null; 513} 514 515pub fn isNativeAbi(self: CrossTarget) bool { 516 return self.os_tag == null and self.abi == null; 517} 518 519pub fn isNative(self: CrossTarget) bool { 520 return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi(); 521} 522 523pub fn zigTriple(self: CrossTarget, allocator: mem.Allocator) error{OutOfMemory}![]u8 { 524 if (self.isNative()) { 525 return allocator.dupe(u8, "native"); 526 } 527 528 const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native"; 529 const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native"; 530 531 var result = std.ArrayList(u8).init(allocator); 532 defer result.deinit(); 533 534 try result.writer().print("{s}-{s}", .{ arch_name, os_name }); 535 536 // The zig target syntax does not allow specifying a max os version with no min, so 537 // if either are present, we need the min. 538 if (self.os_version_min != null or self.os_version_max != null) { 539 switch (self.getOsVersionMin()) { 540 .none => {}, 541 .semver => |v| try result.writer().print(".{}", .{v}), 542 .windows => |v| try result.writer().print("{s}", .{v}), 543 } 544 } 545 if (self.os_version_max) |max| { 546 switch (max) { 547 .none => {}, 548 .semver => |v| try result.writer().print("...{}", .{v}), 549 .windows => |v| try result.writer().print("..{s}", .{v}), 550 } 551 } 552 553 if (self.glibc_version) |v| { 554 try result.writer().print("-{s}.{}", .{ @tagName(self.getAbi()), v }); 555 } else if (self.abi) |abi| { 556 try result.writer().print("-{s}", .{@tagName(abi)}); 557 } 558 559 return result.toOwnedSlice(); 560} 561 562pub fn allocDescription(self: CrossTarget, allocator: mem.Allocator) ![]u8 { 563 // TODO is there anything else worthy of the description that is not 564 // already captured in the triple? 565 return self.zigTriple(allocator); 566} 567 568pub fn linuxTriple(self: CrossTarget, allocator: mem.Allocator) ![]u8 { 569 return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi()); 570} 571 572pub fn wantSharedLibSymLinks(self: CrossTarget) bool { 573 return self.getOsTag() != .windows; 574} 575 576pub const VcpkgLinkage = std.builtin.LinkMode; 577 578/// Returned slice must be freed by the caller. 579pub fn vcpkgTriplet(self: CrossTarget, allocator: mem.Allocator, linkage: VcpkgLinkage) ![]u8 { 580 const arch = switch (self.getCpuArch()) { 581 .i386 => "x86", 582 .x86_64 => "x64", 583 584 .arm, 585 .armeb, 586 .thumb, 587 .thumbeb, 588 .aarch64_32, 589 => "arm", 590 591 .aarch64, 592 .aarch64_be, 593 => "arm64", 594 595 else => return error.UnsupportedVcpkgArchitecture, 596 }; 597 598 const os = switch (self.getOsTag()) { 599 .windows => "windows", 600 .linux => "linux", 601 .macos => "macos", 602 else => return error.UnsupportedVcpkgOperatingSystem, 603 }; 604 605 const static_suffix = switch (linkage) { 606 .Static => "-static", 607 .Dynamic => "", 608 }; 609 610 return std.fmt.allocPrint(allocator, "{s}-{s}{s}", .{ arch, os, static_suffix }); 611} 612 613pub fn isGnuLibC(self: CrossTarget) bool { 614 return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi()); 615} 616 617pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void { 618 assert(self.isGnuLibC()); 619 self.glibc_version = SemVer{ .major = major, .minor = minor, .patch = patch }; 620} 621 622pub fn getObjectFormat(self: CrossTarget) Target.ObjectFormat { 623 return Target.getObjectFormatSimple(self.getOsTag(), self.getCpuArch()); 624} 625 626pub fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void { 627 set.removeFeatureSet(self.cpu_features_sub); 628 set.addFeatureSet(self.cpu_features_add); 629 set.populateDependencies(self.getCpuArch().allFeaturesList()); 630 set.removeFeatureSet(self.cpu_features_sub); 631} 632 633fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void { 634 var it = mem.split(u8, text, "."); 635 const os_name = it.next().?; 636 diags.os_name = os_name; 637 const os_is_native = mem.eql(u8, os_name, "native"); 638 if (!os_is_native) { 639 result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse 640 return error.UnknownOperatingSystem; 641 } 642 const tag = result.getOsTag(); 643 diags.os_tag = tag; 644 645 const version_text = it.rest(); 646 if (it.next() == null) return; 647 648 switch (tag) { 649 .freestanding, 650 .ananas, 651 .cloudabi, 652 .fuchsia, 653 .kfreebsd, 654 .lv2, 655 .solaris, 656 .zos, 657 .haiku, 658 .minix, 659 .rtems, 660 .nacl, 661 .aix, 662 .cuda, 663 .nvcl, 664 .amdhsa, 665 .ps4, 666 .elfiamcu, 667 .mesa3d, 668 .contiki, 669 .amdpal, 670 .hermit, 671 .hurd, 672 .wasi, 673 .emscripten, 674 .uefi, 675 .opencl, 676 .glsl450, 677 .vulkan, 678 .plan9, 679 .other, 680 => return error.InvalidOperatingSystemVersion, 681 682 .freebsd, 683 .macos, 684 .ios, 685 .tvos, 686 .watchos, 687 .netbsd, 688 .openbsd, 689 .linux, 690 .dragonfly, 691 => { 692 var range_it = mem.split(u8, version_text, "..."); 693 694 const min_text = range_it.next().?; 695 const min_ver = SemVer.parse(min_text) catch |err| switch (err) { 696 error.Overflow => return error.InvalidOperatingSystemVersion, 697 error.InvalidCharacter => return error.InvalidOperatingSystemVersion, 698 error.InvalidVersion => return error.InvalidOperatingSystemVersion, 699 }; 700 result.os_version_min = .{ .semver = min_ver }; 701 702 const max_text = range_it.next() orelse return; 703 const max_ver = SemVer.parse(max_text) catch |err| switch (err) { 704 error.Overflow => return error.InvalidOperatingSystemVersion, 705 error.InvalidCharacter => return error.InvalidOperatingSystemVersion, 706 error.InvalidVersion => return error.InvalidOperatingSystemVersion, 707 }; 708 result.os_version_max = .{ .semver = max_ver }; 709 }, 710 711 .windows => { 712 var range_it = mem.split(u8, version_text, "..."); 713 714 const min_text = range_it.next().?; 715 const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse 716 return error.InvalidOperatingSystemVersion; 717 result.os_version_min = .{ .windows = min_ver }; 718 719 const max_text = range_it.next() orelse return; 720 const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse 721 return error.InvalidOperatingSystemVersion; 722 result.os_version_max = .{ .windows = max_ver }; 723 }, 724 } 725} 726 727test "CrossTarget.parse" { 728 if (builtin.target.isGnuLibC()) { 729 var cross_target = try CrossTarget.parse(.{}); 730 cross_target.setGnuLibCVersion(2, 1, 1); 731 732 const text = try cross_target.zigTriple(std.testing.allocator); 733 defer std.testing.allocator.free(text); 734 735 var buf: [256]u8 = undefined; 736 const triple = std.fmt.bufPrint( 737 buf[0..], 738 "native-native-{s}.2.1.1", 739 .{@tagName(builtin.abi)}, 740 ) catch unreachable; 741 742 try std.testing.expectEqualSlices(u8, triple, text); 743 } 744 { 745 const cross_target = try CrossTarget.parse(.{ 746 .arch_os_abi = "aarch64-linux", 747 .cpu_features = "native", 748 }); 749 750 try std.testing.expect(cross_target.cpu_arch.? == .aarch64); 751 try std.testing.expect(cross_target.cpu_model == .native); 752 } 753 { 754 const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" }); 755 756 try std.testing.expect(cross_target.cpu_arch == null); 757 try std.testing.expect(cross_target.isNative()); 758 759 const text = try cross_target.zigTriple(std.testing.allocator); 760 defer std.testing.allocator.free(text); 761 try std.testing.expectEqualSlices(u8, "native", text); 762 } 763 { 764 const cross_target = try CrossTarget.parse(.{ 765 .arch_os_abi = "x86_64-linux-gnu", 766 .cpu_features = "x86_64-sse-sse2-avx-cx8", 767 }); 768 const target = cross_target.toTarget(); 769 770 try std.testing.expect(target.os.tag == .linux); 771 try std.testing.expect(target.abi == .gnu); 772 try std.testing.expect(target.cpu.arch == .x86_64); 773 try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse)); 774 try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx)); 775 try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8)); 776 try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov)); 777 try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr)); 778 779 try std.testing.expect(Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov })); 780 try std.testing.expect(!Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx })); 781 try std.testing.expect(Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87 })); 782 try std.testing.expect(!Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87, .sse })); 783 784 const text = try cross_target.zigTriple(std.testing.allocator); 785 defer std.testing.allocator.free(text); 786 try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text); 787 } 788 { 789 const cross_target = try CrossTarget.parse(.{ 790 .arch_os_abi = "arm-linux-musleabihf", 791 .cpu_features = "generic+v8a", 792 }); 793 const target = cross_target.toTarget(); 794 795 try std.testing.expect(target.os.tag == .linux); 796 try std.testing.expect(target.abi == .musleabihf); 797 try std.testing.expect(target.cpu.arch == .arm); 798 try std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); 799 try std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); 800 801 const text = try cross_target.zigTriple(std.testing.allocator); 802 defer std.testing.allocator.free(text); 803 try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text); 804 } 805 { 806 const cross_target = try CrossTarget.parse(.{ 807 .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", 808 .cpu_features = "generic+v8a", 809 }); 810 const target = cross_target.toTarget(); 811 812 try std.testing.expect(target.cpu.arch == .aarch64); 813 try std.testing.expect(target.os.tag == .linux); 814 try std.testing.expect(target.os.version_range.linux.range.min.major == 3); 815 try std.testing.expect(target.os.version_range.linux.range.min.minor == 10); 816 try std.testing.expect(target.os.version_range.linux.range.min.patch == 0); 817 try std.testing.expect(target.os.version_range.linux.range.max.major == 4); 818 try std.testing.expect(target.os.version_range.linux.range.max.minor == 4); 819 try std.testing.expect(target.os.version_range.linux.range.max.patch == 1); 820 try std.testing.expect(target.os.version_range.linux.glibc.major == 2); 821 try std.testing.expect(target.os.version_range.linux.glibc.minor == 27); 822 try std.testing.expect(target.os.version_range.linux.glibc.patch == 0); 823 try std.testing.expect(target.abi == .gnu); 824 825 const text = try cross_target.zigTriple(std.testing.allocator); 826 defer std.testing.allocator.free(text); 827 try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text); 828 } 829} 830