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