Added automatic backup for debugging incremental compilation.
This commit is contained in:
@@ -107,6 +107,10 @@
|
||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||
</action>
|
||||
|
||||
<action id="CreateIncrementalCompilationBackup" class="org.jetbrains.jet.plugin.internal.makeBackup.CreateIncrementalCompilationBackup">
|
||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||
</action>
|
||||
|
||||
<action id="ExtractFunctionToScope" class="org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractFunctionToScopeAction"
|
||||
text="Extract Function to _Scope...">
|
||||
<keyboard-shortcut keymap="$default" first-keystroke="control alt shift M"/>
|
||||
@@ -183,6 +187,7 @@
|
||||
<fileTypeFactory implementation="org.jetbrains.jet.plugin.JetFileFactory"/>
|
||||
|
||||
<compileServer.plugin classpath="jps/kotlin-jps-plugin.jar;kotlin-runtime.jar;kotlin-plugin.jar"/>
|
||||
<compiler.task execute="BEFORE" implementation="org.jetbrains.jet.plugin.internal.makeBackup.MakeBackupCompileTask"/>
|
||||
|
||||
<lang.syntaxHighlighterFactory key="jet" implementationClass="org.jetbrains.jet.plugin.highlighter.JetSyntaxHighlighterFactory"/>
|
||||
<lang.braceMatcher language="jet" implementationClass="org.jetbrains.jet.plugin.JetPairMatcher"/>
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.jet.plugin.internal.makeBackup
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.history.core.RevisionsCollector
|
||||
import java.io.File
|
||||
import com.intellij.history.integration.patches.PatchCreator
|
||||
import com.intellij.history.integration.LocalHistoryImpl
|
||||
import com.intellij.openapi.vcs.changes.Change
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import java.util.ArrayList
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import java.util.zip.ZipOutputStream
|
||||
import java.io.FileOutputStream
|
||||
import com.intellij.util.io.ZipUtil
|
||||
import com.intellij.ide.actions.ShowFilePathAction
|
||||
import com.intellij.util.WaitForProgressToShow
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
|
||||
public class CreateIncrementalCompilationBackup: AnAction("Create backup for debugging Kotlin incremental compilation") {
|
||||
class object {
|
||||
val BACKUP_DIR_NAME = ".backup"
|
||||
val PATCHES_TO_CREATE = 5
|
||||
|
||||
val PATCHES_FRACTION = .25
|
||||
val LOGS_FRACTION = .05
|
||||
val ZIP_FRACTION = 1.0 - PATCHES_FRACTION - LOGS_FRACTION
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
e.getPresentation().setVisible(incrementalCompilationEnabled(e.getProject()!!))
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.getProject()!!
|
||||
val projectBaseDir = File(project.getBaseDir()!!.getPath())
|
||||
val backupDir = File(FileUtil.createTempDirectory("makeBackup", null), BACKUP_DIR_NAME)
|
||||
|
||||
ProgressManager.getInstance().run(
|
||||
object : Task.Backgroundable(project, "Creating backup for debugging Kotlin incremental compilation", true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
createPatches(backupDir, project, indicator)
|
||||
copyLogs(backupDir, indicator)
|
||||
|
||||
zipProjectDir(backupDir, project, projectBaseDir, indicator)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun createPatches(backupDir: File, project: Project, indicator: ProgressIndicator) {
|
||||
ApplicationManager.getApplication()!!.runReadAction(Runnable {
|
||||
val localHistoryImpl = LocalHistoryImpl.getInstanceImpl()!!
|
||||
val gateway = localHistoryImpl.getGateway()!!
|
||||
val localHistoryFacade = localHistoryImpl.getFacade()
|
||||
|
||||
val revisionsCollector = RevisionsCollector(localHistoryFacade, gateway.createTransientRootEntry(), project.getBaseDir()!!.getPath(), project.getLocationHash(), null)
|
||||
|
||||
var patchesCreated = 0
|
||||
|
||||
val patchesDir = File(backupDir, "patches")
|
||||
patchesDir.mkdirs()
|
||||
|
||||
val revisions = revisionsCollector.getResult()!!
|
||||
for (rev in revisions) {
|
||||
val label = rev.getLabel()
|
||||
if (label != null && label.startsWith(HISTORY_LABEL_PREFIX)) {
|
||||
val patchFile = File(patchesDir, label.trimLeading(HISTORY_LABEL_PREFIX) + ".patch")
|
||||
|
||||
indicator.setText("Creating patch $patchFile")
|
||||
indicator.setFraction(PATCHES_FRACTION * patchesCreated / PATCHES_TO_CREATE)
|
||||
|
||||
val differences = revisions[0].getDifferencesWith(rev)!!
|
||||
val changes = differences.map {
|
||||
d ->
|
||||
Change(d.getLeftContentRevision(gateway), d.getRightContentRevision(gateway))
|
||||
}
|
||||
|
||||
PatchCreator.create(project, changes, patchFile.getPath(), false, null)
|
||||
|
||||
if (++patchesCreated >= PATCHES_TO_CREATE) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun copyLogs(backupDir: File, indicator: ProgressIndicator) {
|
||||
indicator.setText("Copying logs")
|
||||
indicator.setFraction(PATCHES_FRACTION)
|
||||
|
||||
val logsDir = File(backupDir, "logs")
|
||||
FileUtil.copyDir(File(PathManager.getLogPath()), logsDir)
|
||||
|
||||
indicator.setFraction(PATCHES_FRACTION + LOGS_FRACTION)
|
||||
}
|
||||
|
||||
private fun zipProjectDir(backupDir: File, project: Project, projectDir: File, indicator: ProgressIndicator) {
|
||||
// files and relative paths
|
||||
|
||||
val files = ArrayList<Pair<File, String>>() // files and relative paths
|
||||
var totalBytes = 0L
|
||||
|
||||
for (dir in listOf(projectDir, backupDir.getParentFile()!!)) {
|
||||
FileUtil.processFilesRecursively(
|
||||
dir,
|
||||
/*processor*/ {
|
||||
if (it!!.isFile()
|
||||
&& !it.getName().endsWith(".hprof")
|
||||
&& !(it.getName().startsWith("make_backup_") && it.getName().endsWith(".zip"))
|
||||
) {
|
||||
|
||||
indicator.setText("Scanning project dir: $it")
|
||||
|
||||
files.add(Pair(it, FileUtil.getRelativePath(dir, it)!!))
|
||||
totalBytes += it.length()
|
||||
}
|
||||
true
|
||||
},
|
||||
/*directoryFilter*/ {
|
||||
val name = it!!.getName()
|
||||
name != ".git" && name != "out"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
val backupFile = File(projectDir, "make_backup_" + SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(Date()) + ".zip")
|
||||
|
||||
|
||||
val zos = ZipOutputStream(FileOutputStream(backupFile))
|
||||
|
||||
var processedBytes = 0L
|
||||
|
||||
zos.use {
|
||||
for ((file, relativePath) in files) {
|
||||
indicator.setText("Adding file to backup: $relativePath")
|
||||
indicator.setFraction(PATCHES_FRACTION + LOGS_FRACTION + processedBytes.toDouble() / totalBytes * ZIP_FRACTION)
|
||||
|
||||
ZipUtil.addFileToZip(zos, file, relativePath, null, null)
|
||||
|
||||
processedBytes += file.length()
|
||||
}
|
||||
}
|
||||
|
||||
FileUtil.delete(backupDir)
|
||||
|
||||
WaitForProgressToShow.runOrInvokeLaterAboveProgress({
|
||||
ShowFilePathAction.showDialog(
|
||||
project,
|
||||
"Successfully created backup " + backupFile.getAbsolutePath(),
|
||||
"Created backup",
|
||||
backupFile,
|
||||
null
|
||||
)
|
||||
}, null, project)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.jet.plugin.internal.makeBackup
|
||||
|
||||
import com.intellij.history.LocalHistory
|
||||
import com.intellij.openapi.compiler.CompileTask
|
||||
import com.intellij.openapi.compiler.CompileContext
|
||||
import java.util.Random
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.compiler.CompilerWorkspaceConfiguration
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
val random = Random()
|
||||
|
||||
val HISTORY_LABEL_KEY = Key.create<String>("history label")
|
||||
|
||||
public class MakeBackupCompileTask: CompileTask {
|
||||
override fun execute(context: CompileContext?): Boolean {
|
||||
val project = context!!.getProject()!!
|
||||
|
||||
if (!incrementalCompilationEnabled(project)) return true
|
||||
|
||||
val localHistory = LocalHistory.getInstance()!!
|
||||
val label = HISTORY_LABEL_PREFIX + Integer.toHexString(random.nextInt())
|
||||
localHistory.putSystemLabel(project, label)
|
||||
|
||||
context.getCompileScope()!!.putUserData(HISTORY_LABEL_KEY, label)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun incrementalCompilationEnabled(project: Project): Boolean {
|
||||
val compilerVmOptions = CompilerWorkspaceConfiguration.getInstance(project)!!.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS!!
|
||||
return compilerVmOptions.contains("-Dkotlin.incremental.compilation=true")
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.jet.plugin.internal.makeBackup
|
||||
|
||||
val HISTORY_LABEL_PREFIX = "build started "
|
||||
@@ -50,10 +50,13 @@ import org.jetbrains.jet.compiler.runner.KotlinCompilerRunner.runK2JsCompiler
|
||||
import org.jetbrains.jet.compiler.runner.KotlinCompilerRunner.runK2JvmCompiler
|
||||
import org.jetbrains.jet.utils.keysToMap
|
||||
import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
|
||||
public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||
class object {
|
||||
public val KOTLIN_BUILDER_NAME: String = "Kotlin Builder"
|
||||
|
||||
private val LOG = Logger.getInstance("#org.jetbrains.jps.cmdline.BuildSession")
|
||||
}
|
||||
|
||||
override fun getPresentableName() = KOTLIN_BUILDER_NAME
|
||||
@@ -66,6 +69,11 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
|
||||
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
|
||||
outputConsumer: ModuleLevelBuilder.OutputConsumer
|
||||
): ModuleLevelBuilder.ExitCode {
|
||||
val historyLabel = context.getBuilderParameter("history label")
|
||||
if (historyLabel != null) {
|
||||
LOG.info("Label in local history: $historyLabel")
|
||||
}
|
||||
|
||||
val messageCollector = MessageCollectorAdapter(context)
|
||||
// Workaround for Android Studio
|
||||
if (!isJavaPluginEnabled(context)) {
|
||||
|
||||
Reference in New Issue
Block a user