diff --git a/subprojects/integ-tests/src/test/kotlin/org/gradle/kotlin/dsl/integration/PrecompiledScriptPluginIntegrationTest.kt b/subprojects/integ-tests/src/test/kotlin/org/gradle/kotlin/dsl/integration/PrecompiledScriptPluginIntegrationTest.kt index 0078056cd..faf757c15 100644 --- a/subprojects/integ-tests/src/test/kotlin/org/gradle/kotlin/dsl/integration/PrecompiledScriptPluginIntegrationTest.kt +++ b/subprojects/integ-tests/src/test/kotlin/org/gradle/kotlin/dsl/integration/PrecompiledScriptPluginIntegrationTest.kt @@ -1,5 +1,13 @@ package org.gradle.kotlin.dsl.integration +import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE + +import org.gradle.kotlin.dsl.fixtures.gradleRunnerFor + +import org.hamcrest.CoreMatchers.equalTo +import org.junit.Assert.assertThat import org.junit.Test @@ -25,4 +33,48 @@ class PrecompiledScriptPluginIntegrationTest : AbstractPluginIntegrationTest() { build("generateScriptPluginAdapters") build("ktlintC") } + + @Test + fun `precompiled script plugins adapters generation is cached and relocatable`() { + + val firstLocation = "first-location" + val secondLocation = "second-location" + val cacheDir = newDir("cache-dir") + + withSettingsIn(firstLocation, """ + rootProject.name = "test" + $pluginManagementBlock + buildCache { + local { + directory = file("${escapedPathOf(cacheDir)}") + } + } + """) + withBuildScriptIn(firstLocation, """ + plugins { `kotlin-dsl` } + repositories { jcenter() } + """) + + withFile("$firstLocation/src/main/kotlin/plugin-without-package.gradle.kts") + withFile("$firstLocation/src/main/kotlin/plugins/plugin-with-package.gradle.kts", """ + package plugins + """) + + + val firstDir = existing(firstLocation) + val secondDir = newDir(secondLocation) + firstDir.copyRecursively(secondDir) + + val generationTask = ":generateScriptPluginAdapters" + + gradleRunnerFor(firstDir, "classes", "--build-cache").build().apply { + assertThat(outcomeOf(generationTask), equalTo(SUCCESS)) + } + gradleRunnerFor(firstDir, "classes", "--build-cache").build().apply { + assertThat(outcomeOf(generationTask), equalTo(UP_TO_DATE)) + } + gradleRunnerFor(secondDir, "classes", "--build-cache").build().apply { + assertThat(outcomeOf(generationTask), equalTo(FROM_CACHE)) + } + } } diff --git a/subprojects/plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/GenerateScriptPluginAdapters.kt b/subprojects/plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/GenerateScriptPluginAdapters.kt new file mode 100644 index 000000000..96d0ea61d --- /dev/null +++ b/subprojects/plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/GenerateScriptPluginAdapters.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradle.kotlin.dsl.plugins.precompiled + +import org.gradle.api.DefaultTask +import org.gradle.api.file.FileTree +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +import java.io.File + + +@CacheableTask +open class GenerateScriptPluginAdapters : DefaultTask() { + + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + internal + lateinit var scripts: FileTree + + @Internal + internal + lateinit var plugins: List + + @OutputDirectory + var outputDirectory = project.objects.directoryProperty() + + @TaskAction + @Suppress("unused") + internal + fun generate() = + outputDirectory.asFile.get().let { outputDir -> + outputDir.mkdirs() + for (scriptPlugin in plugins) { + scriptPlugin.writeScriptPluginAdapterTo(outputDir) + } + } +} + + +internal +fun ScriptPlugin.writeScriptPluginAdapterTo(outputDir: File) { + + val (packageDir, packageDeclaration) = + packageName?.let { packageName -> + packageDir(outputDir, packageName) to "package $packageName" + } ?: outputDir to "" + + val outputFile = + packageDir.resolve("$simplePluginAdapterClassName.kt") + + outputFile.writeText(""" + + $packageDeclaration + + /** + * Precompiled [$scriptFileName][$compiledScriptTypeName] script plugin. + * + * @see $compiledScriptTypeName + */ + class $simplePluginAdapterClassName : org.gradle.api.Plugin<$targetType> { + override fun apply(target: $targetType) { + try { + Class + .forName("$compiledScriptTypeName") + .getDeclaredConstructor($targetType::class.java) + .newInstance(target) + } catch (e: java.lang.reflect.InvocationTargetException) { + throw e.targetException + } + } + } + + """.replaceIndent().trim() + "\n") +} + + +private +fun packageDir(outputDir: File, packageName: String) = + outputDir.mkdir(packageName.replace('.', '/')) + + +private +fun File.mkdir(relative: String) = + resolve(relative).apply { mkdirs() } diff --git a/subprojects/plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt b/subprojects/plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt index d048ab393..f468d081a 100644 --- a/subprojects/plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt +++ b/subprojects/plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt @@ -40,8 +40,6 @@ import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import java.io.File - /* * Exposes `*.gradle.kts` scripts from regular Kotlin source-sets as binary Gradle plugins. @@ -149,15 +147,10 @@ fun Project.generatePluginAdaptersFor(scriptPlugins: List, scriptS val generatedSourcesDir = layout.buildDirectory.dir("generated-sources/kotlin-dsl-plugins/kotlin") sourceSets["main"].kotlin.srcDir(generatedSourcesDir) - val generateScriptPluginAdapters by tasks.registering { - inputs.files(scriptSourceFiles) - outputs.dir(generatedSourcesDir) - doLast { - val outputDir = generatedSourcesDir.get().asFile - for (scriptPlugin in scriptPlugins) { - scriptPlugin.writeScriptPluginAdapterTo(outputDir) - } - } + val generateScriptPluginAdapters by tasks.registering(GenerateScriptPluginAdapters::class) { + scripts = scriptSourceFiles + plugins = scriptPlugins + outputDirectory.set(generatedSourcesDir) } tasks.named("compileKotlin") { @@ -166,53 +159,6 @@ fun Project.generatePluginAdaptersFor(scriptPlugins: List, scriptS } -internal -fun ScriptPlugin.writeScriptPluginAdapterTo(outputDir: File) { - - val (packageDir, packageDeclaration) = - packageName?.let { packageName -> - packageDir(outputDir, packageName) to "package $packageName" - } ?: outputDir to "" - - val outputFile = - packageDir.resolve("$simplePluginAdapterClassName.kt") - - outputFile.writeText(""" - - $packageDeclaration - - /** - * Precompiled [$scriptFileName][$compiledScriptTypeName] script plugin. - * - * @see $compiledScriptTypeName - */ - class $simplePluginAdapterClassName : org.gradle.api.Plugin<$targetType> { - override fun apply(target: $targetType) { - try { - Class - .forName("$compiledScriptTypeName") - .getDeclaredConstructor($targetType::class.java) - .newInstance(target) - } catch (e: java.lang.reflect.InvocationTargetException) { - throw e.targetException - } - } - } - - """.replaceIndent().trim() + "\n") -} - - -private -fun packageDir(outputDir: File, packageName: String) = - outputDir.mkdir(packageName.replace('.', '/')) - - -private -fun File.mkdir(relative: String) = - resolve(relative).apply { mkdirs() } - - private val Project.sourceSets get() = project.the() diff --git a/subprojects/plugins/src/test/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginTest.kt b/subprojects/plugins/src/test/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginTest.kt index de10946fe..a823422ad 100644 --- a/subprojects/plugins/src/test/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginTest.kt +++ b/subprojects/plugins/src/test/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginTest.kt @@ -16,8 +16,6 @@ import org.hamcrest.CoreMatchers.not import org.junit.Assert.assertThat import org.junit.Test -import java.io.File - class KotlinDslPluginTest : AbstractPluginTest() { @@ -344,10 +342,6 @@ class KotlinDslPluginTest : AbstractPluginTest() { """) } - private - fun escapedPathOf(file: File) = - file.absolutePath.replace("\\", "\\\\") - private fun outputOf(vararg arguments: String) = buildWithPlugin(*arguments).output diff --git a/subprojects/test-fixtures/src/main/kotlin/org/gradle/kotlin/dsl/fixtures/AbstractIntegrationTest.kt b/subprojects/test-fixtures/src/main/kotlin/org/gradle/kotlin/dsl/fixtures/AbstractIntegrationTest.kt index eeaf61113..9470fcc10 100644 --- a/subprojects/test-fixtures/src/main/kotlin/org/gradle/kotlin/dsl/fixtures/AbstractIntegrationTest.kt +++ b/subprojects/test-fixtures/src/main/kotlin/org/gradle/kotlin/dsl/fixtures/AbstractIntegrationTest.kt @@ -215,6 +215,10 @@ open class AbstractIntegrationTest { private val gradlePropertiesFile by lazy { existing("gradle.properties") } + + protected + fun escapedPathOf(file: File) = + file.absolutePath.replace("\\", "\\\\") } @@ -231,7 +235,6 @@ fun containsBuildScanPluginOutput(): Matcher = allOf( ) -private fun gradleRunnerFor(projectDir: File, vararg arguments: String): GradleRunner = GradleRunner.create().run { withGradleInstallation(customInstallation()) withProjectDir(projectDir)