Skip to content

Commit b72424a

Browse files
authored
feat: add support for Gradle configuration cache (#21022)
Fixes #18612
1 parent 23cd6fe commit b72424a

13 files changed

+749
-349
lines changed

flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscMultiModuleTest.kt

+54
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ import org.junit.Test
2424
import java.io.File
2525
import java.nio.file.Files
2626
import kotlin.io.path.writeText
27+
import kotlin.test.assertContains
2728
import kotlin.test.expect
29+
import org.gradle.testkit.runner.TaskOutcome
2830

2931
class MiscMultiModuleTest : AbstractGradleTest() {
3032
/**
@@ -228,4 +230,56 @@ class MiscMultiModuleTest : AbstractGradleTest() {
228230
expect("MY_APP_ID") { tokenFileContent.getString(InitParameters.APPLICATION_IDENTIFIER) }
229231
}
230232

233+
@Test
234+
fun testPrepareFrontend_configurationCache() {
235+
testProject.settingsFile.writeText("include 'lib', 'web'")
236+
testProject.buildFile.writeText("""
237+
plugins {
238+
id 'java'
239+
id 'com.vaadin' apply false
240+
}
241+
allprojects {
242+
repositories {
243+
mavenLocal()
244+
mavenCentral()
245+
maven { url = 'https://maven.vaadin.com/vaadin-prereleases' }
246+
}
247+
}
248+
project(':lib') {
249+
apply plugin: 'java'
250+
}
251+
""".trimIndent())
252+
testProject.newFolder("lib")
253+
val webFolder = testProject.newFolder("web")
254+
// Create frontend folder, that will otherwise be created by the first
255+
// execution of vaadinPrepareFrontend, invalidating the cache on the
256+
// second run
257+
webFolder.resolve("src/main/frontend").mkdirs()
258+
val webBuildFile = Files.createFile(webFolder.toPath().resolve("build.gradle"))
259+
webBuildFile.writeText("""
260+
apply plugin: 'war'
261+
apply plugin: 'com.vaadin'
262+
263+
dependencies {
264+
implementation project(':lib')
265+
implementation("com.vaadin:flow:$flowVersion")
266+
}
267+
268+
vaadin {
269+
nodeAutoUpdate = true // test the vaadin{} block by changing some innocent property with limited side-effect
270+
applicationIdentifier = 'MY_APP_ID'
271+
}
272+
""".trimIndent())
273+
274+
val result = testProject.build("--configuration-cache", "vaadinPrepareFrontend", checkTasksSuccessful = false)
275+
result.expectTaskSucceded("web:vaadinPrepareFrontend")
276+
assertContains(result.output, "Calculating task graph as no cached configuration is available for tasks: vaadinPrepareFrontend")
277+
assertContains(result.output, "Configuration cache entry stored")
278+
279+
val result2 = testProject.build("--configuration-cache", "vaadinPrepareFrontend", checkTasksSuccessful = false)
280+
result2.expectTaskOutcome("web:vaadinPrepareFrontend", TaskOutcome.UP_TO_DATE)
281+
assertContains(result2.output, "Reusing configuration cache")
282+
}
283+
284+
231285
}

flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/VaadinSmokeTest.kt

+73-1
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ class VaadinSmokeTest : AbstractGradleTest() {
437437
addonJar.toPath(), StandardCopyOption.REPLACE_EXISTING
438438
)
439439

440-
val result: BuildResult = testProject.build("-Pvaadin.productionMode", "build")
440+
val result: BuildResult = testProject.build("-Pvaadin.productionMode", "build", debug = true)
441441
result.expectTaskSucceded("vaadinPrepareFrontend")
442442
result.expectTaskSucceded("vaadinBuildFrontend")
443443

@@ -497,6 +497,78 @@ class VaadinSmokeTest : AbstractGradleTest() {
497497
}
498498
}
499499

500+
@Test
501+
fun testPrepareFrontend_configurationCache() {
502+
// Create frontend folder, that will otherwise be created by the first
503+
// execution of vaadinPrepareFrontend, invalidating the cache on the
504+
// second run
505+
testProject.newFolder("src/main/frontend")
506+
507+
val result = testProject.build("--configuration-cache", "vaadinPrepareFrontend")
508+
result.expectTaskSucceded("vaadinPrepareFrontend")
509+
assertContains(result.output, "Calculating task graph as no cached configuration is available for tasks: vaadinPrepareFrontend")
510+
assertContains(result.output, "Configuration cache entry stored")
511+
512+
val result2 = testProject.build("--configuration-cache", "vaadinPrepareFrontend", checkTasksSuccessful = false)
513+
result2.expectTaskOutcome("vaadinPrepareFrontend", TaskOutcome.UP_TO_DATE)
514+
assertContains(result2.output, "Reusing configuration cache")
515+
}
516+
517+
@Test
518+
fun testPrepareFrontend_configurationCache_configurationChange_cacheInvalidated() {
519+
// Create frontend folder, that will otherwise be created by the first
520+
// execution of vaadinPrepareFrontend, invalidating the cache on the
521+
// second run
522+
testProject.newFolder("src/main/frontend")
523+
524+
val result = testProject.build("--configuration-cache", "vaadinPrepareFrontend")
525+
result.expectTaskSucceded("vaadinPrepareFrontend")
526+
assertContains(result.output, "Calculating task graph as no cached configuration is available for tasks: vaadinPrepareFrontend")
527+
assertContains(result.output, "Configuration cache entry stored")
528+
529+
val buildFile = testProject.buildFile.readText()
530+
.replace("nodeAutoUpdate = true", "nodeAutoUpdate = false")
531+
testProject.buildFile.writeText(buildFile)
532+
533+
val result2 = testProject.build("--configuration-cache", "vaadinPrepareFrontend", checkTasksSuccessful = false)
534+
result2.expectTaskOutcome("vaadinPrepareFrontend", TaskOutcome.SUCCESS)
535+
assertContains(result.output, "Calculating task graph as no cached configuration is available for tasks: vaadinPrepareFrontend")
536+
}
537+
538+
@Test
539+
fun testPrepareFrontend_configurationCache_gradlePropertyChange_cacheInvalidated() {
540+
// Create frontend folder, that will otherwise be created by the first
541+
// execution of vaadinPrepareFrontend, invalidating the cache on the
542+
// second run
543+
testProject.newFolder("src/main/frontend")
544+
545+
val result = testProject.build("--configuration-cache", "vaadinPrepareFrontend")
546+
result.expectTaskSucceded("vaadinPrepareFrontend")
547+
assertContains(result.output, "Calculating task graph as no cached configuration is available for tasks: vaadinPrepareFrontend")
548+
assertContains(result.output, "Configuration cache entry stored")
549+
550+
val result2 = testProject.build("--configuration-cache", "vaadinPrepareFrontend", "-Pvaadin.eagerServerLoad=true", checkTasksSuccessful = false)
551+
result2.expectTaskOutcome("vaadinPrepareFrontend", TaskOutcome.SUCCESS)
552+
assertContains(result.output, "Calculating task graph as no cached configuration is available for tasks: vaadinPrepareFrontend")
553+
}
554+
555+
@Test
556+
fun testPrepareFrontend_configurationCache_systemPropertyChange_cacheInvalidated() {
557+
// Create frontend folder, that will otherwise be created by the first
558+
// execution of vaadinPrepareFrontend, invalidating the cache on the
559+
// second run
560+
testProject.newFolder("src/main/frontend")
561+
562+
val result = testProject.build("--configuration-cache", "vaadinPrepareFrontend")
563+
result.expectTaskSucceded("vaadinPrepareFrontend")
564+
assertContains(result.output, "Calculating task graph as no cached configuration is available for tasks: vaadinPrepareFrontend")
565+
assertContains(result.output, "Configuration cache entry stored")
566+
567+
val result2 = testProject.build("--configuration-cache", "vaadinPrepareFrontend", "-Dvaadin.eagerServerLoad=true", checkTasksSuccessful = false)
568+
result2.expectTaskOutcome("vaadinPrepareFrontend", TaskOutcome.SUCCESS)
569+
assertContains(result.output, "Calculating task graph as no cached configuration is available for tasks: vaadinPrepareFrontend")
570+
}
571+
500572
private fun enableHilla() {
501573
testProject.newFolder(FrontendUtils.DEFAULT_FRONTEND_DIR)
502574
testProject.newFile(FrontendUtils.DEFAULT_FRONTEND_DIR + "index.ts")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.vaadin.gradle
2+
3+
import javax.inject.Inject
4+
import com.vaadin.flow.server.frontend.FrontendTools
5+
import com.vaadin.flow.server.frontend.FrontendToolsSettings
6+
import org.gradle.api.provider.Property
7+
import org.gradle.api.provider.Provider
8+
import org.gradle.api.provider.ProviderFactory
9+
import org.gradle.api.provider.ValueSource
10+
import org.gradle.api.provider.ValueSourceParameters
11+
import org.gradle.api.services.BuildService
12+
import org.gradle.api.services.BuildServiceParameters
13+
14+
/**
15+
* Custom ValueSource to support Gradle configuration cache by catching values
16+
* produced by Vaadin FrontendTools processing shell commands.
17+
*/
18+
internal abstract class FrontendToolsValueSource :
19+
ValueSource<String, FrontendToolsValueSource.Parameters> {
20+
21+
override fun obtain(): String? {
22+
return parameters.getToolsSettings().map { FrontendTools(it) }
23+
.flatMap { tools ->
24+
parameters.getAction().map { act -> act.invoke(tools) }
25+
}.orNull
26+
}
27+
28+
interface Parameters : ValueSourceParameters {
29+
fun getToolsSettings(): Property<FrontendToolsSettings>
30+
fun getAction(): Property<(FrontendTools) -> String>
31+
}
32+
}
33+
34+
/**
35+
* Shared service to create Gradle properties based on Vaadin FrontendTools
36+
* execution.
37+
* Properties generated by the toolsProperty method can safely be referenced by
38+
* Tasks input and outputs without breaking the Gradle configuration cache.
39+
*/
40+
internal abstract class FrontendToolService @Inject constructor(private val providerFactory: ProviderFactory) :
41+
BuildService<FrontendToolService.Parameters> {
42+
43+
fun tools(): FrontendTools =
44+
parameters.getToolsSettings().map { FrontendTools(it) }.get()
45+
46+
fun toolsProperty(getter: (FrontendTools) -> String): Provider<String> {
47+
return parameters.getToolsSettings().flatMap { toolsSettings ->
48+
providerFactory.of(FrontendToolsValueSource::class.java) {
49+
it.parameters.getToolsSettings().set(toolsSettings)
50+
it.parameters.getAction().set(getter)
51+
}
52+
}
53+
}
54+
55+
interface Parameters : BuildServiceParameters {
56+
fun getToolsSettings(): Property<FrontendToolsSettings>
57+
}
58+
}

0 commit comments

Comments
 (0)