Skip to content
Open
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
122 changes: 71 additions & 51 deletions extension/android/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
Expand All @@ -13,64 +13,80 @@
endif()

if(NOT ANDROID)
message(FATAL_ERROR "This directory is for Android build only")
endif()
find_package(JNI REQUIRED)
if(NOT FBJNI_HEADERS_DIR OR NOT FBJNI_LIBRARY)
message(FATAL_ERROR "For non-Android platforms, please specify -DFBJNI_HEADERS_DIR=/path/to/fbjni/include and -DFBJNI_LIBRARY=/path/to/libfbjni.so")
endif()

set(EXECUTORCH_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../..")
include(${EXECUTORCH_ROOT}/tools/cmake/Utils.cmake)
set(_common_compile_options
$<$<CXX_COMPILER_ID:MSVC>:/wd4996>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations -fPIC>
)
if(NOT ANDROID_PLATFORM)
set(ANDROID_PLATFORM android-30)
endif()
add_library(fbjni SHARED IMPORTED)
set_target_properties(
fbjni
PROPERTIES
IMPORTED_LOCATION "${FBJNI_LIBRARY}"
)
set(_common_compile_options
$<$<CXX_COMPILER_ID:MSVC>:/wd4996>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations -fPIC>
)
else()
set(EXECUTORCH_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../..")
include(${EXECUTORCH_ROOT}/tools/cmake/Utils.cmake)
set(_common_compile_options
$<$<CXX_COMPILER_ID:MSVC>:/wd4996>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations -fPIC>
)
if(NOT ANDROID_PLATFORM)
set(ANDROID_PLATFORM android-30)
endif()

# We need to download fbjni library from maven, and use its "prefab" library and
# headers, and link executorch library against that fbjni library. We don't know
# which NDK is used to compile fbjni, and we need to link our executorch library
# to the version which Android APK links against for runtime to ensure the
# libc++ dependencies are consistent. WARNING # Users need to use the SAME fbjni
# version here and in app gradle dependency for runtime compatibility!
if(NOT FBJNI_VERSION)
set(FBJNI_VERSION 0.7.0)
endif()
# We need to download fbjni library from maven, and use its "prefab" library and
# headers, and link executorch library against that fbjni library. We don't know
# which NDK is used to compile fbjni, and we need to link our executorch library
# to the version which Android APK links against for runtime to ensure the
# libc++ dependencies are consistent. WARNING # Users need to use the SAME fbjni
# version here and in app gradle dependency for runtime compatibility!
if(NOT FBJNI_VERSION)
set(FBJNI_VERSION 0.7.0)
endif()

set(FBJNI_AAR_URL
https://repo1.maven.org/maven2/com/facebook/fbjni/fbjni/${FBJNI_VERSION}/fbjni-${FBJNI_VERSION}.aar
)
set(FBJNI_DOWNLOAD_PATH ${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/fbjni.aar)
set(FBJNI_AAR_URL
https://repo1.maven.org/maven2/com/facebook/fbjni/fbjni/${FBJNI_VERSION}/fbjni-${FBJNI_VERSION}.aar
)
set(FBJNI_DOWNLOAD_PATH ${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/fbjni.aar)

if(NOT EXISTS "${FBJNI_DOWNLOAD_PATH}")
file(DOWNLOAD "${FBJNI_AAR_URL}" "${FBJNI_DOWNLOAD_PATH}")
endif()
if(NOT EXISTS "${FBJNI_DOWNLOAD_PATH}")
file(DOWNLOAD "${FBJNI_AAR_URL}" "${FBJNI_DOWNLOAD_PATH}")
endif()

add_custom_command(
OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/include/"
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/libs/android.${ANDROID_ABI}/libfbjni.so"
COMMAND unzip -o ${FBJNI_DOWNLOAD_PATH} -d
${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni
DEPENDS "${FBJNI_DOWNLOAD_PATH}"
)
add_custom_command(
OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/include/"
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/libs/android.${ANDROID_ABI}/libfbjni.so"
COMMAND unzip -o ${FBJNI_DOWNLOAD_PATH} -d
${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni
DEPENDS "${FBJNI_DOWNLOAD_PATH}"
)

add_custom_target(
fbjni_prefab
DEPENDS
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/include/"
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/libs/android.${ANDROID_ABI}/libfbjni.so"
)
add_custom_target(
fbjni_prefab
DEPENDS
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/include/"
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/libs/android.${ANDROID_ABI}/libfbjni.so"
)

add_library(fbjni SHARED IMPORTED)
add_dependencies(fbjni fbjni_prefab)
set_target_properties(
fbjni
PROPERTIES
IMPORTED_LOCATION
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/libs/android.${ANDROID_ABI}/libfbjni.so"
)
add_library(fbjni SHARED IMPORTED)
add_dependencies(fbjni fbjni_prefab)
set_target_properties(
fbjni
PROPERTIES
IMPORTED_LOCATION
"${CMAKE_CURRENT_BINARY_DIR}/third-party/fbjni/prefab/modules/fbjni/libs/android.${ANDROID_ABI}/libfbjni.so"
)
endif()

executorch_target_link_options_shared_lib(executorch)
if(ANDROID)
executorch_target_link_options_shared_lib(executorch)
endif()

add_library(
executorch_jni SHARED jni/jni_layer.cpp jni/log.cpp jni/jni_layer_runtime.cpp
Expand Down Expand Up @@ -248,4 +264,8 @@
)
target_link_options_gc_sections(executorch_jni)

target_link_libraries(executorch_jni ${link_libraries} log)
if(ANDROID)
target_link_libraries(executorch_jni ${link_libraries} log)
else()
target_link_libraries(executorch_jni ${link_libraries} ${JNI_LIBRARIES})
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

package org.pytorch.executorch

import java.lang.reflect.Method

/**
* Platform-agnostic logging helper that forwards logs to android.util.Log when running
* on Android, and falls back to standard error console logging when running on desktop JVMs.
*/
internal object Log {
private var androidLogMethod: Method? = null
private var lookupFailed = false

fun e(tag: String, msg: String) {
if (!lookupFailed && androidLogMethod == null) {
try {
val logClass = Class.forName("android.util.Log")
androidLogMethod = logClass.getMethod("e", String::class.java, String::class.java)
} catch (e: Exception) {
lookupFailed = true
}
}

val method = androidLogMethod
if (method != null) {
try {
method.invoke(null, tag, msg)
return
} catch (e: Exception) {
// Fallback to console printing if invocation fails
}
}

System.err.println("[$tag] ERROR: $msg")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

package org.pytorch.executorch

import android.util.Log
import com.facebook.jni.HybridData
import com.facebook.jni.annotations.DoNotStrip
import java.nio.Buffer
Expand Down
81 changes: 81 additions & 0 deletions extension/android/executorch_jvm/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

plugins {
alias(libs.plugins.jetbrains.kotlin.jvm)
id 'java-library'
id "com.vanniktech.maven.publish" version "0.31.0"
}

def execuTorchVersion = System.properties['execuTorchVersion']

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

repositories {
google()
mavenCentral()
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:${libs.versions.kotlin.get()}"
implementation "com.facebook.fbjni:fbjni-java-only:${fbjniJavaOnlyVersion}"
implementation "com.facebook.soloader:nativeloader:${soLoaderNativeLoaderVersion}"
}

sourceSets {
main {
kotlin {
srcDirs = ['../executorch_android/src/main/java']
exclude '**/androidTest/**'
exclude '**/test/**'
}
}
}

compileKotlin {
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs += ["-Xjvm-default=all"]
}
}

mavenPublishing {
publishToMavenCentral()
signAllPublications()

coordinates("org.pytorch", "executorch-jvm", execuTorchVersion ? execuTorchVersion : "1.2.0-SNAPSHOT")

pom {
name = "ExecuTorch JVM"
description = "ExecuTorch Java/Kotlin bindings for standard desktop JVM (Linux, macOS, Windows)"
inceptionYear = "2026"
url = "https://github.com/pytorch/executorch/"
licenses {
license {
name = "BSD 3-Clause"
url = "https://github.com/pytorch/executorch/blob/main/LICENSE"
distribution = "https://github.com/pytorch/executorch/blob/main/LICENSE"
}
}
developers {
developer {
id = "pytorch"
name = "pytorch"
url = "https://github.com/pytorch/executorch/"
}
}
scm {
url = "https://github.com/pytorch/executorch.git"
connection = "scm:git:https://github.com/pytorch/executorch"
developerConnection = "scm:git:git@github.com:pytorch/executorch.git"
}
}
}
1 change: 1 addition & 0 deletions extension/android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ junit = { module = "junit:junit", version.ref = "junit" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
[plugins]
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
2 changes: 1 addition & 1 deletion extension/android/jni/jni_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ class ExecuTorchJni : public facebook::jni::HybridClass<ExecuTorchJni> {

return ret;
#else
return facebook::jni::JArrayClass<String>::newArray(0);
return facebook::jni::JArrayClass<jstring>::newArray(0);
#endif
}

Expand Down
30 changes: 30 additions & 0 deletions extension/android/jni/log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,34 @@ void access_log_buffer(std::function<void(std::vector<log_entry>&)> accessor) {

} // namespace executorch::extension

#else

#include <cstdio>

namespace executorch::extension {

void access_log_buffer(std::function<void(std::vector<log_entry>&)> accessor) {
// No-op for non-Android
(void)accessor;
}

} // namespace executorch::extension

void et_pal_emit_log_message(
et_timestamp_t timestamp,
et_pal_log_level_t level,
const char* filename,
const char* function,
size_t line,
const char* message,
size_t length) {
(void)timestamp;
(void)filename;
(void)function;
(void)line;
(void)length;
// Fallback console log for JVM desktop
fprintf(stderr, "[ExecuTorch JNI %c] %s\n", level, message);
}

#endif
7 changes: 6 additions & 1 deletion extension/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ plugins {

rootProject.name = 'executorch'

include('executorch_android')
if (System.getenv("ANDROID_HOME") != null || System.getenv("ANDROID_SDK_ROOT") != null || new java.io.File(rootDir, "local.properties").exists()) {
include('executorch_android')
} else {
logger.warn("WARNING: ANDROID_HOME is not set, skipping Android subproject.")
}
include('executorch_jvm')
Loading