Commit 3c1ccbdf39

Alex Rønne Petersen <alex@alexrp.com>
2024-10-30 21:57:44
std.Target: Add support for specifying Android API level.
1 parent c5395f7
Changed files (7)
lib/std/Build/Cache.zig
@@ -221,6 +221,7 @@ pub const HashHelper = struct {
                         hh.add(linux.range.min);
                         hh.add(linux.range.max);
                         hh.add(linux.glibc);
+                        hh.add(linux.android);
                     },
                     .windows => |windows| {
                         hh.add(windows.min);
lib/std/Target/Query.zig
@@ -25,10 +25,14 @@ os_version_min: ?OsVersion = null,
 /// When `os_tag` is native, `null` means equal to the native OS version.
 os_version_max: ?OsVersion = null,
 
-/// `null` means default when cross compiling, or native when os_tag is native.
+/// `null` means default when cross compiling, or native when `os_tag` is native.
 /// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
 glibc_version: ?SemanticVersion = null,
 
+/// `null` means default when cross compiling, or native when `os_tag` is native.
+/// If `isAndroid()` is `false`, this must be `null` and is ignored.
+android_api_level: ?u32 = null,
+
 /// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
 abi: ?Target.Abi = null,
 
@@ -98,10 +102,8 @@ pub fn fromTarget(target: Target) Query {
         .os_version_min = undefined,
         .os_version_max = undefined,
         .abi = target.abi,
-        .glibc_version = if (target.isGnuLibC())
-            target.os.version_range.linux.glibc
-        else
-            null,
+        .glibc_version = if (target.isGnuLibC()) target.os.version_range.linux.glibc else null,
+        .android_api_level = if (target.abi.isAndroid()) target.os.version_range.linux.android else null,
     };
     result.updateOsVersionRange(target.os);
 
@@ -147,7 +149,7 @@ pub const ParseOptions = struct {
     /// The fields are, respectively:
     /// * CPU Architecture
     /// * Operating System (and optional version range)
-    /// * C ABI (optional, with optional glibc version)
+    /// * C ABI (optional, with optional glibc version or Android API level)
     /// The string "native" can be used for CPU architecture as well as Operating System.
     /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
     arch_os_abi: []const u8 = "native",
@@ -234,6 +236,11 @@ pub fn parse(args: ParseOptions) !Query {
                     error.Overflow => return error.InvalidAbiVersion,
                     error.InvalidVersion => return error.InvalidAbiVersion,
                 };
+            } else if (abi.isAndroid()) {
+                result.android_api_level = std.fmt.parseUnsigned(u32, abi_ver_text, 10) catch |err| switch (err) {
+                    error.InvalidCharacter => return error.InvalidVersion,
+                    error.Overflow => return error.Overflow,
+                };
             } else {
                 return error.InvalidAbiVersion;
             }
@@ -355,7 +362,7 @@ pub fn isNativeCpu(self: Query) bool {
 
 pub fn isNativeOs(self: Query) bool {
     return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
-        self.dynamic_linker.get() == null and self.glibc_version == null;
+        self.dynamic_linker.get() == null and self.glibc_version == null and self.android_api_level == null;
 }
 
 pub fn isNativeAbi(self: Query) bool {
@@ -439,6 +446,13 @@ pub fn zigTriple(self: Query, allocator: Allocator) Allocator.Error![]u8 {
         result.appendSliceAssumeCapacity(name);
         result.appendAssumeCapacity('.');
         try formatVersion(v, result.writer());
+    } else if (self.android_api_level) |lvl| {
+        const name = if (self.abi) |abi| @tagName(abi) else "android";
+        try result.ensureUnusedCapacity(name.len + 2);
+        result.appendAssumeCapacity('-');
+        result.appendSliceAssumeCapacity(name);
+        result.appendAssumeCapacity('.');
+        try result.writer().print("{d}", .{lvl});
     } else if (self.abi) |abi| {
         const name = @tagName(abi);
         try result.ensureUnusedCapacity(name.len + 1);
@@ -561,6 +575,7 @@ pub fn eql(a: Query, b: Query) bool {
     if (!OsVersion.eqlOpt(a.os_version_min, b.os_version_min)) return false;
     if (!OsVersion.eqlOpt(a.os_version_max, b.os_version_max)) return false;
     if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false;
+    if (a.android_api_level != b.android_api_level) return false;
     if (a.abi != b.abi) return false;
     if (!a.dynamic_linker.eql(b.dynamic_linker)) return false;
     if (a.ofmt != b.ofmt) return false;
@@ -592,6 +607,15 @@ test parse {
 
         try std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text);
     }
+    if (builtin.target.abi.isAndroid()) {
+        var query = try Query.parse(.{});
+        query.android_api_level = 30;
+
+        const text = try query.zigTriple(std.testing.allocator);
+        defer std.testing.allocator.free(text);
+
+        try std.testing.expectEqualSlices(u8, "native-native-android.30", text);
+    }
     {
         const query = try Query.parse(.{
             .arch_os_abi = "aarch64-linux",
@@ -677,4 +701,25 @@ test parse {
         defer std.testing.allocator.free(text);
         try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text);
     }
+    {
+        const query = try Query.parse(.{
+            .arch_os_abi = "aarch64-linux.3.10...4.4.1-android.30",
+        });
+        const target = try std.zig.system.resolveTargetQuery(query);
+
+        try std.testing.expect(target.cpu.arch == .aarch64);
+        try std.testing.expect(target.os.tag == .linux);
+        try std.testing.expect(target.os.version_range.linux.range.min.major == 3);
+        try std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
+        try std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
+        try std.testing.expect(target.os.version_range.linux.range.max.major == 4);
+        try std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
+        try std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
+        try std.testing.expect(target.os.version_range.linux.android == 30);
+        try std.testing.expect(target.abi == .android);
+
+        const text = try query.zigTriple(std.testing.allocator);
+        defer std.testing.allocator.free(text);
+        try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-android.30", text);
+    }
 }
lib/std/zig/system.zig
@@ -331,6 +331,10 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
         os.version_range.linux.glibc = glibc;
     }
 
+    if (query.android_api_level) |android| {
+        os.version_range.linux.android = android;
+    }
+
     // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the
     // native CPU architecture as being different than the current target), we use this:
     const cpu_arch = query.cpu_arch orelse builtin.cpu.arch;
lib/std/c.zig
@@ -44,22 +44,26 @@ comptime {
     }
 }
 
-/// If not linking libc, returns false.
-/// If linking musl libc, returns true.
-/// If linking gnu libc (glibc), returns true if the target version is greater
-/// than or equal to `glibc_version`.
-/// If linking a libc other than these, returns `false`.
-pub inline fn versionCheck(comptime glibc_version: std.SemanticVersion) bool {
+/// * If not linking libc, returns `false`.
+/// * If linking musl libc, returns `true`.
+/// * If linking GNU libc (glibc), returns `true` if the target version is greater than or equal to
+///   `version`.
+/// * If linking Android libc (bionic), returns `true` if the target API level is greater than or
+///   equal to `version.major`, ignoring other components.
+/// * If linking a libc other than these, returns `false`.
+pub inline fn versionCheck(comptime version: std.SemanticVersion) bool {
     return comptime blk: {
         if (!builtin.link_libc) break :blk false;
         if (native_abi.isMusl()) break :blk true;
         if (builtin.target.isGnuLibC()) {
             const ver = builtin.os.version_range.linux.glibc;
-            const order = ver.order(glibc_version);
+            const order = ver.order(version);
             break :blk switch (order) {
                 .gt, .eq => true,
                 .lt => false,
             };
+        } else if (builtin.abi.isAndroid()) {
+            break :blk builtin.os.version_range.linux.android >= version.major;
         } else {
             break :blk false;
         }
lib/std/Target.zig
@@ -359,6 +359,8 @@ pub const Os = struct {
     pub const LinuxVersionRange = struct {
         range: std.SemanticVersion.Range,
         glibc: std.SemanticVersion,
+        /// Android API level.
+        android: u32 = 14, // This default value is to be deleted after zig1.wasm is updated.
 
         pub inline fn includesVersion(range: LinuxVersionRange, ver: std.SemanticVersion) bool {
             return range.range.includesVersion(ver);
@@ -480,6 +482,7 @@ pub const Os = struct {
 
                             break :blk default_min;
                         },
+                        .android = 14,
                     },
                 },
                 .rtems => .{
src/codegen/llvm.zig
@@ -286,7 +286,6 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![]const u8 {
     };
     try llvm_triple.appendSlice(llvm_abi);
 
-    // This should eventually handle the Android API level too.
     switch (target.os.versionRange()) {
         .none,
         .semver,
@@ -296,7 +295,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![]const u8 {
             ver.glibc.major,
             ver.glibc.minor,
             ver.glibc.patch,
-        }),
+        }) else if (target.abi.isAndroid()) try llvm_triple.writer().print("{d}", .{ver.android}),
     }
 
     return llvm_triple.toOwnedSlice();
src/Builtin.zig
@@ -124,6 +124,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
             \\            .minor = {},
             \\            .patch = {},
             \\        }},
+            \\        .android = {},
             \\    }}}},
             \\
         , .{
@@ -138,6 +139,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
             linux.glibc.major,
             linux.glibc.minor,
             linux.glibc.patch,
+
+            linux.android,
         }),
         .windows => |windows| try buffer.writer().print(
             \\ .windows = .{{