Skip to content

Commit dff7531

Browse files
Decouple Android SDK and APK (#38)
Changes - Decouple Android SDK from APK to allow for future improvements like running `sdkmanager` from the Zig build system - Update examples to support `zig build run -Dandroid` which will run `adb install` + `adb shell start` logic for you Fixes #36
1 parent 1ed3836 commit dff7531

File tree

16 files changed

+604
-419
lines changed

16 files changed

+604
-419
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
strategy:
1818
matrix:
1919
include:
20-
- os: "ubuntu-22.04"
20+
- os: "ubuntu-latest"
2121
- os: "windows-latest"
2222
- os: "macos-14" # arm64 as per table: https://github.com/actions/runner-images/blob/8a1eeaf6ac70c66f675a04078d1a7222edd42008/README.md#available-images
2323

@@ -45,7 +45,7 @@ jobs:
4545
# note(jae): 2024-09-15
4646
# Uses download mirror first as preferred by Zig Foundation
4747
# see: https://ziglang.org/news/migrate-to-self-hosting/
48-
uses: mlugg/setup-zig@v1
48+
uses: mlugg/setup-zig@v2
4949
with:
5050
version: "0.14.0"
5151

@@ -99,7 +99,7 @@ jobs:
9999
#
100100

101101
- name: Setup Zig Nightly
102-
uses: mlugg/setup-zig@v1
102+
uses: mlugg/setup-zig@v2
103103
with:
104104
version: "master"
105105

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ zig build -Dandroid=true
1818
const android = @import("android");
1919
2020
pub fn build(b: *std.Build) !void {
21-
const android_tools = android.Tools.create(b, ...);
22-
const apk = android.APK.create(b, android_tools);
21+
const android_sdk = android.Sdk.create(b, .{});
22+
const apk = android_sdk.createApk(.{
23+
.api_level = .android15,
24+
.build_tools_version = "35.0.1",
25+
.ndk_version = "29.0.13113456",
26+
});
2327
apk.setAndroidManifest(b.path("android/AndroidManifest.xml"));
2428
apk.addResourceDirectory(b.path("android/res"));
2529
apk.addJavaSourceFile(.{ .file = b.path("android/src/NativeInvocationHandler.java") });

build.zig

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
const std = @import("std");
22
const androidbuild = @import("src/androidbuild/androidbuild.zig");
3-
const Apk = @import("src/androidbuild/apk.zig");
43

54
// Expose Android build functionality for use in your build.zig
65

7-
pub const Tools = @import("src/androidbuild/tools.zig");
8-
pub const APK = Apk; // TODO(jae): 2025-03-13: Consider deprecating and using 'Apk' to be conventional to Zig
9-
pub const APILevel = androidbuild.APILevel; // TODO(jae): 2025-03-13: Consider deprecating and using 'ApiLevel' to be conventional to Zig
6+
// TODO: rename tools.zig to Sdk.zig
7+
pub const Sdk = @import("src/androidbuild/tools.zig");
8+
pub const Apk = @import("src/androidbuild/apk.zig");
9+
pub const ApiLevel = androidbuild.ApiLevel;
1010
pub const standardTargets = androidbuild.standardTargets;
1111

12-
// Deprecated exposes fields
13-
14-
/// Deprecated: Use Tools.Options instead.
15-
pub const ToolsOptions = Tools.Options;
16-
/// Deprecated: Use Tools.CreateKey instead.
17-
pub const CreateKey = Tools.CreateKey;
12+
// Deprecated exposed fields
13+
14+
/// Deprecated: Use ApiLevel
15+
pub const APILevel = @compileError("use android.ApiLevel instead of android.APILevel");
16+
/// Deprecated: Use Sdk instead
17+
pub const Tools = @compileError("Use android.Sdk instead of android.Tools");
18+
/// Deprecated: Use Apk.Options instead.
19+
pub const ToolsOptions = @compileError("Use android.Sdk.Options instead of android.Apk.Options with the Sdk.createApk method");
20+
/// Deprecated: Use Sdk.CreateKey instead.
21+
pub const CreateKey = @compileError("Use android.Sdk.CreateKey instead of android.CreateKey. Change 'android_tools.createKeyStore(android.CreateKey.example())' to 'android_sdk.createKeyStore(.example)'");
22+
/// Deprecated: Use Apk not APK
23+
pub const APK = @compileError("Use android.Apk instead of android.APK");
1824

1925
/// NOTE: As well as providing the "android" module this declaration is required so this can be imported by other build.zig files
2026
pub fn build(b: *std.Build) void {

examples/minimal/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
As of 2024-09-19, this is a thrown together, very quick copy-paste of the minimal example from the original [ZigAndroidTemplate](https://github.com/ikskuh/ZigAndroidTemplate/blob/master/examples/minimal/main.zig) repository.
44

5+
### Build and run natively on your operating system or install/run on Android device
6+
7+
```sh
8+
zig build run # Native
9+
zig build run -Dandroid # Android
10+
```
11+
512
### Build, install to test one target against a local emulator and run
613

714
```sh
@@ -13,7 +20,7 @@ adb shell am start -S -W -n com.zig.minimal/android.app.NativeActivity
1320
### Build and install for all supported Android targets
1421

1522
```sh
16-
zig build -Dandroid=true
23+
zig build -Dandroid
1724
adb install ./zig-out/bin/minimal.apk
1825
```
1926

examples/minimal/build.zig

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,16 @@ pub fn build(b: *std.Build) void {
1414
else
1515
android_targets;
1616

17-
// If building with Android, initialize the tools / build
18-
const android_apk: ?*android.APK = blk: {
19-
if (android_targets.len == 0) {
20-
break :blk null;
21-
}
22-
const android_tools = android.Tools.create(b, .{
17+
const android_apk: ?*android.Apk = blk: {
18+
if (android_targets.len == 0) break :blk null;
19+
20+
const android_sdk = android.Sdk.create(b, .{});
21+
const apk = android_sdk.createApk(.{
2322
.api_level = .android15,
2423
.build_tools_version = "35.0.1",
2524
.ndk_version = "29.0.13113456",
2625
});
27-
const apk = android.APK.create(b, android_tools);
28-
29-
const key_store_file = android_tools.createKeyStore(android.CreateKey.example());
26+
const key_store_file = android_sdk.createKeyStore(.example);
3027
apk.setKeyStore(key_store_file);
3128
apk.setAndroidManifest(b.path("android/AndroidManifest.xml"));
3229
apk.addResourceDirectory(b.path("android/res"));
@@ -62,7 +59,7 @@ pub fn build(b: *std.Build) void {
6259
// NOTE: Android has different CPU targets so you need to build a version of your
6360
// code for x86, x86_64, arm, arm64 and more
6461
if (target.result.abi.isAndroid()) {
65-
const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized");
62+
const apk: *android.Apk = android_apk orelse @panic("Android APK should be initialized");
6663
const android_dep = b.dependency("android", .{
6764
.optimize = optimize,
6865
.target = target,
@@ -82,6 +79,14 @@ pub fn build(b: *std.Build) void {
8279
}
8380
}
8481
if (android_apk) |apk| {
85-
apk.installApk();
82+
const installed_apk = apk.addInstallApk();
83+
b.getInstallStep().dependOn(&installed_apk.step);
84+
85+
const android_sdk = apk.sdk;
86+
const run_step = b.step("run", "Install and run the application on an Android device");
87+
const adb_install = android_sdk.addAdbInstall(installed_apk.source);
88+
const adb_start = android_sdk.addAdbStart("com.zig.minimal/android.app.NativeActivity");
89+
adb_start.step.dependOn(&adb_install.step);
90+
run_step.dependOn(&adb_start.step);
8691
}
8792
}

examples/raylib/README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ ld.lld: warning: <path-to-project>/.zig-cache/o/4227869d730f094811a7cdaaab535797
77
```
88
You can ignore this error for now.
99

10+
### Build and run natively on your operating system or install/run on Android device
11+
12+
```sh
13+
zig build run # Native
14+
zig build run -Dandroid # Android
15+
```
16+
1017
### Build, install to test one target against a local emulator and run
1118

1219
```sh
@@ -18,16 +25,10 @@ adb shell am start -S -W -n com.zig.raylib/android.app.NativeActivity
1825
### Build and install for all supported Android targets
1926

2027
```sh
21-
zig build -Dandroid=true
28+
zig build -Dandroid
2229
adb install ./zig-out/bin/raylib.apk
2330
```
2431

25-
### Build and run natively on your operating system
26-
27-
```sh
28-
zig build run
29-
```
30-
3132
### Uninstall your application
3233

3334
If installing your application fails with something like:

examples/raylib/build.zig

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
const android = @import("android");
22
const std = @import("std");
33

4-
//This is targeting android version 10 / API level 29.
5-
//Change the value here and in android/AndroidManifest.xml to target your desired API level
6-
const android_version: android.APILevel = .android10;
7-
const android_api = std.fmt.comptimePrint("{}", .{@intFromEnum(android_version)});
84
const exe_name = "raylib";
95

106
pub fn build(b: *std.Build) void {
@@ -18,18 +14,17 @@ pub fn build(b: *std.Build) void {
1814
else
1915
android_targets;
2016

21-
const android_apk: ?*android.APK = blk: {
22-
if (android_targets.len == 0) {
23-
break :blk null;
24-
}
25-
const android_tools = android.Tools.create(b, .{
26-
.api_level = android_version,
17+
const android_apk: ?*android.Apk = blk: {
18+
if (android_targets.len == 0) break :blk null;
19+
20+
const android_sdk = android.Sdk.create(b, .{});
21+
const apk = android_sdk.createApk(.{
22+
.api_level = .android10,
2723
.build_tools_version = "35.0.1",
2824
.ndk_version = "29.0.13113456",
2925
});
30-
const apk = android.APK.create(b, android_tools);
3126

32-
const key_store_file = android_tools.createKeyStore(android.CreateKey.example());
27+
const key_store_file = android_sdk.createKeyStore(.example);
3328
apk.setKeyStore(key_store_file);
3429
apk.setAndroidManifest(b.path("android/AndroidManifest.xml"));
3530
apk.addResourceDirectory(b.path("android/res"));
@@ -51,38 +46,36 @@ pub fn build(b: *std.Build) void {
5146
lib.linkLibC();
5247
b.installArtifact(lib);
5348

54-
const android_ndk_path = if(android_apk) |apk| (b.fmt("{s}/ndk/{s}", .{ apk.tools.android_sdk_path, apk.tools.ndk_version })) else "";
55-
const raylib_dep = if (target.result.abi.isAndroid()) (
56-
b.dependency("raylib_zig", .{
57-
.target = target,
58-
.optimize = optimize,
59-
.android_api_version = @as([]const u8, android_api),
60-
.android_ndk = @as([]const u8, android_ndk_path),
61-
})) else (
49+
const raylib_dep = if (android_apk) |apk|
50+
b.dependency("raylib_zig", .{
51+
.target = target,
52+
.optimize = optimize,
53+
.android_api_version = @as([]const u8, b.fmt("{}", .{@intFromEnum(apk.api_level)})),
54+
.android_ndk = @as([]const u8, apk.ndk.path),
55+
})
56+
else
6257
b.dependency("raylib_zig", .{
6358
.target = target,
6459
.optimize = optimize,
65-
.shared = true
66-
}));
60+
.shared = true,
61+
});
62+
6763
const raylib_artifact = raylib_dep.artifact("raylib");
6864
lib.linkLibrary(raylib_artifact);
6965
const raylib_mod = raylib_dep.module("raylib");
7066
lib.root_module.addImport("raylib", raylib_mod);
7167

72-
if (target.result.abi.isAndroid()) {
73-
const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized");
68+
if (android_apk) |apk| {
7469
const android_dep = b.dependency("android", .{
7570
.optimize = optimize,
7671
.target = target,
7772
});
78-
lib.root_module.linkSystemLibrary("android", .{ .preferred_link_mode = .dynamic });
7973
lib.root_module.addImport("android", android_dep.module("android"));
74+
lib.root_module.linkSystemLibrary("android", .{});
8075

81-
const native_app_glue_dir: std.Build.LazyPath = .{ .cwd_relative = b.fmt("{s}/sources/android/native_app_glue", .{android_ndk_path}) };
76+
const native_app_glue_dir: std.Build.LazyPath = .{ .cwd_relative = b.fmt("{s}/sources/android/native_app_glue", .{apk.ndk.path}) };
8277
lib.root_module.addCSourceFile(.{ .file = native_app_glue_dir.path(b, "android_native_app_glue.c") });
8378
lib.root_module.addIncludePath(native_app_glue_dir);
84-
85-
lib.root_module.linkSystemLibrary("log", .{ .preferred_link_mode = .dynamic });
8679
apk.addArtifact(lib);
8780
} else {
8881
const exe = b.addExecutable(.{ .name = exe_name, .optimize = optimize, .root_module = lib_mod });
@@ -94,6 +87,14 @@ pub fn build(b: *std.Build) void {
9487
}
9588
}
9689
if (android_apk) |apk| {
97-
apk.installApk();
90+
const installed_apk = apk.addInstallApk();
91+
b.getInstallStep().dependOn(&installed_apk.step);
92+
93+
const android_sdk = apk.sdk;
94+
const run_step = b.step("run", "Install and run the application on an Android device");
95+
const adb_install = android_sdk.addAdbInstall(installed_apk.source);
96+
const adb_start = android_sdk.addAdbStart("com.zig.raylib/android.app.NativeActivity");
97+
adb_start.step.dependOn(&adb_install.step);
98+
run_step.dependOn(&adb_start.step);
9899
}
99100
}

examples/sdl2/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
This is a copy-paste of [Andrew Kelly's SDL Zig Demo](https://github.com/andrewrk/sdl-zig-demo) but running on Android. The build is setup so you can also target your native operating system as well.
44

5+
### Build and run natively on your operating system or install/run on Android device
6+
7+
```sh
8+
zig build run # Native
9+
zig build run -Dandroid # Android
10+
```
11+
512
### Build, install to test one target against a local emulator and run
613

714
```sh
@@ -17,12 +24,6 @@ zig build -Dandroid=true
1724
adb install ./zig-out/bin/sdl-zig-demo.apk
1825
```
1926

20-
### Build and run natively on your operating system
21-
22-
```sh
23-
zig build run
24-
```
25-
2627
### Uninstall your application
2728

2829
If installing your application fails with something like:

examples/sdl2/build.zig

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@ pub fn build(b: *std.Build) void {
1515
else
1616
android_targets;
1717

18-
// If building with Android, initialize the tools / build
19-
const android_apk: ?*android.APK = blk: {
20-
if (android_targets.len == 0) {
21-
break :blk null;
22-
}
23-
const android_tools = android.Tools.create(b, .{
18+
const android_apk: ?*android.Apk = blk: {
19+
if (android_targets.len == 0) break :blk null;
20+
21+
const android_sdk = android.Sdk.create(b, .{});
22+
const apk = android_sdk.createApk(.{
2423
.api_level = .android15,
2524
.build_tools_version = "35.0.1",
2625
.ndk_version = "29.0.13113456",
@@ -34,9 +33,8 @@ pub fn build(b: *std.Build) void {
3433
// - ndk/27.0.12077973/toolchains/llvm/prebuilt/{OS}-x86_64/sysroot/usr/include/android/hardware_buffer.h:322:42:
3534
// - error: expression is not an integral constant expression
3635
});
37-
const apk = android.APK.create(b, android_tools);
3836

39-
const key_store_file = android_tools.createKeyStore(android.CreateKey.example());
37+
const key_store_file = android_sdk.createKeyStore(.example);
4038
apk.setKeyStore(key_store_file);
4139
apk.setAndroidManifest(b.path("android/AndroidManifest.xml"));
4240
apk.addResourceDirectory(b.path("android/res"));
@@ -113,7 +111,7 @@ pub fn build(b: *std.Build) void {
113111
// NOTE: Android has different CPU targets so you need to build a version of your
114112
// code for x86, x86_64, arm, arm64 and more
115113
if (target.result.abi.isAndroid()) {
116-
const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized");
114+
const apk: *android.Apk = android_apk orelse @panic("Android APK should be initialized");
117115
const android_dep = b.dependency("android", .{
118116
.optimize = optimize,
119117
.target = target,
@@ -133,6 +131,14 @@ pub fn build(b: *std.Build) void {
133131
}
134132
}
135133
if (android_apk) |apk| {
136-
apk.installApk();
134+
const installed_apk = apk.addInstallApk();
135+
b.getInstallStep().dependOn(&installed_apk.step);
136+
137+
const android_sdk = apk.sdk;
138+
const run_step = b.step("run", "Install and run the application on an Android device");
139+
const adb_install = android_sdk.addAdbInstall(installed_apk.source);
140+
const adb_start = android_sdk.addAdbStart("com.zig.sdl2/com.zig.sdl2.ZigSDLActivity");
141+
adb_start.step.dependOn(&adb_install.step);
142+
run_step.dependOn(&adb_start.step);
137143
}
138144
}

0 commit comments

Comments
 (0)