[go: nahoru, domu]

Call AIDL generation from KSP processor

Test: PrivacySandboxKspCompilerTest
Bug: 243918439
Change-Id: Icddb75ac37c6569389f112019c0f7ba963519516
diff --git a/privacysandbox/tools/tools-apicompiler/build.gradle b/privacysandbox/tools/tools-apicompiler/build.gradle
index fea0b81..d185ba8 100644
--- a/privacysandbox/tools/tools-apicompiler/build.gradle
+++ b/privacysandbox/tools/tools-apicompiler/build.gradle
@@ -15,6 +15,8 @@
  */
 
 import androidx.build.LibraryType
+import androidx.build.SdkHelperKt
+import androidx.build.SupportConfig
 
 plugins {
     id("AndroidXPlugin")
@@ -30,6 +32,21 @@
     testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(libs.junit)
     testImplementation(libs.truth)
+    // Include android jar for compilation of generated sources.
+    testImplementation(fileTree(
+            dir: "${SdkHelperKt.getSdkPath(project)}/platforms/$SupportConfig.COMPILE_SDK_VERSION/",
+            include: "android.jar"
+    ))
+    // Get AIDL compiler path and pass it to tests for code generation.
+    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.BUILD_TOOLS_VERSION}/aidl"
+    test {
+        inputs.files(aidlCompilerPath)
+                .withPropertyName("aidl_compiler_path")
+                .withPathSensitivity(PathSensitivity.NAME_ONLY)
+        doFirst {
+            systemProperty "aidl_compiler_path", aidlCompilerPath
+        }
+    }
 }
 
 androidx {
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompiler.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompiler.kt
index 3423e89..672311d 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompiler.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompiler.kt
@@ -16,42 +16,52 @@
 
 package androidx.privacysandbox.tools.apicompiler
 
+import androidx.privacysandbox.tools.apicompiler.generator.SdkCodeGenerator
 import androidx.privacysandbox.tools.apicompiler.parser.ApiParser
 import com.google.devtools.ksp.processing.CodeGenerator
-import com.google.devtools.ksp.processing.Dependencies
 import com.google.devtools.ksp.processing.KSPLogger
 import com.google.devtools.ksp.processing.Resolver
 import com.google.devtools.ksp.processing.SymbolProcessor
 import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 import com.google.devtools.ksp.processing.SymbolProcessorProvider
 import com.google.devtools.ksp.symbol.KSAnnotated
+import java.nio.file.Paths
 
 class PrivacySandboxKspCompiler(
     private val logger: KSPLogger,
-    private val codeGenerator: CodeGenerator
+    private val codeGenerator: CodeGenerator,
+    private val options: Map<String, String>,
 ) :
     SymbolProcessor {
+    companion object {
+        const val AIDL_COMPILER_PATH_OPTIONS_KEY = "aidl_compiler_path"
+    }
+
     var invoked = false
 
     override fun process(resolver: Resolver): List<KSAnnotated> {
         if (invoked) {
             return emptyList()
         }
-        ApiParser(resolver, logger).parseApi()
-        // TODO: remove once actual conde generation is in place.
-        createTestFile()
         invoked = true
-        return emptyList()
-    }
+        val parsedApi = ApiParser(resolver, logger).parseApi()
 
-    private fun createTestFile() {
-        val testFile = codeGenerator.createNewFile(Dependencies(false), "", "TestFile", "txt")
-        testFile.write("TestFile".toByteArray())
+        val path = options[AIDL_COMPILER_PATH_OPTIONS_KEY]?.let(Paths::get)
+        if (path == null) {
+            logger.error("KSP argument '$AIDL_COMPILER_PATH_OPTIONS_KEY' was not set.")
+            return emptyList()
+        }
+        SdkCodeGenerator(codeGenerator, parsedApi, path).generate()
+        return emptyList()
     }
 
     class Provider : SymbolProcessorProvider {
         override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
-            return PrivacySandboxKspCompiler(environment.logger, environment.codeGenerator)
+            return PrivacySandboxKspCompiler(
+                environment.logger,
+                environment.codeGenerator,
+                environment.options
+            )
         }
     }
 }
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt
new file mode 100644
index 0000000..b080ea3
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.tools.apicompiler.generator
+
+import androidx.privacysandbox.tools.core.ParsedApi
+import androidx.privacysandbox.tools.core.generator.AidlCompiler
+import androidx.privacysandbox.tools.core.generator.AidlGenerator
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.Dependencies
+import java.nio.file.Files.createTempDirectory
+import java.nio.file.Path
+
+class SdkCodeGenerator(
+    private val codeGenerator: CodeGenerator,
+    private val api: ParsedApi,
+    private val aidlCompilerPath: Path,
+) {
+    fun generate() {
+        generateAidlSources()
+    }
+
+    private fun generateAidlSources() {
+        val workingDir = createTempDirectory("aidl")
+        try {
+            AidlGenerator.generate(AidlCompiler(aidlCompilerPath), api, workingDir)
+                .forEach { source ->
+                    // Sources created by the AIDL compiler have to be copied to files created through the
+                    // KSP APIs, so that they are included in downstream compilation.
+                    val kspGeneratedFile = codeGenerator.createNewFile(
+                        Dependencies(false),
+                        source.packageName,
+                        source.interfaceName,
+                        extensionName = "java"
+                    )
+                    source.file.inputStream().copyTo(kspGeneratedFile)
+                }
+        } finally {
+            workingDir.toFile().deleteRecursively()
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
index 168aaf8..9e3b644 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
@@ -37,8 +37,8 @@
                     import androidx.privacysandbox.tools.PrivacySandboxService
                     @PrivacySandboxService
                     interface MySdk {
-                        suspend fun doStuff(x: Int, y: Int): String
-                        suspend fun doMoreStuff()
+                        fun doStuff(x: Int, y: Int): String
+                        fun doMoreStuff()
                     }
                 """
             )
@@ -50,9 +50,15 @@
                 TestCompilationArguments(
                     sources = listOf(source),
                     symbolProcessorProviders = listOf(provider),
+                    processorOptions = getProcessorOptions(),
                 )
             )
-        ).succeeds()
+        ).generatesExactlySources(
+            "com/mysdk/IMySdk.java",
+            "com/mysdk/ICancellationSignal.java",
+            "com/mysdk/IUnitTransactionCallback.java",
+            "com/mysdk/IStringTransactionCallback.java",
+        )
     }
 
     @Test
@@ -77,9 +83,16 @@
                 Files.createTempDirectory("test").toFile(),
                 TestCompilationArguments(
                     sources = listOf(source),
-                    symbolProcessorProviders = listOf(provider)
+                    symbolProcessorProviders = listOf(provider),
+                    processorOptions = getProcessorOptions(),
                 )
             )
         ).fails()
     }
+
+    private fun getProcessorOptions() =
+        mapOf(
+            "aidl_compiler_path" to (System.getProperty("aidl_compiler_path")
+                ?: throw IllegalArgumentException("aidl_compiler_path flag not set."))
+        )
 }
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
index 24eaf8c..394d8cf 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
@@ -72,6 +72,12 @@
         assertWithMessage("UnexpectedErrors: ${getErrorMessages()}").that(result.success).isTrue()
     }
 
+    fun generatesExactlySources(vararg sourcePaths: String) {
+        succeeds()
+        assertThat(result.generatedSources.map(Source::relativePath))
+            .containsExactlyElementsIn(sourcePaths)
+    }
+
     fun fails() {
         assertThat(result.success).isFalse()
     }