|
20 | 20 | import org.gradle.api.tasks.Input; |
21 | 21 | import org.gradle.api.tasks.SourceSet; |
22 | 22 | import org.gradle.api.tasks.SourceSetContainer; |
| 23 | +import org.gradle.api.tasks.TaskProvider; |
| 24 | +import org.gradle.api.tasks.TaskState; |
23 | 25 | import org.gradle.api.tasks.compile.CompileOptions; |
24 | 26 | import org.gradle.api.tasks.compile.JavaCompile; |
25 | 27 | import org.gradle.process.CommandLineArgumentProvider; |
@@ -137,11 +139,43 @@ private void configureProject(Project project) { |
137 | 139 | } |
138 | 140 | } |
139 | 141 |
|
| 142 | + // Kotlin sources are not indexed inside kotlinc: after the Kotlin compile |
| 143 | + // tasks, their sources and classpath are dumped for the scip-java CLI to |
| 144 | + // index after the build, decoupling indexing from the build's Kotlin |
| 145 | + // compiler version. The dump is a finalizer rather than an action of the |
| 146 | + // compile tasks, because Gradle skips their actions when they are up to |
| 147 | + // date or restored from the build cache, while the CLI deletes the |
| 148 | + // targetroot before every build. The finalizer declares no outputs, so |
| 149 | + // Gradle runs it every time. |
| 150 | + List<Task> kotlinCompileTasks = new ArrayList<>(); |
| 151 | + TaskProvider<Task> kotlinConfigs = |
| 152 | + project |
| 153 | + .getTasks() |
| 154 | + .register( |
| 155 | + "scipKotlinConfigs", |
| 156 | + export -> |
| 157 | + export.doLast( |
| 158 | + ignored -> { |
| 159 | + for (Task task : kotlinCompileTasks) { |
| 160 | + // Only tasks of this build that succeeded; up-to-date |
| 161 | + // and cached tasks count as executed. |
| 162 | + TaskState state = task.getState(); |
| 163 | + if (state.getExecuted() && state.getFailure() == null) { |
| 164 | + writeKotlinConfig(task, sourceRoot, targetRoot); |
| 165 | + } |
| 166 | + } |
| 167 | + })); |
140 | 168 | // Not limited to the plugin checks above: AGP 9's built-in Kotlin adds |
141 | 169 | // Kotlin compile tasks to Android modules without applying a Kotlin plugin. |
142 | 170 | project |
143 | 171 | .getTasks() |
144 | | - .configureEach(task -> configureKotlinCompileTask(task, sourceRoot, targetRoot)); |
| 172 | + .configureEach( |
| 173 | + task -> { |
| 174 | + if (task.getClass().getSimpleName().contains("KotlinCompile")) { |
| 175 | + kotlinCompileTasks.add(task); |
| 176 | + task.finalizedBy(kotlinConfigs); |
| 177 | + } |
| 178 | + }); |
145 | 179 |
|
146 | 180 | project.getTasks().create("scipCompileAll").dependsOn(triggers); |
147 | 181 |
|
@@ -237,71 +271,62 @@ private static Set<String> addJavacPlugin(Project project, Object javacPluginDep |
237 | 271 | } |
238 | 272 |
|
239 | 273 | /** |
240 | | - * Kotlin sources are not indexed inside kotlinc: after each Kotlin compile task runs, its sources |
241 | | - * and classpath are dumped to {@code <targetroot>/kotlin-configs/<task>.txt} for the scip-java |
242 | | - * CLI to index after the build, decoupling indexing from the build's Kotlin compiler version. |
| 274 | + * Dumps the sources and classpath of a Kotlin compile task to {@code kotlin-configs/<task>.txt}. |
243 | 275 | */ |
244 | | - private static void configureKotlinCompileTask(Task task, String sourceRoot, String targetRoot) { |
245 | | - if (!task.getClass().getSimpleName().contains("KotlinCompile")) { |
246 | | - return; |
247 | | - } |
| 276 | + private static void writeKotlinConfig(Task task, String sourceRoot, String targetRoot) { |
| 277 | + try { |
| 278 | + List<String> lines = new ArrayList<>(); |
| 279 | + lines.add("sourceroot " + sourceRoot); |
248 | 280 |
|
249 | | - task.doLast( |
250 | | - ignored -> { |
251 | | - try { |
252 | | - List<String> lines = new ArrayList<>(); |
253 | | - lines.add("sourceroot " + sourceRoot); |
254 | | - |
255 | | - // Referring to KotlinCompile directly triggers NoClassDefFoundError - |
256 | | - // the plugin classpath is murky and we deliberately don't bundle the |
257 | | - // Kotlin Gradle plugin. So we commit the sins of reflection. |
258 | | - FileCollection libraries; |
259 | | - try { |
260 | | - libraries = (FileCollection) task.getClass().getMethod("getLibraries").invoke(task); |
261 | | - } catch (ReflectiveOperationException exc) { |
262 | | - libraries = (FileCollection) task.getClass().getMethod("getClasspath").invoke(task); |
263 | | - } |
264 | | - for (File entry : libraries.getFiles()) { |
265 | | - lines.add("classpath " + entry.getAbsolutePath()); |
266 | | - } |
267 | | - |
268 | | - int sources = 0; |
269 | | - for (File file : task.getInputs().getSourceFiles().getFiles()) { |
270 | | - if (file.getName().endsWith(".kt") || file.getName().endsWith(".kts")) { |
271 | | - lines.add("source " + file.getAbsolutePath()); |
272 | | - sources++; |
273 | | - } |
274 | | - } |
275 | | - // Java sources of the same module are listed too, so Kotlin |
276 | | - // references to them resolve. The indexer only emits Kotlin files. |
277 | | - try { |
278 | | - FileCollection javaSources = |
279 | | - (FileCollection) task.getClass().getMethod("getJavaSources").invoke(task); |
280 | | - for (File file : javaSources.getFiles()) { |
281 | | - if (file.getName().endsWith(".java")) { |
282 | | - lines.add("source " + file.getAbsolutePath()); |
283 | | - } |
284 | | - } |
285 | | - } catch (ReflectiveOperationException exc) { |
286 | | - // Kotlin Gradle Plugin without javaSources (pre 1.7). |
287 | | - } |
288 | | - if (sources == 0) { |
289 | | - return; |
290 | | - } |
291 | | - |
292 | | - Path configDir = Paths.get(targetRoot, "kotlin-configs"); |
293 | | - Files.createDirectories(configDir); |
294 | | - String fileName = task.getPath().replaceAll("[^A-Za-z0-9]", "-") + ".txt"; |
295 | | - Files.write(configDir.resolve(fileName), lines); |
296 | | - } catch (Exception exc) { |
297 | | - throw new RuntimeException( |
298 | | - "scip-java: failed to extract Kotlin compile configuration for task '" |
299 | | - + task.getName() |
300 | | - + "': " |
301 | | - + exc, |
302 | | - exc); |
| 281 | + // Referring to KotlinCompile directly triggers NoClassDefFoundError - |
| 282 | + // the plugin classpath is murky and we deliberately don't bundle the |
| 283 | + // Kotlin Gradle plugin. So we commit the sins of reflection. |
| 284 | + FileCollection libraries; |
| 285 | + try { |
| 286 | + libraries = (FileCollection) task.getClass().getMethod("getLibraries").invoke(task); |
| 287 | + } catch (ReflectiveOperationException exc) { |
| 288 | + libraries = (FileCollection) task.getClass().getMethod("getClasspath").invoke(task); |
| 289 | + } |
| 290 | + for (File entry : libraries.getFiles()) { |
| 291 | + lines.add("classpath " + entry.getAbsolutePath()); |
| 292 | + } |
| 293 | + |
| 294 | + int sources = 0; |
| 295 | + for (File file : task.getInputs().getSourceFiles().getFiles()) { |
| 296 | + if (file.getName().endsWith(".kt") || file.getName().endsWith(".kts")) { |
| 297 | + lines.add("source " + file.getAbsolutePath()); |
| 298 | + sources++; |
| 299 | + } |
| 300 | + } |
| 301 | + // Java sources of the same module are listed too, so Kotlin |
| 302 | + // references to them resolve. The indexer only emits Kotlin files. |
| 303 | + try { |
| 304 | + FileCollection javaSources = |
| 305 | + (FileCollection) task.getClass().getMethod("getJavaSources").invoke(task); |
| 306 | + for (File file : javaSources.getFiles()) { |
| 307 | + if (file.getName().endsWith(".java")) { |
| 308 | + lines.add("source " + file.getAbsolutePath()); |
303 | 309 | } |
304 | | - }); |
| 310 | + } |
| 311 | + } catch (ReflectiveOperationException exc) { |
| 312 | + // Kotlin Gradle Plugin without javaSources (pre 1.7). |
| 313 | + } |
| 314 | + if (sources == 0) { |
| 315 | + return; |
| 316 | + } |
| 317 | + |
| 318 | + Path configDir = Paths.get(targetRoot, "kotlin-configs"); |
| 319 | + Files.createDirectories(configDir); |
| 320 | + String fileName = task.getPath().replaceAll("[^A-Za-z0-9]", "-") + ".txt"; |
| 321 | + Files.write(configDir.resolve(fileName), lines); |
| 322 | + } catch (Exception exc) { |
| 323 | + throw new RuntimeException( |
| 324 | + "scip-java: failed to extract Kotlin compile configuration for task '" |
| 325 | + + task.getName() |
| 326 | + + "': " |
| 327 | + + exc, |
| 328 | + exc); |
| 329 | + } |
305 | 330 | } |
306 | 331 |
|
307 | 332 | private static Object requiredExtra(Map<String, Object> extraProperties, String name) { |
|
0 commit comments