Skip to content

Decouple Android SDK and APK #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ zig build -Dandroid=true
const android = @import("android");

pub fn build(b: *std.Build) !void {
const android_tools = android.Tools.create(b, ...);
const apk = android.APK.create(b, android_tools);
const android_sdk = android.Sdk.create(b, .{});
const apk = android_sdk.createApk(.{
.api_level = .android15,
.build_tools_version = "35.0.1",
.ndk_version = "29.0.13113456",
});
apk.setAndroidManifest(b.path("android/AndroidManifest.xml"));
apk.addResourceDirectory(b.path("android/res"));
apk.addJavaSourceFile(.{ .file = b.path("android/src/NativeInvocationHandler.java") });
Expand Down
26 changes: 16 additions & 10 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
const std = @import("std");
const androidbuild = @import("src/androidbuild/androidbuild.zig");
const Apk = @import("src/androidbuild/apk.zig");

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

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

// Deprecated exposes fields

/// Deprecated: Use Tools.Options instead.
pub const ToolsOptions = Tools.Options;
/// Deprecated: Use Tools.CreateKey instead.
pub const CreateKey = Tools.CreateKey;
// Deprecated exposed fields

/// Deprecated: Use ApiLevel
pub const APILevel = @compileError("use android.ApiLevel instead of android.APILevel");
/// Deprecated: Use Sdk instead
pub const Tools = @compileError("Use android.Sdk instead of android.Tools");
/// Deprecated: Use Apk.Options instead.
pub const ToolsOptions = @compileError("Use android.Sdk.Options instead of android.Apk.Options with the Sdk.createApk method");
/// Deprecated: Use Sdk.CreateKey instead.
pub const CreateKey = @compileError("Use android.Sdk.CreateKey instead of android.CreateKey. Change 'android_tools.createKeyStore(android.CreateKey.example())' to 'android_sdk.createKeyStore(.example)'");
/// Deprecated: Use Apk not APK
pub const APK = @compileError("Use android.Apk instead of android.APK");

/// NOTE: As well as providing the "android" module this declaration is required so this can be imported by other build.zig files
pub fn build(b: *std.Build) void {
Expand Down
9 changes: 8 additions & 1 deletion examples/minimal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

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.

### Build and run natively on your operating system or install/run on Android device

```sh
zig build run # Native
zig build run -Dandroid # Android
```

### Build, install to test one target against a local emulator and run

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

```sh
zig build -Dandroid=true
zig build -Dandroid
adb install ./zig-out/bin/minimal.apk
```

Expand Down
27 changes: 16 additions & 11 deletions examples/minimal/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,16 @@ pub fn build(b: *std.Build) void {
else
android_targets;

// If building with Android, initialize the tools / build
const android_apk: ?*android.APK = blk: {
if (android_targets.len == 0) {
break :blk null;
}
const android_tools = android.Tools.create(b, .{
const android_apk: ?*android.Apk = blk: {
if (android_targets.len == 0) break :blk null;

const android_sdk = android.Sdk.create(b, .{});
const apk = android_sdk.createApk(.{
.api_level = .android15,
.build_tools_version = "35.0.1",
.ndk_version = "29.0.13113456",
});
const apk = android.APK.create(b, android_tools);

const key_store_file = android_tools.createKeyStore(android.CreateKey.example());
const key_store_file = android_sdk.createKeyStore(.example);
apk.setKeyStore(key_store_file);
apk.setAndroidManifest(b.path("android/AndroidManifest.xml"));
apk.addResourceDirectory(b.path("android/res"));
Expand Down Expand Up @@ -62,7 +59,7 @@ pub fn build(b: *std.Build) void {
// NOTE: Android has different CPU targets so you need to build a version of your
// code for x86, x86_64, arm, arm64 and more
if (target.result.abi.isAndroid()) {
const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized");
const apk: *android.Apk = android_apk orelse @panic("Android APK should be initialized");
const android_dep = b.dependency("android", .{
.optimize = optimize,
.target = target,
Expand All @@ -82,6 +79,14 @@ pub fn build(b: *std.Build) void {
}
}
if (android_apk) |apk| {
apk.installApk();
const installed_apk = apk.addInstallApk();
b.getInstallStep().dependOn(&installed_apk.step);

const android_sdk = apk.sdk;
const run_step = b.step("run", "Install and run the application on an Android device");
const adb_install = android_sdk.addAdbInstall(installed_apk.source);
const adb_start = android_sdk.addAdbStart("com.zig.minimal/android.app.NativeActivity");
adb_start.step.dependOn(&adb_install.step);
run_step.dependOn(&adb_start.step);
}
}
15 changes: 8 additions & 7 deletions examples/raylib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ ld.lld: warning: <path-to-project>/.zig-cache/o/4227869d730f094811a7cdaaab535797
```
You can ignore this error for now.

### Build and run natively on your operating system or install/run on Android device

```sh
zig build run # Native
zig build run -Dandroid # Android
```

### Build, install to test one target against a local emulator and run

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

```sh
zig build -Dandroid=true
zig build -Dandroid
adb install ./zig-out/bin/raylib.apk
```

### Build and run natively on your operating system

```sh
zig build run
```

### Uninstall your application

If installing your application fails with something like:
Expand Down
59 changes: 30 additions & 29 deletions examples/raylib/build.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
const android = @import("android");
const std = @import("std");

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

pub fn build(b: *std.Build) void {
Expand All @@ -18,18 +14,17 @@ pub fn build(b: *std.Build) void {
else
android_targets;

const android_apk: ?*android.APK = blk: {
if (android_targets.len == 0) {
break :blk null;
}
const android_tools = android.Tools.create(b, .{
.api_level = android_version,
const android_apk: ?*android.Apk = blk: {
if (android_targets.len == 0) break :blk null;

const android_sdk = android.Sdk.create(b, .{});
const apk = android_sdk.createApk(.{
.api_level = .android10,
.build_tools_version = "35.0.1",
.ndk_version = "29.0.13113456",
});
const apk = android.APK.create(b, android_tools);

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

const android_ndk_path = if(android_apk) |apk| (b.fmt("{s}/ndk/{s}", .{ apk.tools.android_sdk_path, apk.tools.ndk_version })) else "";
const raylib_dep = if (target.result.abi.isAndroid()) (
b.dependency("raylib_zig", .{
.target = target,
.optimize = optimize,
.android_api_version = @as([]const u8, android_api),
.android_ndk = @as([]const u8, android_ndk_path),
})) else (
const raylib_dep = if (android_apk) |apk|
b.dependency("raylib_zig", .{
.target = target,
.optimize = optimize,
.android_api_version = @as([]const u8, b.fmt("{}", .{@intFromEnum(apk.api_level)})),
.android_ndk = @as([]const u8, apk.ndk.path),
})
else
b.dependency("raylib_zig", .{
.target = target,
.optimize = optimize,
.shared = true
}));
.shared = true,
});

const raylib_artifact = raylib_dep.artifact("raylib");
lib.linkLibrary(raylib_artifact);
const raylib_mod = raylib_dep.module("raylib");
lib.root_module.addImport("raylib", raylib_mod);

if (target.result.abi.isAndroid()) {
const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized");
if (android_apk) |apk| {
const android_dep = b.dependency("android", .{
.optimize = optimize,
.target = target,
});
lib.root_module.linkSystemLibrary("android", .{ .preferred_link_mode = .dynamic });
lib.root_module.addImport("android", android_dep.module("android"));
lib.root_module.linkSystemLibrary("android", .{});

const native_app_glue_dir: std.Build.LazyPath = .{ .cwd_relative = b.fmt("{s}/sources/android/native_app_glue", .{android_ndk_path}) };
const native_app_glue_dir: std.Build.LazyPath = .{ .cwd_relative = b.fmt("{s}/sources/android/native_app_glue", .{apk.ndk.path}) };
lib.root_module.addCSourceFile(.{ .file = native_app_glue_dir.path(b, "android_native_app_glue.c") });
lib.root_module.addIncludePath(native_app_glue_dir);

lib.root_module.linkSystemLibrary("log", .{ .preferred_link_mode = .dynamic });
apk.addArtifact(lib);
} else {
const exe = b.addExecutable(.{ .name = exe_name, .optimize = optimize, .root_module = lib_mod });
Expand All @@ -94,6 +87,14 @@ pub fn build(b: *std.Build) void {
}
}
if (android_apk) |apk| {
apk.installApk();
const installed_apk = apk.addInstallApk();
b.getInstallStep().dependOn(&installed_apk.step);

const android_sdk = apk.sdk;
const run_step = b.step("run", "Install and run the application on an Android device");
const adb_install = android_sdk.addAdbInstall(installed_apk.source);
const adb_start = android_sdk.addAdbStart("com.zig.raylib/android.app.NativeActivity");
adb_start.step.dependOn(&adb_install.step);
run_step.dependOn(&adb_start.step);
}
}
13 changes: 7 additions & 6 deletions examples/sdl2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

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.

### Build and run natively on your operating system or install/run on Android device

```sh
zig build run # Native
zig build run -Dandroid # Android
```

### Build, install to test one target against a local emulator and run

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

### Build and run natively on your operating system

```sh
zig build run
```

### Uninstall your application

If installing your application fails with something like:
Expand Down
26 changes: 16 additions & 10 deletions examples/sdl2/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ pub fn build(b: *std.Build) void {
else
android_targets;

// If building with Android, initialize the tools / build
const android_apk: ?*android.APK = blk: {
if (android_targets.len == 0) {
break :blk null;
}
const android_tools = android.Tools.create(b, .{
const android_apk: ?*android.Apk = blk: {
if (android_targets.len == 0) break :blk null;

const android_sdk = android.Sdk.create(b, .{});
const apk = android_sdk.createApk(.{
.api_level = .android15,
.build_tools_version = "35.0.1",
.ndk_version = "29.0.13113456",
Expand All @@ -34,9 +33,8 @@ pub fn build(b: *std.Build) void {
// - ndk/27.0.12077973/toolchains/llvm/prebuilt/{OS}-x86_64/sysroot/usr/include/android/hardware_buffer.h:322:42:
// - error: expression is not an integral constant expression
});
const apk = android.APK.create(b, android_tools);

const key_store_file = android_tools.createKeyStore(android.CreateKey.example());
const key_store_file = android_sdk.createKeyStore(.example);
apk.setKeyStore(key_store_file);
apk.setAndroidManifest(b.path("android/AndroidManifest.xml"));
apk.addResourceDirectory(b.path("android/res"));
Expand Down Expand Up @@ -113,7 +111,7 @@ pub fn build(b: *std.Build) void {
// NOTE: Android has different CPU targets so you need to build a version of your
// code for x86, x86_64, arm, arm64 and more
if (target.result.abi.isAndroid()) {
const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized");
const apk: *android.Apk = android_apk orelse @panic("Android APK should be initialized");
const android_dep = b.dependency("android", .{
.optimize = optimize,
.target = target,
Expand All @@ -133,6 +131,14 @@ pub fn build(b: *std.Build) void {
}
}
if (android_apk) |apk| {
apk.installApk();
const installed_apk = apk.addInstallApk();
b.getInstallStep().dependOn(&installed_apk.step);

const android_sdk = apk.sdk;
const run_step = b.step("run", "Install and run the application on an Android device");
const adb_install = android_sdk.addAdbInstall(installed_apk.source);
const adb_start = android_sdk.addAdbStart("com.zig.sdl2/com.zig.sdl2.ZigSDLActivity");
adb_start.step.dependOn(&adb_install.step);
run_step.dependOn(&adb_start.step);
}
}
Loading
Loading