Skip to content

Commit 060a268

Browse files
authored
Merge pull request #1308 from NativeScript/bektchiev/fix-sbg-jars-deps
fix(gradle): Fix `runSbg` and `collectAllJars` tasks' dependencies
2 parents 102edea + ed72de1 commit 060a268

File tree

1 file changed

+126
-49
lines changed

1 file changed

+126
-49
lines changed

test-app/app/build.gradle

Lines changed: 126 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
/*
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]
1212
*
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]
1717
*/
1818

1919
import groovy.json.JsonSlurper
20+
import java.nio.file.Files
2021
import java.nio.file.Paths
22+
import java.nio.file.StandardCopyOption
2123
import java.util.regex.Matcher
2224
import java.util.regex.Pattern
23-
25+
import java.security.MessageDigest
26+
import javax.inject.Inject
2427
apply plugin: "com.android.application"
2528

2629
def onlyX86 = project.hasProperty("onlyX86")
@@ -80,6 +83,8 @@ version of the {N} CLI install a previous version of the runtime package - 'tns
8083
}
8184

8285
project.ext.extractedDependenciesDir = "${project.buildDir}/exploded-dependencies"
86+
project.ext.cleanupAllJarsTimestamp = "${project.buildDir}/cleanupAllJars.timestamp"
87+
project.ext.extractAllJarsTimestamp = "${project.buildDir}/extractAllJars.timestamp"
8388
project.ext.nativescriptDependencies = new JsonSlurper().parseText(dependenciesJson.text)
8489
project.ext.PLATFORMS_ANDROID = "platforms/android"
8590
project.ext.USER_PROJECT_ROOT = "$rootDir/../.."
@@ -257,10 +262,11 @@ android {
257262
}
258263

259264
def externalRuntimeExists = !findProject(':runtime').is(null)
265+
def pluginDependencies
260266

261267
repositories {
262268
// used for local *.AAR files
263-
def pluginDependencies = nativescriptDependencies.collect {
269+
pluginDependencies = nativescriptDependencies.collect {
264270
"$rootDir/${it.directory}/$PLATFORMS_ANDROID"
265271
}
266272

@@ -416,11 +422,14 @@ tasks.whenTaskAdded({ org.gradle.api.DefaultTask currentTask ->
416422
////////////////////////////////////////////////////////////////////////////////////
417423

418424
task runSbg(type: JavaExec) {
425+
dependsOn "collectAllJars"
419426
if (!findProject(':static-binding-generator').is(null)) {
420427
dependsOn ':static-binding-generator:jar'
421428
}
422429

430+
outputs.dir(OUTPUT_JAVA_DIR)
423431
inputs.dir(INPUT_JS_DIR)
432+
inputs.dir(extractedDependenciesDir)
424433

425434
workingDir "$BUILD_TOOLS_PATH"
426435
main "-jar"
@@ -438,22 +447,19 @@ task ensureMetadataOutDir {
438447
}
439448
}
440449

441-
def explodeAar(File compileDependency, String outputDir) {
450+
def explodeAar(File compileDependency, File outputDir) {
451+
logger.info("explodeAar: Extracting ${compileDependency.path} -> ${outputDir.path}")
452+
442453
if (compileDependency.name.endsWith(".aar")) {
443454
java.util.jar.JarFile jar = new java.util.jar.JarFile(compileDependency)
444455
Enumeration enumEntries = jar.entries()
445456
while (enumEntries.hasMoreElements()) {
446457
java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement()
447458
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);
457463
}
458464
if (file.isDirectory()) {
459465
continue
@@ -468,40 +474,111 @@ def explodeAar(File compileDependency, String outputDir) {
468474
}
469475
}
470476

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+
}
473482

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+
}
489538
}
539+
allJars.push([file: jar, outputDir: outputDir])
490540
}
491541
}
492542
}
543+
}
544+
}
493545

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+
}
500564
}
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 ""
501577
}
502578
}
503579

504580
task collectAllJars {
581+
dependsOn extractAllJars
505582
description "gathers all paths to jar dependencies before building metadata with them"
506583

507584
def sdkPath = android.sdkDirectory.getAbsolutePath()

0 commit comments

Comments
 (0)