/* * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. */ package org.jetbrains.compose.desktop.application.tasks import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.jetbrains.compose.desktop.application.internal.ComposeProperties import org.jetbrains.compose.desktop.application.internal.JvmRuntimeProperties import org.jetbrains.compose.desktop.application.internal.ExternalToolRunner import org.jetbrains.compose.desktop.application.internal.JdkVersionProbe import org.jetbrains.compose.desktop.tasks.AbstractComposeDesktopTask import org.jetbrains.compose.internal.utils.OS import org.jetbrains.compose.internal.utils.currentOS import org.jetbrains.compose.internal.utils.executableName import org.jetbrains.compose.internal.utils.ioFile import org.jetbrains.compose.internal.utils.notNullProperty import java.io.ByteArrayInputStream import java.io.File import java.util.* // __COMPOSE_NATIVE_DISTRIBUTIONS_MIN_JAVA_VERSION__ internal const val MIN_JAVA_RUNTIME_VERSION = 17 @CacheableTask abstract class AbstractCheckNativeDistributionRuntime : AbstractComposeDesktopTask() { @get:Classpath val jdkVersionProbeJar: ConfigurableFileCollection = objects.fileCollection() @get:PathSensitive(PathSensitivity.ABSOLUTE) @get:InputDirectory val jdkHome: Property = objects.notNullProperty() @get:Input abstract val checkJdkVendor: Property private val taskDir = project.layout.buildDirectory.dir("compose/tmp/$name") @get:OutputFile val javaRuntimePropertiesFile: Provider = taskDir.map { it.file("properties.bin") } private val jdkHomeFile: File get() = File(jdkHome.orNull ?: error("Missing jdkHome value")) private fun File.getJdkTool(toolName: String): File = resolve("bin/${executableName(toolName)}") private fun ensureToolsExist(vararg tools: File) { val missingTools = tools.filter { !it.exists() }.map { "'${it.name}'" } if (missingTools.isEmpty()) return if (missingTools.size == 1) jdkDistributionProbingError("${missingTools.single()} is missing") jdkDistributionProbingError("${missingTools.joinToString(", ")} are missing") } private fun jdkDistributionProbingError(errorMessage: String): Nothing { val fullErrorMessage = buildString { appendLine("Failed to check JDK distribution: $errorMessage") appendLine("JDK distribution path: ${jdkHomeFile.absolutePath}") } error(fullErrorMessage) } @TaskAction fun run() { taskDir.ioFile.mkdirs() val jdkHome = jdkHomeFile val javaExecutable = jdkHome.getJdkTool("java") val jlinkExecutable = jdkHome.getJdkTool("jlink") val jpackageExecutabke = jdkHome.getJdkTool("jpackage") ensureToolsExist(javaExecutable, jlinkExecutable, jpackageExecutabke) val jdkRuntimeProperties = getJDKRuntimeProperties(javaExecutable) val jdkMajorVersionString = jdkRuntimeProperties.getProperty(JdkVersionProbe.JDK_MAJOR_VERSION_KEY) val jdkMajorVersion = jdkMajorVersionString?.toIntOrNull() ?: jdkDistributionProbingError("JDK version '$jdkMajorVersionString' has unexpected format") check(jdkMajorVersion >= MIN_JAVA_RUNTIME_VERSION) { jdkDistributionProbingError( "minimum required JDK version is '$MIN_JAVA_RUNTIME_VERSION', " + "but actual version is '$jdkMajorVersion'" ) } if (checkJdkVendor.get()) { val vendor = jdkRuntimeProperties.getProperty(JdkVersionProbe.JDK_VENDOR_KEY) if (vendor == null) { logger.warn("JDK vendor probe failed: $jdkHome") } else { if (currentOS == OS.MacOS && vendor.equals("homebrew", ignoreCase = true)) { error( """ |Homebrew's JDK distribution may cause issues with packaging. |See: https://github.com/JetBrains/compose-multiplatform/issues/3107 |Possible solutions: |* Use other vendor's JDK distribution, such as Amazon Corretto; |* To continue using Homebrew distribution for packaging on your own risk, add "${ComposeProperties.CHECK_JDK_VENDOR}=false" to your gradle.properties """.trimMargin()) } } } val modules = arrayListOf() runExternalTool( tool = javaExecutable, args = listOf("--list-modules"), logToConsole = ExternalToolRunner.LogToConsole.Never, processStdout = { stdout -> stdout.lineSequence().forEach { line -> val moduleName = line.trim().substringBefore("@") if (moduleName.isNotBlank()) { modules.add(moduleName) } } } ) val properties = JvmRuntimeProperties(jdkMajorVersion, modules) JvmRuntimeProperties.writeToFile(properties, javaRuntimePropertiesFile.ioFile) } private fun getJDKRuntimeProperties(javaExecutable: File): Properties { val jdkProperties = Properties() runExternalTool( tool = javaExecutable, args = listOf("-jar", jdkVersionProbeJar.files.single().absolutePath), processStdout = { stdout -> ByteArrayInputStream(stdout.trim().toByteArray()).use { jdkProperties.loadFromXML(it) } } ) return jdkProperties } }