Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raise error when Homebrew JDK is used for packaging #3451

Merged
merged 1 commit into from Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions gradle-plugins/compose/build.gradle.kts
Expand Up @@ -70,6 +70,7 @@ dependencies {
embedded("org.jetbrains.kotlinx:kotlinx-serialization-core:${BuildProperties.serializationVersion}")
embedded("org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:${BuildProperties.serializationVersion}")
embedded(project(":preview-rpc"))
embedded(project(":jdk-version-probe"))
}

val shadow = tasks.named<ShadowJar>("shadowJar") {
Expand Down
Expand Up @@ -20,6 +20,7 @@ internal object ComposeProperties {
internal const val MAC_NOTARIZATION_APPLE_ID = "compose.desktop.mac.notarization.appleID"
internal const val MAC_NOTARIZATION_PASSWORD = "compose.desktop.mac.notarization.password"
internal const val MAC_NOTARIZATION_ASC_PROVIDER = "compose.desktop.mac.notarization.ascProvider"
internal const val CHECK_JDK_VENDOR = "compose.desktop.packaging.checkJdkVendor"

fun isVerbose(providers: ProviderFactory): Provider<Boolean> =
providers.findProperty(VERBOSE).toBooleanProvider(false)
Expand Down Expand Up @@ -47,4 +48,7 @@ internal object ComposeProperties {

fun macNotarizationAscProvider(providers: ProviderFactory): Provider<String?> =
providers.findProperty(MAC_NOTARIZATION_ASC_PROVIDER)

fun checkJdkVendor(providers: ProviderFactory): Provider<Boolean> =
providers.findProperty(CHECK_JDK_VENDOR).toBooleanProvider(true)
}
Expand Up @@ -67,6 +67,7 @@ private fun JvmApplicationContext.configureCommonJvmDesktopTasks(): CommonJvmDes
taskNameObject = "runtime"
) {
jdkHome.set(app.javaHomeProvider)
checkJdkVendor.set(ComposeProperties.checkJdkVendor(project.providers))
jdkVersionProbeJar.from(
project.detachedComposeGradleDependency(
artifactId = "gradle-plugin-internal-jdk-version-probe"
Expand Down
Expand Up @@ -10,13 +10,19 @@ 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 org.jetbrains.compose.desktop.application.internal.ExternalToolRunner
import org.jetbrains.compose.desktop.tasks.AbstractComposeDesktopTask
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
Expand All @@ -30,6 +36,9 @@ abstract class AbstractCheckNativeDistributionRuntime : AbstractComposeDesktopTa
@get:InputDirectory
val jdkHome: Property<String> = objects.notNullProperty()

@get:Input
abstract val checkJdkVendor: Property<Boolean>

private val taskDir = project.layout.buildDirectory.dir("compose/tmp/$name")

@get:OutputFile
Expand Down Expand Up @@ -69,18 +78,37 @@ abstract class AbstractCheckNativeDistributionRuntime : AbstractComposeDesktopTa
val jpackageExecutabke = jdkHome.getJdkTool("jpackage")
ensureToolsExist(javaExecutable, jlinkExecutable, jpackageExecutabke)

val jvmRuntimeVersionString = getJavaRuntimeVersion(javaExecutable)
val jdkRuntimeProperties = getJDKRuntimeProperties(javaExecutable)

val jvmRuntimeVersion = jvmRuntimeVersionString?.toIntOrNull()
?: jdkDistributionProbingError("JDK version '$jvmRuntimeVersionString' has unexpected format")
val jdkMajorVersionString = jdkRuntimeProperties.getProperty(JdkVersionProbe.JDK_MAJOR_VERSION_KEY)
val jdkMajorVersion = jdkMajorVersionString?.toIntOrNull()
?: jdkDistributionProbingError("JDK version '$jdkMajorVersionString' has unexpected format")
AlexeyTsvetkov marked this conversation as resolved.
Show resolved Hide resolved

check(jvmRuntimeVersion >= MIN_JAVA_RUNTIME_VERSION) {
check(jdkMajorVersion >= MIN_JAVA_RUNTIME_VERSION) {
jdkDistributionProbingError(
"minimum required JDK version is '$MIN_JAVA_RUNTIME_VERSION', " +
"but actual version is '$jvmRuntimeVersion'"
"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<String>()
runExternalTool(
tool = javaExecutable,
Expand All @@ -96,17 +124,21 @@ abstract class AbstractCheckNativeDistributionRuntime : AbstractComposeDesktopTa
}
)

val properties = JvmRuntimeProperties(jvmRuntimeVersion, modules)
val properties = JvmRuntimeProperties(jdkMajorVersion, modules)
JvmRuntimeProperties.writeToFile(properties, javaRuntimePropertiesFile.ioFile)
}

private fun getJavaRuntimeVersion(javaExecutable: File): String? {
var javaRuntimeVersion: String? = null
private fun getJDKRuntimeProperties(javaExecutable: File): Properties {
val jdkProperties = Properties()
runExternalTool(
tool = javaExecutable,
args = listOf("-jar", jdkVersionProbeJar.files.single().absolutePath),
processStdout = { stdout -> javaRuntimeVersion = stdout.trim() }
processStdout = { stdout ->
ByteArrayInputStream(stdout.trim().toByteArray()).use {
jdkProperties.loadFromXML(it)
}
}
)
return javaRuntimeVersion
return jdkProperties
}
}
Expand Up @@ -4,35 +4,42 @@
*/
package org.jetbrains.compose.desktop.application.internal;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class JdkVersionProbe {
public static void main(String[] args) {
public final static String JDK_MAJOR_VERSION_KEY = "jdk.major.version";
public final static String JDK_VENDOR_KEY = "jdk.vendor";

public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.setProperty(JDK_MAJOR_VERSION_KEY, getJDKMajorVersion());
properties.setProperty(JDK_VENDOR_KEY, System.getProperty("java.vendor"));
properties.storeToXML(System.out, null);
}

private static String getJDKMajorVersion() {
Class<Runtime> runtimeClass = Runtime.class;
try {
Method version = runtimeClass.getMethod("version");
Object runtimeVer = version.invoke(runtimeClass);
Class<?> runtimeVerClass = runtimeVer.getClass();
try {
int feature = (int) runtimeVerClass.getMethod("feature").invoke(runtimeVer);
printVersionAndHalt((Integer.valueOf(feature)).toString());
return (Integer.valueOf(feature)).toString();
} catch (NoSuchMethodException e) {
int major = (int) runtimeVerClass.getMethod("major").invoke(runtimeVer);
printVersionAndHalt((Integer.valueOf(major)).toString());
return (Integer.valueOf(major)).toString();
}
} catch (Exception e) {
String javaVersion = System.getProperty("java.version");
String[] parts = javaVersion.split("\\.");
if (parts.length > 2 && "1".equalsIgnoreCase(parts[0])) {
printVersionAndHalt(parts[1]);
return parts[1];
} else {
throw new IllegalStateException("Could not determine JDK version from string: '" + javaVersion + "'");
}
}
}

private static void printVersionAndHalt(String version) {
System.out.println(version);
Runtime.getRuntime().exit(0);
}
}