diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingExtension.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingExtension.kt index 6a1b9f333c..2084fe618d 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingExtension.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingExtension.kt @@ -274,7 +274,11 @@ abstract class AbstractKotlinSymbolProcessingExtension( logger.reportAll() } return if (finished && !options.withCompilation) { - AnalysisResult.success(BindingContext.EMPTY, module, shouldGenerateCode = false) + if (!options.returnOkOnError && logger.hasError()) { + AnalysisResult.compilationError(BindingContext.EMPTY) + } else { + AnalysisResult.success(BindingContext.EMPTY, module, shouldGenerateCode = false) + } } else { AnalysisResult.RetryWithAdditionalRoots( BindingContext.EMPTY, @@ -332,13 +336,21 @@ abstract class AbstractKotlinSymbolProcessingExtension( e is KSPCompilationError -> { logger.error("${project.findLocationString(e.file, e.offset)}: ${e.message}") logger.reportAll() - return AnalysisResult.success(BindingContext.EMPTY, module, shouldGenerateCode = false) + return if (options.returnOkOnError) { + AnalysisResult.success(BindingContext.EMPTY, module, shouldGenerateCode = false) + } else { + AnalysisResult.compilationError(BindingContext.EMPTY) + } } e.isNotRecoverable() -> { e.logToError() logger.reportAll() - return AnalysisResult.success(BindingContext.EMPTY, module, shouldGenerateCode = false) + return if (options.returnOkOnError) { + AnalysisResult.success(BindingContext.EMPTY, module, shouldGenerateCode = false) + } else { + AnalysisResult.internalError(BindingContext.EMPTY, e) + } } // Let this round finish. diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingPlugin.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingPlugin.kt index 8d153ae854..af6b8dda91 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingPlugin.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/KotlinSymbolProcessingPlugin.kt @@ -79,6 +79,7 @@ class KotlinSymbolProcessingCommandLineProcessor : CommandLineProcessor { KspCliOption.ALL_WARNINGS_AS_ERRORS_OPTION -> allWarningsAsErrors = value.toBoolean() KspCliOption.WITH_COMPILATION_OPTION -> withCompilation = value.toBoolean() KspCliOption.CHANGED_CLASSES_OPTION -> changedClasses.addAll(value.split(':')) + KspCliOption.RETURN_OK_ON_ERROR_OPTION -> returnOkOnError = value.toBoolean() } } @@ -244,7 +245,15 @@ enum class KspCliOption( "canonical / dot-separated names of dirty classes in classpath", false, false - ); + ), + + RETURN_OK_ON_ERROR_OPTION( + "returnOkOnError", + "", + "Return OK even if there are errors", + false, + false + ), } class KspOptions( @@ -271,6 +280,7 @@ class KspOptions( val incrementalLog: Boolean, val allWarningsAsErrors: Boolean, val withCompilation: Boolean, + val returnOkOnError: Boolean, val changedClasses: List, val languageVersion: KotlinVersion, @@ -301,6 +311,8 @@ class KspOptions( var incrementalLog: Boolean = false var allWarningsAsErrors: Boolean = false var withCompilation: Boolean = false + // Default is false. It can be turned on to workaround KT-30172. + var returnOkOnError: Boolean = false var changedClasses: MutableList = mutableListOf() var languageVersion: KotlinVersion = LanguageVersion.LATEST_STABLE.toKotlinVersion() @@ -316,7 +328,7 @@ class KspOptions( resourceOutputDir!!, processingClasspath, processors, processingOptions, knownModified, knownRemoved, cachesDir!!, kspOutputDir!!, incremental, incrementalLog, - allWarningsAsErrors, withCompilation, changedClasses, + allWarningsAsErrors, withCompilation, returnOkOnError, changedClasses, languageVersion, apiVersion, compilerVersion ) } diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt index 1313b4c185..974e78dc7d 100644 --- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt +++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt @@ -130,6 +130,11 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool options += SubpluginOption("projectBaseDir", project.project.projectDir.canonicalPath) options += SubpluginOption("allWarningsAsErrors", allWarningsAsErrors.toString()) options += FilesSubpluginOption("apclasspath", classpath.toList()) + // Turn this on by default to work KT-30172 around. It is off by default in the ccompiler plugin. + options += SubpluginOption( + "returnOkOnError", + project.findProperty("ksp.return.ok.on.error")?.toString() ?: "true" + ) kspExtension.apOptions.forEach { options += SubpluginOption("apoption", "${it.key}=${it.value}") diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt index dce26d9a6e..dde6d59131 100644 --- a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt @@ -90,4 +90,24 @@ class OnErrorIT { } project.restore("workload/build.gradle.kts") } + + @Test + fun testCreateTwiceNotOkOnError() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true) + + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"createTwice\") }\n") + File(project.root, "gradle.properties").appendText("\nksp.return.ok.on.error=false") + gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result -> + val errors = result.output.split("\n").filter { it.startsWith("e: [ksp]") } + + Assert.assertTrue( + errors.any { + it.startsWith("e: [ksp] kotlin.io.FileAlreadyExistsException:") + } + ) + + Assert.assertTrue(result.output.contains("e: java.lang.IllegalStateException: Should not be called!")) + } + project.restore("workload/build.gradle.kts") + } }