@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,784 @@
|
|
|
1
|
+
package run.granite.gradle
|
|
2
|
+
|
|
3
|
+
import com.android.build.api.variant.LibraryAndroidComponentsExtension
|
|
4
|
+
import com.android.build.gradle.LibraryExtension
|
|
5
|
+
import org.gradle.api.Action
|
|
6
|
+
import org.gradle.api.JavaVersion
|
|
7
|
+
import org.gradle.api.Plugin
|
|
8
|
+
import org.gradle.api.Project
|
|
9
|
+
import org.gradle.api.tasks.TaskProvider
|
|
10
|
+
import run.granite.gradle.config.BuildConfigConfigurator
|
|
11
|
+
import run.granite.gradle.config.DependencyConfigurator
|
|
12
|
+
import run.granite.gradle.config.DevServerResourceConfigurator
|
|
13
|
+
import run.granite.gradle.config.JniPackagingConfigurator
|
|
14
|
+
import run.granite.gradle.config.NdkConfigurator
|
|
15
|
+
import run.granite.gradle.config.RepositoryConfigurator
|
|
16
|
+
import run.granite.gradle.config.ResourceConfigurator
|
|
17
|
+
import run.granite.gradle.tasks.AssetPackagingTask
|
|
18
|
+
import run.granite.gradle.tasks.AutolinkingTask
|
|
19
|
+
import run.granite.gradle.tasks.BundleTask
|
|
20
|
+
import run.granite.gradle.tasks.CodegenArtifactsTask
|
|
21
|
+
import run.granite.gradle.tasks.CodegenSchemaTask
|
|
22
|
+
import run.granite.gradle.utils.AutolinkingParser
|
|
23
|
+
import run.granite.gradle.utils.ConflictDetector
|
|
24
|
+
import run.granite.gradle.utils.JdkValidator
|
|
25
|
+
import run.granite.gradle.utils.NodeExecutableFinder
|
|
26
|
+
import java.io.ByteArrayOutputStream
|
|
27
|
+
import java.io.File
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Granite Gradle Plugin
|
|
31
|
+
*
|
|
32
|
+
* Enables packaging React Native functionality within Android library modules (AAR files).
|
|
33
|
+
*
|
|
34
|
+
* This plugin automates:
|
|
35
|
+
* - Codegen for TurboModules and Fabric components
|
|
36
|
+
* - Autolinking for native module discovery
|
|
37
|
+
* - Native compilation (CMake/NDK) with Prefab packaging
|
|
38
|
+
* - JavaScript bundling and Hermes bytecode compilation
|
|
39
|
+
*
|
|
40
|
+
* The plugin only applies to Android library modules (not application modules) and
|
|
41
|
+
* enforces that only one library module per dependency tree can use the plugin.
|
|
42
|
+
*
|
|
43
|
+
* Requirements:
|
|
44
|
+
* - JDK 17+
|
|
45
|
+
* - Gradle 8.x+
|
|
46
|
+
* - Android Gradle Plugin 7.x+
|
|
47
|
+
* - React Native 0.81.x+
|
|
48
|
+
* - Hermes JavaScript engine (always enabled, JSC not supported)
|
|
49
|
+
*
|
|
50
|
+
* @see GraniteExtension for configuration options
|
|
51
|
+
*/
|
|
52
|
+
class GranitePlugin : Plugin<Project> {
|
|
53
|
+
|
|
54
|
+
companion object {
|
|
55
|
+
const val PLUGIN_ID = "run.granite.library"
|
|
56
|
+
const val EXTENSION_NAME = "granite"
|
|
57
|
+
const val PLUGIN_GROUP = "granite"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
override fun apply(project: Project) {
|
|
61
|
+
// Validate JDK version
|
|
62
|
+
JdkValidator.validate(project)
|
|
63
|
+
|
|
64
|
+
// Detect plugin conflicts
|
|
65
|
+
ConflictDetector.validateNoConflicts(project)
|
|
66
|
+
|
|
67
|
+
// Validate this is a library module
|
|
68
|
+
validateLibraryModule(project)
|
|
69
|
+
|
|
70
|
+
// Create the granite {} extension
|
|
71
|
+
val extension = project.extensions.create(
|
|
72
|
+
EXTENSION_NAME,
|
|
73
|
+
GraniteExtension::class.java,
|
|
74
|
+
project,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
// Apply Android Library plugin if not already applied
|
|
78
|
+
project.pluginManager.apply("com.android.library")
|
|
79
|
+
|
|
80
|
+
// Get Android extension for early configuration
|
|
81
|
+
val androidExtension = project.extensions.getByType(LibraryExtension::class.java)
|
|
82
|
+
|
|
83
|
+
// Configure JDK toolchain (Java 17)
|
|
84
|
+
configureJdkToolchain(project, androidExtension)
|
|
85
|
+
|
|
86
|
+
// Configure Java 17 for node_modules projects early (before AGP finalization)
|
|
87
|
+
// Ensure autolinked modules are compiled with Java 17
|
|
88
|
+
configureNodeModulesJavaVersion(project)
|
|
89
|
+
|
|
90
|
+
// Configure NDK/CMake settings early
|
|
91
|
+
// This must be done before afterEvaluate to avoid "too late to set path" error
|
|
92
|
+
val ndkConfigurator = NdkConfigurator(project, extension, androidExtension)
|
|
93
|
+
ndkConfigurator.configure()
|
|
94
|
+
|
|
95
|
+
// Configure resource packaging early
|
|
96
|
+
// This must be done before afterEvaluate to avoid "too late to modify excludes" error
|
|
97
|
+
val resourceConfigurator = ResourceConfigurator(project, extension)
|
|
98
|
+
resourceConfigurator.configure(androidExtension)
|
|
99
|
+
|
|
100
|
+
// Get Android Components Extension for variant-aware configuration
|
|
101
|
+
val androidComponents = project.extensions.getByType(LibraryAndroidComponentsExtension::class.java)
|
|
102
|
+
|
|
103
|
+
// Configure JNI packaging options early (CRITICAL for Hermes)
|
|
104
|
+
// This must be done before variant processing to avoid duplicate .so file conflicts
|
|
105
|
+
val jniPackagingConfigurator = JniPackagingConfigurator(project, extension)
|
|
106
|
+
jniPackagingConfigurator.configure(androidComponents)
|
|
107
|
+
|
|
108
|
+
// CRITICAL: Register generated sources at DSL and variant level BEFORE afterEvaluate
|
|
109
|
+
// This must happen early so finalizeDsl and onVariants callbacks can be registered
|
|
110
|
+
// before the variant processing phase begins
|
|
111
|
+
registerGeneratedSourcesInDsl(project, androidComponents)
|
|
112
|
+
registerGeneratedSourcesInVariants(project, androidComponents)
|
|
113
|
+
|
|
114
|
+
// Register variant-aware tasks BEFORE afterEvaluate
|
|
115
|
+
// This must be done early to avoid "too late to add actions" error
|
|
116
|
+
registerBundleAndAssetTasksEarly(project, extension, androidComponents)
|
|
117
|
+
|
|
118
|
+
// Configure after Android plugin is evaluated
|
|
119
|
+
project.afterEvaluate {
|
|
120
|
+
configurePlugin(project, extension, androidComponents)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private fun validateLibraryModule(project: Project) {
|
|
125
|
+
project.pluginManager.withPlugin("com.android.application") {
|
|
126
|
+
error(
|
|
127
|
+
"""
|
|
128
|
+
|Granite plugin can only be applied to Android library modules, not application modules.
|
|
129
|
+
|
|
|
130
|
+
|The plugin was applied to '${project.path}', which is an Android application module.
|
|
131
|
+
|
|
|
132
|
+
|Solution: Remove the 'run.granite.library' plugin from application modules.
|
|
133
|
+
|Library modules using React Native should apply this plugin, and app modules
|
|
134
|
+
|should simply add the library as a dependency.
|
|
135
|
+
|
|
|
136
|
+
|Example (library module build.gradle):
|
|
137
|
+
| plugins {
|
|
138
|
+
| id("com.android.library")
|
|
139
|
+
| id("run.granite.library")
|
|
140
|
+
| }
|
|
141
|
+
|
|
|
142
|
+
|Example (app module build.gradle):
|
|
143
|
+
| dependencies {
|
|
144
|
+
| implementation(project(":your-library-module"))
|
|
145
|
+
| }
|
|
146
|
+
""".trimMargin(),
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Configures Java/Kotlin toolchain to Java 17.
|
|
153
|
+
*
|
|
154
|
+
* Equivalent to @react-native/gradle-plugin's JdkConfiguratorUtils.configureJavaToolChains().
|
|
155
|
+
*/
|
|
156
|
+
private fun configureJdkToolchain(project: Project, androidExtension: LibraryExtension) {
|
|
157
|
+
// Configure Java 17 source/target compatibility
|
|
158
|
+
androidExtension.compileOptions {
|
|
159
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
160
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Configure Kotlin JVM target
|
|
164
|
+
// Kotlin tasks typically follow Java targetCompatibility automatically,
|
|
165
|
+
// but we explicitly set kotlinOptions.jvmTarget = "17"
|
|
166
|
+
project.tasks.withType(org.gradle.api.tasks.compile.JavaCompile::class.java).configureEach {
|
|
167
|
+
targetCompatibility = "17"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
project.logger.debug("Configured JDK 17 toolchain for ${project.name}")
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Ensures autolinked modules are compiled with Java 17.
|
|
175
|
+
*
|
|
176
|
+
* Uses rootProject.allprojects to apply to already-configured projects.
|
|
177
|
+
* Registers callbacks independent of plugin application timing via pluginManager.withPlugin.
|
|
178
|
+
*
|
|
179
|
+
* Pattern from React Native Gradle Plugin's JdkConfiguratorUtils.kt
|
|
180
|
+
*/
|
|
181
|
+
private fun configureNodeModulesJavaVersion(project: Project) {
|
|
182
|
+
// Apply to all projects using rootProject.allprojects (including already-configured projects)
|
|
183
|
+
project.rootProject.allprojects.forEach { targetProject ->
|
|
184
|
+
// Only target node_modules projects
|
|
185
|
+
if (!targetProject.projectDir.absolutePath.contains("/node_modules/")) return@forEach
|
|
186
|
+
|
|
187
|
+
// Configure Java 17 when Android Library plugin is applied
|
|
188
|
+
targetProject.pluginManager.withPlugin("com.android.library") {
|
|
189
|
+
val componentsExtension = targetProject.extensions
|
|
190
|
+
.getByType(LibraryAndroidComponentsExtension::class.java)
|
|
191
|
+
componentsExtension.finalizeDsl { libraryExtension ->
|
|
192
|
+
libraryExtension.compileOptions.apply {
|
|
193
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
194
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
targetProject.logger.debug("Granite: Configured Java 17 for node_modules project: ${targetProject.name}")
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Configure jvmToolchain when Kotlin Android plugin is applied (using reflection)
|
|
201
|
+
targetProject.pluginManager.withPlugin("org.jetbrains.kotlin.android") {
|
|
202
|
+
try {
|
|
203
|
+
// Use reflection since KotlinAndroidProjectExtension has no compile-time dependency
|
|
204
|
+
val kotlinExtension = targetProject.extensions.findByName("kotlin")
|
|
205
|
+
if (kotlinExtension != null) {
|
|
206
|
+
val jvmToolchainMethod = kotlinExtension.javaClass.getMethod("jvmToolchain", Int::class.javaPrimitiveType)
|
|
207
|
+
jvmToolchainMethod.invoke(kotlinExtension, 17)
|
|
208
|
+
targetProject.logger.debug("Granite: Configured Kotlin jvmToolchain(17) for: ${targetProject.name}")
|
|
209
|
+
}
|
|
210
|
+
} catch (e: Exception) {
|
|
211
|
+
// Fallback: jvmToolchain may not exist in some Kotlin versions
|
|
212
|
+
targetProject.logger.debug("Granite: Could not set jvmToolchain for ${targetProject.name}: ${e.message}")
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
project.logger.debug("Granite: Registered Java 17 configuration for all node_modules projects")
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private fun configurePlugin(
|
|
221
|
+
project: Project,
|
|
222
|
+
extension: GraniteExtension,
|
|
223
|
+
androidComponents: LibraryAndroidComponentsExtension,
|
|
224
|
+
) {
|
|
225
|
+
// Get Android extension
|
|
226
|
+
val androidExtension = project.extensions.getByType(LibraryExtension::class.java)
|
|
227
|
+
|
|
228
|
+
// Validate extension configuration
|
|
229
|
+
extension.validate()
|
|
230
|
+
|
|
231
|
+
// Configure Maven repositories
|
|
232
|
+
val repositoryConfigurator = RepositoryConfigurator(project, extension)
|
|
233
|
+
repositoryConfigurator.configure()
|
|
234
|
+
|
|
235
|
+
// Configure React Native dependencies
|
|
236
|
+
val dependencyConfigurator = DependencyConfigurator(project, extension)
|
|
237
|
+
dependencyConfigurator.configure()
|
|
238
|
+
|
|
239
|
+
// Run react-native config and autolink native module dependencies
|
|
240
|
+
try {
|
|
241
|
+
val autolinkingConfig = runReactNativeConfig(project, extension)
|
|
242
|
+
autolinkLibrariesWithApp(project, autolinkingConfig)
|
|
243
|
+
} catch (e: Exception) {
|
|
244
|
+
project.logger.warn("Failed to autolink native module dependencies: ${e.message}")
|
|
245
|
+
project.logger.debug("Autolinking failure details:", e)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// NDK/CMake settings already configured in apply() method before afterEvaluate
|
|
249
|
+
// Resource packaging already configured in apply() method before afterEvaluate
|
|
250
|
+
|
|
251
|
+
// Configure BuildConfig fields
|
|
252
|
+
val buildConfigConfigurator = BuildConfigConfigurator(project, extension)
|
|
253
|
+
buildConfigConfigurator.configure(androidExtension)
|
|
254
|
+
|
|
255
|
+
// Generate dev server resources for debug builds
|
|
256
|
+
val devServerResourceConfigurator = DevServerResourceConfigurator(project, extension)
|
|
257
|
+
devServerResourceConfigurator.configure()
|
|
258
|
+
|
|
259
|
+
// Register autolinking task
|
|
260
|
+
val autolinkingTask = registerAutolinkingTask(project, extension, androidExtension)
|
|
261
|
+
|
|
262
|
+
// Wire autolinking into compilation
|
|
263
|
+
wireAutolinkingIntoCompilation(project, androidExtension, autolinkingTask)
|
|
264
|
+
|
|
265
|
+
// Register codegen tasks
|
|
266
|
+
val (codegenSchemaTask, codegenArtifactsTask) = registerCodegenTasks(project, extension, androidExtension)
|
|
267
|
+
|
|
268
|
+
// Wire codegen tasks into compilation
|
|
269
|
+
wireCodegenIntoCompilation(project, androidExtension, autolinkingTask, codegenSchemaTask, codegenArtifactsTask)
|
|
270
|
+
|
|
271
|
+
// Note: Generated sources are registered early in apply() method before afterEvaluate
|
|
272
|
+
// to avoid "too late to add actions" errors with finalizeDsl and onVariants
|
|
273
|
+
|
|
274
|
+
// Bundle and asset packaging tasks were registered early (before afterEvaluate)
|
|
275
|
+
// Now configure task dependencies
|
|
276
|
+
configureBundleTaskDependencies(project, codegenArtifactsTask)
|
|
277
|
+
|
|
278
|
+
project.logger.lifecycle("Granite plugin configured for library module: ${project.name}")
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Registers the AutolinkingTask.
|
|
283
|
+
*
|
|
284
|
+
* @return TaskProvider for the autolinking task
|
|
285
|
+
*/
|
|
286
|
+
private fun registerAutolinkingTask(
|
|
287
|
+
project: Project,
|
|
288
|
+
extension: GraniteExtension,
|
|
289
|
+
androidExtension: LibraryExtension,
|
|
290
|
+
): TaskProvider<AutolinkingTask> {
|
|
291
|
+
val taskProvider = project.tasks.register("graniteAutolinking", AutolinkingTask::class.java)
|
|
292
|
+
|
|
293
|
+
taskProvider.configure {
|
|
294
|
+
reactNativeDir.set(extension.reactNativeDir.get())
|
|
295
|
+
nodeModulesDir.set(extension.nodeModulesDir.get())
|
|
296
|
+
projectDir.set(project.layout.projectDirectory)
|
|
297
|
+
outputDir.set(project.layout.buildDirectory.dir("generated/autolinking"))
|
|
298
|
+
|
|
299
|
+
// PackageList must be in com.facebook.react package to match React Native convention
|
|
300
|
+
// This is the expected package that React Native code imports
|
|
301
|
+
packageName.set("com.facebook.react")
|
|
302
|
+
packageListFile.set(
|
|
303
|
+
project.layout.buildDirectory.file("generated/autolinking/src/main/java/com/facebook/react/PackageList.java"),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
// JNI autolinking output
|
|
307
|
+
jniOutputDir.set(project.layout.buildDirectory.dir("generated/autolinking/src/main/jni"))
|
|
308
|
+
autolinkingHeaderFile.set(
|
|
309
|
+
project.layout.buildDirectory.file("generated/autolinking/src/main/jni/autolinking.h"),
|
|
310
|
+
)
|
|
311
|
+
autolinkingCppFile.set(
|
|
312
|
+
project.layout.buildDirectory.file("generated/autolinking/src/main/jni/autolinking.cpp"),
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return taskProvider
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Wires the autolinking task into the compilation process.
|
|
321
|
+
*
|
|
322
|
+
* Ensures that PackageList.kt is generated before compilation starts.
|
|
323
|
+
* Uses standard preBuild task dependency instead of pattern matching.
|
|
324
|
+
*
|
|
325
|
+
* Note: Source registration is handled by registerGeneratedSourcesInDsl/InVariants methods.
|
|
326
|
+
*/
|
|
327
|
+
private fun wireAutolinkingIntoCompilation(
|
|
328
|
+
project: Project,
|
|
329
|
+
androidExtension: LibraryExtension,
|
|
330
|
+
autolinkingTask: TaskProvider<AutolinkingTask>,
|
|
331
|
+
) {
|
|
332
|
+
// Use standard preBuild dependency - more robust than pattern matching
|
|
333
|
+
project.tasks.named("preBuild").configure {
|
|
334
|
+
dependsOn(autolinkingTask)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Make CMake tasks depend on autolinking (for JNI files)
|
|
338
|
+
project.tasks.configureEach {
|
|
339
|
+
if (name.startsWith("configureCMake")) {
|
|
340
|
+
dependsOn(autolinkingTask)
|
|
341
|
+
}
|
|
342
|
+
if (name.startsWith("buildCMake")) {
|
|
343
|
+
dependsOn(autolinkingTask)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
project.logger.debug("Autolinking task wired into preBuild and CMake")
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Registers the Codegen tasks (schema and artifacts).
|
|
352
|
+
*
|
|
353
|
+
* @return Pair of TaskProvider for (schema task, artifacts task)
|
|
354
|
+
*/
|
|
355
|
+
private fun registerCodegenTasks(
|
|
356
|
+
project: Project,
|
|
357
|
+
extension: GraniteExtension,
|
|
358
|
+
androidExtension: LibraryExtension,
|
|
359
|
+
): Pair<TaskProvider<CodegenSchemaTask>, TaskProvider<CodegenArtifactsTask>> {
|
|
360
|
+
// Register CodegenSchemaTask
|
|
361
|
+
val schemaTask = project.tasks.register("graniteCodegenSchema", CodegenSchemaTask::class.java)
|
|
362
|
+
|
|
363
|
+
schemaTask.configure {
|
|
364
|
+
// Determine JavaScript source directories
|
|
365
|
+
val jsSources = mutableListOf<File>()
|
|
366
|
+
val mainJsDir = project.file("src/main/js")
|
|
367
|
+
if (mainJsDir.exists()) {
|
|
368
|
+
jsSources.add(mainJsDir)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
jsSourceDirs.set(jsSources)
|
|
372
|
+
reactNativeDir.set(extension.reactNativeDir.get())
|
|
373
|
+
nodeModulesDir.set(extension.nodeModulesDir.get())
|
|
374
|
+
outputDir.set(project.layout.buildDirectory.dir("generated/codegen/schema"))
|
|
375
|
+
schemaFile.set(
|
|
376
|
+
project.layout.buildDirectory.file("generated/codegen/schema/schema.json"),
|
|
377
|
+
)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Register CodegenArtifactsTask
|
|
381
|
+
val artifactsTask = project.tasks.register("graniteCodegenArtifacts", CodegenArtifactsTask::class.java)
|
|
382
|
+
|
|
383
|
+
artifactsTask.configure {
|
|
384
|
+
schemaFile.set(schemaTask.flatMap { task -> task.schemaFile })
|
|
385
|
+
reactNativeDir.set(extension.reactNativeDir.get())
|
|
386
|
+
nodeModulesDir.set(extension.nodeModulesDir.get())
|
|
387
|
+
|
|
388
|
+
val namespace = androidExtension.namespace
|
|
389
|
+
?: project.group.toString().ifEmpty { "com.example.granite" }
|
|
390
|
+
packageName.set(namespace)
|
|
391
|
+
libraryName.set(project.name)
|
|
392
|
+
|
|
393
|
+
javaOutputDir.set(project.layout.buildDirectory.dir("generated/codegen/java"))
|
|
394
|
+
jniOutputDir.set(project.layout.buildDirectory.dir("generated/codegen/jni"))
|
|
395
|
+
|
|
396
|
+
// Artifacts task depends on schema task
|
|
397
|
+
dependsOn(schemaTask)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return Pair(schemaTask, artifactsTask)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Wires the codegen tasks into the compilation process.
|
|
405
|
+
*
|
|
406
|
+
* Ensures that:
|
|
407
|
+
* - Codegen runs after autolinking
|
|
408
|
+
* - Codegen completes before compilation starts
|
|
409
|
+
*
|
|
410
|
+
* Uses standard preBuild task dependency instead of pattern matching.
|
|
411
|
+
*
|
|
412
|
+
* Note: Source registration is handled by registerGeneratedSourcesInDsl/InVariants methods.
|
|
413
|
+
* Note: C++ sources are handled by CMake configuration, not source sets.
|
|
414
|
+
*/
|
|
415
|
+
private fun wireCodegenIntoCompilation(
|
|
416
|
+
project: Project,
|
|
417
|
+
androidExtension: LibraryExtension,
|
|
418
|
+
autolinkingTask: TaskProvider<AutolinkingTask>,
|
|
419
|
+
codegenSchemaTask: TaskProvider<CodegenSchemaTask>,
|
|
420
|
+
codegenArtifactsTask: TaskProvider<CodegenArtifactsTask>,
|
|
421
|
+
) {
|
|
422
|
+
// Codegen schema should run after autolinking
|
|
423
|
+
codegenSchemaTask.configure {
|
|
424
|
+
mustRunAfter(autolinkingTask)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Use standard preBuild dependency - more robust than pattern matching
|
|
428
|
+
project.tasks.named("preBuild").configure {
|
|
429
|
+
dependsOn(codegenArtifactsTask)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
project.logger.debug("Codegen tasks wired into preBuild")
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Registers bundle and asset packaging tasks for each build variant.
|
|
437
|
+
*
|
|
438
|
+
* Creates per-variant tasks for:
|
|
439
|
+
* - JavaScript bundling with Metro (graniteBundleXxx)
|
|
440
|
+
* - Hermes bytecode compilation
|
|
441
|
+
* - Asset packaging (granitePackageAssetsXxx)
|
|
442
|
+
*
|
|
443
|
+
* IMPORTANT: These tasks are NOT automatically executed during AAR builds.
|
|
444
|
+
* Users must manually run them when bundle generation is needed.
|
|
445
|
+
*
|
|
446
|
+
* Example usage:
|
|
447
|
+
* # Generate bundle only
|
|
448
|
+
* ./gradlew graniteBundleRelease
|
|
449
|
+
*
|
|
450
|
+
* # Generate bundle and package assets
|
|
451
|
+
* ./gradlew graniteBundleRelease granitePackageAssetsRelease
|
|
452
|
+
*
|
|
453
|
+
* # Build AAR with bundled assets
|
|
454
|
+
* ./gradlew graniteBundleRelease granitePackageAssetsRelease bundleReleaseAar
|
|
455
|
+
*
|
|
456
|
+
* Must be called BEFORE afterEvaluate to avoid "too late to add actions" error.
|
|
457
|
+
*/
|
|
458
|
+
private fun registerBundleAndAssetTasksEarly(
|
|
459
|
+
project: Project,
|
|
460
|
+
extension: GraniteExtension,
|
|
461
|
+
componentsExtension: LibraryAndroidComponentsExtension,
|
|
462
|
+
) {
|
|
463
|
+
componentsExtension.onVariants { variant ->
|
|
464
|
+
val variantName = variant.name
|
|
465
|
+
val capitalizedVariantName = variantName.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
|
|
466
|
+
|
|
467
|
+
// Determine if this is a debug/dev variant
|
|
468
|
+
val isDev = variantName.contains("debug", ignoreCase = true)
|
|
469
|
+
|
|
470
|
+
// Register bundle task for this variant
|
|
471
|
+
val bundleTaskProvider = project.tasks.register(
|
|
472
|
+
"graniteBundle$capitalizedVariantName",
|
|
473
|
+
BundleTask::class.java,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
bundleTaskProvider.configure {
|
|
477
|
+
entryFile.set(extension.getEntryFileResolved())
|
|
478
|
+
reactNativeDir.set(extension.reactNativeDir.get())
|
|
479
|
+
nodeModulesDir.set(extension.nodeModulesDir.get())
|
|
480
|
+
projectDir.set(project.layout.projectDirectory)
|
|
481
|
+
|
|
482
|
+
// Output bundle file (always Hermes bytecode .hbc)
|
|
483
|
+
val bundleName = extension.bundleAssetName.get()
|
|
484
|
+
bundleFile.set(
|
|
485
|
+
project.layout.buildDirectory.file(
|
|
486
|
+
"generated/assets/$variantName/$bundleName.hbc",
|
|
487
|
+
),
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
// Source map file
|
|
491
|
+
sourceMapFile.set(
|
|
492
|
+
project.layout.buildDirectory.file(
|
|
493
|
+
"generated/assets/$variantName/$bundleName.map",
|
|
494
|
+
),
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
bundleAssetName.set(extension.bundleAssetName.get())
|
|
498
|
+
this.devMode.set(isDev)
|
|
499
|
+
this.variantName.set(variantName)
|
|
500
|
+
|
|
501
|
+
// Bundle task should run after codegen (will be configured in afterEvaluate)
|
|
502
|
+
// mustRunAfter will be set up later when codegen task is registered
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Register asset packaging task for this variant
|
|
506
|
+
val assetPackagingTaskProvider = project.tasks.register(
|
|
507
|
+
"granitePackageAssets$capitalizedVariantName",
|
|
508
|
+
AssetPackagingTask::class.java,
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
assetPackagingTaskProvider.configure {
|
|
512
|
+
bundleFile.set(bundleTaskProvider.flatMap { it.bundleFile })
|
|
513
|
+
|
|
514
|
+
// Assets directory (Metro generates this during bundling)
|
|
515
|
+
assetsDir.set(
|
|
516
|
+
project.layout.buildDirectory.dir("generated/assets/$variantName"),
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
// Output to variant-specific Android assets directory
|
|
520
|
+
outputAssetsDir.set(
|
|
521
|
+
project.layout.projectDirectory.dir("src/$variantName/assets"),
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
outputResDir.set(
|
|
525
|
+
project.layout.projectDirectory.dir("src/$variantName/res"),
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
bundleAssetName.set(extension.bundleAssetName.get())
|
|
529
|
+
compressionEnabled.set(extension.bundleCompressionEnabled.get())
|
|
530
|
+
this.variantName.set(variantName)
|
|
531
|
+
|
|
532
|
+
// NOTE: No automatic dependency on bundleTaskProvider
|
|
533
|
+
// Users must manually run bundle tasks when needed:
|
|
534
|
+
// ./gradlew graniteBundle$capitalizedVariantName granitePackageAssets$capitalizedVariantName
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// NOTE: Bundle and asset packaging tasks are decoupled from AAR build
|
|
538
|
+
// This allows:
|
|
539
|
+
// 1. Fast AAR builds without bundling (development)
|
|
540
|
+
// 2. Manual bundle generation when needed (production)
|
|
541
|
+
// 3. Flexibility for projects with JS in separate repositories
|
|
542
|
+
//
|
|
543
|
+
// To include bundles in AAR, manually run:
|
|
544
|
+
// ./gradlew graniteBundle$capitalizedVariantName granitePackageAssets$capitalizedVariantName bundle${capitalizedVariantName}Aar
|
|
545
|
+
|
|
546
|
+
project.logger.debug("Registered bundle and asset tasks for variant: $variantName")
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Configures bundle task dependencies after all tasks are registered.
|
|
552
|
+
* Called from afterEvaluate to set up dependency relationships.
|
|
553
|
+
*/
|
|
554
|
+
private fun configureBundleTaskDependencies(
|
|
555
|
+
project: Project,
|
|
556
|
+
codegenArtifactsTask: TaskProvider<CodegenArtifactsTask>,
|
|
557
|
+
) {
|
|
558
|
+
// Find all bundle tasks and configure them to run after codegen
|
|
559
|
+
project.tasks.withType(BundleTask::class.java).configureEach {
|
|
560
|
+
mustRunAfter(codegenArtifactsTask)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
project.logger.debug("Configured bundle task dependencies")
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Registers generated source directories at the DSL level using finalizeDsl.
|
|
568
|
+
*
|
|
569
|
+
* This ensures DSL-level source registration completes before variant processing,
|
|
570
|
+
* making generated sources visible in IDE and local builds.
|
|
571
|
+
*
|
|
572
|
+
* Pattern from React Native Gradle Plugin reference implementation.
|
|
573
|
+
*/
|
|
574
|
+
private fun registerGeneratedSourcesInDsl(
|
|
575
|
+
project: Project,
|
|
576
|
+
androidComponents: LibraryAndroidComponentsExtension,
|
|
577
|
+
) {
|
|
578
|
+
androidComponents.finalizeDsl { extension ->
|
|
579
|
+
// Register autolinking output
|
|
580
|
+
val autolinkingDir = project.layout.buildDirectory.dir("generated/autolinking/src/main/java")
|
|
581
|
+
extension.sourceSets.getByName("main").java.srcDir(autolinkingDir)
|
|
582
|
+
|
|
583
|
+
// Register codegen output
|
|
584
|
+
val codegenDir = project.layout.buildDirectory.dir("generated/codegen/java")
|
|
585
|
+
extension.sourceSets.getByName("main").java.srcDir(codegenDir)
|
|
586
|
+
|
|
587
|
+
project.logger.debug("Registered generated sources in DSL via finalizeDsl")
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Registers generated source directories at the variant level using onVariants.
|
|
593
|
+
*
|
|
594
|
+
* This is CRITICAL for dependent module visibility. Dependent modules read
|
|
595
|
+
* variant-level source configurations, not DSL-level.
|
|
596
|
+
*
|
|
597
|
+
* Uses addStaticSourceDirectory to register sources with each build variant,
|
|
598
|
+
* ensuring they are visible to:
|
|
599
|
+
* - Dependent modules during their configuration phase
|
|
600
|
+
* - AAR metadata for library publication
|
|
601
|
+
* - Android Studio IDE
|
|
602
|
+
*
|
|
603
|
+
* Pattern from React Native Gradle Plugin reference implementation.
|
|
604
|
+
*/
|
|
605
|
+
private fun registerGeneratedSourcesInVariants(
|
|
606
|
+
project: Project,
|
|
607
|
+
androidComponents: LibraryAndroidComponentsExtension,
|
|
608
|
+
) {
|
|
609
|
+
androidComponents.onVariants(androidComponents.selector().all()) { variant ->
|
|
610
|
+
// Register autolinking sources at variant level
|
|
611
|
+
val autolinkingDir = project.layout.buildDirectory
|
|
612
|
+
.dir("generated/autolinking/src/main/java")
|
|
613
|
+
.get()
|
|
614
|
+
.asFile
|
|
615
|
+
.absolutePath
|
|
616
|
+
variant.sources.java?.addStaticSourceDirectory(autolinkingDir)
|
|
617
|
+
|
|
618
|
+
// Register codegen sources at variant level
|
|
619
|
+
val codegenDir = project.layout.buildDirectory
|
|
620
|
+
.dir("generated/codegen/java")
|
|
621
|
+
.get()
|
|
622
|
+
.asFile
|
|
623
|
+
.absolutePath
|
|
624
|
+
variant.sources.java?.addStaticSourceDirectory(codegenDir)
|
|
625
|
+
|
|
626
|
+
project.logger.debug("Registered generated sources for variant ${variant.name} via onVariants")
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Runs react-native config command and parses the output.
|
|
632
|
+
*
|
|
633
|
+
* @param project The Gradle project
|
|
634
|
+
* @param extension The Granite extension with configuration
|
|
635
|
+
* @return Parsed autolinking configuration
|
|
636
|
+
*/
|
|
637
|
+
private fun runReactNativeConfig(
|
|
638
|
+
project: Project,
|
|
639
|
+
extension: GraniteExtension,
|
|
640
|
+
): run.granite.gradle.models.AutolinkingConfig {
|
|
641
|
+
val reactNativeDir: File = extension.reactNativeDir.get()
|
|
642
|
+
val cliPath = File(reactNativeDir, "cli.js")
|
|
643
|
+
|
|
644
|
+
if (!cliPath.exists()) {
|
|
645
|
+
error(
|
|
646
|
+
"""
|
|
647
|
+
|React Native CLI not found: ${cliPath.absolutePath}
|
|
648
|
+
|
|
|
649
|
+
|Solution: Run 'npm install' or 'yarn install' to install React Native
|
|
650
|
+
""".trimMargin(),
|
|
651
|
+
)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Find node executable
|
|
655
|
+
val nodeExecutable = NodeExecutableFinder.findNodeExecutable()
|
|
656
|
+
|
|
657
|
+
// Execute: node path/to/react-native/cli.js config
|
|
658
|
+
val command = listOf(
|
|
659
|
+
nodeExecutable.absolutePath,
|
|
660
|
+
cliPath.absolutePath,
|
|
661
|
+
"config",
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
val stdout = ByteArrayOutputStream()
|
|
665
|
+
val stderr = ByteArrayOutputStream()
|
|
666
|
+
|
|
667
|
+
val result = project.exec {
|
|
668
|
+
workingDir = project.projectDir
|
|
669
|
+
commandLine = command
|
|
670
|
+
standardOutput = stdout
|
|
671
|
+
errorOutput = stderr
|
|
672
|
+
isIgnoreExitValue = true
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (result.exitValue != 0) {
|
|
676
|
+
error(
|
|
677
|
+
"""
|
|
678
|
+
|Failed to execute react-native config command.
|
|
679
|
+
|
|
|
680
|
+
|Command: ${command.joinToString(" ")}
|
|
681
|
+
|Exit code: ${result.exitValue}
|
|
682
|
+
|Error output:
|
|
683
|
+
|$stderr
|
|
684
|
+
""".trimMargin(),
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
val configJson = stdout.toString()
|
|
689
|
+
|
|
690
|
+
// Parse configuration
|
|
691
|
+
return try {
|
|
692
|
+
AutolinkingParser.parse(configJson)
|
|
693
|
+
} catch (e: IllegalArgumentException) {
|
|
694
|
+
throw IllegalArgumentException(
|
|
695
|
+
"react-native config: Failed to parse output. ${e.message}",
|
|
696
|
+
e,
|
|
697
|
+
)
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Extracts Gradle dependency configuration pairs from autolinking config.
|
|
703
|
+
*
|
|
704
|
+
* @param config Autolinking configuration from react-native config
|
|
705
|
+
* @return List of (configuration, projectPath) pairs
|
|
706
|
+
*/
|
|
707
|
+
private fun getGradleDependenciesToApply(
|
|
708
|
+
config: run.granite.gradle.models.AutolinkingConfig,
|
|
709
|
+
): List<Pair<String, String>> {
|
|
710
|
+
val result = mutableListOf<Pair<String, String>>()
|
|
711
|
+
|
|
712
|
+
config.androidDependencies()
|
|
713
|
+
.filter { !it.isPureCxxDependency }
|
|
714
|
+
.forEach { module ->
|
|
715
|
+
// Remove @ prefix and replace / with _ to match settings.gradle project names
|
|
716
|
+
// e.g., "@react-native-async-storage/async-storage" -> "react-native-async-storage_async-storage"
|
|
717
|
+
val nameCleansed = module.name.removePrefix("@").replace("/", "_")
|
|
718
|
+
val dependencyConfiguration = module.dependencyConfiguration ?: "api"
|
|
719
|
+
val buildTypes = module.buildTypes
|
|
720
|
+
|
|
721
|
+
if (buildTypes.isEmpty()) {
|
|
722
|
+
// No build types specified - use base configuration
|
|
723
|
+
result.add(dependencyConfiguration to ":$nameCleansed")
|
|
724
|
+
} else {
|
|
725
|
+
// Build type-specific dependencies
|
|
726
|
+
buildTypes.forEach { buildType ->
|
|
727
|
+
val config = "${buildType}${dependencyConfiguration.replaceFirstChar { it.uppercase() }}"
|
|
728
|
+
result.add(config to ":$nameCleansed")
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
return result
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Programmatically adds Gradle project() dependencies for all autolinked native modules.
|
|
738
|
+
*
|
|
739
|
+
* Must be called in afterEvaluate block to ensure all subprojects are created.
|
|
740
|
+
*
|
|
741
|
+
* @param project The Gradle project where dependencies should be added
|
|
742
|
+
* @param config Autolinking configuration from react-native config
|
|
743
|
+
*/
|
|
744
|
+
private fun autolinkLibrariesWithApp(
|
|
745
|
+
project: Project,
|
|
746
|
+
config: run.granite.gradle.models.AutolinkingConfig,
|
|
747
|
+
) {
|
|
748
|
+
val dependencies = getGradleDependenciesToApply(config)
|
|
749
|
+
|
|
750
|
+
if (dependencies.isEmpty()) {
|
|
751
|
+
project.logger.lifecycle("No autolinked dependencies to add")
|
|
752
|
+
return
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
project.logger.lifecycle("Autolinking ${dependencies.size} native module dependencies...")
|
|
756
|
+
|
|
757
|
+
var successCount = 0
|
|
758
|
+
var failureCount = 0
|
|
759
|
+
|
|
760
|
+
dependencies.forEach { (configuration, projectPath) ->
|
|
761
|
+
// Validate project exists before adding dependency
|
|
762
|
+
val foundProject = project.rootProject.findProject(projectPath)
|
|
763
|
+
if (foundProject != null) {
|
|
764
|
+
try {
|
|
765
|
+
project.dependencies.add(
|
|
766
|
+
configuration,
|
|
767
|
+
project.dependencies.project(mapOf("path" to projectPath)),
|
|
768
|
+
)
|
|
769
|
+
project.logger.lifecycle("✓ Added dependency: $configuration '$projectPath'")
|
|
770
|
+
successCount++
|
|
771
|
+
} catch (e: Exception) {
|
|
772
|
+
project.logger.error("✗ Failed to add dependency $configuration '$projectPath': ${e.message}")
|
|
773
|
+
failureCount++
|
|
774
|
+
}
|
|
775
|
+
} else {
|
|
776
|
+
project.logger.error("✗ Skipping autolink for $projectPath - project not found in settings.gradle")
|
|
777
|
+
project.logger.debug(" Available projects: ${project.rootProject.subprojects.map { it.path }}")
|
|
778
|
+
failureCount++
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
project.logger.lifecycle("Autolinking complete. Added $successCount dependencies successfully${if (failureCount > 0) ", $failureCount failed" else ""}.")
|
|
783
|
+
}
|
|
784
|
+
}
|