package jetbrains.mps.ide.make; /*Generated by MPS */ import jetbrains.mps.make.service.AbstractMakeService; import jetbrains.mps.make.IMakeService; import com.intellij.openapi.components.ApplicationComponent; import org.apache.log4j.Logger; import org.apache.log4j.LogManager; import java.util.concurrent.atomic.AtomicMarkableReference; import jetbrains.mps.make.MakeSession; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.Future; import jetbrains.mps.make.script.IResult; import java.util.List; import jetbrains.mps.make.IMakeNotificationListener; import java.util.Collections; import jetbrains.mps.internal.collections.runtime.ListSequence; import java.util.ArrayList; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import jetbrains.mps.make.resources.IResource; import jetbrains.mps.make.script.IScript; import jetbrains.mps.make.script.IScriptController; import org.jetbrains.mps.openapi.util.ProgressMonitor; import com.intellij.openapi.project.Project; import jetbrains.mps.ide.project.ProjectHelper; import com.intellij.openapi.project.DumbService; import jetbrains.mps.make.MakeNotification; import jetbrains.mps.internal.collections.runtime.IVisitor; import java.util.concurrent.ExecutionException; import jetbrains.mps.messages.IMessageHandler; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.messages.Message; import jetbrains.mps.messages.MessageKind; import jetbrains.mps.internal.make.runtime.util.FutureValue; import jetbrains.mps.make.dependencies.MakeSequence; import com.intellij.openapi.progress.PerformInBackgroundOption; import com.intellij.openapi.application.ApplicationManager; import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.WindowManager; import jetbrains.mps.internal.make.runtime.backports.ProgressMonitorProgressStrategy; import jetbrains.mps.make.script.IConfigMonitor; import jetbrains.mps.make.script.IJobMonitor; import jetbrains.mps.make.script.IPropertiesPool; import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes; import jetbrains.mps.make.script.IFeedback; import jetbrains.mps.make.facet.ITarget; import jetbrains.mps.internal.make.cfg.GenerateFacetInitializer; import jetbrains.mps.generator.IModifiableGenerationSettings; import jetbrains.mps.generator.GenerationSettingsProvider; import jetbrains.mps.internal.make.cfg.TextGenFacetInitializer; import jetbrains.mps.internal.make.cfg.JavaCompileFacetInitializer; import jetbrains.mps.compiler.JavaCompilerOptionsComponent; import jetbrains.mps.make.script.IOption; import jetbrains.mps.make.script.IQuery; import jetbrains.mps.internal.make.runtime.script.MessageFeedbackStrategy; import jetbrains.mps.make.script.IProgress; public class WorkbenchMakeService extends AbstractMakeService implements IMakeService, ApplicationComponent { private static Logger LOG = LogManager.getLogger(WorkbenchMakeService.class); private static WorkbenchMakeService INSTANCE = null; private AtomicMarkableReference<MakeSession> currentSessionStickyMark = new AtomicMarkableReference<MakeSession>(null, false); private volatile AtomicReference<Future<IResult>> currentProcess = new AtomicReference<Future<IResult>>(); private List<IMakeNotificationListener> listeners = Collections.synchronizedList(ListSequence.fromList(new ArrayList<IMakeNotificationListener>())); public WorkbenchMakeService() { } @Override public void initComponent() { INSTANCE = this; IMakeService.INSTANCE.set(this); } @Override public void disposeComponent() { IMakeService.INSTANCE.set(null); INSTANCE = null; } @NonNls @NotNull @Override public String getComponentName() { return "Workbench Make Service"; } private boolean isInstance() { return this == INSTANCE; } @Override public Future<IResult> make(MakeSession session, Iterable<? extends IResource> resources, IScript script, IScriptController controller, @NotNull ProgressMonitor monitor) { this.checkValidUsage(); this.checkValidSession(session); Future<IResult> result = null; try { awaitCurrentProcess(); result = _doMake(resources, script, controller, monitor); } finally { if (result == null || result.isDone()) { this.attemptCloseSession(); } } return result; } @Override public boolean isSessionActive() { this.checkValidUsage(); return this.getSession() != null; } @Override public boolean openNewSession(MakeSession session) { this.checkValidUsage(); Project ideaProject = ProjectHelper.toIdeaProject(session.getProject()); assert ideaProject != null; if (DumbService.getInstance(ideaProject).isDumb()) { DumbService.getInstance(ideaProject).showDumbModeNotification("Generation is not available until indices are built"); return false; } if (!(currentSessionStickyMark.compareAndSet(null, session, false, session.isSticky()))) { return false; } notifyListeners(new MakeNotification(this, MakeNotification.Kind.SESSION_OPENED)); return true; } @Override public void closeSession(MakeSession session) { this.checkValidUsage(); this.checkValidSession(session); currentSessionStickyMark.attemptMark(session, false); Future<IResult> cp = currentProcess.get(); if (cp == null || cp.isDone()) { if (currentSessionStickyMark.compareAndSet(session, null, false, false) || currentSessionStickyMark.compareAndSet(session, null, true, false)) { notifyListeners(new MakeNotification(this, MakeNotification.Kind.SESSION_CLOSED)); } } } /*package*/ MakeSession getSession() { return currentSessionStickyMark.getReference(); } @Override public void addListener(IMakeNotificationListener listener) { checkValidUsage(); ListSequence.fromList(listeners).addElement(listener); } @Override public void removeListener(IMakeNotificationListener listener) { checkValidUsage(); ListSequence.fromList(listeners).removeElement(listener); } private void notifyListeners(final MakeNotification notification) { ListSequence.fromList(ListSequence.fromListWithValues(new ArrayList<IMakeNotificationListener>(), listeners)).visitAll(new IVisitor<IMakeNotificationListener>() { public void visit(IMakeNotificationListener li) { li.handleNotification(notification); } }); } private void attemptCloseSession() { boolean[] mark = new boolean[1]; MakeSession sess = currentSessionStickyMark.get(mark); if (sess != null && !(mark[0]) && currentSessionStickyMark.compareAndSet(sess, null, false, false)) { notifyListeners(new MakeNotification(this, MakeNotification.Kind.SESSION_CLOSED)); } } private void abortSession() { boolean[] mark = new boolean[1]; MakeSession sess = currentSessionStickyMark.get(mark); currentSessionStickyMark.set(null, false); if (sess != null) { notifyListeners(new MakeNotification(this, MakeNotification.Kind.SESSION_CLOSED)); } } private synchronized void awaitCurrentProcess() { Future<IResult> proc = this.currentProcess.get(); try { if (proc != null && !(proc.isDone())) { proc.get(); } } catch (InterruptedException ignore) { } catch (ExecutionException ignore) { } finally { this.currentProcess.set(null); } } private Future<IResult> _doMake(Iterable<? extends IResource> inputRes, final IScript defaultScript, IScriptController controller, @NotNull ProgressMonitor monitor) { String scrName = ((this.getSession().isCleanMake() ? "Rebuild" : "Make")); IMessageHandler mh = this.getSession().getMessageHandler(); if (Sequence.fromIterable(inputRes).isEmpty()) { if (this.getSession().isCleanMake()) { String msg = scrName + " aborted: nothing to do"; mh.handle(new Message(MessageKind.ERROR, msg)); this.displayInfo(msg); return new FutureValue<IResult>(new IResult.FAILURE(null)); } else { this.displayInfo("Everything is up to date"); return new FutureValue<IResult>(new IResult.SUCCESS(null)); } } final MakeSession session = getSession(); MakeSequence makeSeq = new MakeSequence(inputRes, defaultScript, session); Project ideaPrj = ProjectHelper.toIdeaProject(session.getProject()); final MakeTask task = new MakeTask(ideaPrj, scrName, makeSeq, new WorkbenchMakeService.Controller(controller, mh), mh, PerformInBackgroundOption.DEAF) { @Override protected void aboutToStart() { notifyListeners(new MakeNotification(WorkbenchMakeService.this, MakeNotification.Kind.SCRIPT_ABOUT_TO_START)); } @Override protected void done() { currentProcess.compareAndSet(this, null); attemptCloseSession(); notifyListeners(new MakeNotification(WorkbenchMakeService.this, MakeNotification.Kind.SCRIPT_FINISHED)); } @Override protected void displayInfo(String info) { WorkbenchMakeService.this.displayInfo(info); } }; try { getSession().doExecute(new Runnable() { public void run() { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { IdeEventQueue.getInstance().flushQueue(); if (currentProcess.compareAndSet(null, task)) { ProgressManager.getInstance().run(task); } else { throw new IllegalStateException("unexpected: make process is already running"); } IdeEventQueue.getInstance().flushQueue(); } }); } }); } catch (RuntimeException rex) { // abort session if (currentProcess.get() == null) { abortSession(); } throw rex; } return task; } private void checkValidUsage() { if (INSTANCE == null) { throw new IllegalStateException("already disposed"); } if (!(isInstance())) { throw new IllegalStateException("invalid usage of service"); } } public void checkValidSession(MakeSession session) { if (!((this.getSession() == session))) { throw new IllegalStateException("invalid session"); } } private void displayInfo(String info) { IdeFrame frame = WindowManager.getInstance().getIdeFrame(ProjectHelper.toIdeaProject(this.getSession().getProject())); if (frame != null) { frame.getStatusBar().setInfo(info); } } private class Controller extends IScriptController.Stub { private final ProgressMonitorProgressStrategy pmps; private final IScriptController delegateScrCtr; private IConfigMonitor delegateConfMon; private IConfigMonitor confMon; private IJobMonitor jobMon; private IMessageHandler mh; private IPropertiesPool predParamPool; public Controller(IScriptController delegate, IMessageHandler mh) { this.delegateScrCtr = delegate; this.pmps = new ProgressMonitorProgressStrategy(); this.mh = mh; init(); } @Override public void runConfigWithMonitor(final _FunctionTypes._void_P1_E0<? super IConfigMonitor> code) { if (delegateScrCtr != null) { delegateScrCtr.runConfigWithMonitor(new _FunctionTypes._void_P1_E0<IConfigMonitor>() { public void invoke(IConfigMonitor c) { try { Controller.this.delegateConfMon = c; code.invoke(confMon); } finally { Controller.this.delegateConfMon = null; } } }); } else { code.invoke(confMon); } } @Override public void runJobWithMonitor(_FunctionTypes._void_P1_E0<? super IJobMonitor> code) { try { code.invoke(jobMon); } catch (RuntimeException e) { WorkbenchMakeService.LOG.debug("Error running job", e); jobMon.reportFeedback(new IFeedback.ERROR("Error running job", e)); throw e; } } @Override public void setup(IPropertiesPool ppool, Iterable<ITarget> targets, Iterable<? extends IResource> input) { // todo: why should we specify project only for Generate facet? ppool.setPredecessor(predParamPool); predParamPool = ppool; new GenerateFacetInitializer().populate(ppool); IModifiableGenerationSettings genSettings = GenerationSettingsProvider.getInstance().getGenerationSettings(); new TextGenFacetInitializer().generateDebugInfo(genSettings.isGenerateDebugInfo()).populate(ppool); new JavaCompileFacetInitializer().setJavaCompileOptions(JavaCompilerOptionsComponent.getInstance().getJavaCompilerOptions(getSession().getProject())).populate(ppool); if (delegateScrCtr != null) { delegateScrCtr.setup(ppool, targets, input); } } @Override public void useMonitor(ProgressMonitor monitor) { pmps.reset(monitor); } private void init() { final MakeSession makeSession = WorkbenchMakeService.this.getSession(); this.confMon = new IConfigMonitor.Stub(makeSession) { @Override public <T extends IOption> T relayQuery(IQuery<T> query) { T opt = null; if (delegateConfMon != null) { opt = delegateConfMon.relayQuery(query); } return (opt != null ? opt : new UIQueryRelayStrategy().relayQuery(query, makeSession.getProject())); } @Override public boolean stopRequested() { return pmps.isCanceled(); } @Override public void reportFeedback(IFeedback fdbk) { new MessageFeedbackStrategy(mh).reportFeedback(fdbk); } @Override public IProgress currentProgress() { return pmps.currentProgress(); } }; this.jobMon = confMon; } } }