11package kotlinx.benchmark.gradle
22
3- import kotlinx.benchmark.gradle.internal.KotlinxBenchmarkPluginInternalApi
43import org.gradle.api.*
5- import org.gradle.api.tasks.TaskProvider
64import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
75import java.io.InputStream
86import java.util.*
97import java.util.concurrent.TimeUnit
108
9+ private const val GENERATED_ANDROID_PROJECT_NAME = " GeneratedAndroidProject"
1110
12- @KotlinxBenchmarkPluginInternalApi
13- fun Project.processAndroidCompilation (target : KotlinJvmAndroidCompilation ) {
14- project.logger.info(" Configuring benchmarks for '${target.name} ' using Kotlin/Android" )
15- println (" processAndroidCompilation: ${target.name} " )
16- val compilation = target.target.compilations.names.let (::println)
11+ internal fun Project.processAndroidCompilation (target : AndroidBenchmarkTarget , compilation : KotlinJvmAndroidCompilation ) {
12+ project.logger.info(" Configuring benchmarks for '${compilation.name} ' using $target " )
1713
18- val generateSourcesTask = tasks.register(" processAndroid${target.name.capitalize(Locale .getDefault())} Compilation" , DefaultTask ::class .java) {
19- it.group = " benchmark"
20- it.description = " Processes the Android compilation '${target.name} ' for benchmarks"
21- it.dependsOn(" bundle${target.name.capitalize(Locale .getDefault())} Aar" )
22- it.doLast {
23- unpackAndProcessAar(target) { classDescriptors ->
24- generateBenchmarkSourceFiles(classDescriptors)
14+ createUnpackAarTask(target, compilation)
15+ createSetupAndroidProjectTask(target, compilation)
16+ createAndroidBenchmarkGenerateSourceTask(target, compilation)
17+ createAndroidBenchmarkExecTask(target, compilation)
18+ }
19+
20+ private fun Project.androidBenchmarkBuildDir (target : AndroidBenchmarkTarget , compilation : KotlinJvmAndroidCompilation ) =
21+ benchmarkBuildDir(target).resolve(compilation.name)
22+
23+ private fun Project.generatedAndroidProjectDir (target : AndroidBenchmarkTarget , compilation : KotlinJvmAndroidCompilation ) =
24+ androidBenchmarkBuildDir(target, compilation).resolve(GENERATED_ANDROID_PROJECT_NAME )
25+
26+ private fun Project.createSetupAndroidProjectTask (target : AndroidBenchmarkTarget , compilation : KotlinJvmAndroidCompilation ) {
27+ task<DefaultTask >(" setup${compilation.name.capitalize()} AndroidProject" ) {
28+ group = " benchmark"
29+ description = " Sets up an empty android project to generate benchmarks into"
30+
31+ doFirst {
32+ sync {
33+ it.apply {
34+ val pluginJarPath = BenchmarksPlugin ::class .java.protectionDomain.codeSource.location.path
35+ from(project.zipTree(pluginJarPath))
36+ into(androidBenchmarkBuildDir(target, compilation))
37+ include(" $GENERATED_ANDROID_PROJECT_NAME /**" )
38+ }
39+ }
40+ }
41+ doLast {
42+ val generatedAndroidProjectDir = generatedAndroidProjectDir(target, compilation)
43+ logger.info(" Setting up an empty Android project at $generatedAndroidProjectDir " )
44+
45+ generatedAndroidProjectDir.resolve(" microbenchmark/build.gradle.kts" ).let {
46+ val unpackedDir = getUnpackAarDir(compilation)
47+ val newText = it.readText().replace(
48+ " <<BENCHMARK_CLASSES_JAR_PATH>>" ,
49+ unpackedDir.resolve(" classes.jar" ).absolutePath
50+ )
51+ it.writeText(newText)
2552 }
2653 }
2754 }
55+ }
56+
57+ private fun Project.createUnpackAarTask (target : AndroidBenchmarkTarget , compilation : KotlinJvmAndroidCompilation ) {
58+ // TODO: capitalize(Locale.ROOT) everywhere in the project. For toLower/UpperCase() as well.
59+ task<DefaultTask >(" unpack${compilation.name.capitalize()} Aar" ) {
60+ group = " benchmark"
61+ description = " Unpacks the AAR file produced by ${target.name} compilation '${compilation.name} '"
62+ dependsOn(" bundle${compilation.name.capitalize()} Aar" )
63+ doLast {
64+ logger.info(" Unpacking AAR file produced by ${target.name} compilation '${compilation.name} '" )
2865
29- createAndroidBenchmarkExecTask(target, generateSourcesTask)
66+ val aarFile = getAarFile(compilation)
67+
68+ if (! aarFile.exists()) {
69+ throw IllegalStateException (" AAR file not found: ${aarFile.absolutePath} " )
70+ }
71+
72+ // TODO: Register the unpacked dir as an output of this task
73+ // TODO: Delete the directory if exists before unpacking
74+ unpackAarFile(aarFile, compilation)
75+ }
76+ }
3077}
3178
32- fun Project.detectAndroidDevice () {
79+ private fun generateSourcesTaskName (target : AndroidBenchmarkTarget , compilation : KotlinJvmAndroidCompilation ): String {
80+ return " ${target.name}${compilation.name.capitalize()}${BenchmarksPlugin .BENCHMARK_GENERATE_SUFFIX } "
81+ }
82+
83+ private fun Project.createAndroidBenchmarkGenerateSourceTask (target : AndroidBenchmarkTarget , compilation : KotlinJvmAndroidCompilation ) {
84+ task<DefaultTask >(generateSourcesTaskName(target, compilation)) {
85+ group = " benchmark"
86+ description = " Generates Android source files for ${target.name} compilation '${compilation.name} '"
87+ dependsOn(" unpack${compilation.name.capitalize()} Aar" )
88+ dependsOn(" setup${compilation.name.capitalize()} AndroidProject" )
89+
90+ doLast {
91+
92+ val unpackedDir = getUnpackAarDir(compilation)
93+ processClassesJar(unpackedDir, compilation) { classDescriptors ->
94+ val targetDir = generatedAndroidProjectDir(target, compilation)
95+ .resolve(" microbenchmark/src/androidTest/kotlin" )
96+
97+ check(targetDir.exists())
98+
99+ generateBenchmarkSourceFiles(targetDir, classDescriptors)
100+ }
101+ }
102+ }
103+ }
104+
105+ private fun detectAndroidDevice () {
33106 println (" Detect running Android devices..." )
34107 val devices = ProcessBuilder (" adb" , " devices" )
35108 .start()
@@ -48,16 +121,16 @@ fun Project.detectAndroidDevice() {
48121
49122
50123// Use shell command to execute separate project gradle task
51- fun Project.createAndroidBenchmarkExecTask (target : KotlinJvmAndroidCompilation , generateSourcesTask : TaskProvider < * > ) {
52- tasks.register (" android${target .name.capitalize(Locale .getDefault()) } Benchmark" , DefaultTask :: class .java ) {
53- it. group = " benchmark"
54- it. description = " Processes the Android compilation '${target .name} ' for benchmarks "
55- it. dependsOn(generateSourcesTask )
56- it. doLast {
124+ private fun Project.createAndroidBenchmarkExecTask (target : AndroidBenchmarkTarget , compilation : KotlinJvmAndroidCompilation ) {
125+ task< DefaultTask > (" android${compilation .name.capitalize() } Benchmark" ) {
126+ group = " benchmark"
127+ description = " Executes benchmarks for ${target.name} compilation '${compilation .name} '"
128+ dependsOn(generateSourcesTaskName(target, compilation) )
129+ doLast {
57130 detectAndroidDevice()
58131
59132 // TODO: Project path needs to execute benchmark task
60- val executeBenchmarkPath = " /Users/abduqodiri.qurbonzoda_1/AndroidStudioProjects/kotlin-qualification-task "
133+ val executeBenchmarkPath = generatedAndroidProjectDir(target, compilation).path
61134 // Using ./gradlew on Windows shows error:
62135 // CreateProcess error=193, %1 is not a valid Win32 application
63136 val osName = System .getProperty(" os.name" ).toLowerCase(Locale .ROOT )
@@ -83,7 +156,6 @@ fun Project.createAndroidBenchmarkExecTask(target: KotlinJvmAndroidCompilation,
83156 outputGobbler.start()
84157 errorGobbler.start()
85158
86-
87159 clearLogcat()
88160 val exitCode = process.waitFor(10 , TimeUnit .MINUTES )
89161 captureLogcatOutput()
@@ -138,13 +210,9 @@ private fun clearLogcat() {
138210 }
139211}
140212
141- class StreamGobbler (
142- private val inputStream : InputStream ,
143- private val consumer : (String ) -> Unit
144- ) : Thread() {
213+
214+ private class StreamGobbler (private val inputStream : InputStream , private val consumer : (String ) -> Unit ) : Thread() {
145215 override fun run () {
146- inputStream.bufferedReader().useLines { lines ->
147- lines.forEach { consumer(it) }
148- }
216+ inputStream.bufferedReader().lines().forEach(consumer)
149217 }
150218}
0 commit comments