From 5499b7c12b6d7ae20adb0d9c519edb56fd08f817 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Fri, 7 Jul 2017 16:51:48 +0300 Subject: [PATCH 1/7] make configurations be editted in build/configurations instead of mutating those copied from the CLI note: this is a temporary workaround, not a permanent solution, as it's not an incremental solution --- .../project-template-gradle/build.gradle | 401 +++++++++--------- 1 file changed, 210 insertions(+), 191 deletions(-) diff --git a/build-artifacts/project-template-gradle/build.gradle b/build-artifacts/project-template-gradle/build.gradle index b4aa8d36f..c3a015712 100644 --- a/build-artifacts/project-template-gradle/build.gradle +++ b/build-artifacts/project-template-gradle/build.gradle @@ -18,16 +18,19 @@ * -PdontRunSbg=[true/false] */ + +import groovy.io.FileType import groovy.json.JsonSlurper //used to parse package.json import java.nio.file.Files; import java.nio.file.Paths; +import java.nio.file.StandardOpenOption import java.util.regex.Pattern; buildscript { - repositories { - jcenter() - } + repositories { + jcenter() + } dependencies { classpath "com.android.tools.build:gradle:2.2.3" @@ -36,16 +39,14 @@ buildscript { apply plugin: "com.android.application" -def isWinOs = System.properties['os.name'].toLowerCase().contains('windows') def metadataParams = new LinkedList () def allJarPaths = new LinkedList () -def configurationsDir = "$projectDir/configurations" def shouldCreatePluginConfigFile = false def configStage = "\n:config phase: " def nodeModulesDir = "../../node_modules/" -def libDir = "$projectDir/../../lib/Android/" def flavorNames = new ArrayList() -def configDir = file(configurationsDir) +def originalConfigurationsDir = new File(projectDir, "configurations") +def configurationsDir = new File(projectDir, "build/configurations") def packageJsonContents = [:] def excludedDevDependencies = ['**/.bin/**'] @@ -81,6 +82,7 @@ def computeBuildToolsVersion() { return "25.0.2" } } + project.ext.selectedBuildType = project.hasProperty("release") ? "release" : "debug" def renameResultApks (variant) { @@ -94,20 +96,20 @@ def renameResultApks (variant) { def apkNamePrefix = rootProject.name + "-" + variant.buildType.name + abiName name = apkNamePrefix + ".apk" output.packageApplication.outputFile = new File(apkDirectory, name); - } + } } // gets the devDependencies declared in the package.json and excludes them from the build task getDevDependencies { doLast { println "$configStage getDevDependencies" - + String content = new File("$projectDir/../../package.json").getText("UTF-8") def jsonSlurper = new JsonSlurper() def packageJsonMap = jsonSlurper.parseText(content) - + packageJsonContents = packageJsonMap.devDependencies; - + packageJsonContents.each { entry -> excludedDevDependencies.add(entry.key + '/platforms/android/**/*.jar') } @@ -119,27 +121,27 @@ task getDevDependencies { android { compileSdkVersion computeCompileSdkVersion() - buildToolsVersion computeBuildToolsVersion() - + buildToolsVersion computeBuildToolsVersion() + defaultConfig { minSdkVersion 17 targetSdkVersion computeTargetSdkVersion() ndk { - abiFilters "armeabi-v7a", "x86" + abiFilters "armeabi-v7a", "x86" } } - + sourceSets.main { - jniLibs.srcDir "$projectDir/libs/jni" - } - + jniLibs.srcDir "$projectDir/libs/jni" + } + signingConfigs { - release { - if(project.hasProperty("release")) { + release { + if(project.hasProperty("release")) { if(project.hasProperty("ksPath") && - project.hasProperty("ksPassword") && - project.hasProperty("alias") && - project.hasProperty("password")) + project.hasProperty("ksPassword") && + project.hasProperty("alias") && + project.hasProperty("password")) { storeFile file(ksPath) storePassword ksPassword @@ -147,18 +149,18 @@ android { keyPassword password } } - } - } - buildTypes { - release { - signingConfig signingConfigs.release - } - } - + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } + applicationVariants.all { variant -> renameResultApks(variant) } - + applicationVariants.all { variant -> def variantName = variant.name.capitalize() def compileSourcesTaskName = "compile${variantName}Sources" @@ -169,7 +171,7 @@ android { if(!dontRunSbg) { collectAllJars.finalizedBy(setProperties) } - + compileSourcesTask.finalizedBy(buildMetadata) // forces packaging of resources and assets AFTER producing metadata @@ -215,17 +217,17 @@ dependencies { compile "com.android.support:appcompat-v7:$supportVer" debugCompile "com.android.support:design:$supportVer" - // take all jars within the libs dir + // take all jars within the libs dir compile fileTree(dir: "$projectDir/libs", include: ["**/*.jar"]) // take all jars within the node_modules dir compile fileTree(dir: nodeModulesDir, include: ["**/platforms/android/**/*.jar"], exclude: excludedDevDependencies) - + //when gradle has aar support this will be preferred instead of addAarDependencies // compile files("$rootDir/libs/aar") { - // builtBy 'copyAarDependencies' - // } + // builtBy 'copyAarDependencies' + // } copyNativeScriptAar() compile project(':runtime') @@ -251,64 +253,64 @@ def copyNativeScriptAar() { ///////////////////////////// CONFIGURATION PHASE ////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// -def updateProductFlavorsContent(flavor, dimensionName, oldContent) { +static def updateProductFlavorsContent(flavor, dimensionName, oldContent) { def endIndex = oldContent.length() - 1; - def index = 0; - def newContent = ""; - def level = -1; - def dimensionFound = false; - - while(index <= endIndex) { - if(level == 0 && (oldContent[index] == '"' || oldContent[index] == "'")) { - def closingQuotes = oldContent.indexOf('"', index + 1); - if(closingQuotes == -1) { - closingQuotes = oldContent.indexOf("'", index + 1); - } - - index = closingQuotes + 1; - newContent += "\"${flavor}\""; - continue; + def index = 0; + def newContent = ""; + def level = -1; + def dimensionFound = false; + + while(index <= endIndex) { + if(level == 0 && (oldContent[index] == '"' || oldContent[index] == "'")) { + def closingQuotes = oldContent.indexOf('"', index + 1); + if(closingQuotes == -1) { + closingQuotes = oldContent.indexOf("'", index + 1); } - if(oldContent[index] == "{") { - level++; - } + index = closingQuotes + 1; + newContent += "\"${flavor}\""; + continue; + } - if(oldContent[index] == "}") { - level--; - } + if(oldContent[index] == "{") { + level++; + } - if(level > 0) { - if(!dimensionFound && oldContent.indexOf("dimension", index) == index) { - newContent += "dimension \"${dimensionName}\""; - dimensionFound = true; - index += "dimension ".length(); - def openingQuoutes = oldContent.indexOf('"', index); - if(openingQuoutes == -1) { - openingQuoutes = oldContent.indexOf("'", index); - } + if(oldContent[index] == "}") { + level--; + } - def closingQuotes = oldContent.indexOf('"', openingQuoutes + 1); - if(closingQuotes == -1) { - closingQuotes = oldContent.indexOf("'", openingQuoutes + 1); - } + if(level > 0) { + if(!dimensionFound && oldContent.indexOf("dimension", index) == index) { + newContent += "dimension \"${dimensionName}\""; + dimensionFound = true; + index += "dimension ".length(); + def openingQuoutes = oldContent.indexOf('"', index); + if(openingQuoutes == -1) { + openingQuoutes = oldContent.indexOf("'", index); + } - index = closingQuotes + 1; + def closingQuotes = oldContent.indexOf('"', openingQuoutes + 1); + if(closingQuotes == -1) { + closingQuotes = oldContent.indexOf("'", openingQuoutes + 1); } + + index = closingQuotes + 1; } + } - newContent += oldContent[index]; + newContent += oldContent[index]; - index++; - } + index++; + } - return newContent; + return newContent; } -def createProductFlavorsContent(flavor, dimensionName, includeAndroidContent = true) { +static def createProductFlavorsContent(flavor, dimensionName, includeAndroidContent = true) { if (includeAndroidContent) - { - def content = """ + { + def content = """ android { productFlavors { "${flavor}" { @@ -317,46 +319,46 @@ android { } } """ - return content; + return content; } else { - def content = """ + def content = """ productFlavors { "${flavor}" { dimension "${dimensionName}" } } """ - return content; + return content; } } -def createIncludeFile (filePath, flavor, dimensionName) { - println "\t + creating include.gradle file for ${filePath}" - - def defaultIncludeFile = new File(filePath, "include.gradle") +def createIncludeFile (filePath, flavor, dimensionName, destination) { + println "\t + creating include.gradle file for ${filePath}" + + def defaultIncludeFile = new File(destination, "include.gradle") defaultIncludeFile.text = createProductFlavorsContent(flavor, dimensionName); } -def sanitizeDimensionName(str) { +static def sanitizeDimensionName(str) { return str.replaceAll(/\W/, "") } -def replaceProductFlavorInContent(content, dimension, flavor) { +static def modifyProductFlavorInContent(content, dimension, flavor) { def indexStart = content.indexOf("productFlavors"); def index = indexStart + "productFlavors".length(); def indexEnd = -1; - def nestedOpenBraketsCount = 0; + def nestedOpenBracketsCount = 0; while (index < content.length()) { // print content[index]; if (content[index] == "}") { - nestedOpenBraketsCount--; + nestedOpenBracketsCount--; - if (nestedOpenBraketsCount == 0) + if (nestedOpenBracketsCount == 0) { indexEnd = index; break; @@ -364,54 +366,77 @@ def replaceProductFlavorInContent(content, dimension, flavor) { } else if (content[index] == "{") { - nestedOpenBraketsCount++; + nestedOpenBracketsCount++; } index++; } if (indexEnd != -1) - { + { // full content of productFlavors { ... } -> the substring is parenthesis to parenthesis -> { ... } - def oldProductFlavorsText = content.substring(indexStart, indexEnd + 1); + def oldProductFlavorsText = content.substring(indexStart, indexEnd + 1); - def newProductFlavorsContent = updateProductFlavorsContent(flavor, dimension, oldProductFlavorsText); - - return content.replace(oldProductFlavorsText, newProductFlavorsContent); - } - else - { - def androidContentExists = content.indexOf("android {") != -1; - def newProductFlavorsContent = createProductFlavorsContent(flavor, dimension, !androidContentExists); - - if (androidContentExists) - { - return content.replace("android {", "android { ${newProductFlavorsContent}"); - } - else - { - return "${newProductFlavorsContent} \t ${content}" - } - } + def newProductFlavorsContent = updateProductFlavorsContent(flavor, dimension, oldProductFlavorsText); + + return content.replace(oldProductFlavorsText, newProductFlavorsContent); + } + else + { + def androidContentExists = content.indexOf("android {") != -1; + def newProductFlavorsContent = createProductFlavorsContent(flavor, dimension, !androidContentExists); + + if (androidContentExists) + { + return content.replace("android {", "android { ${newProductFlavorsContent}"); + } + else + { + return "${newProductFlavorsContent} \t ${content}" + } + } } // make sure the include.gradle file provided by the user has only allowed characters in dimension attribute and remove any invalid characters if necessary -def updateIncludeGradleFile(targetFile, dimensionName, flavor) { +def updateIncludeGradleFile(targetFile, dimensionName, flavor, destination) { def fileEntry = new File(targetFile.getAbsolutePath()); def content = fileEntry.text; - def replacedContent = replaceProductFlavorInContent(content, dimensionName, flavor); - fileEntry.text = replacedContent; + def replacedContent = modifyProductFlavorInContent(content, dimensionName, flavor); + Files.write(Paths.get(destination.toString(), "include.gradle"), replacedContent.getBytes(), StandardOpenOption.CREATE); +} + +static def createPluginConfigurationPath(configurationsDir, originalIncludeGradle) { + return java.nio.file.FileSystems.getDefault().getPath(configurationsDir.getAbsolutePath(), getParentDirStructure(originalIncludeGradle) as String[]); +} + +static def getParentDirStructure(includeGradleFile) { + def parents = [] + def canonicaFile = includeGradleFile.getCanonicalFile(); + def parentDir = canonicaFile.getParentFile() + def parentDirName = parentDir.name + parents.add(parentDirName) + while (true) { + parentDir = parentDir.getCanonicalFile().getParentFile() + parentDirName = parentDir.name + if (parentDirName == "configurations") { + break + } + + parents.add(parentDirName) + } + + return parents.reverse() } def renamePluginDirToFlavorName(directory, flavor) { - def parentName = directory.getName(); - def parentFile = new File("src", parentName); - if (parentFile.exists()) - { - def targetDirName = new File("src", flavor); - println "Renaming plugin directory to flavor name: ${parentFile.getAbsolutePath()} -> ${targetDirName.getAbsolutePath()}"; - parentFile.renameTo(targetDirName); - } + def parentName = directory.getName(); + def parentFile = new File("src", parentName); + if (parentFile.exists()) + { + def targetDirName = new File("src", flavor); + println "Renaming plugin directory to flavor name: ${parentFile.getAbsolutePath()} -> ${targetDirName.getAbsolutePath()}"; + parentFile.renameTo(targetDirName); + } } def flavorNumber = 0 @@ -419,9 +444,9 @@ def flavorNumber = 0 task createDefaultIncludeFiles { description "creates default include.gradle files for added plugins IF NECESSARY" println "$configStage createDefaultIncludeFiles" - def ft = file(configurationsDir) - - ft.listFiles().each { file -> + def fileTree = file(originalConfigurationsDir) + + fileTree.listFiles().each { file -> if (file.isDirectory()) { shouldCreatePluginConfigFile = true def hasChildrenDirs = false @@ -432,87 +457,90 @@ task createDefaultIncludeFiles { } // if plugin is scoped - traverse its children directories - // e.g. @scope/plugin-with-android-aars + // e.g. @scope/plugin-with-android-aars if (hasChildrenDirs) { file.listFiles().each { subFile -> if (subFile.isDirectory()) { - createIncludeGradleForPlugin(subFile, flavorNumber++, flavorNames) + createIncludeGradleForPlugin(subFile, flavorNumber++, flavorNames, configurationsDir) } } } else { - createIncludeGradleForPlugin(file, flavorNumber++, flavorNames) - } + createIncludeGradleForPlugin(file, flavorNumber++, flavorNames, configurationsDir) + } } } + + createPluginsConfigFile(flavorNames, configurationsDir) } -def createIncludeGradleForPlugin(file, flavorNumber, flavorNames) { +def createIncludeGradleForPlugin(file, flavorNumber, flavorNames, configurationsDir) { def parentDir = new File(file.getParent()) def parentName = parentDir.name def dirToRename = file if (parentName.indexOf("@") == 0) { - dirToRename = new File(parentName + "_" + file.name) + dirToRename = file(parentName + "_" + file.name) } def foundIncludeFile = false def fileName = file.name def dimensionName = sanitizeDimensionName(fileName) - + def flavor = "F" + flavorNumber println "\t+found plugins: " + fileName file.listFiles().each { subFile -> if (subFile.name == "include.gradle") { foundIncludeFile = true - updateIncludeGradleFile(subFile, dimensionName, flavor) - renamePluginDirToFlavorName(dirToRename, flavor); + def newIncludeFilePath = createPluginConfigurationPath(configurationsDir, subFile) + Files.createDirectories(newIncludeFilePath) + updateIncludeGradleFile(subFile, dimensionName, flavor, newIncludeFilePath) + renamePluginDirToFlavorName(dirToRename, flavor); } } - + flavorNames.add('"' + dimensionName + '"') - + if (!foundIncludeFile) { - createIncludeFile(file.getAbsolutePath() , flavor, dimensionName) + def newIncludeFilePath = createPluginConfigurationPath(configurationsDir, file) + Files.createDirectories(newIncludeFilePath) + createIncludeFile(file.getAbsolutePath(), destination, flavor, dimensionName) renamePluginDirToFlavorName(dirToRename, flavor); } } -task createPluginsConfigFile { +def createPluginsConfigFile(flavorNames, configurationsDir) { description "creates product flavor config file based on what plugins are added" - - if (configDir.exists()) { - println "$configStage createPluginsConfigFile" - - def flavorsFile = new File("$configurationsDir/include.gradle") - - if(shouldCreatePluginConfigFile) { - println "\t Creating product flavors include.gradle file in $configurationsDir folder..." - def flavors = flavorNames.join(", ") - - def content = """ + + if (!configurationsDir.exists() || flavorNames.isEmpty()) { + return + } + + println "\t Creating product flavors include.gradle file in $configurationsDir folder..." + + def flavorsFile = new File(configurationsDir, "include.gradle") + def flavors = flavorNames.join(", ") + def content = """ android { flavorDimensions ${flavors} } """ - - flavorsFile.text = content - } - } + + flavorsFile.text = content } task pluginExtend { description "applies additional configuration" - def pathToAppGradle = "$projectDir/../../app/App_Resources/Android/app.gradle" + def pathToAppGradle = "$projectDir/../../app/App_Resources/Android/app.gradle" def appGradle = file(pathToAppGradle) if(appGradle.exists()) { apply from: pathToAppGradle } - if(configDir.exists()) { - println "$configStage pluginExtend" - configDir.eachFileRecurse(groovy.io.FileType.FILES) { - if(it.name.equals('include.gradle')) { + if(configurationsDir.exists()) { + println "$configStage pluginExtend" + configurationsDir.eachFileRecurse(FileType.FILES) { + if(it.name == 'include.gradle') { println "\t+applying configuration from: " + it apply from: it } @@ -526,7 +554,6 @@ task pluginExtend { // createPluginsConfigFile.dependsOn(createDefaultIncludeFiles) // pluginExtend.dependsOn(createPluginsConfigFile) - //////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// BEFORE EXECUTION ///////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// @@ -545,11 +572,11 @@ task copyAarDependencies (type: Copy) { } Object[] files = Files.find( - Paths.get("$projectDir", "$nodeModulesDir"), - Integer.MAX_VALUE, - filterAarFilesFn, - java.nio.file.FileVisitOption.FOLLOW_LINKS - ) + Paths.get("$projectDir", "$nodeModulesDir"), + Integer.MAX_VALUE, + filterAarFilesFn, + java.nio.file.FileVisitOption.FOLLOW_LINKS + ) .map(mapToStringFn) .toArray(); @@ -588,19 +615,19 @@ task ensureMetadataOutDir { task collectAllJars { description "gathers all paths to jar dependencies before building metadata with them" - + def explodedAarDir = project.buildDir.getAbsolutePath() + "/intermediates/exploded-aar/" def sdkPath = android.sdkDirectory.getAbsolutePath(); def androidJar = sdkPath + "/platforms/" + android.compileSdkVersion + "/android.jar" - + doFirst { configurations.compile.each { File dependencyFile -> logger.info("Task: collectAllJars: dependency file: " + dependencyFile.getAbsolutePath()) allJarPaths.add(dependencyFile.getAbsolutePath()) } - + allJarPaths.add(androidJar); - + def ft = fileTree(dir: explodedAarDir, include: "**/*.jar") ft.each { currentJarFile -> allJarPaths.add(currentJarFile.getAbsolutePath()) @@ -616,23 +643,23 @@ task collectAllJars { jars.add(f) } } - + asbgProject.ext.jarFiles = jars } } task buildMetadata (type: JavaExec) { description "builds metadata with provided jar dependencies" - + inputs.files(allJarPaths) inputs.dir("$buildDir/intermediates/classes") outputs.files("metadata/output/assets/metadata/treeNodeStream.dat", "metadata/output/assets/metadata/treeStringsStream.dat", "metadata/output/assets/metadata/treeValueStream.dat") doFirst { - // get compiled classes to pass to metadata generator - // these need to be called after the classes have compiled - def classesDir = "$buildDir/intermediates/classes" + // get compiled classes to pass to metadata generator + // these need to be called after the classes have compiled + def classesDir = "$buildDir/intermediates/classes" def classesSubDirs = new File(classesDir).listFiles() def selectedBuildType = project.ext.selectedBuildType @@ -657,7 +684,7 @@ task buildMetadata (type: JavaExec) { logger.info("Task buildMetadata: Call metadata-generator.jar with arguments: " + metadataParams.toString().replaceAll(',', '')) args metadataParams.toArray() } - + doLast { copy { from "$projectDir/metadata/output/assets/metadata" @@ -699,7 +726,7 @@ generateTypescriptDefinitions.onlyIf { project.hasProperty("generateTypings") && Boolean.parseBoolean(project.generateTypings) } -def shouldIncludeDirForTypings(path, includeDirs) { +static def shouldIncludeDirForTypings(path, includeDirs) { for (String p: includeDirs) { if (path.indexOf(p) > -1) { return true; @@ -751,14 +778,14 @@ task setProperties { project.ext.jarFiles = [] doLast { def list = []; - allJarPaths.each({f -> + allJarPaths.each({f -> if(f.endsWith(".jar")) { list.add(f); } }) project.jarFiles = list; } -} +} setProperties.finalizedBy("asbg:generateBindings", generateTypescriptDefinitions) @@ -780,9 +807,6 @@ collectAllJars.dependsOn(ensureMetadataOutDir) buildMetadata.dependsOn(collectAllJars) generateTypescriptDefinitions.finalizedBy(copyTypings) -//DO NOT UNCOMMENT -// mergeAssets.dependsOn(copyMetadata) -> look in CONFIGURATIONS(top) in android.applicationVariants to see how it's done - task buildapk { // problem is compile dependencies need to be changed before configuration stage // and this is the only way so far @@ -807,19 +831,15 @@ task deleteFlavors (type: Delete){ doLast { def srcDir = new File("$projectDir/src") srcDir.listFiles().each({ f -> - if(!f.getName().equals("main") && - !f.getName().equals("debug") && - !f.getName().equals("release")) { + if(f.getName() != "main" && + f.getName() != "debug" && + f.getName() != "release") { delete f } }) } } -task deleteConfigurations (type: Delete) { - delete "$projectDir/configurations" -} - task deleteGeneratedBindings(type: Delete) { delete "$projectDir/src/main/java/com/tns/gen" } @@ -827,6 +847,5 @@ task deleteGeneratedBindings(type: Delete) { buildapk.finalizedBy("validateAppIdMatch"); deleteMetadata.dependsOn(":asbg:clean") deleteFlavors.dependsOn(deleteMetadata) -deleteConfigurations.dependsOn(deleteFlavors) -deleteGeneratedBindings.dependsOn(deleteConfigurations) +deleteGeneratedBindings.dependsOn(deleteFlavors) clean.dependsOn(deleteGeneratedBindings) From a34864e977794ce15d242f74b9975efd91d9692f Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Mon, 10 Jul 2017 12:39:08 +0300 Subject: [PATCH 2/7] update gitignore to ignore android studio-generated .iml files --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bf94681b7..797426ac9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,4 @@ bin/ .DS_Store .settings -build-artifacts/ \ No newline at end of file +*.iml From b0b85ff945746802dd818d0593e055857a0a2bcb Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Mon, 10 Jul 2017 12:39:47 +0300 Subject: [PATCH 3/7] add dependencies from node_modules as a separate task, slight refactoring job --- .../project-template-gradle/build.gradle | 475 ++++++++---------- 1 file changed, 217 insertions(+), 258 deletions(-) diff --git a/build-artifacts/project-template-gradle/build.gradle b/build-artifacts/project-template-gradle/build.gradle index c3a015712..d1820dc1e 100644 --- a/build-artifacts/project-template-gradle/build.gradle +++ b/build-artifacts/project-template-gradle/build.gradle @@ -20,7 +20,7 @@ import groovy.io.FileType -import groovy.json.JsonSlurper //used to parse package.json +import groovy.json.JsonSlurper import java.nio.file.Files; import java.nio.file.Paths; @@ -28,9 +28,9 @@ import java.nio.file.StandardOpenOption import java.util.regex.Pattern; buildscript { - repositories { - jcenter() - } + repositories { + jcenter() + } dependencies { classpath "com.android.tools.build:gradle:2.2.3" @@ -47,6 +47,9 @@ def nodeModulesDir = "../../node_modules/" def flavorNames = new ArrayList() def originalConfigurationsDir = new File(projectDir, "configurations") def configurationsDir = new File(projectDir, "build/configurations") +def dependenciesJsonPath = file("dependencies.json") +// TODO: Pete: Build script will not work with previous CLI versions +def nativescriptDependencies = new JsonSlurper().parseText(dependenciesJsonPath.text) def packageJsonContents = [:] def excludedDevDependencies = ['**/.bin/**'] @@ -56,41 +59,18 @@ def asbgProject = project(":asbg") asbgProject.ext.outDir = new File("$projectDir", "src/main/java") asbgProject.ext.jsCodeDir = new File("$projectDir", "src/main/assets/app") -def computeCompileSdkVersion () { - if(project.hasProperty("compileSdk")) { - return compileSdk - } - else { - return 23 - } -} - -def computeTargetSdkVersion() { - if(project.hasProperty("targetSdk")) { - return targetSdk - } - else { - return 23 - } -} - -def computeBuildToolsVersion() { - if(project.hasProperty("buildToolsVersion")) { - return buildToolsVersion - } - else { - return "25.0.2" - } -} +def computeCompileSdkVersion = { -> project.hasProperty("compileSdk") ? compileSdk : 23 } +def computeTargetSdkVersion = { -> project.hasProperty("targetSdk") ? targetSdk : 23 } +def computeBuildToolsVersion = { -> project.hasProperty("buildToolsVersion") ? buildToolsVersion : "25.0.2" } project.ext.selectedBuildType = project.hasProperty("release") ? "release" : "debug" -def renameResultApks (variant) { +def renameResultApks = { variant -> def name variant.outputs.each { output -> def apkDirectory = output.packageApplication.outputFile.parentFile def abiName = ""; - if(output.getFilter(com.android.build.OutputFile.ABI)) { + if (output.getFilter(com.android.build.OutputFile.ABI)) { abiName = "-" + output.getFilter(com.android.build.OutputFile.ABI); } def apkNamePrefix = rootProject.name + "-" + variant.buildType.name + abiName @@ -103,13 +83,13 @@ def renameResultApks (variant) { task getDevDependencies { doLast { println "$configStage getDevDependencies" - + String content = new File("$projectDir/../../package.json").getText("UTF-8") def jsonSlurper = new JsonSlurper() def packageJsonMap = jsonSlurper.parseText(content) - + packageJsonContents = packageJsonMap.devDependencies; - + packageJsonContents.each { entry -> excludedDevDependencies.add(entry.key + '/platforms/android/**/*.jar') } @@ -122,7 +102,7 @@ task getDevDependencies { android { compileSdkVersion computeCompileSdkVersion() buildToolsVersion computeBuildToolsVersion() - + defaultConfig { minSdkVersion 17 targetSdkVersion computeTargetSdkVersion() @@ -130,37 +110,37 @@ android { abiFilters "armeabi-v7a", "x86" } } - + sourceSets.main { - jniLibs.srcDir "$projectDir/libs/jni" - } - + jniLibs.srcDir "$projectDir/libs/jni" + } + signingConfigs { - release { - if(project.hasProperty("release")) { - if(project.hasProperty("ksPath") && - project.hasProperty("ksPassword") && - project.hasProperty("alias") && - project.hasProperty("password")) - { + release { + if (project.hasProperty("release")) { + if (project.hasProperty("ksPath") && + project.hasProperty("ksPassword") && + project.hasProperty("alias") && + project.hasProperty("password")) { + storeFile file(ksPath) storePassword ksPassword keyAlias alias keyPassword password } } - } - } - buildTypes { - release { - signingConfig signingConfigs.release - } - } - + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } + applicationVariants.all { variant -> renameResultApks(variant) } - + applicationVariants.all { variant -> def variantName = variant.name.capitalize() def compileSourcesTaskName = "compile${variantName}Sources" @@ -171,7 +151,7 @@ android { if(!dontRunSbg) { collectAllJars.finalizedBy(setProperties) } - + compileSourcesTask.finalizedBy(buildMetadata) // forces packaging of resources and assets AFTER producing metadata @@ -202,14 +182,17 @@ repositories { maven { url 'https://maven.google.com' } // used for local *.AAR files + def pluginDependencies = nativescriptDependencies.collect { "${it.directory}/platforms/android" } + pluginDependencies.add("libs/runtime-libs") + flatDir { - dirs 'libs/aar' + dirs pluginDependencies } } dependencies { def supportVer = "22.2.0"; - if(project.hasProperty("supportVersion")) { + if (project.hasProperty("supportVersion")) { supportVer = supportVersion } @@ -217,35 +200,46 @@ dependencies { compile "com.android.support:appcompat-v7:$supportVer" debugCompile "com.android.support:design:$supportVer" - // take all jars within the libs dir + // take all jars within the libs dir compile fileTree(dir: "$projectDir/libs", include: ["**/*.jar"]) // take all jars within the node_modules dir compile fileTree(dir: nodeModulesDir, include: ["**/platforms/android/**/*.jar"], exclude: excludedDevDependencies) +} +task addNativeScriptRuntimePackageDependency { + def useV8Symbols = nativescriptDependencies.any { + def packageJsonPath = file("${it.directory}/package.json"); + def packageJson = new JsonSlurper().parseText(packageJsonPath.text); + return packageJson.nativescript.useV8Symbols; + } - //when gradle has aar support this will be preferred instead of addAarDependencies - // compile files("$rootDir/libs/aar") { - // builtBy 'copyAarDependencies' - // } - copyNativeScriptAar() + def runtime = useV8Symbols ? "nativescript-regular" : "nativescript-optimized"; + println "\t+adding nativescript runtime package dependency: $runtime" - compile project(':runtime') + project.dependencies.add("compile", [name: runtime, ext: "aar"]) } - -def copyNativeScriptAar() { - def useV8SymbolsFlag = new File("$projectDir/build-tools/useV8"); - def runtimeAarType = "optimized"; - if (useV8SymbolsFlag.exists() && !useV8SymbolsFlag.isDirectory()) { - println "Using less-optimized runtime bundle."; - runtimeAarType = "regular"; +task addAarDependenciesFromNativeScriptPlugins { + nativescriptDependencies.each() { dep -> + def aarFiles = fileTree(dir: file("${dep.directory}/platforms/android"), include: ["**/*.aar"]) + aarFiles.each { aarFile -> + def length = aarFile.name.length() - 4 + def fileName = aarFile.name[0.. 0) { + if(!dimensionFound && oldContent.indexOf("dimension", index) == index) { + newContent += "dimension \"${dimensionName}\""; + dimensionFound = true; + index += "dimension ".length(); + def openingQuoutes = oldContent.indexOf('"', index); + if(openingQuoutes == -1) { + openingQuoutes = oldContent.indexOf("'", index); + } - if(level > 0) { - if(!dimensionFound && oldContent.indexOf("dimension", index) == index) { - newContent += "dimension \"${dimensionName}\""; - dimensionFound = true; - index += "dimension ".length(); - def openingQuoutes = oldContent.indexOf('"', index); - if(openingQuoutes == -1) { - openingQuoutes = oldContent.indexOf("'", index); - } + def closingQuotes = oldContent.indexOf('"', openingQuoutes + 1); + if(closingQuotes == -1) { + closingQuotes = oldContent.indexOf("'", openingQuoutes + 1); + } - def closingQuotes = oldContent.indexOf('"', openingQuoutes + 1); - if(closingQuotes == -1) { - closingQuotes = oldContent.indexOf("'", openingQuoutes + 1); + index = closingQuotes + 1; } - - index = closingQuotes + 1; } - } - newContent += oldContent[index]; + newContent += oldContent[index]; - index++; - } + index++; + } - return newContent; + return newContent; } static def createProductFlavorsContent(flavor, dimensionName, includeAndroidContent = true) { if (includeAndroidContent) - { - def content = """ + { + def content = """ android { productFlavors { "${flavor}" { @@ -319,24 +313,24 @@ android { } } """ - return content; + return content; } else { - def content = """ + def content = """ productFlavors { "${flavor}" { dimension "${dimensionName}" } } """ - return content; + return content; } } def createIncludeFile (filePath, flavor, dimensionName, destination) { - println "\t + creating include.gradle file for ${filePath}" - + println "\t + creating include.gradle file for ${filePath}" + def defaultIncludeFile = new File(destination, "include.gradle") defaultIncludeFile.text = createProductFlavorsContent(flavor, dimensionName); } @@ -373,28 +367,28 @@ static def modifyProductFlavorInContent(content, dimension, flavor) { } if (indexEnd != -1) - { + { // full content of productFlavors { ... } -> the substring is parenthesis to parenthesis -> { ... } - def oldProductFlavorsText = content.substring(indexStart, indexEnd + 1); - - def newProductFlavorsContent = updateProductFlavorsContent(flavor, dimension, oldProductFlavorsText); - - return content.replace(oldProductFlavorsText, newProductFlavorsContent); - } - else - { - def androidContentExists = content.indexOf("android {") != -1; - def newProductFlavorsContent = createProductFlavorsContent(flavor, dimension, !androidContentExists); + def oldProductFlavorsText = content.substring(indexStart, indexEnd + 1); - if (androidContentExists) - { - return content.replace("android {", "android { ${newProductFlavorsContent}"); - } - else - { - return "${newProductFlavorsContent} \t ${content}" - } - } + def newProductFlavorsContent = updateProductFlavorsContent(flavor, dimension, oldProductFlavorsText); + + return content.replace(oldProductFlavorsText, newProductFlavorsContent); + } + else + { + def androidContentExists = content.indexOf("android {") != -1; + def newProductFlavorsContent = createProductFlavorsContent(flavor, dimension, !androidContentExists); + + if (androidContentExists) + { + return content.replace("android {", "android { ${newProductFlavorsContent}"); + } + else + { + return "${newProductFlavorsContent} \t ${content}" + } + } } // make sure the include.gradle file provided by the user has only allowed characters in dimension attribute and remove any invalid characters if necessary @@ -429,14 +423,14 @@ static def getParentDirStructure(includeGradleFile) { } def renamePluginDirToFlavorName(directory, flavor) { - def parentName = directory.getName(); - def parentFile = new File("src", parentName); - if (parentFile.exists()) - { - def targetDirName = new File("src", flavor); - println "Renaming plugin directory to flavor name: ${parentFile.getAbsolutePath()} -> ${targetDirName.getAbsolutePath()}"; - parentFile.renameTo(targetDirName); - } + def parentName = directory.getName(); + def parentFile = new File("src", parentName); + if (parentFile.exists()) + { + def targetDirName = new File("src", flavor); + println "Renaming plugin directory to flavor name: ${parentFile.getAbsolutePath()} -> ${targetDirName.getAbsolutePath()}"; + parentFile.renameTo(targetDirName); + } } def flavorNumber = 0 @@ -446,29 +440,61 @@ task createDefaultIncludeFiles { println "$configStage createDefaultIncludeFiles" def fileTree = file(originalConfigurationsDir) - fileTree.listFiles().each { file -> - if (file.isDirectory()) { - shouldCreatePluginConfigFile = true - def hasChildrenDirs = false - file.listFiles().each { subFile -> - if (subFile.isDirectory()) { - hasChildrenDirs = true - } - } + def allFlavors = new ArrayList() + nativescriptDependencies.each { + def androidDir = file("${it.directory}/platforms/android") + if (!androidDir.exists()) { + return + } - // if plugin is scoped - traverse its children directories - // e.g. @scope/plugin-with-android-aars - if (hasChildrenDirs) { - file.listFiles().each { subFile -> - if (subFile.isDirectory()) { - createIncludeGradleForPlugin(subFile, flavorNumber++, flavorNames, configurationsDir) - } - } - } else { - createIncludeGradleForPlugin(file, flavorNumber++, flavorNames, configurationsDir) - } + def packageJsonPath = file("${it.directory}/package.json") + def packageJson = new JsonSlurper().parseText(packageJsonPath.text) + def pluginName = packageJson.name + def dimensionName = sanitizeDimensionName(pluginName) + flavorNames.add('"' + dimensionName + '"') + def flavor = "F${flavorNumber++}" + + def includeGradleFile = new File(androidDir, "include.gradle") + def destinationDir = file("${configurationsDir}/${pluginName}/") + Files.createDirectories(Paths.get(destinationDir.getAbsolutePath())) + + def destinationIncludeGradleFile = file("${configurationsDir}/${pluginName}/include.gradle") + def content; + if (includeGradleFile.exists()) { + println "\t + add include.gradle from ${includeGradleFile}" + content = modifyProductFlavorInContent(includeGradleFile.text, dimensionName, flavor) + } else { + println "\t + creating include.gradle for plugin ${file(it.directory)}" + content = createProductFlavorsContent(flavor, dimensionName) } - } + + destinationIncludeGradleFile.text = content + } + + // old logic copying from configurations/ into build/config and changing text content +// fileTree.listFiles().each { file -> +// if (file.isDirectory()) { +// shouldCreatePluginConfigFile = true +// def hasChildrenDirs = false +// file.listFiles().each { subFile -> +// if (subFile.isDirectory()) { +// hasChildrenDirs = true +// } +// } +// +// // if plugin is scoped - traverse its children directories +// // e.g. @scope/plugin-with-android-aars +// if (hasChildrenDirs) { +// file.listFiles().each { subFile -> +// if (subFile.isDirectory()) { +// createIncludeGradleForPlugin(subFile, flavorNumber++, flavorNames, configurationsDir) +// } +// } +// } else { +// createIncludeGradleForPlugin(file, flavorNumber++, flavorNames, configurationsDir) +// } +// } +// } createPluginsConfigFile(flavorNames, configurationsDir) } @@ -485,7 +511,7 @@ def createIncludeGradleForPlugin(file, flavorNumber, flavorNames, configurations def foundIncludeFile = false def fileName = file.name def dimensionName = sanitizeDimensionName(fileName) - + def flavor = "F" + flavorNumber println "\t+found plugins: " + fileName file.listFiles().each { subFile -> @@ -494,12 +520,12 @@ def createIncludeGradleForPlugin(file, flavorNumber, flavorNames, configurations def newIncludeFilePath = createPluginConfigurationPath(configurationsDir, subFile) Files.createDirectories(newIncludeFilePath) updateIncludeGradleFile(subFile, dimensionName, flavor, newIncludeFilePath) - renamePluginDirToFlavorName(dirToRename, flavor); + renamePluginDirToFlavorName(dirToRename, flavor); } } - + flavorNames.add('"' + dimensionName + '"') - + if (!foundIncludeFile) { def newIncludeFilePath = createPluginConfigurationPath(configurationsDir, file) Files.createDirectories(newIncludeFilePath) @@ -529,16 +555,8 @@ android { } task pluginExtend { - description "applies additional configuration" - - def pathToAppGradle = "$projectDir/../../app/App_Resources/Android/app.gradle" - def appGradle = file(pathToAppGradle) - if(appGradle.exists()) { - apply from: pathToAppGradle - } - if(configurationsDir.exists()) { - println "$configStage pluginExtend" + println "$configStage pluginExtend" configurationsDir.eachFileRecurse(FileType.FILES) { if(it.name == 'include.gradle') { println "\t+applying configuration from: " + it @@ -548,58 +566,6 @@ task pluginExtend { } } -//// doesn't work unless task is explicitly called (TODO: research configurations hook) -// addAarDependencies.dependsOn(copyAarDependencies) -// createDefaultIncludeFiles.dependsOn(addAarDependencies) -// createPluginsConfigFile.dependsOn(createDefaultIncludeFiles) -// pluginExtend.dependsOn(createPluginsConfigFile) - -//////////////////////////////////////////////////////////////////////////////////// -///////////////////////////// BEFORE EXECUTION ///////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////// - -// we need to copy all dependencies into a flat dir, as pointed by the repositories configurations at the top -task copyAarDependencies (type: Copy) { - def filterAarFilesFn = { path, attrs -> - String pathString = path.toString(); - def isBin = Pattern.matches(".*/\\.bin/.*", pathString); - def isAar = Pattern.matches(".*\\.aar\$", pathString); - return !isBin && isAar; - } - - def mapToStringFn = { path -> - return path.toString(); - } - - Object[] files = Files.find( - Paths.get("$projectDir", "$nodeModulesDir"), - Integer.MAX_VALUE, - filterAarFilesFn, - java.nio.file.FileVisitOption.FOLLOW_LINKS - ) - .map(mapToStringFn) - .toArray(); - - into "$projectDir/libs/aar" - from files -} - -task addAarDependencies { - doLast { - println "$configStage addAarDependencies" - // manually traverse all the locally copied AAR files and add them to the project compilation dependencies list - FileTree tree = fileTree(dir: "$projectDir/libs/aar", include: ["**/*.aar"]) - tree.each { File file -> - // remove the extension of the file (.aar) - def length = file.name.length() - 4 - def fileName = file.name[0.. logger.info("Task: collectAllJars: dependency file: " + dependencyFile.getAbsolutePath()) allJarPaths.add(dependencyFile.getAbsolutePath()) } - + allJarPaths.add(androidJar); - + def ft = fileTree(dir: explodedAarDir, include: "**/*.jar") ft.each { currentJarFile -> allJarPaths.add(currentJarFile.getAbsolutePath()) @@ -643,23 +609,23 @@ task collectAllJars { jars.add(f) } } - + asbgProject.ext.jarFiles = jars } } task buildMetadata (type: JavaExec) { description "builds metadata with provided jar dependencies" - + inputs.files(allJarPaths) inputs.dir("$buildDir/intermediates/classes") outputs.files("metadata/output/assets/metadata/treeNodeStream.dat", "metadata/output/assets/metadata/treeStringsStream.dat", "metadata/output/assets/metadata/treeValueStream.dat") doFirst { - // get compiled classes to pass to metadata generator - // these need to be called after the classes have compiled - def classesDir = "$buildDir/intermediates/classes" + // get compiled classes to pass to metadata generator + // these need to be called after the classes have compiled + def classesDir = "$buildDir/intermediates/classes" def classesSubDirs = new File(classesDir).listFiles() def selectedBuildType = project.ext.selectedBuildType @@ -684,7 +650,7 @@ task buildMetadata (type: JavaExec) { logger.info("Task buildMetadata: Call metadata-generator.jar with arguments: " + metadataParams.toString().replaceAll(',', '')) args metadataParams.toArray() } - + doLast { copy { from "$projectDir/metadata/output/assets/metadata" @@ -747,9 +713,7 @@ task copyTypings { } } -copyTypings.onlyIf({ - generateTypescriptDefinitions.didWork; -}) +copyTypings.onlyIf { generateTypescriptDefinitions.didWork } task validateAppIdMatch { doLast { @@ -778,14 +742,14 @@ task setProperties { project.ext.jarFiles = [] doLast { def list = []; - allJarPaths.each({f -> + allJarPaths.each({f -> if(f.endsWith(".jar")) { list.add(f); } }) project.jarFiles = list; } -} +} setProperties.finalizedBy("asbg:generateBindings", generateTypescriptDefinitions) @@ -808,11 +772,6 @@ buildMetadata.dependsOn(collectAllJars) generateTypescriptDefinitions.finalizedBy(copyTypings) task buildapk { - // problem is compile dependencies need to be changed before configuration stage - // and this is the only way so far - tasks.copyAarDependencies.execute() - tasks.addAarDependencies.execute() - //done to build only necessary apk if(project.hasProperty("release")) { dependsOn "assembleRelease" @@ -824,7 +783,7 @@ task buildapk { //////// custom clean /////////// task deleteMetadata (type: Delete){ - delete "$projectDir/metadata/output" + delete "$projectDir/metadata/output", "$projectDir/src/main/assets/metadata" } task deleteFlavors (type: Delete){ From efb85b7c759cf83adcc904e8da4643e819190073 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Tue, 11 Jul 2017 14:07:48 +0300 Subject: [PATCH 4/7] further refactor script copy flavor directories that may contain manifests and other resources from node_modules instead of relying on the CLI delete the configurations directory after applying flavors and dimensions --- .../project-template-gradle/build.gradle | 512 +++++++----------- 1 file changed, 205 insertions(+), 307 deletions(-) diff --git a/build-artifacts/project-template-gradle/build.gradle b/build-artifacts/project-template-gradle/build.gradle index d1820dc1e..4f3fd5327 100644 --- a/build-artifacts/project-template-gradle/build.gradle +++ b/build-artifacts/project-template-gradle/build.gradle @@ -18,19 +18,15 @@ * -PdontRunSbg=[true/false] */ - -import groovy.io.FileType import groovy.json.JsonSlurper import java.nio.file.Files; import java.nio.file.Paths; -import java.nio.file.StandardOpenOption -import java.util.regex.Pattern; buildscript { - repositories { - jcenter() - } + repositories { + jcenter() + } dependencies { classpath "com.android.tools.build:gradle:2.2.3" @@ -41,11 +37,8 @@ apply plugin: "com.android.application" def metadataParams = new LinkedList () def allJarPaths = new LinkedList () -def shouldCreatePluginConfigFile = false -def configStage = "\n:config phase: " +def configStage = "\tconfig phase: " def nodeModulesDir = "../../node_modules/" -def flavorNames = new ArrayList() -def originalConfigurationsDir = new File(projectDir, "configurations") def configurationsDir = new File(projectDir, "build/configurations") def dependenciesJsonPath = file("dependencies.json") // TODO: Pete: Build script will not work with previous CLI versions @@ -83,13 +76,13 @@ def renameResultApks = { variant -> task getDevDependencies { doLast { println "$configStage getDevDependencies" - + String content = new File("$projectDir/../../package.json").getText("UTF-8") def jsonSlurper = new JsonSlurper() def packageJsonMap = jsonSlurper.parseText(content) - + packageJsonContents = packageJsonMap.devDependencies; - + packageJsonContents.each { entry -> excludedDevDependencies.add(entry.key + '/platforms/android/**/*.jar') } @@ -102,7 +95,7 @@ task getDevDependencies { android { compileSdkVersion computeCompileSdkVersion() buildToolsVersion computeBuildToolsVersion() - + defaultConfig { minSdkVersion 17 targetSdkVersion computeTargetSdkVersion() @@ -110,18 +103,18 @@ android { abiFilters "armeabi-v7a", "x86" } } - + sourceSets.main { - jniLibs.srcDir "$projectDir/libs/jni" - } - + jniLibs.srcDir "$projectDir/libs/jni" + } + signingConfigs { - release { - if (project.hasProperty("release")) { + release { + if (project.hasProperty("release")) { if (project.hasProperty("ksPath") && - project.hasProperty("ksPassword") && - project.hasProperty("alias") && - project.hasProperty("password")) { + project.hasProperty("ksPassword") && + project.hasProperty("alias") && + project.hasProperty("password")) { storeFile file(ksPath) storePassword ksPassword @@ -129,18 +122,18 @@ android { keyPassword password } } - } - } - buildTypes { - release { - signingConfig signingConfigs.release - } - } - + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } + applicationVariants.all { variant -> renameResultApks(variant) } - + applicationVariants.all { variant -> def variantName = variant.name.capitalize() def compileSourcesTaskName = "compile${variantName}Sources" @@ -151,7 +144,7 @@ android { if(!dontRunSbg) { collectAllJars.finalizedBy(setProperties) } - + compileSourcesTask.finalizedBy(buildMetadata) // forces packaging of resources and assets AFTER producing metadata @@ -178,15 +171,20 @@ android { } repositories { +<<<<<<< b0b85ff945746802dd818d0593e055857a0a2bcb jcenter() maven { url 'https://maven.google.com' } // used for local *.AAR files +======= + jcenter() + // used for local *.AAR files +>>>>>>> further refactor script def pluginDependencies = nativescriptDependencies.collect { "${it.directory}/platforms/android" } pluginDependencies.add("libs/runtime-libs") - flatDir { - dirs pluginDependencies + flatDir { + dirs pluginDependencies } } @@ -200,13 +198,17 @@ dependencies { compile "com.android.support:appcompat-v7:$supportVer" debugCompile "com.android.support:design:$supportVer" - // take all jars within the libs dir + // take all jars within the libs dir compile fileTree(dir: "$projectDir/libs", include: ["**/*.jar"]) // take all jars within the node_modules dir compile fileTree(dir: nodeModulesDir, include: ["**/platforms/android/**/*.jar"], exclude: excludedDevDependencies) } +//////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// CONFIGURATION PHASE ////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + task addNativeScriptRuntimePackageDependency { def useV8Symbols = nativescriptDependencies.any { def packageJsonPath = file("${it.directory}/package.json"); @@ -215,96 +217,153 @@ task addNativeScriptRuntimePackageDependency { } def runtime = useV8Symbols ? "nativescript-regular" : "nativescript-optimized"; - println "\t+adding nativescript runtime package dependency: $runtime" + println "\t + adding nativescript runtime package dependency: $runtime" project.dependencies.add("compile", [name: runtime, ext: "aar"]) } -task addAarDependenciesFromNativeScriptPlugins { - nativescriptDependencies.each() { dep -> +task addDependenciesFromNativeScriptPlugins { + nativescriptDependencies.each { dep -> def aarFiles = fileTree(dir: file("${dep.directory}/platforms/android"), include: ["**/*.aar"]) aarFiles.each { aarFile -> def length = aarFile.name.length() - 4 def fileName = aarFile.name[0.. + println "\t + adding jar plugin dependency: " + jarFile.getAbsolutePath() + } + + project.dependencies.add("compile", jarFiles) } } +task applyPluginsIncludeGradleConfigurations { + def dimensions = [] + def includes = new ArrayList() + def flavorNumber = 0 + + nativescriptDependencies.each { dep -> + def androidDir = file("${dep.directory}/platforms/android") + if (!androidDir.exists()) { + return + } + + def packageJsonPath = file("${dep.directory}/package.json") + def packageJson = new JsonSlurper().parseText(packageJsonPath.text) + def pluginName = packageJson.name + def dimensionName = sanitizeDimensionName(pluginName) + + dimensions.add(dimensionName) + def flavor = "F${flavorNumber++}" + + def includeGradleFile = new File(androidDir, "include.gradle") + def destinationDir = file("${configurationsDir}/${pluginName}/") + def destinationIncludeGradleFile = file("${configurationsDir}/${pluginName}/include.gradle") + + Files.createDirectories(Paths.get(destinationDir.getAbsolutePath())) + + if (includeGradleFile.exists()) { + println "\t + add include.gradle from ${includeGradleFile}" + destinationIncludeGradleFile.text = modifyProductFlavorInContent(includeGradleFile.text, dimensionName, flavor) + } else { + println "\t + creating include.gradle for plugin ${file(dep.directory)}" + destinationIncludeGradleFile.text = createProductFlavorsContent(flavor, dimensionName) + } + + includes.add(destinationIncludeGradleFile.getAbsolutePath()); + + // TODO: Pete figure out a way to deal with this + copyAndRenamePluginDirToFlavorName(androidDir, flavor); + } + + includes.each { + println "\t + applying plugin configuration from ${it}" + apply from: it + } + + def concatenatedDimensions = dimensions.collect { "\"${it}\"" }.join(", "); + def includeFile = file("${configurationsDir}/include.gradle") + + includeFile.text = "android { flavorDimensions ${concatenatedDimensions} }" + + apply from: includeFile + + configurationsDir.deleteDir() +} + task applyAppGradleConfiguration { description "applies user-defined gradle configuration" def pathToAppGradle = "$projectDir/../../app/App_Resources/Android/app.gradle" def appGradle = file(pathToAppGradle) if(appGradle.exists()) { - println "\t+applying user-defined configuration from ${appGradle}" + println "\t + applying user-defined configuration from ${appGradle}" apply from: pathToAppGradle } } -//////////////////////////////////////////////////////////////////////////////////// -///////////////////////////// CONFIGURATION PHASE ////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////// - static def updateProductFlavorsContent(flavor, dimensionName, oldContent) { def endIndex = oldContent.length() - 1; - def index = 0; - def newContent = ""; - def level = -1; - def dimensionFound = false; - - while(index <= endIndex) { - if(level == 0 && (oldContent[index] == '"' || oldContent[index] == "'")) { - def closingQuotes = oldContent.indexOf('"', index + 1); - if(closingQuotes == -1) { - closingQuotes = oldContent.indexOf("'", index + 1); - } - - index = closingQuotes + 1; - newContent += "\"${flavor}\""; - continue; + def index = 0; + def newContent = ""; + def level = -1; + def dimensionFound = false; + + while(index <= endIndex) { + if(level == 0 && (oldContent[index] == '"' || oldContent[index] == "'")) { + def closingQuotes = oldContent.indexOf('"', index + 1); + if(closingQuotes == -1) { + closingQuotes = oldContent.indexOf("'", index + 1); } - if(oldContent[index] == "{") { - level++; - } + index = closingQuotes + 1; + newContent += "\"${flavor}\""; + continue; + } - if(oldContent[index] == "}") { - level--; - } + if(oldContent[index] == "{") { + level++; + } + + if(oldContent[index] == "}") { + level--; + } - if(level > 0) { - if(!dimensionFound && oldContent.indexOf("dimension", index) == index) { - newContent += "dimension \"${dimensionName}\""; - dimensionFound = true; - index += "dimension ".length(); - def openingQuoutes = oldContent.indexOf('"', index); - if(openingQuoutes == -1) { - openingQuoutes = oldContent.indexOf("'", index); - } - - def closingQuotes = oldContent.indexOf('"', openingQuoutes + 1); - if(closingQuotes == -1) { - closingQuotes = oldContent.indexOf("'", openingQuoutes + 1); - } - - index = closingQuotes + 1; + if(level > 0) { + if(!dimensionFound && oldContent.indexOf("dimension", index) == index) { + newContent += "dimension \"${dimensionName}\""; + dimensionFound = true; + index += "dimension ".length(); + def openingQuoutes = oldContent.indexOf('"', index); + if(openingQuoutes == -1) { + openingQuoutes = oldContent.indexOf("'", index); } - } - newContent += oldContent[index]; + def closingQuotes = oldContent.indexOf('"', openingQuoutes + 1); + if(closingQuotes == -1) { + closingQuotes = oldContent.indexOf("'", openingQuoutes + 1); + } - index++; + index = closingQuotes + 1; + } } - return newContent; + newContent += oldContent[index]; + + index++; + } + + return newContent; } static def createProductFlavorsContent(flavor, dimensionName, includeAndroidContent = true) { if (includeAndroidContent) - { - def content = """ + { + def content = """ android { productFlavors { "${flavor}" { @@ -313,28 +372,21 @@ android { } } """ - return content; + return content; } else { - def content = """ + def content = """ productFlavors { "${flavor}" { dimension "${dimensionName}" } } """ - return content; + return content; } } -def createIncludeFile (filePath, flavor, dimensionName, destination) { - println "\t + creating include.gradle file for ${filePath}" - - def defaultIncludeFile = new File(destination, "include.gradle") - defaultIncludeFile.text = createProductFlavorsContent(flavor, dimensionName); -} - static def sanitizeDimensionName(str) { return str.replaceAll(/\W/, "") } @@ -367,213 +419,67 @@ static def modifyProductFlavorInContent(content, dimension, flavor) { } if (indexEnd != -1) - { + { // full content of productFlavors { ... } -> the substring is parenthesis to parenthesis -> { ... } - def oldProductFlavorsText = content.substring(indexStart, indexEnd + 1); + def oldProductFlavorsText = content.substring(indexStart, indexEnd + 1); - def newProductFlavorsContent = updateProductFlavorsContent(flavor, dimension, oldProductFlavorsText); - - return content.replace(oldProductFlavorsText, newProductFlavorsContent); - } - else - { - def androidContentExists = content.indexOf("android {") != -1; - def newProductFlavorsContent = createProductFlavorsContent(flavor, dimension, !androidContentExists); - - if (androidContentExists) - { - return content.replace("android {", "android { ${newProductFlavorsContent}"); - } - else - { - return "${newProductFlavorsContent} \t ${content}" - } - } -} + def newProductFlavorsContent = updateProductFlavorsContent(flavor, dimension, oldProductFlavorsText); -// make sure the include.gradle file provided by the user has only allowed characters in dimension attribute and remove any invalid characters if necessary -def updateIncludeGradleFile(targetFile, dimensionName, flavor, destination) { - def fileEntry = new File(targetFile.getAbsolutePath()); - def content = fileEntry.text; - def replacedContent = modifyProductFlavorInContent(content, dimensionName, flavor); - Files.write(Paths.get(destination.toString(), "include.gradle"), replacedContent.getBytes(), StandardOpenOption.CREATE); -} - -static def createPluginConfigurationPath(configurationsDir, originalIncludeGradle) { - return java.nio.file.FileSystems.getDefault().getPath(configurationsDir.getAbsolutePath(), getParentDirStructure(originalIncludeGradle) as String[]); -} + return content.replace(oldProductFlavorsText, newProductFlavorsContent); + } + else + { + def androidContentExists = content.indexOf("android {") != -1; + def newProductFlavorsContent = createProductFlavorsContent(flavor, dimension, !androidContentExists); -static def getParentDirStructure(includeGradleFile) { - def parents = [] - def canonicaFile = includeGradleFile.getCanonicalFile(); - def parentDir = canonicaFile.getParentFile() - def parentDirName = parentDir.name - parents.add(parentDirName) - while (true) { - parentDir = parentDir.getCanonicalFile().getParentFile() - parentDirName = parentDir.name - if (parentDirName == "configurations") { - break + if (androidContentExists) + { + return content.replace("android {", "android { ${newProductFlavorsContent}"); + } + else + { + return "${newProductFlavorsContent} \t ${content}" } - - parents.add(parentDirName) } - - return parents.reverse() } -def renamePluginDirToFlavorName(directory, flavor) { - def parentName = directory.getName(); - def parentFile = new File("src", parentName); - if (parentFile.exists()) - { - def targetDirName = new File("src", flavor); - println "Renaming plugin directory to flavor name: ${parentFile.getAbsolutePath()} -> ${targetDirName.getAbsolutePath()}"; - parentFile.renameTo(targetDirName); - } -} +def copyFolder(source, destination) { + if (source.isDirectory()) { + Files.createDirectories(destination.toPath()); -def flavorNumber = 0 + def sourceFiles = source.list(); -task createDefaultIncludeFiles { - description "creates default include.gradle files for added plugins IF NECESSARY" - println "$configStage createDefaultIncludeFiles" - def fileTree = file(originalConfigurationsDir) + sourceFiles.each { file -> + def srcFile = new File(source, file); + def destFile = new File(destination, file); - def allFlavors = new ArrayList() - nativescriptDependencies.each { - def androidDir = file("${it.directory}/platforms/android") - if (!androidDir.exists()) { - return + //Recursive function call + copyFolder(srcFile, destFile); } - - def packageJsonPath = file("${it.directory}/package.json") - def packageJson = new JsonSlurper().parseText(packageJsonPath.text) - def pluginName = packageJson.name - def dimensionName = sanitizeDimensionName(pluginName) - flavorNames.add('"' + dimensionName + '"') - def flavor = "F${flavorNumber++}" - - def includeGradleFile = new File(androidDir, "include.gradle") - def destinationDir = file("${configurationsDir}/${pluginName}/") - Files.createDirectories(Paths.get(destinationDir.getAbsolutePath())) - - def destinationIncludeGradleFile = file("${configurationsDir}/${pluginName}/include.gradle") - def content; - if (includeGradleFile.exists()) { - println "\t + add include.gradle from ${includeGradleFile}" - content = modifyProductFlavorInContent(includeGradleFile.text, dimensionName, flavor) - } else { - println "\t + creating include.gradle for plugin ${file(it.directory)}" - content = createProductFlavorsContent(flavor, dimensionName) - } - - destinationIncludeGradleFile.text = content - } - - // old logic copying from configurations/ into build/config and changing text content -// fileTree.listFiles().each { file -> -// if (file.isDirectory()) { -// shouldCreatePluginConfigFile = true -// def hasChildrenDirs = false -// file.listFiles().each { subFile -> -// if (subFile.isDirectory()) { -// hasChildrenDirs = true -// } -// } -// -// // if plugin is scoped - traverse its children directories -// // e.g. @scope/plugin-with-android-aars -// if (hasChildrenDirs) { -// file.listFiles().each { subFile -> -// if (subFile.isDirectory()) { -// createIncludeGradleForPlugin(subFile, flavorNumber++, flavorNames, configurationsDir) -// } -// } -// } else { -// createIncludeGradleForPlugin(file, flavorNumber++, flavorNames, configurationsDir) -// } -// } -// } - - createPluginsConfigFile(flavorNames, configurationsDir) -} - -def createIncludeGradleForPlugin(file, flavorNumber, flavorNames, configurationsDir) { - def parentDir = new File(file.getParent()) - def parentName = parentDir.name - def dirToRename = file - - if (parentName.indexOf("@") == 0) { - dirToRename = file(parentName + "_" + file.name) - } - - def foundIncludeFile = false - def fileName = file.name - def dimensionName = sanitizeDimensionName(fileName) - - def flavor = "F" + flavorNumber - println "\t+found plugins: " + fileName - file.listFiles().each { subFile -> - if (subFile.name == "include.gradle") { - foundIncludeFile = true - def newIncludeFilePath = createPluginConfigurationPath(configurationsDir, subFile) - Files.createDirectories(newIncludeFilePath) - updateIncludeGradleFile(subFile, dimensionName, flavor, newIncludeFilePath) - renamePluginDirToFlavorName(dirToRename, flavor); - } - } - - flavorNames.add('"' + dimensionName + '"') - - if (!foundIncludeFile) { - def newIncludeFilePath = createPluginConfigurationPath(configurationsDir, file) - Files.createDirectories(newIncludeFilePath) - createIncludeFile(file.getAbsolutePath(), destination, flavor, dimensionName) - renamePluginDirToFlavorName(dirToRename, flavor); } -} - -def createPluginsConfigFile(flavorNames, configurationsDir) { - description "creates product flavor config file based on what plugins are added" + else { + // Copy the file content from one place to another + def fileName = source.getName() + def extension = fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0 ? fileName.substring(fileName.lastIndexOf(".") + 1) : ""; + // exclude aars from package, as we've already included it in the compile dependencies, and don't want it taking up space + if (extension == "aar") { + return + } - if (!configurationsDir.exists() || flavorNames.isEmpty()) { - return + Files.copy(source.toPath(), destination.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); } - - println "\t Creating product flavors include.gradle file in $configurationsDir folder..." - - def flavorsFile = new File(configurationsDir, "include.gradle") - def flavors = flavorNames.join(", ") - def content = """ -android { - flavorDimensions ${flavors} } -""" - flavorsFile.text = content -} +def copyAndRenamePluginDirToFlavorName(directory, flavor) { + def targetDir = file("src/${flavor}") -task pluginExtend { - if(configurationsDir.exists()) { - println "$configStage pluginExtend" - configurationsDir.eachFileRecurse(FileType.FILES) { - if(it.name == 'include.gradle') { - println "\t+applying configuration from: " + it - apply from: it - } - } - } + copyFolder(directory, targetDir) } //////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// EXECUTUION PHASE ///////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// -task cleanLocalAarFiles(type: Delete) { - delete fileTree(dir: "$projectDir/libs/aar", include: ["*.aar"]) -} - task ensureMetadataOutDir { def outputDir = file("$projectDir/metadata/output/assets/metadata") outputDir.mkdirs() @@ -581,19 +487,19 @@ task ensureMetadataOutDir { task collectAllJars { description "gathers all paths to jar dependencies before building metadata with them" - + def explodedAarDir = project.buildDir.getAbsolutePath() + "/intermediates/exploded-aar/" def sdkPath = android.sdkDirectory.getAbsolutePath(); def androidJar = sdkPath + "/platforms/" + android.compileSdkVersion + "/android.jar" - + doFirst { configurations.compile.each { File dependencyFile -> logger.info("Task: collectAllJars: dependency file: " + dependencyFile.getAbsolutePath()) allJarPaths.add(dependencyFile.getAbsolutePath()) } - + allJarPaths.add(androidJar); - + def ft = fileTree(dir: explodedAarDir, include: "**/*.jar") ft.each { currentJarFile -> allJarPaths.add(currentJarFile.getAbsolutePath()) @@ -609,23 +515,23 @@ task collectAllJars { jars.add(f) } } - + asbgProject.ext.jarFiles = jars } } task buildMetadata (type: JavaExec) { description "builds metadata with provided jar dependencies" - + inputs.files(allJarPaths) inputs.dir("$buildDir/intermediates/classes") outputs.files("metadata/output/assets/metadata/treeNodeStream.dat", "metadata/output/assets/metadata/treeStringsStream.dat", "metadata/output/assets/metadata/treeValueStream.dat") doFirst { - // get compiled classes to pass to metadata generator - // these need to be called after the classes have compiled - def classesDir = "$buildDir/intermediates/classes" + // get compiled classes to pass to metadata generator + // these need to be called after the classes have compiled + def classesDir = "$buildDir/intermediates/classes" def classesSubDirs = new File(classesDir).listFiles() def selectedBuildType = project.ext.selectedBuildType @@ -650,7 +556,7 @@ task buildMetadata (type: JavaExec) { logger.info("Task buildMetadata: Call metadata-generator.jar with arguments: " + metadataParams.toString().replaceAll(',', '')) args metadataParams.toArray() } - + doLast { copy { from "$projectDir/metadata/output/assets/metadata" @@ -738,18 +644,19 @@ task validateAppIdMatch { //////////////////////////////////////////////////////////////////////////////////// ////////////////////////////// OPTIONAL TASKS ////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// + task setProperties { project.ext.jarFiles = [] doLast { def list = []; - allJarPaths.each({f -> + allJarPaths.each({f -> if(f.endsWith(".jar")) { list.add(f); } }) project.jarFiles = list; } -} +} setProperties.finalizedBy("asbg:generateBindings", generateTypescriptDefinitions) @@ -757,16 +664,6 @@ setProperties.finalizedBy("asbg:generateBindings", generateTypescriptDefinitions ////////////////////////////// EXECUTION ORDER ///////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// -// -- configuration phase -// 1. Copy *.aar dependencies -// 2. Add *.aar dependencies -// 3. create default include files -// 4. create plugins config file -// 5. plugin extend (apply from include files) - -// --execution phase - -ensureMetadataOutDir.dependsOn(cleanLocalAarFiles) collectAllJars.dependsOn(ensureMetadataOutDir) buildMetadata.dependsOn(collectAllJars) generateTypescriptDefinitions.finalizedBy(copyTypings) @@ -790,9 +687,10 @@ task deleteFlavors (type: Delete){ doLast { def srcDir = new File("$projectDir/src") srcDir.listFiles().each({ f -> - if(f.getName() != "main" && - f.getName() != "debug" && - f.getName() != "release") { + def dirName = f.getName() + if (dirName != "main" && + dirName != "debug" && + dirName != "release") { delete f } }) From 2db2fdf20ab75987079f2e5dfad9835a95c44f88 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Thu, 13 Jul 2017 11:11:19 +0300 Subject: [PATCH 5/7] refactor asbg gradle project variable names remove redundant runtime gradle project script throw exception if build script is used with a CLI that doesn't produce 'dependencies.json' --- .../project/build.gradle | 55 ++--- .../runtime-modules/runtime/build.gradle | 2 - .../project-template-gradle/build.gradle | 201 +++++++++--------- 3 files changed, 132 insertions(+), 126 deletions(-) delete mode 100644 build-artifacts/project-template-gradle/build-tools/runtime-modules/runtime/build.gradle diff --git a/android-static-binding-generator/project/build.gradle b/android-static-binding-generator/project/build.gradle index 80578d915..adf3d0c26 100644 --- a/android-static-binding-generator/project/build.gradle +++ b/android-static-binding-generator/project/build.gradle @@ -4,10 +4,10 @@ import groovy.json.JsonSlurper import groovy.io.FileType +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths -def isWinOs = System.properties['os.name'].toLowerCase().contains('windows') - -def astParserDir = "$projectDir/parser" def interfaceNamesFileP = "$projectDir/interfaces-names.txt" def bindingsFileP = "$projectDir/bindings.txt" def cachedJarsFilePath = "$projectDir/cached.txt" @@ -30,13 +30,12 @@ if (workersExcludeFile.exists()) { def absoluteOutDir; if (project.hasProperty("outDir")) { absoluteOutDir = project.outDir; - + if (!absoluteOutDir.exists()) { absoluteOutDir.mkdirs() } } - // def absoluteJsCodeDir = new File("./jsCodeDir").getAbsolutePath()//project.jsCodeDir def absoluteJsCodeDir; def jsCodeAbsolutePath; @@ -45,31 +44,30 @@ if (!project.hasProperty("test")) { jsCodeAbsolutePath = absoluteJsCodeDir.getAbsolutePath() } -def utf8 = java.nio.charset.StandardCharsets.UTF_8 -def current = "" +def utf8 = StandardCharsets.UTF_8 +def jarsList = "" def shouldRun = true def rootTraversed = false; def inputJsFiles = new LinkedList (); // depends on passed jars and generated interface-names -task generateInterfaceNamesList() { - +task generateInterfaceNamesList { doFirst { if(!project.hasProperty("test")) { - current = project.jarFiles + jarsList = project.jarFiles def cache = new File(cachedJarsFilePath) - + if (cache.exists()) { def contents = new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(cachedJarsFilePath)), utf8).trim() - shouldRun = !contents.equals(current.toString()) + shouldRun = !contents.equals(jarsList.toString()) } if (shouldRun) { javaexec { main "-jar" - def jarsAsStr = current.toString(); + def jarsAsStr = jarsList.toString(); def jarsArr = jarsAsStr.replaceAll(/[\[\]]/, "").split(", ") def str = new LinkedList (); @@ -80,7 +78,7 @@ task generateInterfaceNamesList() { args str.toArray() } - java.nio.file.Files.write(java.nio.file.Paths.get(cachedJarsFilePath), [current.toString()], utf8) + Files.write(Paths.get(cachedJarsFilePath), [jarsList.toString()], utf8) } } } @@ -95,9 +93,7 @@ def isWorkerScript = { fileName -> } def traverseDirectory - traverseDirectory = { dir, traverseExplicitly -> - def currentDir = new File(dir) def pJsonFile = false; @@ -141,14 +137,23 @@ traverseDirectory = { dir, traverseExplicitly -> } } -task traverseJsFilesArgs << { //(jsCodeDir, bindingsFilePath, interfaceNamesFilePath, jsParserPath, jsFilesParameter) { - jsCodeAbsolutePath = jsCodeDir; - inputJsFiles = new LinkedList(); - traverseDirectory(jsCodeDir, false); +task traverseJsFilesArgs { //(jsCodeDir, bindingsFilePath, interfaceNamesFilePath, jsParserPath, jsFilesParameter) { + doLast { + jsCodeAbsolutePath = jsCodeDir; + inputJsFiles = new LinkedList(); + traverseDirectory(jsCodeDir, false); - new File(jsFilesParameter).withWriter { out -> - inputJsFiles.each {out.println it} - } + new File(jsFilesParameter).withWriter { out -> + inputJsFiles.each {out.println it} + } + + def list = new ArrayList(); + list.add("node") + list.add(jsParserPath) + list.add(jsCodeDir) + list.add(bindingsFilePath) + list.add(interfaceNamesFilePath) + list.add(jsFilesParameter) def list = new ArrayList(); list.add("node") @@ -187,7 +192,7 @@ task runAstParser(type: RunAstParserTask) { // traverses the javascript code input directory // 1. traverses all root directory files // 2. all subdirectories that do not have a package.json containing a "nativescript" key are skipped -task traverseJsFiles () { +task traverseJsFiles { doFirst { // invalidate previously generated bindings.txt file // todo: remove when removing previously generated bindings is implemented @@ -285,7 +290,7 @@ task generateBindings() { str.add(bindingsFileP) str.add(absoluteOutDir) - def jarsAsStr = current.toString(); + def jarsAsStr = jarsList.toString(); def jarsArr = jarsAsStr.replaceAll(/[\[\]]/, "").split(", ") str.addAll(jarsArr) diff --git a/build-artifacts/project-template-gradle/build-tools/runtime-modules/runtime/build.gradle b/build-artifacts/project-template-gradle/build-tools/runtime-modules/runtime/build.gradle deleted file mode 100644 index 8f46fc21e..000000000 --- a/build-artifacts/project-template-gradle/build-tools/runtime-modules/runtime/build.gradle +++ /dev/null @@ -1,2 +0,0 @@ -configurations.maybeCreate("default") -artifacts.add("default", file('../../../libs/runtime-libs/nativescript.aar')) \ No newline at end of file diff --git a/build-artifacts/project-template-gradle/build.gradle b/build-artifacts/project-template-gradle/build.gradle index 4f3fd5327..f2161e704 100644 --- a/build-artifacts/project-template-gradle/build.gradle +++ b/build-artifacts/project-template-gradle/build.gradle @@ -1,6 +1,6 @@ /* * Script builds apk in release or debug mode -* To run: +* To run: * gradle buildapk -Prelease (release mode) * gradle buildapk (debug mode -> default) * Options: @@ -24,9 +24,9 @@ import java.nio.file.Files; import java.nio.file.Paths; buildscript { - repositories { - jcenter() - } + repositories { + jcenter() + } dependencies { classpath "com.android.tools.build:gradle:2.2.3" @@ -40,9 +40,18 @@ def allJarPaths = new LinkedList () def configStage = "\tconfig phase: " def nodeModulesDir = "../../node_modules/" def configurationsDir = new File(projectDir, "build/configurations") -def dependenciesJsonPath = file("dependencies.json") -// TODO: Pete: Build script will not work with previous CLI versions -def nativescriptDependencies = new JsonSlurper().parseText(dependenciesJsonPath.text) +def dependenciesJson = file("dependencies.json") + +// the build script will not work with previous versions of the CLI (3.1 or earlier) +if (!dependenciesJson.exists()) { + throw new BuildCancelledException(""" +'dependencies.json' file not found. Check whether the NativeScript CLI has prepared the project beforehand, +and that your NativeScript version is 3.2, or a more recent one. To build an android project with the current +version of the {N} CLI install a previous version of the runtime package - 'tns platform add android@3.1'. +""") +} + +def nativescriptDependencies = new JsonSlurper().parseText(dependenciesJson.text) def packageJsonContents = [:] def excludedDevDependencies = ['**/.bin/**'] @@ -105,16 +114,16 @@ android { } sourceSets.main { - jniLibs.srcDir "$projectDir/libs/jni" - } + jniLibs.srcDir "$projectDir/libs/jni" + } signingConfigs { - release { - if (project.hasProperty("release")) { + release { + if (project.hasProperty("release")) { if (project.hasProperty("ksPath") && - project.hasProperty("ksPassword") && - project.hasProperty("alias") && - project.hasProperty("password")) { + project.hasProperty("ksPassword") && + project.hasProperty("alias") && + project.hasProperty("password")) { storeFile file(ksPath) storePassword ksPassword @@ -122,13 +131,13 @@ android { keyPassword password } } - } - } - buildTypes { - release { - signingConfig signingConfigs.release - } - } + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } applicationVariants.all { variant -> renameResultApks(variant) @@ -171,20 +180,15 @@ android { } repositories { -<<<<<<< b0b85ff945746802dd818d0593e055857a0a2bcb jcenter() maven { url 'https://maven.google.com' } // used for local *.AAR files -======= - jcenter() - // used for local *.AAR files ->>>>>>> further refactor script def pluginDependencies = nativescriptDependencies.collect { "${it.directory}/platforms/android" } pluginDependencies.add("libs/runtime-libs") - flatDir { - dirs pluginDependencies + flatDir { + dirs pluginDependencies } } @@ -194,11 +198,11 @@ dependencies { supportVer = supportVersion } - compile "com.android.support:support-v4:$supportVer" - compile "com.android.support:appcompat-v7:$supportVer" + compile "com.android.support:support-v4:$supportVer" + compile "com.android.support:appcompat-v7:$supportVer" debugCompile "com.android.support:design:$supportVer" - // take all jars within the libs dir + // take all jars within the libs dir compile fileTree(dir: "$projectDir/libs", include: ["**/*.jar"]) // take all jars within the node_modules dir @@ -276,7 +280,6 @@ task applyPluginsIncludeGradleConfigurations { includes.add(destinationIncludeGradleFile.getAbsolutePath()); - // TODO: Pete figure out a way to deal with this copyAndRenamePluginDirToFlavorName(androidDir, flavor); } @@ -308,62 +311,62 @@ task applyAppGradleConfiguration { static def updateProductFlavorsContent(flavor, dimensionName, oldContent) { def endIndex = oldContent.length() - 1; - def index = 0; - def newContent = ""; - def level = -1; - def dimensionFound = false; - - while(index <= endIndex) { - if(level == 0 && (oldContent[index] == '"' || oldContent[index] == "'")) { - def closingQuotes = oldContent.indexOf('"', index + 1); - if(closingQuotes == -1) { - closingQuotes = oldContent.indexOf("'", index + 1); - } - - index = closingQuotes + 1; - newContent += "\"${flavor}\""; - continue; - } + def index = 0; + def newContent = ""; + def level = -1; + def dimensionFound = false; + + while(index <= endIndex) { + if (level == 0 && (oldContent[index] == '"' || oldContent[index] == "'")) { + def closingQuotes = oldContent.indexOf('"', index + 1); + if (closingQuotes == -1) { + closingQuotes = oldContent.indexOf("'", index + 1); + } - if(oldContent[index] == "{") { - level++; - } + index = closingQuotes + 1; + newContent += "\"${flavor}\""; + continue; + } - if(oldContent[index] == "}") { - level--; - } + if (oldContent[index] == "{") { + level++; + } - if(level > 0) { - if(!dimensionFound && oldContent.indexOf("dimension", index) == index) { - newContent += "dimension \"${dimensionName}\""; - dimensionFound = true; - index += "dimension ".length(); - def openingQuoutes = oldContent.indexOf('"', index); - if(openingQuoutes == -1) { - openingQuoutes = oldContent.indexOf("'", index); - } + if (oldContent[index] == "}") { + level--; + } - def closingQuotes = oldContent.indexOf('"', openingQuoutes + 1); - if(closingQuotes == -1) { - closingQuotes = oldContent.indexOf("'", openingQuoutes + 1); + if (level > 0) { + if (!dimensionFound && oldContent.indexOf("dimension", index) == index) { + newContent += "dimension \"${dimensionName}\""; + dimensionFound = true; + index += "dimension ".length(); + def openingQuoutes = oldContent.indexOf('"', index); + if (openingQuoutes == -1) { + openingQuoutes = oldContent.indexOf("'", index); + } + + def closingQuotes = oldContent.indexOf('"', openingQuoutes + 1); + if (closingQuotes == -1) { + closingQuotes = oldContent.indexOf("'", openingQuoutes + 1); + } + + index = closingQuotes + 1; } - - index = closingQuotes + 1; } - } - newContent += oldContent[index]; + newContent += oldContent[index]; - index++; - } + index++; + } - return newContent; + return newContent; } static def createProductFlavorsContent(flavor, dimensionName, includeAndroidContent = true) { if (includeAndroidContent) - { - def content = """ + { + def content = """ android { productFlavors { "${flavor}" { @@ -372,18 +375,18 @@ android { } } """ - return content; + return content; } else { - def content = """ + def content = """ productFlavors { "${flavor}" { dimension "${dimensionName}" } } """ - return content; + return content; } } @@ -419,28 +422,28 @@ static def modifyProductFlavorInContent(content, dimension, flavor) { } if (indexEnd != -1) - { + { // full content of productFlavors { ... } -> the substring is parenthesis to parenthesis -> { ... } - def oldProductFlavorsText = content.substring(indexStart, indexEnd + 1); + def oldProductFlavorsText = content.substring(indexStart, indexEnd + 1); - def newProductFlavorsContent = updateProductFlavorsContent(flavor, dimension, oldProductFlavorsText); + def newProductFlavorsContent = updateProductFlavorsContent(flavor, dimension, oldProductFlavorsText); - return content.replace(oldProductFlavorsText, newProductFlavorsContent); - } - else - { - def androidContentExists = content.indexOf("android {") != -1; - def newProductFlavorsContent = createProductFlavorsContent(flavor, dimension, !androidContentExists); - - if (androidContentExists) - { - return content.replace("android {", "android { ${newProductFlavorsContent}"); - } - else - { - return "${newProductFlavorsContent} \t ${content}" - } - } + return content.replace(oldProductFlavorsText, newProductFlavorsContent); + } + else + { + def androidContentExists = content.indexOf("android {") != -1; + def newProductFlavorsContent = createProductFlavorsContent(flavor, dimension, !androidContentExists); + + if (androidContentExists) + { + return content.replace("android {", "android { ${newProductFlavorsContent}"); + } + else + { + return "${newProductFlavorsContent} \t ${content}" + } + } } def copyFolder(source, destination) { @@ -508,7 +511,7 @@ task collectAllJars { metadataParams.add("metadata-generator.jar"); metadataParams.add("$projectDir/metadata/output/assets/metadata"); def jars = new LinkedList() - for(def i = 0; i < allJarPaths.size(); i++) { + for (def i = 0; i < allJarPaths.size(); i++) { metadataParams.add(allJarPaths.get(i)); def f = new File(allJarPaths.get(i)) if (f.getName().endsWith(".jar")) { @@ -670,7 +673,7 @@ generateTypescriptDefinitions.finalizedBy(copyTypings) task buildapk { //done to build only necessary apk - if(project.hasProperty("release")) { + if (project.hasProperty("release")) { dependsOn "assembleRelease" } else { From c290946b9201c421cc304734668931e8fc7e2119 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Wed, 2 Aug 2017 18:32:24 +0300 Subject: [PATCH 6/7] add flavorDimensions to build in place, instead of writing to a file --- .../project-template-gradle/build.gradle | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/build-artifacts/project-template-gradle/build.gradle b/build-artifacts/project-template-gradle/build.gradle index f2161e704..c4c2167e5 100644 --- a/build-artifacts/project-template-gradle/build.gradle +++ b/build-artifacts/project-template-gradle/build.gradle @@ -246,6 +246,8 @@ task addDependenciesFromNativeScriptPlugins { } task applyPluginsIncludeGradleConfigurations { + configurationsDir.deleteDir() + def dimensions = [] def includes = new ArrayList() def flavorNumber = 0 @@ -283,27 +285,22 @@ task applyPluginsIncludeGradleConfigurations { copyAndRenamePluginDirToFlavorName(androidDir, flavor); } + flavorDimensions(*dimensions) + includes.each { println "\t + applying plugin configuration from ${it}" apply from: it } - - def concatenatedDimensions = dimensions.collect { "\"${it}\"" }.join(", "); - def includeFile = file("${configurationsDir}/include.gradle") - - includeFile.text = "android { flavorDimensions ${concatenatedDimensions} }" - - apply from: includeFile - - configurationsDir.deleteDir() } +applyPluginsIncludeGradleConfigurations.finalizedBy(applyAppGradleConfiguration) + task applyAppGradleConfiguration { description "applies user-defined gradle configuration" def pathToAppGradle = "$projectDir/../../app/App_Resources/Android/app.gradle" def appGradle = file(pathToAppGradle) - if(appGradle.exists()) { + if (appGradle.exists()) { println "\t + applying user-defined configuration from ${appGradle}" apply from: pathToAppGradle } From 86b9d0821ccb46f681eca5f8374809e19dbb41d1 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Thu, 28 Sep 2017 18:33:06 +0300 Subject: [PATCH 7/7] clean up after rebase to master make plugin config tasks into functions which are called during gradle's config step --- .../project/build.gradle | 30 ++-- .../project/parser/js_parser.js | 2 - .../project-template-gradle/build.gradle | 159 ++++++++---------- 3 files changed, 86 insertions(+), 105 deletions(-) diff --git a/android-static-binding-generator/project/build.gradle b/android-static-binding-generator/project/build.gradle index adf3d0c26..63352275c 100644 --- a/android-static-binding-generator/project/build.gradle +++ b/android-static-binding-generator/project/build.gradle @@ -155,23 +155,16 @@ task traverseJsFilesArgs { //(jsCodeDir, bindingsFilePath, interfaceNamesFilePat list.add(interfaceNamesFilePath) list.add(jsFilesParameter) - def list = new ArrayList(); - list.add("node") - list.add(jsParserPath) - list.add(jsCodeDir) - list.add(bindingsFilePath) - list.add(interfaceNamesFilePath) - list.add(jsFilesParameter) - - logger.info("Task: traverseJsFilesArgs: executed with arguments: " + list.toString().replaceAll(',', '')) - def proc = list.execute() - proc.in.eachLine { line -> println line } - proc.out.close() - proc.waitFor() - - if (proc.exitValue()) { - println "gave the following error: " - println "[ERROR] ${proc.getErrorStream()}" + logger.info("Task: traverseJsFilesArgs: executed with arguments: " + list.toString().replaceAll(',', '')) + def proc = list.execute() + proc.in.eachLine { line -> println line } + proc.out.close() + proc.waitFor() + + if (proc.exitValue()) { + println "gave the following error: " + println "[ERROR] ${proc.getErrorStream()}" + } } } @@ -279,9 +272,10 @@ task generateBindings() { inputs.dir (bindingsFile) doFirst { - if(!file(bindingsFileP).exists()) { + if (!file(bindingsFileP).exists()) { throw new GradleException("No ${bindingsFileP} was found after runAstParser task was ran! Check to see if there are any .js files inside ${jsCodeDir}") } + javaexec { main "-jar" diff --git a/android-static-binding-generator/project/parser/js_parser.js b/android-static-binding-generator/project/parser/js_parser.js index f5744ed18..74df5899b 100644 --- a/android-static-binding-generator/project/parser/js_parser.js +++ b/android-static-binding-generator/project/parser/js_parser.js @@ -103,8 +103,6 @@ function readLinesFromFile(filePath, outArr, resolveParameter) { return reject(err); } - console.log("finished with reading lines with js files"); - return resolve(resolveParameter) }); }); diff --git a/build-artifacts/project-template-gradle/build.gradle b/build-artifacts/project-template-gradle/build.gradle index c4c2167e5..cc0f394f8 100644 --- a/build-artifacts/project-template-gradle/build.gradle +++ b/build-artifacts/project-template-gradle/build.gradle @@ -39,22 +39,20 @@ def metadataParams = new LinkedList () def allJarPaths = new LinkedList () def configStage = "\tconfig phase: " def nodeModulesDir = "../../node_modules/" -def configurationsDir = new File(projectDir, "build/configurations") def dependenciesJson = file("dependencies.json") // the build script will not work with previous versions of the CLI (3.1 or earlier) if (!dependenciesJson.exists()) { throw new BuildCancelledException(""" 'dependencies.json' file not found. Check whether the NativeScript CLI has prepared the project beforehand, -and that your NativeScript version is 3.2, or a more recent one. To build an android project with the current -version of the {N} CLI install a previous version of the runtime package - 'tns platform add android@3.1'. +and that your NativeScript version is 3.3, or a more recent one. To build an android project with the current +version of the {N} CLI install a previous version of the runtime package - 'tns platform add android@3.2'. """) } def nativescriptDependencies = new JsonSlurper().parseText(dependenciesJson.text) def packageJsonContents = [:] -def excludedDevDependencies = ['**/.bin/**'] def dontRunSbg = project.hasProperty("dontRunSbg"); def asbgProject = project(":asbg") @@ -81,25 +79,74 @@ def renameResultApks = { variant -> } } -// gets the devDependencies declared in the package.json and excludes them from the build -task getDevDependencies { - doLast { - println "$configStage getDevDependencies" +//////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// CONFIGURATIONS /////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +def applyPluginsIncludeGradleConfigurations = { -> + def taskNames = project.getGradle().startParameter.taskNames + + // don't apply plugin configurations if clean is invoked + if (taskNames && taskNames.size() > 0 && taskNames.getAt(0).equals("clean")) { + return [] + } + + def configurationsDir = new File(projectDir, "build/configurations") + configurationsDir.deleteDir() + + def dimensions = [] + def includes = new ArrayList() + def flavorNumber = 0 + + nativescriptDependencies.each { dep -> + def androidDir = file("${dep.directory}/platforms/android") + if (!androidDir.exists()) { + return + } + + def packageJsonPath = file("${dep.directory}/package.json") + def packageJson = new JsonSlurper().parseText(packageJsonPath.text) + def pluginName = packageJson.name + def dimensionName = sanitizeDimensionName(pluginName) - String content = new File("$projectDir/../../package.json").getText("UTF-8") - def jsonSlurper = new JsonSlurper() - def packageJsonMap = jsonSlurper.parseText(content) + dimensions.add(dimensionName) + def flavor = "F${flavorNumber++}" + + def includeGradleFile = new File(androidDir, "include.gradle") + def destinationDir = file("${configurationsDir}/${pluginName}/") + def destinationIncludeGradleFile = file("${configurationsDir}/${pluginName}/include.gradle") - packageJsonContents = packageJsonMap.devDependencies; + Files.createDirectories(Paths.get(destinationDir.getAbsolutePath())) - packageJsonContents.each { entry -> - excludedDevDependencies.add(entry.key + '/platforms/android/**/*.jar') + if (includeGradleFile.exists()) { + println "\t + add include.gradle from ${includeGradleFile}" + destinationIncludeGradleFile.text = modifyProductFlavorInContent(includeGradleFile.text, dimensionName, flavor) + } else { + println "\t + creating include.gradle for plugin ${file(dep.directory)}" + destinationIncludeGradleFile.text = createProductFlavorsContent(flavor, dimensionName) } + + includes.add(destinationIncludeGradleFile.getAbsolutePath()); + + copyAndRenamePluginDirToFlavorName(androidDir, flavor); + } + + includes.each { + println "\t + applying plugin configuration from ${it}" + apply from: it + } + + return dimensions +} + +def applyAppGradleConfiguration = { -> + def pathToAppGradle = "$projectDir/../../app/App_Resources/Android/app.gradle" + def appGradle = file(pathToAppGradle) + if (appGradle.exists()) { + println "\t + applying user-defined configuration from ${appGradle}" + apply from: pathToAppGradle } } -//////////////////////////////////////////////////////////////////////////////////// -///////////////////////////// CONFIGURATIONS /////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////// android { compileSdkVersion computeCompileSdkVersion() @@ -177,6 +224,12 @@ android { compileTask.dependsOn("asbg:generateBindings") } } + + def dimensions = applyPluginsIncludeGradleConfigurations() + + flavorDimensions(*dimensions) + + applyAppGradleConfiguration() } repositories { @@ -204,9 +257,6 @@ dependencies { // take all jars within the libs dir compile fileTree(dir: "$projectDir/libs", include: ["**/*.jar"]) - - // take all jars within the node_modules dir - compile fileTree(dir: nodeModulesDir, include: ["**/platforms/android/**/*.jar"], exclude: excludedDevDependencies) } //////////////////////////////////////////////////////////////////////////////////// @@ -245,67 +295,6 @@ task addDependenciesFromNativeScriptPlugins { } } -task applyPluginsIncludeGradleConfigurations { - configurationsDir.deleteDir() - - def dimensions = [] - def includes = new ArrayList() - def flavorNumber = 0 - - nativescriptDependencies.each { dep -> - def androidDir = file("${dep.directory}/platforms/android") - if (!androidDir.exists()) { - return - } - - def packageJsonPath = file("${dep.directory}/package.json") - def packageJson = new JsonSlurper().parseText(packageJsonPath.text) - def pluginName = packageJson.name - def dimensionName = sanitizeDimensionName(pluginName) - - dimensions.add(dimensionName) - def flavor = "F${flavorNumber++}" - - def includeGradleFile = new File(androidDir, "include.gradle") - def destinationDir = file("${configurationsDir}/${pluginName}/") - def destinationIncludeGradleFile = file("${configurationsDir}/${pluginName}/include.gradle") - - Files.createDirectories(Paths.get(destinationDir.getAbsolutePath())) - - if (includeGradleFile.exists()) { - println "\t + add include.gradle from ${includeGradleFile}" - destinationIncludeGradleFile.text = modifyProductFlavorInContent(includeGradleFile.text, dimensionName, flavor) - } else { - println "\t + creating include.gradle for plugin ${file(dep.directory)}" - destinationIncludeGradleFile.text = createProductFlavorsContent(flavor, dimensionName) - } - - includes.add(destinationIncludeGradleFile.getAbsolutePath()); - - copyAndRenamePluginDirToFlavorName(androidDir, flavor); - } - - flavorDimensions(*dimensions) - - includes.each { - println "\t + applying plugin configuration from ${it}" - apply from: it - } -} - -applyPluginsIncludeGradleConfigurations.finalizedBy(applyAppGradleConfiguration) - -task applyAppGradleConfiguration { - description "applies user-defined gradle configuration" - - def pathToAppGradle = "$projectDir/../../app/App_Resources/Android/app.gradle" - def appGradle = file(pathToAppGradle) - if (appGradle.exists()) { - println "\t + applying user-defined configuration from ${appGradle}" - apply from: pathToAppGradle - } -} - static def updateProductFlavorsContent(flavor, dimensionName, oldContent) { def endIndex = oldContent.length() - 1; def index = 0; @@ -476,15 +465,15 @@ def copyAndRenamePluginDirToFlavorName(directory, flavor) { copyFolder(directory, targetDir) } -//////////////////////////////////////////////////////////////////////////////////// -///////////////////////////// EXECUTUION PHASE ///////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////// - task ensureMetadataOutDir { def outputDir = file("$projectDir/metadata/output/assets/metadata") outputDir.mkdirs() } +//////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// EXECUTUION PHASE ///////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + task collectAllJars { description "gathers all paths to jar dependencies before building metadata with them"