expo-modules-core 58.0.1 → 58.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/android/CMakeLists.txt +14 -4
- package/android/build.gradle +267 -8
- package/android/cmake/jsi.cmake +1 -1
- package/android/cmake/prebuilt.cmake +34 -0
- package/android/prebuilt/metadata.json +20 -0
- package/android/prebuilt/native-libs.tar.xz +0 -0
- package/android/src/main/cpp/ArrayBuffer.cpp +6 -5
- package/android/src/main/cpp/MutableBufferNativeState.cpp +36 -0
- package/android/src/main/cpp/MutableBufferNativeState.h +43 -0
- package/android/src/main/cpp/NativeArrayBuffer.cpp +3 -2
- package/android/src/main/cpp/NativeArrayBuffer.h +1 -1
- package/android/src/main/cpp/types/JNIToJSIConverter.h +2 -1
- package/android/src/main/java/expo/modules/v2/ExpoModulesV2Host.kt +1 -1
- package/android/src/main/java/expo/modules/v2/ExpoModulesV2Provider.kt +1 -1
- package/expo-module-gradle-plugin/build.gradle.kts +2 -2
- package/expo-module-gradle-plugin/src/main/kotlin/expo/modules/plugin/ProjectConfiguration.kt +1 -9
- package/expo-module-gradle-plugin/src/main/kotlin/expo/modules/plugin/Version.kt +4 -0
- package/ios/Platform/Platform.swift +3 -0
- package/package.json +3 -3
- package/prebuilds/output/debug/xcframeworks/ExpoModulesCore.tar.gz +0 -0
- package/prebuilds/output/debug/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
- package/prebuilds/output/release/xcframeworks/ExpoModulesCore.tar.gz +0 -0
- package/prebuilds/output/release/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 58.0.2 — 2026-09-15
|
|
14
|
+
|
|
15
|
+
### 🛠 Breaking changes
|
|
16
|
+
|
|
17
|
+
- [Android] Compile `expo-module-gradle-plugin` against Android Gradle Plugin 9.2.1 and drop the Android Gradle Plugin 8 compatibility code. ([#50114](https://github.com/expo/expo/pull/50114) by [@lukmccall](https://github.com/lukmccall))
|
|
18
|
+
|
|
19
|
+
### 🐛 Bug fixes
|
|
20
|
+
|
|
21
|
+
- [Android] Fixed native-backed `ArrayBuffer`s and `NativeArrayBuffer`s being copied instead of shared when passed back from JS on Hermes V1. ([#50132](https://github.com/expo/expo/pull/50132) by [@lukmccall](https://github.com/lukmccall))
|
|
22
|
+
|
|
13
23
|
## 58.0.1 — 2026-09-14
|
|
14
24
|
|
|
15
25
|
### 🎉 New features
|
|
@@ -34,6 +44,7 @@
|
|
|
34
44
|
|
|
35
45
|
### 🎉 New features
|
|
36
46
|
|
|
47
|
+
- [macOS] Added `UIColor`, `UIGestureRecognizer` and `UITextContentType` to the AppKit compatibility aliases in `Platform.swift`. ([#50110](https://github.com/expo/expo/pull/50110) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
|
37
48
|
- [Android] Added a `loadImageForManipulationFromURL` overload to `ImageLoaderInterface` that decodes the image within the given `maxWidth`/`maxHeight` bounds. ([#47877](https://github.com/expo/expo/pull/47877) by [@jiunshinn](https://github.com/jiunshinn))
|
|
38
49
|
- Add `useReleasingSharedObjectWithLifecycle` hook. ([#46494](https://github.com/expo/expo/pull/46494) by [@behenate](https://github.com/behenate))
|
|
39
50
|
- Added `ArrayBuffer` as the preferred safe native module argument and return type, and deprecated `NativeArrayBuffer` in favor of it. ([#47106](https://github.com/expo/expo/pull/47106) by [@barthap](https://github.com/barthap))
|
package/android/CMakeLists.txt
CHANGED
|
@@ -7,11 +7,21 @@ project(expo-modules-core)
|
|
|
7
7
|
include(${CMAKE_SOURCE_DIR}/cmake/variables.cmake)
|
|
8
8
|
include(${CMAKE_SOURCE_DIR}/cmake/common.cmake)
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
|
|
10
|
+
# When prebuilt artifacts are shipped in the package, imports expo-modules-jsi / expo-modules-core
|
|
11
|
+
# as IMPORTED targets (and sets EXPO_JSI_PREBUILT / EXPO_CORE_PREBUILT) so they are not recompiled.
|
|
12
|
+
if (EXPO_PREBUILT_NATIVE_LIBS_DIR)
|
|
13
|
+
include(${CMAKE_SOURCE_DIR}/cmake/prebuilt.cmake)
|
|
14
|
+
endif ()
|
|
15
|
+
|
|
16
|
+
# defines expo-modules-jsi target, unless it was imported from a prebuilt artifact above
|
|
17
|
+
if (NOT EXPO_JSI_PREBUILT)
|
|
18
|
+
include(${CMAKE_SOURCE_DIR}/cmake/jsi.cmake)
|
|
19
|
+
endif ()
|
|
12
20
|
|
|
13
|
-
# defines expo-modules-core target
|
|
14
|
-
|
|
21
|
+
# defines expo-modules-core target, unless it was imported from a prebuilt artifact above
|
|
22
|
+
if (NOT EXPO_CORE_PREBUILT)
|
|
23
|
+
include(${CMAKE_SOURCE_DIR}/cmake/main.cmake)
|
|
24
|
+
endif ()
|
|
15
25
|
|
|
16
26
|
# defines expo-modules-worklets target (worklets integration), only when react-native-worklets is installed
|
|
17
27
|
if (REACT_NATIVE_WORKLETS_DIR)
|
package/android/build.gradle
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import groovy.json.JsonSlurper
|
|
2
1
|
import expo.modules.plugin.gradle.ExpoModuleExtension
|
|
2
|
+
import groovy.json.JsonOutput
|
|
3
|
+
import groovy.json.JsonSlurper
|
|
4
|
+
import org.apache.commons.compress.archivers.tar.TarArchiveEntry
|
|
5
|
+
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
|
|
6
|
+
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
|
|
3
7
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
8
|
+
import org.tukaani.xz.LZMA2Options
|
|
9
|
+
import org.tukaani.xz.XZInputStream
|
|
10
|
+
import org.tukaani.xz.XZOutputStream
|
|
11
|
+
|
|
12
|
+
import java.nio.file.Files
|
|
13
|
+
import java.nio.file.StandardCopyOption
|
|
14
|
+
import java.time.Instant
|
|
15
|
+
import java.util.regex.Matcher
|
|
4
16
|
|
|
5
17
|
buildscript {
|
|
6
18
|
// List of features that are required by linked modules
|
|
@@ -16,6 +28,8 @@ buildscript {
|
|
|
16
28
|
classpath("org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin:${kotlinVersion}")
|
|
17
29
|
}
|
|
18
30
|
|
|
31
|
+
classpath "org.tukaani:xz:1.12"
|
|
32
|
+
classpath "org.apache.commons:commons-compress:1.28.0"
|
|
19
33
|
}
|
|
20
34
|
}
|
|
21
35
|
|
|
@@ -27,7 +41,7 @@ if (shouldIncludeCompose) {
|
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
group = 'host.exp.exponent'
|
|
30
|
-
version = '58.0.
|
|
44
|
+
version = '58.0.2'
|
|
31
45
|
|
|
32
46
|
def isExpoModulesCoreTests = {
|
|
33
47
|
Gradle gradle = getGradle()
|
|
@@ -78,7 +92,7 @@ if (workletsProject != null) {
|
|
|
78
92
|
|
|
79
93
|
def reactNativeWorkletsRootDir = workletsProject?.projectDir?.parentFile
|
|
80
94
|
|
|
81
|
-
def expoModulesV2Version = "0.1.
|
|
95
|
+
def expoModulesV2Version = "0.1.5"
|
|
82
96
|
|
|
83
97
|
configurations {
|
|
84
98
|
// Artifact-only: the C++ sources, not something to put on any classpath.
|
|
@@ -99,6 +113,36 @@ tasks.matching {
|
|
|
99
113
|
it.dependsOn(unpackExpoModulesV2Cpp)
|
|
100
114
|
}
|
|
101
115
|
|
|
116
|
+
def reactNativeVersionString = expoModuleExtension.reactNativeVersion.toString()
|
|
117
|
+
|
|
118
|
+
def prebuiltNativeLibsDir = project.file("prebuilt")
|
|
119
|
+
def prebuiltNativeLibsArchive = project.file("prebuilt/native-libs.tar.xz")
|
|
120
|
+
def prebuiltMetadataFile = project.file("prebuilt/metadata.json")
|
|
121
|
+
|
|
122
|
+
def decompressedNativeLibsDir = layout.buildDirectory.dir("expo-prebuilt-natives").get().asFile
|
|
123
|
+
|
|
124
|
+
def prebuiltVariants = ["debug", "release"]
|
|
125
|
+
|
|
126
|
+
def isPrebuiltCachingRun = gradle.startParameter.taskNames.any { it.contains("cachePrebuiltNativeLibs") }
|
|
127
|
+
|
|
128
|
+
def isPrebuiltUsable = {
|
|
129
|
+
if (!prebuiltMetadataFile.exists() || !prebuiltNativeLibsArchive.exists()) {
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
def metadata = new JsonSlurper().parseText(prebuiltMetadataFile.text)
|
|
133
|
+
if (metadata.libraryVersion != version) {
|
|
134
|
+
logger.warn("[expo-modules-core] Ignoring prebuilt libs: built for v${metadata.libraryVersion}, package is v${version} - building from source")
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
if (metadata.reactNativeVersion != reactNativeVersionString) {
|
|
138
|
+
logger.warn("[expo-modules-core] Ignoring prebuilt libs: built for RN ${metadata.reactNativeVersion}, installed RN ${reactNativeVersionString} - building from source")
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
return true
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
def usePrebuiltNativeLibs = !isPrebuiltCachingRun && !isTests && isPrebuiltUsable()
|
|
145
|
+
|
|
102
146
|
expoModule {
|
|
103
147
|
canBePublished false
|
|
104
148
|
}
|
|
@@ -115,7 +159,7 @@ android {
|
|
|
115
159
|
defaultConfig {
|
|
116
160
|
consumerProguardFiles 'proguard-rules.pro'
|
|
117
161
|
versionCode 1
|
|
118
|
-
versionName "58.0.
|
|
162
|
+
versionName "58.0.2"
|
|
119
163
|
buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
|
|
120
164
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
|
|
121
165
|
|
|
@@ -137,6 +181,10 @@ android {
|
|
|
137
181
|
cppArguments += "-DREACT_NATIVE_WORKLETS_DIR=${reactNativeWorkletsRootDir.absolutePath}"
|
|
138
182
|
}
|
|
139
183
|
|
|
184
|
+
if (usePrebuiltNativeLibs) {
|
|
185
|
+
cppArguments += "-DEXPO_PREBUILT_NATIVE_LIBS_DIR=${decompressedNativeLibsDir.absolutePath}"
|
|
186
|
+
}
|
|
187
|
+
|
|
140
188
|
abiFilters(*reactNativeArchitectures())
|
|
141
189
|
arguments(*cppArguments)
|
|
142
190
|
}
|
|
@@ -210,6 +258,14 @@ android {
|
|
|
210
258
|
}
|
|
211
259
|
}
|
|
212
260
|
}
|
|
261
|
+
|
|
262
|
+
// Package the decompressed prebuilt .so into each variant. The layout `<variant>/<abi>/lib*.so`
|
|
263
|
+
// matches what AGP expects from a jniLibs source set.
|
|
264
|
+
if (usePrebuiltNativeLibs) {
|
|
265
|
+
prebuiltVariants.each { variant ->
|
|
266
|
+
getByName(variant).jniLibs.srcDir(new File(decompressedNativeLibsDir, variant))
|
|
267
|
+
}
|
|
268
|
+
}
|
|
213
269
|
}
|
|
214
270
|
|
|
215
271
|
testOptions {
|
|
@@ -320,10 +376,10 @@ def generateStubPCHTask = tasks.register("generateStubPCH") {
|
|
|
320
376
|
def stubHeaderPath = stubHeader.absolutePath.replace('\\', '/')
|
|
321
377
|
|
|
322
378
|
def cmd = entry.command
|
|
323
|
-
|
|
324
|
-
.replaceAll(/-Xclang -include -Xclang [^\s]+cmake_pch\.hxx(?=\s)/,
|
|
325
|
-
|
|
326
|
-
.replaceAll(/[^\s]+cmake_pch\.hxx\.cxx/,
|
|
379
|
+
// Replace the forced-include path: `-Xclang -include -Xclang <path>/cmake_pch.hxx`
|
|
380
|
+
.replaceAll(/-Xclang -include -Xclang [^\s]+cmake_pch\.hxx(?=\s)/, Matcher.quoteReplacement("-Xclang -include -Xclang ${stubHeaderPath}"))
|
|
381
|
+
// Replace the source file operand: `<path>/cmake_pch.hxx.cxx`
|
|
382
|
+
.replaceAll(/[^\s]+cmake_pch\.hxx\.cxx/, Matcher.quoteReplacement(stubHeaderPath))
|
|
327
383
|
|
|
328
384
|
def process = new ProcessBuilder(cmd.split(" ").toList())
|
|
329
385
|
.directory(new File(entry.directory))
|
|
@@ -344,3 +400,206 @@ def generateStubPCHTask = tasks.register("generateStubPCH") {
|
|
|
344
400
|
tasks.register("prepareKotlinBuildScriptModel") {
|
|
345
401
|
dependsOn(generateStubPCHTask)
|
|
346
402
|
}
|
|
403
|
+
|
|
404
|
+
def prebuiltNativeLibNames = ["libexpo-modules-core.so", "libexpo-modules-jsi.so"]
|
|
405
|
+
|
|
406
|
+
// Helpers for the cachePrebuiltNativeLibs task. They are `static` so the task action can call them
|
|
407
|
+
// without capturing the build script, which keeps the task configuration-cache compatible.
|
|
408
|
+
class PrebuiltNativeLibs {
|
|
409
|
+
/**
|
|
410
|
+
* Returns host tag based on the OS name.
|
|
411
|
+
*/
|
|
412
|
+
private static String hostTag() {
|
|
413
|
+
def osName = System.getProperty("os.name").toLowerCase()
|
|
414
|
+
if (osName.contains("mac")) {
|
|
415
|
+
return "darwin-x86_64"
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (osName.contains("win")) {
|
|
419
|
+
return "windows-x86_64"
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return "linux-x86_64"
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private static void strippedLib(File stripBinary, File source, File dest) {
|
|
426
|
+
def proc = new ProcessBuilder(
|
|
427
|
+
stripBinary.absolutePath,
|
|
428
|
+
"--strip-all",
|
|
429
|
+
source.absolutePath,
|
|
430
|
+
"-o",
|
|
431
|
+
dest.absolutePath
|
|
432
|
+
)
|
|
433
|
+
.redirectErrorStream(true)
|
|
434
|
+
.start()
|
|
435
|
+
|
|
436
|
+
if (proc.waitFor() != 0) {
|
|
437
|
+
throw new GradleException("[expo-modules-core] llvm-strip failed for ${source}: ${proc.inputStream.text}")
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Locate the NDK's `llvm-strip`, or null when the NDK/binary can't be found.
|
|
443
|
+
*/
|
|
444
|
+
static File findLlvmStrip(File ndkDir) {
|
|
445
|
+
if (ndkDir == null || !ndkDir.exists()) {
|
|
446
|
+
return null
|
|
447
|
+
}
|
|
448
|
+
def hostTag = hostTag()
|
|
449
|
+
def strip = new File(ndkDir, "toolchains/llvm/prebuilt/${hostTag}/bin/llvm-strip")
|
|
450
|
+
return strip.exists() ? strip : null
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Copy each of `soNames` from `objDir/<abi>/` into `stagingVariantDir/<abi>/`, stripping debug
|
|
455
|
+
* symbols. Returns the set of ABIs that were staged.
|
|
456
|
+
*/
|
|
457
|
+
static Set<String> stageStrippedLibs(File objDir, List<String> soNames, File stagingVariantDir, File stripBinary) {
|
|
458
|
+
def abis = new TreeSet<String>()
|
|
459
|
+
objDir
|
|
460
|
+
.listFiles({ it.isDirectory() } as FileFilter)
|
|
461
|
+
?.each { abiDir ->
|
|
462
|
+
soNames.each { soName ->
|
|
463
|
+
def source = new File(abiDir, soName)
|
|
464
|
+
if (!source.exists()) {
|
|
465
|
+
return
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
def dest = new File(stagingVariantDir, "${abiDir.name}/${soName}")
|
|
469
|
+
dest.parentFile.mkdirs()
|
|
470
|
+
|
|
471
|
+
if (stripBinary != null) {
|
|
472
|
+
strippedLib(stripBinary, source, dest)
|
|
473
|
+
} else {
|
|
474
|
+
Files.copy(source.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
abis << abiDir.name
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return abis
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Bundle every file under `stagingDir` into a single solid tar.xz
|
|
485
|
+
*/
|
|
486
|
+
static void bundleToTarXz(File stagingDir, File archive) {
|
|
487
|
+
archive.parentFile.mkdirs()
|
|
488
|
+
def stagingPath = stagingDir.toPath()
|
|
489
|
+
|
|
490
|
+
def files = []
|
|
491
|
+
stagingDir.eachFileRecurse {
|
|
492
|
+
if (it.isFile()) {
|
|
493
|
+
files << it
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
files.sort { it.absolutePath }
|
|
497
|
+
|
|
498
|
+
new TarArchiveOutputStream(
|
|
499
|
+
new XZOutputStream(archive.newOutputStream(), new LZMA2Options(9))
|
|
500
|
+
).withCloseable { tar ->
|
|
501
|
+
files.each { file ->
|
|
502
|
+
def archiveEntry = new TarArchiveEntry(file, stagingPath.relativize(file.toPath()).toString())
|
|
503
|
+
tar.putArchiveEntry(archiveEntry)
|
|
504
|
+
Files.copy(file.toPath(), tar)
|
|
505
|
+
tar.closeArchiveEntry()
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
def stripBinary = PrebuiltNativeLibs.findLlvmStrip(android.ndkDirectory)
|
|
512
|
+
def libraryVersion = version.toString()
|
|
513
|
+
def cacheStagingDir = layout.buildDirectory.dir("tmp/cache-prebuilt-natives").get().asFile
|
|
514
|
+
def variantDescriptors = prebuiltVariants.collect { variant ->
|
|
515
|
+
def variantCap = variant.capitalize()
|
|
516
|
+
def libsDir = layout.buildDirectory.dir("intermediates/merged_native_libs/${variant}/merge${variantCap}NativeLibs/out/lib").get().asFile
|
|
517
|
+
def outDir = layout.buildDirectory.dir("tmp/cache-prebuilt-natives/${variant}").get().asFile
|
|
518
|
+
[
|
|
519
|
+
name : variant,
|
|
520
|
+
mergeTask: "merge${variantCap}NativeLibs",
|
|
521
|
+
libsDir : libsDir,
|
|
522
|
+
outDir : outDir,
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
tasks.register("cachePrebuiltNativeLibs") {
|
|
527
|
+
group = "prebuilt natives"
|
|
528
|
+
description = "Build expo-modules-core from source (debug + release), strip and cache the shippable .so files into prebuilt/"
|
|
529
|
+
dependsOn(variantDescriptors.collect { it.mergeTask })
|
|
530
|
+
|
|
531
|
+
doLast {
|
|
532
|
+
if (stripBinary == null) {
|
|
533
|
+
logger.warn("[expo-modules-core] NDK llvm-strip not found - .so files will be cached unstripped")
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (prebuiltNativeLibsDir.exists()) {
|
|
537
|
+
prebuiltNativeLibsDir.deleteDir()
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (cacheStagingDir.exists()) {
|
|
541
|
+
cacheStagingDir.deleteDir()
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
def allAbis = new TreeSet<String>()
|
|
545
|
+
variantDescriptors.each { vd ->
|
|
546
|
+
logger.lifecycle("[expo-modules-core] Caching ${vd.name} from ${vd.libsDir}")
|
|
547
|
+
allAbis.addAll(PrebuiltNativeLibs.stageStrippedLibs(vd.libsDir, prebuiltNativeLibNames, vd.outDir, stripBinary))
|
|
548
|
+
}
|
|
549
|
+
if (allAbis.isEmpty()) {
|
|
550
|
+
throw new GradleException("[expo-modules-core] No ${prebuiltNativeLibNames} were cached")
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
PrebuiltNativeLibs.bundleToTarXz(cacheStagingDir, prebuiltNativeLibsArchive)
|
|
554
|
+
cacheStagingDir.deleteDir()
|
|
555
|
+
|
|
556
|
+
prebuiltMetadataFile.text = JsonOutput.prettyPrint(JsonOutput.toJson([
|
|
557
|
+
libraryVersion : libraryVersion,
|
|
558
|
+
reactNativeVersion: reactNativeVersionString,
|
|
559
|
+
variants : variantDescriptors.collect { it.name },
|
|
560
|
+
abis : allAbis as List,
|
|
561
|
+
soFiles : prebuiltNativeLibNames as List,
|
|
562
|
+
stripped : stripBinary != null,
|
|
563
|
+
createdAt : Instant.now().toString(),
|
|
564
|
+
])) + "\n"
|
|
565
|
+
|
|
566
|
+
def archiveSizeMb = prebuiltNativeLibsArchive.length() / (1024 * 1024)
|
|
567
|
+
logger.lifecycle(
|
|
568
|
+
"[expo-modules-core] Create ${prebuiltNativeLibNames.join(", ")} for ${allAbis.size()} ABIs " +
|
|
569
|
+
"(${allAbis.join(", ")}) x ${variantDescriptors.size()} variants into " +
|
|
570
|
+
"${prebuiltNativeLibsArchive.name} (~${archiveSizeMb}MB, stripped, solid tar.xz)"
|
|
571
|
+
)
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Unpacks the shipped solid `prebuilt/native-libs.tar.xz` into the build dir so CMake can import the
|
|
576
|
+
// libs and AGP can package them via the jniLibs source sets
|
|
577
|
+
if (usePrebuiltNativeLibs) {
|
|
578
|
+
def archiveFile = prebuiltNativeLibsArchive
|
|
579
|
+
def outputDir = decompressedNativeLibsDir
|
|
580
|
+
def decompressPrebuiltNativeLibs = tasks.register("decompressPrebuiltNativeLibs") {
|
|
581
|
+
description = "Unpack the shipped native-libs.tar.xz into the build dir for CMake import and packaging"
|
|
582
|
+
inputs.file(archiveFile)
|
|
583
|
+
outputs.dir(outputDir)
|
|
584
|
+
|
|
585
|
+
doLast {
|
|
586
|
+
outputDir.deleteDir()
|
|
587
|
+
new TarArchiveInputStream(
|
|
588
|
+
new XZInputStream(archiveFile.newInputStream())
|
|
589
|
+
).withCloseable { tar ->
|
|
590
|
+
def entry
|
|
591
|
+
while ((entry = tar.nextEntry) != null) {
|
|
592
|
+
if (entry.isDirectory()) {
|
|
593
|
+
continue
|
|
594
|
+
}
|
|
595
|
+
// entries are `<variant>/<abi>/lib*.so`
|
|
596
|
+
def dest = new File(outputDir, entry.name)
|
|
597
|
+
dest.parentFile.mkdirs()
|
|
598
|
+
dest.withOutputStream { out -> tar.transferTo(out) }
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
tasks.named("preBuild").configure { dependsOn(decompressPrebuiltNativeLibs) }
|
|
605
|
+
}
|
package/android/cmake/jsi.cmake
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
if (${CMAKE_BUILD_TYPE} MATCHES "Debug")
|
|
2
|
+
set(EXPO_PREBUILT_VARIANT "debug")
|
|
3
|
+
else ()
|
|
4
|
+
set(EXPO_PREBUILT_VARIANT "release")
|
|
5
|
+
endif ()
|
|
6
|
+
|
|
7
|
+
set(EXPO_PREBUILT_ABI_DIR "${EXPO_PREBUILT_NATIVE_LIBS_DIR}/${EXPO_PREBUILT_VARIANT}/${ANDROID_ABI}")
|
|
8
|
+
|
|
9
|
+
# expo-modules-jsi
|
|
10
|
+
set(EXPO_PREBUILT_JSI_SO "${EXPO_PREBUILT_ABI_DIR}/libexpo-modules-jsi.so")
|
|
11
|
+
if (EXISTS "${EXPO_PREBUILT_JSI_SO}")
|
|
12
|
+
add_library(expo-modules-jsi SHARED IMPORTED)
|
|
13
|
+
set_target_properties(
|
|
14
|
+
expo-modules-jsi
|
|
15
|
+
PROPERTIES
|
|
16
|
+
IMPORTED_LOCATION "${EXPO_PREBUILT_JSI_SO}"
|
|
17
|
+
INTERFACE_INCLUDE_DIRECTORIES "${COMMON_DIR}/JSI;${ANDROID_SRC_DIR}/jsi"
|
|
18
|
+
)
|
|
19
|
+
set(EXPO_JSI_PREBUILT TRUE)
|
|
20
|
+
message(STATUS "[expo-modules-core] Using prebuilt libexpo-modules-jsi.so (${EXPO_PREBUILT_VARIANT}/${ANDROID_ABI})")
|
|
21
|
+
endif ()
|
|
22
|
+
|
|
23
|
+
# expo-modules-core
|
|
24
|
+
set(EXPO_PREBUILT_CORE_SO "${EXPO_PREBUILT_ABI_DIR}/libexpo-modules-core.so")
|
|
25
|
+
if (EXISTS "${EXPO_PREBUILT_CORE_SO}")
|
|
26
|
+
add_library(expo-modules-core SHARED IMPORTED)
|
|
27
|
+
set_target_properties(
|
|
28
|
+
expo-modules-core
|
|
29
|
+
PROPERTIES
|
|
30
|
+
IMPORTED_LOCATION "${EXPO_PREBUILT_CORE_SO}"
|
|
31
|
+
)
|
|
32
|
+
set(EXPO_CORE_PREBUILT TRUE)
|
|
33
|
+
message(STATUS "[expo-modules-core] Using prebuilt libexpo-modules-core.so (${EXPO_PREBUILT_VARIANT}/${ANDROID_ABI})")
|
|
34
|
+
endif ()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"libraryVersion": "58.0.1",
|
|
3
|
+
"reactNativeVersion": "0.88.0",
|
|
4
|
+
"variants": [
|
|
5
|
+
"debug",
|
|
6
|
+
"release"
|
|
7
|
+
],
|
|
8
|
+
"abis": [
|
|
9
|
+
"arm64-v8a",
|
|
10
|
+
"armeabi-v7a",
|
|
11
|
+
"x86",
|
|
12
|
+
"x86_64"
|
|
13
|
+
],
|
|
14
|
+
"soFiles": [
|
|
15
|
+
"libexpo-modules-core.so",
|
|
16
|
+
"libexpo-modules-jsi.so"
|
|
17
|
+
],
|
|
18
|
+
"stripped": true,
|
|
19
|
+
"createdAt": "2026-09-15T14:36:32.485597Z"
|
|
20
|
+
}
|
|
Binary file
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "ArrayBuffer.h"
|
|
2
|
+
#include "MutableBufferNativeState.h"
|
|
2
3
|
|
|
3
4
|
#include "Exceptions.h"
|
|
4
5
|
#include "JavaReferencesCache.h"
|
|
@@ -184,7 +185,7 @@ jni::local_ref<ArrayBuffer::javaobject> makeArrayBufferFromJSIArrayBuffer(
|
|
|
184
185
|
size_t length,
|
|
185
186
|
const std::function<uint8_t *(jsi::ArrayBuffer &)> ©SourceProvider
|
|
186
187
|
) {
|
|
187
|
-
auto mutableBuffer =
|
|
188
|
+
auto mutableBuffer = expo::tryGetMutableBuffer(runtime, arrayBuffer);
|
|
188
189
|
if (mutableBuffer) {
|
|
189
190
|
return makeArrayBufferObject(
|
|
190
191
|
jsiContext,
|
|
@@ -285,7 +286,7 @@ std::shared_ptr<jsi::MutableBuffer> ByteBufferArrayBufferStorage::jsiMutableBuff
|
|
|
285
286
|
}
|
|
286
287
|
|
|
287
288
|
jsi::Value ByteBufferArrayBufferStorage::toJSIValue(jsi::Runtime &runtime) {
|
|
288
|
-
return jsi::Value(runtime, runtime
|
|
289
|
+
return jsi::Value(runtime, createNativeBackedArrayBuffer(runtime, jsiMutableBuffer()));
|
|
289
290
|
}
|
|
290
291
|
|
|
291
292
|
jni::local_ref<jni::JObject> ByteBufferArrayBufferStorage::withJSBytes(
|
|
@@ -328,7 +329,7 @@ std::shared_ptr<jsi::MutableBuffer> MutableBufferViewArrayBufferStorage::jsiMuta
|
|
|
328
329
|
}
|
|
329
330
|
|
|
330
331
|
jsi::Value MutableBufferViewArrayBufferStorage::toJSIValue(jsi::Runtime &runtime) {
|
|
331
|
-
return jsi::Value(runtime, runtime
|
|
332
|
+
return jsi::Value(runtime, createNativeBackedArrayBuffer(runtime, jsiMutableBuffer()));
|
|
332
333
|
}
|
|
333
334
|
|
|
334
335
|
jni::local_ref<jni::JObject> MutableBufferViewArrayBufferStorage::withJSBytes(
|
|
@@ -446,11 +447,11 @@ jsi::Value JavaScriptBackedArrayBufferStorage::toJSIValue(jsi::Runtime &runtime)
|
|
|
446
447
|
}
|
|
447
448
|
auto byteBuffer = copyToDirectBuffer(_arrayBuffer->data(runtime) + _offset, _length);
|
|
448
449
|
auto mutableBuffer = std::make_shared<ByteBufferJSIMutableBuffer>(byteBuffer);
|
|
449
|
-
return jsi::Value(runtime, runtime
|
|
450
|
+
return jsi::Value(runtime, createNativeBackedArrayBuffer(runtime, std::move(mutableBuffer)));
|
|
450
451
|
}
|
|
451
452
|
}
|
|
452
453
|
|
|
453
|
-
return jsi::Value(runtime, runtime
|
|
454
|
+
return jsi::Value(runtime, createNativeBackedArrayBuffer(runtime, jsiMutableBuffer()));
|
|
454
455
|
}
|
|
455
456
|
|
|
456
457
|
jni::local_ref<jni::JObject> JavaScriptBackedArrayBufferStorage::withJSBytes(
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#include "MutableBufferNativeState.h"
|
|
2
|
+
|
|
3
|
+
namespace expo {
|
|
4
|
+
|
|
5
|
+
MutableBufferNativeState::MutableBufferNativeState(std::shared_ptr<jsi::MutableBuffer> buffer)
|
|
6
|
+
: buffer(std::move(buffer)) {}
|
|
7
|
+
|
|
8
|
+
jsi::ArrayBuffer createNativeBackedArrayBuffer(
|
|
9
|
+
jsi::Runtime &runtime,
|
|
10
|
+
std::shared_ptr<jsi::MutableBuffer> buffer
|
|
11
|
+
) {
|
|
12
|
+
auto arrayBuffer = runtime.createArrayBuffer(buffer);
|
|
13
|
+
arrayBuffer.setNativeState(runtime, std::make_shared<MutableBufferNativeState>(std::move(buffer)));
|
|
14
|
+
return arrayBuffer;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
std::shared_ptr<jsi::MutableBuffer> tryGetMutableBuffer(
|
|
18
|
+
jsi::Runtime &runtime,
|
|
19
|
+
const jsi::ArrayBuffer &arrayBuffer
|
|
20
|
+
) {
|
|
21
|
+
if (auto buffer = arrayBuffer.tryGetMutableBuffer(runtime)) {
|
|
22
|
+
return buffer;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!arrayBuffer.hasNativeState<MutableBufferNativeState>(runtime)) {
|
|
26
|
+
return nullptr;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
auto buffer = arrayBuffer.getNativeState<MutableBufferNativeState>(runtime)->buffer;
|
|
30
|
+
if (!buffer || arrayBuffer.data(runtime) != buffer->data()) {
|
|
31
|
+
return nullptr;
|
|
32
|
+
}
|
|
33
|
+
return buffer;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
} // namespace expo
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "ExpoHeader.pch"
|
|
4
|
+
|
|
5
|
+
namespace expo {
|
|
6
|
+
|
|
7
|
+
namespace jsi = facebook::jsi;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Native state attached to every native-backed `jsi::ArrayBuffer` created by Expo.
|
|
11
|
+
* It retains the `jsi::MutableBuffer` that owns the bytes, so the buffer can be recovered
|
|
12
|
+
* later with `expo::tryGetMutableBuffer` even when the runtime doesn't implement
|
|
13
|
+
* `jsi::Runtime::tryGetMutableBuffer`. Hermes V1 returns `nullptr` from that method,
|
|
14
|
+
* which would otherwise force a copy and break the zero-copy contract.
|
|
15
|
+
*/
|
|
16
|
+
class MutableBufferNativeState final : public jsi::NativeState {
|
|
17
|
+
public:
|
|
18
|
+
explicit MutableBufferNativeState(std::shared_ptr<jsi::MutableBuffer> buffer);
|
|
19
|
+
|
|
20
|
+
std::shared_ptr<jsi::MutableBuffer> buffer;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a JS `ArrayBuffer` that shares the bytes of `buffer` and tags it with
|
|
25
|
+
* `MutableBufferNativeState`, so `expo::tryGetMutableBuffer` can find the buffer again.
|
|
26
|
+
*/
|
|
27
|
+
jsi::ArrayBuffer createNativeBackedArrayBuffer(
|
|
28
|
+
jsi::Runtime &runtime,
|
|
29
|
+
std::shared_ptr<jsi::MutableBuffer> buffer
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns the `jsi::MutableBuffer` backing `arrayBuffer`, or `nullptr` when the bytes live
|
|
34
|
+
* on the JS heap. Asks the runtime first and falls back to the native state attached by
|
|
35
|
+
* `createNativeBackedArrayBuffer`. The fallback is used only when the JS object still points
|
|
36
|
+
* at the retained buffer's memory, so a detached or re-attached `ArrayBuffer` is never shared.
|
|
37
|
+
*/
|
|
38
|
+
std::shared_ptr<jsi::MutableBuffer> tryGetMutableBuffer(
|
|
39
|
+
jsi::Runtime &runtime,
|
|
40
|
+
const jsi::ArrayBuffer &arrayBuffer
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
} // namespace expo
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "NativeArrayBuffer.h"
|
|
2
|
+
#include "MutableBufferNativeState.h"
|
|
2
3
|
|
|
3
4
|
#include "JavaScriptRuntime.h"
|
|
4
5
|
#include "JSIContext.h"
|
|
@@ -57,7 +58,7 @@ NativeArrayBuffer::initHybrid(jni::alias_ref<JavaPart::javaobject>,
|
|
|
57
58
|
jni::local_ref<NativeArrayBuffer::javaobject>
|
|
58
59
|
NativeArrayBuffer::newInstance(JSIContext *jsiContext, jsi::Runtime &runtime,
|
|
59
60
|
jsi::ArrayBuffer &arrayBuffer) {
|
|
60
|
-
auto mutableBuffer =
|
|
61
|
+
auto mutableBuffer = expo::tryGetMutableBuffer(runtime, arrayBuffer);
|
|
61
62
|
if (mutableBuffer) {
|
|
62
63
|
auto byteBuffer = jni::JByteBuffer::wrapBytes(mutableBuffer->data(), mutableBuffer->size());
|
|
63
64
|
byteBuffer->order(jni::JByteOrder::nativeOrder());
|
|
@@ -82,7 +83,7 @@ NativeArrayBuffer::newInstance(JSIContext *jsiContext, jsi::Runtime &runtime,
|
|
|
82
83
|
size_t size = typedArray.byteLength(runtime);
|
|
83
84
|
|
|
84
85
|
auto backingBuffer = typedArray.getBuffer(runtime);
|
|
85
|
-
auto mutableBuffer =
|
|
86
|
+
auto mutableBuffer = expo::tryGetMutableBuffer(runtime, backingBuffer);
|
|
86
87
|
if (mutableBuffer) {
|
|
87
88
|
size_t offset = typedArray.byteOffset(runtime);
|
|
88
89
|
auto byteBuffer = jni::JByteBuffer::wrapBytes(
|
|
@@ -56,7 +56,7 @@ public:
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Creates a NativeArrayBuffer from the given ArrayBuffer. Uses zero-copy when the
|
|
59
|
-
* buffer is native-backed (tryGetMutableBuffer), otherwise copies the data.
|
|
59
|
+
* buffer is native-backed (`expo::tryGetMutableBuffer`), otherwise copies the data.
|
|
60
60
|
*/
|
|
61
61
|
static jni::local_ref<NativeArrayBuffer::javaobject> newInstance(
|
|
62
62
|
JSIContext *jsiContext,
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
#include "ObjectDeallocator.h"
|
|
11
11
|
#include "../JavaScriptArrayBuffer.h"
|
|
12
12
|
#include "../NativeArrayBuffer.h"
|
|
13
|
+
#include "../MutableBufferNativeState.h"
|
|
13
14
|
#include "../concepts/jni_deref.h"
|
|
14
15
|
#include "../concepts/jni.h"
|
|
15
16
|
#include "../concepts/jsi.h"
|
|
@@ -182,7 +183,7 @@ struct JNIToJSIConverter<JavaScriptArrayBuffer *> {
|
|
|
182
183
|
template<>
|
|
183
184
|
struct JNIToJSIConverter<NativeArrayBuffer *> {
|
|
184
185
|
static jsi::Value convert(JNIEnv *, jsi::Runtime &rt, NativeArrayBuffer *value) {
|
|
185
|
-
|
|
186
|
+
auto arrayBuffer = createNativeBackedArrayBuffer(rt, value->jsiMutableBuffer());
|
|
186
187
|
return jsi::Value{rt, arrayBuffer};
|
|
187
188
|
}
|
|
188
189
|
};
|
|
@@ -2,7 +2,7 @@ package expo.modules.v2
|
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
4
|
import expo.modules.kotlin.logger
|
|
5
|
-
import io.github.expo.modules.v2.
|
|
5
|
+
import io.github.expo.modules.v2.Module
|
|
6
6
|
import io.github.expo.modules.v2.modules.ModuleRegistry
|
|
7
7
|
import io.github.expo.modules.v2.react.ReactRuntime
|
|
8
8
|
import java.lang.reflect.Modifier
|
|
@@ -29,11 +29,11 @@ val isExpoAutolinkingSettingsPluginAvailable = expoAutolinkingSettingsPlugin ==
|
|
|
29
29
|
|
|
30
30
|
dependencies {
|
|
31
31
|
implementation(gradleApi())
|
|
32
|
-
compileOnly("com.android.tools.build:gradle:
|
|
32
|
+
compileOnly("com.android.tools.build:gradle:9.2.1")
|
|
33
33
|
implementation("com.facebook.react:react-native-gradle-plugin")
|
|
34
34
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
|
35
35
|
implementation("io.github.expo.pika:pika-gradle:1.0.0")
|
|
36
|
-
implementation("io.github.expo:expo-modules-v2-gradle-plugin:0.1.
|
|
36
|
+
implementation("io.github.expo:expo-modules-v2-gradle-plugin:0.1.5")
|
|
37
37
|
|
|
38
38
|
if (isExpoAutolinkingSettingsPluginAvailable) {
|
|
39
39
|
implementation("expo.modules:expo-autolinking-plugin-shared")
|
package/expo-module-gradle-plugin/src/main/kotlin/expo/modules/plugin/ProjectConfiguration.kt
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
package expo.modules.plugin
|
|
4
4
|
|
|
5
5
|
import com.android.build.api.dsl.LibraryExtension
|
|
6
|
-
import com.android.build.api.variant.AndroidComponentsExtension
|
|
7
6
|
import expo.modules.plugin.android.PublicationInfo
|
|
8
7
|
import expo.modules.plugin.android.applyLinterOptions
|
|
9
8
|
import expo.modules.plugin.android.applyPublishingVariant
|
|
@@ -132,18 +131,11 @@ internal fun Project.applyPublishing(expoModulesExtension: ExpoModuleExtension)
|
|
|
132
131
|
}
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
private const val AGP_BUILT_IN_KOTLIN_MAJOR = 9
|
|
136
|
-
|
|
137
134
|
/**
|
|
138
135
|
* Whether AGP's built-in Kotlin support is active, meaning the `kotlin-android` plugin must not be
|
|
139
|
-
* applied.
|
|
136
|
+
* applied. AGP 9 enables it by default unless the project explicitly opts out with `android.builtInKotlin=false`.
|
|
140
137
|
*/
|
|
141
138
|
internal fun Project.hasBuiltInKotlinSupport(): Boolean {
|
|
142
|
-
val androidComponents = extensions.findByType(AndroidComponentsExtension::class.java)
|
|
143
|
-
?: return false
|
|
144
|
-
if (androidComponents.pluginVersion.major < AGP_BUILT_IN_KOTLIN_MAJOR) {
|
|
145
|
-
return false
|
|
146
|
-
}
|
|
147
139
|
return findProperty("android.builtInKotlin")?.toString()?.toBoolean() ?: true
|
|
148
140
|
}
|
|
149
141
|
|
|
@@ -16,6 +16,9 @@ public typealias UILabel = NSLabel
|
|
|
16
16
|
public typealias UIImage = NSImage
|
|
17
17
|
public typealias UIImageView = NSImageView
|
|
18
18
|
public typealias UIPasteboard = NSPasteboard
|
|
19
|
+
public typealias UIColor = NSColor
|
|
20
|
+
public typealias UIGestureRecognizer = NSGestureRecognizer
|
|
21
|
+
public typealias UITextContentType = NSTextContentType
|
|
19
22
|
|
|
20
23
|
extension UIApplication {
|
|
21
24
|
public typealias LaunchOptionsKey = String
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "58.0.
|
|
3
|
+
"version": "58.0.2",
|
|
4
4
|
"description": "The core of Expo Modules architecture",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"expo",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@expo/expo-modules-macros-plugin": "0.10.0",
|
|
68
|
-
"expo-modules-jsi": "~58.0.
|
|
68
|
+
"expo-modules-jsi": "~58.0.2",
|
|
69
69
|
"invariant": "^2.2.4"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"optional": true
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "313e67f5c9dfb16f9deca74a271cd1d4f33a6f82",
|
|
88
88
|
"scripts": {
|
|
89
89
|
"build": "expo-build src",
|
|
90
90
|
"precompile-ios": "et prebuild-package-for-publish",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|