From 36f362e6ce865323012ba0529b8ba46988b81869 Mon Sep 17 00:00:00 2001 From: Sergii Kondrusiev Date: Mon, 12 Aug 2024 12:05:08 +0300 Subject: [PATCH 1/2] Worker API implementation with respect of JVM toolchains --- .../vaadin/gradle/VaadinBuildFrontendTask.kt | 116 +++++++++++------- .../gradle/VaadinPrepareFrontendTask.kt | 41 +++++-- .../gradle/worker/ClassFinderAdapter.kt | 29 +++++ .../worker/ClassFinderClasspathFactory.kt | 61 +++++++++ .../worker/DebugFlagProcessConfigurer.kt | 21 ++++ .../vaadin/gradle/worker/FactoryMethods.kt | 73 +++++++++++ .../kotlin/com/vaadin/gradle/worker/Flags.kt | 26 ++++ .../com/vaadin/gradle/worker/FlagsFactory.kt | 44 +++++++ .../gradle/worker/GradleWorkerApiAdapter.kt | 74 +++++++++++ ...itEnvironmentVariablesProcessConfigurer.kt | 17 +++ .../gradle/worker/JavaExecutionService.kt | 27 ++++ .../gradle/worker/JavaToolchainSpecFactory.kt | 29 +++++ .../com/vaadin/gradle/worker/Locations.kt | 24 ++++ .../vaadin/gradle/worker/LocationsFactory.kt | 79 ++++++++++++ .../com/vaadin/gradle/worker/LoggerAdapter.kt | 41 +++++++ .../worker/ProcessWorkerSpecConfigurer.kt | 8 ++ .../com/vaadin/gradle/worker/Strings.kt | 10 ++ .../vaadin/gradle/worker/StringsFactory.kt | 22 ++++ ...oolchainJavaExecutableProcessConfigurer.kt | 21 ++++ .../gradle/worker/VaadinTaskConfiguration.kt | 11 ++ .../worker/VaadinTaskConfigurationFactory.kt | 23 ++++ .../worker/VaadinWorkActionParameter.kt | 8 ++ 22 files changed, 752 insertions(+), 53 deletions(-) create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ClassFinderAdapter.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ClassFinderClasspathFactory.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/DebugFlagProcessConfigurer.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/FactoryMethods.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Flags.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/FlagsFactory.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/GradleWorkerApiAdapter.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/InheritEnvironmentVariablesProcessConfigurer.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/JavaExecutionService.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/JavaToolchainSpecFactory.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Locations.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/LocationsFactory.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/LoggerAdapter.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ProcessWorkerSpecConfigurer.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Strings.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/StringsFactory.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ToolchainJavaExecutableProcessConfigurer.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinTaskConfiguration.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinTaskConfigurationFactory.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinWorkActionParameter.kt diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinBuildFrontendTask.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinBuildFrontendTask.kt index aff895f7359..cf7c757f4e6 100644 --- a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinBuildFrontendTask.kt +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinBuildFrontendTask.kt @@ -16,14 +16,25 @@ package com.vaadin.gradle import com.vaadin.flow.plugin.base.BuildFrontendUtil +import com.vaadin.flow.plugin.base.PluginAdapterBuild import com.vaadin.flow.server.Constants import com.vaadin.flow.server.frontend.BundleValidationUtil import com.vaadin.flow.server.frontend.FrontendUtils import com.vaadin.flow.server.frontend.TaskCleanFrontendFiles +import com.vaadin.gradle.worker.JavaExecutionService +import com.vaadin.gradle.worker.GradleWorkerApiAdapter +import com.vaadin.gradle.worker.VaadinTaskConfigurationFactory +import com.vaadin.gradle.worker.VaadinWorkActionParameter +import com.vaadin.gradle.worker.from import com.vaadin.pro.licensechecker.LicenseChecker import org.gradle.api.DefaultTask +import org.gradle.api.logging.Logger +import org.gradle.api.logging.Logging +import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.bundling.Jar +import org.gradle.workers.WorkAction +import javax.inject.Inject /** * Task that builds the frontend bundle. @@ -40,9 +51,13 @@ import org.gradle.api.tasks.bundling.Jar * * Update [FrontendUtils.WEBPACK_CONFIG] file. * */ -public open class VaadinBuildFrontendTask : DefaultTask() { - private val config: PluginEffectiveConfiguration = - PluginEffectiveConfiguration.get(project) +public open class VaadinBuildFrontendTask @Inject constructor(objectFactory: ObjectFactory) : DefaultTask() { + + private val config: PluginEffectiveConfiguration = PluginEffectiveConfiguration.get(project) + + private val javaExecutionService = JavaExecutionService.from(objectFactory) + + private val taskConfigurationFactory = VaadinTaskConfigurationFactory.from(project, config, false) init { group = "Vaadin" @@ -67,54 +82,67 @@ public open class VaadinBuildFrontendTask : DefaultTask() { @TaskAction public fun vaadinBuildFrontend() { logger.info("Running the vaadinBuildFrontend task with effective configuration $config") - val adapter = GradlePluginAdapter(project, config, false) - // sanity check - val tokenFile = BuildFrontendUtil.getTokenFile(adapter) - check(tokenFile.exists()) { "token file $tokenFile doesn't exist!" } + val taskConfiguration = taskConfigurationFactory.get() - val cleanTask = TaskCleanFrontendFiles(config.npmFolder.get(), + javaExecutionService.submit(VaadinBuildFrontendWorkAction::class.java) { + it.getVaadinTaskConfiguration().set(taskConfiguration) + } + } + + public abstract class VaadinBuildFrontendWorkAction : WorkAction { + private val logger: Logger = Logging.getLogger(VaadinBuildFrontendWorkAction::class.java) + + override fun execute() { + val taskConfiguration = parameters.getVaadinTaskConfiguration().get() + val adapter = GradleWorkerApiAdapter.from(taskConfiguration, logger) + + // sanity check + val tokenFile = BuildFrontendUtil.getTokenFile(adapter) + check(tokenFile.exists()) { "token file $tokenFile doesn't exist!" } + + val cleanTask = TaskCleanFrontendFiles(taskConfiguration.locations.npmFolder, BuildFrontendUtil.getGeneratedFrontendDirectory(adapter), adapter.classFinder - ) - BuildFrontendUtil.runNodeUpdater(adapter) + ) + BuildFrontendUtil.runNodeUpdater(adapter) - if (adapter.generateBundle() && BundleValidationUtil.needsBundleBuild - (adapter.servletResourceOutputDirectory())) { - BuildFrontendUtil.runFrontendBuild(adapter) - if (cleanFrontendFiles()) { - cleanTask.execute() + if (adapter.generateBundle() && BundleValidationUtil.needsBundleBuild + (adapter.servletResourceOutputDirectory())) { + BuildFrontendUtil.runFrontendBuild(adapter) + if (cleanFrontendFiles(adapter, taskConfiguration.flags.cleanFrontendFiles)) { + cleanTask.execute() + } } + LicenseChecker.setStrictOffline(true) + val licenseRequired = BuildFrontendUtil.validateLicenses(adapter) + + BuildFrontendUtil.updateBuildFile(adapter, licenseRequired) } - LicenseChecker.setStrictOffline(true) - val licenseRequired = BuildFrontendUtil.validateLicenses(adapter) - - BuildFrontendUtil.updateBuildFile(adapter, licenseRequired) - } - /** - * Define if frontend files generated by bundle build should be cleaned or - * not. - * - * The targeted frontend files are files that do not exist when - * build-frontend target is executed. - * - * Extending mojo can override this method to return false so that any - * frontend files created for the bundle build are not removed. - * - * @return `true` to remove created files, `false` to keep the files - */ - protected open fun cleanFrontendFiles(): Boolean { - val adapter = GradlePluginAdapter(project, config, false) - if (FrontendUtils.isHillaUsed(BuildFrontendUtil.getGeneratedFrontendDirectory(adapter), - adapter.classFinder)) { - /* - * Override this to not clean generated frontend files after the - * build. For Hilla, the generated files can still be useful for - * developers after the build. For example, a developer can use - * {@code vite.generated.ts} to run tests with vitest in CI. - */ - return false + /** + * Define if frontend files generated by bundle build should be cleaned or + * not. + * + * The targeted frontend files are files that do not exist when + * build-frontend target is executed. + * + * Extending mojo can override this method to return false so that any + * frontend files created for the bundle build are not removed. + * + * @return `true` to remove created files, `false` to keep the files + */ + private fun cleanFrontendFiles(adapter: PluginAdapterBuild, cleanFrontendFiles: Boolean): Boolean { + if (FrontendUtils.isHillaUsed(BuildFrontendUtil.getGeneratedFrontendDirectory(adapter), + adapter.classFinder)) { + /* + * Override this to not clean generated frontend files after the + * build. For Hilla, the generated files can still be useful for + * developers after the build. For example, a developer can use + * {@code vite.generated.ts} to run tests with vitest in CI. + */ + return false + } + return cleanFrontendFiles } - return config.cleanFrontendFiles.get() } } diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinPrepareFrontendTask.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinPrepareFrontendTask.kt index 91f42c5ff69..b85b2d40d74 100644 --- a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinPrepareFrontendTask.kt +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/VaadinPrepareFrontendTask.kt @@ -16,13 +16,17 @@ package com.vaadin.gradle import com.vaadin.flow.plugin.base.BuildFrontendUtil -import groovy.lang.Closure +import com.vaadin.gradle.worker.* import org.gradle.api.DefaultTask -import org.gradle.api.Task +import org.gradle.api.logging.Logger +import org.gradle.api.logging.Logging +import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Nested import org.gradle.api.tasks.TaskAction -import kotlin.math.log +import org.gradle.workers.WorkAction +import javax.inject.Inject + /** * This task checks that node and npm tools are installed, copies frontend @@ -34,10 +38,14 @@ import kotlin.math.log * files) are up-to-date and have the same values as for previous build. */ @CacheableTask -public open class VaadinPrepareFrontendTask : DefaultTask() { +public open class VaadinPrepareFrontendTask @Inject constructor(objectFactory: ObjectFactory) : DefaultTask() { private val config = PluginEffectiveConfiguration.get(project) + private val javaExecutionService = JavaExecutionService.from(objectFactory) + + private val taskConfigurationFactory = VaadinTaskConfigurationFactory.from(project, config, true) + /** * Defines an object containing all the inputs of this task. */ @@ -59,12 +67,27 @@ public open class VaadinPrepareFrontendTask : DefaultTask() { @TaskAction public fun vaadinPrepareFrontend() { // Remove Frontend/generated folder to get clean files copied/generated - val adapter = GradlePluginAdapter(project, config, true) logger.debug("Running the vaadinPrepareFrontend task with effective configuration $config") - val tokenFile = BuildFrontendUtil.propagateBuildInfo(adapter) + + val taskConfiguration = taskConfigurationFactory.get() + + javaExecutionService.submit(VaadinPrepareFrontendWorkAction::class.java) { + it.getVaadinTaskConfiguration().set(taskConfiguration) + } + } + + public abstract class VaadinPrepareFrontendWorkAction : WorkAction { + private val logger: Logger = Logging.getLogger(VaadinPrepareFrontendWorkAction::class.java) + + override fun execute() { + val taskConfiguration = parameters.getVaadinTaskConfiguration().get() + val adapter = GradleWorkerApiAdapter.from(taskConfiguration, logger) + + val tokenFile = BuildFrontendUtil.propagateBuildInfo(adapter) - logger.info("Generated token file $tokenFile") - check(tokenFile.exists()) { "token file $tokenFile doesn't exist!" } - BuildFrontendUtil.prepareFrontend(adapter) + logger.info("Generated token file $tokenFile") + check(tokenFile.exists()) { "token file $tokenFile doesn't exist!" } + BuildFrontendUtil.prepareFrontend(adapter) + } } } diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ClassFinderAdapter.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ClassFinderAdapter.kt new file mode 100644 index 00000000000..2e6c1e3b3d8 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ClassFinderAdapter.kt @@ -0,0 +1,29 @@ +package com.vaadin.gradle.worker + +import com.vaadin.flow.plugin.base.BuildFrontendUtil +import com.vaadin.flow.server.frontend.scanner.ClassFinder +import com.vaadin.gradle.toPrettyFormat +import org.gradle.api.logging.Logger +import java.io.File + +internal class ClassFinderAdapter( + private val classFinderClasspath: Set, + private val logger: Logger, + private val projectName: String +) { + + internal fun getClassFinder(): ClassFinder { + val classFinder = BuildFrontendUtil.getClassFinder(classFinderClasspath.map { it.absolutePath }) + + // sanity check that the project has flow-server.jar as a dependency + try { + classFinder.loadClass("com.vaadin.flow.server.webcomponent.WebComponentModulesWriter") + } catch (e: ClassNotFoundException) { + throw RuntimeException("Failed to find classes from flow-server.jar. The project '${projectName}' needs to have a dependency on flow-server.jar") + } + + logger.info("Passing this classpath to NodeTasks.Builder: ${classFinderClasspath.toPrettyFormat()}") + + return classFinder + } +} diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ClassFinderClasspathFactory.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ClassFinderClasspathFactory.kt new file mode 100644 index 00000000000..0996e01ef2c --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ClassFinderClasspathFactory.kt @@ -0,0 +1,61 @@ +package com.vaadin.gradle.worker + +import com.vaadin.gradle.PluginEffectiveConfiguration +import com.vaadin.gradle.getSourceSet +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ResolvedArtifact +import java.io.File +import java.util.function.Supplier +import javax.inject.Inject + +private val servletApiJarRegex = Regex(".*(/|\\\\)(portlet-api|javax\\.servlet-api)-.+jar$") + +internal class ClassFinderClasspathFactory @Inject constructor( + private val project: Project, + private val config: PluginEffectiveConfiguration, +) : Supplier> { + + override fun get(): Set { + val dependencyConfiguration: Configuration? = project.configurations.findByName(config.dependencyScope.get()) + val dependencyConfigurationJars: List = if (dependencyConfiguration != null) { + var artifacts: List = + dependencyConfiguration.resolvedConfiguration.resolvedArtifacts.toList() + + // Detect local filesystem dependencies that are not resolved as artifacts + // They will be added to the filtered artifacts list + val filesystemDependencies = + dependencyConfiguration.resolvedConfiguration.files.minus(artifacts.map { it.file }.toSet()) + + val artifactFilter = config.classpathFilter.toPredicate() + artifacts = artifacts.filter { artifactFilter.test(it.moduleVersion.id.module) } + + artifacts.map { it.file }.plus(filesystemDependencies) + } else listOf() + + // we need to also analyze the project's classes + val classesDirs: List = project.getSourceSet(config.sourceSetName.get()).output.classesDirs + .toList() + .filter { it.exists() } + + val resourcesDir: List = + listOfNotNull(project.getSourceSet(config.sourceSetName.get()).output.resourcesDir) + .filter { it.exists() } + + // for Spring Boot project there is no "providedCompile" scope: the WAR plugin brings that in. + val providedDeps: Configuration? = project.configurations.findByName("providedCompile") + val servletJar: List = providedDeps + ?.filter { it.absolutePath.matches(servletApiJarRegex) } + ?.toList() + ?: listOf() + + val apis: Set = (dependencyConfigurationJars + classesDirs + resourcesDir + servletJar).toSet() + + // eagerly check that all the files/folders exist, to avoid spamming the console later on + // see https://github.com/vaadin/vaadin-gradle-plugin/issues/38 for more details + apis.forEach { + check(it.exists()) { "$it doesn't exist" } + } + return apis + } +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/DebugFlagProcessConfigurer.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/DebugFlagProcessConfigurer.kt new file mode 100644 index 00000000000..ded5f1bb9bc --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/DebugFlagProcessConfigurer.kt @@ -0,0 +1,21 @@ +package com.vaadin.gradle.worker + +import org.gradle.workers.ProcessWorkerSpec +import javax.inject.Inject + +private const val COM_VAADIN_GRADLE_WORKER_DEBUG = "com.vaadin.gradle.worker.debug" + +internal open class DebugFlagProcessConfigurer @Inject constructor() : ProcessWorkerSpecConfigurer { + + override fun execute(spec: ProcessWorkerSpec) { + if (System.getProperty(COM_VAADIN_GRADLE_WORKER_DEBUG)?.toBoolean() == true) { + spec.forkOptions.debugOptions { + it.enabled.set(true) + it.server.set(true) + it.suspend.set(true) + } + } + } + + companion object +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/FactoryMethods.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/FactoryMethods.kt new file mode 100644 index 00000000000..7c3b0132935 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/FactoryMethods.kt @@ -0,0 +1,73 @@ +package com.vaadin.gradle.worker + +import com.vaadin.gradle.PluginEffectiveConfiguration +import org.gradle.api.Project +import org.gradle.api.logging.Logger +import org.gradle.api.model.ObjectFactory +import org.gradle.workers.ProcessWorkerSpec + +internal fun GradleWorkerApiAdapter.Companion.from( + taskConfiguration: VaadinTaskConfiguration, + logger: Logger +): GradleWorkerApiAdapter { + + return GradleWorkerApiAdapter( + taskConfiguration, + ClassFinderAdapter(taskConfiguration.classFinderClasspath, logger, taskConfiguration.strings.projectName), + LoggerAdapter(logger), + ) +} + +internal fun VaadinTaskConfigurationFactory.Companion.from( + project: Project, + config: PluginEffectiveConfiguration, + isBeforeProcessResources: Boolean +): VaadinTaskConfigurationFactory { + + return VaadinTaskConfigurationFactory( + FlagsFactory(project, config), + LocationsFactory(project, config, isBeforeProcessResources), + StringsFactory(project, config), + ClassFinderClasspathFactory(project, config), + ) +} + + +internal fun JavaExecutionService.Companion.from(objectFactory: ObjectFactory): JavaExecutionService { + return objectFactory.newInstance( + JavaExecutionService::class.java, + ProcessWorkerSpecConfigurer.chainFrom( + DebugFlagProcessConfigurer.from(objectFactory), + InheritEnvironmentVariablesProcessConfigurer.from(objectFactory), + ToolchainJavaExecutableProcessConfigurer.from(objectFactory), + ) + ) +} + + +internal fun ProcessWorkerSpecConfigurer.Companion.chainFrom(vararg configurer: ProcessWorkerSpecConfigurer): ProcessWorkerSpecConfigurer { + return object : ProcessWorkerSpecConfigurer { + override fun execute(spec: ProcessWorkerSpec) { + configurer.forEach { it.execute(spec) } + } + } +} + +internal fun InheritEnvironmentVariablesProcessConfigurer.Companion.from(objectFactory: ObjectFactory): InheritEnvironmentVariablesProcessConfigurer { + return objectFactory.newInstance(InheritEnvironmentVariablesProcessConfigurer::class.java) +} + +internal fun ToolchainJavaExecutableProcessConfigurer.Companion.from(objectFactory: ObjectFactory): ToolchainJavaExecutableProcessConfigurer { + return objectFactory.newInstance( + ToolchainJavaExecutableProcessConfigurer::class.java, + JavaToolchainSpecFactory.from(objectFactory) + ) +} + +internal fun DebugFlagProcessConfigurer.Companion.from(objectFactory: ObjectFactory): DebugFlagProcessConfigurer { + return objectFactory.newInstance(DebugFlagProcessConfigurer::class.java) +} + +internal fun JavaToolchainSpecFactory.Companion.from(objectFactory: ObjectFactory): JavaToolchainSpecFactory { + return objectFactory.newInstance(JavaToolchainSpecFactory::class.java) +} diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Flags.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Flags.kt new file mode 100644 index 00000000000..11643326282 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Flags.kt @@ -0,0 +1,26 @@ +package com.vaadin.gradle.worker + +import java.io.Serializable + +public data class Flags( + val eagerServerLoad: Boolean, + val jarProject: Boolean, + val debugEnabled: Boolean, + val nodeAutoUpdate: Boolean, + val pnpmEnable: Boolean, + val bunEnable: Boolean, + val useGlobalPnpm: Boolean, + val requireHomeNodeExec: Boolean, + val frontendHotdeploy: Boolean, + val skipDevBundleBuild: Boolean, + val prepareFrontendCacheDisabled: Boolean, + val reactEnabled: Boolean, + val generateBundle: Boolean, + val generateEmbeddableWebComponents: Boolean, + val optimizeBundle: Boolean, + val runNpmInstall: Boolean, + val ciBuild: Boolean, + val forceProductionBuild: Boolean, + val compressBundle: Boolean, + val cleanFrontendFiles: Boolean, +) : Serializable \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/FlagsFactory.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/FlagsFactory.kt new file mode 100644 index 00000000000..3ce05a1aaa1 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/FlagsFactory.kt @@ -0,0 +1,44 @@ +package com.vaadin.gradle.worker + +import com.vaadin.gradle.PluginEffectiveConfiguration +import org.gradle.api.Project +import org.gradle.api.tasks.bundling.War +import java.util.function.Supplier +import javax.inject.Inject + +internal class FlagsFactory @Inject constructor( + private val project: Project, + private val config: PluginEffectiveConfiguration, +) : Supplier { + + override fun get(): Flags { + with(config) { + return Flags( + eagerServerLoad = eagerServerLoad.get(), + jarProject = isProjectHasWarTask(), + debugEnabled = true, + nodeAutoUpdate = nodeAutoUpdate.get(), + pnpmEnable = pnpmEnable.get(), + bunEnable = bunEnable.get(), + useGlobalPnpm = useGlobalPnpm.get(), + requireHomeNodeExec = requireHomeNodeExec.get(), + frontendHotdeploy = frontendHotdeploy.get(), + skipDevBundleBuild = skipDevBundleBuild.get(), + prepareFrontendCacheDisabled = alwaysExecutePrepareFrontend.get(), + reactEnabled = reactEnable.get(), + generateBundle = generateBundle.get(), + generateEmbeddableWebComponents = generateEmbeddableWebComponents.get(), + optimizeBundle = optimizeBundle.get(), + runNpmInstall = runNpmInstall.get(), + ciBuild = ciBuild.get(), + forceProductionBuild = forceProductionBuild.get(), + compressBundle = true, + cleanFrontendFiles = cleanFrontendFiles.get() + ) + } + } + + private fun isProjectHasWarTask(): Boolean { + return project.tasks.withType(War::class.java).isEmpty() + } +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/GradleWorkerApiAdapter.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/GradleWorkerApiAdapter.kt new file mode 100644 index 00000000000..8f48490fd58 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/GradleWorkerApiAdapter.kt @@ -0,0 +1,74 @@ +package com.vaadin.gradle.worker + +import com.vaadin.flow.plugin.base.PluginAdapterBuild +import com.vaadin.flow.server.frontend.scanner.ClassFinder +import java.io.File +import java.net.URI +import java.nio.file.Path + +internal class GradleWorkerApiAdapter( + taskConfiguration: VaadinTaskConfiguration, + private val classFinderAdapter: ClassFinderAdapter, + private val loggerAdapter: LoggerAdapter, +) : PluginAdapterBuild { + + private val flags = taskConfiguration.flags + private val locations = taskConfiguration.locations + private val strings = taskConfiguration.strings + + // flags + override fun eagerServerLoad(): Boolean = flags.eagerServerLoad + override fun isJarProject(): Boolean = flags.jarProject + override fun isDebugEnabled(): Boolean = flags.debugEnabled + override fun nodeAutoUpdate(): Boolean = flags.nodeAutoUpdate + override fun pnpmEnable(): Boolean = flags.pnpmEnable + override fun bunEnable(): Boolean = flags.bunEnable + override fun useGlobalPnpm(): Boolean = flags.useGlobalPnpm + override fun requireHomeNodeExec(): Boolean = flags.requireHomeNodeExec + override fun isFrontendHotdeploy(): Boolean = flags.frontendHotdeploy + override fun skipDevBundleBuild(): Boolean = flags.skipDevBundleBuild + override fun isPrepareFrontendCacheDisabled(): Boolean = flags.prepareFrontendCacheDisabled + override fun isReactEnabled(): Boolean = flags.reactEnabled + override fun generateBundle(): Boolean = flags.generateBundle + override fun generateEmbeddableWebComponents(): Boolean = flags.generateEmbeddableWebComponents + override fun optimizeBundle(): Boolean = flags.optimizeBundle + override fun runNpmInstall(): Boolean = flags.runNpmInstall + override fun ciBuild(): Boolean = flags.ciBuild + override fun forceProductionBuild(): Boolean = flags.forceProductionBuild + override fun compressBundle(): Boolean = flags.compressBundle + + // locations + override fun applicationProperties(): File = locations.applicationProperties + override fun frontendDirectory(): File = locations.frontendDirectory + override fun generatedTsFolder(): File = locations.generatedTsFolder + override fun getJarFiles(): Set = locations.jarFiles + override fun javaSourceFolder(): File = locations.javaSourceFolder + override fun javaResourceFolder(): File = locations.javaResourceFolder + override fun npmFolder(): File = locations.npmFolder + override fun openApiJsonFile(): File = locations.openApiJsonFile + override fun projectBaseDirectory(): Path = locations.projectBaseDirectory.toPath() + override fun servletResourceOutputDirectory(): File = locations.servletResourceOutputDirectory + override fun webpackOutputDirectory(): File = locations.webpackOutputDirectory + override fun buildFolder(): String = locations.buildFolder + override fun frontendResourcesDirectory(): File = locations.frontendResourcesDirectory + override fun nodeDownloadRoot(): URI = locations.nodeDownloadRoot + override fun postinstallPackages(): List = locations.postinstallPackages + + // strings + override fun nodeVersion(): String = strings.nodeVersion + override fun applicationIdentifier(): String = strings.applicationIdentifier + + // classLoading + override fun getClassFinder(): ClassFinder = classFinderAdapter.getClassFinder() + + // logging + override fun logDebug(p0: CharSequence?): Unit = loggerAdapter.logDebug(p0) + override fun logDebug(p0: CharSequence?, p1: Throwable?): Unit = loggerAdapter.logDebug(p0, p1) + override fun logInfo(p0: CharSequence?): Unit = loggerAdapter.logInfo(p0) + override fun logWarn(p0: CharSequence?): Unit = loggerAdapter.logWarn(p0) + override fun logWarn(p0: CharSequence?, p1: Throwable?): Unit = loggerAdapter.logWarn(p0, p1) + override fun logError(p0: CharSequence?): Unit = loggerAdapter.logError(p0) + override fun logError(p0: CharSequence?, p1: Throwable?): Unit = loggerAdapter.logError(p0, p1) + + public companion object +} diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/InheritEnvironmentVariablesProcessConfigurer.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/InheritEnvironmentVariablesProcessConfigurer.kt new file mode 100644 index 00000000000..798c264dd42 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/InheritEnvironmentVariablesProcessConfigurer.kt @@ -0,0 +1,17 @@ +package com.vaadin.gradle.worker + +import org.gradle.workers.ProcessWorkerSpec +import javax.inject.Inject + +internal open class InheritEnvironmentVariablesProcessConfigurer @Inject constructor() : ProcessWorkerSpecConfigurer { + + override fun execute(spec: ProcessWorkerSpec) { + spec.forkOptions.environment(getEnvironment()) + } + + private fun getEnvironment(): MutableMap? { + return System.getenv() + } + + companion object +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/JavaExecutionService.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/JavaExecutionService.kt new file mode 100644 index 00000000000..9d9a872e2c7 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/JavaExecutionService.kt @@ -0,0 +1,27 @@ +package com.vaadin.gradle.worker + +import com.vaadin.gradle.worker.ProcessWorkerSpecConfigurer +import org.gradle.api.Action +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.gradle.workers.WorkerExecutor +import javax.inject.Inject + +/** + * This service executes worker actions in an isolated JVM with respect of Gradle JVM toolchains configuration. + */ +internal open class JavaExecutionService @Inject constructor( + executor: WorkerExecutor, + processConfigurer: ProcessWorkerSpecConfigurer, +) { + private val queue = executor.processIsolation(processConfigurer) + + /** + * Submits a given work action for an execution. + */ + internal fun submit(actionClass: Class>, parameterAction: Action) { + queue.submit(actionClass, parameterAction) + } + + companion object +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/JavaToolchainSpecFactory.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/JavaToolchainSpecFactory.kt new file mode 100644 index 00000000000..4c1bb3cdf11 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/JavaToolchainSpecFactory.kt @@ -0,0 +1,29 @@ +package com.vaadin.gradle.worker + +import org.gradle.api.Action +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.jvm.toolchain.JavaToolchainSpec +import java.util.function.Supplier +import javax.inject.Inject + +internal open class JavaToolchainSpecFactory @Inject constructor(private val project: Project) : + Supplier> { + + override fun get(): Action { + val javaPluginExtension = project.extensions.findByType(JavaPluginExtension::class.java) + val spec = javaPluginExtension?.toolchain + + return Action { + if (spec != null) { + it.languageVersion.set(spec.languageVersion) + it.implementation.set(spec.implementation) + it.vendor.set(spec.vendor) + } else { + //Gradle's default spec configures nothing. + } + } + } + + companion object +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Locations.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Locations.kt new file mode 100644 index 00000000000..5bb8186233d --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Locations.kt @@ -0,0 +1,24 @@ +package com.vaadin.gradle.worker + +import java.io.File +import java.io.Serializable +import java.net.URI + +public data class Locations( + val applicationProperties: File, + val frontendDirectory: File, + val generatedTsFolder: File, + val jarFiles: Set, + val javaSourceFolder: File, + val javaResourceFolder: File, + val npmFolder: File, + val openApiJsonFile: File, + val projectBaseDirectory: File, + val servletResourceOutputDirectory: File, + val webpackOutputDirectory: File, + val buildFolder: String, + val frontendResourcesDirectory: File, + val nodeDownloadRoot: URI, + val postinstallPackages: List, +) : Serializable + diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/LocationsFactory.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/LocationsFactory.kt new file mode 100644 index 00000000000..157ef2fc9db --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/LocationsFactory.kt @@ -0,0 +1,79 @@ +package com.vaadin.gradle.worker + +import com.vaadin.flow.server.Constants +import com.vaadin.gradle.PluginEffectiveConfiguration +import com.vaadin.gradle.getBuildResourcesDir +import com.vaadin.gradle.jars +import org.gradle.api.Project +import org.gradle.api.provider.Property +import java.io.File +import java.net.URI +import java.util.function.Supplier +import javax.inject.Inject + +internal class LocationsFactory @Inject constructor( + private val project: Project, + private val config: PluginEffectiveConfiguration, + private val isBeforeProcessResources: Boolean +) : Supplier { + + override fun get(): Locations { + with(config) { + return Locations( + applicationProperties = applicationProperties.get(), + frontendDirectory = frontendDirectory.get(), + generatedTsFolder = generatedTsFolder.get(), + jarFiles = getJarFiles(), + javaSourceFolder = javaSourceFolder.get(), + javaResourceFolder = javaResourceFolder.get(), + npmFolder = npmFolder.get(), + openApiJsonFile = openApiJsonFile.get(), + projectBaseDirectory = getProjectBaseDir(), + servletResourceOutputDirectory = getServletResourceOutputDirectory(), + webpackOutputDirectory = webpackOutputDirectory.get(), + buildFolder = getBuildFolder(), + frontendResourcesDirectory = frontendResourcesDirectory.get(), + nodeDownloadRoot = getNodeDownloadRoot(nodeDownloadRoot), + postinstallPackages = postinstallPackages.get(), + ) + } + } + + + private fun getNodeDownloadRoot(nodeDownloadRoot: Property): URI = URI.create(nodeDownloadRoot.get()) + + private fun getProjectBaseDir(): File { + return project.projectDir + } + + + private fun getJarFiles(): Set { + val jarFiles: Set = project.configurations.getByName(config.dependencyScope.get()).jars.toSet() + return jarFiles.toMutableSet() + } + + private fun getServletResourceOutputDirectory(): File { + // when running a task which runs before processResources, we need to + // generate stuff to build/vaadin-generated. + // + // However, after processResources is done, anything generated into + // build/vaadin-generated would simply be ignored. In such case we therefore + // need to generate stuff directly to build/resources/main. + if (isBeforeProcessResources) { + return File( + config.resourceOutputDirectory.get(), + Constants.VAADIN_SERVLET_RESOURCES + ) + } + return File(project.getBuildResourcesDir(config.sourceSetName.get()), Constants.VAADIN_SERVLET_RESOURCES) + } + + private fun getBuildFolder(): String { + val projectBuildDir = config.projectBuildDir.get() + if (projectBuildDir.startsWith(project.projectDir.toString())) { + return File(projectBuildDir).relativeTo(project.projectDir).toString() + } + return projectBuildDir + } + +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/LoggerAdapter.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/LoggerAdapter.kt new file mode 100644 index 00000000000..f8bb76faee7 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/LoggerAdapter.kt @@ -0,0 +1,41 @@ +package com.vaadin.gradle.worker + +import org.gradle.api.logging.Logger + +internal class LoggerAdapter(private val logger: Logger) { + fun logDebug(p0: CharSequence?) { + logger.debug(asMessage(p0)) + } + + fun logDebug(p0: CharSequence?, p1: Throwable?) { + logger.debug(asMessage(p0), asThrowable(p1)) + } + + fun logInfo(p0: CharSequence?) { + logger.info(asMessage(p0)) + } + + fun logWarn(p0: CharSequence?) { + logger.warn(asMessage(p0)) + } + + fun logWarn(p0: CharSequence?, p1: Throwable?) { + logger.warn(asMessage(p0), asThrowable(p1)) + } + + fun logError(p0: CharSequence?) { + logger.error(asMessage(p0)) + } + + fun logError(p0: CharSequence?, p1: Throwable?) { + logger.error(asMessage(p0), asThrowable(p1)) + } + + private fun asMessage(p0: CharSequence?): String { + return p0!!.toString() + } + + private fun asThrowable(p1: Throwable?): Throwable { + return p1!! + } +} diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ProcessWorkerSpecConfigurer.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ProcessWorkerSpecConfigurer.kt new file mode 100644 index 00000000000..8404ac6fedf --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ProcessWorkerSpecConfigurer.kt @@ -0,0 +1,8 @@ +package com.vaadin.gradle.worker + +import org.gradle.api.Action +import org.gradle.workers.ProcessWorkerSpec + +internal interface ProcessWorkerSpecConfigurer : Action { + companion object +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Strings.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Strings.kt new file mode 100644 index 00000000000..b1e792f1926 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/Strings.kt @@ -0,0 +1,10 @@ +package com.vaadin.gradle.worker + +import java.io.Serializable + +public data class Strings( + val applicationIdentifier: String, + val nodeVersion: String, + val projectName: String, +) : Serializable + diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/StringsFactory.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/StringsFactory.kt new file mode 100644 index 00000000000..485e34527d0 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/StringsFactory.kt @@ -0,0 +1,22 @@ +package com.vaadin.gradle.worker + +import com.vaadin.gradle.PluginEffectiveConfiguration +import org.gradle.api.Project +import java.util.function.Supplier +import javax.inject.Inject + +internal class StringsFactory @Inject constructor( + private val project: Project, + private val config: PluginEffectiveConfiguration, +) : Supplier { + + override fun get(): Strings { + with(config) { + return Strings( + applicationIdentifier = applicationIdentifier.get(), + nodeVersion = nodeVersion.get(), + projectName = project.name + ) + } + } +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ToolchainJavaExecutableProcessConfigurer.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ToolchainJavaExecutableProcessConfigurer.kt new file mode 100644 index 00000000000..291aa8064f1 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/ToolchainJavaExecutableProcessConfigurer.kt @@ -0,0 +1,21 @@ +package com.vaadin.gradle.worker + +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.workers.ProcessWorkerSpec +import javax.inject.Inject + +internal open class ToolchainJavaExecutableProcessConfigurer @Inject constructor( + private val toolchainService: JavaToolchainService, + private val toolchainSpecSupplier: JavaToolchainSpecFactory, +) : ProcessWorkerSpecConfigurer { + + override fun execute(spec: ProcessWorkerSpec) { + val javaExeAbsolutePath = toolchainService.launcherFor(toolchainSpecSupplier.get()) + .map { it.executablePath.asFile.absolutePath } + .get() + + spec.forkOptions.executable(javaExeAbsolutePath) + } + + internal companion object +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinTaskConfiguration.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinTaskConfiguration.kt new file mode 100644 index 00000000000..f75396c979a --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinTaskConfiguration.kt @@ -0,0 +1,11 @@ +package com.vaadin.gradle.worker + +import java.io.File +import java.io.Serializable + +public data class VaadinTaskConfiguration( + val flags: Flags, + val locations: Locations, + val strings: Strings, + val classFinderClasspath: Set +) : Serializable diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinTaskConfigurationFactory.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinTaskConfigurationFactory.kt new file mode 100644 index 00000000000..e3ea836fc3d --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinTaskConfigurationFactory.kt @@ -0,0 +1,23 @@ +package com.vaadin.gradle.worker + +import java.util.function.Supplier +import javax.inject.Inject + +internal class VaadinTaskConfigurationFactory @Inject constructor( + private val flagsFactory: FlagsFactory, + private val locationsFactory: LocationsFactory, + private val stringsFactory: StringsFactory, + private val classpathFactory: ClassFinderClasspathFactory +) : Supplier { + + override fun get(): VaadinTaskConfiguration { + return VaadinTaskConfiguration( + flagsFactory.get(), + locationsFactory.get(), + stringsFactory.get(), + classpathFactory.get() + ) + } + + public companion object +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinWorkActionParameter.kt b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinWorkActionParameter.kt new file mode 100644 index 00000000000..d186bed3f9e --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/main/kotlin/com/vaadin/gradle/worker/VaadinWorkActionParameter.kt @@ -0,0 +1,8 @@ +package com.vaadin.gradle.worker + +import org.gradle.api.provider.Property +import org.gradle.workers.WorkParameters + +public interface VaadinWorkActionParameter : WorkParameters { + public fun getVaadinTaskConfiguration(): Property +} From 2f1ea33edc7f487dd4f1f19e2e795755bffd61b3 Mon Sep 17 00:00:00 2001 From: Sergii Kondrusiev Date: Wed, 14 Aug 2024 16:17:00 +0300 Subject: [PATCH 2/2] Worker API implementation with respect of JVM toolchains (tests) --- flow-plugins/flow-gradle-plugin/build.gradle | 10 +++++ .../gradle/GradleWorkerApiTestCategory.kt | 4 ++ .../JvmToolchainsMiscMultiModuleTest.kt | 16 +++++++ .../vaadin/gradle/JvmToolchainsSmokeTest.kt | 44 +++++++++++++++++++ .../com/vaadin/gradle/MiscMultiModuleTest.kt | 12 ++++- .../com/vaadin/gradle/MiscSingleModuleTest.kt | 10 ++--- .../kotlin/com/vaadin/gradle/TestUtils.kt | 11 ++--- .../com/vaadin/gradle/VaadinSmokeTest.kt | 6 +-- 8 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/GradleWorkerApiTestCategory.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/JvmToolchainsMiscMultiModuleTest.kt create mode 100644 flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/JvmToolchainsSmokeTest.kt diff --git a/flow-plugins/flow-gradle-plugin/build.gradle b/flow-plugins/flow-gradle-plugin/build.gradle index 0cd73b28f46..bbeeb88ee65 100644 --- a/flow-plugins/flow-gradle-plugin/build.gradle +++ b/flow-plugins/flow-gradle-plugin/build.gradle @@ -131,6 +131,16 @@ task functionalTest(type: Test) { exceptionFormat = 'full' showStandardStreams = true } + + // Gradlew CLI option -Dorg.gradle.testkit.debug=true allows to debug test projects running by testkit. + // However, during debug, the Gradle testkit limits the ability to use process isolation in Worker API and does not initialize underlying native services. + // The next condition excludes the tests that require Worker API and native services during debugging + // This limitation seem to be fixed in Gradle 8.8, see https://github.com/gradle/gradle/blob/98f0db0546f9d9c31268e079389d7fda8d8143dc/platforms/extensibility/unit-test-fixtures/src/main/java/org/gradle/testfixtures/internal/ProjectBuilderImpl.java#L134-L141 + if(Boolean.getBoolean('org.gradle.testkit.debug')) { + useJUnit { + excludeCategories ' com.vaadin.gradle.GradleWorkerApiTestCategory' + } + } } check.dependsOn functionalTest /*********************************************************************************************************************** diff --git a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/GradleWorkerApiTestCategory.kt b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/GradleWorkerApiTestCategory.kt new file mode 100644 index 00000000000..d4c2c4eb135 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/GradleWorkerApiTestCategory.kt @@ -0,0 +1,4 @@ +package com.vaadin.gradle + +interface GradleWorkerApiTestCategory { +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/JvmToolchainsMiscMultiModuleTest.kt b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/JvmToolchainsMiscMultiModuleTest.kt new file mode 100644 index 00000000000..9d6a6610c8d --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/JvmToolchainsMiscMultiModuleTest.kt @@ -0,0 +1,16 @@ +package com.vaadin.gradle + +import org.junit.experimental.categories.Category + +@Category(GradleWorkerApiTestCategory::class) +class JvmToolchainsMiscMultiModuleTest : MiscMultiModuleTest() { + override fun extraGradleDSL(): String { + return """ + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + """.trimIndent() + } +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/JvmToolchainsSmokeTest.kt b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/JvmToolchainsSmokeTest.kt new file mode 100644 index 00000000000..93a478fba12 --- /dev/null +++ b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/JvmToolchainsSmokeTest.kt @@ -0,0 +1,44 @@ +/** + * Copyright 2000-2022 Vaadin Ltd + * + * 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 com.vaadin.gradle + +import org.junit.Before +import org.junit.experimental.categories.Category + + +/** + * This is smoke tests of vaadin with respect of JVM toolchain configuration. + * + * @author skondrusiev + */ + +@Category(GradleWorkerApiTestCategory::class) +class JvmToolchainsSmokeTest : VaadinSmokeTest() { + @Before + override fun setup() { + super.setup() + + testProject.buildFile.appendText( + """ + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscMultiModuleTest.kt b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscMultiModuleTest.kt index 0bec936766e..4406d9b17e3 100644 --- a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscMultiModuleTest.kt +++ b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscMultiModuleTest.kt @@ -25,7 +25,11 @@ import java.nio.file.Files import kotlin.io.path.writeText import kotlin.test.expect -class MiscMultiModuleTest : AbstractGradleTest() { +open class MiscMultiModuleTest : AbstractGradleTest() { + internal open fun extraGradleDSL(): String { + return "" + } + /** * Tests https://github.com/vaadin/vaadin-gradle-plugin/issues/38 */ @@ -60,6 +64,7 @@ class MiscMultiModuleTest : AbstractGradleTest() { nodeAutoUpdate = true // test the vaadin{} block by changing some innocent property with limited side-effect } } + ${extraGradleDSL()} """.trimIndent()) testProject.newFolder("lib") testProject.newFolder("web") @@ -104,6 +109,7 @@ class MiscMultiModuleTest : AbstractGradleTest() { nodeAutoUpdate = true // test the vaadin{} block by changing some innocent property with limited side-effect } } + ${extraGradleDSL()} """.trimIndent()) testProject.newFolder("lib") testProject.newFolder("web") @@ -142,6 +148,7 @@ class MiscMultiModuleTest : AbstractGradleTest() { project(':lib') { apply plugin: 'java' } + ${extraGradleDSL()} """.trimIndent()) testProject.newFolder("lib") val webFolder = testProject.newFolder("web") @@ -158,6 +165,7 @@ class MiscMultiModuleTest : AbstractGradleTest() { vaadin { nodeAutoUpdate = true // test the vaadin{} block by changing some innocent property with limited side-effect } + ${extraGradleDSL()} """.trimIndent()) val b: BuildResult = testProject.build("-Pvaadin.productionMode", "vaadinBuildFrontend", checkTasksSuccessful = false) @@ -191,6 +199,7 @@ class MiscMultiModuleTest : AbstractGradleTest() { project(':lib') { apply plugin: 'java' } + ${extraGradleDSL()} """.trimIndent()) testProject.newFolder("lib") val webFolder = testProject.newFolder("web") @@ -208,6 +217,7 @@ class MiscMultiModuleTest : AbstractGradleTest() { nodeAutoUpdate = true // test the vaadin{} block by changing some innocent property with limited side-effect applicationIdentifier = 'MY_APP_ID' } + ${extraGradleDSL()} """.trimIndent()) val b: BuildResult = testProject.build("-Pvaadin.productionMode", "vaadinBuildFrontend", checkTasksSuccessful = false) diff --git a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscSingleModuleTest.kt b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscSingleModuleTest.kt index 451acef7648..8d16a8a099f 100644 --- a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscSingleModuleTest.kt +++ b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/MiscSingleModuleTest.kt @@ -424,7 +424,7 @@ class MiscSingleModuleTest : AbstractGradleTest() { } """ ) - val result = testProject.build("vaadinPrepareFrontend", debug = true) + val result = testProject.build("vaadinPrepareFrontend") expect(false) { result.output.contains("org.reflections.ReflectionsException") } } @@ -446,11 +446,11 @@ class MiscSingleModuleTest : AbstractGradleTest() { implementation("org.slf4j:slf4j-simple:$slf4jVersion") } """) - var result = testProject.build("vaadinPrepareFrontend", debug = true) + var result = testProject.build("vaadinPrepareFrontend") expect(true) { result.output.contains( "Task ':vaadinPrepareFrontend' is not up-to-date") } - result = testProject.build("vaadinPrepareFrontend", debug = true, checkTasksSuccessful = false) + result = testProject.build("vaadinPrepareFrontend", checkTasksSuccessful = false) result.expectTaskOutcome("vaadinPrepareFrontend", TaskOutcome.UP_TO_DATE) println("Caching: " + result.output) expect(true) { result.output.contains( @@ -475,7 +475,7 @@ class MiscSingleModuleTest : AbstractGradleTest() { frontendHotdeploy = true } """) - result = testProject.build("vaadinPrepareFrontend", debug = true) + result = testProject.build("vaadinPrepareFrontend") println("Caching: " + result.output) expect(true) { result.output.contains( "Task ':vaadinPrepareFrontend' is not up-to-date") } @@ -505,7 +505,7 @@ class MiscSingleModuleTest : AbstractGradleTest() { """ ) repeat(5) { - val result = testProject.build("vaadinPrepareFrontend", debug = true) + val result = testProject.build("vaadinPrepareFrontend") expect(true) { result.output.contains( "Task ':vaadinPrepareFrontend' is not up-to-date" diff --git a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/TestUtils.kt b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/TestUtils.kt index a3c9c7e192d..dd36f1daebf 100644 --- a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/TestUtils.kt +++ b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/TestUtils.kt @@ -21,10 +21,8 @@ import org.gradle.testkit.runner.BuildTask import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome import java.io.File -import java.io.IOException import java.nio.file.FileSystems import java.nio.file.Files -import java.nio.file.Path import java.nio.file.PathMatcher import java.util.zip.ZipInputStream import kotlin.test.expect @@ -230,10 +228,9 @@ class TestProject(val gradleVersion: String = if(JavaVersion.current().majorVers val settingsFile: File get() = File(dir, "settings.gradle") - private fun createGradleRunner(debug: Boolean): GradleRunner = GradleRunner.create() + private fun createGradleRunner(): GradleRunner = GradleRunner.create() .withProjectDir(dir) .withPluginClasspath() - .withDebug(debug) .forwardOutput() // a must, otherwise ./gradlew check freezes on windows! .withGradleVersion(gradleVersion) @@ -267,11 +264,11 @@ class TestProject(val gradleVersion: String = if(JavaVersion.current().majorVers * The function by default checks that all tasks have succeeded; if not, throws an informative exception. * You can suppress this functionality by setting [checkTasksSuccessful] to false. */ - fun build(vararg args: String, checkTasksSuccessful: Boolean = true, debug: Boolean = false): BuildResult { + fun build(vararg args: String, checkTasksSuccessful: Boolean = true): BuildResult { expect(true, "$buildFile doesn't exist, can't run build") { buildFile.exists() } println("$dir/./gradlew ${args.joinToString(" ")}") - val result: BuildResult = createGradleRunner(debug) + val result: BuildResult = createGradleRunner() .withArguments(args.toList() + "--stacktrace" + "--info") .build() @@ -291,7 +288,7 @@ class TestProject(val gradleVersion: String = if(JavaVersion.current().majorVers */ fun buildAndFail(vararg args: String): BuildResult { println("$dir/./gradlew ${args.joinToString(" ")}") - return createGradleRunner(false) + return createGradleRunner() .withArguments(args.toList() + "--stacktrace" + "--info") .buildAndFail() } diff --git a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/VaadinSmokeTest.kt b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/VaadinSmokeTest.kt index 8dbecd53f40..e6cd0f6871e 100644 --- a/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/VaadinSmokeTest.kt +++ b/flow-plugins/flow-gradle-plugin/src/functionalTest/kotlin/com/vaadin/gradle/VaadinSmokeTest.kt @@ -35,9 +35,9 @@ import java.nio.file.StandardCopyOption * other test classes will possibly fail as well. * @author mavi */ -class VaadinSmokeTest : AbstractGradleTest() { +open class VaadinSmokeTest : AbstractGradleTest() { @Before - fun setup() { + open fun setup() { testProject.buildFile.writeText(""" plugins { id 'war' @@ -106,7 +106,7 @@ class VaadinSmokeTest : AbstractGradleTest() { @Test fun testBuildFrontendInProductionMode_customApplicationIdentifier() { - val result: BuildResult = testProject.build("-Pvaadin.applicationIdentifier=MY_APP_ID", "-Pvaadin.productionMode", "vaadinBuildFrontend", debug = true) + val result: BuildResult = testProject.build("-Pvaadin.applicationIdentifier=MY_APP_ID", "-Pvaadin.productionMode", "vaadinBuildFrontend") // vaadinBuildFrontend depends on vaadinPrepareFrontend // let's explicitly check that vaadinPrepareFrontend has been run result.expectTaskSucceded("vaadinPrepareFrontend")