983 lines
36 KiB
Groovy
983 lines
36 KiB
Groovy
/*
|
|
* Script builds apk in release or debug mode
|
|
* To run:
|
|
* gradle assembleRelease -Prelease (release mode)
|
|
* gradle assembleDebug (debug mode -> default)
|
|
* Options:
|
|
* -Prelease //this flag will run build in release mode
|
|
* -PksPath=[path_to_keystore_file]
|
|
* -PksPassword=[password_for_keystore_file]
|
|
* -Palias=[alias_to_use_from_keystore_file]
|
|
* -Ppassword=[password_for_alias]
|
|
*
|
|
* -PtargetSdk=[target_sdk]
|
|
* -PbuildToolsVersion=[build_tools_version]
|
|
* -PcompileSdk=[compile_sdk_version]
|
|
* -PandroidXLegacy=[androidx_legacy_version]
|
|
* -PandroidXAppCompat=[androidx_appcompat_version]
|
|
* -PandroidXMaterial=[androidx_material_version]
|
|
* -PappPath=[app_path]
|
|
* -PappResourcesPath=[app_resources_path]
|
|
*/
|
|
|
|
|
|
import groovy.io.FileType
|
|
import groovy.json.JsonSlurper
|
|
|
|
import javax.inject.Inject
|
|
import java.nio.file.Files
|
|
import java.nio.file.Paths
|
|
import java.nio.file.StandardCopyOption
|
|
import java.security.MessageDigest
|
|
import org.gradle.internal.logging.text.StyledTextOutputFactory
|
|
import static org.gradle.internal.logging.text.StyledTextOutput.Style
|
|
|
|
apply plugin: "com.android.application"
|
|
apply from: "gradle-helpers/BuildToolTask.gradle"
|
|
apply from: "gradle-helpers/CustomExecutionLogger.gradle"
|
|
apply from: "gradle-helpers/AnalyticsCollector.gradle"
|
|
|
|
def enableKotlin = (project.hasProperty("useKotlin") && project.useKotlin == "true")
|
|
|
|
if (enableKotlin) {
|
|
apply plugin: 'kotlin-android'
|
|
apply plugin: 'kotlin-android-extensions'
|
|
}
|
|
|
|
def onlyX86 = project.hasProperty("onlyX86")
|
|
if (onlyX86) {
|
|
outLogger.withStyle(Style.Info).println "OnlyX86 build triggered."
|
|
}
|
|
|
|
//common
|
|
def BUILD_TOOLS_PATH = "$rootDir/build-tools"
|
|
def PASSED_TYPINGS_PATH = System.getenv("TNS_TYPESCRIPT_DECLARATIONS_PATH")
|
|
def TYPINGS_PATH = "$BUILD_TOOLS_PATH/typings"
|
|
if (PASSED_TYPINGS_PATH != null) {
|
|
TYPINGS_PATH = PASSED_TYPINGS_PATH
|
|
}
|
|
|
|
def PACKAGE_JSON = "package.json"
|
|
|
|
//static binding generator
|
|
def SBG_JAVA_DEPENDENCIES = "sbg-java-dependencies.txt"
|
|
def SBG_INPUT_FILE = "sbg-input-file.txt"
|
|
def SBG_OUTPUT_FILE = "sbg-output-file.txt"
|
|
def SBG_JS_PARSED_FILES = "sbg-js-parsed-files.txt"
|
|
def SBG_BINDINGS_NAME = "sbg-bindings.txt"
|
|
def SBG_INTERFACE_NAMES = "sbg-interface-names.txt"
|
|
def INPUT_JS_DIR = "$projectDir/src/main/assets/app"
|
|
def OUTPUT_JAVA_DIR = "$projectDir/src/main/java"
|
|
|
|
//metadata generator
|
|
def MDG_OUTPUT_DIR = "mdg-output-dir.txt"
|
|
def MDG_JAVA_DEPENDENCIES = "mdg-java-dependencies.txt"
|
|
def METADATA_OUT_PATH = "$projectDir/src/main/assets/metadata"
|
|
|
|
// paths to jar libraries
|
|
def pluginsJarLibraries = new LinkedList<String>()
|
|
def allJarLibraries = new LinkedList<String>()
|
|
|
|
def computeKotlinVersion = { -> project.hasProperty("kotlinVersion") ? kotlinVersion : "1.3.41" }
|
|
def computeCompileSdkVersion = { -> project.hasProperty("compileSdk") ? compileSdk : 29 }
|
|
def computeTargetSdkVersion = { -> project.hasProperty("targetSdk") ? targetSdk : 29 }
|
|
def computeBuildToolsVersion = { ->
|
|
project.hasProperty("buildToolsVersion") ? buildToolsVersion : "29.0.2"
|
|
}
|
|
|
|
def enableAnalytics = (project.hasProperty("gatherAnalyticsData") && project.gatherAnalyticsData == "true")
|
|
def enableVerboseMDG = project.gradle.startParameter.logLevel.name() == 'DEBUG'
|
|
def analyticsFilePath = "$rootDir/analytics/build-statistics.json"
|
|
def analyticsCollector = project.ext.AnalyticsCollector.withOutputPath(analyticsFilePath)
|
|
if (enableAnalytics) {
|
|
analyticsCollector.markUseKotlinPropertyInApp(enableKotlin)
|
|
analyticsCollector.writeAnalyticsFile()
|
|
}
|
|
|
|
project.ext.selectedBuildType = project.hasProperty("release") ? "release" : "debug"
|
|
|
|
buildscript {
|
|
// we have moved these initializations here as we need getAppResourcesPath to search for buildscript.gradle files,
|
|
// but the buildscript is executed before executing anything else from the gradle file
|
|
def initialize = { ->
|
|
// the build script will not work with previous versions of the CLI (3.1 or earlier)
|
|
def dependenciesJson = file("$rootDir/dependencies.json")
|
|
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.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'.
|
|
""")
|
|
}
|
|
|
|
project.ext.extractedDependenciesDir = "${project.buildDir}/exploded-dependencies"
|
|
project.ext.cleanupAllJarsTimestamp = "${project.buildDir}/cleanupAllJars.timestamp"
|
|
project.ext.extractAllJarsTimestamp = "${project.buildDir}/extractAllJars.timestamp"
|
|
project.ext.nativescriptDependencies = new JsonSlurper().parseText(dependenciesJson.text)
|
|
project.ext.PLATFORMS_ANDROID = "platforms/android"
|
|
project.ext.USER_PROJECT_ROOT = "$rootDir/../.."
|
|
project.ext.outLogger = services.get(StyledTextOutputFactory).create("colouredOutputLogger")
|
|
|
|
project.ext.getAppPath = { ->
|
|
def relativePathToApp = "app"
|
|
def nsConfigFile = file("$USER_PROJECT_ROOT/nsconfig.json")
|
|
def nsConfig
|
|
|
|
if (nsConfigFile.exists()) {
|
|
nsConfig = new JsonSlurper().parseText(nsConfigFile.getText("UTF-8"))
|
|
}
|
|
|
|
if (project.hasProperty("appPath")) {
|
|
// when appPath is passed through -PappPath=/path/to/app
|
|
// the path could be relative or absolute - either case will work
|
|
relativePathToApp = appPath
|
|
} else if (nsConfig != null && nsConfig.appPath != null) {
|
|
relativePathToApp = nsConfig.appPath
|
|
}
|
|
|
|
project.ext.appPath = java.nio.file.Paths.get(USER_PROJECT_ROOT).resolve(relativePathToApp).toAbsolutePath()
|
|
|
|
return project.ext.appPath
|
|
}
|
|
|
|
project.ext.getAppResourcesPath = { ->
|
|
def relativePathToAppResources
|
|
def absolutePathToAppResources
|
|
def nsConfigFile = file("$USER_PROJECT_ROOT/nsconfig.json")
|
|
def nsConfig
|
|
|
|
if (nsConfigFile.exists()) {
|
|
nsConfig = new JsonSlurper().parseText(nsConfigFile.getText("UTF-8"))
|
|
}
|
|
|
|
if (project.hasProperty("appResourcesPath")) {
|
|
// when appResourcesPath is passed through -PappResourcesPath=/path/to/App_Resources
|
|
// the path could be relative or absolute - either case will work
|
|
relativePathToAppResources = appResourcesPath
|
|
absolutePathToAppResources = java.nio.file.Paths.get(USER_PROJECT_ROOT).resolve(relativePathToAppResources).toAbsolutePath()
|
|
} else if (nsConfig != null && nsConfig.appResourcesPath != null) {
|
|
relativePathToAppResources = nsConfig.appResourcesPath
|
|
absolutePathToAppResources = java.nio.file.Paths.get(USER_PROJECT_ROOT).resolve(relativePathToAppResources).toAbsolutePath()
|
|
} else {
|
|
absolutePathToAppResources = "${getAppPath()}/App_Resources"
|
|
}
|
|
|
|
project.ext.appResourcesPath = absolutePathToAppResources
|
|
|
|
return absolutePathToAppResources
|
|
}
|
|
}
|
|
def applyBuildScriptConfigurations = { ->
|
|
def absolutePathToAppResources = getAppResourcesPath()
|
|
def pathToBuildScriptGradle = "$absolutePathToAppResources/Android/buildscript.gradle"
|
|
def buildScriptGradle = file(pathToBuildScriptGradle)
|
|
if (buildScriptGradle.exists()) {
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + applying user-defined buildscript from ${buildScriptGradle}"
|
|
apply from: pathToBuildScriptGradle, to: buildscript
|
|
}
|
|
|
|
nativescriptDependencies.each { dep ->
|
|
def pathToPluginBuildScriptGradle = "$rootDir/${dep.directory}/$PLATFORMS_ANDROID/buildscript.gradle"
|
|
def pluginBuildScriptGradle = file(pathToPluginBuildScriptGradle)
|
|
if (pluginBuildScriptGradle.exists()) {
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + applying user-defined buildscript from dependency ${pluginBuildScriptGradle}"
|
|
apply from: pathToPluginBuildScriptGradle, to: buildscript
|
|
}
|
|
}
|
|
}
|
|
|
|
initialize()
|
|
applyBuildScriptConfigurations()
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////// CONFIGURATIONS ///////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def applyBeforePluginGradleConfiguration = { ->
|
|
def appResourcesPath = getAppResourcesPath()
|
|
def pathToBeforePluginGradle = "$appResourcesPath/Android/before-plugins.gradle"
|
|
def beforePluginGradle = file(pathToBeforePluginGradle)
|
|
if (beforePluginGradle.exists()) {
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + applying user-defined configuration from ${beforePluginGradle}"
|
|
apply from: pathToBeforePluginGradle
|
|
}
|
|
}
|
|
|
|
def applyAppGradleConfiguration = { ->
|
|
def appResourcesPath = getAppResourcesPath()
|
|
def pathToAppGradle = "$appResourcesPath/Android/app.gradle"
|
|
def appGradle = file(pathToAppGradle)
|
|
if (appGradle.exists()) {
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + applying user-defined configuration from ${appGradle}"
|
|
apply from: pathToAppGradle
|
|
} else {
|
|
outLogger.withStyle(Style.Info).println "\t + couldn't load user-defined configuration from ${appGradle}. File doesn't exist."
|
|
}
|
|
}
|
|
|
|
def applyPluginGradleConfigurations = { ->
|
|
nativescriptDependencies.each { dep ->
|
|
def includeGradlePath = "$rootDir/${dep.directory}/$PLATFORMS_ANDROID/include.gradle"
|
|
if (file(includeGradlePath).exists()) {
|
|
apply from: includeGradlePath
|
|
}
|
|
}
|
|
}
|
|
|
|
def getAppIdentifier = { packageJsonMap ->
|
|
def appIdentifier = ""
|
|
if (packageJsonMap && packageJsonMap.nativescript) {
|
|
appIdentifier = packageJsonMap.nativescript.id
|
|
if (!(appIdentifier instanceof String)) {
|
|
appIdentifier = appIdentifier.android
|
|
}
|
|
}
|
|
|
|
return appIdentifier
|
|
}
|
|
|
|
def setAppIdentifier = { ->
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + setting applicationId"
|
|
File packageJsonFile = new File("$USER_PROJECT_ROOT/$PACKAGE_JSON")
|
|
|
|
if (packageJsonFile.exists()) {
|
|
def content = packageJsonFile.getText("UTF-8")
|
|
def jsonSlurper = new JsonSlurper()
|
|
def packageJsonMap = jsonSlurper.parseText(content)
|
|
def appIdentifier = getAppIdentifier(packageJsonMap)
|
|
|
|
if (appIdentifier) {
|
|
project.ext.nsApplicationIdentifier = appIdentifier
|
|
android.defaultConfig.applicationId = appIdentifier
|
|
}
|
|
}
|
|
}
|
|
|
|
android {
|
|
|
|
if (enableKotlin) {
|
|
kotlinOptions {
|
|
jvmTarget = '1.8'
|
|
}
|
|
}
|
|
|
|
compileSdkVersion computeCompileSdkVersion()
|
|
buildToolsVersion computeBuildToolsVersion()
|
|
|
|
defaultConfig {
|
|
def manifest = new XmlSlurper().parse(file(android.sourceSets.main.manifest.srcFile))
|
|
def minSdkVer = manifest."uses-sdk"."@android:minSdkVersion".text() ?: 17
|
|
minSdkVersion minSdkVer
|
|
targetSdkVersion computeTargetSdkVersion()
|
|
ndk {
|
|
if (onlyX86) {
|
|
abiFilters 'x86'
|
|
} else {
|
|
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
|
}
|
|
}
|
|
dexOptions {
|
|
jumboMode = true
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility JavaVersion.VERSION_1_8
|
|
targetCompatibility JavaVersion.VERSION_1_8
|
|
}
|
|
|
|
sourceSets.main {
|
|
jniLibs.srcDirs = ["$projectDir/libs/jni", "$projectDir/snapshot-build/build/ndk-build/libs"]
|
|
}
|
|
|
|
signingConfigs {
|
|
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
|
|
}
|
|
}
|
|
|
|
setAppIdentifier()
|
|
applyBeforePluginGradleConfiguration()
|
|
applyPluginGradleConfigurations()
|
|
applyAppGradleConfiguration()
|
|
|
|
def initializeMergedAssetsOutputPath = { ->
|
|
android.applicationVariants.all { variant ->
|
|
if (variant.buildType.name == project.selectedBuildType) {
|
|
def task
|
|
if (variant.metaClass.respondsTo(variant, "getMergeAssetsProvider")) {
|
|
def provider = variant.getMergeAssetsProvider()
|
|
task = provider.get();
|
|
} else {
|
|
// fallback for older android gradle plugin versions
|
|
task = variant.getMergeAssets()
|
|
}
|
|
for (File file : task.getOutputs().getFiles()) {
|
|
if (!file.getPath().contains("${File.separator}incremental${File.separator}")) {
|
|
project.ext.mergedAssetsOutputPath = file.getPath()
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
initializeMergedAssetsOutputPath()
|
|
}
|
|
|
|
def externalRuntimeExists = !findProject(':runtime').is(null)
|
|
def pluginDependencies
|
|
|
|
repositories {
|
|
// used for local *.AAR files
|
|
pluginDependencies = nativescriptDependencies.collect {
|
|
"$rootDir/${it.directory}/$PLATFORMS_ANDROID"
|
|
}
|
|
|
|
// some plugins may have their android dependencies in a /libs subdirectory
|
|
pluginDependencies.addAll(nativescriptDependencies.collect {
|
|
"$rootDir/${it.directory}/$PLATFORMS_ANDROID/libs"
|
|
})
|
|
|
|
if (!externalRuntimeExists) {
|
|
pluginDependencies.add("libs/runtime-libs")
|
|
}
|
|
|
|
def appResourcesPath = getAppResourcesPath()
|
|
def localAppResourcesLibraries = "$appResourcesPath/Android/libs"
|
|
|
|
pluginDependencies.add(localAppResourcesLibraries)
|
|
|
|
if (pluginDependencies.size() > 0) {
|
|
flatDir {
|
|
dirs pluginDependencies
|
|
}
|
|
}
|
|
mavenCentral()
|
|
}
|
|
|
|
dependencies {
|
|
def androidXLegacyVersion = "1.0.0"
|
|
if (project.hasProperty("androidXLegacy")) {
|
|
androidXLegacyVersion = androidXLegacy
|
|
}
|
|
|
|
def androidXAppCompatVersion = "1.1.0"
|
|
if (project.hasProperty("androidXAppCompat")) {
|
|
androidXAppCompatVersion = androidXAppCompat
|
|
}
|
|
|
|
def androidXMaterialVersion = "1.1.0"
|
|
if (project.hasProperty("androidXMaterial")) {
|
|
androidXMaterialVersion = androidXMaterial
|
|
}
|
|
|
|
def androidXExifInterfaceVersion = "1.2.0"
|
|
if (project.hasProperty("androidXExifInterface")) {
|
|
androidxExifInterfaceVersion = androidXExifInterface
|
|
}
|
|
|
|
def androidXViewPagerVersion = "1.0.0"
|
|
if (project.hasProperty("androidXViewPager")) {
|
|
androidXViewPagerVersion = androidXViewPager
|
|
}
|
|
|
|
def androidXFragmentVersion = "1.2.5"
|
|
if (project.hasProperty("androidXFragment")) {
|
|
androidXFragmentVersion = androidXFragment
|
|
}
|
|
|
|
def androidXTransitionVersion = "1.3.1"
|
|
if (project.hasProperty("androidXTransition")) {
|
|
androidXTransitionVersion = androidXTransition
|
|
}
|
|
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + using android X library androidx.legacy:legacy-support-v4:$androidXLegacyVersion"
|
|
|
|
implementation "androidx.multidex:multidex:2.0.1"
|
|
implementation "androidx.legacy:legacy-support-v4:$androidXLegacyVersion"
|
|
implementation "androidx.appcompat:appcompat:$androidXAppCompatVersion"
|
|
implementation "com.google.android.material:material:$androidXMaterialVersion"
|
|
implementation "androidx.exifinterface:exifinterface:$androidXExifInterfaceVersion"
|
|
implementation "androidx.viewpager2:viewpager2:$androidXViewPagerVersion"
|
|
implementation "androidx.fragment:fragment:$androidXFragmentVersion"
|
|
implementation "androidx.transition:transition:$androidXTransitionVersion"
|
|
|
|
def useV8Symbols = false
|
|
|
|
def appPackageJsonFile = file("${getAppPath()}/$PACKAGE_JSON")
|
|
if (appPackageJsonFile.exists()) {
|
|
def appPackageJson = new JsonSlurper().parseText(appPackageJsonFile.text)
|
|
useV8Symbols = appPackageJson.android && appPackageJson.android.useV8Symbols
|
|
}
|
|
|
|
if (!useV8Symbols) {
|
|
// check whether any of the dependencies require v8 symbols
|
|
useV8Symbols = nativescriptDependencies.any {
|
|
def packageJsonFile = file("$rootDir/${it.directory}/$PACKAGE_JSON")
|
|
def packageJson = new JsonSlurper().parseText(packageJsonFile.text)
|
|
return packageJson.nativescript && packageJson.nativescript.useV8Symbols
|
|
}
|
|
}
|
|
|
|
if (!externalRuntimeExists) {
|
|
def runtime = "nativescript-optimized-with-inspector"
|
|
|
|
if (project.gradle.startParameter.taskNames.any { it.toLowerCase().contains('release') }) {
|
|
runtime = "nativescript-optimized"
|
|
}
|
|
|
|
if (useV8Symbols) {
|
|
runtime = "nativescript-regular"
|
|
}
|
|
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + adding nativescript runtime package dependency: $runtime"
|
|
project.dependencies.add("implementation", [name: runtime, ext: "aar"])
|
|
} else {
|
|
implementation project(':runtime')
|
|
}
|
|
|
|
def kotlinVersion = computeKotlinVersion()
|
|
if (enableKotlin) {
|
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
|
}
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////// CONFIGURATION PHASE //////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
task addDependenciesFromNativeScriptPlugins {
|
|
nativescriptDependencies.each { dep ->
|
|
def aarFiles = fileTree(dir: file("$rootDir/${dep.directory}/$PLATFORMS_ANDROID"), include: ["**/*.aar"])
|
|
aarFiles.each { aarFile ->
|
|
def length = aarFile.name.length() - 4
|
|
def fileName = aarFile.name[0..<length]
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + adding aar plugin dependency: " + aarFile.getAbsolutePath()
|
|
project.dependencies.add("implementation", [name: fileName, ext: "aar"])
|
|
}
|
|
|
|
def jarFiles = fileTree(dir: file("$rootDir/${dep.directory}/$PLATFORMS_ANDROID"), include: ["**/*.jar"])
|
|
jarFiles.each { jarFile ->
|
|
def jarFileAbsolutePath = jarFile.getAbsolutePath()
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + adding jar plugin dependency: $jarFileAbsolutePath"
|
|
pluginsJarLibraries.add(jarFile.getAbsolutePath())
|
|
}
|
|
|
|
project.dependencies.add("implementation", jarFiles)
|
|
}
|
|
}
|
|
|
|
task addDependenciesFromAppResourcesLibraries {
|
|
def appResourcesPath = getAppResourcesPath()
|
|
def appResourcesLibraries = file("$appResourcesPath/Android/libs")
|
|
if (appResourcesLibraries.exists()) {
|
|
def aarFiles = fileTree(dir: appResourcesLibraries, include: ["**/*.aar"])
|
|
aarFiles.each { aarFile ->
|
|
def length = aarFile.name.length() - 4
|
|
def fileName = aarFile.name[0..<length]
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + adding aar library dependency: " + aarFile.getAbsolutePath()
|
|
project.dependencies.add("implementation", [name: fileName, ext: "aar"])
|
|
}
|
|
|
|
def jarFiles = fileTree(dir: appResourcesLibraries, include: ["**/*.jar"])
|
|
jarFiles.each { jarFile ->
|
|
def jarFileAbsolutePath = jarFile.getAbsolutePath()
|
|
outLogger.withStyle(Style.SuccessHeader).println "\t + adding jar plugin dependency: $jarFileAbsolutePath"
|
|
pluginsJarLibraries.add(jarFile.getAbsolutePath())
|
|
}
|
|
|
|
project.dependencies.add("implementation", jarFiles)
|
|
}
|
|
}
|
|
|
|
if (failOnCompilationWarningsEnabled()) {
|
|
tasks.withType(JavaCompile) {
|
|
options.compilerArgs << '-Xlint:all' << "-Werror"
|
|
options.deprecation = true
|
|
}
|
|
}
|
|
|
|
tasks.whenTaskAdded({ org.gradle.api.DefaultTask currentTask ->
|
|
if (currentTask =~ /generate.+BuildConfig/) {
|
|
currentTask.finalizedBy(extractAllJars)
|
|
extractAllJars.finalizedBy(collectAllJars)
|
|
}
|
|
if (currentTask =~ /compile.+JavaWithJavac/) {
|
|
currentTask.dependsOn(runSbg)
|
|
currentTask.finalizedBy(buildMetadata)
|
|
}
|
|
if (currentTask =~ /merge.*Assets/) {
|
|
currentTask.shouldRunAfter(buildMetadata)
|
|
}
|
|
if (currentTask =~ /assemble.*Debug/ || currentTask =~ /assemble.*Release/) {
|
|
currentTask.finalizedBy("validateAppIdMatch")
|
|
}
|
|
})
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////// EXECUTUION PHASE /////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
task runSbg(type: BuildToolTask) {
|
|
dependsOn "collectAllJars"
|
|
if (!findProject(':static-binding-generator').is(null)) {
|
|
dependsOn ':static-binding-generator:jar'
|
|
}
|
|
|
|
outputs.dir("$OUTPUT_JAVA_DIR/com/tns/gen")
|
|
inputs.dir(INPUT_JS_DIR)
|
|
inputs.dir(extractedDependenciesDir)
|
|
|
|
workingDir "$BUILD_TOOLS_PATH"
|
|
main "-jar"
|
|
|
|
def paramz = new ArrayList<String>()
|
|
paramz.add("static-binding-generator.jar")
|
|
|
|
if (failOnCompilationWarningsEnabled()) {
|
|
paramz.add("-show-deprecation-warnings")
|
|
}
|
|
|
|
setOutputs outLogger
|
|
|
|
args paramz
|
|
|
|
doFirst {
|
|
new File("$OUTPUT_JAVA_DIR/com/tns/gen").deleteDir()
|
|
}
|
|
}
|
|
|
|
def failOnCompilationWarningsEnabled() {
|
|
return project.hasProperty("failOnCompilationWarnings") && (failOnCompilationWarnings || failOnCompilationWarnings.toBoolean())
|
|
}
|
|
|
|
def explodeAar(File compileDependency, File outputDir) {
|
|
logger.info("explodeAar: Extracting ${compileDependency.path} -> ${outputDir.path}")
|
|
|
|
if (compileDependency.name.endsWith(".aar")) {
|
|
java.util.jar.JarFile jar = new java.util.jar.JarFile(compileDependency)
|
|
Enumeration enumEntries = jar.entries()
|
|
while (enumEntries.hasMoreElements()) {
|
|
java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement()
|
|
if (file.name.endsWith(".jar")) {
|
|
def targetFile = new File(outputDir, file.name)
|
|
InputStream inputStream = jar.getInputStream(file)
|
|
new File(targetFile.parent).mkdirs()
|
|
Files.copy(inputStream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
|
}
|
|
if (file.isDirectory()) {
|
|
continue
|
|
}
|
|
}
|
|
jar.close()
|
|
} else if (compileDependency.name.endsWith(".jar")) {
|
|
copy {
|
|
from compileDependency.absolutePath
|
|
into outputDir
|
|
}
|
|
}
|
|
}
|
|
|
|
def md5(String string) {
|
|
MessageDigest digest = MessageDigest.getInstance("MD5")
|
|
digest.update(string.bytes)
|
|
return new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
|
|
}
|
|
|
|
class WorkerTask extends DefaultTask {
|
|
@Inject
|
|
WorkerExecutor getWorkerExecutor() {
|
|
throw new UnsupportedOperationException()
|
|
}
|
|
}
|
|
|
|
class EmptyRunnable implements Runnable {
|
|
void run() {
|
|
}
|
|
}
|
|
|
|
def getMergedAssetsOutputPath() {
|
|
if (!project.hasProperty("mergedAssetsOutputPath")) {
|
|
// mergedAssetsOutputPath not found fallback to the default value for android gradle plugin 3.5.1
|
|
project.ext.mergedAssetsOutputPath = "$projectDir/build/intermediates/merged_assets/" + project.selectedBuildType + "/out"
|
|
}
|
|
return project.ext.mergedAssetsOutputPath
|
|
}
|
|
|
|
// Discover all jars and dynamically create tasks for the extraction of each of them
|
|
project.ext.allJars = []
|
|
afterEvaluate { project ->
|
|
def buildType = project.selectedBuildType == "release" ? "Release" : "Debug"
|
|
def jars = []
|
|
def artifactType = Attribute.of('artifactType', String)
|
|
configurations.all { config ->
|
|
if (config.name.toLowerCase().endsWith("${buildType}RuntimeClasspath".toLowerCase())) {
|
|
config.incoming.artifactView {
|
|
attributes {
|
|
it.attribute(artifactType, 'jar')
|
|
}
|
|
}.artifacts.each {
|
|
processJar(it.file, jars)
|
|
}
|
|
|
|
def projectDependencies = config.getAllDependencies().withType(ProjectDependency)
|
|
def dependentProjects = projectDependencies*.dependencyProject
|
|
dependentProjects.findAll {
|
|
// if there's a project dependency search for its result jar file in the build/intermediates/runtime_library_classes folder
|
|
// this is the output folder in gradle 5.1.1, but it can be changed in the future versions of gradle
|
|
def jarDir = new File("${it.getBuildDir()}/intermediates/runtime_library_classes/${buildType.toLowerCase()}")
|
|
if (jarDir.exists()) {
|
|
jarDir.eachFileRecurse(FileType.FILES) { file ->
|
|
if (file.path.endsWith(".jar")) {
|
|
processJar(file, jars)
|
|
}
|
|
}
|
|
} else {
|
|
outLogger.withStyle(Style.Info).println "WARNING: Folder ${jarDir.path} does not exists, the dependent project's classes won't be included in the metadata"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def processJar(File jar, jars) {
|
|
if (!jars.contains(jar)) {
|
|
jars.add(jar)
|
|
def destDir = md5(jar.path)
|
|
def outputDir = new File(Paths.get(extractedDependenciesDir, destDir).normalize().toString())
|
|
|
|
def taskName = "extract_${jar.name}_to_${destDir}"
|
|
logger.debug("Creating dynamic task ${taskName}")
|
|
|
|
// Add discovered jars as dependencies of cleanupAllJars.
|
|
// This is cruicial for cloud builds because they are different
|
|
// on each incremental build (as each time the gradle user home
|
|
// directory is a randomly generated string)
|
|
cleanupAllJars.inputs.files jar
|
|
|
|
task "${taskName}"(type: WorkerTask) {
|
|
dependsOn cleanupAllJars
|
|
extractAllJars.dependsOn it
|
|
|
|
// This dependency seems redundant but probably due to some Gradle issue with workers,
|
|
// without it `runSbg` sporadically starts before all extraction tasks have finished and
|
|
// fails due to missing JARs
|
|
runSbg.dependsOn it
|
|
|
|
inputs.files jar
|
|
outputs.dir outputDir
|
|
|
|
doLast {
|
|
// Runing in parallel no longer seems to bring any benefit.
|
|
// It mattered only when we were extracting JARs from AARs.
|
|
// To try it simply remove the following comments.
|
|
// workerExecutor.submit(EmptyRunnable.class) {
|
|
explodeAar(jar, outputDir)
|
|
// }
|
|
}
|
|
}
|
|
project.ext.allJars.add([file: jar, outputDir: outputDir])
|
|
}
|
|
}
|
|
|
|
task cleanupAllJars {
|
|
// We depend on the list of libs directories that might contain aar or jar files
|
|
// and on the list of all discovered jars
|
|
inputs.files(pluginDependencies)
|
|
|
|
outputs.files cleanupAllJarsTimestamp
|
|
|
|
doLast {
|
|
def allDests = project.ext.allJars*.outputDir*.name
|
|
def dir = new File(extractedDependenciesDir)
|
|
if (dir.exists()) {
|
|
dir.eachDir {
|
|
// An old directory which is no longer a dependency (e.g. orphaned by a deleted plugin)
|
|
if (!allDests.contains(it.name)) {
|
|
logger.info("Task cleanupAllJars: Deleting orphaned ${it.path}")
|
|
org.apache.commons.io.FileUtils.deleteDirectory(it)
|
|
}
|
|
}
|
|
}
|
|
new File(cleanupAllJarsTimestamp).write ""
|
|
}
|
|
}
|
|
|
|
|
|
// Placeholder task which depends on all dynamically generated extraction tasks
|
|
task extractAllJars {
|
|
dependsOn cleanupAllJars
|
|
outputs.files extractAllJarsTimestamp
|
|
|
|
doLast {
|
|
new File(cleanupAllJarsTimestamp).write ""
|
|
}
|
|
}
|
|
|
|
task collectAllJars {
|
|
dependsOn extractAllJars
|
|
description "gathers all paths to jar dependencies before building metadata with them"
|
|
|
|
def sdkPath = android.sdkDirectory.getAbsolutePath()
|
|
def androidJar = sdkPath + "/platforms/" + android.compileSdkVersion + "/android.jar"
|
|
|
|
doFirst {
|
|
def allJarPaths = new LinkedList<String>()
|
|
allJarPaths.add(androidJar)
|
|
allJarPaths.addAll(pluginsJarLibraries)
|
|
def ft = fileTree(dir: extractedDependenciesDir, include: "**/*.jar")
|
|
ft.each { currentJarFile ->
|
|
allJarPaths.add(currentJarFile.getAbsolutePath())
|
|
}
|
|
|
|
new File("$BUILD_TOOLS_PATH/$SBG_JAVA_DEPENDENCIES").withWriter { out ->
|
|
allJarPaths.each { out.println it }
|
|
}
|
|
new File("$BUILD_TOOLS_PATH/$MDG_JAVA_DEPENDENCIES").withWriter { out ->
|
|
allJarPaths.each {
|
|
if (it.endsWith(".jar")) {
|
|
out.println it
|
|
}
|
|
}
|
|
}
|
|
|
|
new File("$BUILD_TOOLS_PATH/$SBG_INPUT_FILE").withWriter { out ->
|
|
out.println INPUT_JS_DIR
|
|
}
|
|
new File("$BUILD_TOOLS_PATH/$SBG_OUTPUT_FILE").withWriter { out ->
|
|
out.println OUTPUT_JAVA_DIR
|
|
}
|
|
|
|
allJarLibraries.addAll(allJarPaths)
|
|
}
|
|
}
|
|
|
|
task copyMetadataFilters(type: Copy) {
|
|
from "$rootDir/whitelist.mdg", "$rootDir/blacklist.mdg"
|
|
into "$BUILD_TOOLS_PATH"
|
|
}
|
|
|
|
task copyMetadata {
|
|
doLast {
|
|
copy {
|
|
from "$projectDir/src/main/assets/metadata"
|
|
into getMergedAssetsOutputPath() + "/metadata"
|
|
}
|
|
}
|
|
}
|
|
|
|
task buildMetadata(type: BuildToolTask) {
|
|
if (!findProject(':android-metadata-generator').is(null)) {
|
|
dependsOn ':android-metadata-generator:jar'
|
|
}
|
|
|
|
dependsOn copyMetadataFilters
|
|
|
|
// As some external gradle plugins can reorder the execution order of the tasks it may happen that buildMetadata is executed after merge{Debug/Release}Assets
|
|
// in that case the metadata won't be included in the result apk and it will crash, so to avoid this we are adding the copyMetadata task which will manually copy
|
|
// the metadata files in the merge assets folder and they will be added to the result apk
|
|
|
|
// The next line is added to avoid adding another copyData implementation from the firebase plugin - https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/3943bb9147f43c41599e801d026378eba93d3f3a/publish/scripts/installer.js#L1105
|
|
//buildMetadata.finalizedBy(copyMetadata)
|
|
finalizedBy copyMetadata
|
|
|
|
description "builds metadata with provided jar dependencies"
|
|
|
|
inputs.files("$MDG_JAVA_DEPENDENCIES")
|
|
|
|
// make MDG aware of whitelist.mdg and blacklist.mdg files
|
|
inputs.files(project.fileTree(dir: "$rootDir", include: "**/*.mdg"))
|
|
|
|
def classesDir = "$buildDir/intermediates/javac"
|
|
inputs.dir(classesDir)
|
|
|
|
def kotlinClassesDir = "$buildDir/tmp/kotlin-classes"
|
|
if (file(kotlinClassesDir).exists()) {
|
|
inputs.dir(kotlinClassesDir)
|
|
}
|
|
|
|
outputs.files("$METADATA_OUT_PATH/treeNodeStream.dat", "$METADATA_OUT_PATH/treeStringsStream.dat", "$METADATA_OUT_PATH/treeValueStream.dat")
|
|
|
|
workingDir "$BUILD_TOOLS_PATH"
|
|
main "-jar"
|
|
|
|
doFirst {
|
|
// get compiled classes to pass to metadata generator
|
|
// these need to be called after the classes have compiled
|
|
assert file(classesDir).exists()
|
|
|
|
new File(getMergedAssetsOutputPath() + "/metadata").deleteDir()
|
|
|
|
def classesSubDirs = new File(classesDir).listFiles()
|
|
def selectedBuildType = project.ext.selectedBuildType
|
|
|
|
def generatedClasses = new LinkedList<String>()
|
|
for (File subDir : classesSubDirs) {
|
|
if (subDir.getName().equals(selectedBuildType)) {
|
|
generatedClasses.add(subDir.getAbsolutePath())
|
|
}
|
|
}
|
|
|
|
if (file(kotlinClassesDir).exists()) {
|
|
def kotlinClassesSubDirs = new File(kotlinClassesDir).listFiles()
|
|
for (File subDir : kotlinClassesSubDirs) {
|
|
if (subDir.getName() == selectedBuildType) {
|
|
generatedClasses.add(subDir.getAbsolutePath())
|
|
}
|
|
}
|
|
}
|
|
|
|
new File("$BUILD_TOOLS_PATH/$MDG_OUTPUT_DIR").withWriter { out ->
|
|
out.println "$METADATA_OUT_PATH"
|
|
}
|
|
|
|
new File("$BUILD_TOOLS_PATH/$MDG_JAVA_DEPENDENCIES").withWriterAppend { out ->
|
|
generatedClasses.each { out.println it }
|
|
}
|
|
|
|
setOutputs outLogger
|
|
|
|
def paramz = new ArrayList<String>()
|
|
paramz.add("android-metadata-generator.jar")
|
|
|
|
if(enableAnalytics){
|
|
paramz.add("analyticsFilePath=$analyticsFilePath")
|
|
}
|
|
|
|
if(enableVerboseMDG){
|
|
paramz.add("verbose")
|
|
}
|
|
|
|
args paramz.toArray()
|
|
}
|
|
}
|
|
|
|
task generateTypescriptDefinitions(type: BuildToolTask) {
|
|
if (!findProject(':dts-generator').is(null)) {
|
|
dependsOn ':dts-generator:jar'
|
|
}
|
|
|
|
def paramz = new ArrayList<String>()
|
|
def includeDirs = ["com.android.support", "/platforms/" + android.compileSdkVersion]
|
|
|
|
workingDir "$BUILD_TOOLS_PATH"
|
|
main "-jar"
|
|
|
|
doFirst {
|
|
delete "$TYPINGS_PATH"
|
|
|
|
paramz.add("dts-generator.jar")
|
|
paramz.add("-input")
|
|
|
|
for (String jarPath : allJarLibraries) {
|
|
// don't generate typings for runtime jars and classes
|
|
if (shouldIncludeDirForTypings(jarPath, includeDirs)) {
|
|
paramz.add(jarPath)
|
|
}
|
|
}
|
|
|
|
paramz.add("-output")
|
|
paramz.add("$TYPINGS_PATH")
|
|
|
|
new File("$TYPINGS_PATH").mkdirs()
|
|
|
|
logger.info("Task generateTypescriptDefinitions: Call dts-generator.jar with arguments: " + paramz.toString().replaceAll(',', ''))
|
|
outLogger.withStyle(Style.SuccessHeader).println "Task generateTypescriptDefinitions: Call dts-generator.jar with arguments: " + paramz.toString().replaceAll(',', '')
|
|
|
|
setOutputs outLogger
|
|
|
|
args paramz.toArray()
|
|
}
|
|
}
|
|
|
|
generateTypescriptDefinitions.onlyIf {
|
|
(project.hasProperty("generateTypings") && Boolean.parseBoolean(project.generateTypings)) || PASSED_TYPINGS_PATH != null
|
|
}
|
|
|
|
collectAllJars.finalizedBy(generateTypescriptDefinitions)
|
|
|
|
static def shouldIncludeDirForTypings(path, includeDirs) {
|
|
for (String p : includeDirs) {
|
|
if (path.indexOf(p) > -1) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
task copyTypings {
|
|
doLast {
|
|
outLogger.withStyle(Style.Info).println "Copied generated typings to application root level. Make sure to import android.d.ts in reference.d.ts"
|
|
|
|
copy {
|
|
from "$TYPINGS_PATH"
|
|
into "$USER_PROJECT_ROOT"
|
|
}
|
|
}
|
|
}
|
|
|
|
copyTypings.onlyIf { generateTypescriptDefinitions.didWork }
|
|
generateTypescriptDefinitions.finalizedBy(copyTypings)
|
|
|
|
task validateAppIdMatch {
|
|
doLast {
|
|
def lineSeparator = System.getProperty("line.separator")
|
|
|
|
if (project.hasProperty("nsApplicationIdentifier") && !project.hasProperty("release")) {
|
|
if (project.nsApplicationIdentifier != android.defaultConfig.applicationId) {
|
|
def errorMessage = "${lineSeparator}WARNING: The Application identifier is different from the one inside \"package.json\" file.$lineSeparator" +
|
|
"NativeScript CLI might not work properly.$lineSeparator" +
|
|
"Remove applicationId from app.gradle and update the \"nativescript.id\" in package.json.$lineSeparator" +
|
|
"Actual: ${android.defaultConfig.applicationId}$lineSeparator" +
|
|
"Expected(from \"package.json\"): ${project.nsApplicationIdentifier}$lineSeparator"
|
|
|
|
logger.error(errorMessage)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////// OPTIONAL TASKS //////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////// custom clean ///////////
|
|
task cleanSbg(type: Delete) {
|
|
delete "$BUILD_TOOLS_PATH/$SBG_JS_PARSED_FILES",
|
|
"$BUILD_TOOLS_PATH/$SBG_JAVA_DEPENDENCIES",
|
|
"$BUILD_TOOLS_PATH/$SBG_INTERFACE_NAMES",
|
|
"$BUILD_TOOLS_PATH/$SBG_BINDINGS_NAME",
|
|
"$BUILD_TOOLS_PATH/$SBG_INPUT_FILE",
|
|
"$BUILD_TOOLS_PATH/$SBG_OUTPUT_FILE",
|
|
"$OUTPUT_JAVA_DIR/com/tns/gen"
|
|
}
|
|
|
|
task cleanMdg(type: Delete) {
|
|
delete "$BUILD_TOOLS_PATH/$MDG_OUTPUT_DIR",
|
|
"$BUILD_TOOLS_PATH/whitelist.mdg",
|
|
"$BUILD_TOOLS_PATH/blacklist.mdg",
|
|
"$BUILD_TOOLS_PATH/$MDG_JAVA_DEPENDENCIES",
|
|
"$METADATA_OUT_PATH"
|
|
}
|
|
|
|
cleanSbg.dependsOn(cleanMdg)
|
|
clean.dependsOn(cleanSbg)
|