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} '" )
65+
66+ val aarFile = getAarFile(compilation)
67+
68+ if (! aarFile.exists()) {
69+ throw IllegalStateException (" AAR file not found: ${aarFile.absolutePath} " )
70+ }
2871
29- createAndroidBenchmarkExecTask(target, generateSourcesTask)
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+ }
77+ }
78+
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+ }
30103}
31104
32- fun Project. detectAndroidDevice () {
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 )
@@ -92,7 +165,7 @@ fun Project.createAndroidBenchmarkExecTask(target: KotlinJvmAndroidCompilation,
92165 }
93166}
94167
95- class StreamGobbler (private val inputStream : InputStream , private val consumer : (String ) -> Unit ) : Thread() {
168+ private class StreamGobbler (private val inputStream : InputStream , private val consumer : (String ) -> Unit ) : Thread() {
96169 override fun run () {
97170 inputStream.bufferedReader().lines().forEach(consumer)
98171 }
0 commit comments