Skip to content

Commit

Permalink
Auto-generating Android packages list (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
wkozyra95 authored and tsapeta committed Apr 23, 2019
1 parent 4b2257e commit df6f3e5
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
.gradle
235 changes: 180 additions & 55 deletions gradle.groovy
Original file line number Diff line number Diff line change
@@ -1,16 +1,116 @@
import groovy.json.JsonSlurper

def doesUnimoduleSupportPlatform(Map unimoduleJson, String platform) {
def platforms = unimoduleJson.platforms
return platforms instanceof List && platforms.contains(platform)
import java.util.regex.Pattern

class Unimodule {
String name
List platforms
List targets
List androidPackages
String directory
String version
String androidGroup
String androidSubdirectory

boolean supportsPlatform(String platform) {
return platforms instanceof List && platforms.contains(platform)
}

boolean supportsTarget(String target) {
return targets.size() == 0 || targets.contains(target)
}
}

def readPackageFromJavaFile(String file) {
def javaFile = new File(file)
def javaFileReader = new BufferedReader(new FileReader(javaFile))
def javaFileContent = ""
while (javaFileContent.trim() == "") {
javaFileContent = javaFileReader.readLine()
}
javaFileReader.close()
def match = javaFileContent =~ /^package ([0-9a-zA-Z._]*);$/
if (match.size() == 1 && match[0].size() == 2) {
return match[0][1]
}

throw new GradleException("File $file does not include package declaration")
}

def readFromBuildGradle(String file) {
def gradleFile = new File(file)
if (!gradleFile.exists()) {
return [:]
}
def fileReader = new BufferedReader(new FileReader(gradleFile))
def result = [:]
for (def line = fileReader.readLine(); line != null; line = fileReader.readLine()) {
def versionMatch = line.trim() =~ /^version ?= ?'([0-9]+.[0-9]+.[0-9]+)'$/
def groupMatch = line.trim() =~ /^group ?= ?'([a-zA-Z]+.[a-zA-Z]+.[a-zA-Z]+)'$/
if (versionMatch.size() == 1 && versionMatch[0].size() == 2) {
result.version = versionMatch[0][1]
}
if (groupMatch.size() == 1 && groupMatch[0].size() == 2) {
result.group = groupMatch[0][1]
}
}
fileReader.close()
return result
}

def findDefaultBasePackage(String packageDir) {
def paths = new FileNameFinder().getFileNames(packageDir, "android/src/**/*Package.java", "")
if (paths.size != 1) {
return []
}
def packageName = readPackageFromJavaFile(paths[0])
def className = new File(paths[0]).getName().split(Pattern.quote("."))[0]
return ["$packageName.$className"]
}

def doesUnimoduleSupportTarget(Map unimoduleJson, String target) {
def targets = unimoduleJson.targets
return !targets || targets.contains(target)
def generateBasePackageList(List<Unimodule> unimodules) {
def findMainApp = new FileNameFinder().getFileNames(rootProject.getProjectDir().getPath(), '**/MainApplication.java', '')
if (findMainApp.size() != 1) {
throw new GradleException("You need to have MainApplication.java in your project")
}
def mainAppDirectory = new File(findMainApp[0]).parentFile
def packageName = readPackageFromJavaFile(findMainApp[0])

def fileBuilder = new StringBuilder()
fileBuilder.append("package ${packageName}.generated;\n\n")

fileBuilder.append("import java.util.Arrays;\n")
fileBuilder.append("import java.util.List;\n")
fileBuilder.append("import org.unimodules.core.interfaces.Package;\n\n")

fileBuilder.append("public class BasePackageList {\n")
fileBuilder.append(" public List<Package> getPackageList() {\n")
fileBuilder.append(" return Arrays.<Package>asList(\n")
def isEmptyList = true
for (module in unimodules) {
for (pkg in module.androidPackages) {
fileBuilder.append(" new $pkg(),\n")
isEmptyList = false
}
}
if (!isEmptyList) {
fileBuilder.deleteCharAt(fileBuilder.length() - 2) // remove last comma in a list
}
fileBuilder.append(" );\n")
fileBuilder.append(" }\n")
fileBuilder.append("}\n")


new File(mainAppDirectory, "generated").mkdirs()
def javaFile = new File(mainAppDirectory, "generated/BasePackageList.java")
javaFile.createNewFile()
def javaFileWriter = new BufferedWriter(new FileWriter(javaFile))
javaFileWriter.write(fileBuilder.toString())
javaFileWriter.close()
}

def findUnimodules(String target, List modulesToExclude, List modulesPaths) {

def findUnimodules(String target, List exclude, List modulesPaths) {
def unimodules = [:]
def unimodulesDuplicates = []

Expand All @@ -22,74 +122,72 @@ def findUnimodules(String target, List modulesToExclude, List modulesPaths) {
def unimoduleConfig = new File(moduleConfigPath)
def unimoduleJson = new JsonSlurper().parseText(unimoduleConfig.text)
def directory = unimoduleConfig.getParent()

if (doesUnimoduleSupportPlatform(unimoduleJson, 'android') && doesUnimoduleSupportTarget(unimoduleJson, target)) {
def packageJsonFile = new File(directory, 'package.json')
def packageJson = new JsonSlurper().parseText(packageJsonFile.text)
def unimoduleName = unimoduleJson.name ?: packageJson.name

if (!modulesToExclude.contains(unimoduleName)) {
def platformConfig = [subdirectory: 'android'] << unimoduleJson.get('android', [:])
def unimoduleVersion = packageJson.version

if (unimodules[unimoduleName]) {
unimodulesDuplicates.add(unimoduleName)
def buildGradle = readFromBuildGradle(new File(directory, "android/build.gradle").toString())
def packageJsonFile = new File(directory, 'package.json')
def packageJson = new JsonSlurper().parseText(packageJsonFile.text)

def unimodule = new Unimodule()
unimodule.name = unimoduleJson.name ?: packageJson.name
unimodule.directory = directory
unimodule.version = buildGradle.version ?: packageJson.version ?: "UNVERSIONED"
unimodule.androidGroup = buildGradle.group ?: "org.unimodules"
unimodule.androidSubdirectory = unimoduleJson.android?.subdirectory ?: "android"
unimodule.platforms = unimoduleJson.platforms != null ? unimoduleJson.platforms : []
assert unimodule.platforms instanceof List
unimodule.targets = unimoduleJson.targets != null ? unimoduleJson.targets : []
assert unimodule.targets instanceof List
unimodule.androidPackages = unimoduleJson.android?.packages != null ?
unimoduleJson.android.packages : findDefaultBasePackage(directory)
assert unimodule.androidPackages instanceof List

if (unimodule.supportsPlatform('android') && unimodule.supportsTarget(target)) {
if (!exclude.contains(unimodule.name)) {
if (unimodules[unimodule.name]) {
unimodulesDuplicates.add(unimodule.name)
}

if (!unimodules[unimoduleName] || VersionNumber.parse(unimoduleVersion) >= VersionNumber.parse(unimodules[unimoduleName].version)) {
unimodules[unimoduleName] = [
name: unimoduleJson.name,
directory: directory,
version: unimoduleVersion,
config: platformConfig,
]
if (!unimodules[unimodule.name] ||
VersionNumber.parse(unimodule.version) >= VersionNumber.parse(unimodules[unimodule.name].version)) {
unimodules[unimodule.name] = unimodule
}
}
}
}
}
return [
unimodules: unimodules.collect { entry -> entry.value },
duplicates: unimodulesDuplicates.unique()
unimodules: unimodules.collect { entry -> entry.value },
duplicates: unimodulesDuplicates.unique()
]
}


class Colors {
static final String NORMAL = "\u001B[0m"
static final String RED = "\u001B[31m"
static final String GREEN = "\u001B[32m"
static final String YELLOW = "\u001B[33m"
static final String NORMAL = "\u001B[0m"
static final String RED = "\u001B[31m"
static final String GREEN = "\u001B[32m"
static final String YELLOW = "\u001B[33m"
static final String MAGENTA = "\u001B[35m"
}

ext.addUnimodulesDependencies = { Map customOptions = [:] ->
def addUnimodulesDependencies(String target, List exclude, List modulesPaths, Closure<Boolean> addUnimodule) {
if (!(new File(project.rootProject.projectDir.parentFile, 'package.json').exists())) {
// There's no package.json
throw new GradleException(
"'addUnimodulesDependencies()' is being used in a project that doesn't seem to be a React Native project."
"'addUnimodulesDependencies()' is being used in a project that doesn't seem to be a React Native project."
)
}

def options = [
modulesPaths: ['../../node_modules'],
configuration: 'implementation',
target: 'react-native',
exclude: [],
] << customOptions

def results = findUnimodules(options.target, options.exclude, options.modulesPaths)
def results = findUnimodules(target, exclude, modulesPaths)
def unimodules = results.unimodules
def duplicates = results.duplicates
generateBasePackageList(unimodules)

if (unimodules.size() > 0) {
println()
println Colors.YELLOW + 'Installing unimodules:' + Colors.NORMAL

for (unimodule in unimodules) {
println ' ' + Colors.GREEN + unimodule.name + Colors.YELLOW + '@' + Colors.RED + unimodule.version + Colors.NORMAL + ' from ' + Colors.MAGENTA + unimodule.directory + Colors.NORMAL

Object dependency = project.project(':' + unimodule.name)
project.dependencies.add(options.configuration, dependency, null)
addUnimodule(unimodule)
}

if (duplicates.size() > 0) {
Expand All @@ -98,29 +196,56 @@ ext.addUnimodulesDependencies = { Map customOptions = [:] ->
println Colors.YELLOW + 'Make sure following dependencies of your project are resolving to one specific version:' + Colors.NORMAL

println ' ' + duplicates
.collect { unimoduleName -> Colors.GREEN + unimoduleName + Colors.NORMAL }
.join(', ')
.collect { unimoduleName -> Colors.GREEN + unimoduleName + Colors.NORMAL }
.join(', ')
}
} else {
println()
println Colors.YELLOW + "No unimodules found. Are you sure you've installed JS dependencies?" + Colors.NORMAL
}
}

ext.addUnimodulesDependencies = { Map customOptions = [:] ->
def options = [
modulesPaths : ['../../node_modules'],
configuration: 'implementation',
target : 'react-native',
exclude : [],
] << customOptions

addUnimodulesDependencies(options.target, options.exclude, options.modulesPaths, {unimodule ->
Object dependency = project.project(':' + unimodule.name)
project.dependencies.add(options.configuration, dependency)
})
}

ext.addMavenUnimodulesDependencies = { Map customOptions = [:] ->
def options = [
modulesPaths : ['../../node_modules'],
configuration: 'implementation',
target : 'react-native',
exclude : [],
] << customOptions

addUnimodulesDependencies(options.target, options.exclude, options.modulesPaths, {unimodule ->
project.dependencies.add(
options.configuration,
"${unimodule.androidGroup}:${unimodule.name}:${unimodule.version}"
)
})
}

ext.includeUnimodulesProjects = { Map customOptions = [:] ->
def options = [
modulesPaths: ['../../node_modules'],
target: 'react-native',
exclude: [],
modulesPaths: ['../../node_modules'],
target : 'react-native',
exclude : [],
] << customOptions

def unimodules = findUnimodules(options.target, options.exclude, options.modulesPaths).unimodules

for (unimodule in unimodules) {
def config = unimodule.config
def subdirectory = config.subdirectory

include ":${unimodule.name}"
project(":${unimodule.name}").projectDir = new File(unimodule.directory, subdirectory)
project(":${unimodule.name}").projectDir = new File(unimodule.directory, unimodule.androidSubdirectory)
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"expo-constants": "~4.0.0",
"expo-file-system": "~4.0.0",
"expo-permissions": "~4.0.0",
"expo-app-loader-provider": "~4.0.0",
"unimodules-barcode-scanner-interface": "~1.0.0",
"unimodules-camera-interface": "~1.0.0",
"unimodules-constants-interface": "~1.0.0",
Expand Down

0 comments on commit df6f3e5

Please sign in to comment.