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 ab6185fa784..0b1f246b2eb 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 @@ -7,7 +7,9 @@ package org.jetbrains.kotlin.gradle.native import org.gradle.api.JavaVersion import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.testbase.TestVersions.Gradle import org.jetbrains.kotlin.gradle.util.replaceText import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.condition.OS @@ -19,7 +21,7 @@ import kotlin.io.path.* @OsCondition(supportedOn = [OS.MAC], enabledOnCI = [OS.MAC]) @DisplayName("Tests for K/N with Apple Framework") -@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0) +@GradleTestVersions(minVersion = Gradle.G_7_0) @NativeGradlePluginTests class AppleFrameworkIT : KGPBaseTest() { @@ -576,6 +578,23 @@ class AppleFrameworkIT : KGPBaseTest() { } } } + + // Should always be green because the CI Xcode version must be supported + @DisplayName("Xcode version too high diagnostic isn't emitted") + @GradleTest + @GradleTestVersions(minVersion = Gradle.G_7_4) + fun testXcodeVersionTooHighDiagnosticNotEmitted(gradleVersion: GradleVersion) { + nativeProject( + "sharedAppleFramework", + gradleVersion, + // enable CC to make sure that external process isn't run during configuration + buildOptions = defaultBuildOptions.copy(configurationCache = true), + ) { + build(":shared:linkReleaseFrameworkIosSimulatorArm64") { + assertNoDiagnostic(KotlinToolingDiagnostics.XcodeVersionTooHighWarning) + } + } + } } private val GradleProject.darwinBuildProductsDir: Path diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkNonMacIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkNonMacIT.kt index a6624c3ca01..fad62dda302 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkNonMacIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/AppleFrameworkNonMacIT.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.native import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.targets.native.cocoapods.CocoapodsPluginDiagnostics import org.junit.jupiter.api.DisplayName @@ -47,6 +48,8 @@ class AppleFrameworkNonMacIT : KGPBaseTest() { assertTasksSkipped(":shared:linkDebugFrameworkWatchosX64") assertTasksSkipped(":shared:assembleDebugWatchosFatFrameworkForSharedXCFramework") assertTasksSkipped(":shared:assembleSharedDebugXCFramework") + + assertNoDiagnostic(KotlinToolingDiagnostics.XcodeVersionTooHighWarning) } } } 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 65fd4a950d1..21e3e2e815f 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 @@ -520,6 +520,9 @@ internal class PropertiesProvider private constructor(private val project: Proje val appleCopyFrameworkToBuiltProductsDir: Boolean get() = booleanProperty(PropertyNames.KOTLIN_APPLE_COPY_FRAMEWORK_TO_BUILT_PRODUCTS_DIR) ?: true + val appleIgnoreXcodeVersionCompatibility: Boolean + get() = booleanProperty(PropertyNames.KOTLIN_APPLE_XCODE_COMPATIBILITY_NOWARN) ?: false + /** * Enables kotlin native toolchain in native projects. */ @@ -673,6 +676,7 @@ internal class PropertiesProvider private constructor(private val project: Proje val KOTLIN_PROJECT_PERSISTENT_DIR_GRADLE_DISABLE_WRITE = property("kotlin.project.persistent.dir.gradle.disableWrite") val KOTLIN_NATIVE_TOOLCHAIN_ENABLED = property("kotlin.native.toolchain.enabled") val KOTLIN_APPLE_COPY_FRAMEWORK_TO_BUILT_PRODUCTS_DIR = property("kotlin.apple.copyFrameworkToBuiltProductsDir") + val KOTLIN_APPLE_XCODE_COMPATIBILITY_NOWARN = property("kotlin.apple.xcodeCompatibility.nowarn") val KOTLIN_APPLE_COCOAPODS_EXECUTABLE = property("kotlin.apple.cocoapods.bin") val KOTLIN_SWIFT_EXPORT_ENABLED = property("kotlin.swift-export.enabled") diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt index 52dc9323b16..9c391e804e8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt @@ -209,7 +209,7 @@ object KotlinToolingDiagnostics { Kotlin Multiplatform <-> Android Gradle Plugin compatibility issue: The applied Android Gradle Plugin version ($androidGradlePluginVersionString) is higher than the maximum known to the Kotlin Gradle Plugin. - Tooling stability in such configuration isn't tested, please report encountered issues to https://kotl.in/issue" + Tooling stability in such configuration isn't tested, please report encountered issues to https://kotl.in/issue Minimum supported Android Gradle Plugin version: $minSupported Maximum tested Android Gradle Plugin version: $maxTested @@ -784,6 +784,20 @@ object KotlinToolingDiagnostics { """.trimIndent() ) } + + object XcodeVersionTooHighWarning : ToolingDiagnosticFactory(WARNING) { + operator fun invoke(xcodeVersionString: String, maxTested: String) = build( + """ + Kotlin <-> Xcode compatibility issue: + The selected Xcode version ($xcodeVersionString) is higher than the maximum known to the Kotlin Gradle Plugin. + Stability in such configuration hasn't been tested, please report encountered issues to https://kotl.in/issue + + Maximum tested Xcode version: $maxTested + + To suppress this message add '${PropertiesProvider.PropertyNames.KOTLIN_APPLE_XCODE_COMPATIBILITY_NOWARN}=true' to your gradle.properties + """.trimIndent() + ) + } } private fun String.indentLines(nSpaces: Int = 4, skipFirstLine: Boolean = true): String { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt index 181027abc7c..1f11b98f396 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnosticsCollector.kt @@ -37,14 +37,11 @@ internal abstract class KotlinToolingDiagnosticsCollector : BuildService, logger: Logger, diagnostic: ToolingDiagnostic) { - val options = transform.parameters.diagnosticRenderingOptions.get() + fun report(from: UsesKotlinToolingDiagnosticsParameters, logger: Logger, diagnostic: ToolingDiagnostic) { + val options = from.diagnosticRenderingOptions.get() if (!diagnostic.isSuppressed(options)) { renderReportedDiagnostic(diagnostic, logger, options) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/UsesKotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/UsesKotlinToolingDiagnostics.kt index 0663dc5cea3..546d0839651 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/UsesKotlinToolingDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/UsesKotlinToolingDiagnostics.kt @@ -9,19 +9,22 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.transform.TransformAction import org.gradle.api.artifacts.transform.TransformParameters -import org.gradle.api.artifacts.transform.TransformSpec import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.api.provider.Property +import org.gradle.api.services.BuildService +import org.gradle.api.services.BuildServiceParameters import org.gradle.api.tasks.Internal -internal interface UsesKotlinToolingDiagnostics : Task { +internal interface UsesKotlinToolingDiagnosticsParameters { @get:Internal val toolingDiagnosticsCollector: Property @get:Internal val diagnosticRenderingOptions: Property +} +internal interface UsesKotlinToolingDiagnostics : UsesKotlinToolingDiagnosticsParameters, Task { fun reportDiagnostic(diagnostic: ToolingDiagnostic) { toolingDiagnosticsCollector.get().report(this, diagnostic) } @@ -29,20 +32,14 @@ internal interface UsesKotlinToolingDiagnostics : Task { /** * Use this interface to make your [TransformAction] able to report [ToolingDiagnostic]. - * You must use [setupTransformActionToolingDiagnostics] to set up [TransformActionUsingKotlinToolingDiagnostics.Parameters]. + * You must use [setupKotlinToolingDiagnosticsParameters] to set up the parameters. */ internal interface TransformActionUsingKotlinToolingDiagnostics

: TransformAction

{ - interface Parameters : TransformParameters { - @get:Internal - val toolingDiagnosticsCollector: Property - - @get:Internal - val diagnosticRenderingOptions: Property - } + interface Parameters : TransformParameters, UsesKotlinToolingDiagnosticsParameters fun reportDiagnostic(diagnostic: ToolingDiagnostic) { - parameters.toolingDiagnosticsCollector.get().report(this, logger, diagnostic) + parameters.toolingDiagnosticsCollector.get().report(parameters, logger, diagnostic) } companion object { @@ -50,7 +47,24 @@ internal interface TransformActionUsingKotlinToolingDiagnostics

.setupTransformActionToolingDiagnostics(project: Project) { - parameters.toolingDiagnosticsCollector.set(project.kotlinToolingDiagnosticsCollectorProvider) - parameters.diagnosticRenderingOptions.set(ToolingDiagnosticRenderingOptions.forProject(project)) +/** + * Use this interface to make your [BuildService] able to report [ToolingDiagnostic]. + * You must use [setupKotlinToolingDiagnosticsParameters] to set up the parameters. + */ +internal interface BuildServiceUsingKotlinToolingDiagnostics

: + BuildService

{ + interface Parameters : BuildServiceParameters, UsesKotlinToolingDiagnosticsParameters + + fun reportDiagnostic(diagnostic: ToolingDiagnostic) { + parameters.toolingDiagnosticsCollector.get().report(parameters, logger, diagnostic) + } + + companion object { + internal val logger: Logger = Logging.getLogger(Project::class.java) + } +} + +internal fun UsesKotlinToolingDiagnosticsParameters.setupKotlinToolingDiagnosticsParameters(project: Project) { + toolingDiagnosticsCollector.set(project.kotlinToolingDiagnosticsCollectorProvider) + diagnosticRenderingOptions.set(ToolingDiagnosticRenderingOptions.forProject(project)) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeVersionService.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeVersionService.kt new file mode 100644 index 00000000000..9f2cebe7d9b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeVersionService.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.plugin.mpp.apple + +import org.gradle.api.Project +import org.gradle.api.logging.Logging +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.diagnostics.BuildServiceUsingKotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics.XcodeVersionTooHighWarning +import org.jetbrains.kotlin.gradle.plugin.diagnostics.setupKotlinToolingDiagnosticsParameters +import org.jetbrains.kotlin.gradle.utils.registerClassLoaderScopedBuildService +import org.jetbrains.kotlin.konan.target.Xcode +import org.jetbrains.kotlin.konan.target.XcodeVersion + +internal abstract class XcodeVersionService : BuildServiceUsingKotlinToolingDiagnostics { + + interface Parameters : BuildServiceUsingKotlinToolingDiagnostics.Parameters { + val ignoreVersionCompatibilityCheck: Property + } + + companion object { + fun registerIfAbsent(project: Project): Provider { + return project.gradle.registerClassLoaderScopedBuildService(XcodeVersionService::class) { spec -> + spec.parameters.setupKotlinToolingDiagnosticsParameters(project) + spec.parameters.ignoreVersionCompatibilityCheck.convention(project.kotlinPropertiesProvider.appleIgnoreXcodeVersionCompatibility) + } + } + } + + private val logger = Logging.getLogger(this::class.java) + + val version: XcodeVersion by lazy { + Xcode.findCurrent().version.also(::checkVersionCompatibility) + } + + private fun checkVersionCompatibility(xcodeVersion: XcodeVersion) = with(parameters) { + if (!ignoreVersionCompatibilityCheck.get() && xcodeVersion > XcodeVersion.maxTested) { + toolingDiagnosticsCollector.get().report( + this, logger, + XcodeVersionTooHighWarning( + xcodeVersionString = xcodeVersion.toString(), + maxTested = XcodeVersion.maxTested.toString(), + ) + ) + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeVersionTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeVersionTask.kt index 804183992bf..a62c012b0d5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeVersionTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/apple/XcodeVersionTask.kt @@ -7,30 +7,67 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.apple import org.gradle.api.DefaultTask import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFile import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.work.DisableCachingByDefault +import org.jetbrains.kotlin.gradle.plugin.KotlinProjectSetupAction +import org.jetbrains.kotlin.gradle.plugin.launch import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask +import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.utils.getFile import org.jetbrains.kotlin.gradle.utils.onlyIfCompat import org.jetbrains.kotlin.konan.target.HostManager -import org.jetbrains.kotlin.konan.target.Xcode +import org.jetbrains.kotlin.konan.target.XcodeVersion + +internal val XcodeVersionSetupAction = KotlinProjectSetupAction { + launch { + val xcodeVersionTask = XcodeVersionTask.locateOrRegister(this@KotlinProjectSetupAction) + + tasks.withType().configureEach { task -> + task._xcodeVersion.set(xcodeVersionTask.flatMap { it.outputFile }) + } + } +} + +internal interface UsesXcodeVersion : Task { + @Suppress("PropertyName") + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:Optional + @get:InputFile + val _xcodeVersion: RegularFileProperty + + @get:Internal + val xcodeVersion: XcodeVersion? + get() = if (HostManager.hostIsMac) { + val version = _xcodeVersion.getFile().readText() + XcodeVersion.parse(version) ?: error("Couldn't parse Xcode version from '$version'") + } else { + null + } +} + @DisableCachingByDefault internal abstract class XcodeVersionTask : DefaultTask() { companion object { fun locateOrRegister(project: Project): Provider { - return project.rootProject.locateOrRegisterTask("xcodeVersion") { task -> + + return project.locateOrRegisterTask("xcodeVersion") { task -> if (!HostManager.hostIsMac) { task.onlyIfCompat("Task can be run only on MacOS") { false } return@locateOrRegisterTask } + val service = XcodeVersionService.registerIfAbsent(project) + + task.usesService(service) + task.service.convention(service) + // xcode-select stores a symlink to a developer dir of currently selected Xcode in "/var/db/xcode_select_link" task.xcodeSelectLink.from("/var/db/xcode_select_link/usr/bin/xcodebuild") @@ -42,6 +79,9 @@ internal abstract class XcodeVersionTask : DefaultTask() { } } + @get:Internal + abstract val service: Property + @get:PathSensitive(PathSensitivity.ABSOLUTE) @get:InputFiles // @InputFiles instead of @InputFile because it allows non-existing files abstract val xcodeSelectLink: ConfigurableFileCollection @@ -51,13 +91,11 @@ internal abstract class XcodeVersionTask : DefaultTask() { abstract val xcodeDeveloperDir: Property @get:OutputFile + @get:Optional abstract val outputFile: RegularFileProperty @TaskAction fun execute() { - outputFile.getFile().writeText(Xcode.findCurrent().version.toString()) + outputFile.getFile().writeText(service.get().version.toString()) } -} - -internal val Provider.version: Provider - get() = flatMap { it.outputFile } \ No newline at end of file +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt index 8261880bcd8..fc7e583f925 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImportSetupAction import org.jetbrains.kotlin.gradle.plugin.ide.IdeResolveDependenciesTaskSetupAction import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.mpp.apple.AddBuildListenerForXCodeSetupAction +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XcodeVersionSetupAction import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.* import org.jetbrains.kotlin.gradle.plugin.mpp.internal.DeprecatedMppGradlePropertiesMigrationSetupAction import org.jetbrains.kotlin.gradle.plugin.mpp.resources.RegisterMultiplatformResourcesPublicationExtensionAction @@ -72,6 +73,7 @@ internal fun Project.registerKotlinPluginExtensions() { register(project, IdeMultiplatformImportSetupAction) register(project, IdeResolveDependenciesTaskSetupAction) register(project, CInteropCommonizedCInteropApiElementsConfigurationsSetupAction) + register(project, XcodeVersionSetupAction) register(project, AddBuildListenerForXCodeSetupAction) register(project, CreateFatFrameworksSetupAction) register(project, KotlinRegisterCompilationArchiveTasksExtension) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotilnNativeConfigureBinariesSideEffect.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotilnNativeConfigureBinariesSideEffect.kt index 96c7b63ada7..79e1232fb2a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotilnNativeConfigureBinariesSideEffect.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotilnNativeConfigureBinariesSideEffect.kt @@ -16,8 +16,6 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.launchInStage import org.jetbrains.kotlin.gradle.plugin.mpp.* -import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XcodeVersionTask -import org.jetbrains.kotlin.gradle.plugin.mpp.apple.version import org.jetbrains.kotlin.gradle.targets.KotlinTargetSideEffect import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink import org.jetbrains.kotlin.gradle.tasks.dependsOn @@ -77,7 +75,7 @@ private fun Project.createLinkTask(binary: NativeBinary) { // this afterEvaluate comes from NativeCompilerOptions @Suppress("DEPRECATION") val compilationCompilerOptions = binary.compilation.compilerOptions val konanPropertiesBuildService = KonanPropertiesBuildService.registerIfAbsent(project) - val xcodeVersionTask = XcodeVersionTask.locateOrRegister(project) + val linkTask = registerTask( binary.linkTaskName, listOf(binary) ) { task -> @@ -93,7 +91,6 @@ private fun Project.createLinkTask(binary: NativeBinary) { task.toolOptions.freeCompilerArgs.value(compilationCompilerOptions.options.freeCompilerArgs) task.toolOptions.freeCompilerArgs.addAll(providers.provider { PropertiesProvider(project).nativeLinkArgs }) task.runViaBuildToolsApi.value(false).disallowChanges() // K/N is not yet supported - task.xcodeVersion.set(xcodeVersionTask.version) // Frameworks actively uses symlinks. // Gradle build cache transforms symlinks into regular files https://guides.gradle.org/using-build-cache/#symbolic_links diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt index 8bac645d185..888c2c02a10 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/KotlinCocoapodsPlugin.kt @@ -524,8 +524,6 @@ open class KotlinCocoapodsPlugin : Plugin { else -> error("Unknown cocoapods platform: $family") } - val xcodeVersionTask = XcodeVersionTask.locateOrRegister(project) - val podGenTask = project.registerTask(family.toPodGenTaskName) { task -> task.description = "Сreates a synthetic Xcode project to retrieve CocoaPods dependencies" task.podName.set(project.provider { cocoapodsExtension.name }) @@ -533,7 +531,6 @@ open class KotlinCocoapodsPlugin : Plugin { task.family.set(family) task.platformSettings.set(platformSettings) task.pods.set(cocoapodsExtension.pods) - task.xcodeVersion.set(xcodeVersionTask.version) } project.registerTask(family.toPodInstallSyntheticTaskName) { task -> diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt index 501a01f3ce8..35b53951ec7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodGenTask.kt @@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider @@ -17,7 +16,7 @@ import org.gradle.work.DisableCachingByDefault import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs import org.jetbrains.kotlin.gradle.plugin.cocoapods.platformLiteral -import org.jetbrains.kotlin.gradle.utils.parse +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.UsesXcodeVersion import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.konan.target.XcodeVersion import java.io.File @@ -27,7 +26,7 @@ import javax.inject.Inject * The task generates a synthetic project with all cocoapods dependencies */ @DisableCachingByDefault -abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : CocoapodsTask() { +abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : CocoapodsTask(), UsesXcodeVersion { init { onlyIf { @@ -50,11 +49,6 @@ abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : Co @get:Nested internal abstract val pods: ListProperty - @get:PathSensitive(PathSensitivity.RELATIVE) - @get:Optional - @get:InputFile - internal abstract val xcodeVersion: RegularFileProperty - @get:OutputFile val podfile: Provider = projectLayout.cocoapodsBuildDirs.synthetic(family).map { it.file("Podfile").asFile } @@ -122,7 +116,7 @@ abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : Co } private fun insertXcode143DeploymentTargetWorkarounds(family: Family): String { - if (XcodeVersion.parse(xcodeVersion) < XcodeVersion(14, 3)) { + if (xcodeVersion.let { version -> version != null && version < XcodeVersion(14, 3) }) { return "" } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeLink.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeLink.kt index 977f67a07f0..fd92cd9ea5d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeLink.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeLink.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.categoryByName import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.UsesXcodeVersion import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf import org.jetbrains.kotlin.gradle.report.UsesBuildMetricsService import org.jetbrains.kotlin.gradle.targets.native.UsesKonanPropertiesBuildService @@ -57,6 +58,7 @@ constructor( ) : AbstractKotlinCompileTool(objectFactory), UsesKonanPropertiesBuildService, UsesBuildMetricsService, + UsesXcodeVersion, KotlinToolTask, UsesKotlinNativeBundleBuildService { @@ -199,11 +201,6 @@ constructor( attributes.setAttribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY)) }.filterKlibsPassedToCompiler() - @get:Optional - @get:InputFile - @get:PathSensitive(PathSensitivity.RELATIVE) - internal val xcodeVersion = objectFactory.fileProperty() - private val externalDependenciesArgs by lazy { @Suppress("DEPRECATION") ExternalDependenciesBuilder(project, compilation).buildCompilerArgs() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeFramework.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeFramework.kt index 6bc8b4ab68b..8f291a1a3ad 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeFramework.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeFramework.kt @@ -15,8 +15,6 @@ import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind -import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XcodeVersionTask -import org.jetbrains.kotlin.gradle.plugin.mpp.apple.version import org.jetbrains.kotlin.gradle.plugin.mpp.enabledOnCurrentHost import org.jetbrains.kotlin.gradle.tasks.dependsOn import org.jetbrains.kotlin.gradle.tasks.registerTask @@ -110,7 +108,6 @@ internal fun KotlinNativeArtifact.registerLinkFrameworkTask( ): TaskProvider { val kind = NativeOutputKind.FRAMEWORK val destinationDir = project.layout.buildDirectory.dir("$outDirName/${target.visibleName}/${buildType.visibleName}") - val xcodeVersionTask = XcodeVersionTask.locateOrRegister(project) val resultTask = project.registerTask( lowerCamelCaseName("assemble", name, buildType.visibleName, kind.taskNameClassifier, target.presetName, taskNameSuffix), listOf(target, kind.compilerOutputKind) @@ -123,7 +120,6 @@ internal fun KotlinNativeArtifact.registerLinkFrameworkTask( task.debuggable.set(buildType.debuggable) task.linkerOptions.set(linkerOptions) task.binaryOptions.set(binaryOptions) - task.xcodeVersion.set(xcodeVersionTask.version) task.staticFramework.set(isStatic) if (embedBitcode != null) { task.embedBitcode.set(embedBitcode) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeLinkArtifactTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeLinkArtifactTask.kt index c2b4231da08..0f3955a6c5a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeLinkArtifactTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/artifact/KotlinNativeLinkArtifactTask.kt @@ -11,7 +11,6 @@ import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileCollection import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty @@ -30,6 +29,7 @@ import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.UsesXcodeVersion import org.jetbrains.kotlin.gradle.report.GradleBuildMetricsReporter import org.jetbrains.kotlin.gradle.report.UsesBuildMetricsService import org.jetbrains.kotlin.gradle.targets.native.tasks.buildKotlinNativeBinaryLinkerArgs @@ -56,8 +56,9 @@ abstract class KotlinNativeLinkArtifactTask @Inject constructor( private val projectLayout: ProjectLayout, ) : DefaultTask(), UsesBuildMetricsService, - KotlinToolTask, - UsesKotlinNativeBundleBuildService { + UsesKotlinNativeBundleBuildService, + UsesXcodeVersion, + KotlinToolTask { @get:Input abstract val baseName: Property @@ -100,11 +101,6 @@ abstract class KotlinNativeLinkArtifactTask @Inject constructor( @get:Input abstract val binaryOptions: MapProperty - @get:PathSensitive(PathSensitivity.RELATIVE) - @get:Optional - @get:InputFile - abstract val xcodeVersion: RegularFileProperty - private val nativeBinaryOptions = PropertiesProvider(project).nativeBinaryOptions @get:Input diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KotlinCompileConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KotlinCompileConfig.kt index a65429bbd53..a5d6535934b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KotlinCompileConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/KotlinCompileConfig.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.gradle.internal.transforms.BuildToolsApiClasspathEnt import org.jetbrains.kotlin.gradle.plugin.BUILD_TOOLS_API_CLASSPATH_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationInfo import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider -import org.jetbrains.kotlin.gradle.plugin.diagnostics.setupTransformActionToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.setupKotlinToolingDiagnosticsParameters import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation @@ -183,13 +183,13 @@ internal open class BaseKotlinCompileConfig : AbstractKotl it.from.setAttribute(ARTIFACT_TYPE_ATTRIBUTE, JAR_ARTIFACT_TYPE) it.to.setAttribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE) it.configureCommonParameters(kgpVersion, classLoadersCachingService, classpath, jvmToolchain, runKotlinCompilerViaBuildToolsApi, suppressVersionInconsistencyChecks) - it.setupTransformActionToolingDiagnostics(project) + it.parameters.setupKotlinToolingDiagnosticsParameters(project) } project.dependencies.registerTransform(BuildToolsApiClasspathEntrySnapshotTransform::class.java) { it.from.setAttribute(ARTIFACT_TYPE_ATTRIBUTE, DIRECTORY_ARTIFACT_TYPE) it.to.setAttribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE) it.configureCommonParameters(kgpVersion, classLoadersCachingService, classpath, jvmToolchain, runKotlinCompilerViaBuildToolsApi, suppressVersionInconsistencyChecks) - it.setupTransformActionToolingDiagnostics(project) + it.parameters.setupKotlinToolingDiagnosticsParameters(project) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/XcodeUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/XcodeUtils.kt index 165d2d5afce..28597c042aa 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/XcodeUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/XcodeUtils.kt @@ -5,8 +5,6 @@ package org.jetbrains.kotlin.gradle.utils -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.Provider import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode import org.jetbrains.kotlin.konan.target.* @@ -20,7 +18,7 @@ object XcodeUtils { fun bitcodeEmbeddingMode( outputKind: CompilerOutputKind, userMode: BitcodeEmbeddingMode?, - xcodeVersion: Provider, + xcodeVersion: XcodeVersion?, target: KonanTarget, debuggable: Boolean, ): BitcodeEmbeddingMode { @@ -35,14 +33,10 @@ object XcodeUtils { } } - private fun bitcodeSupported(xcodeVersion: Provider, target: KonanTarget): Boolean { - return XcodeVersion.parse(xcodeVersion).major < 14 + private fun bitcodeSupported(xcodeVersion: XcodeVersion?, target: KonanTarget): Boolean { + return xcodeVersion != null + && xcodeVersion.major < 14 && target.family in listOf(IOS, WATCHOS, TVOS) && target.architecture in listOf(ARM32, ARM64) } -} - -internal fun XcodeVersion.Companion.parse(file: Provider): XcodeVersion { - val version = file.getFile().readText() - return parse(version) ?: error("Couldn't parse Xcode version from '$version'") } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/XcodeUtilsTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/XcodeUtilsTest.kt index 6bc526cfc0f..a683d12076b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/XcodeUtilsTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/XcodeUtilsTest.kt @@ -7,10 +7,7 @@ package org.jetbrains.kotlin.gradle.unitTests -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.Provider import org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode.* -import org.jetbrains.kotlin.gradle.util.buildProject import org.jetbrains.kotlin.gradle.utils.XcodeUtils.bitcodeEmbeddingMode import org.jetbrains.kotlin.konan.target.CompilerOutputKind.FRAMEWORK import org.jetbrains.kotlin.konan.target.KonanTarget.* @@ -22,19 +19,10 @@ class XcodeUtilsTest { @Test fun `test bitcode embedding mode selection`() { - val project = buildProject() - - fun xcode(major: Int, minor: Int): Provider { - val version = XcodeVersion(major, minor).toString() - val file = project.file(version) - file.writeText(version) - return project.layout.file(project.provider { file }) - } - - assertEquals(DISABLE, bitcodeEmbeddingMode(FRAMEWORK, null, xcode(14, 1), IOS_ARM64, debuggable = true)) - assertEquals(MARKER, bitcodeEmbeddingMode(FRAMEWORK, null, xcode(13, 9), IOS_ARM64, debuggable = true)) - assertEquals(BITCODE, bitcodeEmbeddingMode(FRAMEWORK, null, xcode(13, 4), IOS_ARM64, debuggable = false)) - assertEquals(DISABLE, bitcodeEmbeddingMode(FRAMEWORK, null, xcode(10, 3), IOS_X64, debuggable = true)) - assertEquals(DISABLE, bitcodeEmbeddingMode(FRAMEWORK, null, xcode(13, 4), MACOS_ARM64, debuggable = false)) + assertEquals(DISABLE, bitcodeEmbeddingMode(FRAMEWORK, null, XcodeVersion(14, 1), IOS_ARM64, debuggable = true)) + assertEquals(MARKER, bitcodeEmbeddingMode(FRAMEWORK, null, XcodeVersion(13, 9), IOS_ARM64, debuggable = true)) + assertEquals(BITCODE, bitcodeEmbeddingMode(FRAMEWORK, null, XcodeVersion(13, 4), IOS_ARM64, debuggable = false)) + assertEquals(DISABLE, bitcodeEmbeddingMode(FRAMEWORK, null, XcodeVersion(10, 3), IOS_X64, debuggable = true)) + assertEquals(DISABLE, bitcodeEmbeddingMode(FRAMEWORK, null, XcodeVersion(13, 4), MACOS_ARM64, debuggable = false)) } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/agpCompatibility/versionTooHighMajor.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/agpCompatibility/versionTooHighMajor.txt index 4cba6363c67..65afa7836fb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/agpCompatibility/versionTooHighMajor.txt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/agpCompatibility/versionTooHighMajor.txt @@ -1,7 +1,7 @@ [IncompatibleAgpVersionTooHighWarning | WARNING] Kotlin Multiplatform <-> Android Gradle Plugin compatibility issue: The applied Android Gradle Plugin version (9.0.0) is higher than the maximum known to the Kotlin Gradle Plugin. -Tooling stability in such configuration isn't tested, please report encountered issues to https://kotl.in/issue" +Tooling stability in such configuration isn't tested, please report encountered issues to https://kotl.in/issue Minimum supported Android Gradle Plugin version: 4.2 Maximum tested Android Gradle Plugin version: 8.0 diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/agpCompatibility/versionTooHighMinor.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/agpCompatibility/versionTooHighMinor.txt index c8480c78e19..544f7d3c152 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/agpCompatibility/versionTooHighMinor.txt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/agpCompatibility/versionTooHighMinor.txt @@ -1,7 +1,7 @@ [IncompatibleAgpVersionTooHighWarning | WARNING] Kotlin Multiplatform <-> Android Gradle Plugin compatibility issue: The applied Android Gradle Plugin version (8.1.0) is higher than the maximum known to the Kotlin Gradle Plugin. -Tooling stability in such configuration isn't tested, please report encountered issues to https://kotl.in/issue" +Tooling stability in such configuration isn't tested, please report encountered issues to https://kotl.in/issue Minimum supported Android Gradle Plugin version: 4.2 Maximum tested Android Gradle Plugin version: 8.0 diff --git a/native/utils/src/org/jetbrains/kotlin/konan/target/Xcode.kt b/native/utils/src/org/jetbrains/kotlin/konan/target/Xcode.kt index fe782cc5bda..0a5ab82911f 100644 --- a/native/utils/src/org/jetbrains/kotlin/konan/target/Xcode.kt +++ b/native/utils/src/org/jetbrains/kotlin/konan/target/Xcode.kt @@ -41,6 +41,8 @@ data class XcodeVersion(val major: Int, val minor: Int) : Comparable