Skip to content

Instantly share code, notes, and snippets.

@planetis-m
Created March 1, 2023 17:53
Show Gist options
  • Save planetis-m/a437db724185215910a59c3b31d1cdf4 to your computer and use it in GitHub Desktop.
Save planetis-m/a437db724185215910a59c3b31d1cdf4 to your computer and use it in GitHub Desktop.
import os, enum, shutil, subprocess
class CpuPlatform(enum.Enum):
arm = 1
arm64 = 2
i386 = 3
amd64 = 4
class DeviceOrientation(enum.Enum):
portrait = 1
landscape = 2
sensor = 3
def toArchName(x):
match x:
case CpuPlatform.arm:
return "armeabi-v7a"
case CpuPlatform.arm64:
return "arm64-v8a"
case CpuPlatform.i386:
return "x86"
case CpuPlatform.amd64:
return "x86_64"
# Define Android architecture (armeabi-v7a, arm64-v8a, x86, x86-64) and API version
AndroidApiVersion = 29
AndroidCPUs = [CpuPlatform.arm64]
# Required path variables
JavaHome = "/usr/lib/jvm/java-19-openjdk"
AndroidNdk = "/opt/android-ndk"
AndroidHome = "/opt/android-sdk"
AndroidBuildTools = os.path.join(AndroidHome, "build-tools/33.0.1")
AndroidPlatformTools = os.path.join(AndroidHome, "platform-tools")
# Android project configuration variables
ProjectName = "raylib_game"
ProjectLibraryName = "main"
ProjectBuildId = "android"
ProjectBuildPath = ProjectBuildId + "." + ProjectName
ProjectResourcesPath = "src/resources"
ProjectSourceFile = "src/raylib_game.nim"
# Android app configuration variables
AppLabelName = "rGame"
AppCompanyName = "raylib"
AppProductName = "rgame"
AppVersionCode = 1
AppVersionName = "1.0"
AppIconLdpi = "logo/raylib_36x36.png"
AppIconMdpi = "logo/raylib_48x48.png"
AppIconHdpi = "logo/raylib_72x72.png"
AppScreenOrientation = DeviceOrientation.landscape
AppKeystorePass = "raylib"
def setupAndroid():
# Create required temp directories for APK building
os.makedirs(os.path.join(ProjectBuildPath, "src", "com", AppCompanyName, AppProductName), exist_ok=True)
for cpu in AndroidCPUs:
os.makedirs(os.path.join(ProjectBuildPath, "lib", toArchName(cpu)), exist_ok=True)
os.makedirs(os.path.join(ProjectBuildPath, "bin"), exist_ok=True)
os.makedirs(os.path.join(ProjectBuildPath, "res", "drawable-ldpi"), exist_ok=True)
os.makedirs(os.path.join(ProjectBuildPath, "res", "drawable-mdpi"), exist_ok=True)
os.makedirs(os.path.join(ProjectBuildPath, "res", "drawable-hdpi"), exist_ok=True)
os.makedirs(os.path.join(ProjectBuildPath, "res", "values"), exist_ok=True)
# os.makedirs(os.path.join(ProjectBuildPath, "assets", "resources"), exist_ok=True)
os.makedirs(os.path.join(ProjectBuildPath, "obj", "screens"), exist_ok=True)
# Copy project required resources: strings.xml, icon.png, assets
with open(os.path.join(ProjectBuildPath, "res", "values", "strings.xml"), "w") as f:
f.write("""<?xml version='1.0' encoding='utf-8'?>\n<resources><string name='app_name'>{}</string></resources>\n""".format(AppLabelName))
shutil.copy2(AppIconLdpi, os.path.join(ProjectBuildPath, "res", "drawable-ldpi", "icon.png"))
shutil.copy2(AppIconMdpi, os.path.join(ProjectBuildPath, "res", "drawable-mdpi", "icon.png"))
shutil.copy2(AppIconHdpi, os.path.join(ProjectBuildPath, "res", "drawable-hdpi", "icon.png"))
shutil.copytree(ProjectResourcesPath, os.path.join(ProjectBuildPath, "assets", "resources"))
# Generate NativeLoader.java to load required shared libraries
with open(os.path.join(ProjectBuildPath, "src", "com", AppCompanyName, AppProductName, "NativeLoader.java"), "w") as f:
f.write("""package com.{}.{};
public class NativeLoader extends android.app.NativeActivity {{
static {{
System.loadLibrary("{}");
}}
}}
""".format(AppCompanyName, AppProductName, ProjectLibraryName))
# Generate AndroidManifest.xml with all the required options
with open(os.path.join(ProjectBuildPath, "AndroidManifest.xml"), "w") as f:
f.write("""<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.{}.{}"
android:versionCode="{}" android:versionName="{}" >
<uses-sdk android:minSdkVersion="{}" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<application android:allowBackup="false" android:label="@string/app_name" android:icon="@drawable/icon" >
<activity android:name="com.{}.{}.NativeLoader"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="{}" android:launchMode="singleTask"
android:clearTaskOnLaunch="true">
<meta-data android:name="android.app.lib_name" android:value="{}" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
""".format(AppCompanyName, AppProductName, AppVersionCode, AppVersionName, AndroidApiVersion, AppCompanyName, AppProductName, AppScreenOrientation, ProjectLibraryName))
def buildAndroid():
androidResourcePath = os.path.join(AndroidHome, "platforms/android-{}/android.jar".format(AndroidApiVersion))
subprocess.run([os.path.join(AndroidBuildTools, "aapt"), "package", "-f", "-m", "-S", os.path.join(ProjectBuildPath, "res"),
"-J", os.path.join(ProjectBuildPath, "src"), "-M", os.path.join(ProjectBuildPath, "AndroidManifest.xml"),
"-I", androidResourcePath])
for cpu in AndroidCPUs:
subprocess.run(["nim", "c", "-d:release", "--os:android", "--cpu:{}".format(cpu), "-d:AndroidApiVersion={}".format(AndroidApiVersion),
"-d:AndroidNdk={}".format(AndroidNdk), "-o:{}".format(os.path.join(ProjectBuildPath, "lib", toArchName(cpu), "lib{}.so".format(ProjectLibraryName))),
"--nimcache:{}".format(os.path.join("~/.cache/nim", "{}_{}".format(ProjectName, cpu))),
ProjectSourceFile])
subprocess.run([os.path.join(JavaHome, "bin/javac"), "-verbose", "-source", "1.8", "-target", "1.8", "-d", os.path.join(ProjectBuildPath, "obj"),
"-bootclasspath", os.path.join(JavaHome, "jre/lib/rt.jar"), "-classpath", "{}:{}".format(androidResourcePath, os.path.join(ProjectBuildPath, "obj")),
"-sourcepath", os.path.join(ProjectBuildPath, "src"), os.path.join(ProjectBuildPath, "src/com", AppCompanyName, AppProductName, "R.java"),
os.path.join(ProjectBuildPath, "src/com", AppCompanyName, AppProductName, "NativeLoader.java")])
classes = []
for f in os.listdir(os.path.join(ProjectBuildPath, "obj/com", AppCompanyName, AppProductName)):
if f.endswith(".class"):
classes.append(f)
subprocess.run([os.path.join(AndroidBuildTools, "d8"), "--release", "--output", os.path.join(ProjectBuildPath, "bin"),
" ".join(classes), "--lib", androidResourcePath])
unsignedApkPath = os.path.join(ProjectBuildPath, "bin", "{}.unsigned.apk".format(ProjectName))
signed_apk_path = os.path.join(ProjectBuildPath, "bin", "{}.signed.apk".format(ProjectName))
os.remove(unsignedApkPath) # fixes rebuilding
os.remove(signedApkPath)
os.system(f'{AndroidBuildTools}/aapt package -f -M {ProjectBuildPath}/AndroidManifest.xml -S {ProjectBuildPath}/res -A {ProjectBuildPath}/assets -I {androidResourcePath} -F {unsignedApkPath} {ProjectBuildPath}/bin')
setupAndroid()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment