Function resolveTargetQuery [src]

Given a Target.Query, which specifies in detail which parts of the target should be detected natively, which should be standard or default, and which are provided explicitly, this function resolves the native components by detecting the native system, and then resolves standard/default parts relative to that.

Prototype

pub fn resolveTargetQuery(query: Target.Query) DetectError!Target

Parameters

query: Target.Query

Possible Errors

DeviceBusy
FileSystem
OSVersionDetectionFail
ProcessFdQuotaExceeded
ProcessNotFound
SymLinkLoop
SystemFdQuotaExceeded
SystemResources
Unexpected

Source

pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the // native CPU architecture as being different than the current target), we use this: const query_cpu_arch = query.cpu_arch orelse builtin.cpu.arch; const query_os_tag = query.os_tag orelse builtin.os.tag; const query_abi = query.abi orelse builtin.abi; var os = query_os_tag.defaultVersionRange(query_cpu_arch, query_abi); if (query.os_tag == null) { switch (builtin.target.os.tag) { .linux => { const uts = posix.uname(); const release = mem.sliceTo(&uts.release, 0); // The release field sometimes has a weird format, // `Version.parse` will attempt to find some meaningful interpretation. if (std.SemanticVersion.parse(release)) |ver| { os.version_range.linux.range.min = ver; os.version_range.linux.range.max = ver; } else |err| switch (err) { error.Overflow => {}, error.InvalidVersion => {}, } }, .solaris, .illumos => { const uts = posix.uname(); const release = mem.sliceTo(&uts.release, 0); if (std.SemanticVersion.parse(release)) |ver| { os.version_range.semver.min = ver; os.version_range.semver.max = ver; } else |err| switch (err) { error.Overflow => {}, error.InvalidVersion => {}, } }, .windows => { const detected_version = windows.detectRuntimeVersion(); os.version_range.windows.min = detected_version; os.version_range.windows.max = detected_version; }, .macos => try darwin.macos.detect(&os), .freebsd, .netbsd, .dragonfly => { const key = switch (builtin.target.os.tag) { .freebsd => "kern.osreldate", .netbsd, .dragonfly => "kern.osrevision", else => unreachable, }; var value: u32 = undefined; var len: usize = @sizeOf(@TypeOf(value)); posix.sysctlbynameZ(key, &value, &len, null, 0) catch |err| switch (err) { error.NameTooLong => unreachable, // constant, known good value error.PermissionDenied => unreachable, // only when setting values, error.SystemResources => unreachable, // memory already on the stack error.UnknownName => unreachable, // constant, known good value error.Unexpected => return error.OSVersionDetectionFail, }; switch (builtin.target.os.tag) { .freebsd => { // https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/versions.html // Major * 100,000 has been convention since FreeBSD 2.2 (1997) // Minor * 1(0),000 summed has been convention since FreeBSD 2.2 (1997) // e.g. 492101 = 4.11-STABLE = 4.(9+2) const major = value / 100_000; const minor1 = value % 100_000 / 10_000; // usually 0 since 5.1 const minor2 = value % 10_000 / 1_000; // 0 before 5.1, minor version since const patch = value % 1_000; os.version_range.semver.min = .{ .major = major, .minor = minor1 + minor2, .patch = patch }; os.version_range.semver.max = os.version_range.semver.min; }, .netbsd => { // #define __NetBSD_Version__ MMmmrrpp00 // // M = major version // m = minor version; a minor number of 99 indicates current. // r = 0 (*) // p = patchlevel const major = value / 100_000_000; const minor = value % 100_000_000 / 1_000_000; const patch = value % 10_000 / 100; os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch }; os.version_range.semver.max = os.version_range.semver.min; }, .dragonfly => { // https://github.com/DragonFlyBSD/DragonFlyBSD/blob/cb2cde83771754aeef9bb3251ee48959138dec87/Makefile.inc1#L15-L17 // flat base10 format: Mmmmpp // M = major // m = minor; odd-numbers indicate current dev branch // p = patch const major = value / 100_000; const minor = value % 100_000 / 100; const patch = value % 100; os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch }; os.version_range.semver.max = os.version_range.semver.min; }, else => unreachable, } }, .openbsd => { const mib: [2]c_int = [_]c_int{ posix.CTL.KERN, posix.KERN.OSRELEASE, }; var buf: [64]u8 = undefined; // consider that sysctl result includes null-termination // reserve 1 byte to ensure we never overflow when appending ".0" var len: usize = buf.len - 1; posix.sysctl(&mib, &buf, &len, null, 0) catch |err| switch (err) { error.NameTooLong => unreachable, // constant, known good value error.PermissionDenied => unreachable, // only when setting values, error.SystemResources => unreachable, // memory already on the stack error.UnknownName => unreachable, // constant, known good value error.Unexpected => return error.OSVersionDetectionFail, }; // append ".0" to satisfy semver buf[len - 1] = '.'; buf[len] = '0'; len += 1; if (std.SemanticVersion.parse(buf[0..len])) |ver| { os.version_range.semver.min = ver; os.version_range.semver.max = ver; } else |_| { return error.OSVersionDetectionFail; } }, else => { // Unimplemented, fall back to default version range. }, } } if (query.os_version_min) |min| switch (min) { .none => {}, .semver => |semver| switch (os.tag.versionRangeTag()) { inline .hurd, .linux => |t| @field(os.version_range, @tagName(t)).range.min = semver, else => os.version_range.semver.min = semver, }, .windows => |win_ver| os.version_range.windows.min = win_ver, }; if (query.os_version_max) |max| switch (max) { .none => {}, .semver => |semver| switch (os.tag.versionRangeTag()) { inline .hurd, .linux => |t| @field(os.version_range, @tagName(t)).range.max = semver, else => os.version_range.semver.max = semver, }, .windows => |win_ver| os.version_range.windows.max = win_ver, }; if (query.glibc_version) |glibc| { switch (os.tag.versionRangeTag()) { inline .hurd, .linux => |t| @field(os.version_range, @tagName(t)).glibc = glibc, else => {}, } } if (query.android_api_level) |android| { os.version_range.linux.android = android; } var cpu = switch (query.cpu_model) { .native => detectNativeCpuAndFeatures(query_cpu_arch, os, query), .baseline => Target.Cpu.baseline(query_cpu_arch, os), .determined_by_arch_os => if (query.cpu_arch == null) detectNativeCpuAndFeatures(query_cpu_arch, os, query) else Target.Cpu.baseline(query_cpu_arch, os), .explicit => |model| model.toCpu(query_cpu_arch), } orelse backup_cpu_detection: { break :backup_cpu_detection Target.Cpu.baseline(query_cpu_arch, os); }; // For x86, we need to populate some CPU feature flags depending on architecture // and mode: // * 16bit_mode => if the abi is code16 // * 32bit_mode => if the arch is x86 // However, the "mode" flags can be used as overrides, so if the user explicitly // sets one of them, that takes precedence. switch (query_cpu_arch) { .x86 => { if (!Target.x86.featureSetHasAny(query.cpu_features_add, .{ .@"16bit_mode", .@"32bit_mode", })) { switch (query_abi) { .code16 => cpu.features.addFeature( @intFromEnum(Target.x86.Feature.@"16bit_mode"), ), else => cpu.features.addFeature( @intFromEnum(Target.x86.Feature.@"32bit_mode"), ), } } }, .arm, .armeb => { // XXX What do we do if the target has the noarm feature? // What do we do if the user specifies +thumb_mode? }, .thumb, .thumbeb => { cpu.features.addFeature( @intFromEnum(Target.arm.Feature.thumb_mode), ); }, else => {}, } updateCpuFeatures( &cpu.features, cpu.arch.allFeaturesList(), query.cpu_features_add, query.cpu_features_sub, ); var result = try detectAbiAndDynamicLinker(cpu, os, query); // These CPU feature hacks have to come after ABI detection. { if (result.cpu.arch == .hexagon) { // Both LLVM and LLD have broken support for the small data area. Yet LLVM has the // feature on by default for all Hexagon CPUs. Clang sort of solves this by defaulting // the `-gpsize` command line parameter for the Hexagon backend to 0, so that no // constants get placed in the SDA. (This of course breaks down if the user passes // `-G ` to Clang...) We can't do the `-gpsize` hack because we can have multiple // concurrent LLVM emit jobs, and command line options in LLVM are shared globally. So // just force this feature off. Lovely stuff. result.cpu.features.removeFeature(@intFromEnum(Target.hexagon.Feature.small_data)); } // https://github.com/llvm/llvm-project/issues/105978 if (result.cpu.arch.isArm() and result.abi.float() == .soft) { result.cpu.features.removeFeature(@intFromEnum(Target.arm.Feature.vfp2)); } } // It's possible that we detect the native ABI, but fail to detect the OS version or were told // to use the default OS version range. In that case, while we can't determine the exact native // OS version, we do at least know that some ABIs require a particular OS version (by way of // `std.zig.target.available_libcs`). So in this case, adjust the OS version to the minimum that // we know is required. if (result.abi != query_abi and query.os_version_min == null) { const result_ver_range = &result.os.version_range; const abi_ver_range = result.os.tag.defaultVersionRange(result.cpu.arch, result.abi).version_range; switch (result.os.tag.versionRangeTag()) { .none => {}, .semver => if (result_ver_range.semver.min.order(abi_ver_range.semver.min) == .lt) { result_ver_range.semver.min = abi_ver_range.semver.min; }, inline .hurd, .linux => |t| { if (@field(result_ver_range, @tagName(t)).range.min.order(@field(abi_ver_range, @tagName(t)).range.min) == .lt) { @field(result_ver_range, @tagName(t)).range.min = @field(abi_ver_range, @tagName(t)).range.min; } if (@field(result_ver_range, @tagName(t)).glibc.order(@field(abi_ver_range, @tagName(t)).glibc) == .lt and query.glibc_version == null) { @field(result_ver_range, @tagName(t)).glibc = @field(abi_ver_range, @tagName(t)).glibc; } }, .windows => if (!result_ver_range.windows.min.isAtLeast(abi_ver_range.windows.min)) { result_ver_range.windows.min = abi_ver_range.windows.min; }, } } return result; }