You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
8.5 KiB
277 lines
8.5 KiB
import groovy.json.JsonSlurper
|
|
import java.nio.file.Paths
|
|
|
|
|
|
// Object representing a gradle project.
|
|
class ExpoModuleGradleProject {
|
|
// Name of the Android project
|
|
String name
|
|
|
|
// Path to the folder with Android project
|
|
String sourceDir
|
|
|
|
ExpoModuleGradleProject(Object data) {
|
|
this.name = data.name
|
|
this.sourceDir = data.sourceDir
|
|
}
|
|
}
|
|
|
|
// Object representing a module.
|
|
class ExpoModule {
|
|
// Name of the JavaScript package
|
|
String name
|
|
|
|
// Version of the package, loaded from `package.json`
|
|
String version
|
|
|
|
// Gradle projects
|
|
ExpoModuleGradleProject[] projects
|
|
|
|
ExpoModule(Object data) {
|
|
this.name = data.packageName
|
|
this.version = data.packageVersion
|
|
this.projects = data.projects.collect { new ExpoModuleGradleProject(it) }
|
|
}
|
|
}
|
|
|
|
class ExpoAutolinkingManager {
|
|
private File projectDir
|
|
private Map options
|
|
private Object cachedResolvingResults
|
|
|
|
static String generatedPackageListNamespace = 'expo.modules'
|
|
static String generatedPackageListFilename = 'ExpoModulesPackageList.java'
|
|
static String generatedFilesSrcDir = 'generated/expo/src/main/java'
|
|
|
|
ExpoAutolinkingManager(File projectDir, Map options = [:]) {
|
|
this.projectDir = projectDir
|
|
this.options = options
|
|
}
|
|
|
|
Object resolve() {
|
|
if (cachedResolvingResults) {
|
|
return cachedResolvingResults
|
|
}
|
|
String[] args = convertOptionsToCommandArgs('resolve', this.options)
|
|
args += ['--json']
|
|
|
|
String output = exec(args, projectDir)
|
|
Object json = new JsonSlurper().parseText(output)
|
|
|
|
cachedResolvingResults = json
|
|
return json
|
|
}
|
|
|
|
boolean shouldUseAAR() {
|
|
return options?.useAAR == true
|
|
}
|
|
|
|
ExpoModule[] getModules() {
|
|
Object json = resolve()
|
|
return json.modules.collect { new ExpoModule(it) }
|
|
}
|
|
|
|
static void generatePackageList(Project project, Map options) {
|
|
String[] args = convertOptionsToCommandArgs('generate-package-list', options)
|
|
|
|
// Construct absolute path to generated package list.
|
|
def generatedFilePath = Paths.get(
|
|
project.buildDir.toString(),
|
|
generatedFilesSrcDir,
|
|
generatedPackageListNamespace.replace('.', '/'),
|
|
generatedPackageListFilename
|
|
)
|
|
|
|
args += [
|
|
'--namespace',
|
|
generatedPackageListNamespace,
|
|
'--target',
|
|
generatedFilePath.toString()
|
|
]
|
|
|
|
if (options == null) {
|
|
// Options are provided only when settings.gradle was configured.
|
|
// If not or opted-out from autolinking, the generated list should be empty.
|
|
args += '--empty'
|
|
}
|
|
|
|
exec(args, project.rootDir)
|
|
}
|
|
|
|
static String exec(String[] commandArgs, File dir) {
|
|
Process proc = commandArgs.execute(null, dir)
|
|
StringBuffer outputStream = new StringBuffer()
|
|
proc.waitForProcessOutput(outputStream, System.err)
|
|
return outputStream.toString()
|
|
}
|
|
|
|
static private String[] convertOptionsToCommandArgs(String command, Map options) {
|
|
String[] args = [
|
|
'node',
|
|
'--eval',
|
|
'require(\'expo-modules-autolinking\')(process.argv.slice(1))',
|
|
'--',
|
|
command,
|
|
'--platform',
|
|
'android'
|
|
]
|
|
|
|
def searchPaths = options?.get("searchPaths", options?.get("modulesPaths", null))
|
|
if (searchPaths) {
|
|
args += searchPaths
|
|
}
|
|
|
|
if (options?.ignorePaths) {
|
|
args += '--ignore-paths'
|
|
args += options.ignorePaths
|
|
}
|
|
|
|
if (options?.exclude) {
|
|
args += '--exclude'
|
|
args += options.exclude
|
|
}
|
|
|
|
return args
|
|
}
|
|
}
|
|
|
|
class Colors {
|
|
static final String GREEN = "\u001B[32m"
|
|
static final String RESET = "\u001B[0m"
|
|
}
|
|
|
|
// We can't cast a manager that is created in `settings.gradle` to the `ExpoAutolinkingManager`
|
|
// because if someone is using `buildSrc`, the `ExpoAutolinkingManager` class
|
|
// will be loaded by two different class loader - `settings.gradle` will use a diffrent loader.
|
|
// In the JVM, classes are equal only if were loaded by the same loader.
|
|
// There is nothing that we can do in that case, but to make our code safer, we check if the class name is the same.
|
|
def validateExpoAutolinkingManager(manager) {
|
|
assert ExpoAutolinkingManager.name == manager.getClass().name
|
|
return manager
|
|
}
|
|
|
|
// Here we split the implementation, depending on Gradle context.
|
|
// `rootProject` is a `ProjectDescriptor` if this file is imported in `settings.gradle` context,
|
|
// otherwise we can assume it is imported in `build.gradle`.
|
|
if (rootProject instanceof ProjectDescriptor) {
|
|
// Method to be used in `settings.gradle`. Options passed here will have an effect in `build.gradle` context as well,
|
|
// i.e. adding the dependencies and generating the package list.
|
|
ext.useExpoModules = { Map options = [:] ->
|
|
ExpoAutolinkingManager manager = new ExpoAutolinkingManager(rootProject.projectDir, options)
|
|
ExpoModule[] modules = manager.getModules()
|
|
|
|
for (module in modules) {
|
|
for (moduleProject in module.projects) {
|
|
include(":${moduleProject.name}")
|
|
project(":${moduleProject.name}").projectDir = new File(moduleProject.sourceDir)
|
|
}
|
|
}
|
|
|
|
// Save the manager in the shared context, so that we can later use it in `build.gradle`.
|
|
gradle.ext.expoAutolinkingManager = manager
|
|
}
|
|
} else {
|
|
def addModule = { DependencyHandler handler, String projectName, Boolean useAAR ->
|
|
Project dependency = rootProject.project(":${projectName}")
|
|
|
|
if (useAAR) {
|
|
handler.add('api', "${dependency.group}:${projectName}:${dependency.version}")
|
|
} else {
|
|
handler.add('api', dependency)
|
|
}
|
|
}
|
|
|
|
def addDependencies = { DependencyHandler handler, Project project ->
|
|
def manager = validateExpoAutolinkingManager(gradle.ext.expoAutolinkingManager)
|
|
def modules = manager.getModules()
|
|
|
|
if (!modules.length) {
|
|
return
|
|
}
|
|
|
|
println ''
|
|
println 'Using expo modules'
|
|
|
|
for (module in modules) {
|
|
// Don't link itself
|
|
if (module.name == project.name) {
|
|
continue
|
|
}
|
|
// Can remove this once we move all the interfaces into the core.
|
|
if (module.name.endsWith('-interface')) {
|
|
continue
|
|
}
|
|
|
|
for (moduleProject in module.projects) {
|
|
addModule(handler, moduleProject.name, manager.shouldUseAAR())
|
|
println " - ${Colors.GREEN}${moduleProject.name}${Colors.RESET} (${module.version})"
|
|
}
|
|
}
|
|
|
|
println ''
|
|
}
|
|
|
|
// Adding dependencies
|
|
ext.addExpoModulesDependencies = { DependencyHandler handler, Project project ->
|
|
// Return early if `useExpoModules` was not called in `settings.gradle`
|
|
if (!gradle.ext.has('expoAutolinkingManager')) {
|
|
logger.error('Error: Autolinking is not set up in `settings.gradle`: expo modules won\'t be autolinked.')
|
|
return
|
|
}
|
|
|
|
def manager = validateExpoAutolinkingManager(gradle.ext.expoAutolinkingManager)
|
|
|
|
if (rootProject.findProject(':expo-modules-core')) {
|
|
// `expo` requires `expo-modules-core` as a dependency, even if autolinking is turned off.
|
|
addModule(handler, 'expo-modules-core', manager.shouldUseAAR())
|
|
} else {
|
|
logger.error('Error: `expo-modules-core` project is not included by autolinking.')
|
|
}
|
|
|
|
// If opted-in not to autolink modules as dependencies
|
|
if (manager.options == null) {
|
|
return
|
|
}
|
|
|
|
addDependencies(handler, project)
|
|
}
|
|
|
|
// Generating the package list
|
|
ext.generatedFilesSrcDir = ExpoAutolinkingManager.generatedFilesSrcDir
|
|
|
|
ext.generateExpoModulesPackageList = {
|
|
// Get options used in `settings.gradle` or null if it wasn't set up.
|
|
Map options = gradle.ext.has('expoAutolinkingManager') ? gradle.ext.expoAutolinkingManager.options : null
|
|
|
|
if (options == null) {
|
|
// TODO(@tsapeta): Temporarily muted this error — uncomment it once we start migrating from autolinking v1 to v2
|
|
// logger.error('Autolinking is not set up in `settings.gradle`: generated package list with expo modules will be empty.')
|
|
}
|
|
ExpoAutolinkingManager.generatePackageList(project, options)
|
|
}
|
|
|
|
ext.ensureDependeciesWereEvaluated = { Project project ->
|
|
if (!gradle.ext.has('expoAutolinkingManager')) {
|
|
return
|
|
}
|
|
|
|
def modules = gradle.ext.expoAutolinkingManager.getModules()
|
|
for (module in modules) {
|
|
for (moduleProject in module.projects) {
|
|
def dependency = project.findProject(":${moduleProject.name}")
|
|
if (dependency == null) {
|
|
logger.warn("Coudn't find project ${moduleProject.name}. Please, make sure that `useExpoModules` was called in `settings.gradle`.")
|
|
continue
|
|
}
|
|
|
|
// Prevent circular dependencies
|
|
if (moduleProject.name == project.name) {
|
|
continue
|
|
}
|
|
|
|
project.evaluationDependsOn(":${moduleProject.name}")
|
|
}
|
|
}
|
|
}
|
|
}
|