diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index f1a091bd97f..2ac202ad0c6 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -107,6 +107,10 @@ + + + + @@ -183,6 +187,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/CreateIncrementalCompilationBackup.kt b/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/CreateIncrementalCompilationBackup.kt new file mode 100644 index 00000000000..03550c03b65 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/CreateIncrementalCompilationBackup.kt @@ -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>() // 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) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/MakeBackupCompileTask.kt b/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/MakeBackupCompileTask.kt new file mode 100644 index 00000000000..4ad9125e9d3 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/MakeBackupCompileTask.kt @@ -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("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") +} diff --git a/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/makeBackup.kt b/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/makeBackup.kt new file mode 100644 index 00000000000..728b13d49ca --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/internal/makeBackup/makeBackup.kt @@ -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 " diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.kt b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.kt index b8226bd333b..3efd7de51ec 100644 --- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.kt +++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.kt @@ -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, 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)) {