[Gradle] Pass through errors from Gradle to Xcode ^KT-55650 Verification Pending
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkIT.kt index 8b25d86..7f1855b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkIT.kt
@@ -220,4 +220,143 @@ } } } + + + @Test + fun `configuration errors reported to Xcode when embedAndSign task requested`() { + with(Project("sharedAppleFramework")) { + setupWorkingDir() + projectDir.resolve("shared/build.gradle.kts").appendText( + """ + kotlin { + sourceSets["commonMain"].dependencies { + implementation("com.example.unknown:dependency:0.0.1") + } + } + """.trimIndent() + ) + + build( + ":shared:embedAndSignAppleFrameworkForXcode", + options = defaultBuildOptions().copy( + customEnvironmentVariables = mapOf( + "CONFIGURATION" to "debug", + "SDK_NAME" to "iphoneos123", + "ARCHS" to "arm64", + "TARGET_BUILD_DIR" to "no use", + "FRAMEWORKS_FOLDER_PATH" to "no use" + ) + ) + ) { + assertFailed() + assertContains("error: Could not find com.example.unknown:dependency:0.0.1.") + } + } + } + + @Test + fun `compilation errors reported to Xcode when embedAndSign task requested`() { + with(Project("sharedAppleFramework")) { + setupWorkingDir() + projectDir.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt") + .appendText("this can't be compiled") + + build( + ":shared:embedAndSignAppleFrameworkForXcode", + options = defaultBuildOptions().copy( + customEnvironmentVariables = mapOf( + "CONFIGURATION" to "debug", + "SDK_NAME" to "iphoneos123", + "ARCHS" to "arm64", + "TARGET_BUILD_DIR" to "no use", + "FRAMEWORKS_FOLDER_PATH" to "no use" + ) + ) + ) { + assertFailed() + assertContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration") + assertContains("error: Compilation finished with errors") + } + } + } + + + @Test + fun `compilation errors printed with Gradle-style when any other task requested`() { + with(Project("sharedAppleFramework")) { + setupWorkingDir() + projectDir.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt") + .appendText("this can't be compiled") + + build( + ":shared:assembleDebugAppleFrameworkForXcodeIosArm64", + options = defaultBuildOptions().copy( + customEnvironmentVariables = mapOf( + "CONFIGURATION" to "debug", + "SDK_NAME" to "iphoneos123", + "ARCHS" to "arm64", + "TARGET_BUILD_DIR" to "no use", + "FRAMEWORKS_FOLDER_PATH" to "no use" + ) + ) + ) { + assertFailed() + assertContains("e: file:///") + assertNotContains("error: Compilation finished with errors") + } + } + } + + + @Test + fun `compilation errors printed with Xcode-style with explicit option`() { + with(Project("sharedAppleFramework")) { + setupWorkingDir() + projectDir.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt") + .appendText("this can't be compiled") + + build( + ":shared:assembleDebugAppleFrameworkForXcodeIosArm64", "-Pkotlin.native.useXcodeMessageStyle=true", + options = defaultBuildOptions().copy( + customEnvironmentVariables = mapOf( + "CONFIGURATION" to "debug", + "SDK_NAME" to "iphoneos123", + "ARCHS" to "arm64", + "TARGET_BUILD_DIR" to "no use", + "FRAMEWORKS_FOLDER_PATH" to "no use" + ) + ) + ) { + assertFailed() + assertContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration") + assertContains("error: Compilation finished with errors") + } + } + } + + @Test + fun `compilation errors reported to Xcode when embedAndSign task requested and compiler runs in a separate process`() { + with(Project("sharedAppleFramework")) { + setupWorkingDir() + projectDir.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt") + .appendText("this can't be compiled") + + build( + ":shared:embedAndSignAppleFrameworkForXcode", "-Pkotlin.native.disableCompilerDaemon=true", + options = defaultBuildOptions().copy( + customEnvironmentVariables = mapOf( + "CONFIGURATION" to "debug", + "SDK_NAME" to "iphoneos123", + "ARCHS" to "arm64", + "TARGET_BUILD_DIR" to "no use", + "FRAMEWORKS_FOLDER_PATH" to "no use" + ) + ) + ) { + assertFailed() + assertContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration") + } + } + } + } \ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt index 5dd4163..66c0f6d 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/CocoaPodsIT.kt
@@ -875,6 +875,77 @@ } } + + @Test + fun testSyncFrameworkUseXcodeStyleErrorsWhenConfigurationFailed() { + with(project) { + gradleBuildScript().appendText( + """ + kotlin { + sourceSets["commonMain"].dependencies { + implementation("com.example.unknown:dependency:0.0.1") + } + } + """.trimIndent() + ) + + build( + "syncFramework", + "-Pkotlin.native.cocoapods.platform=iphonesimulator", + "-Pkotlin.native.cocoapods.archs=x86_64", + "-Pkotlin.native.cocoapods.configuration=Debug" + ) { + assertFailed() + assertContains("error: Could not find com.example.unknown:dependency:0.0.1.") + } + } + } + + @Test + fun testSyncFrameworkUseXcodeStyleErrorsWhenCompilationFailed() { + with(project) { + projectDir.resolve("src/commonMain/kotlin/A.kt").appendText("this can't be compiled") + + build( + "syncFramework", + "-Pkotlin.native.cocoapods.platform=iphonesimulator", + "-Pkotlin.native.cocoapods.archs=x86_64", + "-Pkotlin.native.cocoapods.configuration=Debug", + ) { + assertFailed() + assertContains("/native-cocoapods-template/src/commonMain/kotlin/A.kt:5:2: error: Expecting a top level declaration") + assertContains("error: Compilation finished with errors") + } + } + } + + @Test + fun testOtherTasksUseGradleStyleErrorsWhenCompilationFailed() { + with(project) { + projectDir.resolve("src/commonMain/kotlin/A.kt").appendText("this can't be compiled") + + build("linkPodDebugFrameworkIOS") { + assertFailed() + assertContains("e: file:///") + assertContains("/native-cocoapods-template/src/commonMain/kotlin/A.kt:5:2 Expecting a top level declaration") + assertNotContains("error: Compilation finished with errors") + } + } + } + + @Test + fun testOtherTasksUseXcodeStyleErrorsWhenCompilationFailedAndOptionEnabled() { + with(project) { + projectDir.resolve("src/commonMain/kotlin/A.kt").appendText("this can't be compiled") + + build("linkPodDebugFrameworkIOS", "-Pkotlin.native.useXcodeMessageStyle=true") { + assertFailed() + assertContains("/native-cocoapods-template/src/commonMain/kotlin/A.kt:5:2: error: Expecting a top level declaration") + assertContains("error: Compilation finished with errors") + } + } + } + @Test fun testPodDependencyInUnitTests() = getProjectByName(cocoapodsTestsProjectName).testWithWrapper(":iosX64Test")
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt index c0bd340..473edc7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt
@@ -186,7 +186,8 @@ try { val mainClass = isolatedClassLoader.loadClass(mainClass) - val entryPoint = mainClass.methods.single { it.name == daemonEntryPoint } + val entryPoint = mainClass.methods + .singleOrNull { it.name == daemonEntryPoint } ?: error("Couldn't find daemon entry point '$daemonEntryPoint'") entryPoint.invoke(null, transformedArgs.toTypedArray()) } catch (t: InvocationTargetException) {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt index d292fb9..540054c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt
@@ -7,11 +7,11 @@ import org.gradle.api.Project import org.gradle.api.file.FileCollection -import org.gradle.api.tasks.* import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind import org.jetbrains.kotlin.gradle.dsl.NativeCacheOrchestration import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.useXcodeMessageStyle import org.jetbrains.kotlin.gradle.plugin.mpp.isAtLeast import org.jetbrains.kotlin.gradle.plugin.mpp.nativeUseEmbeddableCompilerJar import org.jetbrains.kotlin.gradle.targets.native.KonanPropertiesBuildService @@ -59,6 +59,7 @@ else "$konanHome/konan/lib/kotlin-native.jar" + internal abstract class KotlinNativeToolRunner( protected val toolName: String, private val settings: Settings, @@ -69,6 +70,7 @@ val konanVersion: CompilerVersion, val konanHome: String, val konanPropertiesFile: File, + val useXcodeMessageStyle: Boolean, val jvmArgs: List<String>, val classpath: FileCollection ) { @@ -77,6 +79,7 @@ konanVersion = project.konanVersion, konanHome = project.konanHome, konanPropertiesFile = project.file("${project.konanHome}/konan/konan.properties"), + useXcodeMessageStyle = project.useXcodeMessageStyle, jvmArgs = project.jvmArgs, classpath = project.files(project.kotlinNativeCompilerJar, "${project.konanHome}/konan/lib/trove4j.jar") ) @@ -86,7 +89,8 @@ final override val displayName get() = toolName final override val mainClass get() = "org.jetbrains.kotlin.cli.utilities.MainKt" - final override val daemonEntryPoint get() = "daemonMain" + final override val daemonEntryPoint + get() = if (!settings.useXcodeMessageStyle) "daemonMain" else "daemonMainWithXcodeRenderer" // We need to unset some environment variables which are set by XCode and may potentially affect the tool executed. final override val execEnvironmentBlacklist: Set<String> by lazy { @@ -102,9 +106,11 @@ val konanHomeRequired = !settings.konanVersion.isAtLeast(1, 4, 0) || settings.konanVersion.toString(showMeta = false, showBuild = false) in listOf("1.4-M1", "1.4-M2") + val messageRenderer = if (settings.useXcodeMessageStyle) MessageRenderer.XCODE_STYLE else MessageRenderer.GRADLE_STYLE + listOfNotNull( if (konanHomeRequired) "konan.home" to settings.konanHome else null, - MessageRenderer.PROPERTY_KEY to MessageRenderer.GRADLE_STYLE.name + MessageRenderer.PROPERTY_KEY to messageRenderer.name ).toMap() }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index 38235a2..c6c88d5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_IMPORT_ENABLE_SLOW_SOURCES_JAR_RESOLVER import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_DEPENDENCY_PROPAGATION +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_USE_XCODE_MESSAGE_STYLE import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_STDLIB_DEFAULT_DEPENDENCY import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_STDLIB_JDK_VARIANTS_VERSION_ALIGNMENT import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService @@ -362,6 +363,12 @@ } /** + * Forces K/N compiler to print messages which could be parsed by Xcode + */ + val nativeUseXcodeMessageStyle: Boolean? + get() = booleanProperty(KOTLIN_NATIVE_USE_XCODE_MESSAGE_STYLE) + + /** * Allows a user to specify additional arguments of a JVM executing KLIB commonizer. */ val commonizerJvmArgs: List<String> @@ -548,6 +555,7 @@ const val KOTLIN_BUILD_REPORT_SINGLE_FILE = "kotlin.build.report.single_file" const val KOTLIN_BUILD_REPORT_HTTP_URL = "kotlin.build.report.http.url" const val KOTLIN_OPTIONS_SUPPRESS_FREEARGS_MODIFICATION_WARNING = "kotlin.options.suppressFreeCompilerArgsModificationWarning" + const val KOTLIN_NATIVE_USE_XCODE_MESSAGE_STYLE = "kotlin.native.useXcodeMessageStyle" } companion object {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index c63bab9..01f137c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.plugin.ide.kotlinIdeMultiplatformImport import org.jetbrains.kotlin.gradle.plugin.ide.locateOrRegisterIdeResolveDependenciesTask import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.Companion.sourceSetFreeCompilerArgsPropertyName +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.addBuildListenerForXcode import org.jetbrains.kotlin.gradle.plugin.mpp.internal.checkAndReportDeprecatedNativeTargets import org.jetbrains.kotlin.gradle.plugin.mpp.internal.handleHierarchicalStructureFlagsMigration import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* @@ -81,6 +82,8 @@ // Ensure that the instance is created and configured during apply project.kotlinIdeMultiplatformImport project.locateOrRegisterIdeResolveDependenciesTask() + + project.addBuildListenerForXcode() } private fun exportProjectStructureMetadataForOtherBuilds(
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt index 583619a..259e1b8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/AppleXcodeTasks.kt
@@ -31,6 +31,11 @@ import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly import java.io.File +internal object AppleXcodeTasks { + const val embedAndSignTaskPrefix = "embedAndSign" + const val embedAndSignTaskPostfix = "AppleFrameworkForXcode" +} + private object XcodeEnvironment { val buildType: NativeBuildType? get() { @@ -144,7 +149,7 @@ val envFrameworkSearchDir = XcodeEnvironment.frameworkSearchDir val envSign = XcodeEnvironment.sign - val frameworkTaskName = lowerCamelCaseName("embedAndSign", framework.namePrefix, "AppleFrameworkForXcode") + val frameworkTaskName = lowerCamelCaseName(AppleXcodeTasks.embedAndSignTaskPrefix, framework.namePrefix, AppleXcodeTasks.embedAndSignTaskPostfix) if (envBuildType == null || envTargets.isEmpty() || envEmbeddedFrameworksDir == null || envFrameworkSearchDir == null) { locateOrRegisterTask<DefaultTask>(frameworkTaskName) { task ->