@granite-js/screen 1.0.0
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 +7 -0
- package/GraniteScreen.podspec +25 -0
- package/LICENSE +202 -0
- package/android/CMakeLists.txt +62 -0
- package/android/build.gradle +63 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/BundleEvaluator.cpp +27 -0
- package/android/src/main/cpp/BundleEvaluator.h +17 -0
- package/android/src/main/cpp/onLoad.cpp +6 -0
- package/android/src/main/kotlin/run/granite/BundleEvaluator.kt +50 -0
- package/android/src/main/kotlin/run/granite/BundleLoader.kt +40 -0
- package/android/src/main/kotlin/run/granite/DefaultBundleLoader.kt +20 -0
- package/android/src/main/kotlin/run/granite/DefaultErrorView.kt +58 -0
- package/android/src/main/kotlin/run/granite/DefaultLoadingView.kt +51 -0
- package/android/src/main/kotlin/run/granite/GraniteReactDelegate.kt +76 -0
- package/android/src/main/kotlin/run/granite/GraniteReactDelegateImpl.kt +448 -0
- package/android/src/main/kotlin/run/granite/GraniteReactHost.kt +113 -0
- package/android/src/main/kotlin/run/granite/ReactHostFactory.kt +106 -0
- package/gradle-plugin/LICENSE +201 -0
- package/gradle-plugin/README.md +578 -0
- package/gradle-plugin/build.gradle.kts +97 -0
- package/gradle-plugin/gradle/libs.versions.toml +17 -0
- package/gradle-plugin/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle-plugin/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/gradle-plugin/gradle.properties +12 -0
- package/gradle-plugin/gradlew +248 -0
- package/gradle-plugin/gradlew.bat +93 -0
- package/gradle-plugin/settings.gradle.kts +1 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteExtension.kt +225 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GranitePlugin.kt +784 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootExtension.kt +107 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/GraniteRootProjectPlugin.kt +290 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/BuildConfigConfigurator.kt +69 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyConfigurator.kt +232 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DependencyCoordinates.kt +29 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DevServerResourceConfigurator.kt +101 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/JniPackagingConfigurator.kt +160 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/NdkConfigurator.kt +135 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/RepositoryConfigurator.kt +148 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/ResourceConfigurator.kt +56 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CMakeGenerator.kt +105 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/CppAutolinkingGenerator.kt +152 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/generators/EntryPointGenerator.kt +100 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AndroidDependencyConfig.kt +23 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/AutolinkingConfig.kt +89 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/CMakeEntry.kt +47 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/models/NativeModule.kt +177 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AssetPackagingTask.kt +194 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/AutolinkingTask.kt +431 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/BundleTask.kt +275 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenArtifactsTask.kt +218 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/tasks/CodegenSchemaTask.kt +186 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/AutolinkingParser.kt +128 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ConflictDetector.kt +121 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/JdkValidator.kt +73 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/NodeExecutableFinder.kt +43 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/ReactNativeVersionReader.kt +329 -0
- package/gradle-plugin/src/main/kotlin/run/granite/gradle/utils/TaskDependencyValidator.kt +198 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteExtensionTest.kt +191 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GranitePluginTest.kt +156 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/GraniteRootProjectPluginTest.kt +87 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/BuildConfigConfiguratorTest.kt +115 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DependencyConfiguratorTest.kt +338 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/DevServerResourceConfiguratorTest.kt +205 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/config/ResourceConfiguratorTest.kt +131 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/fixtures/NativeModuleFixtures.kt +67 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CMakeGeneratorTest.kt +71 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/CppAutolinkingGeneratorTest.kt +344 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/generators/EntryPointGeneratorTest.kt +40 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/AutolinkingConfigTest.kt +350 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/CMakeEntryTest.kt +200 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/models/NativeModuleTest.kt +562 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AssetPackagingTaskTest.kt +318 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/AutolinkingTaskTest.kt +89 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/BundleTaskTest.kt +68 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/tasks/CodegenTasksTest.kt +410 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/AutolinkingParserTest.kt +335 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ConflictDetectorTest.kt +75 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/JdkValidatorTest.kt +88 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/ReactNativeVersionReaderTest.kt +585 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskDependencyValidatorTest.kt +123 -0
- package/gradle-plugin/src/test/kotlin/run/granite/gradle/utils/TaskTestUtils.kt +88 -0
- package/gradle-plugin/src/test/resources/fixtures/sample-rn-config.json +45 -0
- package/ios/BundleLoader/BundleEvaluator.h +16 -0
- package/ios/BundleLoader/BundleEvaluator.mm +76 -0
- package/ios/BundleLoader/BundleLoadable.swift +91 -0
- package/ios/GraniteBundleLoaderTypes.swift +7 -0
- package/ios/GraniteScreen.h +12 -0
- package/ios/ReactNativeHosting/DefaultViews.swift +138 -0
- package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.h +24 -0
- package/ios/ReactNativeHosting/GraniteDefaultModuleProvider.mm +22 -0
- package/ios/ReactNativeHosting/GraniteHostingHelper.swift +103 -0
- package/ios/ReactNativeHosting/GraniteNativeFactory.swift +35 -0
- package/ios/ReactNativeHosting/GraniteNativeFactoryDelegateImpl.swift +30 -0
- package/ios/ReactNativeHosting/GraniteNativeFactoryImpl.swift +24 -0
- package/ios/ReactNativeHosting/GraniteReactHost.swift +39 -0
- package/ios/ReactNativeHosting/GraniteScreen-Bridging-Header.h +12 -0
- package/package.json +59 -0
- package/react-native.config.js +8 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dependency coordinate information for React Native and Hermes.
|
|
5
|
+
*
|
|
6
|
+
* @property reactVersion React Native version (e.g., "0.84.0-rc.4")
|
|
7
|
+
* @property hermesVersion Hermes Classic version (e.g., "0.15.0")
|
|
8
|
+
* @property hermesV1Version Hermes V1 version (e.g., "250829098.0.6")
|
|
9
|
+
* @property reactGroup React Native Maven group (default: com.facebook.react)
|
|
10
|
+
* @property hermesGroup Hermes Maven group (default: com.facebook.hermes)
|
|
11
|
+
*/
|
|
12
|
+
data class DependencyCoordinates(
|
|
13
|
+
val reactVersion: String,
|
|
14
|
+
val hermesVersion: String,
|
|
15
|
+
val hermesV1Version: String,
|
|
16
|
+
val reactGroup: String = DEFAULT_REACT_GROUP,
|
|
17
|
+
val hermesGroup: String = DEFAULT_HERMES_GROUP,
|
|
18
|
+
) {
|
|
19
|
+
companion object {
|
|
20
|
+
const val DEFAULT_REACT_GROUP = "com.facebook.react"
|
|
21
|
+
const val DEFAULT_HERMES_GROUP = "com.facebook.hermes"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns the Hermes version to use.
|
|
26
|
+
* Granite always uses Hermes V1.
|
|
27
|
+
*/
|
|
28
|
+
fun getEffectiveHermesVersion(): String = if (hermesV1Version.isNotBlank()) hermesV1Version else hermesVersion
|
|
29
|
+
}
|
package/gradle-plugin/src/main/kotlin/run/granite/gradle/config/DevServerResourceConfigurator.kt
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
import org.gradle.api.Project
|
|
4
|
+
import run.granite.gradle.GraniteExtension
|
|
5
|
+
import java.io.File
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configurator for development server resource generation.
|
|
9
|
+
*
|
|
10
|
+
* Generates Android resources for React Native development server configuration:
|
|
11
|
+
* - res/values/strings.xml: Dev server host
|
|
12
|
+
* - res/values/integers.xml: Dev server port
|
|
13
|
+
*
|
|
14
|
+
* These resources are used by React Native's DevSupportManager for connecting
|
|
15
|
+
* to the Metro bundler during development.
|
|
16
|
+
*/
|
|
17
|
+
class DevServerResourceConfigurator(
|
|
18
|
+
private val project: Project,
|
|
19
|
+
private val extension: GraniteExtension,
|
|
20
|
+
) {
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Generates dev server configuration resources for debug builds.
|
|
24
|
+
*
|
|
25
|
+
* Creates:
|
|
26
|
+
* - src/debug/res/values/strings.xml with react_native_dev_server_host
|
|
27
|
+
* - src/debug/res/values/integers.xml with react_native_dev_server_port
|
|
28
|
+
*
|
|
29
|
+
* Only generates resources if devServerHost and devServerPort are configured.
|
|
30
|
+
*/
|
|
31
|
+
fun configure() {
|
|
32
|
+
val devServerHost = extension.devServerHost.orNull
|
|
33
|
+
val devServerPort = extension.devServerPort.orNull
|
|
34
|
+
|
|
35
|
+
if (devServerHost == null && devServerPort == null) {
|
|
36
|
+
project.logger.debug("Dev server configuration not set, skipping resource generation")
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Generate debug variant resources
|
|
41
|
+
val debugResDir = project.file("src/debug/res/values")
|
|
42
|
+
debugResDir.mkdirs()
|
|
43
|
+
|
|
44
|
+
if (devServerHost != null) {
|
|
45
|
+
generateStringsXml(debugResDir, devServerHost)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (devServerPort != null) {
|
|
49
|
+
generateIntegersXml(debugResDir, devServerPort)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
project.logger.lifecycle(
|
|
53
|
+
"Dev server resources generated: host=${devServerHost ?: "default"}, port=${devServerPort ?: "default"}",
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generates strings.xml with dev server host configuration.
|
|
59
|
+
*/
|
|
60
|
+
private fun generateStringsXml(resDir: File, host: String) {
|
|
61
|
+
val stringsFile = File(resDir, "strings.xml")
|
|
62
|
+
|
|
63
|
+
val content = """
|
|
64
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
65
|
+
<resources>
|
|
66
|
+
<!-- React Native Metro bundler development server host -->
|
|
67
|
+
<string name="react_native_dev_server_host" translatable="false">${escapeXml(host)}</string>
|
|
68
|
+
</resources>
|
|
69
|
+
""".trimIndent()
|
|
70
|
+
|
|
71
|
+
stringsFile.writeText(content)
|
|
72
|
+
project.logger.debug("Generated ${stringsFile.absolutePath}")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Escapes special XML characters to prevent injection vulnerabilities.
|
|
77
|
+
*/
|
|
78
|
+
private fun escapeXml(value: String): String = value.replace("&", "&")
|
|
79
|
+
.replace("<", "<")
|
|
80
|
+
.replace(">", ">")
|
|
81
|
+
.replace("\"", """)
|
|
82
|
+
.replace("'", "'")
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generates integers.xml with dev server port configuration.
|
|
86
|
+
*/
|
|
87
|
+
private fun generateIntegersXml(resDir: File, port: Int) {
|
|
88
|
+
val integersFile = File(resDir, "integers.xml")
|
|
89
|
+
|
|
90
|
+
val content = """
|
|
91
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
92
|
+
<resources>
|
|
93
|
+
<!-- React Native Metro bundler development server port -->
|
|
94
|
+
<integer name="react_native_dev_server_port">$port</integer>
|
|
95
|
+
</resources>
|
|
96
|
+
""".trimIndent()
|
|
97
|
+
|
|
98
|
+
integersFile.writeText(content)
|
|
99
|
+
project.logger.debug("Generated ${integersFile.absolutePath}")
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
import com.android.build.api.variant.LibraryAndroidComponentsExtension
|
|
4
|
+
import com.android.build.api.variant.Variant
|
|
5
|
+
import org.gradle.api.Project
|
|
6
|
+
import run.granite.gradle.GraniteExtension
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configures JNI library packaging options for React Native native modules.
|
|
10
|
+
*
|
|
11
|
+
* This configurator sets up pickFirst rules to handle duplicate .so files
|
|
12
|
+
* from various React Native dependencies. Based on React Native Gradle Plugin's
|
|
13
|
+
* NdkConfiguratorUtils.configureJsEnginePackagingOptions and configureNewArchPackagingOptions.
|
|
14
|
+
*
|
|
15
|
+
* Key responsibilities:
|
|
16
|
+
* - Configure Hermes .so file packaging (libhermes.so, libhermestooling.so)
|
|
17
|
+
* - Configure fbjni and c++_shared library packaging
|
|
18
|
+
* - Configure React Native core library packaging (libreactnative.so, libjsi.so)
|
|
19
|
+
* - Exclude JSC libraries when Hermes is enabled
|
|
20
|
+
*
|
|
21
|
+
* Without proper packaging configuration, duplicate .so files from multiple AARs
|
|
22
|
+
* will cause build failures or runtime crashes.
|
|
23
|
+
*/
|
|
24
|
+
class JniPackagingConfigurator(
|
|
25
|
+
private val project: Project,
|
|
26
|
+
private val extension: GraniteExtension,
|
|
27
|
+
) {
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configures JNI packaging options for all build variants.
|
|
31
|
+
*
|
|
32
|
+
* Must be called during plugin configuration (typically in apply() or afterEvaluate).
|
|
33
|
+
*/
|
|
34
|
+
fun configure(androidComponents: LibraryAndroidComponentsExtension) {
|
|
35
|
+
project.logger.lifecycle("Configuring JNI packaging options for React Native")
|
|
36
|
+
|
|
37
|
+
// Configure packaging for each variant
|
|
38
|
+
androidComponents.onVariants { variant ->
|
|
39
|
+
val hermesEnabled = true // Hermes is always enabled
|
|
40
|
+
val newArchEnabled = true // New Architecture is mandatory
|
|
41
|
+
|
|
42
|
+
project.logger.debug(
|
|
43
|
+
"Configuring packaging for variant ${variant.name}: " +
|
|
44
|
+
"hermesEnabled=$hermesEnabled (mandatory), newArchEnabled=$newArchEnabled (mandatory)",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
// Configure JS engine packaging (Hermes only)
|
|
48
|
+
configureJsEnginePackaging(variant, hermesEnabled)
|
|
49
|
+
|
|
50
|
+
// Configure New Architecture packaging
|
|
51
|
+
configureNewArchPackaging(variant, newArchEnabled)
|
|
52
|
+
|
|
53
|
+
project.logger.debug("Packaging configuration complete for variant ${variant.name}")
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Configures JavaScript engine .so file packaging.
|
|
59
|
+
*
|
|
60
|
+
* Based on NdkConfiguratorUtils.configureJsEnginePackagingOptions.
|
|
61
|
+
*
|
|
62
|
+
* When Hermes is enabled:
|
|
63
|
+
* - Exclude JSC libraries (libjsc.so, libjsctooling.so)
|
|
64
|
+
* - Include Hermes libraries (libhermes.so, libhermestooling.so) as pickFirst
|
|
65
|
+
*
|
|
66
|
+
* When JSC is enabled:
|
|
67
|
+
* - Exclude Hermes libraries
|
|
68
|
+
* - Include JSC libraries as pickFirst
|
|
69
|
+
*
|
|
70
|
+
* @param variant The build variant being configured
|
|
71
|
+
* @param hermesEnabled Whether Hermes is enabled for this build
|
|
72
|
+
*/
|
|
73
|
+
private fun configureJsEnginePackaging(
|
|
74
|
+
variant: com.android.build.api.variant.Variant,
|
|
75
|
+
hermesEnabled: Boolean,
|
|
76
|
+
) {
|
|
77
|
+
if (hermesEnabled) {
|
|
78
|
+
// Hermes enabled: exclude JSC, include Hermes
|
|
79
|
+
variant.packaging.jniLibs.excludes.addAll(
|
|
80
|
+
listOf(
|
|
81
|
+
"**/libjsc.so",
|
|
82
|
+
"**/libjsctooling.so",
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
variant.packaging.jniLibs.pickFirsts.addAll(
|
|
86
|
+
listOf(
|
|
87
|
+
"**/libhermes.so",
|
|
88
|
+
"**/libhermestooling.so",
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
project.logger.debug("Configured Hermes packaging: excluding JSC, including Hermes")
|
|
93
|
+
} else {
|
|
94
|
+
// JSC enabled (not supported by Granite, but keep for compatibility)
|
|
95
|
+
variant.packaging.jniLibs.excludes.addAll(
|
|
96
|
+
listOf(
|
|
97
|
+
"**/libhermes.so",
|
|
98
|
+
"**/libhermestooling.so",
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
variant.packaging.jniLibs.pickFirsts.addAll(
|
|
102
|
+
listOf(
|
|
103
|
+
"**/libjsc.so",
|
|
104
|
+
"**/libjsctooling.so",
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
project.logger.warn(
|
|
109
|
+
"JSC packaging configured, but Granite plugin only supports Hermes. " +
|
|
110
|
+
"This configuration may cause runtime errors.",
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Configures New Architecture .so file packaging.
|
|
117
|
+
*
|
|
118
|
+
* Based on NdkConfiguratorUtils.configureNewArchPackagingOptions.
|
|
119
|
+
*
|
|
120
|
+
* For Old Architecture:
|
|
121
|
+
* - pickFirst for fbjni and c++_shared (commonly duplicated)
|
|
122
|
+
*
|
|
123
|
+
* For New Architecture:
|
|
124
|
+
* - pickFirst for fbjni, c++_shared, libreactnative.so, libjsi.so
|
|
125
|
+
*
|
|
126
|
+
* @param variant The build variant being configured
|
|
127
|
+
* @param newArchEnabled Whether New Architecture is enabled
|
|
128
|
+
*/
|
|
129
|
+
private fun configureNewArchPackaging(
|
|
130
|
+
variant: com.android.build.api.variant.Variant,
|
|
131
|
+
newArchEnabled: Boolean,
|
|
132
|
+
) {
|
|
133
|
+
if (!newArchEnabled) {
|
|
134
|
+
// Old Architecture: basic pickFirst for common duplicates
|
|
135
|
+
variant.packaging.jniLibs.pickFirsts.addAll(
|
|
136
|
+
listOf(
|
|
137
|
+
"**/libfbjni.so",
|
|
138
|
+
"**/libc++_shared.so",
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
project.logger.debug("Configured Old Architecture packaging: basic pickFirsts")
|
|
143
|
+
} else {
|
|
144
|
+
// New Architecture: additional React Native core libraries
|
|
145
|
+
variant.packaging.jniLibs.pickFirsts.addAll(
|
|
146
|
+
listOf(
|
|
147
|
+
// FBJNI provided via prefab
|
|
148
|
+
"**/libfbjni.so",
|
|
149
|
+
// React Native prefab libraries
|
|
150
|
+
"**/libreactnative.so",
|
|
151
|
+
"**/libjsi.so",
|
|
152
|
+
// C++ standard library
|
|
153
|
+
"**/libc++_shared.so",
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
project.logger.debug("Configured New Architecture packaging: extended pickFirsts")
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
import com.android.build.gradle.LibraryExtension
|
|
4
|
+
import org.gradle.api.Project
|
|
5
|
+
import run.granite.gradle.GraniteExtension
|
|
6
|
+
import java.io.File
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configures Android NDK and CMake build settings for React Native native modules.
|
|
10
|
+
*
|
|
11
|
+
* This configurator:
|
|
12
|
+
* - Enables Prefab for consuming native library dependencies
|
|
13
|
+
* - Configures externalNativeBuild with CMake
|
|
14
|
+
* - Configures Android ABIs
|
|
15
|
+
* - Sets up CMake arguments for React Native
|
|
16
|
+
*
|
|
17
|
+
* Native libraries (.so files) are built by CMake and packaged in the AAR.
|
|
18
|
+
* App modules can consume these libraries via Prefab without additional configuration.
|
|
19
|
+
*/
|
|
20
|
+
class NdkConfigurator(
|
|
21
|
+
private val project: Project,
|
|
22
|
+
private val extension: GraniteExtension,
|
|
23
|
+
private val androidExtension: LibraryExtension,
|
|
24
|
+
) {
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Configures all NDK and CMake settings.
|
|
28
|
+
*/
|
|
29
|
+
fun configure() {
|
|
30
|
+
project.logger.lifecycle("Configuring NDK and CMake for React Native native modules")
|
|
31
|
+
|
|
32
|
+
// Enable Prefab
|
|
33
|
+
configurePrefab()
|
|
34
|
+
|
|
35
|
+
// Configure externalNativeBuild with CMake
|
|
36
|
+
configureCMake()
|
|
37
|
+
|
|
38
|
+
// Configure ABIs
|
|
39
|
+
configureAbis()
|
|
40
|
+
|
|
41
|
+
project.logger.lifecycle("NDK configuration complete")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Enables Prefab for packaging native libraries.
|
|
46
|
+
*
|
|
47
|
+
* Prefab allows the AAR to export native libraries (.so files) that can be
|
|
48
|
+
* consumed by app modules without manual configuration.
|
|
49
|
+
*
|
|
50
|
+
* Implementation details.
|
|
51
|
+
*/
|
|
52
|
+
private fun configurePrefab() {
|
|
53
|
+
androidExtension.buildFeatures.prefab = true
|
|
54
|
+
|
|
55
|
+
project.logger.debug("Prefab enabled for native library packaging")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Configures externalNativeBuild with CMake.
|
|
60
|
+
*
|
|
61
|
+
* Sets up CMake configuration for building React Native and custom native modules.
|
|
62
|
+
*
|
|
63
|
+
* Implementation details.
|
|
64
|
+
*/
|
|
65
|
+
private fun configureCMake() {
|
|
66
|
+
val cmakeListsFile = project.file("CMakeLists.txt")
|
|
67
|
+
|
|
68
|
+
if (!cmakeListsFile.exists()) {
|
|
69
|
+
project.logger.warn(
|
|
70
|
+
"""
|
|
71
|
+
|CMakeLists.txt not found: ${cmakeListsFile.absolutePath}
|
|
72
|
+
|
|
|
73
|
+
|Native module builds will be skipped. If your library has C++ native modules,
|
|
74
|
+
|create a CMakeLists.txt file at the project root.
|
|
75
|
+
""".trimMargin(),
|
|
76
|
+
)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
androidExtension.externalNativeBuild.cmake {
|
|
81
|
+
path = cmakeListsFile
|
|
82
|
+
|
|
83
|
+
// CMake version (compatible with React Native 0.81+)
|
|
84
|
+
version = "3.22.1"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
androidExtension.defaultConfig.externalNativeBuild.cmake {
|
|
88
|
+
// Add CMake arguments for React Native
|
|
89
|
+
val codegenJniDir = project.layout.buildDirectory
|
|
90
|
+
.dir("generated/codegen/jni")
|
|
91
|
+
.get().asFile.absolutePath
|
|
92
|
+
|
|
93
|
+
val reactNativeDir = extension.getReactNativeDirResolved().absolutePath
|
|
94
|
+
val nodeModulesDir = extension.getNodeModulesDirResolved().absolutePath
|
|
95
|
+
// For RN 0.71-0.75, use ReactAndroid; for RN 0.76+, use android
|
|
96
|
+
val reactAndroidDir = "$reactNativeDir/ReactAndroid"
|
|
97
|
+
val projectBuildDir = project.layout.buildDirectory.get().asFile.absolutePath
|
|
98
|
+
val projectRootDir = project.rootDir.absolutePath
|
|
99
|
+
|
|
100
|
+
arguments(
|
|
101
|
+
"-DREACT_NATIVE_DIR=$reactNativeDir",
|
|
102
|
+
"-DREACT_ANDROID_DIR=$reactAndroidDir",
|
|
103
|
+
"-DNODE_MODULES_DIR=$nodeModulesDir",
|
|
104
|
+
"-DCODEGEN_JNI_DIR=$codegenJniDir",
|
|
105
|
+
"-DPROJECT_BUILD_DIR=$projectBuildDir",
|
|
106
|
+
"-DPROJECT_ROOT_DIR=$projectRootDir",
|
|
107
|
+
"-DANDROID_STL=c++_shared",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
project.logger.debug("CMake configured with React Native arguments")
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Configures Android ABIs (architectures) to build.
|
|
116
|
+
*
|
|
117
|
+
* By default, builds for all ABIs. Users can customize via GraniteExtension.
|
|
118
|
+
*
|
|
119
|
+
* Implementation details.
|
|
120
|
+
*/
|
|
121
|
+
private fun configureAbis() {
|
|
122
|
+
val abis = extension.nativeArchitectures.get()
|
|
123
|
+
|
|
124
|
+
if (abis.isEmpty()) {
|
|
125
|
+
project.logger.warn("No ABIs configured - using default (all ABIs)")
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
androidExtension.defaultConfig.ndk {
|
|
130
|
+
abiFilters.addAll(abis)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
project.logger.lifecycle("Configured ABIs: ${abis.joinToString(", ")}")
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
import org.gradle.api.Project
|
|
4
|
+
import run.granite.gradle.GraniteExtension
|
|
5
|
+
import java.io.File
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configures Maven repositories for React Native dependencies.
|
|
9
|
+
*
|
|
10
|
+
* This configurator automatically adds required repositories:
|
|
11
|
+
* - Maven Central (for React Native, Hermes, SoLoader)
|
|
12
|
+
* - Google Maven (for Android dependencies)
|
|
13
|
+
* - React Native Maven repository (node_modules/react-native/android)
|
|
14
|
+
* - JSC/Hermes Maven repository (for JavaScript engines)
|
|
15
|
+
* - Project-specific repositories (if configured)
|
|
16
|
+
*/
|
|
17
|
+
class RepositoryConfigurator(
|
|
18
|
+
private val project: Project,
|
|
19
|
+
private val extension: GraniteExtension,
|
|
20
|
+
) {
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Configures all required Maven repositories.
|
|
24
|
+
*
|
|
25
|
+
* Called during plugin application to ensure repositories are available
|
|
26
|
+
* before dependency resolution.
|
|
27
|
+
*
|
|
28
|
+
* Adds repositories to all projects (including root) so that third-party modules
|
|
29
|
+
* can resolve react-native dependencies.
|
|
30
|
+
*/
|
|
31
|
+
fun configure() {
|
|
32
|
+
project.logger.lifecycle("Configuring Maven repositories for React Native")
|
|
33
|
+
|
|
34
|
+
val reactNativeDir = extension.getReactNativeDirResolved()
|
|
35
|
+
val nodeModulesDir = extension.getNodeModulesDirResolved()
|
|
36
|
+
|
|
37
|
+
// Add repositories to all projects (official RN plugin approach)
|
|
38
|
+
project.rootProject.allprojects.forEach { eachProject ->
|
|
39
|
+
// Add standard repositories
|
|
40
|
+
addStandardRepositories(eachProject)
|
|
41
|
+
|
|
42
|
+
// Add React Native Maven repository
|
|
43
|
+
addReactNativeMavenRepository(eachProject, reactNativeDir)
|
|
44
|
+
|
|
45
|
+
// Add Android Maven repository from node_modules
|
|
46
|
+
addAndroidMavenRepository(eachProject, nodeModulesDir)
|
|
47
|
+
|
|
48
|
+
// Add JSC Maven repository (even though we use Hermes, some RN components may reference it)
|
|
49
|
+
addJscMavenRepository(eachProject, nodeModulesDir)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Adds standard Maven repositories (Google, Maven Central).
|
|
55
|
+
*/
|
|
56
|
+
private fun addStandardRepositories(targetProject: Project) {
|
|
57
|
+
targetProject.repositories.apply {
|
|
58
|
+
// Google Maven for Android dependencies
|
|
59
|
+
if (findByName("Google") == null) {
|
|
60
|
+
google()
|
|
61
|
+
project.logger.debug("[${targetProject.name}] Added repository: Google Maven")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Maven Central for React Native and Hermes
|
|
65
|
+
if (findByName("MavenRepo") == null) {
|
|
66
|
+
mavenCentral()
|
|
67
|
+
project.logger.debug("[${targetProject.name}] Added repository: Maven Central")
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Adds React Native Maven repository from node_modules.
|
|
74
|
+
*
|
|
75
|
+
* This repository contains React Native Android artifacts.
|
|
76
|
+
*/
|
|
77
|
+
private fun addReactNativeMavenRepository(targetProject: Project, reactNativeDir: File) {
|
|
78
|
+
val reactNativeAndroidDir = reactNativeDir.resolve("ReactAndroid")
|
|
79
|
+
|
|
80
|
+
if (!reactNativeAndroidDir.exists()) {
|
|
81
|
+
project.logger.warn(
|
|
82
|
+
"React Native Android directory not found: ${reactNativeAndroidDir.absolutePath}",
|
|
83
|
+
)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Prevent duplicate additions
|
|
88
|
+
if (targetProject.repositories.findByName("ReactNative") != null) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
targetProject.repositories.maven {
|
|
93
|
+
name = "ReactNative"
|
|
94
|
+
setUrl(reactNativeAndroidDir)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
project.logger.debug("[${targetProject.name}] Added repository: React Native Maven (${reactNativeAndroidDir.absolutePath})")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Adds Android Maven repository from node_modules.
|
|
102
|
+
*
|
|
103
|
+
* This repository contains prebuilt React Native Android artifacts.
|
|
104
|
+
*/
|
|
105
|
+
private fun addAndroidMavenRepository(targetProject: Project, nodeModulesDir: File) {
|
|
106
|
+
val androidMavenDir = nodeModulesDir.resolve("react-native/android")
|
|
107
|
+
|
|
108
|
+
if (androidMavenDir.exists()) {
|
|
109
|
+
// Prevent duplicate additions
|
|
110
|
+
if (targetProject.repositories.findByName("ReactNativeAndroid") != null) {
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
targetProject.repositories.maven {
|
|
115
|
+
name = "ReactNativeAndroid"
|
|
116
|
+
setUrl(androidMavenDir)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
project.logger.debug("[${targetProject.name}] Added repository: React Native Android Maven (${androidMavenDir.absolutePath})")
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Adds JSC Maven repository from node_modules.
|
|
125
|
+
*
|
|
126
|
+
* Even though Hermes is required, some React Native components may reference JSC.
|
|
127
|
+
*/
|
|
128
|
+
private fun addJscMavenRepository(targetProject: Project, nodeModulesDir: File) {
|
|
129
|
+
val jscAndroidDir = nodeModulesDir.resolve("jsc-android/dist")
|
|
130
|
+
|
|
131
|
+
if (jscAndroidDir.exists()) {
|
|
132
|
+
// Prevent duplicate additions
|
|
133
|
+
if (targetProject.repositories.findByName("JSC") != null) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
targetProject.repositories.maven {
|
|
138
|
+
name = "JSC"
|
|
139
|
+
setUrl(jscAndroidDir)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
project.logger.debug("[${targetProject.name}] Added repository: JSC Maven (${jscAndroidDir.absolutePath})")
|
|
143
|
+
} else {
|
|
144
|
+
// JSC may not be installed if using Hermes exclusively
|
|
145
|
+
project.logger.debug("[${targetProject.name}] JSC Maven repository not found (not required for Hermes builds)")
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
package run.granite.gradle.config
|
|
2
|
+
|
|
3
|
+
import com.android.build.gradle.LibraryExtension
|
|
4
|
+
import org.gradle.api.Project
|
|
5
|
+
import run.granite.gradle.GraniteExtension
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configurator for Android resource packaging options.
|
|
9
|
+
*
|
|
10
|
+
* Configures:
|
|
11
|
+
* - Bundle compression settings (aaptOptions.noCompress)
|
|
12
|
+
* - Resource merging behavior
|
|
13
|
+
* - Asset packaging optimizations
|
|
14
|
+
*/
|
|
15
|
+
class ResourceConfigurator(
|
|
16
|
+
private val project: Project,
|
|
17
|
+
private val extension: GraniteExtension,
|
|
18
|
+
) {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Configures Android resource packaging options.
|
|
22
|
+
*
|
|
23
|
+
* Sets up:
|
|
24
|
+
* 1. Bundle compression control based on extension settings
|
|
25
|
+
* 2. No-compress patterns for JavaScript bundles
|
|
26
|
+
*/
|
|
27
|
+
fun configure(androidExtension: LibraryExtension) {
|
|
28
|
+
configureAaptOptions(androidExtension)
|
|
29
|
+
project.logger.debug("Resource packaging configured")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Configures AAPT options for bundle compression.
|
|
34
|
+
*
|
|
35
|
+
* If bundle compression is disabled, adds bundle file patterns to noCompress list
|
|
36
|
+
* to prevent APK/AAR packaging from compressing already-compressed Hermes bytecode.
|
|
37
|
+
*/
|
|
38
|
+
private fun configureAaptOptions(androidExtension: LibraryExtension) {
|
|
39
|
+
androidExtension.packaging {
|
|
40
|
+
resources {
|
|
41
|
+
// Don't compress JavaScript bundles if they're already gzip compressed
|
|
42
|
+
if (!extension.bundleCompressionEnabled.get()) {
|
|
43
|
+
excludes.add("**/*.bundle")
|
|
44
|
+
excludes.add("**/*.hbc")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Always exclude .map files from compression
|
|
48
|
+
excludes.add("**/*.map")
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
project.logger.lifecycle(
|
|
53
|
+
"Bundle compression: ${if (extension.bundleCompressionEnabled.get()) "enabled" else "disabled"}",
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|