Skip to content
Closed
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
10 changes: 10 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ jobs:
path: |
sdk/runanywhere-commons/dist/RACommons-android-${{ matrix.abi }}-v${{ needs.validate.outputs.version }}.zip
sdk/runanywhere-commons/dist/RACommons-android-${{ matrix.abi }}-v${{ needs.validate.outputs.version }}.zip.sha256
sdk/runanywhere-commons/dist/RABackendLLAMACPP-android-${{ matrix.abi }}-v${{ needs.validate.outputs.version }}.zip
sdk/runanywhere-commons/dist/RABackendLLAMACPP-android-${{ matrix.abi }}-v${{ needs.validate.outputs.version }}.zip.sha256
sdk/runanywhere-commons/dist/RABackendONNX-android-${{ matrix.abi }}-v${{ needs.validate.outputs.version }}.zip
sdk/runanywhere-commons/dist/RABackendONNX-android-${{ matrix.abi }}-v${{ needs.validate.outputs.version }}.zip.sha256
retention-days: 7
if-no-files-found: error

Expand Down Expand Up @@ -1426,6 +1430,12 @@ jobs:
assert_pair "Android arm64-v8a" "RACommons-android-arm64-v8a-v${VERSION}.zip"
assert_pair "Android armeabi-v7a" "RACommons-android-armeabi-v7a-v${VERSION}.zip"
assert_pair "Android x86_64" "RACommons-android-x86_64-v${VERSION}.zip"
assert_pair "Android LlamaCPP arm64-v8a" "RABackendLLAMACPP-android-arm64-v8a-v${VERSION}.zip"
assert_pair "Android LlamaCPP armeabi-v7a" "RABackendLLAMACPP-android-armeabi-v7a-v${VERSION}.zip"
assert_pair "Android LlamaCPP x86_64" "RABackendLLAMACPP-android-x86_64-v${VERSION}.zip"
assert_pair "Android ONNX arm64-v8a" "RABackendONNX-android-arm64-v8a-v${VERSION}.zip"
assert_pair "Android ONNX armeabi-v7a" "RABackendONNX-android-armeabi-v7a-v${VERSION}.zip"
assert_pair "Android ONNX x86_64" "RABackendONNX-android-x86_64-v${VERSION}.zip"
assert_pair "Linux x86_64" "RACommons-linux-x86_64-v${VERSION}.tar.gz"
assert_pair "Windows x64" "RACommons-windows-x64-v${VERSION}.zip"
assert_pair "Web WASM" "RACommons-web-v${VERSION}.tar.gz"
Expand Down
35 changes: 35 additions & 0 deletions sdk/runanywhere-commons/scripts/build-android.sh
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,35 @@ write_checksum() {
fi
}

create_component_archive() {
local source_dir="$1"
local component="$2"
local archive="$3"
shift 3

local package_parent package_root
package_parent="$(mktemp -d "${COMMONS_ROOT}/dist/android-${component}.XXXXXX")"
package_root="${package_parent}/${component}"
mkdir -p "$package_root"
stage_component "$source_dir" "$package_root" "$@"
validate_component_source "$component" "$package_root" "$@"

mkdir -p "$(dirname "$archive")"
archive="$(cd "$(dirname "$archive")" && pwd)/$(basename "$archive")"
find "$package_root" -type d -exec chmod 0755 {} +
find "$package_root" -type f -exec chmod 0755 {} +
find "$package_root" -exec touch -h -t 198001010000 {} +
rm -f "$archive"
(
cd "$package_parent"
LC_ALL=C find "$component" -print \
| LC_ALL=C sort \
| zip -qX "$archive" -@
)
rm -rf "$package_parent"
write_checksum "$archive"
}

main() {
if [ "$#" -ne 1 ]; then
echo "usage: build-android.sh <abi>" >&2
Expand Down Expand Up @@ -240,6 +269,12 @@ main() {
create_deterministic_archive "$staging_parent" "$abi" "$archive"
write_checksum "$archive"

local llamacpp_archive="${dist}/RABackendLLAMACPP-android-${abi}-v${version}.zip"
local onnx_archive="${dist}/RABackendONNX-android-${abi}-v${version}.zip"
rm -f "$llamacpp_archive" "$llamacpp_archive.sha256" "$onnx_archive" "$onnx_archive.sha256"
create_component_archive "$staging/llamacpp" "llamacpp" "$llamacpp_archive" "${LLAMACPP_LIBS[@]}"
create_component_archive "$staging/onnx" "onnx" "$onnx_archive" "${ONNX_LIBS[@]}"

echo "✓ build-android.sh complete; staged ABI '${abi}' → ${archive}"
}

Expand Down
7 changes: 3 additions & 4 deletions sdk/runanywhere-kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import com.runanywhere.gradle.NativeLibraryDownload
import org.gradle.api.artifacts.dsl.LockMode
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

Expand Down Expand Up @@ -403,9 +404,7 @@ tasks.register("downloadJniLibs") {
val tempZip = file("$tempDir/$packageName")

try {
ant.withGroovyBuilder {
"get"("src" to zipUrl, "dest" to tempZip, "verbose" to false)
}
NativeLibraryDownload.downloadZipWithChecksum(zipUrl, tempZip)

val extractDir = file("$tempDir/extracted-${packageName.replace(".zip", "")}")
extractDir.mkdirs()
Expand All @@ -424,7 +423,7 @@ tasks.register("downloadJniLibs") {

tempZip.delete()
} catch (e: Exception) {
logger.warn("Failed to download $packageName: ${e.message}")
throw GradleException("Failed to download and verify $packageName: ${e.message}", e)
}
}

Expand Down
8 changes: 8 additions & 0 deletions sdk/runanywhere-kotlin/buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins {
`kotlin-dsl`
}

repositories {
gradlePluginPortal()
mavenCentral()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.runanywhere.gradle

import org.gradle.api.GradleException
import java.io.File
import java.net.HttpURLConnection
import java.net.URI
import java.security.MessageDigest

object NativeLibraryDownload {
private const val CONNECT_TIMEOUT_MILLIS = 30_000
private const val READ_TIMEOUT_MILLIS = 120_000

fun downloadZipWithChecksum(
zipUrl: String,
destination: File,
) {
destination.parentFile.mkdirs()
val expectedSha256 = downloadText("$zipUrl.sha256").firstSha256Token()

try {
downloadToFile(zipUrl, destination)
val actualSha256 = destination.sha256()
if (!actualSha256.equals(expectedSha256, ignoreCase = true)) {
throw GradleException(
"SHA256 mismatch for $zipUrl: expected $expectedSha256 but downloaded $actualSha256",
)
}
} catch (e: Exception) {
destination.delete()
throw e
}
}

private fun downloadToFile(
url: String,
destination: File,
) {
val connection = openHttpConnection(url)
try {
connection.inputStream.use { input ->
destination.outputStream().use { output ->
input.copyTo(output)
}
}
} finally {
connection.disconnect()
}
}

private fun downloadText(url: String): String {
val connection = openHttpConnection(url)
return try {
connection.inputStream.bufferedReader().use { it.readText() }
} finally {
connection.disconnect()
}
}

private fun openHttpConnection(url: String): HttpURLConnection {
val connection = URI(url).toURL().openConnection() as HttpURLConnection
connection.connectTimeout = CONNECT_TIMEOUT_MILLIS
connection.readTimeout = READ_TIMEOUT_MILLIS
connection.instanceFollowRedirects = true
connection.requestMethod = "GET"

val responseCode = connection.responseCode
if (responseCode !in 200..299) {
connection.disconnect()
throw GradleException("Failed to download $url: HTTP $responseCode")
}

return connection
}

private fun String.firstSha256Token(): String {
val token = trim().split(Regex("\\s+")).firstOrNull()
if (token == null || !token.matches(Regex("[a-fA-F0-9]{64}"))) {
throw GradleException("Invalid SHA256 checksum content")
}
return token.lowercase()
}

private fun File.sha256(): String {
val digest = MessageDigest.getInstance("SHA-256")
inputStream().use { input ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
while (true) {
val read = input.read(buffer)
if (read == -1) break
digest.update(buffer, 0, read)
}
}
return digest.digest().joinToString("") { "%02x".format(it) }
}
}
Loading
Loading