From dc428d3a221dc461a8bb686e55348038ed01216e Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Tue, 16 Mar 2021 10:58:25 +0300 Subject: [PATCH] Add suggestRuntimeModules task Resolves #463 --- .../application/dsl/NativeDistributions.kt | 6 +- .../application/internal/cliArgUtils.kt | 2 +- .../internal/configureApplication.kt | 12 +++ .../desktop/application/internal/osUtils.kt | 11 +++ .../AbstractCheckNativeDistributionRuntime.kt | 2 +- .../tasks/AbstractJvmToolOperationTask.kt | 10 +-- .../tasks/AbstractSuggestModulesTask.kt | 76 +++++++++++++++++++ .../compose/DesktopApplicationTest.kt | 11 +++ 8 files changed, 119 insertions(+), 11 deletions(-) create mode 100644 gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractSuggestModulesTask.kt diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/NativeDistributions.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/NativeDistributions.kt index 2718409e44..538ec2ae3f 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/NativeDistributions.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/NativeDistributions.kt @@ -7,6 +7,10 @@ import org.gradle.api.model.ObjectFactory import java.util.* import javax.inject.Inject +internal val DEFAULT_RUNTIME_MODULES = arrayOf( + "java.base", "java.desktop", "java.logging" +) + open class NativeDistributions @Inject constructor( objects: ObjectFactory, layout: ProjectLayout @@ -30,7 +34,7 @@ open class NativeDistributions @Inject constructor( set(layout.buildDirectory.dir("compose/binaries")) } - var modules = arrayListOf("java.desktop", "java.logging") + var modules = arrayListOf(*DEFAULT_RUNTIME_MODULES) fun modules(vararg modules: String) { this.modules.addAll(modules.toList()) } diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/cliArgUtils.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/cliArgUtils.kt index d2af5f7f2e..5c80d7cebf 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/cliArgUtils.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/cliArgUtils.kt @@ -35,5 +35,5 @@ private fun defaultToString(): (T) -> String = "\"$asString\"" } -private fun File.normalizedPath() = +internal fun File.normalizedPath() = if (currentOS == OS.Windows) absolutePath.replace("\\", "\\\\") else absolutePath \ No newline at end of file diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureApplication.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureApplication.kt index 17216f460b..5088aabebf 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureApplication.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureApplication.kt @@ -63,6 +63,18 @@ internal fun Project.configurePackagingTasks(apps: Collection) { ) } + tasks.composeTask(taskName("suggestRuntimeModules", app)) { + dependsOn(checkRuntime) + javaHome.set(provider { app.javaHomeOrDefault() }) + modules.set(provider { app.nativeDistributions.modules }) + + app._configurationSource?.let { configSource -> + dependsOn(configSource.jarTaskName) + files.from(configSource.runtimeClasspath(project)) + launcherMainJar.set(app.mainJar.orElse(configSource.jarTask(project).flatMap { it.archiveFile })) + } + } + val createRuntimeImage = tasks.composeTask( taskName("createRuntimeImage", app) ) { diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/osUtils.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/osUtils.kt index 3abc79bba3..be826c96b9 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/osUtils.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/osUtils.kt @@ -1,7 +1,9 @@ package org.jetbrains.compose.desktop.application.internal +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Internal import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.compose.desktop.application.tasks.MIN_JAVA_RUNTIME_VERSION import java.io.File internal enum class OS(val id: String) { @@ -63,6 +65,15 @@ internal object MacUtils { } } +internal fun jvmToolFile(toolName: String, javaHome: Provider): File { + val jtool = File(javaHome.get()).resolve("bin/${executableName(toolName)}") + check(jtool.isFile) { + "Invalid JDK: $jtool is not a file! \n" + + "Ensure JAVA_HOME or buildSettings.javaHome is set to JDK $MIN_JAVA_RUNTIME_VERSION or newer" + } + return jtool +} + @Internal internal fun findOutputFileOrDir(dir: File, targetFormat: TargetFormat): File = when (targetFormat) { diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractCheckNativeDistributionRuntime.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractCheckNativeDistributionRuntime.kt index 32016973ad..7ba8c5bb4e 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractCheckNativeDistributionRuntime.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractCheckNativeDistributionRuntime.kt @@ -12,7 +12,7 @@ import org.jetbrains.compose.desktop.application.internal.notNullProperty import java.io.File // __COMPOSE_NATIVE_DISTRIBUTIONS_MIN_JAVA_VERSION__ -private const val MIN_JAVA_RUNTIME_VERSION = 15 +internal const val MIN_JAVA_RUNTIME_VERSION = 15 @CacheableTask abstract class AbstractCheckNativeDistributionRuntime : AbstractComposeDesktopTask() { diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJvmToolOperationTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJvmToolOperationTask.kt index 3110d993d4..a65731b170 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJvmToolOperationTask.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJvmToolOperationTask.kt @@ -7,10 +7,9 @@ import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.process.ExecResult -import org.gradle.process.ExecSpec import org.gradle.work.InputChanges import org.jetbrains.compose.desktop.application.internal.ComposeProperties -import org.jetbrains.compose.desktop.application.internal.executableName +import org.jetbrains.compose.desktop.application.internal.jvmToolFile import org.jetbrains.compose.desktop.application.internal.ioFile import org.jetbrains.compose.desktop.application.internal.notNullProperty import java.io.File @@ -49,13 +48,8 @@ abstract class AbstractJvmToolOperationTask(private val toolName: String) : Abst @TaskAction fun run(inputChanges: InputChanges) { initState() - val javaHomePath = javaHome.get() - val jtool = File(javaHomePath).resolve("bin/${executableName(toolName)}") - check(jtool.isFile) { - "Invalid JDK: $jtool is not a file! \n" + - "Ensure JAVA_HOME or buildSettings.javaHome is set to JDK 14 or newer" - } + val jtool = jvmToolFile(toolName, javaHome = javaHome) fileOperations.delete(destinationDir) prepareWorkingDir(inputChanges) diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractSuggestModulesTask.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractSuggestModulesTask.kt new file mode 100644 index 0000000000..73efedc788 --- /dev/null +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractSuggestModulesTask.kt @@ -0,0 +1,76 @@ +package org.jetbrains.compose.desktop.application.tasks + +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.Directory +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* +import org.jetbrains.compose.desktop.application.dsl.DEFAULT_RUNTIME_MODULES +import org.jetbrains.compose.desktop.application.internal.* +import org.jetbrains.compose.desktop.application.internal.ComposeProperties +import org.jetbrains.compose.desktop.application.internal.normalizedPath +import org.jetbrains.kotlin.konan.file.File + +abstract class AbstractSuggestModulesTask : AbstractComposeDesktopTask() { + @get:Input + val javaHome: Property = objects.notNullProperty().apply { + set(providers.systemProperty("java.home")) + } + + @get:InputFiles + val files: ConfigurableFileCollection = objects.fileCollection() + + @get:InputFile + @get:PathSensitive(PathSensitivity.ABSOLUTE) + val launcherMainJar: RegularFileProperty = objects.fileProperty() + + @get:Input + val modules: ListProperty = objects.listProperty(String::class.java) + + @get:Input + val jvmTarget: Property = objects.notNullProperty(MIN_JAVA_RUNTIME_VERSION.toString()) + + @get:LocalState + protected val workingDir: Provider = project.layout.buildDirectory.dir("compose/tmp/$name") + + @TaskAction + fun run() { + val jtool = jvmToolFile("jdeps", javaHome = javaHome) + + fileOperations.delete(workingDir) + fileOperations.mkdir(workingDir) + val args = arrayListOf().apply { + add("--print-module-deps") + add("--ignore-missing-deps") + add("--multi-release") + add(jvmTarget.get()) + add("--class-path") + add(files.joinToString(File.pathSeparator) { it.normalizedPath() }) + add(launcherMainJar.ioFile.normalizedPath()) + } + + try { + runExternalTool( + tool = jtool, + args = args, + forceLogToFile = true, + processStdout = { output -> + val defaultModules = hashSetOf(*DEFAULT_RUNTIME_MODULES) + val suggestedModules = output.splitToSequence(",") + .map { it.trim() } + .filter { it.isNotBlank() && it !in defaultModules } + .toSortedSet() + val suggestion = "modules(${suggestedModules.joinToString(", ") { "\"$it\"" }})" + logger.quiet("Suggested runtime modules to include:") + logger.quiet(suggestion) + } + ) + } finally { + if (!ComposeProperties.preserveWorkingDir(providers).get()) { + fileOperations.delete(workingDir) + } + } + } +} \ No newline at end of file diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/DesktopApplicationTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/DesktopApplicationTest.kt index 72611a6152..1a9720fcb1 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/DesktopApplicationTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/DesktopApplicationTest.kt @@ -170,4 +170,15 @@ class DesktopApplicationTest : GradlePluginTestBase() { } } } + + @Test + fun testSuggestModules() { + with(testProject(TestProjects.jvm)) { + gradle(":suggestRuntimeModules").build().checks { check -> + check.taskOutcome(":suggestRuntimeModules", TaskOutcome.SUCCESS) + check.logContains("Suggested runtime modules to include:") + check.logContains("modules(\"java.instrument\", \"jdk.unsupported\")") + } + } + } }