Skip to content

Commit efc8f76

Browse files
authored
move ndk code to a new library (#6951)
1 parent e0f5561 commit efc8f76

File tree

14 files changed

+145
-49
lines changed

14 files changed

+145
-49
lines changed

examples/llama.android/app/build.gradle.kts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ android {
77
namespace = "com.example.llama"
88
compileSdk = 34
99

10-
ndkVersion = "26.1.10909125"
11-
1210
defaultConfig {
1311
applicationId = "com.example.llama"
1412
minSdk = 33
@@ -20,17 +18,6 @@ android {
2018
vectorDrawables {
2119
useSupportLibrary = true
2220
}
23-
ndk {
24-
// Add NDK properties if wanted, e.g.
25-
// abiFilters += listOf("arm64-v8a")
26-
}
27-
externalNativeBuild {
28-
cmake {
29-
arguments += "-DCMAKE_BUILD_TYPE=Release"
30-
cppFlags += listOf()
31-
arguments += listOf()
32-
}
33-
}
3421
}
3522

3623
buildTypes {
@@ -55,17 +42,6 @@ android {
5542
composeOptions {
5643
kotlinCompilerExtensionVersion = "1.5.1"
5744
}
58-
packaging {
59-
resources {
60-
excludes += "/META-INF/{AL2.0,LGPL2.1}"
61-
}
62-
}
63-
externalNativeBuild {
64-
cmake {
65-
path = file("src/main/cpp/CMakeLists.txt")
66-
version = "3.22.1"
67-
}
68-
}
6945
}
7046

7147
dependencies {
@@ -78,6 +54,7 @@ dependencies {
7854
implementation("androidx.compose.ui:ui-graphics")
7955
implementation("androidx.compose.ui:ui-tooling-preview")
8056
implementation("androidx.compose.material3:material3")
57+
implementation(project(":llama"))
8158
testImplementation("junit:junit:4.13.2")
8259
androidTestImplementation("androidx.test.ext:junit:1.1.5")
8360
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

examples/llama.android/app/src/main/java/com/example/llama/MainViewModel.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.example.llama
22

3+
import android.llama.cpp.LLamaAndroid
34
import android.util.Log
45
import androidx.compose.runtime.getValue
56
import androidx.compose.runtime.mutableStateOf
@@ -9,7 +10,7 @@ import androidx.lifecycle.viewModelScope
910
import kotlinx.coroutines.flow.catch
1011
import kotlinx.coroutines.launch
1112

12-
class MainViewModel(private val llm: Llm = Llm.instance()): ViewModel() {
13+
class MainViewModel(private val llamaAndroid: LLamaAndroid = LLamaAndroid.instance()): ViewModel() {
1314
companion object {
1415
@JvmStatic
1516
private val NanosPerSecond = 1_000_000_000.0
@@ -28,7 +29,7 @@ class MainViewModel(private val llm: Llm = Llm.instance()): ViewModel() {
2829

2930
viewModelScope.launch {
3031
try {
31-
llm.unload()
32+
llamaAndroid.unload()
3233
} catch (exc: IllegalStateException) {
3334
messages += exc.message!!
3435
}
@@ -44,7 +45,7 @@ class MainViewModel(private val llm: Llm = Llm.instance()): ViewModel() {
4445
messages += ""
4546

4647
viewModelScope.launch {
47-
llm.send(text)
48+
llamaAndroid.send(text)
4849
.catch {
4950
Log.e(tag, "send() failed", it)
5051
messages += it.message!!
@@ -57,7 +58,7 @@ class MainViewModel(private val llm: Llm = Llm.instance()): ViewModel() {
5758
viewModelScope.launch {
5859
try {
5960
val start = System.nanoTime()
60-
val warmupResult = llm.bench(pp, tg, pl, nr)
61+
val warmupResult = llamaAndroid.bench(pp, tg, pl, nr)
6162
val end = System.nanoTime()
6263

6364
messages += warmupResult
@@ -70,7 +71,7 @@ class MainViewModel(private val llm: Llm = Llm.instance()): ViewModel() {
7071
return@launch
7172
}
7273

73-
messages += llm.bench(512, 128, 1, 3)
74+
messages += llamaAndroid.bench(512, 128, 1, 3)
7475
} catch (exc: IllegalStateException) {
7576
Log.e(tag, "bench() failed", exc)
7677
messages += exc.message!!
@@ -81,7 +82,7 @@ class MainViewModel(private val llm: Llm = Llm.instance()): ViewModel() {
8182
fun load(pathToModel: String) {
8283
viewModelScope.launch {
8384
try {
84-
llm.load(pathToModel)
85+
llamaAndroid.load(pathToModel)
8586
messages += "Loaded $pathToModel"
8687
} catch (exc: IllegalStateException) {
8788
Log.e(tag, "load() failed", exc)

examples/llama.android/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
plugins {
33
id("com.android.application") version "8.2.0" apply false
44
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
5+
id("com.android.library") version "8.2.0" apply false
56
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

examples/llama.android/app/src/main/cpp/CMakeLists.txt renamed to examples/llama.android/llama/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ FetchContent_MakeAvailable(llama)
3737
# used in the AndroidManifest.xml file.
3838
add_library(${CMAKE_PROJECT_NAME} SHARED
3939
# List C/C++ source files with relative paths to this CMakeLists.txt.
40-
llama-android.cpp)
40+
llama-android.cpp)
4141

4242
# Specifies libraries CMake should link to your target library. You
4343
# can link libraries from various origins, such as libraries defined in this

examples/llama.android/llama/consumer-rules.pro

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package android.llama.cpp
2+
3+
import androidx.test.platform.app.InstrumentationRegistry
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("android.llama.cpp.test", appContext.packageName)
23+
}
24+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
</manifest>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# For more information about using CMake with Android Studio, read the
2+
# documentation: https://d.android.com/studio/projects/add-native-code.html.
3+
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
4+
5+
# Sets the minimum CMake version required for this project.
6+
cmake_minimum_required(VERSION 3.22.1)
7+
8+
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
9+
# Since this is the top level CMakeLists.txt, the project name is also accessible
10+
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
11+
# build script scope).
12+
project("llama-android")
13+
14+
include(FetchContent)
15+
FetchContent_Declare(
16+
llama
17+
GIT_REPOSITORY https://github.com/ggerganov/llama.cpp
18+
GIT_TAG master
19+
)
20+
21+
# Also provides "common"
22+
FetchContent_MakeAvailable(llama)
23+
24+
# Creates and names a library, sets it as either STATIC
25+
# or SHARED, and provides the relative paths to its source code.
26+
# You can define multiple libraries, and CMake builds them for you.
27+
# Gradle automatically packages shared libraries with your APK.
28+
#
29+
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
30+
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
31+
# is preferred for the same purpose.
32+
#
33+
# In order to load a library into your app from Java/Kotlin, you must call
34+
# System.loadLibrary() and pass the name of the library defined here;
35+
# for GameActivity/NativeActivity derived applications, the same library name must be
36+
# used in the AndroidManifest.xml file.
37+
add_library(${CMAKE_PROJECT_NAME} SHARED
38+
# List C/C++ source files with relative paths to this CMakeLists.txt.
39+
llama-android.cpp)
40+
41+
# Specifies libraries CMake should link to your target library. You
42+
# can link libraries from various origins, such as libraries defined in this
43+
# build script, prebuilt third-party libraries, or Android system libraries.
44+
target_link_libraries(${CMAKE_PROJECT_NAME}
45+
# List libraries link to the target library
46+
llama
47+
common
48+
android
49+
log)

examples/llama.android/app/src/main/cpp/llama-android.cpp renamed to examples/llama.android/llama/src/main/cpp/llama-android.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ static void log_callback(ggml_log_level level, const char * fmt, void * data) {
8181

8282
extern "C"
8383
JNIEXPORT jlong JNICALL
84-
Java_com_example_llama_Llm_load_1model(JNIEnv *env, jobject, jstring filename) {
84+
Java_android_llama_cpp_LLamaAndroid_load_1model(JNIEnv *env, jobject, jstring filename) {
8585
llama_model_params model_params = llama_model_default_params();
8686

8787
auto path_to_model = env->GetStringUTFChars(filename, 0);
@@ -101,13 +101,13 @@ Java_com_example_llama_Llm_load_1model(JNIEnv *env, jobject, jstring filename) {
101101

102102
extern "C"
103103
JNIEXPORT void JNICALL
104-
Java_com_example_llama_Llm_free_1model(JNIEnv *, jobject, jlong model) {
104+
Java_android_llama_cpp_LLamaAndroid_free_1model(JNIEnv *, jobject, jlong model) {
105105
llama_free_model(reinterpret_cast<llama_model *>(model));
106106
}
107107

108108
extern "C"
109109
JNIEXPORT jlong JNICALL
110-
Java_com_example_llama_Llm_new_1context(JNIEnv *env, jobject, jlong jmodel) {
110+
Java_android_llama_cpp_LLamaAndroid_new_1context(JNIEnv *env, jobject, jlong jmodel) {
111111
auto model = reinterpret_cast<llama_model *>(jmodel);
112112

113113
if (!model) {
@@ -139,25 +139,25 @@ Java_com_example_llama_Llm_new_1context(JNIEnv *env, jobject, jlong jmodel) {
139139

140140
extern "C"
141141
JNIEXPORT void JNICALL
142-
Java_com_example_llama_Llm_free_1context(JNIEnv *, jobject, jlong context) {
142+
Java_android_llama_cpp_LLamaAndroid_free_1context(JNIEnv *, jobject, jlong context) {
143143
llama_free(reinterpret_cast<llama_context *>(context));
144144
}
145145

146146
extern "C"
147147
JNIEXPORT void JNICALL
148-
Java_com_example_llama_Llm_backend_1free(JNIEnv *, jobject) {
148+
Java_android_llama_cpp_LLamaAndroid_backend_1free(JNIEnv *, jobject) {
149149
llama_backend_free();
150150
}
151151

152152
extern "C"
153153
JNIEXPORT void JNICALL
154-
Java_com_example_llama_Llm_log_1to_1android(JNIEnv *, jobject) {
154+
Java_android_llama_cpp_LLamaAndroid_log_1to_1android(JNIEnv *, jobject) {
155155
llama_log_set(log_callback, NULL);
156156
}
157157

158158
extern "C"
159159
JNIEXPORT jstring JNICALL
160-
Java_com_example_llama_Llm_bench_1model(
160+
Java_android_llama_cpp_LLamaAndroid_bench_1model(
161161
JNIEnv *env,
162162
jobject,
163163
jlong context_pointer,
@@ -271,13 +271,13 @@ Java_com_example_llama_Llm_bench_1model(
271271

272272
extern "C"
273273
JNIEXPORT void JNICALL
274-
Java_com_example_llama_Llm_free_1batch(JNIEnv *, jobject, jlong batch_pointer) {
274+
Java_android_llama_cpp_LLamaAndroid_free_1batch(JNIEnv *, jobject, jlong batch_pointer) {
275275
llama_batch_free(*reinterpret_cast<llama_batch *>(batch_pointer));
276276
}
277277

278278
extern "C"
279279
JNIEXPORT jlong JNICALL
280-
Java_com_example_llama_Llm_new_1batch(JNIEnv *, jobject, jint n_tokens, jint embd, jint n_seq_max) {
280+
Java_android_llama_cpp_LLamaAndroid_new_1batch(JNIEnv *, jobject, jint n_tokens, jint embd, jint n_seq_max) {
281281

282282
// Source: Copy of llama.cpp:llama_batch_init but heap-allocated.
283283

@@ -313,19 +313,19 @@ Java_com_example_llama_Llm_new_1batch(JNIEnv *, jobject, jint n_tokens, jint emb
313313

314314
extern "C"
315315
JNIEXPORT void JNICALL
316-
Java_com_example_llama_Llm_backend_1init(JNIEnv *, jobject) {
316+
Java_android_llama_cpp_LLamaAndroid_backend_1init(JNIEnv *, jobject) {
317317
llama_backend_init();
318318
}
319319

320320
extern "C"
321321
JNIEXPORT jstring JNICALL
322-
Java_com_example_llama_Llm_system_1info(JNIEnv *env, jobject) {
322+
Java_android_llama_cpp_LLamaAndroid_system_1info(JNIEnv *env, jobject) {
323323
return env->NewStringUTF(llama_print_system_info());
324324
}
325325

326326
extern "C"
327327
JNIEXPORT jint JNICALL
328-
Java_com_example_llama_Llm_completion_1init(
328+
Java_android_llama_cpp_LLamaAndroid_completion_1init(
329329
JNIEnv *env,
330330
jobject,
331331
jlong context_pointer,
@@ -376,7 +376,7 @@ Java_com_example_llama_Llm_completion_1init(
376376

377377
extern "C"
378378
JNIEXPORT jstring JNICALL
379-
Java_com_example_llama_Llm_completion_1loop(
379+
Java_android_llama_cpp_LLamaAndroid_completion_1loop(
380380
JNIEnv * env,
381381
jobject,
382382
jlong context_pointer,
@@ -438,6 +438,6 @@ Java_com_example_llama_Llm_completion_1loop(
438438

439439
extern "C"
440440
JNIEXPORT void JNICALL
441-
Java_com_example_llama_Llm_kv_1cache_1clear(JNIEnv *, jobject, jlong context) {
441+
Java_android_llama_cpp_LLamaAndroid_kv_1cache_1clear(JNIEnv *, jobject, jlong context) {
442442
llama_kv_cache_clear(reinterpret_cast<llama_context *>(context));
443443
}

examples/llama.android/app/src/main/java/com/example/llama/Llm.kt renamed to examples/llama.android/llama/src/main/java/android/llama/cpp/LLamaAndroid.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.example.llama
1+
package android.llama.cpp
22

33
import android.util.Log
44
import kotlinx.coroutines.CoroutineDispatcher
@@ -10,7 +10,7 @@ import kotlinx.coroutines.withContext
1010
import java.util.concurrent.Executors
1111
import kotlin.concurrent.thread
1212

13-
class Llm {
13+
class LLamaAndroid {
1414
private val tag: String? = this::class.simpleName
1515

1616
private val threadLocalState: ThreadLocal<State> = ThreadLocal.withInitial { State.Idle }
@@ -165,8 +165,8 @@ class Llm {
165165
}
166166

167167
// Enforce only one instance of Llm.
168-
private val _instance: Llm = Llm()
168+
private val _instance: LLamaAndroid = LLamaAndroid()
169169

170-
fun instance(): Llm = _instance
170+
fun instance(): LLamaAndroid = _instance
171171
}
172172
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package android.llama.cpp
2+
3+
import org.junit.Test
4+
5+
import org.junit.Assert.*
6+
7+
/**
8+
* Example local unit test, which will execute on the development machine (host).
9+
*
10+
* See [testing documentation](http://d.android.com/tools/testing).
11+
*/
12+
class ExampleUnitTest {
13+
@Test
14+
fun addition_isCorrect() {
15+
assertEquals(4, 2 + 2)
16+
}
17+
}

examples/llama.android/settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ dependencyResolutionManagement {
1515

1616
rootProject.name = "LlamaAndroid"
1717
include(":app")
18+
include(":llama")

0 commit comments

Comments
 (0)