mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
android: detect native ABI and API level correctly
ABI detection previously did not take into account the non-standard directory structure of Android. This has been fixed. The API level is detected by running `getprop ro.build.version.sdk`, since we don't want to depend on bionic, and reading system properties ourselves is not trivially possible.
This commit is contained in:
parent
bfe3317059
commit
c2cd8df622
2 changed files with 72 additions and 2 deletions
|
|
@ -2120,6 +2120,10 @@ pub inline fn isMuslLibC(target: *const Target) bool {
|
|||
return target.os.tag == .linux and target.abi.isMusl();
|
||||
}
|
||||
|
||||
pub inline fn isBionicLibC(target: *const Target) bool {
|
||||
return target.os.tag == .linux and target.abi.isAndroid();
|
||||
}
|
||||
|
||||
pub inline fn isDarwinLibC(target: *const Target) bool {
|
||||
return switch (target.abi) {
|
||||
.none, .simulator => target.os.tag.isDarwin(),
|
||||
|
|
|
|||
|
|
@ -210,6 +210,8 @@ pub const DetectError = error{
|
|||
OSVersionDetectionFail,
|
||||
Unexpected,
|
||||
ProcessNotFound,
|
||||
/// Android-only. Querying API level through `getprop` failed.
|
||||
ApiLevelQueryFailed,
|
||||
} || Io.Cancelable;
|
||||
|
||||
/// Given a `Target.Query`, which specifies in detail which parts of the
|
||||
|
|
@ -500,6 +502,29 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
|
|||
}
|
||||
}
|
||||
|
||||
if (builtin.os.tag == .linux and result.isBionicLibC() and query.os_tag == null and query.android_api_level == null) {
|
||||
result.os.version_range.linux.android = detectAndroidApiLevel(io) catch |err| return switch (err) {
|
||||
error.InvalidWtf8,
|
||||
error.CurrentWorkingDirectoryUnlinked,
|
||||
error.InvalidBatchScriptArg,
|
||||
error.InvalidHandle,
|
||||
=> unreachable, // Windows-only
|
||||
error.ApiLevelQueryFailed => |e| e,
|
||||
else => blk: {
|
||||
std.log.err("spawning or reading from getprop failed ({s})", .{@errorName(err)});
|
||||
switch (err) {
|
||||
error.SystemResources,
|
||||
error.FileSystem,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.SymLinkLoop,
|
||||
=> |e| break :blk e,
|
||||
else => break :blk error.ApiLevelQueryFailed,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1044,8 +1069,11 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
|
|||
error.NetworkNotFound,
|
||||
error.FileTooBig,
|
||||
error.Unexpected,
|
||||
=> return error.UnableToOpenElfFile,
|
||||
|
||||
=> |e| if (e == error.FileNotFound and os.tag == .linux and mem.eql(u8, file_name, "/usr/bin/env")) {
|
||||
// Android does not have a /usr directory, so try again
|
||||
file_name = "/system/bin/env";
|
||||
continue;
|
||||
} else return error.UnableToOpenElfFile,
|
||||
else => |e| return e,
|
||||
};
|
||||
var is_elf_file = false;
|
||||
|
|
@ -1130,6 +1158,44 @@ const LdInfo = struct {
|
|||
abi: Target.Abi,
|
||||
};
|
||||
|
||||
fn detectAndroidApiLevel(io: Io) !u32 {
|
||||
comptime if (builtin.os.tag != .linux) unreachable;
|
||||
|
||||
// `child.spawn()` uses an arena and the exact memory requirement isn't easily determined,
|
||||
// so we give it 128 * 3 bytes, which was shown in testing to be enough.
|
||||
var alloc_buf: [128 * 3]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&alloc_buf);
|
||||
var child = std.process.Child.init(&.{ "/system/bin/getprop", "ro.build.version.sdk" }, fba.allocator());
|
||||
// pass empty EnvMap, no allocator and deinit() required
|
||||
child.env_map = &std.process.EnvMap.init(undefined);
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Pipe;
|
||||
child.stderr_behavior = .Ignore;
|
||||
|
||||
try child.spawn();
|
||||
errdefer _ = child.kill() catch {};
|
||||
|
||||
// PROP_VALUE_MAX is 92, output is value + newline.
|
||||
// Currently API levels are two-digit numbers, but we want to make sure we never read a partial value.
|
||||
var stdout_buf: [92 + 1]u8 = undefined;
|
||||
var reader = child.stdout.?.readerStreaming(io, &.{});
|
||||
const n = try reader.interface.readSliceShort(&stdout_buf);
|
||||
const api_level = std.fmt.parseInt(u32, stdout_buf[0 .. n - 1], 10) catch |e| {
|
||||
std.log.err(
|
||||
"Could not parse API level, unexpected getprop output '{s}' ({s})",
|
||||
.{ stdout_buf[0 .. n - 1], @errorName(e) },
|
||||
);
|
||||
return error.ApiLevelQueryFailed;
|
||||
};
|
||||
|
||||
const term = try child.wait();
|
||||
if (term != .Exited or term.Exited != 0) {
|
||||
std.log.err("getprop terminated abnormally: {}", .{term});
|
||||
return error.ApiLevelQueryFailed;
|
||||
}
|
||||
return api_level;
|
||||
}
|
||||
|
||||
test {
|
||||
_ = NativePaths;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue