1
1
/*
2
- * Script builds apk in release or debug mode
3
- * To run:
4
- * gradle assembleRelease -Prelease (release mode)
5
- * gradle assembleDebug (debug mode -> default)
6
- * Options:
7
- * -Prelease //this flag will run build in release mode
8
- * -PksPath=[path_to_keystore_file]
9
- * -PksPassword=[password_for_keystore_file]
10
- * -Palias=[alias_to_use_from_keystore_file]
11
- * -Ppassword=[password_for_alias]
2
+ * Script builds apk in release or debug mode
3
+ * To run:
4
+ * gradle assembleRelease -Prelease (release mode)
5
+ * gradle assembleDebug (debug mode -> default)
6
+ * Options:
7
+ * -Prelease //this flag will run build in release mode
8
+ * -PksPath=[path_to_keystore_file]
9
+ * -PksPassword=[password_for_keystore_file]
10
+ * -Palias=[alias_to_use_from_keystore_file]
11
+ * -Ppassword=[password_for_alias]
12
12
*
13
- * -PtargetSdk=[target_sdk]
14
- * -PbuildToolsVersion=[build_tools_version]
15
- * -PsupportVersion=[support_version]
16
- * -PcompileSdk=[compile_sdk_version]
13
+ * -PtargetSdk=[target_sdk]
14
+ * -PbuildToolsVersion=[build_tools_version]
15
+ * -PsupportVersion=[support_version]
16
+ * -PcompileSdk=[compile_sdk_version]
17
17
*/
18
18
19
19
import groovy.json.JsonSlurper
20
+ import java.nio.file.Files
20
21
import java.nio.file.Paths
22
+ import java.nio.file.StandardCopyOption
21
23
import java.util.regex.Matcher
22
24
import java.util.regex.Pattern
23
-
25
+ import java.security.MessageDigest
26
+ import javax.inject.Inject
24
27
apply plugin : " com.android.application"
25
28
26
29
def onlyX86 = project. hasProperty(" onlyX86" )
@@ -80,6 +83,8 @@ version of the {N} CLI install a previous version of the runtime package - 'tns
80
83
}
81
84
82
85
project. ext. extractedDependenciesDir = " ${ project.buildDir} /exploded-dependencies"
86
+ project. ext. cleanupAllJarsTimestamp = " ${ project.buildDir} /cleanupAllJars.timestamp"
87
+ project. ext. extractAllJarsTimestamp = " ${ project.buildDir} /extractAllJars.timestamp"
83
88
project. ext. nativescriptDependencies = new JsonSlurper (). parseText(dependenciesJson. text)
84
89
project. ext. PLATFORMS_ANDROID = " platforms/android"
85
90
project. ext. USER_PROJECT_ROOT = " $rootDir /../.."
@@ -257,10 +262,11 @@ android {
257
262
}
258
263
259
264
def externalRuntimeExists = ! findProject(' :runtime' ). is(null )
265
+ def pluginDependencies
260
266
261
267
repositories {
262
268
// used for local *.AAR files
263
- def pluginDependencies = nativescriptDependencies. collect {
269
+ pluginDependencies = nativescriptDependencies. collect {
264
270
" $rootDir /${ it.directory} /$PLATFORMS_ANDROID "
265
271
}
266
272
@@ -416,11 +422,14 @@ tasks.whenTaskAdded({ org.gradle.api.DefaultTask currentTask ->
416
422
// //////////////////////////////////////////////////////////////////////////////////
417
423
418
424
task runSbg (type : JavaExec ) {
425
+ dependsOn " collectAllJars"
419
426
if (! findProject(' :static-binding-generator' ). is(null )) {
420
427
dependsOn ' :static-binding-generator:jar'
421
428
}
422
429
430
+ outputs. dir(OUTPUT_JAVA_DIR )
423
431
inputs. dir(INPUT_JS_DIR )
432
+ inputs. dir(extractedDependenciesDir)
424
433
425
434
workingDir " $BUILD_TOOLS_PATH "
426
435
main " -jar"
@@ -438,22 +447,19 @@ task ensureMetadataOutDir {
438
447
}
439
448
}
440
449
441
- def explodeAar (File compileDependency , String outputDir ) {
450
+ def explodeAar (File compileDependency , File outputDir ) {
451
+ logger. info(" explodeAar: Extracting ${ compileDependency.path} -> ${ outputDir.path} " )
452
+
442
453
if (compileDependency. name. endsWith(" .aar" )) {
443
454
java.util.jar.JarFile jar = new java.util.jar.JarFile (compileDependency)
444
455
Enumeration enumEntries = jar. entries()
445
456
while (enumEntries. hasMoreElements()) {
446
457
java.util.jar.JarEntry file = (java.util.jar.JarEntry ) enumEntries. nextElement()
447
458
if (file. name. endsWith(" .jar" )) {
448
- def f = new File (outputDir, file. name)
449
- new File (f. parent). mkdirs()
450
- InputStream is = jar. getInputStream(file)
451
- FileOutputStream fos = new FileOutputStream (f)
452
- while (is. available() > 0 ) {
453
- fos. write(is. read())
454
- }
455
- fos. close()
456
- is. close()
459
+ def targetFile = new File (outputDir, file. name)
460
+ InputStream inputStream = jar. getInputStream(file)
461
+ new File (targetFile. parent). mkdirs()
462
+ Files . copy(inputStream, targetFile. toPath(), StandardCopyOption . REPLACE_EXISTING );
457
463
}
458
464
if (file. isDirectory()) {
459
465
continue
@@ -468,40 +474,111 @@ def explodeAar(File compileDependency, String outputDir) {
468
474
}
469
475
}
470
476
471
- task extractAllJars {
472
- outputs. dir extractedDependenciesDir
477
+ def md5 (String string ) {
478
+ MessageDigest digest = MessageDigest . getInstance(" MD5" ) ;
479
+ digest. update(string. bytes);
480
+ return new BigInteger (1 , digest. digest()). toString(16 ). padLeft(32 , ' 0' );
481
+ }
473
482
474
- doLast {
475
- def buildType = project. selectedBuildType == " release" ? " Release" : " Debug"
476
- def iter = []
477
- Pattern pattern = Pattern . compile(" ^(.+)${ buildType} CompileClasspath\$ " )
478
- def artifactType = Attribute . of(' artifactType' , String )
479
- configurations. all { config ->
480
- Matcher matcher = pattern. matcher(config. name)
481
- if (matcher. find() || config. name == " ${ buildType.toLowerCase()} CompileClasspath" ) {
482
- config. incoming. artifactView {
483
- attributes {
484
- it. attribute(artifactType, ' jar' )
485
- }
486
- }. artifacts. each {
487
- if (! iter. contains(it. file)) {
488
- iter. push(it. file)
483
+ class WorkerTask extends DefaultTask {
484
+ @Inject
485
+ WorkerExecutor getWorkerExecutor () {
486
+ throw new UnsupportedOperationException ()
487
+ }
488
+ }
489
+
490
+ class EmptyRunnable implements Runnable {
491
+ void run () {
492
+ }
493
+ }
494
+
495
+ // Discover all jars and dynamically create tasks for the extraction of each of them
496
+ def allJars = []
497
+ afterEvaluate { project ->
498
+ def buildType = project. selectedBuildType == " release" ? " Release" : " Debug"
499
+ def jars = []
500
+ Pattern pattern = Pattern . compile(" ^(.+)${ buildType} CompileClasspath\$ " )
501
+ def artifactType = Attribute . of(' artifactType' , String )
502
+ configurations. all { config ->
503
+ Matcher matcher = pattern. matcher(config. name)
504
+ if (matcher. find() || config. name == " ${ buildType.toLowerCase()} CompileClasspath" ) {
505
+ config. incoming. artifactView {
506
+ attributes {
507
+ it. attribute(artifactType, ' jar' )
508
+ }
509
+ }. artifacts. each {
510
+ def jar = it. file;
511
+ if (! jars. contains(jar)) {
512
+ jars. push(jar)
513
+ def destDir = md5(jar. path);
514
+ def outputDir = new File (Paths . get(extractedDependenciesDir, destDir). normalize(). toString())
515
+
516
+ def taskName = " extract_${ jar.name} _to_${ destDir} "
517
+ logger. debug(" Creating dynamic task ${ taskName} " )
518
+ task " ${ taskName} " (type : WorkerTask ) {
519
+ dependsOn cleanupAllJars
520
+ extractAllJars. dependsOn it
521
+
522
+ // This dependency seems redundant but probably due to some Gradle issue with workers,
523
+ // without it `runSbg` sporadically starts before all extraction tasks have finished and
524
+ // fails due to missing JARs
525
+ runSbg. dependsOn it
526
+
527
+ inputs. files jar
528
+ outputs. dir outputDir
529
+
530
+ doLast {
531
+ // Runing in parallel no longer seems to bring any benefit.
532
+ // It mattered only when we were extracting JARs from AARs.
533
+ // To try it simply remove the following comments.
534
+ // workerExecutor.submit(EmptyRunnable.class) {
535
+ explodeAar(jar, outputDir)
536
+ // }
537
+ }
489
538
}
539
+ allJars. push([file : jar, outputDir : outputDir])
490
540
}
491
541
}
492
542
}
543
+ }
544
+ }
493
545
494
- def dependencyCounter = 0
495
- iter. each {
496
- def nextDependency = it
497
- def outputDir = java.nio.file.Paths . get(extractedDependenciesDir, " " + dependencyCounter). normalize(). toString()
498
- explodeAar(nextDependency, outputDir)
499
- dependencyCounter++
546
+ task cleanupAllJars {
547
+ // Ideally we would depend on the list of all discovered jars, but we cannot discover them before
548
+ // the execution phase of the build begins, so instead we depend on the list of libs directories that might
549
+ // contain aar or jar files.
550
+ inputs. files(pluginDependencies)
551
+ outputs. files cleanupAllJarsTimestamp
552
+
553
+ doLast {
554
+ def allDests = allJars* . outputDir* . name;
555
+ def dir = new File (extractedDependenciesDir)
556
+ if (dir. exists()) {
557
+ dir. eachDir {
558
+ // An old directory which is no longer a dependency (e.g. orphaned by a deleted plugin)
559
+ if (! allDests. contains(it. name)) {
560
+ logger. info(" Task cleanupAllJars: Deleting orphaned ${ it.path} " )
561
+ org.apache.commons.io.FileUtils . deleteDirectory(it)
562
+ }
563
+ }
500
564
}
565
+ new File (cleanupAllJarsTimestamp). write " "
566
+ }
567
+ }
568
+
569
+
570
+ // Placeholder task which depends on all dynamically generated extraction tasks
571
+ task extractAllJars {
572
+ dependsOn cleanupAllJars
573
+ outputs. files extractAllJarsTimestamp
574
+
575
+ doLast {
576
+ new File (cleanupAllJarsTimestamp). write " "
501
577
}
502
578
}
503
579
504
580
task collectAllJars {
581
+ dependsOn extractAllJars
505
582
description " gathers all paths to jar dependencies before building metadata with them"
506
583
507
584
def sdkPath = android. sdkDirectory. getAbsolutePath()
0 commit comments