Skip to content

Commit

Permalink
[Android] Decouple lib and app build (mlc-ai#478)
Browse files Browse the repository at this point in the history
This PR refactors the build of Android application. It decouples the build of `tvm4j` and `mlc-llm` with the build of Android app:

1. prepare the `tvm4j-core.jar` and `libtvm4j_runtime_packed.so`
2. import the lib into the Android app

rather than build the libs and Android app together in the past. Meanwhile, this PR adopts the CMake as the only lib build tool, instead of the past Maven-CMake-NDK combination. With one `prepare_libs.sh`, we are able to build the lib from sources directly with CMake.
  • Loading branch information
cyx-6 authored Jun 28, 2023
1 parent 87d8b04 commit 74b0c65
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 240 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ if (MLC_LLM_INSTALL_STATIC_LIB)
)
# tokenizers need special handling as it builds from rust
if(MSVC)
install(FILES ${CMAKE_BINARY_DIR}/tokenizers/libtokenizers_c.lib
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/tokenizers/libtokenizers_c.lib
DESTINATION lib${LIB_SUFFIX}
)
else()
install(FILES ${CMAKE_BINARY_DIR}/tokenizers/libtokenizers_c.a
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/tokenizers/libtokenizers_c.a
DESTINATION lib${LIB_SUFFIX}
)
endif()
Expand Down
68 changes: 68 additions & 0 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
cmake_minimum_required(VERSION 3.18)

project(mlc-chat C CXX)

set(ANDROID_DIR ${CMAKE_CURRENT_LIST_DIR})
set(ANDROID_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})

set(MLC_LLM_DIR ${ANDROID_DIR}/..)
set(MLC_LLM_BINARY_DIR mlc_llm)
add_subdirectory(${MLC_LLM_DIR} ${MLC_LLM_BINARY_DIR} EXCLUDE_FROM_ALL)

if (NOT DEFINED TVM_HOME)
set(TVM_HOME ${MLC_LLM_DIR}/3rdparty/tvm)
endif (NOT DEFINED TVM_HOME)
message(STATUS "TVM_HOME: ${TVM_HOME}")

find_package(Java REQUIRED)
find_package(JNI REQUIRED)
if (JNI_FOUND)
message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}")
message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}")
endif()
include(UseJava)


file(GLOB_RECURSE javasources
${TVM_HOME}/jvm/core/src/main/java/org/apache/tvm/*.java
${ANDROID_DIR}/src/java/*.java
)
set(JNI_HEADER ${CMAKE_BINARY_DIR}/jni_header)
add_jar(tvm4j_core ${javasources} GENERATE_NATIVE_HEADERS tvm4jheaders DESTINATION ${JNI_HEADER})

add_custom_command(
TARGET tvm4j_core POST_BUILD
COMMAND ${CMAKE_COMMAND} -E rename ${JNI_HEADER}/org_apache_tvm_LibInfo.h ${JNI_HEADER}/org_apache_tvm_native_c_api.h
)

add_library(model_android STATIC IMPORTED)
set_target_properties(model_android PROPERTIES IMPORTED_LOCATION ${ANDROID_BIN_DIR}/model_lib/libmodel_android.a)

add_library(tvm4j_runtime_packed SHARED ${TVM_HOME}/jvm/native/src/main/native/org_apache_tvm_native_c_api.cc)

target_include_directories(tvm4j_runtime_packed PUBLIC
${JNI_INCLUDE_DIRS}
${JNI_HEADER}
${ANDROID_DIR}/src/cpp
${TVM_HOME}/3rdparty/dlpack/include
${TVM_HOME}/3rdparty/dmlc-core/include
${TVM_HOME}/include
)

target_link_libraries(tvm4j_runtime_packed
sentencepiece-static
tokenizers_c
tokenizers_cpp
log
-Wl,--whole-archive
tvm_runtime
mlc_llm_static
model_android
-Wl,--no-whole-archive
)

target_compile_definitions(tvm4j_runtime_packed PUBLIC TVM4J_ANDROID)
add_dependencies(tvm4j_runtime_packed tvm4j_core)

install_jar(tvm4j_core output)
install(TARGETS tvm4j_runtime_packed LIBRARY DESTINATION output/${ANDROID_ABI})
3 changes: 2 additions & 1 deletion android/MLCChat/app/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/build
/build
/src/main/libs
39 changes: 1 addition & 38 deletions android/MLCChat/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,6 @@ plugins {
id 'org.jetbrains.kotlin.android'
}

task generateJniHeaders(type: Exec, description: 'Generate JNI Headers') {
def headerPath = "${project.projectDir}/src/main/jni"
def classPath = "${project.projectDir}/../../build/tvm_home/jvm/core/target/*"
def filePath = "${project.projectDir}/../../build/tvm_home/jvm/core/src/main/java/org/apache/tvm/LibInfo.java"
commandLine "javac", "-h", headerPath, "-classpath", classPath, filePath
doLast {
file("${headerPath}/org_apache_tvm_LibInfo.h").renameTo(file("${headerPath}/org_apache_tvm_native_c_api.h"))
}
}

task copyFiles(type: Copy, description: 'Copy Sources for ndk-build') {
dependsOn "generateJniHeaders"
def ndkFilesPath = "${project.projectDir}/../../build/tvm_home/jvm/native/src/main/native"
def srcPath = "${project.projectDir}/src/main/jni/"

from "${ndkFilesPath}/org_apache_tvm_native_c_api.cc", "${ndkFilesPath}/jni_helper_func.h"
into srcPath
}

task deleteLibs(type: Delete, description: "Delete Compiled Libraries") {
dependsOn "copyFiles"
def libsPath = "${project.projectDir}/src/main/libs"
delete libsPath
}

task buildJni(type: Exec, description: 'Build JNI libs') {
dependsOn "deleteLibs"
def buildPath = "${project.projectDir}/src/main/jni"
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
commandLine "${properties.getProperty('ndk.dir')}/ndk-build", "--directory", buildPath
}

tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildJni
}

android {
namespace 'ai.mlc.mlcchat'
compileSdk 33
Expand Down Expand Up @@ -104,6 +67,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.navigation:navigation-compose:2.5.3'
implementation 'com.google.code.gson:gson:2.10.1'
implementation fileTree(dir: 'src/main/libs', include: ['*.aar', '*.jar'], exclude: [])
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
Expand All @@ -112,5 +76,4 @@ dependencies {
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'

implementation files("${project.projectDir}/../../build/tvm_home/jvm/core/target/tvm4j-core-0.0.1-SNAPSHOT.jar")
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ai.mlc.mlcchat

import ai.mlc.mlcllm.ChatModule
import android.app.Application
import android.os.Environment
import android.widget.Toast
Expand Down
97 changes: 0 additions & 97 deletions android/MLCChat/app/src/main/java/ai/mlc/mlcchat/ChatModule.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier


class MainActivity : ComponentActivity() {

@ExperimentalMaterial3Api
Expand Down
82 changes: 0 additions & 82 deletions android/MLCChat/app/src/main/jni/Android.mk

This file was deleted.

5 changes: 0 additions & 5 deletions android/MLCChat/app/src/main/jni/Application.mk

This file was deleted.

25 changes: 12 additions & 13 deletions android/prepare_libs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,28 @@ set -euxo pipefail

rustup target add aarch64-linux-android

mkdir -p build
rm -rf build
mkdir -p build/model_lib

python prepare_model_lib.py

cd build
touch config.cmake
cmake ../..\
-DCMAKE_BUILD_TYPE=Release\
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \
-DCMAKE_INSTALL_PREFIX=.\
-DCMAKE_CXX_FLAGS="-O3"\
-DCMAKE_INSTALL_PREFIX=. \
-DCMAKE_CXX_FLAGS="-O3" \
-DANDROID_ABI=arm64-v8a \
-DANDROID_NATIVE_API_LEVEL=android-24 \
-DANDROID_PLATFORM=android-24 \
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \
-DANDROID_STL=c++_static \
-DUSE_HEXAGON_SDK=OFF \
-DMLC_LLM_INSTALL_STATIC_LIB=ON\
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON\
-DMLC_LLM_INSTALL_STATIC_LIB=ON \
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON \
-DUSE_OPENCL=ON

make mlc_chat_cli -j8
make tvm4j_runtime_packed -j8
cmake --build . --target install --config release -j

cd ..
rm -rf build/tvm_home
ln -s ../3rdparty/tvm build/tvm_home

python prepare_model_lib.py
Loading

0 comments on commit 74b0c65

Please sign in to comment.