From 8f3e11947f867c1e644e92c7f4d632efe90f27c7 Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Mon, 27 May 2019 16:02:17 +0300 Subject: [PATCH 1/5] Improve handling of not found interfaces --- .../src/src/com/telerik/metadata/parsing/ClassParser.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/ClassParser.java b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/ClassParser.java index 640dfed3e..044b8bc08 100644 --- a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/ClassParser.java +++ b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/ClassParser.java @@ -29,6 +29,11 @@ private HashSet getAllDefaultMethodsFromImplementedInterfacesR for (String implementedInterfaceName : implementedInterfacesNames) { ClassDescriptor interfaceClass = ClassRepo.findClass(implementedInterfaceName); + if (interfaceClass == null) { + System.out.println(String.format("WARNING: Skipping interface %s implemented in %s as it cannot be resolved", implementedInterfaceName, clazz.getClassName())); + continue; + } + for (MethodDescriptor md : interfaceClass.getMethods()) { if (!md.isStatic() && !md.isAbstract()) { collectedDefaultMethods.add(md); From c68d5a4b043bce9da59af6fe9c80307b89022e15 Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Mon, 27 May 2019 16:35:18 +0300 Subject: [PATCH 2/5] use RuntimeClasspath instead of CompileClasspath https://techblog.bozho.net/runtime-classpath-vs-compile-time-classpath/ --- test-app/app/build.gradle | 90 +++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/test-app/app/build.gradle b/test-app/app/build.gradle index 5a72c7c01..fcf1ca96a 100644 --- a/test-app/app/build.gradle +++ b/test-app/app/build.gradle @@ -20,8 +20,7 @@ import groovy.json.JsonSlurper import java.nio.file.Files import java.nio.file.Paths import java.nio.file.StandardCopyOption -import java.util.regex.Matcher -import java.util.regex.Pattern +import groovy.io.FileType import java.security.MessageDigest import javax.inject.Inject apply plugin: "com.android.application" @@ -518,60 +517,61 @@ class EmptyRunnable implements Runnable { } // Discover all jars and dynamically create tasks for the extraction of each of them -def allJars = [] +project.ext.allJars = [] afterEvaluate { project -> def buildType = project.selectedBuildType == "release" ? "Release" : "Debug" def jars = [] - Pattern pattern = Pattern.compile("^(.+)${buildType}CompileClasspath\$") def artifactType = Attribute.of('artifactType', String) configurations.all { config -> - Matcher matcher = pattern.matcher(config.name) - if (matcher.find() || config.name == "${buildType.toLowerCase()}CompileClasspath") { + if (config.name == "${buildType.toLowerCase()}RuntimeClasspath") { config.incoming.artifactView { attributes { it.attribute(artifactType, 'jar') } }.artifacts.each { - def jar = it.file; - 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) - // } - } - } - allJars.add([file: jar, outputDir: outputDir]) - } + processJar(it.file, jars) + } + } + } +} + +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]) } } @@ -583,7 +583,7 @@ task cleanupAllJars { outputs.files cleanupAllJarsTimestamp doLast { - def allDests = allJars*.outputDir*.name; + def allDests = project.ext.allJars*.outputDir*.name; def dir = new File(extractedDependenciesDir) if (dir.exists()) { dir.eachDir { From 37ed28686ed03129255609d18e71342946536f70 Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Mon, 27 May 2019 16:58:09 +0300 Subject: [PATCH 3/5] get project dependency jars --- test-app/app/build.gradle | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test-app/app/build.gradle b/test-app/app/build.gradle index fcf1ca96a..a33d49bf4 100644 --- a/test-app/app/build.gradle +++ b/test-app/app/build.gradle @@ -531,6 +531,20 @@ afterEvaluate { project -> }.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 + 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) + } + } + } + } } } } From 06f2ac1a87d8156f53f039fe7d6b229cc441ed52 Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Mon, 27 May 2019 17:17:01 +0300 Subject: [PATCH 4/5] remove unneeded semicolons --- test-app/app/build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test-app/app/build.gradle b/test-app/app/build.gradle index a33d49bf4..07db2aa34 100644 --- a/test-app/app/build.gradle +++ b/test-app/app/build.gradle @@ -483,7 +483,7 @@ def explodeAar(File compileDependency, File outputDir) { 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); + Files.copy(inputStream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING) } if (file.isDirectory()) { continue @@ -499,9 +499,9 @@ def explodeAar(File compileDependency, File 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'); + MessageDigest digest = MessageDigest.getInstance("MD5") + digest.update(string.bytes) + return new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0') } class WorkerTask extends DefaultTask { @@ -597,7 +597,7 @@ task cleanupAllJars { outputs.files cleanupAllJarsTimestamp doLast { - def allDests = project.ext.allJars*.outputDir*.name; + def allDests = project.ext.allJars*.outputDir*.name def dir = new File(extractedDependenciesDir) if (dir.exists()) { dir.eachDir { From 2461537c3e83ab98eee57e76aebc9a8a800c8c6b Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Tue, 28 May 2019 11:04:22 +0300 Subject: [PATCH 5/5] add a warning when the dependency jar is not found --- test-app/app/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-app/app/build.gradle b/test-app/app/build.gradle index 07db2aa34..9df9b3711 100644 --- a/test-app/app/build.gradle +++ b/test-app/app/build.gradle @@ -536,6 +536,7 @@ afterEvaluate { project -> 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 -> @@ -543,6 +544,8 @@ afterEvaluate { project -> processJar(file, jars) } } + } else { + println "WARNING: Folder ${jarDir.path} does not exists, the dependent project's classess won't be included in the metadata" } } }