Added automatic backup for debugging incremental compilation.

This commit is contained in:
Evgeny Gerashchenko
2014-09-18 16:50:04 +04:00
parent 36b50a25f9
commit 9af1c3e4dc
5 changed files with 264 additions and 0 deletions
+5
View File
@@ -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"/>
@@ -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)) {