1 /* 2 * Copyright 2020 Google LLC 3 * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package com.google.devtools.ksp.processing 18 19 import com.google.devtools.ksp.symbol.* 20 import java.io.File 21 import java.io.OutputStream 22 23 /** 24 * [CodeGenerator] creates and manages files. 25 * 26 * Files created by [CodeGenerator] are considered in incremental processing. 27 * Kotlin and Java files will be compiled together with other source files in the module. 28 * Files created without using this API will not participate in incremental processing nor subsequent compilations. 29 */ 30 interface CodeGenerator { 31 /** 32 * Creates a file which is managed by [CodeGenerator] 33 * 34 * Sources of corresponding [KSNode]s which are obtained directly from [Resolver] need to be specified. 35 * Namely, the containing files of those [KSNode]s who are obtained from: 36 * * [Resolver.getAllFiles] 37 * * [Resolver.getSymbolsWithAnnotation] 38 * * [Resolver.getClassDeclarationByName] 39 * 40 * Instead of requiring processors to specify all source files which are relevant in generating the given output, 41 * KSP traces dependencies automatically and only needs to know those sources that only processors know what they 42 * are for. If a [KSFile] is indirectly obtained through other [KSNode]s, it hasn't to be specified for the given 43 * output, even if its contents contribute to the generation of the output. 44 * 45 * For example, a processor generates an output `O` after reading class `A` in `A.kt` and class `B` in `B.kt`, 46 * where `A` extends `B`. The processor got `A` by [Resolver.getSymbolsWithAnnotation] and then got `B` by 47 * [KSClassDeclaration.superTypes] from `A`. Because the inclusion of `B` is due to `A`, `B.kt` needn't to be 48 * specified in [dependencies] for `O`. Note that specifying `B.kt` in this case doesn't hurt, it is only unnecessary. 49 * 50 * @param dependencies are [KSFile]s from which this output is built. Only those that are obtained directly 51 * from [Resolver] are required. 52 * @param packageName corresponds to the relative path of the generated file; using either '.'or '/' as separator. 53 * @param fileName file name 54 * @param extensionName If "kt" or "java", this file will participate in subsequent compilation. 55 * Otherwise its creation is only considered in incremental processing. 56 * @return OutputStream for writing into files. 57 * @see [CodeGenerator] for more details. 58 */ createNewFilenull59 fun createNewFile( 60 dependencies: Dependencies, 61 packageName: String, 62 fileName: String, 63 extensionName: String = "kt" 64 ): OutputStream 65 66 /** 67 * Creates a file which is managed by [CodeGenerator] 68 * 69 * Sources of corresponding [KSNode]s which are obtained directly from [Resolver] need to be specified. 70 * Namely, the containing files of those [KSNode]s who are obtained from: 71 * * [Resolver.getAllFiles] 72 * * [Resolver.getSymbolsWithAnnotation] 73 * * [Resolver.getClassDeclarationByName] 74 * 75 * Instead of requiring processors to specify all source files which are relevant in generating the given output, 76 * KSP traces dependencies automatically and only needs to know those sources that only processors know what they 77 * are for. If a [KSFile] is indirectly obtained through other [KSNode]s, it hasn't to be specified for the given 78 * output, even if its contents contribute to the generation of the output. 79 * 80 * For example, a processor generates an output `O` after reading class `A` in `A.kt` and class `B` in `B.kt`, 81 * where `A` extends `B`. The processor got `A` by [Resolver.getSymbolsWithAnnotation] and then got `B` by 82 * [KSClassDeclaration.superTypes] from `A`. Because the inclusion of `B` is due to `A`, `B.kt` needn't to be 83 * specified in [dependencies] for `O`. Note that specifying `B.kt` in this case doesn't hurt, it is only unnecessary. 84 * 85 * @param dependencies are [KSFile]s from which this output is built. Only those that are obtained directly 86 * from [Resolver] are required. 87 * @param path corresponds to the relative path of the generated file; includes the full file name 88 * @param fileType determines the target directory to store the file 89 * @return OutputStream for writing into files. 90 * @see [CodeGenerator] for more details. 91 */ 92 fun createNewFileByPath( 93 dependencies: Dependencies, 94 path: String, 95 extensionName: String = "kt" 96 ): OutputStream 97 98 /** 99 * Associate [sources] to an output file. 100 * 101 * @param sources are [KSFile]s from which this output is built. Only those that are obtained directly 102 * from [Resolver] are required. 103 * @param packageName corresponds to the relative path of the generated file; using either '.'or '/' as separator. 104 * @param fileName file name 105 * @param extensionName If "kt" or "java", this file will participate in subsequent compilation. 106 * Otherwise its creation is only considered in incremental processing. 107 * @see [CodeGenerator] for more details. 108 */ 109 fun associate(sources: List<KSFile>, packageName: String, fileName: String, extensionName: String = "kt") 110 111 /** 112 * Associate [sources] to an output file. 113 * 114 * @param sources are [KSFile]s from which this output is built. Only those that are obtained directly 115 * from [Resolver] are required. 116 * @param path corresponds to the relative path of the generated file; includes the full file name 117 * @param fileType determines the target directory where the file should exist 118 * @see [CodeGenerator] for more details. 119 */ 120 fun associateByPath(sources: List<KSFile>, path: String, extensionName: String = "kt") 121 122 /** 123 * Associate [classes] to an output file. 124 * 125 * @param classes are [KSClassDeclaration]s from which this output is built. Only those that are obtained directly 126 * from [Resolver] are required. 127 * @param packageName corresponds to the relative path of the generated file; using either '.'or '/' as separator. 128 * @param fileName file name 129 * @param extensionName If "kt" or "java", this file will participate in subsequent compilation. 130 * Otherwise its creation is only considered in incremental processing. 131 * @see [CodeGenerator] for more details. 132 */ 133 fun associateWithClasses( 134 classes: List<KSClassDeclaration>, 135 packageName: String, 136 fileName: String, 137 extensionName: String = "kt" 138 ) 139 140 val generatedFile: Collection<File> 141 } 142 143 /** 144 * Dependencies of an output file. 145 */ 146 class Dependencies private constructor( 147 val isAllSources: Boolean, 148 val aggregating: Boolean, 149 val originatingFiles: List<KSFile> 150 ) { 151 152 /** 153 * Create a [Dependencies] to associate with an output. 154 * 155 * @param aggregating whether the output should be invalidated by a new source file or a change in any of the existing files. 156 * Namely, whenever there is new information. 157 * @param sources Sources for this output to depend on. 158 */ 159 constructor(aggregating: Boolean, vararg sources: KSFile) : this(false, aggregating, sources.toList()) 160 161 companion object { 162 /** 163 * A short-hand to all source files. 164 * 165 * Associating an output to [ALL_SOURCES] essentially disables incremental processing, as the tiniest change will clobber all files. 166 * This should not be used in processors which care about processing speed. 167 */ 168 val ALL_FILES = Dependencies(true, true, emptyList()) 169 } 170 } 171