diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt index e04d1acfacb..caa9dc59cea 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt @@ -9,7 +9,6 @@ import org.gradle.api.logging.LogLevel import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.report.BuildReportType import org.jetbrains.kotlin.gradle.testbase.* -import org.jetbrains.kotlin.konan.target.HostManager import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.io.TempDir import java.nio.file.Path @@ -79,53 +78,6 @@ class ConfigurationCacheIT : AbstractConfigurationCacheIT() { } } - @NativeGradlePluginTests - @DisplayName("works with native tasks in complex project") - @GradleTestVersions( - minVersion = TestVersions.Gradle.G_7_4, - additionalVersions = [TestVersions.Gradle.G_7_6], - maxVersion = TestVersions.Gradle.G_8_1 - ) - @GradleTest - fun testNativeTasks(gradleVersion: GradleVersion) { - val expectedTasks = mutableListOf( - ":lib:cinteropMyCinteropLinuxX64", - ":lib:commonizeCInterop", - ":lib:compileKotlinLinuxX64", - ":lib:linkExecutableDebugExecutableLinuxX64", - ":lib:linkSharedDebugSharedLinuxX64", - ":lib:linkStaticDebugStaticLinuxX64", - ":lib:linkDebugTestLinuxX64", - ) - - if (HostManager.hostIsMac) { - expectedTasks += listOf( - ":lib:cinteropMyCinteropIosX64", - ":lib:compileKotlinIosX64", - ":lib:assembleMyframeDebugFrameworkIosArm64", - ":lib:assembleMyfatframeDebugFatFramework", - ":lib:assembleLibDebugXCFramework", - ":lib:compileTestKotlinIosX64", - ":lib:linkDebugTestIosX64", - ":lib:transformCommonMainDependenciesMetadata", - ":lib:transformCommonMainCInteropDependenciesMetadata", - ":lib:linkDebugFrameworkIosArm64", - ":lib:linkDebugFrameworkIosX64", - ":lib:linkDebugFrameworkIosFat", - ":lib:linkReleaseFrameworkIosArm64", - ":lib:linkReleaseFrameworkIosX64", - ":lib:linkReleaseFrameworkIosFat", - ) - } - - project("native-configuration-cache", gradleVersion) { - testConfigurationCacheOf( - "build", - executedTaskNames = expectedTasks, - ) - } - } - @NativeGradlePluginTests @DisplayName("works with commonizer") @GradleTestVersions( diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MacosCapableConfigurationCacheIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MacosCapableConfigurationCacheIT.kt new file mode 100644 index 00000000000..4928b8e249c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MacosCapableConfigurationCacheIT.kt @@ -0,0 +1,92 @@ +/* + * 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 + +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.konan.target.HostManager +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.condition.OS +import org.junit.jupiter.api.io.TempDir +import java.nio.file.Path + +@DisplayName("Configuration cache tests that can be run on MacOS") +@NativeGradlePluginTests +@OsCondition(supportedOn = [OS.LINUX, OS.MAC, OS.WINDOWS], enabledOnCI = [OS.LINUX, OS.MAC, OS.WINDOWS]) +class MacosCapableConfigurationCacheIT : AbstractConfigurationCacheIT() { + + @DisplayName("works with native tasks in complex project") + @GradleTestVersions( + minVersion = TestVersions.Gradle.G_7_4, + additionalVersions = [TestVersions.Gradle.G_7_6], + maxVersion = TestVersions.Gradle.G_8_1 + ) + @GradleTest + fun testNativeTasks(gradleVersion: GradleVersion) { + val expectedTasks = mutableListOf( + ":lib:cinteropMyCinteropLinuxX64", + ":lib:commonizeCInterop", + ":lib:compileKotlinLinuxX64", + ":lib:linkExecutableDebugExecutableLinuxX64", + ":lib:linkSharedDebugSharedLinuxX64", + ":lib:linkStaticDebugStaticLinuxX64", + ":lib:linkDebugTestLinuxX64", + ) + + if (HostManager.hostIsMac) { + expectedTasks += listOf( + ":lib:cinteropMyCinteropIosX64", + ":lib:compileKotlinIosX64", + ":lib:assembleMyframeDebugFrameworkIosArm64", + ":lib:assembleMyfatframeDebugFatFramework", + ":lib:assembleLibDebugXCFramework", + ":lib:compileTestKotlinIosX64", + ":lib:linkDebugTestIosX64", + ":lib:transformCommonMainDependenciesMetadata", + ":lib:transformCommonMainCInteropDependenciesMetadata", + ":lib:linkDebugFrameworkIosArm64", + ":lib:linkDebugFrameworkIosX64", + ":lib:linkDebugFrameworkIosFat", + ":lib:linkReleaseFrameworkIosArm64", + ":lib:linkReleaseFrameworkIosX64", + ":lib:linkReleaseFrameworkIosFat", + ) + } + + project("native-configuration-cache", gradleVersion) { + testConfigurationCacheOf( + "build", + executedTaskNames = expectedTasks, + ) + } + } + + @OptIn(EnvironmentalVariablesOverride::class) + @DisplayName("works with apple framework embedding and signing") + @OsCondition(supportedOn = [OS.MAC], enabledOnCI = [OS.MAC]) + @GradleTestVersions( + minVersion = TestVersions.Gradle.G_7_4, + additionalVersions = [TestVersions.Gradle.G_7_6], + maxVersion = TestVersions.Gradle.G_8_1, + ) + @GradleTest + fun testAppleFrameworkTasks(gradleVersion: GradleVersion, @TempDir targetBuildDir: Path) { + project( + projectName = "sharedAppleFramework", + gradleVersion = gradleVersion, + environmentVariables = EnvironmentalVariables( + "CONFIGURATION" to "Debug", + "SDK_NAME" to "iphoneos", + "ARCHS" to "arm64", + "EXPANDED_CODE_SIGN_IDENTITY" to "-", + "TARGET_BUILD_DIR" to targetBuildDir.toString(), + "FRAMEWORKS_FOLDER_PATH" to "testFrameworksDir" + ), + ) { + testConfigurationCacheOf(":shared:embedAndSignAppleFrameworkForXcode") + } + } +} \ No newline at end of file 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 1b6716035e5..ae17e7939f3 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 @@ -12,56 +12,44 @@ import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.util.replaceText import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.condition.OS +import org.junit.jupiter.api.io.TempDir +import java.nio.file.Path import kotlin.io.path.absolutePathString import kotlin.io.path.appendText @OsCondition(supportedOn = [OS.MAC], enabledOnCI = [OS.MAC]) @DisplayName("Tests for K/N with Apple Framework") -@AndroidTestVersions(minVersion = TestVersions.AGP.AGP_70) +@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0) @NativeGradlePluginTests class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Assembling AppleFrameworkForXcode tasks for IosArm64") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0) + @GradleTest fun shouldAssembleAppleFrameworkForXcodeForIosArm64( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { nativeProject( "sharedAppleFramework", gradleVersion, - buildJdk = jdkProvider.location, - buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion + buildOptions = defaultBuildOptions, + environmentVariables = EnvironmentalVariables( + "CONFIGURATION" to "debug", + "SDK_NAME" to "iphoneos123", + "ARCHS" to "arm64", + "TARGET_BUILD_DIR" to "no use", + "FRAMEWORKS_FOLDER_PATH" to "no use" ), ) { - val environmentVariables = EnvironmentalVariables( - mapOf( - "CONFIGURATION" to "debug", - "SDK_NAME" to "iphoneos123", - "ARCHS" to "arm64", - "TARGET_BUILD_DIR" to "no use", - "FRAMEWORKS_FOLDER_PATH" to "no use" - ) - ) - build( - "assembleDebugAppleFrameworkForXcodeIosArm64", - environmentVariables = environmentVariables, - ) { + build("assembleDebugAppleFrameworkForXcodeIosArm64") { assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeIosArm64") assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework") assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework.dSYM") } - build( - "assembleCustomDebugAppleFrameworkForXcodeIosArm64", - environmentVariables = environmentVariables - ) { + build("assembleCustomDebugAppleFrameworkForXcodeIosArm64") { assertTasksExecuted(":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64") assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/lib.framework") assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/lib.framework.dSYM") @@ -71,21 +59,15 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Assembling fat AppleFrameworkForXcode tasks for Arm64 and X64 simulators") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0) + @GradleTest fun shouldAssembleAppleFrameworkForXcodeForArm64AndX64Simulators( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { nativeProject( "sharedAppleFramework", gradleVersion, - buildJdk = jdkProvider.location, - buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) + buildOptions = defaultBuildOptions ) { val environmentVariables = mapOf( "CONFIGURATION" to "Release", @@ -106,57 +88,42 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("MacOS framework has symlinks") @OptIn(EnvironmentalVariablesOverride::class) - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) - @GradleAndroidTest + @GradleTest fun shouldCheckThatMacOSFrameworkHasSymlinks( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, + @TempDir testBuildDir: Path, ) { nativeProject( "sharedAppleFramework", gradleVersion, - buildJdk = jdkProvider.location, - buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) ) { val environmentVariables = mapOf( "CONFIGURATION" to "debug", "SDK_NAME" to "macosx", "ARCHS" to "x86_64", "EXPANDED_CODE_SIGN_IDENTITY" to "-", - "TARGET_BUILD_DIR" to projectPath.absolutePathString(), + "TARGET_BUILD_DIR" to testBuildDir.toString(), "FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived" ) build(":shared:embedAndSignAppleFrameworkForXcode", environmentVariables = EnvironmentalVariables(environmentVariables)) { assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeMacosX64") assertSymlinkInProjectExists("shared/build/xcode-frameworks/debug/macosx/sdk.framework/Headers") - assertSymlinkInProjectExists("build/xcode-derived/sdk.framework/Headers") + assertSymlinkExists(testBuildDir.resolve("build/xcode-derived/sdk.framework/Headers")) } } } - @DisplayName("EmbedAnsSign executes normally when signing is disabled") + @DisplayName("embedAndSign executes normally when signing is disabled") @OptIn(EnvironmentalVariablesOverride::class) - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) - @GradleAndroidTest + @GradleTest fun testEmbedAnsSignExecutionWithoutSigning( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { nativeProject( "sharedAppleFramework", gradleVersion, - buildJdk = jdkProvider.location, - buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) ) { val environmentVariables = mapOf( "CONFIGURATION" to "debug", @@ -172,19 +139,12 @@ class AppleFrameworkIT : KGPBaseTest() { } @DisplayName("embedAndSignAppleFrameworkForXcode fail") - @GradleAndroidTest - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) + @GradleTest fun shouldFailWithExecutingEmbedAndSignAppleFrameworkForXcode( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { - nativeProject("sharedAppleFramework", gradleVersion, buildJdk = jdkProvider.location) { - buildAndFail( - ":shared:embedAndSignAppleFrameworkForXcode", - buildOptions = defaultBuildOptions.copy(androidVersion = agpVersion) - ) { + nativeProject("sharedAppleFramework", gradleVersion) { + buildAndFail(":shared:embedAndSignAppleFrameworkForXcode") { assertOutputContains("Please run the embedAndSignAppleFrameworkForXcode task from Xcode") } } @@ -192,28 +152,21 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Registered tasks with Xcode environment for Debug IosArm64 configuration") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0) + @GradleTest fun shouldCheckAllRegisteredTasksWithXcodeEnvironmentForDebugIosArm64( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, + @TempDir testBuildDir: Path, ) { - val buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) nativeProject( "sharedAppleFramework", gradleVersion, - buildJdk = jdkProvider.location, - buildOptions = buildOptions ) { val environmentVariables = mapOf( "CONFIGURATION" to "Debug", "SDK_NAME" to "iphoneos", "ARCHS" to "arm64", "EXPANDED_CODE_SIGN_IDENTITY" to "-", - "TARGET_BUILD_DIR" to "testBuildDir", + "TARGET_BUILD_DIR" to testBuildDir.toString(), "FRAMEWORKS_FOLDER_PATH" to "testFrameworksDir" ) buildAndAssertAllTasks( @@ -236,22 +189,13 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("embedAndSignAppleFrameworkForXcode was registered without required Xcode environments") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) + @GradleTest fun shouldCheckEmbedAndSignAppleFrameworkForXcodeDoesNotRequireXcodeEnv( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { - val buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) nativeProject( "sharedAppleFramework", gradleVersion, - buildJdk = jdkProvider.location, - buildOptions = buildOptions ) { val environmentVariables = EnvironmentalVariables( mapOf( @@ -289,21 +233,15 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Static framework for Arm64 is built but is not embedded") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0) + @GradleTest fun shouldCheckThatStaticFrameworkForArm64IsBuildAndNotEmbedded( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { nativeProject( "sharedAppleFramework", gradleVersion, - buildJdk = jdkProvider.location, - buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) + buildOptions = defaultBuildOptions ) { val environmentVariables = EnvironmentalVariables( mapOf( @@ -333,22 +271,13 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Configuration errors reported to Xcode when embedAndSign task requested") @OptIn(EnvironmentalVariablesOverride::class) - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) - @GradleAndroidTest + @GradleTest fun shouldReportConfErrorsToXcodeWhenRequestedByEmbedAndSign( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { - val buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) nativeProject( "sharedAppleFramework", gradleVersion, - buildJdk = jdkProvider.location, - buildOptions = buildOptions ) { val environmentVariables = EnvironmentalVariables( mapOf( @@ -378,22 +307,13 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Compilation errors reported to Xcode when embedAndSign task requested") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) + @GradleTest fun shouldReportCompilationErrorsToXcodeWhenRequestedByEmbedAndSign( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { - val buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) nativeProject( "sharedAppleFramework", gradleVersion, - buildOptions = buildOptions, - buildJdk = jdkProvider.location ) { val environmentVariables = EnvironmentalVariables( mapOf( @@ -417,22 +337,13 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Compilation errors printed with Gradle-style when any other task requested") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) + @GradleTest fun shouldPrintCompilationErrorsWithGradleStyle( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { - val buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) nativeProject( "sharedAppleFramework", gradleVersion, - buildOptions = buildOptions, - buildJdk = jdkProvider.location ) { val environmentVariables = EnvironmentalVariables( mapOf( @@ -456,22 +367,13 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Compilation errors printed with Xcode-style with explicit option") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) + @GradleTest fun shouldPrintCompilationErrorsWithXcodeStyle( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { - val buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) nativeProject( "sharedAppleFramework", gradleVersion, - buildOptions = buildOptions, - buildJdk = jdkProvider.location ) { val environmentVariables = EnvironmentalVariables( mapOf( @@ -499,22 +401,13 @@ class AppleFrameworkIT : KGPBaseTest() { @DisplayName("Compilation errors reported to Xcode when embedAndSign task requested and compiler runs in a separate process") @OptIn(EnvironmentalVariablesOverride::class) - @GradleAndroidTest - // We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957 - @GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2) + @GradleTest fun shouldReportErrorsToXcodeWhenEmbedAndSignRequestedAndDisableCompilerDaemon( gradleVersion: GradleVersion, - agpVersion: String, - jdkProvider: JdkVersions.ProvidedJdk, ) { - val buildOptions = defaultBuildOptions.copy( - androidVersion = agpVersion - ) nativeProject( "sharedAppleFramework", gradleVersion, - buildOptions = buildOptions, - buildJdk = jdkProvider.location ) { val environmentVariables = EnvironmentalVariables( mapOf( @@ -585,7 +478,7 @@ class AppleFrameworkIT : KGPBaseTest() { assertContainsVariant("mainDynamicReleaseFrameworkIos") } - // NB: '0' is required at the end since dependency is added with custom attribute and it creates new configuration + // NB: '0' is required at the end since dependency is added with custom attribute, and it creates new configuration build(*dependencyInsight("iosAppIosX64DebugImplementation0"), "-PmultipleFrameworks") { assertContainsVariant("mainStaticDebugFrameworkIos") } 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 ccfa0d4f958..2615a0df15f 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 @@ -14,6 +14,8 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Compan import org.jetbrains.kotlin.gradle.testbase.ImportMode import org.jetbrains.kotlin.gradle.testbase.cocoaPodsEnvironmentVariables import org.jetbrains.kotlin.gradle.testbase.ensureCocoapodsInstalled +import org.jetbrains.kotlin.gradle.testbase.TestVersions.Gradle.G_8_1 +import org.jetbrains.kotlin.gradle.transformProjectWithPluginsDsl import org.jetbrains.kotlin.gradle.util.modify import org.jetbrains.kotlin.gradle.util.runProcess import org.jetbrains.kotlin.konan.target.HostManager @@ -25,10 +27,7 @@ import java.io.File import java.util.* import java.util.concurrent.TimeUnit import java.util.zip.ZipFile -import kotlin.test.assertContains -import kotlin.test.assertEquals -import kotlin.test.assertTrue -import kotlin.test.fail +import kotlin.test.* class CocoaPodsIT : BaseGradleIT() { @@ -809,6 +808,72 @@ class CocoaPodsIT : BaseGradleIT() { } } + @Test + @Ignore // will be fixed in the next step + fun `test configuration cache works in a complex scenario with Gradle 8_1`() { + project = transformProjectWithPluginsDsl(templateProjectName, GradleVersionRequired.Exact(G_8_1)).apply { + preparePodfile("ios-app", ImportMode.FRAMEWORKS) + } + `test configuration cache works in a complex scenario`() + } + + @Test + fun `test configuration cache works in a complex scenario`() { + project.gradleBuildScript().appendToCocoapodsBlock("""pod("Base64", version = "1.1.2")""") + + val tasks = arrayOf( + ":podspec", + ":podImport", + ":podPublishDebugXCFramework", + ":podPublishReleaseXCFramework", + ":syncFramework", + ) + + val executableTasks = listOf( + ":podspec", + ":podPublishDebugXCFramework", + ":podPublishReleaseXCFramework", + ":linkPodDebugFrameworkIOS", + ) + + fun build(vararg tasks: String, check: CompiledProject.() -> Unit) { + project.build( + *tasks, + "-Pkotlin.native.cocoapods.generate.wrapper=true", + "-Pkotlin.native.cocoapods.platform=iphonesimulator", + "-Pkotlin.native.cocoapods.archs=x86_64", + "-Pkotlin.native.cocoapods.configuration=Debug", + options = defaultBuildOptions().copy(configurationCache = true), + check = check, + ) + } + + build(*tasks) { + assertSuccessful() + assertTasksExecuted(executableTasks) + + assertContains("Calculating task graph as no configuration cache is available for tasks") + + assertContains("Configuration cache entry stored.") + } + + build("clean") { + assertSuccessful() + } + + build(*tasks) { + assertSuccessful() + + assertContains("Reusing configuration cache.") + } + + build(*tasks) { + assertSuccessful() + + assertTasksUpToDate(executableTasks) + } + } + // test configuration phase private class CustomHooks { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/configurationCacheHelpers.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/configurationCacheHelpers.kt index 4c4c709cf38..d87071fa91e 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/configurationCacheHelpers.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/configurationCacheHelpers.kt @@ -7,12 +7,6 @@ package org.jetbrains.kotlin.gradle.testbase import org.gradle.testkit.runner.BuildResult import org.jetbrains.kotlin.gradle.BaseGradleIT -import java.net.URI -import java.nio.file.Path -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.exists -import kotlin.io.path.name -import kotlin.test.fail /** * Tests whether configuration cache for the tasks specified by [buildArguments] works on simple scenario when project is built twice non-incrementally. diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt index 9e42e2abcef..a69cfd283b6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt @@ -312,7 +312,9 @@ open class GradleProject( } @JvmInline -value class EnvironmentalVariables @EnvironmentalVariablesOverride constructor(val environmentalVariables: Map = emptyMap()) +value class EnvironmentalVariables @EnvironmentalVariablesOverride constructor(val environmentalVariables: Map = emptyMap()) { + @EnvironmentalVariablesOverride constructor(vararg environmentVariables: Pair) : this(mapOf(*environmentVariables)) +} @RequiresOptIn("Environmental variables override may lead to interference of parallel builds and breaks Gradle tests debugging") annotation class EnvironmentalVariablesOverride diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testHelpers.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testHelpers.kt index 441dca0f407..6fa211b1d8f 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testHelpers.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testHelpers.kt @@ -38,9 +38,10 @@ fun TestProject.makeSnapshotTo(destinationPath: String) { dest.resolve("run.sh").run { writeText( """ - #!/usr/bin/env sh - ./gradlew ${buildOptions.toArguments(gradleVersion).joinToString(separator = " ")} ${'$'}@ - """.trimIndent() + |#!/usr/bin/env sh + |${formatEnvironmentForScript(envCommand = "export")} + |./gradlew ${buildOptions.toArguments(gradleVersion).joinToString(separator = " ")} ${'$'}@ + |""".trimMargin() ) setPosixFilePermissions( @@ -55,9 +56,10 @@ fun TestProject.makeSnapshotTo(destinationPath: String) { dest.resolve("run.bat").run { writeText( """ - @rem Executing Gradle build - gradlew.bat ${buildOptions.toArguments(gradleVersion).joinToString(separator = " ")} %* - """.trimIndent() + |@rem Executing Gradle build + |${formatEnvironmentForScript(envCommand = "set")} + |gradlew.bat ${buildOptions.toArguments(gradleVersion).joinToString(separator = " ")} %* + |""".trimMargin() ) } @@ -80,6 +82,12 @@ fun TestProject.makeSnapshotTo(destinationPath: String) { } } +private fun TestProject.formatEnvironmentForScript(envCommand: String): String { + return environmentVariables.environmentalVariables.asSequence().joinToString(separator = "\n|") { (key, value) -> + "$envCommand $key=\"$value\"" + } +} + /** * * Configures the JVM memory settings for the Gradle project. diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/build.gradle.kts index a26e49466fa..7d5a02c0cb0 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/build.gradle.kts @@ -7,7 +7,6 @@ buildscript { } dependencies { classpath(kotlin("gradle-plugin:${property("kotlin_version")}")) - classpath("com.android.tools.build:gradle:${property("android_tools_version")}") } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/build.gradle.kts index 7a4badc2bd3..d576708cafb 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/build.gradle.kts @@ -1,10 +1,9 @@ plugins { kotlin("multiplatform") - id("com.android.library") } kotlin { - android() + jvm() val macosX64 = macosX64() val iosX64 = iosX64() @@ -35,12 +34,3 @@ kotlin { } } } - -android { - compileSdkVersion(30) - sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") - defaultConfig { - minSdkVersion(21) - targetSdkVersion(30) - } -} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/androidMain/AndroidManifest.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/androidMain/AndroidManifest.xml deleted file mode 100644 index f7655ddbb99..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/androidMain/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/androidMain/kotlin/com/github/jetbrains/myapplication/Platform.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/jvmMain/kotlin/com/github/jetbrains/myapplication/Platform.kt similarity index 52% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/androidMain/kotlin/com/github/jetbrains/myapplication/Platform.kt rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/jvmMain/kotlin/com/github/jetbrains/myapplication/Platform.kt index 572b187a694..afb822745d6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/androidMain/kotlin/com/github/jetbrains/myapplication/Platform.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/sharedAppleFramework/shared/src/jvmMain/kotlin/com/github/jetbrains/myapplication/Platform.kt @@ -1,5 +1,5 @@ package com.github.jetbrains.myapplication actual class Platform actual constructor() { - actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}" + actual val platform: String = "JVM ${System.getProperty("java.version")}" } \ No newline at end of file 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 5ec4e9a342b..a7976f5e0b0 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 @@ -6,19 +6,13 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.apple import org.gradle.api.DefaultTask -import org.gradle.api.file.FileCollection import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.file.DirectoryProperty import org.gradle.api.plugins.BasePlugin -import org.gradle.api.tasks.Copy -import org.gradle.api.tasks.IgnoreEmptyDirectories -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity -import org.gradle.api.tasks.SkipWhenEmpty -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.TaskProvider +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* +import org.gradle.process.ExecOperations import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer import org.jetbrains.kotlin.gradle.plugin.mpp.Framework import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType @@ -27,9 +21,11 @@ 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.lowerCamelCaseName +import org.jetbrains.kotlin.gradle.utils.mapToFile import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly import java.io.File +import javax.inject.Inject internal object AppleXcodeTasks { const val embedAndSignTaskPrefix = "embedAndSign" @@ -70,7 +66,7 @@ private object XcodeEnvironment { get() { val xcodeTargetBuildDir = System.getenv("TARGET_BUILD_DIR") ?: return null val xcodeFrameworksFolderPath = System.getenv("FRAMEWORKS_FOLDER_PATH") ?: return null - return File(xcodeTargetBuildDir, xcodeFrameworksFolderPath) + return File(xcodeTargetBuildDir, xcodeFrameworksFolderPath).absoluteFile } val sign: String? get() = System.getenv("EXPANDED_CODE_SIGN_IDENTITY") @@ -135,9 +131,9 @@ private fun Project.registerAssembleAppleFrameworkTask(framework: Framework): Ta else -> registerTask(frameworkTaskName) { task -> task.description = "Packs $frameworkBuildType ${frameworkTarget.name} framework for Xcode" task.isEnabled = frameworkBuildType == envBuildType - task.dependsOn(framework.linkTaskName) - task.files = files({ framework.outputDirectory.listFiles() }) - task.destDir = appleFrameworkDir(envFrameworkSearchDir) + task.sourceFramework.fileProvider(framework.linkTaskProvider.flatMap { it.outputFile }) + task.dependsOn(framework.linkTaskProvider) + task.destinationDirectory.set(appleFrameworkDir(envFrameworkSearchDir)) } } } @@ -193,15 +189,16 @@ internal fun Project.registerEmbedAndSignAppleFrameworkTask(framework: Framework if (framework.buildType != envBuildType || !envTargets.contains(framework.konanTarget)) return embedAndSignTask.configure { task -> + val frameworkFile = framework.outputFile task.dependsOn(assembleTask) - task.files = files(File(appleFrameworkDir(envFrameworkSearchDir), framework.outputFile.name)) - task.destDir = envEmbeddedFrameworksDir + task.sourceFramework.set(File(appleFrameworkDir(envFrameworkSearchDir), frameworkFile.name)) + task.destinationDirectory.set(envEmbeddedFrameworksDir) if (envSign != null) { task.doLast { val binary = envEmbeddedFrameworksDir - .resolve(framework.outputFile.name) - .resolve(framework.outputFile.nameWithoutExtension) - exec { + .resolve(frameworkFile.name) + .resolve(frameworkFile.nameWithoutExtension) + task.execOperations.exec { it.commandLine("codesign", "--force", "--sign", envSign, "--", binary) } } @@ -224,26 +221,39 @@ private fun Project.appleFrameworkDir(frameworkSearchDir: File) = * To preserve these symlinks we are using the `cp` command instead. * See https://youtrack.jetbrains.com/issue/KT-48594. */ +@Suppress("LeakingThis") // Should be extended only by Gradle internal abstract class FrameworkCopy : DefaultTask() { + @get:Inject + abstract val execOperations: ExecOperations + + @get:InputDirectory + abstract val sourceFramework: DirectoryProperty + @get:InputFiles - @get:SkipWhenEmpty @get:IgnoreEmptyDirectories - @get:PathSensitive(PathSensitivity.ABSOLUTE) - abstract var files: FileCollection + protected val sourceDsym = sourceFramework.mapToFile().map { File(it.path + ".dSYM") } @get:OutputDirectory - abstract var destDir: File + abstract val destinationDirectory: DirectoryProperty @TaskAction - fun copy() { - destDir.mkdirs() - files.forEach { file -> - File(destDir, file.name).let destFile@{ destFile -> - if (!destFile.exists()) return@destFile - project.exec { it.commandLine("rm", "-r", destFile.absolutePath) } - } - project.exec { it.commandLine("cp", "-R", file.absolutePath, destDir.absolutePath) } + open fun copy() { + copy(sourceFramework.mapToFile()) + if (sourceDsym.get().exists()) { + copy(sourceDsym) } } + + private fun copy(sourceProvider: Provider) { + val source = sourceProvider.get() + val destination = destinationDirectory.asFile.get() + + val destinationFile = File(destination, source.name) + if (destinationFile.exists()) { + execOperations.exec { it.commandLine("rm", "-r", destinationFile.absolutePath) } + } + + execOperations.exec { it.commandLine("cp", "-R", source.absolutePath, destination.absolutePath) } + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt index b9dbb75c02d..00077d81f2e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt @@ -269,7 +269,6 @@ class Framework( compilation: KotlinNativeCompilation ) : AbstractNativeLibrary(name, baseName, buildType, compilation), HasAttributes { - @Transient // Is required configuration cache support for KotlinNative tasks that capture whole binary object as task state private val attributeContainer = HierarchyAttributeContainer(parent = compilation.attributes) override fun getAttributes() = attributeContainer diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt index 242f26e5b79..f800e8671f2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/CocoapodsExtension.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.Cocoapods import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.POD_FRAMEWORK_PREFIX import org.jetbrains.kotlin.gradle.plugin.mpp.Framework import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import org.jetbrains.kotlin.gradle.utils.getFile import java.io.File import java.net.URI import javax.inject.Inject @@ -135,7 +136,7 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje /** * Configure output directory for pod publishing */ - var publishDir: File = CocoapodsBuildDirs(project).publish + var publishDir: File = CocoapodsBuildDirs(project.layout).publish.getFile() internal val specRepos = SpecRepos() @@ -179,8 +180,8 @@ abstract class CocoapodsExtension @Inject constructor(private val project: Proje val newContent = lineContent.replace(path.name, "") """ |Deprecated DSL found on ${buildScript.absolutePath}${File.pathSeparator}${lineNumber + 1}: - |Found: "${lineContent}" - |Expected: "${newContent}" + |Found: "$lineContent" + |Expected: "$newContent" |Please, change the path to avoid this warning. | """.trimMargin() 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 80f76d5d342..b6e06c46379 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 @@ -10,9 +10,14 @@ import org.gradle.api.DefaultTask import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFile import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.wrapper.Wrapper import org.jetbrains.kotlin.daemon.common.trimQuotes import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation @@ -21,10 +26,9 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.Cocoapods import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.* import org.jetbrains.kotlin.gradle.plugin.mpp.apple.AppleSdk import org.jetbrains.kotlin.gradle.plugin.mpp.apple.AppleTarget -import org.jetbrains.kotlin.gradle.plugin.mpp.apple.FrameworkCopy -import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFrameworkTask import org.jetbrains.kotlin.gradle.plugin.whenEvaluated import org.jetbrains.kotlin.gradle.targets.native.cocoapods.KotlinArtifactsPodspecExtension import org.jetbrains.kotlin.gradle.targets.native.cocoapods.kotlinArtifactsPodspecExtension @@ -33,7 +37,6 @@ import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.kotlinArtifacts import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.gradle.utils.asValidTaskName -import org.jetbrains.kotlin.gradle.utils.filesProvider import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.newInstance import org.jetbrains.kotlin.konan.target.Family @@ -44,34 +47,37 @@ import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import org.jetbrains.kotlin.utils.addToStdlib.cast import java.io.File -internal val Project.cocoapodsBuildDirs: CocoapodsBuildDirs + + +internal val ProjectLayout.cocoapodsBuildDirs: CocoapodsBuildDirs get() = CocoapodsBuildDirs(this) -internal class CocoapodsBuildDirs(val project: Project) { - val root: File - get() = project.buildDir.resolve("cocoapods") +internal class CocoapodsBuildDirs(private val layout: ProjectLayout) { - val framework: File - get() = root.resolve("framework") + val root: Provider + get() = layout.buildDirectory.dir("cocoapods") + val framework: Provider + get() = dir("framework") + val dummyFramework: Provider + get() = dir("dummy.framework") + val defs: Provider + get() = dir("defs") + val publish: Provider + get() = dir("publish") - val dummyFramework: File - get() = root.resolve("dummy.framework") + fun synthetic(family: Provider): Provider { + return dir("synthetic").map { it.dir(family.get().platformLiteral) } + } - val defs: File - get() = root.resolve("defs") + fun fatFramework(buildType: NativeBuildType): Provider { + return root.map { it.dir("fat-frameworks/${buildType.getName()}") } + } - val buildSettings: File - get() = root.resolve("buildSettings") + fun buildSettings(pod: Provider, sdk: Provider): Provider { + return dir("buildSettings").map { it.file("build-settings-${sdk.get()}-${pod.get().schemeName}.properties") } + } - val synthetic: File - get() = root.resolve("synthetic") - - fun synthetic(family: Family) = synthetic.resolve(family.platformLiteral) - - val publish: File = root.resolve("publish") - - fun fatFramework(buildType: NativeBuildType) = - root.resolve("fat-frameworks/${buildType.getName()}") + private fun dir(pathFromRoot: String): Provider = root.map { it.dir(pathFromRoot) } } internal fun String.asValidFrameworkName() = replace('-', '_') @@ -115,24 +121,13 @@ private val KotlinNativeTarget.toValidSDK: String else -> throw IllegalArgumentException("Bad target ${konanTarget.name}.") } -internal fun Project.getPodBuildTaskProvider( +private fun Project.getPodBuildTaskProvider( target: KotlinNativeTarget, pod: CocoapodsDependency ): TaskProvider { return tasks.named(target.toValidSDK.toBuildDependenciesTaskName(pod), PodBuildTask::class.java) } -internal fun Project.getPodBuildSettingsProperties( - target: KotlinNativeTarget, - pod: CocoapodsDependency -): PodBuildSettingsProperties { - return getPodBuildTaskProvider(target, pod).get().buildSettingsFile.get() - .reader() - .use { - PodBuildSettingsProperties.readSettingsFromReader(it) - } -} - internal val PodBuildSettingsProperties.frameworkSearchPaths: List get() { val frameworkPathsSelfIncluding = mutableListOf() @@ -154,6 +149,7 @@ private val PodBuildSettingsProperties.frameworkHeadersSearchPaths: List * Ignores whitespaces in quotes and drops quotes, e.g. a string * `foo "bar baz" qux="quux"` will be split into ["foo", "bar baz", "qux=quux"]. */ +@Suppress("RegExpUnnecessaryNonCapturingGroup") internal fun String.splitQuotedArgs(): List = Regex("""(?:[^\s"]|(?:"[^"]*"))+""").findAll(this).map { it.value.replace("\"", "") @@ -178,15 +174,15 @@ open class KotlinCocoapodsPlugin : Plugin { } private fun Project.createCopyFrameworkTask( - originalDirectory: Provider, + frameworkFile: Provider, buildingTask: TaskProvider<*> ) = registerTask(SYNC_TASK_NAME) { it.group = TASK_GROUP it.description = "Copies a framework for given platform and build type into the CocoaPods build directory" + it.sourceFramework.fileProvider(frameworkFile) it.dependsOn(buildingTask) - it.files = filesProvider { originalDirectory.map { dir -> dir.listFiles().orEmpty() } } - it.destDir = cocoapodsBuildDirs.framework + it.destinationDirectory.set(layout.cocoapodsBuildDirs.framework) } private fun createSyncForFatFramework( @@ -210,7 +206,7 @@ open class KotlinCocoapodsPlugin : Plugin { val fatFrameworkTask = project.registerTask("fatFramework") { task -> task.group = TASK_GROUP task.description = "Creates a fat framework for requested architectures" - task.destinationDir = project.cocoapodsBuildDirs.fatFramework(requestedBuildType) + task.destinationDir = project.layout.cocoapodsBuildDirs.fatFramework(requestedBuildType).getFile() fatTargets.forEach { (_, targets) -> targets.singleOrNull()?.let { @@ -221,7 +217,7 @@ open class KotlinCocoapodsPlugin : Plugin { } } - project.createCopyFrameworkTask(fatFrameworkTask.map { it.destinationDir }, fatFrameworkTask) + project.createCopyFrameworkTask(fatFrameworkTask.map { it.fatFramework }, fatFrameworkTask) } private fun createSyncForRegularFramework( @@ -236,7 +232,7 @@ open class KotlinCocoapodsPlugin : Plugin { check(targets.size == 1) { "The project has more than one target for the requested platform: `${requestedPlatform.visibleName}`" } val frameworkLinkTask = targets.single().binaries.getFramework(POD_FRAMEWORK_PREFIX, requestedBuildType).linkTaskProvider - project.createCopyFrameworkTask(frameworkLinkTask.flatMap { it.destinationDirectory.map { dir -> dir.asFile } }, frameworkLinkTask) + project.createCopyFrameworkTask(frameworkLinkTask.flatMap { it.outputFile }, frameworkLinkTask) } private fun createSyncTask( @@ -332,6 +328,8 @@ open class KotlinCocoapodsPlugin : Plugin { val interopTask = project.tasks.named(interop.interopProcessingTaskName).get() + interopTask.onlyIf { HostManager.hostIsMac } + interopTask.dependsOn(defTask) pod.interopBindingDependencies.forEach { dependencyName -> @@ -339,21 +337,20 @@ open class KotlinCocoapodsPlugin : Plugin { } with(interop) { - defFileProperty.set(defTask.map { it.outputFile }) + defFileProperty.set(defTask.flatMap { it.defFile.mapToFile() }) _packageNameProp.set(project.provider { pod.packageName }) _extraOptsProp.addAll(project.provider { pod.extraOpts }) } - if (HostManager.hostIsMac) { - val podBuildTaskProvider = project.getPodBuildTaskProvider(target, pod) - interopTask.inputs.file(podBuildTaskProvider.map { it.buildSettingsFile }) - interopTask.dependsOn(podBuildTaskProvider) - } + val podBuildTaskProvider = project.getPodBuildTaskProvider(target, pod) + val buildSettingsFileProvider = project.buildSettingsFileProvider(pod, target) + interopTask.inputs.file(buildSettingsFileProvider) + interopTask.dependsOn(podBuildTaskProvider) + interopTask.doFirst { _ -> // Since we cannot expand the configuration phase of interop tasks // receiving the required environment variables happens on execution phase. - // TODO This needs to be fixed to improve UP-TO-DATE checks. - val podBuildSettings = project.getPodBuildSettingsProperties(target, pod) + val podBuildSettings = PodBuildSettingsProperties.readSettingsFromFile(buildSettingsFileProvider.getFile()) podBuildSettings.cflags?.let { args -> // Xcode quotes around paths with spaces. @@ -363,7 +360,6 @@ open class KotlinCocoapodsPlugin : Plugin { interop.compilerOpts.addAll(podBuildSettings.frameworkHeadersSearchPaths.map { "-I$it" }) interop.compilerOpts.addAll(podBuildSettings.frameworkSearchPaths.map { "-F$it" }) - } } } @@ -409,28 +405,19 @@ open class KotlinCocoapodsPlugin : Plugin { project: Project, cocoapodsExtension: CocoapodsExtension ) { - project.tasks.register(POD_SPEC_TASK_NAME, PodspecTask::class.java) { - it.group = TASK_GROUP - it.description = "Generates a podspec file for CocoaPods import" - it.needPodspec = project.provider { cocoapodsExtension.needPodspec } - it.publishing.set(false) - it.pods.set(cocoapodsExtension.pods) - it.version.set(cocoapodsExtension.version ?: project.version.toString()) - it.specName.set(cocoapodsExtension.name) - it.extraSpecAttributes.set(cocoapodsExtension.extraSpecAttributes) - it.outputDir.set(project.projectDir) - it.homepage.set(cocoapodsExtension.homepage) - it.license.set(cocoapodsExtension.license) - it.authors.set(cocoapodsExtension.authors) - it.summary.set(cocoapodsExtension.summary) - it.frameworkName = cocoapodsExtension.podFrameworkName - it.ios = project.provider { cocoapodsExtension.ios } - it.osx = project.provider { cocoapodsExtension.osx } - it.tvos = project.provider { cocoapodsExtension.tvos } - it.watchos = project.provider { cocoapodsExtension.watchos } + project.registerTask(POD_SPEC_TASK_NAME) { task -> + task.group = TASK_GROUP + task.description = "Generates a podspec file for CocoaPods import" + task.outputDir.set(project.projectDir) + task.needPodspec.set(project.provider { cocoapodsExtension.needPodspec }) + task.publishing.set(false) + + task.configure(cocoapodsExtension, project) + + task.gradleWrapperPath.set(project.gradleWrapperPath()) val generateWrapper = project.findProperty(GENERATE_WRAPPER_PROPERTY)?.toString()?.toBoolean() ?: false if (generateWrapper) { - it.dependsOn(":wrapper") + task.dependsOn(":wrapper") } } } @@ -578,10 +565,10 @@ open class KotlinCocoapodsPlugin : Plugin { project.registerTask(sdk.toSetupBuildTaskName(pod)) { task -> task.group = TASK_GROUP task.description = "Collect environment variables from .xcworkspace file" - task.pod = project.provider { pod } - task.sdk = project.provider { sdk } - task.podsXcodeProjDir = podInstallTask.map { it.podsXcodeProjDirProvider.get() } - task.frameworkName = cocoapodsExtension.podFrameworkName + task.pod.set(pod) + task.sdk.set(sdk) + task.podsXcodeProjDir.set(podInstallTask.map { it.podsXcodeProjDirProvider.get() }) + task.frameworkName.set(cocoapodsExtension.podFrameworkName) task.dependsOn(podInstallTask) } } @@ -616,13 +603,15 @@ open class KotlinCocoapodsPlugin : Plugin { val podSetupBuildTaskProvider = project.tasks.named(sdk.toSetupBuildTaskName(pod), PodSetupBuildTask::class.java) - project.tasks.register(sdk.toBuildDependenciesTaskName(pod), PodBuildTask::class.java) { - it.group = TASK_GROUP - it.description = "Calls `xcodebuild` on xcworkspace for the pod scheme" - it.sdk = project.provider { sdk } - it.pod = project.provider { pod } - it.podsXcodeProjDir = podSetupBuildTaskProvider.map { task -> task.podsXcodeProjDir.get() } - it.buildSettingsFile = podSetupBuildTaskProvider.map { task -> task.buildSettingsFile.get() } + project.tasks.register(sdk.toBuildDependenciesTaskName(pod), PodBuildTask::class.java) { task -> + task.group = TASK_GROUP + task.description = "Calls `xcodebuild` on xcworkspace for the pod scheme" + task.buildSettingsFile.set(podSetupBuildTaskProvider.flatMap { it.buildSettingsFile }) + task.pod.set(pod) + task.sdk.set(sdk) + task.family.set(target.konanTarget.family) + task.podsXcodeProjDir.fileProvider(podSetupBuildTaskProvider.flatMap { it.podsXcodeProjDir }) + task.dependsOn(podSetupBuildTaskProvider) } } } @@ -667,28 +656,32 @@ open class KotlinCocoapodsPlugin : Plugin { private fun configureLinkingOptions(project: Project, cocoapodsExtension: CocoapodsExtension, nativeBinary: NativeBinary) { cocoapodsExtension.pods.all { pod -> nativeBinary.linkTaskProvider.configure { task -> + task.onlyIf { HostManager.hostIsMac } + val binary = task.binary - if (HostManager.hostIsMac) { - val podBuildTaskProvider = project.getPodBuildTaskProvider(binary.target, pod) - task.inputs.file(podBuildTaskProvider.map { it.buildSettingsFile }) - task.dependsOn(podBuildTaskProvider) - } - task.doFirst { _ -> - val isExecutable = binary is AbstractExecutable - val isDynamicFramework = (binary is Framework && !binary.isStatic) + val podBuildTaskProvider = project.getPodBuildTaskProvider(binary.target, pod) + val buildSettingsFileProvider = project.buildSettingsFileProvider(pod, binary.target) + task.inputs.file(buildSettingsFileProvider) + task.dependsOn(podBuildTaskProvider) - val podBuildSettings = project.getPodBuildSettingsProperties(binary.target, pod) + val isExecutable = binary is AbstractExecutable + val isDynamicFramework = project.provider { binary is Framework && !binary.isStatic } + + task.doFirst { + val podBuildSettings = PodBuildSettingsProperties.readSettingsFromFile(buildSettingsFileProvider.getFile()) val frameworkFileName = pod.moduleName + ".framework" val frameworkSearchPaths = podBuildSettings.frameworkSearchPaths - if (isExecutable || isDynamicFramework) { + val linkerOpts = task.additionalLinkerOpts + + if (isExecutable || isDynamicFramework.get()) { val frameworkFileExists = frameworkSearchPaths.any { dir -> File(dir, frameworkFileName).exists() } - if (frameworkFileExists) binary.linkerOpts.addArg("-framework", pod.moduleName) - binary.linkerOpts.addAll(frameworkSearchPaths.map { "-F$it" }) + if (frameworkFileExists) linkerOpts.addArg("-framework", pod.moduleName) + linkerOpts.addAll(frameworkSearchPaths.map { "-F$it" }) } - if (isExecutable) binary.linkerOpts.addArgs("-rpath", frameworkSearchPaths) + if (isExecutable) linkerOpts.addArgs("-rpath", frameworkSearchPaths) } } } @@ -720,36 +713,39 @@ open class KotlinCocoapodsPlugin : Plugin { cocoapodsExtension: CocoapodsExtension, xcFrameworkTask: TaskProvider, buildType: NativeBuildType - ): TaskProvider = - with(project) { - val task = - tasks.register(lowerCamelCaseName(POD_FRAMEWORK_PREFIX, "spec", buildType.getName()), PodspecTask::class.java) { task -> - task.description = "Generates podspec for ${buildType.getName().capitalizeAsciiOnly()} XCFramework publishing" - task.outputDir.set(xcFrameworkTask.map { it.outputDir.resolve(it.buildType.getName()) }) - task.needPodspec = provider { true } - task.publishing.set(true) - task.pods.set(cocoapodsExtension.pods) - task.specName.set(cocoapodsExtension.name) - task.version.set(cocoapodsExtension.version ?: version.toString()) - task.extraSpecAttributes.set(cocoapodsExtension.extraSpecAttributes) - task.homepage.set(cocoapodsExtension.homepage) - task.license.set(cocoapodsExtension.license) - task.authors.set(cocoapodsExtension.authors) - task.summary.set(cocoapodsExtension.summary) - task.source.set(cocoapodsExtension.source) - task.frameworkName = cocoapodsExtension.podFrameworkName - task.ios = provider { cocoapodsExtension.ios } - task.osx = provider { cocoapodsExtension.osx } - task.tvos = provider { cocoapodsExtension.tvos } - task.watchos = provider { cocoapodsExtension.watchos } - val generateWrapper = project.findProperty(GENERATE_WRAPPER_PROPERTY)?.toString()?.toBoolean() ?: false - if (generateWrapper) { - task.dependsOn(":wrapper") - } - } - xcFrameworkTask.dependsOn(task) - return task + ): TaskProvider { + val task = project.registerTask(lowerCamelCaseName(POD_FRAMEWORK_PREFIX, "spec", buildType.getName())) { task -> + task.description = "Generates podspec for ${buildType.getName().capitalizeAsciiOnly()} XCFramework publishing" + task.outputDir.set(xcFrameworkTask.map { it.outputDir.resolve(it.buildType.getName()) }) + task.needPodspec.set(true) + task.publishing.set(true) + task.source.set(project.provider { cocoapodsExtension.source }) + + task.configure(cocoapodsExtension, project) } + xcFrameworkTask.dependsOn(task) + return task + } + + private fun PodspecTask.configure(cocoapodsExtension: CocoapodsExtension, project: Project) { + fun Property.setProvider(provider: () -> T?) = set(project.provider(provider)) + + pods.set(cocoapodsExtension.pods) + specName.setProvider { cocoapodsExtension.name } + version.setProvider { cocoapodsExtension.version ?: project.version.toString() } + extraSpecAttributes.set(project.provider { cocoapodsExtension.extraSpecAttributes }) + homepage.setProvider { cocoapodsExtension.homepage } + license.setProvider { cocoapodsExtension.license } + authors.setProvider { cocoapodsExtension.authors } + summary.setProvider { cocoapodsExtension.summary } + frameworkName.set(cocoapodsExtension.podFrameworkName) + ios.set(cocoapodsExtension.ios) + osx.set(cocoapodsExtension.osx) + tvos.set(cocoapodsExtension.tvos) + watchos.set(cocoapodsExtension.watchos) + projectPath.set(project.taskProjectPath()) + hasPodfile.set(project.hasPodfileOwnOrParent()) + } private fun registerPodPublishFatFrameworkTasks( project: Project, @@ -811,6 +807,29 @@ open class KotlinCocoapodsPlugin : Plugin { } } + private fun Project.gradleWrapperPath(): Provider { + return provider { rootProject.tasks.locateTask("wrapper")?.get()?.scriptFile?.absolutePath } + } + + private fun Project.taskProjectPath(): String { + return if (project.depth != 0) project.path else "" + } + + private fun Project.buildSettingsFileProvider(pod: CocoapodsDependency, target: KotlinNativeTarget): Provider { + return layout.cocoapodsBuildDirs.buildSettings(provider { pod }, provider { target.toValidSDK }) + } + + private val KotlinMultiplatformExtension?.cocoapodsExtensionOrNull: CocoapodsExtension? + get() = (this as? ExtensionAware)?.extensions?.findByName(COCOAPODS_EXTENSION_NAME) as? CocoapodsExtension + + private fun Project.hasPodfileOwnOrParent(): Provider { + return provider { + multiplatformExtensionOrNull?.cocoapodsExtensionOrNull?.podfile != null + || (parent?.hasPodfileOwnOrParent()?.get() ?: false) + } + } + + override fun apply(project: Project): Unit = with(project) { pluginManager.withPlugin("kotlin-multiplatform") { @@ -846,6 +865,7 @@ open class KotlinCocoapodsPlugin : Plugin { } } + companion object { const val COCOAPODS_EXTENSION_NAME = "cocoapods" const val TASK_GROUP = "CocoaPods" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/PodBuildSettingsProperties.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/PodBuildSettingsProperties.kt index bfad1221659..547c440bb09 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/PodBuildSettingsProperties.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/PodBuildSettingsProperties.kt @@ -53,6 +53,10 @@ data class PodBuildSettingsProperties( const val PUBLIC_HEADERS_FOLDER_PATH = "PUBLIC_HEADERS_FOLDER_PATH" const val FRAMEWORK_SEARCH_PATHS = "FRAMEWORK_SEARCH_PATHS" + internal fun readSettingsFromFile(file: File): PodBuildSettingsProperties { + return file.reader().use { readSettingsFromReader(it) } + } + fun readSettingsFromReader(reader: Reader): PodBuildSettingsProperties { with(Properties()) { load(reader) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DefFileTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DefFileTask.kt index dea206fdad9..c90e935a329 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DefFileTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DefFileTask.kt @@ -7,20 +7,26 @@ package org.jetbrains.kotlin.gradle.tasks import org.gradle.api.DefaultTask +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Nested import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs import org.jetbrains.kotlin.gradle.utils.appendLine +import org.jetbrains.kotlin.gradle.utils.getFile import java.io.File +import javax.inject.Inject /** * Generates a def-file for the given CocoaPods dependency. */ -abstract class DefFileTask : DefaultTask() { +abstract class DefFileTask @Inject constructor(projectLayout: ProjectLayout) : DefaultTask() { @get:Nested abstract val pod: Property @@ -29,13 +35,18 @@ abstract class DefFileTask : DefaultTask() { abstract val useLibraries: Property @get:OutputFile + val defFile: Provider = projectLayout.cocoapodsBuildDirs.defs.map { it.file("${pod.get().moduleName}.def") } + + @get:Internal + @Deprecated("Use `defFile` instead", replaceWith = ReplaceWith("defFile.get().asFile")) val outputFile: File - get() = project.cocoapodsBuildDirs.defs.resolve("${pod.get().moduleName}.def") + get() = defFile.getFile() @TaskAction fun generate() { - outputFile.parentFile.mkdirs() - outputFile.writeText(buildString { + val output = defFile.getFile() + output.parentFile.mkdirs() + output.writeText(buildString { appendLine("language = Objective-C") with(pod.get()) { when { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DummyFrameworkTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DummyFrameworkTask.kt index 95672b4ba84..07566484af5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DummyFrameworkTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/DummyFrameworkTask.kt @@ -7,13 +7,16 @@ package org.jetbrains.kotlin.gradle.tasks import org.gradle.api.DefaultTask +import org.gradle.api.file.ProjectLayout import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs +import org.jetbrains.kotlin.gradle.utils.mapToFile import java.io.File +import javax.inject.Inject /** * Creates a dummy framework in the target directory. @@ -26,7 +29,7 @@ import java.io.File * So we create a dummy static framework to allow CocoaPods install our pod correctly * and then replace it with the real one during a real build process. */ -abstract class DummyFrameworkTask : DefaultTask() { +abstract class DummyFrameworkTask @Inject constructor(projectLayout: ProjectLayout) : DefaultTask() { @get:Input abstract val frameworkName: Property @@ -35,7 +38,7 @@ abstract class DummyFrameworkTask : DefaultTask() { abstract val useStaticFramework: Property @get:OutputDirectory - val outputFramework: Provider = project.provider { project.cocoapodsBuildDirs.dummyFramework } + val outputFramework: Provider = projectLayout.cocoapodsBuildDirs.dummyFramework.mapToFile() private val dummyFrameworkResource: String get() { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodBuildTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodBuildTask.kt index 825d157e065..92c5273c25a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodBuildTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodBuildTask.kt @@ -7,71 +7,80 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks -import org.gradle.api.file.FileCollection -import org.gradle.api.file.FileTree +import org.gradle.api.file.* +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation.* +import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs +import org.jetbrains.kotlin.gradle.utils.getFile import org.jetbrains.kotlin.gradle.utils.runCommand -import java.io.File -import java.util.* +import org.jetbrains.kotlin.konan.target.Family +import javax.inject.Inject /** * The task compiles external cocoa pods sources. */ -open class PodBuildTask : CocoapodsTask() { +abstract class PodBuildTask @Inject constructor( + providerFactory: ProviderFactory, + projectLayout: ProjectLayout, + objectFactory: ObjectFactory, +) : CocoapodsTask() { - @get:PathSensitive(PathSensitivity.ABSOLUTE) @get:InputFile - lateinit var buildSettingsFile: Provider - internal set + abstract val buildSettingsFile: RegularFileProperty @get:Nested - internal lateinit var pod: Provider - - @get:PathSensitive(PathSensitivity.ABSOLUTE) - @get:IgnoreEmptyDirectories - @get:InputFiles - internal val srcDir: FileTree - get() = project.fileTree( - buildSettingsFile.map { PodBuildSettingsProperties.readSettingsFromReader(it.reader()).podsTargetSrcRoot } - ) - - @get:Internal - internal var buildDir: Provider = project.provider { - project.file(PodBuildSettingsProperties.readSettingsFromReader(buildSettingsFile.get().reader()).buildDir) - } + internal abstract val pod: Property @get:Input - internal lateinit var sdk: Provider + internal abstract val sdk: Property - @Suppress("unused") // declares an ouptut - @get:OutputFiles - internal val buildResult: Provider = project.provider { - project.fileTree(buildDir.get()) { - it.include("**/${pod.get().schemeName}.*/") - it.include("**/${pod.get().schemeName}/") + @get:Input + internal abstract val family: Property + + private val synthetic = projectLayout.cocoapodsBuildDirs.synthetic(family) + + @get:IgnoreEmptyDirectories + @get:InputDirectory + internal val srcDir: Provider = pod.flatMap { pod -> + val podLocation = pod.source + if (podLocation is Path) { + projectLayout.dir(providerFactory.provider { podLocation.dir }) + } else { + synthetic.map { it.dir("Pods/${pod.schemeName}") } } } + @Suppress("unused") // declares an output + @get:OutputFiles + internal val buildResult: FileCollection = objectFactory.fileTree() + .from(synthetic.map { it.dir("build") }) + .matching { + it.include("**/${pod.get().schemeName}.*/") + it.include("**/${pod.get().schemeName}/") + } + @get:Internal - internal lateinit var podsXcodeProjDir: Provider + internal abstract val podsXcodeProjDir: DirectoryProperty @TaskAction fun buildDependencies() { - val podBuildSettings = PodBuildSettingsProperties.readSettingsFromReader(buildSettingsFile.get().reader()) + val podBuildSettings = PodBuildSettingsProperties.readSettingsFromFile(buildSettingsFile.getFile()) val podsXcodeProjDir = podsXcodeProjDir.get() val podXcodeBuildCommand = listOf( "xcodebuild", - "-project", podsXcodeProjDir.name, + "-project", podsXcodeProjDir.asFile.name, "-scheme", pod.get().schemeName, "-sdk", sdk.get(), "-configuration", podBuildSettings.configuration ) - runCommand(podXcodeBuildCommand, project.logger) { directory(podsXcodeProjDir.parentFile) } + runCommand(podXcodeBuildCommand, logger) { directory(podsXcodeProjDir.asFile.parentFile) } } } 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 ec22f705de4..8478825480e 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 @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks +import org.gradle.api.file.ProjectLayout import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider @@ -17,11 +18,12 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.platformLiteral import org.jetbrains.kotlin.gradle.utils.XcodeVersion import org.jetbrains.kotlin.konan.target.Family import java.io.File +import javax.inject.Inject /** * The task generates a synthetic project with all cocoapods dependencies */ -abstract class PodGenTask : CocoapodsTask() { +abstract class PodGenTask @Inject constructor(projectLayout: ProjectLayout) : CocoapodsTask() { init { onlyIf { @@ -54,7 +56,7 @@ abstract class PodGenTask : CocoapodsTask() { internal abstract val xcodeVersion: Property @get:OutputFile - val podfile: Provider = family.map { project.cocoapodsBuildDirs.synthetic(it).resolve("Podfile") } + val podfile: Provider = projectLayout.cocoapodsBuildDirs.synthetic(family).map { it.file("Podfile").asFile } @TaskAction fun generate() { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt index 0ced95e7493..62e6604456d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodInstallTask.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks +import org.gradle.api.file.ProjectLayout import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider @@ -17,8 +18,9 @@ import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs import org.jetbrains.kotlin.gradle.targets.native.cocoapods.MissingCocoapodsMessage import org.jetbrains.kotlin.gradle.targets.native.cocoapods.MissingSpecReposMessage import java.io.File +import javax.inject.Inject -abstract class PodInstallTask : AbstractPodInstallTask() { +abstract class PodInstallTask @Inject constructor(projectLayout: ProjectLayout) : AbstractPodInstallTask() { @get:Optional @get:InputFile @@ -36,7 +38,7 @@ abstract class PodInstallTask : AbstractPodInstallTask() { @get:InputDirectory abstract val dummyFramework: Property - private val framework = project.provider { project.cocoapodsBuildDirs.framework.resolve("${frameworkName.get()}.framework") } + private val framework = projectLayout.cocoapodsBuildDirs.framework.map { it.file("${frameworkName.get()}.framework").asFile } private val tmpFramework = dummyFramework.map { dummy -> dummy.parentFile.resolve("tmp.framework").also { it.deleteOnExit() } } override fun doPodInstall() { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodSetupBuildTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodSetupBuildTask.kt index 21fd984292a..b5a0e342253 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodSetupBuildTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodSetupBuildTask.kt @@ -7,33 +7,34 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs +import org.jetbrains.kotlin.gradle.utils.getFile import org.jetbrains.kotlin.gradle.utils.runCommand import java.io.File +import javax.inject.Inject -open class PodSetupBuildTask : CocoapodsTask() { +abstract class PodSetupBuildTask @Inject constructor(projectLayout: ProjectLayout) : CocoapodsTask() { @get:Input - lateinit var frameworkName: Provider + abstract val frameworkName: Property @get:Input - internal lateinit var sdk: Provider + internal abstract val sdk: Property @get:Nested - lateinit var pod: Provider - - @get:OutputFile - val buildSettingsFile: Provider = project.provider { - project.cocoapodsBuildDirs - .buildSettings - .resolve(getBuildSettingFileName(pod.get(), sdk.get())) - } + abstract val pod: Property @get:Internal - internal lateinit var podsXcodeProjDir: Provider + internal abstract val podsXcodeProjDir: Property + + @get:OutputFile + val buildSettingsFile: Provider = projectLayout.cocoapodsBuildDirs.buildSettings(pod, sdk) @TaskAction fun setupBuild() { @@ -46,15 +47,12 @@ open class PodSetupBuildTask : CocoapodsTask() { "-sdk", sdk.get() ) - val outputText = runCommand(buildSettingsReceivingCommand, project.logger) { directory(podsXcodeProjDir.parentFile) } + val outputText = runCommand(buildSettingsReceivingCommand, logger) { directory(podsXcodeProjDir.parentFile) } val buildSettingsProperties = PodBuildSettingsProperties.readSettingsFromReader(outputText.reader()) - buildSettingsFile.get().let { bsf -> + buildSettingsFile.getFile().let { bsf -> buildSettingsProperties.writeSettings(bsf) } } } -private fun getBuildSettingFileName(pod: CocoapodsDependency, sdk: String): String = - "build-settings-$sdk-${pod.schemeName}.properties" - diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodspecTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodspecTask.kt index 3a6aaf93e86..8a2e0547f18 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodspecTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/cocoapods/tasks/PodspecTask.kt @@ -3,93 +3,103 @@ * 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("PackageDirectoryMismatch") // Old package for compatibility +@file:Suppress("PackageDirectoryMismatch", "LeakingThis") // Old package for compatibility package org.jetbrains.kotlin.gradle.tasks import org.gradle.api.DefaultTask import org.gradle.api.Project -import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.file.ProjectLayout +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* -import org.gradle.api.tasks.wrapper.Wrapper -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull -import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.PodspecPlatformSettings import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin -import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.COCOAPODS_EXTENSION_NAME import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.GENERATE_WRAPPER_PROPERTY import org.jetbrains.kotlin.gradle.plugin.cocoapods.KotlinCocoapodsPlugin.Companion.SYNC_TASK_NAME import org.jetbrains.kotlin.gradle.plugin.cocoapods.cocoapodsBuildDirs +import org.jetbrains.kotlin.gradle.utils.getFile import java.io.File +import javax.inject.Inject /** * The task generates a podspec file which allows a user to * integrate a Kotlin/Native framework into a CocoaPods project. */ -open class PodspecTask : DefaultTask() { +abstract class PodspecTask @Inject constructor(private val projectLayout: ProjectLayout) : DefaultTask() { @get:Input - internal val specName = project.objects.property(String::class.java) + internal abstract val specName: Property @get:Internal - internal val outputDir = project.objects.property(File::class.java) + internal abstract val outputDir: Property @get:OutputFile val outputFile: File get() = outputDir.get().resolve("${specName.get()}.podspec") @get:Input - internal lateinit var needPodspec: Provider + internal abstract val needPodspec: Property @get:Nested - val pods = project.objects.listProperty(CocoapodsDependency::class.java) + abstract val pods: ListProperty @get:Input - internal val version = project.objects.property(String::class.java) + internal abstract val version: Property @get:Input - internal val publishing = project.objects.property(Boolean::class.java) + internal abstract val publishing: Property @get:Input @get:Optional - internal val source = project.objects.property(String::class.java) + internal abstract val source: Property @get:Input @get:Optional - internal val homepage = project.objects.property(String::class.java) + internal abstract val homepage: Property @get:Input @get:Optional - internal val license = project.objects.property(String::class.java) + internal abstract val license: Property @get:Input @get:Optional - internal val authors = project.objects.property(String::class.java) + internal abstract val authors: Property @get:Input @get:Optional - internal val summary = project.objects.property(String::class.java) + internal abstract val summary: Property @get:Input @get:Optional - internal val extraSpecAttributes = project.objects.mapProperty(String::class.java, String::class.java) + internal abstract val extraSpecAttributes: MapProperty @get:Input - internal lateinit var frameworkName: Provider + internal abstract val frameworkName: Property @get:Nested - internal lateinit var ios: Provider + internal abstract val ios: Property @get:Nested - internal lateinit var osx: Provider + internal abstract val osx: Property @get:Nested - internal lateinit var tvos: Provider + internal abstract val tvos: Property @get:Nested - internal lateinit var watchos: Provider + internal abstract val watchos: Property + + @get:Input + @get:Optional + internal abstract val gradleWrapperPath: Property + + @get:Input + internal abstract val projectPath: Property + + @get:Input + internal abstract val hasPodfile: Property init { onlyIf { needPodspec.get() } @@ -107,17 +117,6 @@ open class PodspecTask : DefaultTask() { """.trimIndent() } - val gradleWrapper = (project.rootProject.tasks.getByName("wrapper") as? Wrapper)?.scriptFile - require(gradleWrapper != null && gradleWrapper.exists()) { - """ - The Gradle wrapper is required to run the build from Xcode. - - Please run the same command with `-P$GENERATE_WRAPPER_PROPERTY=true` or run the `:wrapper` task to generate the wrapper manually. - - See details about the wrapper at https://docs.gradle.org/current/userguide/gradle_wrapper.html - """.trimIndent() - } - val deploymentTargets = run { listOf(ios, osx, tvos, watchos).map { it.get() }.filter { it.deploymentTarget != null }.joinToString("\n") { if (extraSpecAttributes.get().containsKey("${it.name}.deployment_target")) "" else "| spec.${it.name}.deployment_target = '${it.deploymentTarget}'" @@ -129,7 +128,7 @@ open class PodspecTask : DefaultTask() { "| spec.dependency '${pod.name}'$versionSuffix" }.joinToString(separator = "\n") - val frameworkDir = project.cocoapodsBuildDirs.framework.relativeTo(outputFile.parentFile) + val frameworkDir = projectLayout.cocoapodsBuildDirs.framework.getFile().relativeTo(outputFile.parentFile) val vendoredFramework = if (publishing.get()) "${frameworkName.get()}.xcframework" else frameworkDir.resolve("${frameworkName.get()}.framework").invariantSeparatorsPath val vendoredFrameworks = if (extraSpecAttributes.get().containsKey("vendored_frameworks")) "" else "| spec.vendored_frameworks = '$vendoredFramework'" @@ -138,12 +137,11 @@ open class PodspecTask : DefaultTask() { val xcConfig = if (publishing.get() || extraSpecAttributes.get().containsKey("pod_target_xcconfig")) "" else """ | | spec.pod_target_xcconfig = { - | 'KOTLIN_PROJECT_PATH' => '${if (project.depth != 0) project.path else ""}', + | 'KOTLIN_PROJECT_PATH' => '${projectPath.get()}', | 'PRODUCT_MODULE_NAME' => '${frameworkName.get()}', | } """.trimMargin() - val gradleCommand = "\$REPO_ROOT/${gradleWrapper.relativeTo(project.projectDir).invariantSeparatorsPath}" val scriptPhase = if (publishing.get() || extraSpecAttributes.get().containsKey("script_phases")) "" else """ | | spec.script_phases = [ @@ -158,7 +156,7 @@ open class PodspecTask : DefaultTask() { | fi | set -ev | REPO_ROOT="${'$'}PODS_TARGET_SRCROOT" - | "$gradleCommand" -p "${'$'}REPO_ROOT" ${'$'}KOTLIN_PROJECT_PATH:$SYNC_TASK_NAME \ + | "${gradleCommand()}" -p "${'$'}REPO_ROOT" ${'$'}KOTLIN_PROJECT_PATH:$SYNC_TASK_NAME \ | -P${KotlinCocoapodsPlugin.PLATFORM_PROPERTY}=${'$'}PLATFORM_NAME \ | -P${KotlinCocoapodsPlugin.ARCHS_PROPERTY}="${'$'}ARCHS" \ | -P${KotlinCocoapodsPlugin.CONFIGURATION_PROPERTY}="${'$'}CONFIGURATION" @@ -191,34 +189,39 @@ open class PodspecTask : DefaultTask() { """.trimMargin() ) - if (hasPodfileOwnOrParent(project) && publishing.get().not()) { + if (hasPodfile.get() && !publishing.get()) { logger.quiet( """ - Generated a podspec file at: ${absolutePath}. - To include it in your Xcode project, check that the following dependency snippet exists in your Podfile: + Generated a podspec file at: ${absolutePath}. + To include it in your Xcode project, check that the following dependency snippet exists in your Podfile: - pod '${specName.get()}', :path => '${parentFile.absolutePath}' - - """.trimIndent() + pod '${specName.get()}', :path => '${parentFile.absolutePath}' + """.trimIndent() ) } } } + private fun gradleCommand(): String { + val gradleWrapperPath: String? = gradleWrapperPath.get() + val gradleWrapper = gradleWrapperPath?.let(::File) + require(gradleWrapper != null && gradleWrapper.exists()) { + """ + The Gradle wrapper is required to run the build from Xcode. + + Please run the same command with `-P$GENERATE_WRAPPER_PROPERTY=true` or run the `:wrapper` task to generate the wrapper manually. + + See details about the wrapper at https://docs.gradle.org/current/userguide/gradle_wrapper.html + """.trimIndent() + } + + return "\$REPO_ROOT/${gradleWrapper.relativeTo(projectLayout.projectDirectory.asFile).invariantSeparatorsPath}" + } + private fun Provider.getOrEmpty(): String = getOrElse("") private fun String.surroundWithSingleQuotesIfNeeded(): String = if (startsWith("{") || startsWith("<<-") || startsWith("'")) this else "'$this'" - companion object { - private val KotlinMultiplatformExtension?.cocoapodsExtensionOrNull: CocoapodsExtension? - get() = (this as? ExtensionAware)?.extensions?.findByName(COCOAPODS_EXTENSION_NAME) as? CocoapodsExtension - - private fun hasPodfileOwnOrParent(project: Project): Boolean = - if (project.rootProject == project) project.multiplatformExtensionOrNull?.cocoapodsExtensionOrNull?.podfile != null - else project.multiplatformExtensionOrNull?.cocoapodsExtensionOrNull?.podfile != null - || (project.parent?.let { hasPodfileOwnOrParent(it) } ?: false) - - } } 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 8785fe1df2e..2e35384ced7 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 @@ -43,12 +43,14 @@ abstract class KotlinNativeLink @Inject constructor( @Internal + @Transient // This property can't be accessed in the execution phase val binary: NativeBinary, private val objectFactory: ObjectFactory, - private val execOperations: ExecOperations + private val execOperations: ExecOperations, ) : AbstractKotlinCompileTool(objectFactory), UsesKonanPropertiesBuildService, KotlinToolTask { + @Deprecated("Visibility will be lifted to private in the future releases") @get:Internal val compilation: KotlinNativeCompilation @@ -85,16 +87,19 @@ constructor( ) @get:Input - val outputKind: CompilerOutputKind get() = binary.outputKind.compilerOutputKind + val outputKind: CompilerOutputKind by lazyConvention { binary.outputKind.compilerOutputKind } @get:Input - val optimized: Boolean get() = binary.optimized + val optimized: Boolean by lazyConvention { binary.optimized } @get:Input - val debuggable: Boolean get() = binary.debuggable + val debuggable: Boolean by lazyConvention { binary.debuggable } @get:Input - val baseName: String get() = binary.baseName + val baseName: String by lazyConvention { binary.baseName } + + @get:Input + internal val binaryName: String by lazyConvention { binary.name } @Suppress("DEPRECATION") private val konanTarget = compilation.konanTarget @@ -132,24 +137,29 @@ constructor( } fun kotlinOptions(fn: Closure<*>) { - @Suppress("DEPRECATION") fn.delegate = kotlinOptions fn.call() } // Binary-specific options. - @get:Input - @get:Optional - val entryPoint: String? get() = (binary as? Executable)?.entryPoint + private val _entryPoint: String by lazyConvention { (binary as? Executable)?.entryPoint.orEmpty() } @get:Input - val linkerOpts: List get() = binary.linkerOpts + @get:Optional + val entryPoint: String? + get() = _entryPoint.ifEmpty { null } + + @get:Input + val linkerOpts: List by lazyConvention { binary.linkerOpts } + + @get:Input + internal val additionalLinkerOpts: MutableList = mutableListOf() @get:Input val binaryOptions: Map by lazy { PropertiesProvider(project).nativeBinaryOptions + binary.binaryOptions } @get:Input - val processTests: Boolean get() = binary is TestExecutable + val processTests: Boolean by lazyConvention { binary is TestExecutable } @get:Classpath val exportLibraries: FileCollection get() = exportLibrariesResolvedConfiguration?.files ?: objectFactory.fileCollection() @@ -161,8 +171,7 @@ constructor( } @get:Input - val isStaticFramework: Boolean - get() = binary.let { it is Framework && it.isStatic } + val isStaticFramework: Boolean by lazyConvention { binary.let { it is Framework && it.isStatic } } @Suppress("DEPRECATION") @get:Input @@ -221,7 +230,7 @@ constructor( null, BitcodeEmbeddingMode.DISABLE -> Unit } - args.singleLinkerArguments = linkerOpts.toTypedArray() + args.singleLinkerArguments = (linkerOpts + additionalLinkerOpts).toTypedArray() args.binaryOptions = binaryOptions.map { (key, value) -> "$key=$value" }.toTypedArray() args.staticFramework = isStaticFramework @@ -269,7 +278,7 @@ constructor( } """ - |Following dependencies exported in the ${binary.name} binary are not specified as API-dependencies of a corresponding source set: + |Following dependencies exported in the $binaryName binary are not specified as API-dependencies of a corresponding source set: | $failedDependenciesList | @@ -378,4 +387,8 @@ constructor( executionContext = executionContext ).run(buildArguments) } + + private inline fun lazyConvention(noinline lazyConventionValue: () -> T): Provider { + return objectFactory.providerWithLazyConvention(lazyConventionValue) + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt index 848bbf1242f..b3647dc0e94 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt @@ -721,16 +721,17 @@ internal class CacheBuilder( val konanCacheKind: NativeCacheKind, val libraries: FileCollection, val gradleUserHomeDir: File, - val binary: NativeBinary, val konanTarget: KonanTarget, val toolOptions: KotlinCommonCompilerToolOptions, - val externalDependenciesArgs: List + val externalDependenciesArgs: List, + val debuggable: Boolean, + val optimized: Boolean, ) { val rootCacheDirectory get() = getRootCacheDirectory( File(runnerSettings.parent.konanHome), konanTarget, - binary.debuggable, + debuggable, konanCacheKind ) @@ -748,7 +749,11 @@ internal class CacheBuilder( konanCacheKind = konanCacheKind, libraries = binary.compilation.compileDependencyFiles, gradleUserHomeDir = project.gradle.gradleUserHomeDir, - binary, konanTarget, toolOptions, externalDependenciesArgs + konanTarget = konanTarget, + toolOptions = toolOptions, + externalDependenciesArgs = externalDependenciesArgs, + debuggable = binary.debuggable, + optimized = binary.optimized, ) } } @@ -760,17 +765,14 @@ internal class CacheBuilder( listOf(KLIB_INTEROP_IR_PROVIDER_IDENTIFIER) ) - private val binary: NativeBinary - get() = settings.binary - private val konanTarget: KonanTarget get() = settings.konanTarget private val optimized: Boolean - get() = binary.optimized + get() = settings.optimized private val debuggable: Boolean - get() = binary.debuggable + get() = settings.debuggable private val konanCacheKind: NativeCacheKind get() = settings.konanCacheKind diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/fileUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/fileUtils.kt index d57785f59fb..c61b38ae2e1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/fileUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/fileUtils.kt @@ -6,7 +6,9 @@ package org.jetbrains.kotlin.gradle.utils import org.gradle.api.Project +import org.gradle.api.file.Directory import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider import java.io.File import java.io.IOException import java.nio.file.Files @@ -116,4 +118,14 @@ fun contentEquals(file1: File, file2: File): Boolean { } } -internal fun RegularFile.toUri() = asFile.toPath().toUri() \ No newline at end of file +internal fun RegularFile.toUri() = asFile.toPath().toUri() + +internal fun Provider.mapToFile(): Provider = map { it.asFile } + +@JvmName("mapDirectoryToFile") // avoids jvm signature clash +internal fun Provider.mapToFile(): Provider = map { it.asFile } + +internal fun Provider.getFile(): File = get().asFile + +@JvmName("getDirectoryAsFile") // avoids jvm signature clash +internal fun Provider.getFile(): File = get().asFile