Skip to content

added minimal example for raylib #35

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

Merged
merged 7 commits into from
May 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions examples/raylib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Raylib Example
**Note**:
Due to [an upstream bug](https://github.com/ziglang/zig/issues/20476), you will probably receive a warning (or multiple warnings if building for multiple targets) like this:
```
error: warning(link): unexpected LLD stderr:
ld.lld: warning: <path-to-project>/.zig-cache/o/4227869d730f094811a7cdaaab535797/libraylib.a: archive member '<path-to-ndk>/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/35/libGLESv2.so' is neither ET_REL nor LLVM bitcode
```
You can ignore this error for now.

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

```sh
zig build -Dtarget=x86_64-linux-android
adb install ./zig-out/bin/raylib.apk
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
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:
```
adb: failed to install ./zig-out/bin/raylib.apk: Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.zig.raylib signatures do not match newer version; ignoring!]
```

```sh
adb uninstall "com.zig.raylib"
```

### View logs of application

Powershell (app doesn't need to be running)
```sh
adb logcat | Select-String com.zig.raylib:
```

Bash (app doesn't need running to be running)
```sh
adb logcat com.zig.raylib:D *:S
```

Bash (app must be running, logs everything by the process including modules)
```sh
adb logcat --pid=`adb shell pidof -s com.zig.raylib`
```


30 changes: 30 additions & 0 deletions examples/raylib/android/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.zig.raylib">
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
<uses-sdk android:minSdkVersion="9" />

<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:hasCode="false"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
tools:targetApi="29"
android:alwaysRetainTaskState="true"
android:launchMode="singleInstance"
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
>
<activity
android:name="android.app.NativeActivity"
android:exported="true"
android:label="@string/app_name">
<meta-data android:name="android.app.lib_name" android:value="main"/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions examples/raylib/android/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Pretty name of your app -->
<string name="app_name">Zig Raylib</string>
<!--
This is required for the APK name. This identifies your app, Android will associate
your signing key with this identifier and will prevent updates if the key changes.
-->
<string name="package_name">com.zig.raylib</string>
</resources>
99 changes: 99 additions & 0 deletions examples/raylib/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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 {
const root_target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const android_targets = android.standardTargets(b, root_target);

var root_target_single = [_]std.Build.ResolvedTarget{root_target};
const targets: []std.Build.ResolvedTarget = if (android_targets.len == 0)
root_target_single[0..]
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,
.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());
apk.setKeyStore(key_store_file);
apk.setAndroidManifest(b.path("android/AndroidManifest.xml"));
apk.addResourceDirectory(b.path("android/res"));

break :blk apk;
};

for (targets) |target| {
const lib_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const lib = b.addLibrary(.{
.linkage = .dynamic,
.name = exe_name,
.root_module = lib_mod,
});
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 (
b.dependency("raylib_zig", .{
.target = target,
.optimize = optimize,
.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");
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"));

const native_app_glue_dir: std.Build.LazyPath = .{ .cwd_relative = b.fmt("{s}/sources/android/native_app_glue", .{android_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 });
b.installArtifact(exe);

const run_exe = b.addRunArtifact(exe);
const run_step = b.step("run", "Run the application");
run_step.dependOn(&run_exe.step);
}
}
if (android_apk) |apk| {
apk.installApk();
}
}
18 changes: 18 additions & 0 deletions examples/raylib/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.{
.name = .raylib,
.version = "0.0.0",
.minimum_zig_version = "0.14.0",
.fingerprint = 0x13035e5cf42e313f,
.dependencies = .{
.raylib_zig = .{
.url = "git+https://github.com/lumenkeyes/raylib-zig#b00d4c2b973665e3a88c2565b6cd63c56d0173c2",
.hash = "raylib_zig-5.6.0-dev-KE8REPwqBQC35iWa2sFblBUNWkTlEi1gjit9dtyOLu_b"
Comment on lines +8 to +9
Copy link
Owner

@silbinarywolf silbinarywolf May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the plan to do a PR here to upstream your Android build support?
https://github.com/Not-Nik/raylib-zig

It'd be ideal if we point at the main maintainer of the library but we can do that in a future PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only real difference is which version of upstream raylib we're depending on in our zon file. I can definitely submit a PR, but I also figure they'll have to update their dependencies eventually. I can try to get that moving, though, as I agree it would be preferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have submitted the PR, but I haven't heard anything back yet

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to merge as-is if you'd like. If/when your PR is merged, we can upstream it if that works?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

},
.android = .{ .path = "../.." },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}
46 changes: 46 additions & 0 deletions examples/raylib/src/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const std = @import("std");
const builtin = @import("builtin");
const android = @import("android");
const rl = @import("raylib");

pub fn main() void {
const screenWidth = 800;
const screenHeight = 450;
rl.initWindow(screenWidth, screenHeight, "raylib-zig [core] example - basic window");
defer rl.closeWindow();
rl.setTargetFPS(60);
while (!rl.windowShouldClose()) {
rl.beginDrawing();
defer rl.endDrawing();

rl.clearBackground(.white);

rl.drawText("Congrats! You created your first window!", 190, 200, 20, .light_gray);
}
}

/// custom panic handler for Android
pub const panic = if (builtin.abi.isAndroid())
android.panic
else
std.debug.FullPanic(std.debug.defaultPanic);

/// custom standard options for Android
pub const std_options: std.Options = if (builtin.abi.isAndroid())
.{
.logFn = android.logFn,
}
else
.{};

comptime {
if (builtin.abi.isAndroid()) {
// Setup exported C-function as defined in AndroidManifest.xml
// ie. <meta-data android:name="android.app.lib_name" android:value="main"/>
@export(&androidMain, .{ .name = "main" });
}
}

fn androidMain() callconv(.c) c_int {
return std.start.callMain();
}