1 /*
2  * Copyright 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.appfunctions.compiler
18 
19 import androidx.appfunctions.compiler.core.AnnotatedAppFunctions
20 import androidx.appfunctions.compiler.core.AppFunctionSymbolResolver
21 import androidx.appfunctions.compiler.core.ProcessingException
22 import androidx.appfunctions.compiler.core.SymbolNotReadyException
23 import androidx.appfunctions.compiler.core.logException
24 import androidx.appfunctions.compiler.processors.AppFunctionAggregateProcessor
25 import androidx.appfunctions.compiler.processors.AppFunctionFunctionRegistryProcessor
26 import androidx.appfunctions.compiler.processors.AppFunctionIdProcessor
27 import androidx.appfunctions.compiler.processors.AppFunctionInventoryProcessor
28 import androidx.appfunctions.compiler.processors.AppFunctionInvokerProcessor
29 import androidx.appfunctions.compiler.processors.AppFunctionSerializableProcessor
30 import com.google.devtools.ksp.processing.KSPLogger
31 import com.google.devtools.ksp.processing.Resolver
32 import com.google.devtools.ksp.processing.SymbolProcessor
33 import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
34 import com.google.devtools.ksp.processing.SymbolProcessorProvider
35 import com.google.devtools.ksp.symbol.KSAnnotated
36 import com.squareup.kotlinpoet.AnnotationSpec
37 import javax.annotation.processing.Generated
38 
39 /** The compiler to process AppFunction implementations. */
40 class AppFunctionCompiler(
41     private val processors: List<SymbolProcessor>,
42     private val logger: KSPLogger,
43 ) : SymbolProcessor {
44 
processnull45     override fun process(resolver: Resolver): List<KSAnnotated> {
46         return try {
47             val deferred = shouldDeferAllProcessing(resolver)
48             if (deferred.isNotEmpty()) {
49                 deferred
50             } else {
51                 buildList {
52                     for (processor in processors) {
53                         addAll(processor.process(resolver))
54                     }
55                 }
56             }
57         } catch (e: ProcessingException) {
58             logger.logException(e)
59             emptyList()
60         }
61     }
62 
63     /**
64      * Returns a non-empty list of [KSAnnotated] nodes if the processor should defer all these
65      * symbols.
66      *
67      * To ensure that all generated components are recorded in AppFunctionComponentRegistry in each
68      * compilation unit, the processor should start the processing only when all the nodes are
69      * ready.
70      */
shouldDeferAllProcessingnull71     private fun shouldDeferAllProcessing(resolver: Resolver): List<KSAnnotated> {
72         val appFunctionSymbolResolver = AppFunctionSymbolResolver(resolver)
73         val annotatedAppFunctions = appFunctionSymbolResolver.resolveAnnotatedAppFunctions()
74         for (annotatedAppFunction in annotatedAppFunctions) {
75             try {
76                 annotatedAppFunction.validate()
77             } catch (e: SymbolNotReadyException) {
78                 logger.logging(e.message.toString(), e.node)
79                 return annotatedAppFunctions.flatMap(AnnotatedAppFunctions::getAllAnnotated)
80             }
81         }
82         return emptyList()
83     }
84 
85     class Provider : SymbolProcessorProvider {
86 
createnull87         override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
88             val options = AppFunctionCompilerOptions.from(environment.options)
89 
90             val functionRegistryProcessor =
91                 AppFunctionFunctionRegistryProcessor(environment.codeGenerator)
92             val idProcessor = AppFunctionIdProcessor(environment.codeGenerator)
93             val inventoryProcessor = AppFunctionInventoryProcessor(environment.codeGenerator)
94             val invokerProcessor = AppFunctionInvokerProcessor(environment.codeGenerator)
95             val entityProcessor =
96                 AppFunctionSerializableProcessor(environment.codeGenerator, environment.logger)
97             val aggregateProcessor =
98                 AppFunctionAggregateProcessor(options, environment.codeGenerator)
99             return AppFunctionCompiler(
100                 listOf(
101                     functionRegistryProcessor,
102                     idProcessor,
103                     inventoryProcessor,
104                     invokerProcessor,
105                     entityProcessor,
106                     aggregateProcessor,
107                 ),
108                 environment.logger,
109             )
110         }
111     }
112 
113     companion object {
114         internal val GENERATED_ANNOTATION =
115             AnnotationSpec.builder(Generated::class)
116                 .addMember("%S", AppFunctionCompiler::class.java.canonicalName)
117                 .build()
118     }
119 }
120