Skip to content

Commit 00bdfdf

Browse files
authored
fix: Allow Gradle builds to use custom sourcesets (#15739)
When a sourceset other than `main` is used for holding classes using Vaadin components, or for providing a resolution scope for Vaadin dependencies, the `prepareVaadinFrontend` task does not correctly extract the correct Node libraries required by those classes, and the `buildVaadinFrontend` task subsequently fails to complete due to Vite requiring those Node dependencies to have been extracted into the project so failing to find all of its front-end dependencies. The `VaadinFlowPluginExtension` is being altered to include a `sourceSetName` field, that defaults to `main` for backwards compatibility but allows users to specify a custom sourceset to use for the prepare and build tasks. As the use of a custom sourceset generally implies that different Gradle tasks will be executed for operating on those sourcesets, and a different dependency scope generated for the custom sourceset, fields have also been added to the extension that allow users to override the values the plugin uses, whilst the default values follow the Gradle conventions for both `main` and non-`main` sourcesets. Fixes #15738
1 parent 7286880 commit 00bdfdf

File tree

6 files changed

+157
-31
lines changed

6 files changed

+157
-31
lines changed

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

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2000-2022 Vaadin Ltd
2+
* Copyright 2000-2023 Vaadin Ltd
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -532,4 +532,97 @@ class MiscSingleModuleTest : AbstractGradleTest() {
532532
classpath.map { it.removeSuffix("-SNAPSHOT.jar").dropLastWhile { it != '-' } }
533533
}
534534
}
535+
536+
@Test
537+
fun testUsingNonMainSourceSet() {
538+
testProject.settingsFile.writeText(
539+
"""
540+
pluginManagement {
541+
repositories {
542+
gradlePluginPortal()
543+
}
544+
}
545+
"""
546+
)
547+
testProject.buildFile.writeText(
548+
"""
549+
plugins {
550+
id 'java'
551+
id 'org.springframework.boot' version '3.0.0'
552+
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
553+
id("com.vaadin")
554+
}
555+
556+
repositories {
557+
mavenLocal()
558+
mavenCentral()
559+
maven { url 'https://maven.vaadin.com/vaadin-prereleases' }
560+
}
561+
562+
sourceSets {
563+
ui {
564+
java
565+
}
566+
main {
567+
java {
568+
compileClasspath += ui.output
569+
runtimeClasspath += ui.output + ui.runtimeClasspath
570+
}
571+
}
572+
}
573+
574+
vaadin {
575+
productionMode = true
576+
sourceSetName = 'ui'
577+
}
578+
579+
dependencies {
580+
uiImplementation('com.vaadin:flow:$flowVersion')
581+
implementation('org.springframework.boot:spring-boot-starter-web')
582+
}
583+
584+
jar {
585+
enabled = false
586+
}
587+
"""
588+
)
589+
590+
testProject.newFile(
591+
"src/main/java/com/example/demo/DemoApplication.java", """
592+
package com.example.demo;
593+
594+
import org.springframework.boot.SpringApplication;
595+
import org.springframework.boot.autoconfigure.SpringBootApplication;
596+
597+
@SpringBootApplication
598+
public class DemoApplication {
599+
600+
public static void main(String[] args) {
601+
SpringApplication.run(DemoApplication.class, args);
602+
}
603+
604+
}
605+
""".trimIndent()
606+
)
607+
608+
testProject.newFile(
609+
"src/ui/java/com/example/demo/AppShell.java", """
610+
package com.example.demo;
611+
612+
import com.vaadin.flow.component.page.AppShellConfigurator;
613+
import com.vaadin.flow.server.PWA;
614+
615+
@PWA(name = "Demo application", shortName = "Demo")
616+
public class AppShell implements AppShellConfigurator {
617+
}
618+
""".trimIndent()
619+
)
620+
621+
val build: BuildResult = testProject.build("build")
622+
build.expectTaskSucceded("vaadinPrepareFrontend")
623+
build.expectTaskSucceded("vaadinBuildFrontend")
624+
625+
val jar: File = testProject.builtJar
626+
expectArchiveContainsVaadinBundle(jar, true)
627+
}
535628
}

flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/GradlePluginAdapter.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2000-2022 Vaadin Ltd
2+
* Copyright 2000-2023 Vaadin Ltd
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -45,10 +45,10 @@ internal class GradlePluginAdapter(val project: Project, private val isBeforePro
4545
override fun generatedTsFolder(): File = extension.generatedTsFolder
4646

4747
override fun getClassFinder(): ClassFinder {
48-
val runtimeClasspath: Configuration? = project.configurations.findByName("runtimeClasspath")
49-
val runtimeClasspathJars: List<File> = if (runtimeClasspath != null) {
48+
val dependencyConfiguration: Configuration? = project.configurations.findByName(extension.dependencyScope!!)
49+
val dependencyConfigurationJars: List<File> = if (dependencyConfiguration != null) {
5050
var artifacts: List<ResolvedArtifact> =
51-
runtimeClasspath.resolvedConfiguration.resolvedArtifacts.toList()
51+
dependencyConfiguration.resolvedConfiguration.resolvedArtifacts.toList()
5252
val extension = VaadinFlowPluginExtension.get(project)
5353
val artifactFilter = extension.classpathFilter.toPredicate()
5454
artifacts = artifacts.filter { artifactFilter.test(it.moduleVersion.id.module) }
@@ -57,11 +57,11 @@ internal class GradlePluginAdapter(val project: Project, private val isBeforePro
5757

5858
// we need to also analyze the project's classes
5959
val sourceSet: SourceSetContainer = project.properties["sourceSets"] as SourceSetContainer
60-
val classesDirs: List<File> = sourceSet.getByName("main").output.classesDirs
60+
val classesDirs: List<File> = sourceSet.getByName(extension.sourceSetName).output.classesDirs
6161
.toList()
6262
.filter { it.exists() }
6363

64-
val resourcesDir: List<File> = listOfNotNull(sourceSet.getByName("main").output.resourcesDir)
64+
val resourcesDir: List<File> = listOfNotNull(sourceSet.getByName(extension.sourceSetName).output.resourcesDir)
6565
.filter { it.exists() }
6666

6767
// for Spring Boot project there is no "providedCompile" scope: the WAR plugin brings that in.
@@ -71,7 +71,7 @@ internal class GradlePluginAdapter(val project: Project, private val isBeforePro
7171
?.toList()
7272
?: listOf()
7373

74-
val apis: Set<File> = (runtimeClasspathJars + classesDirs + resourcesDir + servletJar).toSet()
74+
val apis: Set<File> = (dependencyConfigurationJars + classesDirs + resourcesDir + servletJar).toSet()
7575

7676
// eagerly check that all the files/folders exist, to avoid spamming the console later on
7777
// see https://github.com/vaadin/vaadin-gradle-plugin/issues/38 for more details
@@ -94,7 +94,7 @@ internal class GradlePluginAdapter(val project: Project, private val isBeforePro
9494
}
9595

9696
override fun getJarFiles(): MutableSet<File> {
97-
val jarFiles: Set<File> = project.configurations.runtimeClasspath.jars.toSet()
97+
val jarFiles: Set<File> = project.configurations.getByName(extension.dependencyScope!!).jars.toSet()
9898
return jarFiles.toMutableSet()
9999
}
100100

flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinFlowPluginExtension.kt

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2000-2022 Vaadin Ltd
2+
* Copyright 2000-2023 Vaadin Ltd
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package com.vaadin.gradle
1818
import com.vaadin.flow.server.Constants
1919
import com.vaadin.flow.server.InitParameters
2020
import com.vaadin.flow.server.frontend.FrontendTools
21-
import com.vaadin.flow.server.frontend.FrontendUtils
2221
import com.vaadin.flow.server.frontend.installer.NodeInstaller
2322
import groovy.lang.Closure
2423
import groovy.lang.DelegatesTo
@@ -190,6 +189,27 @@ public open class VaadinFlowPluginExtension(project: Project) {
190189

191190
public var classpathFilter: ClasspathFilter = ClasspathFilter()
192191

192+
/**
193+
* The name of the SourceSet to scan for Vaadin components - i.e. the classes that are annoated with
194+
* Vaadin annotations.
195+
*/
196+
public var sourceSetName : String = "main"
197+
198+
/**
199+
* The Gradle scope the Vaadin dependencies have been added to. Defaults to 'runtimeClasspath' if
200+
* no sourceSetName has been specified, or '<code>sourceSetName</code>RuntimeClasspath' if a non-main sourceset
201+
* has been set.
202+
*/
203+
public var dependencyScope : String? = null
204+
205+
/**
206+
* The Gradle task that the `vaadinPrepareFrontend` task must run before. The target task should run before
207+
* or be the task that copies the files from the resources directories of the specified SourceSet to the relevant
208+
* output directory for that SourceSet. Defaults to 'processResources' if no sourceSetName has been specified, or
209+
* 'process<code>SourceSetName</code>Resources' if a non-main sourceset has been specified.
210+
*/
211+
public var processResourcesTaskName : String? = null
212+
193213
public fun filterClasspath(@DelegatesTo(value = ClasspathFilter::class, strategy = Closure.DELEGATE_FIRST) block: Closure<*>? = null): ClasspathFilter {
194214
if (block != null) {
195215
block.delegate = classpathFilter
@@ -234,6 +254,24 @@ public open class VaadinFlowPluginExtension(project: Project) {
234254
if (useGlobalPnpmProperty != null) {
235255
useGlobalPnpm = useGlobalPnpmProperty
236256
}
257+
258+
// calculate processResourcesTaskName if not set by user
259+
if (processResourcesTaskName == null) {
260+
processResourcesTaskName = if (sourceSetName == "main") {
261+
"processResources"
262+
} else {
263+
"process${sourceSetName.replaceFirstChar(Char::titlecase)}Resources"
264+
}
265+
}
266+
267+
// calculate dependencyScope if not set by user
268+
if (dependencyScope == null) {
269+
dependencyScope = if (sourceSetName == "main") {
270+
"runtimeClasspath"
271+
} else {
272+
sourceSetName + "RuntimeClasspath"
273+
}
274+
}
237275
}
238276

239277
override fun toString(): String = "VaadinFlowPluginExtension(" +
@@ -258,13 +296,16 @@ public open class VaadinFlowPluginExtension(project: Project) {
258296
"generatedTsFolder=$generatedTsFolder, " +
259297
"nodeVersion=$nodeVersion, " +
260298
"nodeDownloadRoot=$nodeDownloadRoot, " +
261-
"nodeAutoUpdate=$nodeAutoUpdate" +
262-
"resourceOutputDirectory=$resourceOutputDirectory" +
263-
"postinstallPackages=$postinstallPackages" +
299+
"nodeAutoUpdate=$nodeAutoUpdate, " +
300+
"resourceOutputDirectory=$resourceOutputDirectory, " +
301+
"postinstallPackages=$postinstallPackages, " +
302+
"sourceSetName=$sourceSetName, " +
303+
"dependencyScope=$dependencyScope, " +
304+
"processResourcesTaskName=$processResourcesTaskName" +
264305
")"
265306
}
266307

267308
internal val Project.buildResourcesDir: File get() {
268309
val sourceSets: SourceSetContainer = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets
269-
return sourceSets.getByName("main").output.resourcesDir!!
310+
return sourceSets.getByName(extensions.getByType(VaadinFlowPluginExtension::class.java).sourceSetName).output.resourcesDir!!
270311
}

flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinPlugin.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2000-2022 Vaadin Ltd
2+
* Copyright 2000-2023 Vaadin Ltd
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,10 +49,10 @@ public class VaadinPlugin : Plugin<Project> {
4949

5050
// add a new source-set folder for generated stuff, by default `vaadin-generated`
5151
val sourceSets: SourceSetContainer = it.properties["sourceSets"] as SourceSetContainer
52-
sourceSets.getByName("main").resources.srcDirs(extension.resourceOutputDirectory)
52+
sourceSets.getByName(extension.sourceSetName).resources.srcDirs(extension.resourceOutputDirectory)
5353

5454
// auto-activate tasks: https://github.com/vaadin/vaadin-gradle-plugin/issues/48
55-
project.tasks.getByPath("processResources").dependsOn("vaadinPrepareFrontend")
55+
project.tasks.getByPath(extension.processResourcesTaskName!!).dependsOn("vaadinPrepareFrontend")
5656
if (extension.productionMode) {
5757
// this will also catch the War task since it extends from Jar
5858
project.tasks.withType(Jar::class.java) { task: Jar ->

flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinPrepareFrontendTask.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2000-2022 Vaadin Ltd
2+
* Copyright 2000-2023 Vaadin Ltd
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@ package com.vaadin.gradle
1818
import com.vaadin.flow.plugin.base.BuildFrontendUtil
1919
import com.vaadin.flow.server.frontend.FrontendTools
2020
import org.gradle.api.DefaultTask
21-
import org.gradle.api.Task
22-
import org.gradle.api.artifacts.ProjectDependency
2321
import org.gradle.api.tasks.TaskAction
2422

2523
/**
@@ -33,17 +31,18 @@ public open class VaadinPrepareFrontendTask : DefaultTask() {
3331
group = "Vaadin"
3432
description = "checks that node and npm tools are installed, copies frontend resources available inside `.jar` dependencies to `node_modules`, and creates or updates `package.json` and `webpack.config.json` files."
3533

34+
val extension: VaadinFlowPluginExtension = VaadinFlowPluginExtension.get(project)
3635
// Maven's task run in the LifecyclePhase.PROCESS_RESOURCES phase
3736

3837
// the processResources copies stuff from build/vaadin-generated
3938
// (which is populated by this task) and therefore must run after this task.
40-
project.tasks.getByName("processResources").mustRunAfter("vaadinPrepareFrontend")
39+
project.tasks.getByName(extension.processResourcesTaskName!!).mustRunAfter("vaadinPrepareFrontend")
4140

4241
// make sure all dependent projects have finished building their jars, otherwise
4342
// the Vaadin classpath scanning will not work properly. See
4443
// https://github.com/vaadin/vaadin-gradle-plugin/issues/38
4544
// for more details.
46-
dependsOn(project.configurations.runtimeClasspath.jars)
45+
dependsOn(project.configurations.getByName(extension.dependencyScope!!).jars)
4746
}
4847

4948
@TaskAction

flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinUtils.kt

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2000-2022 Vaadin Ltd
2+
* Copyright 2000-2023 Vaadin Ltd
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@ package com.vaadin.gradle
1717

1818

1919
import org.gradle.api.artifacts.Configuration
20-
import org.gradle.api.artifacts.ConfigurationContainer
2120
import org.gradle.api.file.FileCollection
2221
import com.vaadin.flow.function.SerializableSupplier
2322
import com.vaadin.flow.server.frontend.FrontendTools
@@ -74,12 +73,6 @@ internal fun VaadinFlowPluginExtension.createFrontendTools(): FrontendTools {
7473
return FrontendTools(settings)
7574
}
7675

77-
/**
78-
* Returns the "runtimeClasspath" file collection.
79-
*/
80-
internal val ConfigurationContainer.runtimeClasspath: Configuration
81-
get() = getByName("runtimeClasspath")
82-
8376
/**
8477
* Returns only jar files from given file collection.
8578
*/

0 commit comments

Comments
 (0)