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;
}
}
}