GradleBuildRoot: ability to detect if file was existed before import
This commit is contained in:
+1
@@ -101,6 +101,7 @@ private fun KotlinDslScriptsModel.toListOfScriptModels(project: Project): List<K
|
||||
}
|
||||
|
||||
class KotlinDslGradleBuildSync(val workingDir: String, val taskId: ExternalSystemTaskId) {
|
||||
val ts = System.currentTimeMillis()
|
||||
var project: Project? = null
|
||||
val projectRoots = mutableSetOf<String>()
|
||||
val models = mutableListOf<KotlinDslScriptModel>()
|
||||
|
||||
+3
-1
@@ -25,7 +25,9 @@ import java.io.File
|
||||
*
|
||||
* See [GradleBuildRootsManager] for more details.
|
||||
*/
|
||||
sealed class GradleBuildRoot(private val lastModifiedFiles: LastModifiedFiles) {
|
||||
sealed class GradleBuildRoot(
|
||||
private val lastModifiedFiles: LastModifiedFiles
|
||||
) {
|
||||
@Volatile
|
||||
var importing = false
|
||||
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.scripting.gradle.roots
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
|
||||
|
||||
data class GradleBuildRootData(
|
||||
val importTs: Long,
|
||||
val projectRoots: Collection<String>,
|
||||
val templateClasspath: Collection<String>,
|
||||
val models: Collection<KotlinDslScriptModel>
|
||||
|
||||
+4
-3
@@ -19,7 +19,7 @@ import java.io.DataInputStream
|
||||
import java.io.DataOutput
|
||||
|
||||
internal object GradleBuildRootDataSerializer {
|
||||
private val attribute = FileAttribute("kotlin-dsl-script-models", 5, false)
|
||||
private val attribute = FileAttribute("kotlin-dsl-script-models", 6, false)
|
||||
|
||||
fun read(buildRoot: VirtualFile): GradleBuildRootData? {
|
||||
return attribute.readAttribute(buildRoot)?.use {
|
||||
@@ -54,6 +54,7 @@ internal fun writeKotlinDslScriptModels(output: DataOutput, data: GradleBuildRoo
|
||||
strings.addStrings(it.imports)
|
||||
}
|
||||
strings.writeHeader()
|
||||
output.writeLong(data.importTs)
|
||||
strings.writeStringIds(data.projectRoots)
|
||||
strings.writeStringIds(data.templateClasspath)
|
||||
output.writeList(data.models) {
|
||||
@@ -69,9 +70,9 @@ internal fun writeKotlinDslScriptModels(output: DataOutput, data: GradleBuildRoo
|
||||
internal fun readKotlinDslScriptModels(input: DataInputStream, buildRoot: String): GradleBuildRootData {
|
||||
val strings = StringsPool.reader(input)
|
||||
|
||||
val importTs = input.readLong()
|
||||
val projectRoots = strings.readStrings()
|
||||
val templateClasspath = strings.readStrings()
|
||||
|
||||
val models = input.readList {
|
||||
KotlinDslScriptModel(
|
||||
strings.readString(),
|
||||
@@ -83,7 +84,7 @@ internal fun readKotlinDslScriptModels(input: DataInputStream, buildRoot: String
|
||||
)
|
||||
}
|
||||
|
||||
return GradleBuildRootData(projectRoots, templateClasspath, models)
|
||||
return GradleBuildRootData(importTs, projectRoots, templateClasspath, models)
|
||||
}
|
||||
|
||||
private object StringsPool {
|
||||
|
||||
+23
-9
@@ -61,11 +61,18 @@ abstract class GradleBuildRootsLocator {
|
||||
standalone
|
||||
}
|
||||
|
||||
class ScriptUnderRoot(
|
||||
/**
|
||||
* Timestamp of an moment when script file was discovered (indexed).
|
||||
* Used to detect if that script was existed at the moment of import
|
||||
*/
|
||||
abstract fun getScriptFirstSeenTs(path: String): Long
|
||||
|
||||
inner class ScriptUnderRoot(
|
||||
val filePath: String,
|
||||
val root: GradleBuildRoot?,
|
||||
val script: GradleScriptInfo? = null,
|
||||
val standalone: Boolean = false,
|
||||
val nearest: GradleBuildRoot? = null
|
||||
val nearest: GradleBuildRoot? = root
|
||||
) {
|
||||
val notificationKind: NotificationKind
|
||||
get() = when {
|
||||
@@ -76,13 +83,20 @@ abstract class GradleBuildRootsLocator {
|
||||
else -> when (nearest) {
|
||||
is Legacy -> NotificationKind.dontCare
|
||||
is New -> NotificationKind.wasNotImportedAfterCreation
|
||||
is Imported -> NotificationKind.notEvaluatedInLastImport // todo: wasNotImportedAfterCreation
|
||||
is Imported -> when {
|
||||
wasImportedAndNotEvaluated -> NotificationKind.notEvaluatedInLastImport
|
||||
else -> NotificationKind.wasNotImportedAfterCreation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val isImported: Boolean
|
||||
get() = script != null
|
||||
|
||||
private val wasImportedAndNotEvaluated: Boolean
|
||||
get() = nearest is Imported &&
|
||||
getScriptFirstSeenTs(filePath) < nearest.data.importTs
|
||||
|
||||
override fun toString(): String {
|
||||
return "ScriptUnderRoot(root=$root, script=$script, standalone=$standalone, nearest=$nearest)"
|
||||
}
|
||||
@@ -96,7 +110,7 @@ abstract class GradleBuildRootsLocator {
|
||||
|
||||
val scriptInfo = getScriptInfo(filePath)
|
||||
val imported = scriptInfo?.buildRoot
|
||||
if (imported != null) return ScriptUnderRoot(imported, scriptInfo)
|
||||
if (imported != null) return ScriptUnderRoot(filePath, imported, scriptInfo)
|
||||
|
||||
if (filePath.endsWith("/build.gradle.kts") ||
|
||||
filePath.endsWith("/settings.gradle.kts") ||
|
||||
@@ -104,7 +118,7 @@ abstract class GradleBuildRootsLocator {
|
||||
) {
|
||||
// build|settings|init.gradle.kts scripts should be located near gradle project root only
|
||||
val gradleBuild = roots.getBuildByProjectDir(filePath.substringBeforeLast("/"))
|
||||
if (gradleBuild != null) return ScriptUnderRoot(gradleBuild)
|
||||
if (gradleBuild != null) return ScriptUnderRoot(filePath, gradleBuild)
|
||||
}
|
||||
|
||||
// other scripts: "included", "precompiled" scripts, scripts in unlinked projects,
|
||||
@@ -112,17 +126,17 @@ abstract class GradleBuildRootsLocator {
|
||||
|
||||
val standaloneScriptRoot = roots.getStandaloneScriptRoot(filePath)
|
||||
if (standaloneScriptRoot != null) {
|
||||
return ScriptUnderRoot(standaloneScriptRoot, standalone = true)
|
||||
return ScriptUnderRoot(filePath, standaloneScriptRoot, standalone = true)
|
||||
}
|
||||
|
||||
if (searchNearestLegacy) {
|
||||
val nearest = roots.findNearestRoot(filePath)
|
||||
return when (nearest) {
|
||||
is Legacy -> ScriptUnderRoot(nearest)
|
||||
else -> ScriptUnderRoot(null, nearest = nearest)
|
||||
is Legacy -> ScriptUnderRoot(filePath, nearest)
|
||||
else -> ScriptUnderRoot(filePath, null, nearest = nearest)
|
||||
}
|
||||
} else {
|
||||
return ScriptUnderRoot(null)
|
||||
return ScriptUnderRoot(filePath, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-14
@@ -107,6 +107,8 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
|
||||
override fun getScriptInfo(localPath: String): GradleScriptInfo? =
|
||||
manager.getLightScriptInfo(localPath) as? GradleScriptInfo
|
||||
|
||||
override fun getScriptFirstSeenTs(path: String): Long = 0
|
||||
|
||||
fun fileChanged(filePath: String, ts: Long = System.currentTimeMillis()) {
|
||||
findAffectedFileRoot(filePath)?.fileChanged(filePath, ts)
|
||||
scheduleLastModifiedFilesSave()
|
||||
@@ -117,31 +119,34 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
|
||||
updateNotifications { it.startsWith(workingDir) }
|
||||
}
|
||||
|
||||
fun update(build: KotlinDslGradleBuildSync) {
|
||||
fun update(sync: KotlinDslGradleBuildSync) {
|
||||
// fast path for linked gradle builds without .gradle.kts support
|
||||
if (build.models.isEmpty()) {
|
||||
val root = getBuildRootByWorkingDir(build.workingDir) ?: return
|
||||
if (sync.models.isEmpty()) {
|
||||
val root = getBuildRootByWorkingDir(sync.workingDir) ?: return
|
||||
if (root is Imported && root.data.models.isEmpty()) return
|
||||
}
|
||||
|
||||
try {
|
||||
val root = actualizeBuildRoot(build.workingDir) ?: return
|
||||
root.importing = false
|
||||
val oldRoot = actualizeBuildRoot(sync.workingDir) ?: return
|
||||
oldRoot.importing = false
|
||||
|
||||
if (root is Legacy) return
|
||||
if (oldRoot is Legacy) return
|
||||
|
||||
val templateClasspath = GradleScriptDefinitionsContributor.getDefinitionsTemplateClasspath(project)
|
||||
val newData = GradleBuildRootData(build.projectRoots, templateClasspath, build.models)
|
||||
val mergedData = if (build.failed && root is Imported) merge(root.data, newData) else newData
|
||||
val newData = GradleBuildRootData(sync.ts, sync.projectRoots, templateClasspath, sync.models)
|
||||
val mergedData = if (sync.failed && oldRoot is Imported) merge(oldRoot.data, newData) else newData
|
||||
|
||||
val lastModifiedFilesReset = LastModifiedFiles()
|
||||
val newSupport = tryCreateImportedRoot(build.workingDir, lastModifiedFilesReset) { mergedData } ?: return
|
||||
GradleBuildRootDataSerializer.write(newSupport.dir ?: return, mergedData)
|
||||
newSupport.saveLastModifiedFiles()
|
||||
val newRoot = tryCreateImportedRoot(
|
||||
sync.workingDir,
|
||||
lastModifiedFilesReset
|
||||
) { mergedData } ?: return
|
||||
GradleBuildRootDataSerializer.write(newRoot.dir ?: return, mergedData)
|
||||
newRoot.saveLastModifiedFiles()
|
||||
|
||||
add(newSupport)
|
||||
add(newRoot)
|
||||
} finally {
|
||||
updateNotifications { it.startsWith(build.workingDir) }
|
||||
updateNotifications { it.startsWith(sync.workingDir) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +157,7 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
|
||||
val models = old.models.associateByTo(mutableMapOf()) { it.file }
|
||||
new.models.associateByTo(models) { it.file }
|
||||
|
||||
return GradleBuildRootData(roots, new.templateClasspath, models.values)
|
||||
return GradleBuildRootData(new.importTs, roots, new.templateClasspath, models.values)
|
||||
}
|
||||
|
||||
private val lastModifiedFilesSaveScheduled = AtomicBoolean()
|
||||
|
||||
+1
@@ -20,6 +20,7 @@ class GradleBuildRootDataSerializerTest {
|
||||
@Test
|
||||
fun write() {
|
||||
val data = GradleBuildRootData(
|
||||
123,
|
||||
listOf("a", "b", "c"),
|
||||
listOf("a"),
|
||||
listOf(
|
||||
|
||||
+21
-13
@@ -11,11 +11,14 @@ import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
open class AbstractGradleBuildRootsLocatorTest {
|
||||
private val scripts = mutableMapOf<String, GradleScriptInfo>()
|
||||
private val scripts = mutableMapOf<String, ScriptFixture>()
|
||||
private val locator = MyRootsLocator()
|
||||
|
||||
class ScriptFixture(val introductionTs: Long, val info: GradleScriptInfo)
|
||||
|
||||
private inner class MyRootsLocator : GradleBuildRootsLocator() {
|
||||
override fun getScriptInfo(localPath: String): GradleScriptInfo? = scripts[localPath]
|
||||
override fun getScriptFirstSeenTs(path: String): Long = scripts[path]?.introductionTs ?: 0
|
||||
override fun getScriptInfo(localPath: String): GradleScriptInfo? = scripts[localPath]?.info
|
||||
fun accessRoots() = roots
|
||||
}
|
||||
|
||||
@@ -26,13 +29,15 @@ open class AbstractGradleBuildRootsLocatorTest {
|
||||
fun newImportedGradleProject(
|
||||
dir: String,
|
||||
relativeProjectRoots: List<String> = listOf(""),
|
||||
relativeScripts: List<String> = listOf("build.gradle.kts")
|
||||
relativeScripts: List<String> = listOf("build.gradle.kts"),
|
||||
ts: Long = 0
|
||||
) {
|
||||
val pathPrefix = "$dir/"
|
||||
val root = Imported(
|
||||
dir,
|
||||
null,
|
||||
GradleBuildRootData(
|
||||
ts,
|
||||
relativeProjectRoots.map { (pathPrefix + it).removeSuffix("/") },
|
||||
listOf(),
|
||||
listOf()
|
||||
@@ -44,16 +49,19 @@ open class AbstractGradleBuildRootsLocatorTest {
|
||||
|
||||
relativeScripts.forEach {
|
||||
val path = pathPrefix + it
|
||||
scripts[path] = GradleScriptInfo(
|
||||
root,
|
||||
null,
|
||||
KotlinDslScriptModel(
|
||||
file = path,
|
||||
inputs = GradleKotlinScriptConfigurationInputs("", 0, dir),
|
||||
classPath = listOf(),
|
||||
sourcePath = listOf(),
|
||||
imports = listOf(),
|
||||
messages = listOf()
|
||||
scripts[path] = ScriptFixture(
|
||||
0,
|
||||
GradleScriptInfo(
|
||||
root,
|
||||
null,
|
||||
KotlinDslScriptModel(
|
||||
file = path,
|
||||
inputs = GradleKotlinScriptConfigurationInputs("", 0, dir),
|
||||
classPath = listOf(),
|
||||
sourcePath = listOf(),
|
||||
imports = listOf(),
|
||||
messages = listOf()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user