mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-07 22:34:28 +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();
|
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 {
|
pub inline fn isDarwinLibC(target: *const Target) bool {
|
||||||
return switch (target.abi) {
|
return switch (target.abi) {
|
||||||
.none, .simulator => target.os.tag.isDarwin(),
|
.none, .simulator => target.os.tag.isDarwin(),
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,8 @@ pub const DetectError = error{
|
||||||
OSVersionDetectionFail,
|
OSVersionDetectionFail,
|
||||||
Unexpected,
|
Unexpected,
|
||||||
ProcessNotFound,
|
ProcessNotFound,
|
||||||
|
/// Android-only. Querying API level through `getprop` failed.
|
||||||
|
ApiLevelQueryFailed,
|
||||||
} || Io.Cancelable;
|
} || Io.Cancelable;
|
||||||
|
|
||||||
/// Given a `Target.Query`, which specifies in detail which parts of the
|
/// 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1044,8 +1069,11 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
|
||||||
error.NetworkNotFound,
|
error.NetworkNotFound,
|
||||||
error.FileTooBig,
|
error.FileTooBig,
|
||||||
error.Unexpected,
|
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,
|
else => |e| return e,
|
||||||
};
|
};
|
||||||
var is_elf_file = false;
|
var is_elf_file = false;
|
||||||
|
|
@ -1130,6 +1158,44 @@ const LdInfo = struct {
|
||||||
abi: Target.Abi,
|
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 {
|
test {
|
||||||
_ = NativePaths;
|
_ = NativePaths;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue