override suspend fun buildAndReload()

in hot-reload-devtools/src/main/kotlin/org/jetbrains/compose/devtools/gradle/GradleRecompiler.kt [48:150]


    override suspend fun buildAndReload(context: RecompilerContext): ExitCode {
        val orchestrationPort = context.orchestration.port.awaitOrThrow()

        /* Check if there is another known Gradle Process running */
        val gradlePidFile = HotReloadEnvironment.pidFile?.let { pidFile ->
            pidFile.resolveSibling("${pidFile.nameWithoutExtension}.gradle.pid")
        }

        /*
        If we can find another process under the previous pid file:
        - warn the user if possible
        - wait for the previous build to finish
         */
        if (gradlePidFile != null && gradlePidFile.isRegularFile()) run kill@{
            val previousPid = runDirectoryLockFile?.withLock {
                gradlePidFile.toFile().readText().toLongOrNull()
            } ?: return@kill
            val previousProcessHandle = ProcessHandle.of(previousPid).getOrNull() ?: return@kill
            context.logger.error("Previous Gradle process with pid '$previousPid' found; Waiting...")
            previousProcessHandle.onExit().toFuture().await()
        }

        /*
        Launch the build, creating a new process.
         */
        val processBuilder = context.process {
            /* Setup JAVA_HOME: Prefer the java home of the original Gradle compilation */
            if (HotReloadEnvironment.gradleJavaHome == null) {
                context.logger.warn("Missing '${HotReloadProperty.GradleJavaHome}' property. Using system java")
            }

            val javaHome = HotReloadEnvironment.gradleJavaHome
                ?: System.getProperty("java.home")?.let(::Path)?.takeIf { it.exists() }

            if (javaHome != null) {
                environment()["JAVA_HOME"] = javaHome.pathString
            }

            /* Setup gradle wrapper command */
            val gradleScriptCommand = if (Os.currentOrNull() == Os.Windows) arrayOf("cmd", "/c", "gradlew.bat")
            else arrayOf("./gradlew")

            val gradleTaskPath = if (buildProject == ":") ":$buildTask"
            else "$buildProject:$buildTask"

            directory(buildRoot.toFile())
            redirectErrorStream(true)
            command(
                listOfNotNull(
                    *gradleScriptCommand,
                    gradleTaskPath,
                    "--console=plain",

                    "-D${HotReloadProperty.IsHotReloadBuild.key}=true",
                    "-P${HotReloadProperty.IsHotReloadBuild.key}=true",
                    *subprocessSystemProperties(BuildTool, orchestrationPort).toTypedArray(),
                    "-D${HotReloadProperty.GradleJavaHome.key}=${HotReloadEnvironment.gradleJavaHome?.pathString}"
                        .takeIf { HotReloadEnvironment.gradleJavaHome != null },

                    /* Continuous mode arguments */
                    "-t".takeIf { HotReloadEnvironment.gradleBuildContinuous },
                    "--no-daemon".takeIf { !useGradleDaemon },
                    "--offline".takeIf { HotReloadEnvironment.gradleOfflineMode },

                    /* Performance arguments */
                    "--configuration-cache".takeIf { HotReloadEnvironment.gradleBuildOptimize },
                    "--configuration-cache-problems=warn".takeIf { HotReloadEnvironment.gradleBuildOptimize },
                    "--build-cache".takeIf { HotReloadEnvironment.gradleBuildOptimize },
                    "--parallel".takeIf { HotReloadEnvironment.gradleBuildOptimize },
                    "-Pkotlin.internal.incremental.enableUnsafeOptimizationsForMultiplatform=true"
                        .takeIf { HotReloadEnvironment.gradleBuildOptimize }
                )
            )
        }

        /* Start the process and wire up the disposal */
        val process = processBuilder.start()
        context.invokeOnDispose { process.toHandle().destroyGradleProcess() }
        context.logger.debug("'Recompiler': Started (${process.pid()})")

        /* Write the pid file with the new process id */
        runDirectoryLockFile?.withLock {
            gradlePidFile?.writeText(process.pid().toString())
        }

        process.onExit().whenComplete { _, _ -> gradlePidFile?.deleteIfExists() }

        /* Read the output of the new process and forward it as log messages */
        withContext(Dispatchers.IO) {
            process.inputStream.bufferedReader().forEachLine { line ->
                val level = when {
                    line.startsWith("e: ") -> Level.Error
                    line.startsWith("warning: ") -> Level.Warn
                    line.startsWith("w: ") -> Level.Warn
                    line.startsWith(">") -> Level.Debug
                    else -> Level.Info
                }
                context.logger.log(level, line)
            }
        }

        return ExitCode(process.onExit().toFuture().awaitOrThrow().exitValue())
    }