Skip to content

Commit

Permalink
Added support for zulu like jdks by performing the java home check ba…
Browse files Browse the repository at this point in the history
…sed on bin(executables folder). (#336)

* Added support for zulu-like jdks by performing the java home check based on bin(executables folder).

* Went back to the original approach with passing DoctorExtension directly into the JavaHomeCheck instead of using JavaHomeCheckConfigurations.
Minor fix for extra message appending mechanism in case it's not set.

---------

Co-authored-by: Nelson Osacky <nosacky@gradle.com>
  • Loading branch information
slowpacer and runningcode authored Apr 25, 2024
1 parent 09c7c0a commit 7442802
Show file tree
Hide file tree
Showing 8 changed files with 427 additions and 63 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/gradle-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
java-version: |
17
8
distribution: 'liberica'
- uses: gradle/actions/setup-gradle@v3
with:
add-job-summary-as-pr-comment: on-failure
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/gradle-macos-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
java-version: |
8
17
distribution: 'liberica'
- uses: gradle/actions/setup-gradle@v3
with:
add-job-summary-as-pr-comment: on-failure
Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ configure<DoctorExtension> {
downloadSpeedWarningThreshold.set(2.0f)
daggerThreshold.set(100)
javaHome {
ensureJavaHomeMatches.set(false)
ensureJavaHomeIsSet.set(false)
ensureJavaHomeMatches.set(true)
ensureJavaHomeIsSet.set(true)
}
}

Expand Down
12 changes: 11 additions & 1 deletion doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.testing.Test
import org.gradle.internal.build.event.BuildEventListenerRegistryInternal
import org.gradle.internal.jvm.Jvm
import org.gradle.internal.operations.BuildOperationListener
import org.gradle.internal.operations.BuildOperationListenerManager
import org.gradle.kotlin.dsl.create
Expand All @@ -46,7 +47,7 @@ class DoctorPlugin : Plugin<Project> {
val intervalMeasurer = IntervalMeasurer()
val pillBoxPrinter = PillBoxPrinter(target.logger)
val daemonChecker = BuildDaemonChecker(extension, createDaemonChecker(os, cliCommandExecutor), pillBoxPrinter)
val javaHomeCheck = JavaHomeCheck(extension, pillBoxPrinter)
val javaHomeCheck = createJavaHomeCheck(extension, pillBoxPrinter)
val appleRosettaTranslationCheck =
AppleRosettaTranslationCheck(
os,
Expand Down Expand Up @@ -157,6 +158,15 @@ class DoctorPlugin : Plugin<Project> {
}
}

private fun createJavaHomeCheck(
extension: DoctorExtension,
pillBoxPrinter: PillBoxPrinter,
): JavaHomeCheck {
val jvmVariables =
JvmVariables(environmentJavaHome = System.getenv(JAVA_HOME), gradleJavaHome = Jvm.current().javaHome.path)
return JavaHomeCheck(jvmVariables, extension.javaHomeHandler, pillBoxPrinter)
}

private fun tagFreshDaemon(
target: Project,
buildScanApi: BuildScanAdapter,
Expand Down
127 changes: 71 additions & 56 deletions doctor-plugin/src/main/java/com/osacky/doctor/JavaHomeCheck.kt
Original file line number Diff line number Diff line change
@@ -1,83 +1,98 @@
package com.osacky.doctor

import com.gradle.develocity.agent.gradle.adapters.BuildScanAdapter
import com.osacky.doctor.internal.DefaultPrescriptionGenerator
import com.osacky.doctor.internal.JAVA_HOME_TAG
import com.osacky.doctor.internal.JavaHomeCheckPrescriptionsGenerator
import com.osacky.doctor.internal.PillBoxPrinter
import org.gradle.api.GradleException
import org.gradle.internal.jvm.Jvm
import java.io.File
import java.nio.file.NoSuchFileException
import java.nio.file.Path
import java.util.Collections
import kotlin.collections.LinkedHashSet
import kotlin.io.path.exists
import kotlin.io.path.pathString

internal const val JAVA_HOME = "JAVA_HOME"
internal const val JAVA_EXECUTABLES_FOLDER = "bin"
internal const val JAVA_HOME_NOT_FOUND =
"There is no existing filesystem structure for %s. Please specify proper path for $JAVA_HOME!"

class JavaHomeCheck(
private val extension: DoctorExtension,
jvmVariables: JvmVariables,
private val javaHomeHandler: JavaHomeHandler,
private val pillBoxPrinter: PillBoxPrinter,
private val prescriptionsGenerator: JavaHomeCheckPrescriptionsGenerator =
DefaultPrescriptionGenerator { javaHomeHandler.extraMessage.orNull },
) : BuildStartFinishListener, HasBuildScanTag {
private val environmentJavaHome: String? = System.getenv("JAVA_HOME")
private val gradleJavaHome = Jvm.current().javaHome
private val gradleJavaExecutablePath by lazy { resolveExecutableJavaPath(jvmVariables.gradleJavaHome) }
private val environmentJavaExecutablePath by lazy { resolveEnvironmentJavaHome(jvmVariables.environmentJavaHome) }
private val recordedErrors = Collections.synchronizedSet(LinkedHashSet<String>())
private val isGradleUsingJavaHome: Boolean
get() = gradleJavaExecutablePath == environmentJavaExecutablePath

override fun onStart() {
val extraMessage = extension.javaHomeHandler.extraMessage.orNull
val failOnError = extension.javaHomeHandler.failOnError.get()

if (extension.javaHomeHandler.ensureJavaHomeIsSet.get() && environmentJavaHome == null) {
val message =
buildString {
appendln("JAVA_HOME is not set.")
appendln(
"Please set JAVA_HOME so that switching between Android Studio and the terminal does not trigger a full rebuild.",
)
appendln("To set JAVA_HOME: (using bash)")
appendln("echo \"export JAVA_HOME=${'$'}(/usr/libexec/java_home)\" >> ~/.bash_profile")
appendln("or `~/.zshrc` if using zsh.")
extraMessage?.let {
appendln()
appendln(extraMessage)
}
}
if (failOnError) {
throw GradleException(pillBoxPrinter.createPill(message))
} else {
recordedErrors.add(message)
}
}
if (extension.javaHomeHandler.ensureJavaHomeMatches.get() && !isGradleUsingJavaHome()) {
val message =
buildString {
appendln("Gradle is not using JAVA_HOME.")
appendln("JAVA_HOME is ${environmentJavaHome?.toFile()?.toPath()?.toAbsolutePath()}")
appendln("Gradle is using ${gradleJavaHome.toPath().toAbsolutePath()}")
appendln("This can slow down your build significantly when switching from Android Studio to the terminal.")
appendln("To fix: Project Structure -> JDK Location.")
appendln("Set this to your JAVA_HOME.")
extraMessage?.let {
appendln()
appendln(extraMessage)
}
}
if (failOnError) {
throw GradleException(pillBoxPrinter.createPill(message))
} else {
recordedErrors.add(message)
}
}
ensureJavaHomeIsSet()
ensureJavaHomeMatchesGradleHome()
}

override fun onFinish(): List<String> {
return recordedErrors.toList()
}

private fun isGradleUsingJavaHome(): Boolean {
// Follow symlinks when checking that java home matches.
if (environmentJavaHome != null && gradleJavaHome.toPath().toRealPath() == File(environmentJavaHome).toPath().toRealPath()) {
return true
override fun addCustomValues(buildScanApi: BuildScanAdapter) {
buildScanApi.tag(JAVA_HOME_TAG)
}

private fun ensureJavaHomeIsSet() {
if (javaHomeHandler.ensureJavaHomeIsSet.get() && environmentJavaExecutablePath == null) {
failOrRecordMessage(prescriptionsGenerator.generateJavaHomeIsNotSetMessage())
}
return false
}

private fun String.toFile() = File(this)
private fun ensureJavaHomeMatchesGradleHome() {
if (javaHomeHandler.ensureJavaHomeMatches.get() && !isGradleUsingJavaHome) {
failOrRecordMessage(
prescriptionsGenerator.generateJavaHomeMismatchesGradleHome(
environmentJavaExecutablePath?.pathString,
gradleJavaExecutablePath.pathString,
),
)
}
}

override fun addCustomValues(buildScanApi: BuildScanAdapter) {
buildScanApi.tag(JAVA_HOME_TAG)
private fun failOrRecordMessage(message: String) {
if (javaHomeHandler.failOnError.get()) {
throw GradleException(pillBoxPrinter.createPill(message))
} else {
recordedErrors.add(message)
}
}

private fun resolveEnvironmentJavaHome(location: String?) =
location?.let {
resolveExecutableJavaPath(it) { path ->
if (!path.exists()) {
throw GradleException(String.format(JAVA_HOME_NOT_FOUND, path))
}
return@resolveExecutableJavaPath path
}
}

private fun resolveExecutableJavaPath(
location: String,
fallback: (Path) -> Path = { it },
): Path {
val path = File(location).toPath()
return try {
// Follow symlinks when checking that java home matches.
path.resolve(JAVA_EXECUTABLES_FOLDER).toRealPath()
} catch (exc: NoSuchFileException) {
// fallback to initial path
return fallback(path)
}
}
}

data class JvmVariables(val environmentJavaHome: String?, val gradleJavaHome: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2024 the original author or authors.
*
* 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.osacky.doctor.internal

internal const val NO_JAVA_HOME = "JAVA_HOME is not set."
internal const val JAVA_HOME_AT_LOCATION = "JAVA_HOME is %s"
internal const val GRADLE_JAVA_HOME_AT_LOCATION = "Gradle is using %s"
internal const val NO_JAVA_HOME_MESSAGE = """
$NO_JAVA_HOME
Please set JAVA_HOME so that switching between Android Studio and the terminal does not trigger a full rebuild.
To set JAVA_HOME: (using bash)
echo "export JAVA_HOME=${'$'}(/usr/libexec/java_home)" >> ~/.bash_profile
or `~/.zshrc` if using zsh.
%s
"""

internal const val JAVA_HOME_DOESNT_MATCH_GRADLE_HOME = """
Gradle is not using JAVA_HOME.
%s
%s
This can slow down your build significantly when switching from Android Studio to the terminal.
To fix: Project Structure -> JDK Location.
Set this to your JAVA_HOME.
%s
"""

interface JavaHomeCheckPrescriptionsGenerator {
fun generateJavaHomeIsNotSetMessage(): String

fun generateJavaHomeMismatchesGradleHome(
javaHomeLocation: String?,
gradleJavaHomeLocation: String,
): String
}

internal class DefaultPrescriptionGenerator(private val extraMessage: () -> String?) :
JavaHomeCheckPrescriptionsGenerator {
override fun generateJavaHomeIsNotSetMessage() = String.format(NO_JAVA_HOME_MESSAGE, extraMessage().orEmpty()).trimIndent()

override fun generateJavaHomeMismatchesGradleHome(
javaHomeLocation: String?,
gradleJavaHomeLocation: String,
): String {
val javaHomeMessage =
javaHomeLocation?.let { String.format(JAVA_HOME_AT_LOCATION, it) } ?: NO_JAVA_HOME
val gradleJavaHomeMessage = String.format(GRADLE_JAVA_HOME_AT_LOCATION, gradleJavaHomeLocation)
return String.format(
JAVA_HOME_DOESNT_MATCH_GRADLE_HOME,
javaHomeMessage,
gradleJavaHomeMessage,
extraMessage().orEmpty(),
).trimIndent()
}
}
Loading

0 comments on commit 7442802

Please sign in to comment.