/* * Copyright 2003-2011 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 jetbrains.mps.idea.core.make; import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.CompilerWorkspaceConfiguration; import com.intellij.compiler.impl.CompilerUtil; import com.intellij.compiler.server.CustomBuilderMessageHandler; import com.intellij.openapi.compiler.CompileScope; import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.compiler.CompilerMessageCategory; import com.intellij.openapi.compiler.CompilerPaths; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.project.Project; import jetbrains.mps.fileTypes.MPSFileTypeFactory; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.idea.core.MPSBundle; import jetbrains.mps.idea.core.module.CachedRepositoryData; import jetbrains.mps.library.LibraryInitializer; import jetbrains.mps.project.MPSExtentions; import jetbrains.mps.util.io.ModelOutputStream; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.incremental.messages.BuildMessage.Kind; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** * evgeny, 11/21/11 */ public class MPSCompilerComponent implements ProjectComponent { private final Project myProject; private final CompilerManager compilerManager; private final CompilerConfiguration compilerConfiguration; public MPSCompilerComponent(Project project, CompilerManager compilerManager, CompilerConfiguration compilerConfiguration) { myProject = project; this.compilerManager = compilerManager; this.compilerConfiguration = compilerConfiguration; } @Override public void projectOpened() { final List<String> errorMessages = new ArrayList<>(); myProject.getMessageBus().connect().subscribe(CustomBuilderMessageHandler.TOPIC, new RefreshFilesCompilationStatusListener()); myProject.getMessageBus().connect().subscribe(CustomBuilderMessageHandler.TOPIC, new NavigateToNodesWithErrors(errorMessages)); compilerManager.addCompilableFileType(MPSFileTypeFactory.MPS_FILE_TYPE); compilerManager.addCompilableFileType(MPSFileTypeFactory.MPS_ROOT_FILE_TYPE); for (String ext : Arrays.asList(MPSExtentions.MODEL, MPSExtentions.MODEL_ROOT, MPSExtentions.MODEL_HEADER)) { if (compilerConfiguration.isResourceFile("." + ext)) { String negatedPattern = "!*." + ext; compilerConfiguration.addResourceFilePattern(negatedPattern); } } compilerManager.addBeforeTask(context -> { final CompileScope compileScope = context.getCompileScope(); if (compileScope == null) return true; final File repositoryCache = new File(CompilerPaths.getCompilerSystemDirectory(myProject), "mps_repository.dat"); final long start = System.nanoTime(); ProjectHelper.fromIdeaProject(myProject).getModelAccess().runReadAction(() -> { CachedRepositoryData cachedRepositoryData = MPSRepositoryUtil.buildData(LibraryInitializer.getInstance().getModuleHandles()); ModelOutputStream mos = null; try { mos = new ModelOutputStream(new FileOutputStream(repositoryCache)); cachedRepositoryData.save(mos); compileScope.putUserData(MPSMakeConstants.MPS_REPOSITORY, repositoryCache.getPath()); } catch (IOException e) { context.addMessage(CompilerMessageCategory.INFORMATION, MPSBundle.message("mps.compiler.component.message.slow"), null, 0, 0); } finally { jetbrains.mps.util.FileUtil.closeFileSafe(mos); } }); long result = (System.nanoTime() - start) / 1000000; if (CompilerWorkspaceConfiguration.getInstance(myProject).COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS.contains(MPSBundle.message("mps.compiler.component.debug.flag"))) { context.addMessage(CompilerMessageCategory.INFORMATION, String.format(MPSBundle.message("mps.compiler.component.message.cache.saved"), result), null, 0, 0); } return true; }); compilerManager.addAfterTask(context -> { for (String errorMessage : errorMessages) { ModelNodeNavigatable navigatable = ModelNodeNavigatable.extractNavigatable(errorMessage, context.getProject(), null); context.addMessage(CompilerMessageCategory.ERROR, errorMessage, null, -1, -1, navigatable); } boolean noErrors = errorMessages.isEmpty(); errorMessages.clear(); return noErrors; }); } @Override public void projectClosed() { } @Override public void initComponent() { } @Override public void disposeComponent() { } @Override @NotNull public String getComponentName() { return "MPS Compiler Component"; } private class RefreshFilesCompilationStatusListener implements CustomBuilderMessageHandler { private final AtomicReference<List<File>> myAffectedFiles = new AtomicReference<>(new ArrayList<>()); @Override public void messageReceived(String builderId, String messageType, String messageText) { if (MPSMakeConstants.BUILDER_ID.equals(builderId)) { if (messageType.equals(MPSCustomMessages.MSG_GENERATED)) { myAffectedFiles.get().add(new File(messageText)); } else if (messageType.equals(MPSCustomMessages.MSG_REFRESH)) { final List<File> generatedFiles = myAffectedFiles.getAndSet(new ArrayList<>()); if (myProject.isDisposed() || generatedFiles.isEmpty()) { return; } // refresh affected files CompilerUtil.refreshIOFiles(generatedFiles); } } } } private class NavigateToNodesWithErrors implements CustomBuilderMessageHandler { private final List<String> myErrorMessages; public NavigateToNodesWithErrors(List<String> errorMessages) { myErrorMessages = errorMessages; } @Override public void messageReceived(String builderId, String messageType, final String messageText) { if (MPSMakeConstants.BUILDER_ID.equals(builderId) && (Kind.ERROR.toString().equals(messageType))) { myErrorMessages.add(messageText); } } } }