| ... | ... | @@ -211,6 +211,8 @@ pub const DetectError = error{ |
| 211 | 211 | DeviceBusy, |
| 212 | 212 | OSVersionDetectionFail, |
| 213 | 213 | Unexpected, |
| 214 | /// Android-only. Querying API level through `getprop` failed. |
| 215 | ApiLevelQueryFailed, |
| 214 | 216 | } || Io.Cancelable; |
| 215 | 217 | |
| 216 | 218 | /// Given a `Target.Query`, which specifies in detail which parts of the |
| ... | ... | @@ -500,6 +502,28 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target { |
| 500 | 502 | } |
| 501 | 503 | } |
| 502 | 504 | |
| 505 | if (builtin.os.tag == .linux and result.isBionicLibC() and query.os_tag == null and query.android_api_level == null) { |
| 506 | result.os.version_range.linux.android = detectAndroidApiLevel(io) catch |err| return switch (err) { |
| 507 | error.InvalidWtf8, |
| 508 | error.CurrentWorkingDirectoryUnlinked, |
| 509 | error.InvalidBatchScriptArg, |
| 510 | => unreachable, // Windows-only |
| 511 | error.ApiLevelQueryFailed => |e| e, |
| 512 | else => blk: { |
| 513 | std.log.err("spawning or reading from getprop failed ({s})", .{@errorName(err)}); |
| 514 | switch (err) { |
| 515 | error.SystemResources, |
| 516 | error.FileSystem, |
| 517 | error.ProcessFdQuotaExceeded, |
| 518 | error.SystemFdQuotaExceeded, |
| 519 | error.SymLinkLoop, |
| 520 | => |e| break :blk e, |
| 521 | else => break :blk error.ApiLevelQueryFailed, |
| 522 | } |
| 523 | }, |
| 524 | }; |
| 525 | } |
| 526 | |
| 503 | 527 | return result; |
| 504 | 528 | } |
| 505 | 529 | |
| ... | ... | @@ -1046,8 +1070,11 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1046 | 1070 | error.NetworkNotFound, |
| 1047 | 1071 | error.FileTooBig, |
| 1048 | 1072 | error.Unexpected, |
| 1049 | | => return error.UnableToOpenElfFile, |
| 1050 | | |
| 1073 | => |e| if (e == error.FileNotFound and os.tag == .linux and mem.eql(u8, file_name, "/usr/bin/env")) { |
| 1074 | // Android does not have a /usr directory, so try again |
| 1075 | file_name = "/system/bin/env"; |
| 1076 | continue; |
| 1077 | } else return error.UnableToOpenElfFile, |
| 1051 | 1078 | else => |e| return e, |
| 1052 | 1079 | }; |
| 1053 | 1080 | var is_elf_file = false; |
| ... | ... | @@ -1131,6 +1158,41 @@ const LdInfo = struct { |
| 1131 | 1158 | abi: Target.Abi, |
| 1132 | 1159 | }; |
| 1133 | 1160 | |
| 1161 | fn detectAndroidApiLevel(io: Io) !u32 { |
| 1162 | comptime if (builtin.os.tag != .linux) unreachable; |
| 1163 | |
| 1164 | var child = try std.process.spawn(io, .{ |
| 1165 | .argv = &.{ |
| 1166 | "/system/bin/getprop", |
| 1167 | "ro.build.version.sdk", |
| 1168 | }, |
| 1169 | .stdin = .ignore, |
| 1170 | .stdout = .pipe, |
| 1171 | .stderr = .ignore, |
| 1172 | }); |
| 1173 | errdefer child.kill(io); |
| 1174 | |
| 1175 | // PROP_VALUE_MAX is 92, output is value + newline. |
| 1176 | // Currently API levels are two-digit numbers, but we want to make sure we never read a partial value. |
| 1177 | var stdout_buf: [92 + 1]u8 = undefined; |
| 1178 | var reader = child.stdout.?.readerStreaming(io, &.{}); |
| 1179 | const n = try reader.interface.readSliceShort(&stdout_buf); |
| 1180 | const api_level = std.fmt.parseInt(u32, stdout_buf[0 .. n - 1], 10) catch |e| { |
| 1181 | std.log.err( |
| 1182 | "Could not parse API level, unexpected getprop output '{s}' ({s})", |
| 1183 | .{ stdout_buf[0 .. n - 1], @errorName(e) }, |
| 1184 | ); |
| 1185 | return error.ApiLevelQueryFailed; |
| 1186 | }; |
| 1187 | |
| 1188 | const term = try child.wait(io); |
| 1189 | if (term != .exited or term.exited != 0) { |
| 1190 | std.log.err("getprop terminated abnormally: {}", .{term}); |
| 1191 | return error.ApiLevelQueryFailed; |
| 1192 | } |
| 1193 | return api_level; |
| 1194 | } |
| 1195 | |
| 1134 | 1196 | test { |
| 1135 | 1197 | _ = NativePaths; |
| 1136 | 1198 | |