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

Support hotreloading of shared common modules too #1405

Merged
merged 1 commit into from
Mar 13, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package com.soywiz.korge.gradle.targets.jvm

import com.soywiz.korge.gradle.*
import com.soywiz.korge.gradle.kotlin
import com.soywiz.korge.gradle.targets.*
import com.soywiz.korlibs.*
import org.gradle.api.*
import org.gradle.api.artifacts.*
import org.gradle.api.file.*
import org.gradle.api.tasks.*
import org.gradle.jvm.tasks.*
import java.io.*

fun Project.findAllProjectDependencies(visited: MutableSet<Project> = mutableSetOf()): Set<Project> {
if (this in visited) return visited
visited.add(this)
val dependencies = project.configurations.flatMap { it.dependencies.withType(ProjectDependency::class.java) }.filterIsInstance<ProjectDependency>()
return (dependencies.flatMap { it.dependencyProject.findAllProjectDependencies(visited) } + this).toSet()
}

open class KorgeJavaExecWithAutoreload : KorgeJavaExec() {
@get:Input
var enableRedefinition: Boolean = false
Expand All @@ -22,41 +28,54 @@ open class KorgeJavaExecWithAutoreload : KorgeJavaExec() {
const val CMD_SEPARATOR = "<@/@>"
}

private var projectPath: String = project.path
private lateinit var projectPaths: List<String>
private var rootDir: File = project.rootProject.rootDir
@get:InputFiles
lateinit var rootJars: FileCollection
lateinit var rootJars: List<File>
@get:InputFile
//private var reloadAgentConfiguration: Configuration = project.configurations.getByName(KORGE_RELOAD_AGENT_CONFIGURATION_NAME)//.resolve().first()
lateinit var reloadAgentJar: File

init {
//val reloadAgent = project.findProject(":korge-reload-agent")
//if (reloadAgent != null)
rootJars = (project.tasks.findByName("compileKotlinJvm") as org.jetbrains.kotlin.gradle.tasks.KotlinCompile).outputs.files
//project.dependencies.add()
project.afterEvaluate {
val allProjects = project.findAllProjectDependencies()
//projectPaths = allProjects.map { it.path }
projectPaths = listOf(project.path)
rootJars = allProjects.map { File(it.buildDir, "classes/kotlin/jvm/main") }
//println("allProjects=${allProjects.map { it.name }}")
//println("projectPaths=$projectPaths")
//println("rootJars=\n${rootJars.toList().joinToString("\n")}")
}
/*
project.afterEvaluate {
project.afterEvaluate {
project.afterEvaluate {
//project.configurations.getByName("compile")
//println("*****" + project.findAllProjectDependencies())
val allProjects = project.findAllProjectDependencies()
val allProjectsWithCompileKotinJvm = allProjects.filter { it.tasks.findByName("compileKotlinJvm") != null }
projectPaths = allProjectsWithCompileKotinJvm.map { it.path }
rootJars = allProjectsWithCompileKotinJvm
.mapNotNull { (it.tasks.findByName("compileKotlinJvm") as? org.jetbrains.kotlin.gradle.tasks.KotlinCompile?)?.outputs?.files }
.reduce { a, b -> a + b }
println("allProjects=${allProjects.map { it.name }}")
println("allProjectsWithCompileKotinJvm=${allProjectsWithCompileKotinJvm.map { it.name }}")
println("rootJars=\n${rootJars.toList().joinToString("\n")}")
//println("::::" + project.configurations.toList())
}
}
}
*/
val reloadAgent = project.findProject(":korge-reload-agent")
reloadAgentJar = when {
reloadAgent != null -> (project.rootProject.tasks.getByPath(":korge-reload-agent:jar") as Jar).outputs.files.files.first()
else -> project.configurations.getByName(KORGE_RELOAD_AGENT_CONFIGURATION_NAME).resolve().first()
}
}

override fun autoconfigure() {
//super.autoconfigure()
////println("---------------")
////println((project.tasks.findByName("compileKotlinJvm") as org.jetbrains.kotlin.gradle.tasks.KotlinCompile).outputs.files.toList())
//
//project.afterEvaluate {
// //println("++++++++++++++")
// rootJars = (project.tasks.findByName("compileKotlinJvm") as org.jetbrains.kotlin.gradle.tasks.KotlinCompile).outputs.files
// val reloadAgent = project.findProject(":korge-reload-agent")
// reloadAgentJar = when {
// reloadAgent != null -> (project.rootProject.tasks.getByPath(":korge-reload-agent:jar") as Jar).outputs.files.files.first()
// else -> project.configurations.getByName(KORGE_RELOAD_AGENT_CONFIGURATION_NAME).resolve().first()
// }
//}
}

override fun exec() {
//val gradlewCommand = if (isWindows) "gradlew.bat" else "gradlew"

Expand All @@ -83,7 +102,11 @@ open class KorgeJavaExecWithAutoreload : KorgeJavaExec() {
add("--configuration-cache-problems=warn")
}
add("-t")
add("${projectPath.trimEnd(':')}:compileKotlinJvmAndNotify")
add("compileKotlinJvm")
//add("compileKotlinJvmAndNotify")
for (projectPath in projectPaths) {
add("${projectPath.trimEnd(':')}:compileKotlinJvmAndNotify")
}
}.joinToString(CMD_SEPARATOR),
"$enableRedefinition",
rootJars.joinToString(CMD_SEPARATOR) { it.absolutePath }
Expand All @@ -95,11 +118,8 @@ open class KorgeJavaExecWithAutoreload : KorgeJavaExec() {
}
}

open class KorgeJavaExec : JavaExec() {
//dependsOn(getKorgeProcessResourcesTaskName("jvm", "main"))

@get:InputFiles
val korgeClassPath: FileCollection = ArrayList<FileCollection>().apply {
fun Project.getKorgeClassPath(): FileCollection {
return ArrayList<FileCollection>().apply {
val mainJvmCompilation = project.mainJvmCompilation
add(mainJvmCompilation.runtimeDependencyFiles)
add(mainJvmCompilation.compileDependencyFiles)
Expand All @@ -112,10 +132,13 @@ open class KorgeJavaExec : JavaExec() {
//add(project.files().from((project.tasks.findByName(jvmProcessedResourcesTaskName) as KorgeProcessedResourcesTask).processedResourcesFolder))
}
.reduceRight { l, r -> l + r }
}

open fun autoconfigure() {
open class KorgeJavaExec : JavaExec() {
//dependsOn(getKorgeProcessResourcesTaskName("jvm", "main"))

}
@get:InputFiles
val korgeClassPath: FileCollection = project.getKorgeClassPath()

override fun exec() {
val firstThread = firstThread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ object KorgeReloadAgent {
println("[KorgeReloadAgent] - Running $continuousCommand")
while (true) {
try {
val isWindows = System.getProperty("os.name").toLowerCase().contains("win")
val isWindows = System.getProperty("os.name").lowercase().contains("win")
//val args = arrayOf<String>()
//val args = if (isWindows) arrayOf("cmd.exe", "/k") else arrayOf("/bin/sh", "-c")
//val args = if (isWindows) arrayOf() else arrayOf("/bin/sh", "-c")
Expand Down Expand Up @@ -219,7 +219,7 @@ class KorgeReloaderProcessor(val rootFolders: List<String>, val inst: Instrument
if (modifiedClassNames.isEmpty()) {
println("[KorgeReloadAgent] modifiedClassNames=$modifiedClassNames [EMPTY] STOPPING")
} else {
println("[KorgeReloadAgent] modifiedClassNames=$modifiedClassNames")
println("[KorgeReloadAgent] modifiedClassNames=\n${modifiedClassNames.joinToString("\n")}")
var successRedefinition = true
val changedDefinitions = arrayListOf<ClassDefinition>()
val times = arrayListOf<Long>()
Expand Down Expand Up @@ -256,8 +256,8 @@ class KorgeReloaderProcessor(val rootFolders: List<String>, val inst: Instrument
successRedefinition = false
}
println("[KorgeReloadAgent] reload enableRedefinition=$enableRedefinition, successRedefinition=$successRedefinition, changedDefinitions=${changedDefinitions.size}, classNameToBytes=${classNameToBytes.size}, times[${times.size}]=${times.sum()}ms")
val triggerReload = Class.forName("com.soywiz.korge.KorgeReload").getMethod("triggerReload", java.util.List::class.java, java.lang.Boolean.TYPE)
triggerReload.invoke(null, changedDefinitions.map { it.definitionClass.name }, successRedefinition)
val triggerReload = Class.forName("com.soywiz.korge.KorgeReload").getMethod("triggerReload", java.util.List::class.java, java.lang.Boolean.TYPE, java.util.List::class.java)
triggerReload.invoke(null, changedDefinitions.map { it.definitionClass.name }, successRedefinition, rootFolders)
}
}

Expand Down
11 changes: 6 additions & 5 deletions korge/src/commonMain/kotlin/com/soywiz/korge/KorgeReload.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.jvm.JvmStatic
import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass

class ReloadClassContext(val injector: AsyncInjector, val refreshedClasses: Set<String>)
class ReloadClassContext(val injector: AsyncInjector, val refreshedClasses: Set<String>, val rootFolders: List<String>)

internal open class KorgeReloadInternalImpl {
open fun <T : Any> getReloadedClass(clazz: KClass<T>, context: ReloadClassContext): KClass<T> = clazz
Expand All @@ -23,9 +23,9 @@ object KorgeReload {

@JvmStatic
@Suppress("unused") // This is called from [com.soywiz.korge.reloadagent.KorgeReloadAgent]
fun triggerReload(classes: List<String>, success: Boolean) {
fun triggerReload(classes: List<String>, success: Boolean, rootFolders: List<String>) {
println("KorgeReloadAgent detected a class change. Reload: $classes")
KorgeReload_eventDispatcher?.dispatch(ReloadEvent(classes.toSet(), success))
KorgeReload_eventDispatcher?.dispatch(ReloadEvent(classes.toSet(), success, rootFolders))
}

fun registerEventDispatcher(eventDispatcher: EventListener) {
Expand All @@ -45,13 +45,14 @@ object KorgeReload {
data class ReloadEvent(
val refreshedClasses: Set<String>,
/** Was able to reload all classes successfully in the existing class loader */
val reloadSuccess: Boolean
val reloadSuccess: Boolean,
val rootFolders: List<String>
) : Event(), TEvent<ReloadEvent> {
override val type: EventType<ReloadEvent> = ReloadEvent
companion object : EventType<ReloadEvent>

val doFullReload: Boolean get() = !reloadSuccess
fun <T : Any> getReloadedClass(clazz: KClass<T>, injector: AsyncInjector): KClass<T> = KorgeReloadInternal.getReloadedClass(clazz, ReloadClassContext(injector, refreshedClasses))
fun <T : Any> getReloadedClass(clazz: KClass<T>, injector: AsyncInjector): KClass<T> = KorgeReloadInternal.getReloadedClass(clazz, ReloadClassContext(injector, refreshedClasses, rootFolders))
fun transferKeepProperties(old: Any, new: Any) = KorgeReloadInternal.transferKeepProperties(old, new)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal actual val KorgeReloadInternal: KorgeReloadInternalImpl = object : Korg
override fun <T : Any> getReloadedClass(clazz: KClass<T>, context: ReloadClassContext): KClass<T> {
println("### KorgeReload_getReloadedClass: $clazz")
val oldClass = clazz
val newClass = KorgeReloadClassLoader().loadClass(oldClass.qualifiedName).kotlin as KClass<T>
val newClass = KorgeReloadClassLoader(extraFolders = context.rootFolders).loadClass(oldClass.qualifiedName).kotlin as KClass<T>
context.injector.jvmRemoveMappingsByClassName(context.refreshedClasses)
context.injector.removeMapping(oldClass)
context.injector.root.jvmAutomapping()
Expand Down Expand Up @@ -52,18 +52,19 @@ internal actual val KorgeReloadInternal: KorgeReloadInternalImpl = object : Korg
}

class KorgeReloadClassLoader(
val folders: List<File> = System.getProperty("java.class.path")
.split(File.pathSeparator)
.map { File(it) }
.filter { !it.name.endsWith(".jar") }
, parent: ClassLoader? = null
val extraFolders: List<String> = emptyList(),
val allEntries: List<File> = (extraFolders + System.getProperty("java.class.path").split(File.pathSeparator)).map { File(it).absoluteFile }.distinct(),
val jars: List<File> = allEntries.filter { it.name.endsWith(".jar") },
val folders: List<File> = allEntries.filter { !it.name.endsWith(".jar") },
parent: ClassLoader? = null
) : ClassLoader(parent ?: ClassLoader.getSystemClassLoader()) {
companion object {
val logger = Logger("KorgeReloadClassLoader")
}

init {
logger.info { "KorgeReloadClassLoader:\n${folders.joinToString("\n")}" }
logger.info { "KorgeReloadClassLoader.jars:\n${jars.joinToString("\n")}" }
logger.info { "KorgeReloadClassLoader.folders:\n${folders.joinToString("\n")}" }
}

override fun loadClass(name: String, resolve: Boolean): Class<*> {
Expand Down