[Gradle] Support configuration cache in Xcode/CocoaPods tasks with Gradle 8.1
This commit moves invocation of `xcodebuild` from configuration to execution time. This also fixes the problem with Xcode version being cached inside the daemon. ^KT-59252 Verification Pending
This commit is contained in:
committed by
Space Team
parent
78f4d399d4
commit
b6847d0835
@@ -80,7 +80,7 @@ object PlatformInfo {
|
||||
&& properties.checkXcodeVersion
|
||||
&& requiredMajorVersion != null
|
||||
) {
|
||||
val currentXcodeVersion = Xcode.findCurrent().version
|
||||
val currentXcodeVersion = Xcode.findCurrent().version.toString()
|
||||
val currentMajorVersion = currentXcodeVersion.splitToSequence('.').first()
|
||||
if (currentMajorVersion != requiredMajorVersion) {
|
||||
throw IllegalStateException(
|
||||
|
||||
+2
-10
@@ -25,7 +25,7 @@ import kotlin.io.path.*
|
||||
import kotlin.test.*
|
||||
|
||||
@OsCondition(supportedOn = [OS.MAC], enabledOnCI = [OS.MAC])
|
||||
@DisplayName("K/N cocoapods tests")
|
||||
@DisplayName("CocoaPods plugin tests")
|
||||
@NativeGradlePluginTests
|
||||
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0)
|
||||
@OptIn(EnvironmentalVariablesOverride::class)
|
||||
@@ -771,16 +771,8 @@ class CocoaPodsIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Configuration cache works in a complex scenario with Gradle 8_1")
|
||||
@GradleTest
|
||||
@GradleTestVersions(minVersion = TestVersions.Gradle.G_8_1)
|
||||
@Disabled // will be fixed in the next step
|
||||
fun testConfigurationCacheWorksInAComplexScenarioWithGradle8_1(gradleVersion: GradleVersion) {
|
||||
testConfigurationCacheWorksInAComplexScenario(gradleVersion)
|
||||
}
|
||||
|
||||
@DisplayName("Configuration cache works in a complex scenario")
|
||||
@GradleTestVersions(minVersion = TestVersions.Gradle.MAX_SUPPORTED)
|
||||
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_6, maxVersion = TestVersions.Gradle.G_8_1)
|
||||
@GradleTest
|
||||
fun testConfigurationCacheWorksInAComplexScenario(gradleVersion: GradleVersion) {
|
||||
val buildOptions = defaultBuildOptions.copy(
|
||||
|
||||
+2
-7
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.native
|
||||
|
||||
import com.intellij.testFramework.TestDataFile
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.logging.configuration.WarningMode
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jdom.input.SAXBuilder
|
||||
import org.jetbrains.kotlin.gradle.*
|
||||
@@ -18,11 +17,7 @@ import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.jetbrains.kotlin.gradle.testbase.TestVersions.Kotlin.NATIVE_STABLE_RELEASE
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.jetbrains.kotlin.gradle.util.runProcess
|
||||
import org.jetbrains.kotlin.gradle.utils.Xcode
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.presetName
|
||||
import org.jetbrains.kotlin.konan.target.*
|
||||
import org.junit.Assume
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
@@ -364,7 +359,7 @@ class GeneralNativeIT : BaseGradleIT() {
|
||||
frameworkPaths.forEach { assertFileExists(it) }
|
||||
|
||||
assertTrue(fileInWorkingDir(headerPaths[0]).readText().contains("+ (int32_t)exported"))
|
||||
val xcodeMajorVersion = Xcode!!.currentVersion.major
|
||||
val xcodeMajorVersion = Xcode.findCurrent().version.major
|
||||
|
||||
// Check that by default release frameworks have bitcode embedded.
|
||||
withNativeCommandLineArguments(":linkMainReleaseFrameworkIos") { arguments ->
|
||||
|
||||
+3
-3
@@ -14,10 +14,10 @@ import org.jetbrains.kotlin.gradle.testbase.extractNativeCompilerCommandLineArgu
|
||||
import org.jetbrains.kotlin.gradle.transformProjectWithPluginsDsl
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
|
||||
import org.jetbrains.kotlin.gradle.utils.Xcode
|
||||
import org.jetbrains.kotlin.gradle.utils.XcodeVersion
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.Xcode
|
||||
import org.jetbrains.kotlin.konan.target.XcodeVersion
|
||||
import org.jetbrains.kotlin.konan.target.presetName
|
||||
import org.jetbrains.kotlin.konan.util.DependencyDirectories
|
||||
import org.junit.Assume
|
||||
@@ -244,7 +244,7 @@ class NativeDownloadAndPlatformLibsIT : BaseGradleIT() {
|
||||
|
||||
if (HostManager.hostIsMac) {
|
||||
// Building platform libs require Xcode 14.1
|
||||
Assume.assumeTrue(Xcode!!.currentVersion >= XcodeVersion(14, 1))
|
||||
Assume.assumeTrue(Xcode.findCurrent().version >= XcodeVersion(14, 1))
|
||||
}
|
||||
|
||||
with(transformNativeTestProjectWithPluginDsl("native-download-maven")) {
|
||||
|
||||
+2
-3
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.native
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.jetbrains.kotlin.gradle.util.runProcess
|
||||
import org.jetbrains.kotlin.gradle.utils.Xcode
|
||||
import org.jetbrains.kotlin.gradle.utils.XcodeUtils
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
@@ -21,8 +21,7 @@ import kotlin.test.assertEquals
|
||||
@OsCondition(supportedOn = [OS.MAC], enabledOnCI = [OS.MAC])
|
||||
class NativeXcodeSimulatorTestsIT : KGPBaseTest() {
|
||||
private val defaultIosSimulator by lazy {
|
||||
val xcode = Xcode ?: error("XCode is expected to be defined")
|
||||
xcode.getDefaultTestDeviceId(KonanTarget.IOS_SIMULATOR_ARM64) ?: error("No simulator found for iOS ARM64")
|
||||
XcodeUtils.getDefaultTestDeviceId(KonanTarget.IOS_SIMULATOR_ARM64) ?: error("No simulator found for iOS ARM64")
|
||||
}
|
||||
|
||||
@DisplayName("A user-friendly error message is produced when the standalone mode is disabled and no simulator has booted")
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.enabledOnCurrentHost
|
||||
import org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
import org.jetbrains.kotlin.gradle.utils.getFile
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.gradle.utils.mapToFile
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
@@ -247,7 +248,7 @@ internal abstract class FrameworkCopy : DefaultTask() {
|
||||
|
||||
private fun copy(sourceProvider: Provider<File>) {
|
||||
val source = sourceProvider.get()
|
||||
val destination = destinationDirectory.asFile.get()
|
||||
val destination = destinationDirectory.getFile()
|
||||
|
||||
val destinationFile = File(destination, source.name)
|
||||
if (destinationFile.exists()) {
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
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.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||
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
|
||||
|
||||
internal abstract class XcodeVersionTask : DefaultTask() {
|
||||
|
||||
companion object {
|
||||
fun locateOrRegister(project: Project): Provider<XcodeVersionTask> {
|
||||
return project.rootProject.locateOrRegisterTask("xcodeVersion") { task ->
|
||||
if (!HostManager.hostIsMac) {
|
||||
task.onlyIfCompat("Task can be run only on MacOS") { false }
|
||||
return@locateOrRegisterTask
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
// DEVELOPER_DIR may override currently selected Xcode (it's respected by xcode-select and xcrun)
|
||||
task.xcodeDeveloperDir.convention(project.providers.environmentVariable("DEVELOPER_DIR"))
|
||||
|
||||
task.outputFile.convention(project.layout.buildDirectory.file("xcode-version.txt"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@get:InputFiles // @InputFiles instead of @InputFile because it allows non-existing files
|
||||
abstract val xcodeSelectLink: ConfigurableFileCollection
|
||||
|
||||
@get:Input
|
||||
@get:Optional
|
||||
abstract val xcodeDeveloperDir: Property<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputFile: RegularFileProperty
|
||||
|
||||
@TaskAction
|
||||
fun execute() {
|
||||
outputFile.getFile().writeText(Xcode.findCurrent().version.toString())
|
||||
}
|
||||
}
|
||||
|
||||
internal val Provider<XcodeVersionTask>.version: Provider<RegularFile>
|
||||
get() = flatMap { it.outputFile }
|
||||
+7
-3
@@ -29,7 +29,9 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.ReadyForEx
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.KOTLIN_NATIVE_IGNORE_INCORRECT_DEPENDENCIES
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.artifactTypeAttribute
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XcodeVersionTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.registerEmbedAndSignAppleFrameworkTask
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.version
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmVariant
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
||||
@@ -46,7 +48,7 @@ import org.jetbrains.kotlin.gradle.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
|
||||
import org.jetbrains.kotlin.gradle.testing.testTaskName
|
||||
import org.jetbrains.kotlin.gradle.utils.Xcode
|
||||
import org.jetbrains.kotlin.gradle.utils.XcodeUtils
|
||||
import org.jetbrains.kotlin.gradle.utils.newInstance
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
@@ -63,6 +65,7 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
|
||||
// this afterEvaluate comes from NativeCompilerOptions
|
||||
val compilationCompilerOptions = binary.compilation.compilerOptions
|
||||
val konanPropertiesBuildService = KonanPropertiesBuildService.registerIfAbsent(project)
|
||||
val xcodeVersionTask = XcodeVersionTask.locateOrRegister(project)
|
||||
val linkTask = registerTask<KotlinNativeLink>(
|
||||
binary.linkTaskName, listOf(binary)
|
||||
) {
|
||||
@@ -75,6 +78,7 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
|
||||
it.toolOptions.freeCompilerArgs.value(compilationCompilerOptions.options.freeCompilerArgs)
|
||||
it.toolOptions.freeCompilerArgs.addAll(providers.provider { PropertiesProvider(project).nativeLinkArgs })
|
||||
it.runViaBuildToolsApi.value(false).disallowChanges() // K/N is not yet supported
|
||||
it.xcodeVersion.set(xcodeVersionTask.version)
|
||||
}
|
||||
|
||||
|
||||
@@ -584,9 +588,9 @@ class KotlinNativeTargetWithSimulatorTestsConfigurator :
|
||||
|
||||
override fun configureTestTask(target: KotlinNativeTargetWithSimulatorTests, testTask: KotlinNativeSimulatorTest) {
|
||||
super.configureTestTask(target, testTask)
|
||||
if (Xcode != null) {
|
||||
if (isTestTaskEnabled(target)) {
|
||||
val deviceIdProvider = testTask.project.provider {
|
||||
Xcode.getDefaultTestDeviceId(target.konanTarget)
|
||||
XcodeUtils.getDefaultTestDeviceId(target.konanTarget)
|
||||
?: error("Xcode does not support simulator tests for ${target.konanTarget.name}. Check that requested SDK is installed.")
|
||||
}
|
||||
testTask.device.convention(deviceIdProvider).finalizeValueOnRead()
|
||||
|
||||
+1
-8
@@ -13,13 +13,11 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.attributes.Attribute
|
||||
import org.gradle.api.attributes.HasAttributes
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.AbstractExecTask
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
|
||||
import org.jetbrains.kotlin.gradle.utils.Xcode
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly
|
||||
@@ -280,12 +278,7 @@ class Framework(
|
||||
/**
|
||||
* Embed bitcode for the framework or not. See [BitcodeEmbeddingMode].
|
||||
*/
|
||||
val embedBitcodeMode = project.objects.property(org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode::class.java).apply {
|
||||
convention(project.provider {
|
||||
Xcode?.defaultBitcodeEmbeddingMode(konanTarget, buildType)
|
||||
?: org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode.DISABLE
|
||||
})
|
||||
}
|
||||
val embedBitcodeMode = project.objects.property(org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode::class.java)
|
||||
|
||||
@Deprecated("Use 'embedBitcodeMode' property instead.")
|
||||
var embedBitcode: org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode
|
||||
|
||||
+3
-2
@@ -48,7 +48,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import java.io.File
|
||||
|
||||
|
||||
|
||||
internal val ProjectLayout.cocoapodsBuildDirs: CocoapodsBuildDirs
|
||||
get() = CocoapodsBuildDirs(this)
|
||||
|
||||
@@ -515,6 +514,8 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
||||
else -> error("Unknown cocoapods platform: $family")
|
||||
}
|
||||
|
||||
val xcodeVersionTask = XcodeVersionTask.locateOrRegister(project)
|
||||
|
||||
val podGenTask = project.registerTask<PodGenTask>(family.toPodGenTaskName) { task ->
|
||||
task.description = "Сreates a synthetic Xcode project to retrieve CocoaPods dependencies"
|
||||
task.podspec.set(podspecTaskProvider.map { it.outputFile })
|
||||
@@ -524,7 +525,7 @@ open class KotlinCocoapodsPlugin : Plugin<Project> {
|
||||
task.family.set(family)
|
||||
task.platformSettings.set(platformSettings)
|
||||
task.pods.set(cocoapodsExtension.pods)
|
||||
task.xcodeVersion.set(Xcode?.currentVersion)
|
||||
task.xcodeVersion.set(xcodeVersionTask.version)
|
||||
}
|
||||
|
||||
project.registerTask<PodInstallSyntheticTask>(family.toPodInstallSyntheticTaskName) { task ->
|
||||
|
||||
+7
-4
@@ -8,6 +8,7 @@
|
||||
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
|
||||
@@ -15,8 +16,9 @@ import org.gradle.api.tasks.*
|
||||
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.XcodeVersion
|
||||
import org.jetbrains.kotlin.gradle.utils.parse
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.konan.target.XcodeVersion
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -52,8 +54,9 @@ abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : Co
|
||||
@get:Nested
|
||||
internal abstract val pods: ListProperty<CocoapodsDependency>
|
||||
|
||||
@get:Input
|
||||
internal abstract val xcodeVersion: Property<XcodeVersion>
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
internal abstract val xcodeVersion: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
val podfile: Provider<File> = projectLayout.cocoapodsBuildDirs.synthetic(family).map { it.file("Podfile").asFile }
|
||||
@@ -123,7 +126,7 @@ abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : Co
|
||||
}
|
||||
|
||||
private fun insertXcode143DeploymentTargetWorkarounds(family: Family): String {
|
||||
if (xcodeVersion.get() < XcodeVersion(14, 3)) {
|
||||
if (XcodeVersion.parse(xcodeVersion) < XcodeVersion(14, 3)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
+13
-3
@@ -183,12 +183,18 @@ constructor(
|
||||
get() = embedBitcodeMode.get()
|
||||
|
||||
@get:Input
|
||||
@get:Optional
|
||||
val embedBitcodeMode: Provider<BitcodeEmbeddingMode> =
|
||||
(binary as? Framework)?.embedBitcodeMode ?: project.provider { BitcodeEmbeddingMode.DISABLE }
|
||||
(binary as? Framework)?.embedBitcodeMode ?: objectFactory.property()
|
||||
|
||||
@get:Internal
|
||||
val apiFiles = project.files(project.configurations.getByName(compilation.apiConfigurationName)).filterKlibsPassedToCompiler()
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
internal val xcodeVersion = objectFactory.fileProperty()
|
||||
|
||||
private val externalDependenciesArgs by lazy { ExternalDependenciesBuilder(project, compilation).buildCompilerArgs() }
|
||||
|
||||
private val cacheBuilderSettings by lazy {
|
||||
@@ -224,10 +230,10 @@ constructor(
|
||||
args.generateTestRunner = processTests
|
||||
args.mainPackage = entryPoint
|
||||
|
||||
when (embedBitcodeMode.get()) {
|
||||
when (bitcodeEmbeddingMode()) {
|
||||
BitcodeEmbeddingMode.BITCODE -> args.embedBitcode = true
|
||||
BitcodeEmbeddingMode.MARKER -> args.embedBitcodeMarker = true
|
||||
null, BitcodeEmbeddingMode.DISABLE -> Unit
|
||||
BitcodeEmbeddingMode.DISABLE -> Unit
|
||||
}
|
||||
|
||||
args.singleLinkerArguments = (linkerOpts + additionalLinkerOpts).toTypedArray()
|
||||
@@ -391,4 +397,8 @@ constructor(
|
||||
private inline fun <reified T : Any> lazyConvention(noinline lazyConventionValue: () -> T): Provider<T> {
|
||||
return objectFactory.providerWithLazyConvention(lazyConventionValue)
|
||||
}
|
||||
|
||||
private fun bitcodeEmbeddingMode(): BitcodeEmbeddingMode {
|
||||
return XcodeUtils.bitcodeEmbeddingMode(outputKind, embedBitcodeMode.orNull, xcodeVersion, konanTarget, debuggable)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -15,10 +15,11 @@ 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
|
||||
import org.jetbrains.kotlin.gradle.utils.Xcode
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.presetName
|
||||
@@ -109,6 +110,7 @@ internal fun KotlinNativeArtifact.registerLinkFrameworkTask(
|
||||
): TaskProvider<KotlinNativeLinkArtifactTask> {
|
||||
val kind = NativeOutputKind.FRAMEWORK
|
||||
val destinationDir = project.buildDir.resolve("$outDirName/${target.visibleName}/${buildType.visibleName}")
|
||||
val xcodeVersionTask = XcodeVersionTask.locateOrRegister(project)
|
||||
val resultTask = project.registerTask<KotlinNativeLinkArtifactTask>(
|
||||
lowerCamelCaseName("assemble", name, buildType.visibleName, kind.taskNameClassifier, target.presetName, taskNameSuffix),
|
||||
listOf(target, kind.compilerOutputKind)
|
||||
@@ -121,11 +123,10 @@ 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)
|
||||
} else if (Xcode != null) {
|
||||
task.embedBitcode.set(project.provider { Xcode.defaultBitcodeEmbeddingMode(target, buildType) })
|
||||
}
|
||||
task.libraries.setFrom(project.configurations.getByName(librariesConfigurationName))
|
||||
task.exportLibraries.setFrom(project.configurations.getByName(exportConfigurationName))
|
||||
|
||||
+12
-2
@@ -11,6 +11,7 @@ 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
|
||||
@@ -26,6 +27,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.buildKotlinNativeBinaryLinkerArgs
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinToolTask
|
||||
import org.jetbrains.kotlin.gradle.utils.XcodeUtils
|
||||
import org.jetbrains.kotlin.gradle.utils.newInstance
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
@@ -73,6 +75,7 @@ abstract class KotlinNativeLinkArtifactTask @Inject constructor(
|
||||
abstract val staticFramework: Property<Boolean>
|
||||
|
||||
@get:Input
|
||||
@get:Optional
|
||||
abstract val embedBitcode: Property<BitcodeEmbeddingMode>
|
||||
|
||||
@get:Classpath
|
||||
@@ -90,6 +93,10 @@ abstract class KotlinNativeLinkArtifactTask @Inject constructor(
|
||||
@get:Input
|
||||
abstract val binaryOptions: MapProperty<String, String>
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
abstract val xcodeVersion: RegularFileProperty
|
||||
|
||||
private val nativeBinaryOptions = PropertiesProvider(project).nativeBinaryOptions
|
||||
|
||||
@get:Input
|
||||
@@ -163,7 +170,6 @@ abstract class KotlinNativeLinkArtifactTask @Inject constructor(
|
||||
enableEndorsedLibs.value(false).finalizeValue()
|
||||
processTests.convention(false)
|
||||
staticFramework.convention(false)
|
||||
embedBitcode.convention(BitcodeEmbeddingMode.DISABLE)
|
||||
destinationDir.convention(debuggable.flatMap {
|
||||
val kind = outputKind.visibleName
|
||||
val target = konanTarget.visibleName
|
||||
@@ -191,7 +197,7 @@ abstract class KotlinNativeLinkArtifactTask @Inject constructor(
|
||||
compilerPlugins = emptyList(),//CompilerPlugins aren't needed here because it's no compilation but linking
|
||||
processTests = processTests.get(),
|
||||
entryPoint = entryPoint.getOrNull(),
|
||||
embedBitcode = embedBitcode.get(),
|
||||
embedBitcode = bitcodeEmbeddingMode(),
|
||||
linkerOpts = linkerOptions.get(),
|
||||
binaryOptions = allBinaryOptions.get(),
|
||||
isStaticFramework = staticFramework.get(),
|
||||
@@ -205,4 +211,8 @@ abstract class KotlinNativeLinkArtifactTask @Inject constructor(
|
||||
executionContext = KotlinToolRunner.GradleExecutionContext.fromTaskContext(objectFactory, execOperations, logger)
|
||||
).run(buildArgs)
|
||||
}
|
||||
|
||||
private fun bitcodeEmbeddingMode(): BitcodeEmbeddingMode {
|
||||
return XcodeUtils.bitcodeEmbeddingMode(outputKind, embedBitcode.orNull, xcodeVersion, konanTarget, debuggable.get())
|
||||
}
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.utils
|
||||
|
||||
import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.BitcodeEmbeddingMode
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
|
||||
import org.jetbrains.kotlin.konan.target.Architecture.ARM32
|
||||
import org.jetbrains.kotlin.konan.target.Architecture.ARM64
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.konan.target.Family.*
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly
|
||||
import java.io.Serializable
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
val Xcode = XcodeUtils.INSTANCE
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
data class XcodeVersion(val major: Int, val minor: Int) : Serializable, Comparable<XcodeVersion> {
|
||||
override fun compareTo(other: XcodeVersion): Int {
|
||||
return when (val majorComparison = major.compareTo(other.major)) {
|
||||
0 -> minor.compareTo(other.minor)
|
||||
else -> majorComparison
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
class XcodeUtils private constructor() {
|
||||
|
||||
companion object {
|
||||
internal val INSTANCE: XcodeUtils? =
|
||||
if (HostManager.hostIsMac) XcodeUtils() else null
|
||||
|
||||
internal fun parseFromXcodebuild(output: String): XcodeVersion? {
|
||||
val version = output.lines()[0].removePrefix("Xcode ")
|
||||
val split = version.split("(\\s+|\\.|-)".toRegex())
|
||||
return XcodeVersion(
|
||||
major = split[0].toIntOrNull() ?: return null,
|
||||
minor = split.getOrNull(1)?.toIntOrNull() ?: return null,
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val currentVersion: XcodeVersion by lazy {
|
||||
val out = runCommand(listOf("/usr/bin/xcrun", "xcodebuild", "-version"))
|
||||
parseFromXcodebuild(out) ?: XcodeVersion(1, 0)
|
||||
}
|
||||
|
||||
private val defaultTestDevices: Map<Family, String> by lazy {
|
||||
val osRegex = "-- .* --".toRegex()
|
||||
val deviceRegex = """[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}""".toRegex()
|
||||
|
||||
val out = runCommand(listOf("/usr/bin/xcrun", "simctl", "list", "devices", "available"))
|
||||
|
||||
val result = mutableMapOf<Family, String>()
|
||||
var os: Family? = null
|
||||
out.lines().forEach { s ->
|
||||
val osFound = osRegex.find(s)?.value
|
||||
if (osFound != null) {
|
||||
val osName = osFound.split(" ")[1]
|
||||
os = try {
|
||||
Family.valueOf(osName.toUpperCaseAsciiOnly())
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
val currentOs = os
|
||||
if (currentOs != null) {
|
||||
val deviceFound = deviceRegex.find(s)?.value
|
||||
if (deviceFound != null) {
|
||||
result[currentOs] = deviceFound
|
||||
os = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fun getDefaultTestDeviceId(target: KonanTarget): String? = defaultTestDevices[target.family]
|
||||
|
||||
fun defaultBitcodeEmbeddingMode(target: KonanTarget, buildType: NativeBuildType): BitcodeEmbeddingMode {
|
||||
if (currentVersion.major < 14) {
|
||||
if (target.family in listOf(IOS, WATCHOS, TVOS) && target.architecture in listOf(ARM32, ARM64)) {
|
||||
when (buildType) {
|
||||
NativeBuildType.RELEASE -> return BitcodeEmbeddingMode.BITCODE
|
||||
NativeBuildType.DEBUG -> return BitcodeEmbeddingMode.MARKER
|
||||
}
|
||||
}
|
||||
}
|
||||
return BitcodeEmbeddingMode.DISABLE
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.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.*
|
||||
import org.jetbrains.kotlin.konan.target.Architecture.ARM32
|
||||
import org.jetbrains.kotlin.konan.target.Architecture.ARM64
|
||||
import org.jetbrains.kotlin.konan.target.Family.*
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly
|
||||
|
||||
@InternalKotlinGradlePluginApi
|
||||
object XcodeUtils {
|
||||
|
||||
fun bitcodeEmbeddingMode(
|
||||
outputKind: CompilerOutputKind,
|
||||
userMode: BitcodeEmbeddingMode?,
|
||||
xcodeVersion: Provider<RegularFile>,
|
||||
target: KonanTarget,
|
||||
debuggable: Boolean,
|
||||
): BitcodeEmbeddingMode {
|
||||
return when {
|
||||
outputKind != CompilerOutputKind.FRAMEWORK -> BitcodeEmbeddingMode.DISABLE
|
||||
userMode != null -> userMode
|
||||
bitcodeSupported(xcodeVersion, target) -> when (debuggable) {
|
||||
true -> BitcodeEmbeddingMode.MARKER
|
||||
false -> BitcodeEmbeddingMode.BITCODE
|
||||
}
|
||||
else -> BitcodeEmbeddingMode.DISABLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun bitcodeSupported(xcodeVersion: Provider<RegularFile>, target: KonanTarget): Boolean {
|
||||
return XcodeVersion.parse(xcodeVersion).major < 14
|
||||
&& target.family in listOf(IOS, WATCHOS, TVOS)
|
||||
&& target.architecture in listOf(ARM32, ARM64)
|
||||
}
|
||||
|
||||
fun getDefaultTestDeviceId(target: KonanTarget): String? = defaultTestDevices[target.family]
|
||||
|
||||
private val defaultTestDevices: Map<Family, String> by lazy {
|
||||
if (!HostManager.hostIsMac) {
|
||||
return@lazy emptyMap()
|
||||
}
|
||||
|
||||
val osRegex = "-- .* --".toRegex()
|
||||
val deviceRegex = """[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}""".toRegex()
|
||||
|
||||
val out = runCommand(listOf("/usr/bin/xcrun", "simctl", "list", "devices", "available"))
|
||||
|
||||
val result = mutableMapOf<Family, String>()
|
||||
var os: Family? = null
|
||||
out.lines().forEach { s ->
|
||||
val osFound = osRegex.find(s)?.value
|
||||
if (osFound != null) {
|
||||
val osName = osFound.split(" ")[1]
|
||||
os = try {
|
||||
Family.valueOf(osName.toUpperCaseAsciiOnly())
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
val currentOs = os
|
||||
if (currentOs != null) {
|
||||
val deviceFound = deviceRegex.find(s)?.value
|
||||
if (deviceFound != null) {
|
||||
result[currentOs] = deviceFound
|
||||
os = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun XcodeVersion.Companion.parse(file: Provider<RegularFile>): XcodeVersion {
|
||||
val version = file.getFile().readText()
|
||||
return parse(version) ?: error("Couldn't parse Xcode version from '$version'")
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName")
|
||||
|
||||
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.*
|
||||
import org.jetbrains.kotlin.konan.target.XcodeVersion
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class XcodeUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `test bitcode embedding mode selection`() {
|
||||
val project = buildProject()
|
||||
|
||||
fun xcode(major: Int, minor: Int): Provider<RegularFile> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.utils
|
||||
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class XcodeVersionTest {
|
||||
@Test
|
||||
fun testParsing() {
|
||||
assertEquals(XcodeVersion(14, 1), XcodeUtils.parseFromXcodebuild("Xcode 14.1\nBuild version 14B47b\n"))
|
||||
assertEquals(XcodeVersion(14, 3), XcodeUtils.parseFromXcodebuild("Xcode 14.3\nBuild version 14E222b\n"))
|
||||
assertEquals(XcodeVersion(1, 0), XcodeUtils.parseFromXcodebuild("Xcode 1.0 RC\nBuild version 7B85\n"))
|
||||
assertEquals(XcodeVersion(1, 15), XcodeUtils.parseFromXcodebuild("Xcode 1.15-RC\nBuild version 7B85\n"))
|
||||
assertEquals(XcodeVersion(14, 1), XcodeUtils.parseFromXcodebuild("Xcode 14.1.1.3.2.1.123\nBuild version 14B47b\n"))
|
||||
assertEquals(null, XcodeUtils.parseFromXcodebuild("Xcode 14\nBuild version 14B47b\n"))
|
||||
assertEquals(null, XcodeUtils.parseFromXcodebuild("XC 14.1\nBuild version 14B47b\n"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testComparison() {
|
||||
assert(XcodeVersion(3, 2) == XcodeVersion(3, 2))
|
||||
assert(XcodeVersion(3, 2) > XcodeVersion(2, 14))
|
||||
assert(XcodeVersion(3, 2) > XcodeVersion(3, 1))
|
||||
assert(XcodeVersion(3, 2) < XcodeVersion(3, 3))
|
||||
assert(XcodeVersion(4, 0) > XcodeVersion(3, 3))
|
||||
}
|
||||
}
|
||||
+2
-8
@@ -143,16 +143,10 @@ class XcodeSimulatorExecutor(
|
||||
|
||||
private fun downloadRuntimeFor(osName: String) {
|
||||
val version = Xcode.findCurrent().version
|
||||
val versionSplit = version.split(".")
|
||||
check(versionSplit.size >= 2) {
|
||||
"Unrecognised version of Xcode $version was split to $versionSplit"
|
||||
}
|
||||
val major = versionSplit[0].toInt()
|
||||
val minor = versionSplit[1].toInt()
|
||||
check(major >= 14) {
|
||||
check(version.major >= 14) {
|
||||
"Was unable to get the required runtimes running on Xcode $version. Check the Xcode installation"
|
||||
}
|
||||
if (minor >= 1) {
|
||||
if (version.minor >= 1) {
|
||||
// Option -downloadPlatform NAME available only since 14.1
|
||||
hostExecutor.run("/usr/bin/xcrun", "xcodebuild", "-downloadPlatform", osName)
|
||||
} else {
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.konan.target
|
||||
import org.jetbrains.kotlin.konan.properties.KonanPropertiesLoader
|
||||
import org.jetbrains.kotlin.konan.properties.Properties
|
||||
import org.jetbrains.kotlin.konan.util.InternalServer
|
||||
import kotlin.math.max
|
||||
|
||||
class AppleConfigurablesImpl(
|
||||
target: KonanTarget,
|
||||
@@ -58,7 +57,7 @@ class AppleConfigurablesImpl(
|
||||
val xcode = Xcode.findCurrent()
|
||||
|
||||
if (properties.getProperty("ignoreXcodeVersionCheck") != "true") {
|
||||
properties.getProperty("minimalXcodeVersion")?.let { minimalXcodeVersion ->
|
||||
properties.getProperty("minimalXcodeVersion")?.let(XcodeVersion::parse)?.let { minimalXcodeVersion ->
|
||||
val currentXcodeVersion = xcode.version
|
||||
checkXcodeVersion(minimalXcodeVersion, currentXcodeVersion)
|
||||
}
|
||||
@@ -68,22 +67,9 @@ class AppleConfigurablesImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkXcodeVersion(minimalVersion: String, currentVersion: String) {
|
||||
// Xcode versions contain only numbers (even betas).
|
||||
// But we still split by '-' and whitespaces to take into account versions like 11.2-beta.
|
||||
val minimalVersionParts = minimalVersion.split("(\\s+|\\.|-)".toRegex()).map { it.toIntOrNull() ?: 0 }
|
||||
val currentVersionParts = currentVersion.split("(\\s+|\\.|-)".toRegex()).map { it.toIntOrNull() ?: 0 }
|
||||
val size = max(minimalVersionParts.size, currentVersionParts.size)
|
||||
|
||||
for (i in 0 until size) {
|
||||
val currentPart = currentVersionParts.getOrElse(i) { 0 }
|
||||
val minimalPart = minimalVersionParts.getOrElse(i) { 0 }
|
||||
|
||||
when {
|
||||
currentPart > minimalPart -> return
|
||||
currentPart < minimalPart ->
|
||||
error("Unsupported Xcode version $currentVersion, minimal supported version is $minimalVersion.")
|
||||
}
|
||||
private fun checkXcodeVersion(minimalVersion: XcodeVersion, currentVersion: XcodeVersion) {
|
||||
if (currentVersion < minimalVersion) {
|
||||
error("Unsupported Xcode version $currentVersion, minimal supported version is $minimalVersion.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,35 @@ import org.jetbrains.kotlin.konan.MissingXcodeException
|
||||
import org.jetbrains.kotlin.konan.exec.Command
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
|
||||
data class XcodeVersion(val major: Int, val minor: Int) : Comparable<XcodeVersion> {
|
||||
override fun compareTo(other: XcodeVersion): Int {
|
||||
return when (val majorComparison = major.compareTo(other.major)) {
|
||||
0 -> minor.compareTo(other.minor)
|
||||
else -> majorComparison
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$major.$minor"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun parse(version: String): XcodeVersion? {
|
||||
val split = version.split("(\\s+|\\.|-)".toRegex())
|
||||
return XcodeVersion(
|
||||
major = split[0].toIntOrNull() ?: return null,
|
||||
minor = split.getOrNull(1)?.toIntOrNull() ?: return null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Xcode {
|
||||
val toolchain: String
|
||||
val macosxSdk: String
|
||||
val iphoneosSdk: String
|
||||
val iphonesimulatorSdk: String
|
||||
val version: String
|
||||
val version: XcodeVersion
|
||||
val appletvosSdk: String
|
||||
val appletvsimulatorSdk: String
|
||||
val watchosSdk: String
|
||||
@@ -87,12 +110,14 @@ internal class CurrentXcode : Xcode {
|
||||
override val watchosSdk: String by lazy { getSdkPath("watchos") }
|
||||
override val watchsimulatorSdk: String by lazy { getSdkPath("watchsimulator") }
|
||||
|
||||
internal val xcodebuildVersion: String
|
||||
internal val xcodebuildVersion: XcodeVersion
|
||||
get() = xcrun("xcodebuild", "-version")
|
||||
.removePrefix("Xcode ")
|
||||
.removePrefix("Xcode ")
|
||||
.parseXcodeVersion()
|
||||
|
||||
internal val bundleVersion: String
|
||||
internal val bundleVersion: XcodeVersion
|
||||
get() = bash("""/usr/libexec/PlistBuddy "$(xcode-select -print-path)/../Info.plist" -c "Print :CFBundleShortVersionString"""")
|
||||
.parseXcodeVersion()
|
||||
|
||||
override val version by lazy {
|
||||
try {
|
||||
@@ -119,4 +144,8 @@ internal class CurrentXcode : Xcode {
|
||||
private fun bash(command: String): String = Command("/bin/bash", "-c", command).getOutputLines().joinToString("\n")
|
||||
|
||||
private fun getSdkPath(sdk: String) = xcrun("--sdk", sdk, "--show-sdk-path")
|
||||
|
||||
private fun String.parseXcodeVersion(): XcodeVersion {
|
||||
return XcodeVersion.parse(this) ?: throw MissingXcodeException("Couldn't parse Xcode version from '$this'")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.konan.target
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
|
||||
class XcodeVersionTest {
|
||||
@Test
|
||||
fun `xcode version parsed correctly`() {
|
||||
assertEquals(XcodeVersion(14, 1), XcodeVersion.parse("14.1\nBuild version 14B47b\n"))
|
||||
assertEquals(XcodeVersion(14, 3), XcodeVersion.parse("14.3\nBuild version 14E222b\n"))
|
||||
assertEquals(XcodeVersion(1, 0), XcodeVersion.parse("1.0 RC\nBuild version 7B85\n"))
|
||||
assertEquals(XcodeVersion(1, 15), XcodeVersion.parse("1.15-RC\nBuild version 7B85\n"))
|
||||
assertEquals(XcodeVersion(14, 1), XcodeVersion.parse("14.1.1.3.2.1.123"))
|
||||
assertEquals(null, XcodeVersion.parse("14\nBuild version 14B47b\n"))
|
||||
assertEquals(null, XcodeVersion.parse("13"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test xcode version comparison`() {
|
||||
assert(XcodeVersion(3, 2) == XcodeVersion(3, 2))
|
||||
assert(XcodeVersion(3, 2) > XcodeVersion(2, 14))
|
||||
assert(XcodeVersion(3, 2) > XcodeVersion(3, 1))
|
||||
assert(XcodeVersion(3, 2) < XcodeVersion(3, 3))
|
||||
assert(XcodeVersion(4, 0) > XcodeVersion(3, 3))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user