| ... | @@ -211,6 +211,8 @@ pub const DetectError = error{ | ... | @@ -211,6 +211,8 @@ pub const DetectError = error{ |
| 211 | DeviceBusy, | 211 | DeviceBusy, |
| 212 | OSVersionDetectionFail, | 212 | OSVersionDetectionFail, |
| 213 | Unexpected, | 213 | Unexpected, |
| | 214 | /// Android-only. Querying API level through `getprop` failed. |
| | 215 | ApiLevelQueryFailed, |
| 214 | } || Io.Cancelable; | 216 | } || Io.Cancelable; |
| 215 | | 217 | |
| 216 | /// Given a `Target.Query`, which specifies in detail which parts of the | 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,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 | return result; | 527 | return result; |
| 504 | } | 528 | } |
| 505 | | 529 | |
| ... | @@ -1046,8 +1070,11 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ | ... | @@ -1046,8 +1070,11 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1046 | error.NetworkNotFound, | 1070 | error.NetworkNotFound, |
| 1047 | error.FileTooBig, | 1071 | error.FileTooBig, |
| 1048 | error.Unexpected, | 1072 | error.Unexpected, |
| 1049 | => return error.UnableToOpenElfFile, | 1073 | => |e| if (e == error.FileNotFound and os.tag == .linux and mem.eql(u8, file_name, "/usr/bin/env")) { |
| 1050 | | 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 | else => |e| return e, | 1078 | else => |e| return e, |
| 1052 | }; | 1079 | }; |
| 1053 | var is_elf_file = false; | 1080 | var is_elf_file = false; |
| ... | @@ -1131,6 +1158,41 @@ const LdInfo = struct { | ... | @@ -1131,6 +1158,41 @@ const LdInfo = struct { |
| 1131 | abi: Target.Abi, | 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 | test { | 1196 | test { |
| 1135 | _ = NativePaths; | 1197 | _ = NativePaths; |
| 1136 | | 1198 | |