authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-17 11:54:50+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-17 15:27:52+02:00
logb61678c1b631f78c3a5da4eca2e77ae3577c1093
tree8c9139726a47759cb5435bee648a5dab16df9f8b
parent4d1c5d698fffe9684b72bcd72d60dfd758eb0ccd
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

std.Target.Query: rework native ABI detection a bit

Native detection of the libc version now only happens if the ABI component in the triple is native (= omitted in text form), rather than the OS component. So the ABI is either fully native or fully specified with no weird in-between. I think this much more closely matches people's mental model for this stuff; Matthew and I struggled to make sense of the current semantics for almost an hour, and ultimately agreed that it really shouldn't work the way it did prior to this patch. This is technically a breaking change because `native-native-gnu` no longer means "use glibc with the detected native glibc version", and this concept is no longer expressible. I don't consider this a great loss; I couldn't find any usage in the wild that would be broken by this, and I would argue it was more likely to be an unhelpful surprise. On the other hand, this patch makes `native-native-gnueabihf` on e.g. an `x86_64-linux-gnu` system use the default glibc version rather than the native one - which was a behavior I found bizarre. I think the ABI detection code is now also much easier to reason about. closes https://codeberg.org/ziglang/zig/issues/36863

2 files changed, 116 insertions(+), 135 deletions(-)

lib/std/Target/Query.zig+36-43
...@@ -26,33 +26,37 @@ cpu_features_sub: Target.Cpu.Feature.Set = .empty,...@@ -26,33 +26,37 @@ cpu_features_sub: Target.Cpu.Feature.Set = .empty,
26/// `null` means native.26/// `null` means native.
27os_tag: ?Target.Os.Tag = null,27os_tag: ?Target.Os.Tag = null,
2828
29/// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)29/// `null` means native when `os_tag` is native; otherwise, the default minimum version for
30/// then `null` for this field means native.30/// `cpu_arch`, `os_tag`, and `abi`.
31os_version_min: ?OsVersion = null,31os_version_min: ?OsVersion = null,
3232
33/// When cross compiling, `null` means default (latest known OS version).33/// `null` means native when `os_tag` is native; otherwise, the latest known OS version.
34/// When `os_tag` is native, `null` means equal to the native OS version.
35os_version_max: ?OsVersion = null,34os_version_max: ?OsVersion = null,
3635
37/// `null` means default when cross compiling, or native when `os_tag` is native.36/// `null` means native when `abi` is native; otherwise, the default minimum version for `cpu_arch`,
38/// If `isGnu()` is `false`, this must be `null` and is ignored.37/// `os_tag`, and `abi`.
38///
39/// Must be `null` when `abi` is `null` or `isGnu()` is `false`.
39glibc_version: ?SemanticVersion = null,40glibc_version: ?SemanticVersion = null,
4041
41/// `null` means default when cross compiling, or native when `os_tag` is native.42/// `null` means native when `abi` is native; otherwise, the default minimum version for `cpu_arch`
42/// If `isAndroid()` is `false`, this must be `null` and is ignored.43/// and `abi`.
44///
45/// Must be `null` when `abi` is `null` or `isAndroid()` is `false`.
43android_api_level: ?u32 = null,46android_api_level: ?u32 = null,
4447
45/// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.48/// `null` means native if `os_tag` is native; otherwise, best-effort default for `cpu_arch` and
49/// `os_tag`.
46abi: ?Target.Abi = null,50abi: ?Target.Abi = null,
4751
48/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path52/// `null` means default for the `cpu_arch` and `os_tag` combination.
49/// based on the `os_tag`. When `dynamic_linker` is a non-`null` empty string, no dynamic
50/// linker is used regardless of `os_tag`.
51dynamic_linker: ?Target.DynamicLinker = null,
52
53/// `null` means default for the cpu/arch/os combo.
54ofmt: ?Target.ObjectFormat = null,53ofmt: ?Target.ObjectFormat = null,
5554
55/// `null` means native when `abi` is native; otherwise, the standard path based on `cpu_arch`,
56/// `os_tag`, and `abi`. A non-`null` empty string means no dynamic linker is used regardless of the
57/// values of other fields.
58dynamic_linker: ?Target.DynamicLinker = null,
59
56pub const CpuModel = union(enum) {60pub const CpuModel = union(enum) {
57 /// Always native61 /// Always native
58 native,62 native,
...@@ -175,12 +179,12 @@ pub const ParseOptions = struct {...@@ -175,12 +179,12 @@ pub const ParseOptions = struct {
175 /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".179 /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
176 cpu_features: ?[]const u8 = null,180 cpu_features: ?[]const u8 = null,
177181
182 object_format: ?[]const u8 = null,
183
178 /// Absolute path to dynamic linker, to override the default, which is either a natively184 /// Absolute path to dynamic linker, to override the default, which is either a natively
179 /// detected path, or a standard path.185 /// detected path, or a standard path.
180 dynamic_linker: ?[]const u8 = null,186 dynamic_linker: ?[]const u8 = null,
181187
182 object_format: ?[]const u8 = null,
183
184 /// If this is provided, the function will populate some information about parsing failures,188 /// If this is provided, the function will populate some information about parsing failures,
185 /// so that user-friendly error messages can be delivered.189 /// so that user-friendly error messages can be delivered.
186 diagnostics: ?*Diagnostics = null,190 diagnostics: ?*Diagnostics = null,
...@@ -381,12 +385,16 @@ pub fn isNativeCpu(self: Query) bool {...@@ -381,12 +385,16 @@ pub fn isNativeCpu(self: Query) bool {
381}385}
382386
383pub fn isNativeOs(self: Query) bool {387pub fn isNativeOs(self: Query) bool {
384 return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and388 return self.os_tag == null and
385 self.dynamic_linker == null and self.glibc_version == null and self.android_api_level == null;389 self.os_version_min == null and
390 self.os_version_max == null;
386}391}
387392
388pub fn isNativeAbi(self: Query) bool {393pub fn isNativeAbi(self: Query) bool {
389 return self.os_tag == null and self.abi == null;394 return self.os_tag == null and
395 self.abi == null and
396 self.glibc_version == null and
397 self.android_api_level == null;
390}398}
391399
392pub fn isNativeTriple(self: Query) bool {400pub fn isNativeTriple(self: Query) bool {
...@@ -394,11 +402,13 @@ pub fn isNativeTriple(self: Query) bool {...@@ -394,11 +402,13 @@ pub fn isNativeTriple(self: Query) bool {
394}402}
395403
396pub fn isNative(self: Query) bool {404pub fn isNative(self: Query) bool {
397 return self.isNativeTriple() and self.ofmt == null;405 return self.isNativeTriple() and
406 self.ofmt == null and
407 self.dynamic_linker == null;
398}408}
399409
400pub fn canDetectLibC(self: Query) bool {410pub fn canDetectLibC(self: Query) bool {
401 if (self.isNativeOs()) return true;411 if (self.isNativeOs() and self.isNativeAbi()) return true;
402 if (self.os_tag) |os| {412 if (self.os_tag) |os| {
403 if (builtin.os.tag == .macos and os.isDarwin()) return true;413 if (builtin.os.tag == .macos and os.isDarwin()) return true;
404 if (os == .linux) {414 if (os == .linux) {
...@@ -459,14 +469,14 @@ pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 {...@@ -459,14 +469,14 @@ pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 {
459 }469 }
460470
461 if (self.glibc_version) |v| {471 if (self.glibc_version) |v| {
462 const name = if (self.abi) |abi| @tagName(abi) else "gnu";472 const name = @tagName(self.abi.?);
463 try result.ensureUnusedCapacity(gpa, name.len + 2);473 try result.ensureUnusedCapacity(gpa, name.len + 2);
464 result.appendAssumeCapacity('-');474 result.appendAssumeCapacity('-');
465 result.appendSliceAssumeCapacity(name);475 result.appendSliceAssumeCapacity(name);
466 result.appendAssumeCapacity('.');476 result.appendAssumeCapacity('.');
467 try formatVersion(v, gpa, &result);477 try formatVersion(v, gpa, &result);
468 } else if (self.android_api_level) |lvl| {478 } else if (self.android_api_level) |lvl| {
469 const name = if (self.abi) |abi| @tagName(abi) else "android";479 const name = @tagName(self.abi.?);
470 try result.ensureUnusedCapacity(gpa, name.len + 2);480 try result.ensureUnusedCapacity(gpa, name.len + 2);
471 result.appendAssumeCapacity('-');481 result.appendAssumeCapacity('-');
472 result.appendSliceAssumeCapacity(name);482 result.appendSliceAssumeCapacity(name);
...@@ -538,6 +548,7 @@ pub fn allocDescription(self: Query, allocator: Allocator) ![]u8 {...@@ -538,6 +548,7 @@ pub fn allocDescription(self: Query, allocator: Allocator) ![]u8 {
538 return self.zigTriple(allocator);548 return self.zigTriple(allocator);
539}549}
540550
551/// Deprecated; set `glibc_version` directly. Will be removed in 0.18.0.
541pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void {552pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void {
542 self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };553 self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };
543}554}
...@@ -599,8 +610,8 @@ pub fn eql(a: Query, b: Query) bool {...@@ -599,8 +610,8 @@ pub fn eql(a: Query, b: Query) bool {
599 if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false;610 if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false;
600 if (a.android_api_level != b.android_api_level) return false;611 if (a.android_api_level != b.android_api_level) return false;
601 if (a.abi != b.abi) return false;612 if (a.abi != b.abi) return false;
602 if (!dynamicLinkerEqualOpt(a.dynamic_linker, b.dynamic_linker)) return false;
603 if (a.ofmt != b.ofmt) return false;613 if (a.ofmt != b.ofmt) return false;
614 if (!dynamicLinkerEqualOpt(a.dynamic_linker, b.dynamic_linker)) return false;
604615
605 return true;616 return true;
606}617}
...@@ -620,24 +631,6 @@ fn dynamicLinkerEqualOpt(a: ?Target.DynamicLinker, b: ?Target.DynamicLinker) boo...@@ -620,24 +631,6 @@ fn dynamicLinkerEqualOpt(a: ?Target.DynamicLinker, b: ?Target.DynamicLinker) boo
620test parse {631test parse {
621 const io = std.testing.io;632 const io = std.testing.io;
622633
623 if (builtin.target.isGnuLibC()) {
624 var query = try Query.parse(.{});
625 query.setGnuLibCVersion(2, 1, 1);
626
627 const text = try query.zigTriple(std.testing.allocator);
628 defer std.testing.allocator.free(text);
629
630 try std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text);
631 }
632 if (builtin.target.abi.isAndroid()) {
633 var query = try Query.parse(.{});
634 query.android_api_level = 30;
635
636 const text = try query.zigTriple(std.testing.allocator);
637 defer std.testing.allocator.free(text);
638
639 try std.testing.expectEqualSlices(u8, "native-native-android.30", text);
640 }
641 {634 {
642 const query = try Query.parse(.{635 const query = try Query.parse(.{
643 .arch_os_abi = "aarch64-linux",636 .arch_os_abi = "aarch64-linux",
lib/std/zig/system.zig+80-92
...@@ -419,6 +419,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {...@@ -419,6 +419,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
419 var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) {419 var result = detectAbiAndDynamicLinker(io, cpu, os, query) catch |err| switch (err) {
420 error.Canceled => |e| return e,420 error.Canceled => |e| return e,
421 error.Unexpected => |e| return e,421 error.Unexpected => |e| return e,
422 error.ApiLevelQueryFailed => |e| return e,
422 error.WouldBlock => return error.Unexpected,423 error.WouldBlock => return error.Unexpected,
423 error.ConnectionResetByPeer => return error.Unexpected,424 error.ConnectionResetByPeer => return error.Unexpected,
424 error.NotOpenForReading => return error.Unexpected,425 error.NotOpenForReading => return error.Unexpected,
...@@ -497,27 +498,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {...@@ -497,27 +498,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
497 }498 }
498 }499 }
499500
500 if (builtin.os.tag == .linux and result.isBionicLibC() and query.os_tag == null and query.android_api_level == null) {
501 result.os.version_range.linux.android = detectAndroidApiLevel(io) catch |err| return switch (err) {
502 error.InvalidWtf8,
503 error.InvalidBatchScriptArg,
504 => unreachable, // Windows-only
505 error.ApiLevelQueryFailed => |e| e,
506 else => blk: {
507 std.log.err("spawning or reading from getprop failed ({s})", .{@errorName(err)});
508 switch (err) {
509 error.SystemResources,
510 error.FileSystem,
511 error.ProcessFdQuotaExceeded,
512 error.SystemFdQuotaExceeded,
513 error.SymLinkLoop,
514 => |e| break :blk e,
515 else => break :blk error.ApiLevelQueryFailed,
516 }
517 },
518 };
519 }
520
521 return result;501 return result;
522}502}
523503
...@@ -591,11 +571,13 @@ fn abiAndDynamicLinkerFromFile(...@@ -591,11 +571,13 @@ fn abiAndDynamicLinkerFromFile(
591 ld_info_list: []const LdInfo,571 ld_info_list: []const LdInfo,
592 query: Target.Query,572 query: Target.Query,
593) AbiAndDynamicLinkerFromFileError!Target {573) AbiAndDynamicLinkerFromFileError!Target {
574 assert(query.abi == null); // See the early exit in `detectAbiAndDynamicLinker`.
575
594 const io = file_reader.io;576 const io = file_reader.io;
595 var result: Target = .{577 var result: Target = .{
596 .cpu = cpu,578 .cpu = cpu,
597 .os = os,579 .os = os,
598 .abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag),580 .abi = Target.Abi.default(cpu.arch, os.tag),
599 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),581 .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
600 .dynamic_linker = query.dynamic_linker orelse .none,582 .dynamic_linker = query.dynamic_linker orelse .none,
601 };583 };
...@@ -609,26 +591,25 @@ fn abiAndDynamicLinkerFromFile(...@@ -609,26 +591,25 @@ fn abiAndDynamicLinkerFromFile(
609 .INTERP => {591 .INTERP => {
610 got_dyn_section = true;592 got_dyn_section = true;
611593
612 if (look_for_ld) {594 var interp: Target.DynamicLinker = .none;
613 const p_filesz = phdr.filesz;595 const p_filesz = phdr.filesz;
614 if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;596 if (p_filesz < 2) return error.InvalidElfFile;
615 const filesz: usize = @intCast(p_filesz);597 if (p_filesz > interp.buffer.len) return error.NameTooLong;
616 try file_reader.seekTo(phdr.offset);598 const filesz: usize = @intCast(p_filesz);
617 try file_reader.interface.readSliceAll(result.dynamic_linker.buffer[0..filesz]);599 try file_reader.seekTo(phdr.offset);
618 // PT.INTERP includes a null byte in filesz.600 try file_reader.interface.readSliceAll(interp.buffer[0..filesz]);
619 const len = filesz - 1;601 // PT.INTERP includes a null byte in filesz.
620 // dynamic_linker.max_byte is "max", not "len".602 const len = filesz - 1;
621 // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.603 // dynamic_linker.max_byte is "max", not "len".
622 result.dynamic_linker.len = @intCast(len);604 // We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
623605 interp.len = @intCast(len);
624 // Use it to determine ABI.606
625 const full_ld_path = result.dynamic_linker.buffer[0..len];607 // Use it to determine ABI.
626 for (ld_info_list) |ld_info| {608 for (ld_info_list) |ld_info| {
627 const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);609 if (std.mem.endsWith(u8, interp.get().?, fs.path.basename(ld_info.ld.get().?))) {
628 if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {610 result.abi = ld_info.abi;
629 result.abi = ld_info.abi;611 if (look_for_ld) result.dynamic_linker = interp;
630 break;612 break;
631 }
632 }613 }
633 }614 }
634 },615 },
...@@ -650,6 +631,10 @@ fn abiAndDynamicLinkerFromFile(...@@ -650,6 +631,10 @@ fn abiAndDynamicLinkerFromFile(
650 };631 };
651 }632 }
652633
634 if (look_for_ld and result.dynamic_linker.get() == null) {
635 result.dynamic_linker = .standard(cpu, os.tag, result.abi);
636 }
637
653 if (!got_dyn_section) {638 if (!got_dyn_section) {
654 return error.StaticElfFile;639 return error.StaticElfFile;
655 }640 }
...@@ -951,17 +936,14 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {...@@ -951,17 +936,14 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
951/// file recursively. If that does not provide the answer, then the function falls back to936/// file recursively. If that does not provide the answer, then the function falls back to
952/// defaults.937/// defaults.
953fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target {938fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target {
954 const native_target_has_ld = comptime Target.DynamicLinker.kind(builtin.os.tag) != .none;939 const native_os_uses_elf = comptime Target.ObjectFormat.default(builtin.target.os.tag, builtin.target.cpu.arch) == .elf;
955 const is_linux = builtin.target.os.tag == .linux;940 const native_ld_needs_abi = comptime Target.DynamicLinker.kind(builtin.target.os.tag) == .arch_os_abi;
956 const is_illumos = builtin.target.os.tag == .illumos;941 if (!native_os_uses_elf or !native_ld_needs_abi or query.os_tag != null or query.abi != null) {
957 const is_darwin = builtin.target.os.tag.isDarwin();
958 const have_all_info = query.dynamic_linker != null and
959 query.abi != null and (!is_linux or query.abi.?.isGnu());
960 const os_is_non_native = query.os_tag != null;
961 // The illumos environment is always the same.
962 if (!native_target_has_ld or have_all_info or os_is_non_native or is_illumos or is_darwin) {
963 return defaultAbiAndDynamicLinker(cpu, os, query);942 return defaultAbiAndDynamicLinker(cpu, os, query);
964 }943 }
944
945 assert(os.tag == builtin.target.os.tag);
946
965 // The current target's ABI cannot be relied on for this. For example, we may build the zig947 // The current target's ABI cannot be relied on for this. For example, we may build the zig
966 // compiler for target riscv64-linux-musl and provide a tarball for users to download.948 // compiler for target riscv64-linux-musl and provide a tarball for users to download.
967 // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined949 // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined
...@@ -979,30 +961,17 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -979,30 +961,17 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
979 var ld_info_list_buffer: [all_abis.len]LdInfo = undefined;961 var ld_info_list_buffer: [all_abis.len]LdInfo = undefined;
980 var ld_info_list_len: usize = 0;962 var ld_info_list_len: usize = 0;
981963
982 switch (Target.DynamicLinker.kind(os.tag)) {964 for (all_abis) |abi| {
983 // The OS has no dynamic linker. Leave the list empty and rely on `Abi.default()` to pick965 const ld: Target.DynamicLinker = .standard(cpu, os.tag, abi);
984 // something sensible in `abiAndDynamicLinkerFromFile()`.966
985 .none => {},967 // Does the generated target triple actually have a standard dynamic linker path?
986 // The OS has a system-wide dynamic linker. Unfortunately, this implies that there's no968 if (ld.get() == null) continue;
987 // useful ABI information that we can glean from it merely being present. That means the969
988 // best we can do for this case (for now) is also `Abi.default()`.970 ld_info_list_buffer[ld_info_list_len] = .{
989 .arch_os => {},971 .ld = ld,
990 // The OS can have different dynamic linker paths depending on libc/ABI. In this case, we972 .abi = abi,
991 // need to gather all the valid arch/OS/ABI combinations. `abiAndDynamicLinkerFromFile()`973 };
992 // will then look for a dynamic linker with a matching path on the system and pick the ABI974 ld_info_list_len += 1;
993 // we associated it with here.
994 .arch_os_abi => for (all_abis) |abi| {
995 const ld = Target.DynamicLinker.standard(cpu, os.tag, abi);
996
997 // Does the generated target triple actually have a standard dynamic linker path?
998 if (ld.get() == null) continue;
999
1000 ld_info_list_buffer[ld_info_list_len] = .{
1001 .ld = ld,
1002 .abi = abi,
1003 };
1004 ld_info_list_len += 1;
1005 },
1006 }975 }
1007976
1008 const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];977 const ld_info_list = ld_info_list_buffer[0..ld_info_list_len];
...@@ -1031,13 +1000,9 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1031,13 +1000,9 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1031 // it uses the file it references instead, doing the same logic1000 // it uses the file it references instead, doing the same logic
1032 // recursively in case it finds another shebang line.1001 // recursively in case it finds another shebang line.
10331002
1034 var file_name: []const u8 = switch (os.tag) {1003 // Since /usr/bin/env is hard-coded into the shebang line of many
1035 // Since /usr/bin/env is hard-coded into the shebang line of many1004 // portable scripts, it's a reasonably reliable path to start with.
1036 // portable scripts, it's a reasonably reliable path to start with.1005 var file_name: []const u8 = "/usr/bin/env";
1037 else => "/usr/bin/env",
1038 // Haiku does not have a /usr root directory.
1039 .haiku => "/bin/env",
1040 };
10411006
1042 while (true) {1007 while (true) {
1043 const file = Io.Dir.openFileAbsolute(io, file_name, .{}) catch |err| switch (err) {1008 const file = Io.Dir.openFileAbsolute(io, file_name, .{}) catch |err| switch (err) {
...@@ -1059,7 +1024,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1059,7 +1024,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1059 error.NetworkNotFound,1024 error.NetworkNotFound,
1060 error.FileTooBig,1025 error.FileTooBig,
1061 error.Unexpected,1026 error.Unexpected,
1062 => |e| if (e == error.FileNotFound and os.tag == .linux and mem.eql(u8, file_name, "/usr/bin/env")) {1027 => |e| if (builtin.target.os.tag == .linux and e == error.FileNotFound and mem.eql(u8, file_name, "/usr/bin/env")) {
1063 // Android does not have a /usr directory, so try again1028 // Android does not have a /usr directory, so try again
1064 file_name = "/system/bin/env";1029 file_name = "/system/bin/env";
1065 continue;1030 continue;
...@@ -1113,7 +1078,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1113,7 +1078,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1113 };1078 };
1114 defer file_reader.file.close(io);1079 defer file_reader.file.close(io);
11151080
1116 return abiAndDynamicLinkerFromFile(&file_reader, &header, cpu, os, ld_info_list, query) catch |err| switch (err) {1081 var result = abiAndDynamicLinkerFromFile(&file_reader, &header, cpu, os, ld_info_list, query) catch |err| switch (err) {
1117 error.FileSystem,1082 error.FileSystem,
1118 error.SystemResources,1083 error.SystemResources,
1119 error.SymLinkLoop,1084 error.SymLinkLoop,
...@@ -1129,6 +1094,29 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1129,6 +1094,29 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1129 return defaultAbiAndDynamicLinker(cpu, os, query);1094 return defaultAbiAndDynamicLinker(cpu, os, query);
1130 },1095 },
1131 };1096 };
1097
1098 if (builtin.target.os.tag == .linux and result.isBionicLibC() and query.android_api_level == null) {
1099 result.os.version_range.linux.android = detectAndroidApiLevel(io) catch |err| return switch (err) {
1100 error.InvalidWtf8,
1101 error.InvalidBatchScriptArg,
1102 => unreachable, // Windows-only
1103 error.ApiLevelQueryFailed => |e| e,
1104 else => blk: {
1105 std.log.err("spawning or reading from getprop failed ({s})", .{@errorName(err)});
1106 switch (err) {
1107 error.SystemResources,
1108 error.FileSystem,
1109 error.ProcessFdQuotaExceeded,
1110 error.SystemFdQuotaExceeded,
1111 error.SymLinkLoop,
1112 => |e| break :blk e,
1113 else => break :blk error.ApiLevelQueryFailed,
1114 }
1115 },
1116 };
1117 }
1118
1119 return result;
1132}1120}
11331121
1134fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target {1122fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) Target {
...@@ -1166,13 +1154,6 @@ fn detectAndroidApiLevel(io: Io) !u32 {...@@ -1166,13 +1154,6 @@ fn detectAndroidApiLevel(io: Io) !u32 {
1166 var stdout_buf: [92 + 1]u8 = undefined;1154 var stdout_buf: [92 + 1]u8 = undefined;
1167 var reader = child.stdout.?.readerStreaming(io, &.{});1155 var reader = child.stdout.?.readerStreaming(io, &.{});
1168 const n = try reader.interface.readSliceShort(&stdout_buf);1156 const n = try reader.interface.readSliceShort(&stdout_buf);
1169 const api_level = std.fmt.parseInt(u32, stdout_buf[0 .. n - 1], 10) catch |e| {
1170 std.log.err(
1171 "Could not parse API level, unexpected getprop output '{s}' ({s})",
1172 .{ stdout_buf[0 .. n - 1], @errorName(e) },
1173 );
1174 return error.ApiLevelQueryFailed;
1175 };
11761157
1177 switch (try child.wait(io)) {1158 switch (try child.wait(io)) {
1178 .exited => |code| if (code != 0) {1159 .exited => |code| if (code != 0) {
...@@ -1193,7 +1174,14 @@ fn detectAndroidApiLevel(io: Io) !u32 {...@@ -1193,7 +1174,14 @@ fn detectAndroidApiLevel(io: Io) !u32 {
1193 },1174 },
1194 }1175 }
11951176
1196 return api_level;1177 const result = mem.trimEnd(u8, stdout_buf[0..n], "\n");
1178 return std.fmt.parseInt(u32, result, 10) catch |e| {
1179 std.log.err(
1180 "Could not parse API level, unexpected getprop output '{s}' ({s})",
1181 .{ result, @errorName(e) },
1182 );
1183 return error.ApiLevelQueryFailed;
1184 };
1197}1185}
11981186
1199test {1187test {