/*
* Copyright 2003-2017 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.jps.build;
import jetbrains.mps.generator.DefaultModifiableGenerationSettings;
import jetbrains.mps.generator.GenerationFacade;
import jetbrains.mps.generator.GenerationSettingsProvider;
import jetbrains.mps.idea.core.make.MPSMakeConstants;
import jetbrains.mps.internal.collections.runtime.ISelector;
import jetbrains.mps.internal.collections.runtime.ISequence;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.jps.project.JpsMPSProject;
import jetbrains.mps.make.MakeSession;
import jetbrains.mps.make.facet.IFacet;
import jetbrains.mps.make.resources.IResource;
import jetbrains.mps.make.script.IConfigMonitor.Stub;
import jetbrains.mps.make.script.IFeedback;
import jetbrains.mps.make.script.IJobMonitor;
import jetbrains.mps.make.script.IResult;
import jetbrains.mps.make.script.IScript;
import jetbrains.mps.make.script.IScriptController;
import jetbrains.mps.make.script.ScriptBuilder;
import jetbrains.mps.messages.IMessage;
import jetbrains.mps.messages.IMessageHandler;
import jetbrains.mps.smodel.resources.MResource;
import jetbrains.mps.smodel.resources.ModelsToResources;
import jetbrains.mps.tool.builder.make.BuildMakeService;
import jetbrains.mps.tool.builder.make.ReducedMakeFacetConfiguration;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.builders.java.JavaBuilderUtil;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ModuleBuildTarget;
import org.jetbrains.jps.incremental.ModuleLevelBuilder.OutputConsumer;
import org.jetbrains.jps.incremental.messages.BuildMessage.Kind;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.mps.openapi.model.SModel;
import java.lang.Iterable;
import java.util.Collection;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* User: fyodor
* Date: 12/19/12
* TODO Something with {@link ReducedMakeFacetConfiguration#getFileHashes()}. It is possible to persist any caches by a jps mechanism.
*/
public class MPSMakeMediator {
@NonNls
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("jetbrains.mps.idea.core.MPSCoreBundle");
private final JpsMPSProject myProject;
private final CompileContext myContext;
private final Map<SModel, ModuleBuildTarget> myModelToTargetMap;
private final OutputConsumer myOutputConsumer;
private final JpsMpsMessageHandler myMessageHandler = new JpsMpsMessageHandler();
public MPSMakeMediator(JpsMPSProject project, Map<SModel, ModuleBuildTarget> modelToTargetToMake, CompileContext context, OutputConsumer outputConsumer) {
myProject = project;
myModelToTargetMap = modelToTargetToMake;
myContext = context;
myOutputConsumer = outputConsumer;
}
/**
* @return true if successful
*/
public boolean build() {
GenerationSettingsProvider.getInstance().setGenerationSettings(new DefaultModifiableGenerationSettings());
Iterable<MResource> resources = collectResources(myModelToTargetMap.keySet());
GenerationPathsController pathsController = new GenerationPathsController(myProject, myContext, resources);
pathsController.init(myModelToTargetMap.values());
BuildMakeService buildMakeService = new BuildMakeService();
MakeSession makeSession = createCleanMakeSession();
final MakeFacetWrapper makeFacetWrapper = new MakeFacetWrapper(myContext, makeSession, pathsController);
ReducedMakeFacetConfiguration makeFacetConfiguration = makeFacetWrapper.constructMakeFacetConfiguration();
IScriptController scriptCtl = makeFacetWrapper.configureFacets();
try {
Future<IResult> res = buildMakeService.make(makeSession, resources, null, scriptCtl);
boolean success = res.get().isSucessful();
final MPSMakeFilesAfterProcessor afterProcessor = new MPSMakeFilesAfterProcessor(myModelToTargetMap, pathsController, myOutputConsumer, myContext);
success &= afterProcessor.process(makeFacetConfiguration);
return success;
} catch (InterruptedException e) {
reportError(BUNDLE.getString("error.while.make"), e);
} catch (ExecutionException e) {
reportError(BUNDLE.getString("error.while.make"), e);
}
return false;
}
private MakeSession createCleanMakeSession() {
return new MakeSession(myProject, myMessageHandler, true) {
@Override
public IScript toScript(ScriptBuilder scriptBuilder) {
scriptBuilder.withFacetNames(
new IFacet.Name("jetbrains.mps.make.reduced.ReportFiles"),
new IFacet.Name("jetbrains.mps.make.reduced.CollectHashes"));
return scriptBuilder.toScript();
}
};
}
private void reportError(String msg, Throwable e) {
myContext.processMessage(new CompilerMessage(msg, Kind.ERROR, e.getMessage()));
}
private Iterable<MResource> collectResources(final Collection<SModel> models) {
final ISequence<SModel> generatableModels = Sequence.fromIterable(models).where(new IWhereFilter<SModel>() {
@Override
public boolean accept(SModel smd) {
return GenerationFacade.canGenerate(smd);
}
});
boolean dirtyOnly = JavaBuilderUtil.isCompileJavaIncrementally(myContext);
final Iterable<IResource> modelsResources = new ModelsToResources(generatableModels).resources(dirtyOnly);
return Sequence.fromIterable(modelsResources).select(new ISelector<IResource, MResource>() {
@Override
public MResource select(IResource r) {
return (MResource) r;
}
});
}
private class JpsMpsMessageHandler implements IMessageHandler {
@Override
public void handle(@NotNull IMessage msg) {
switch (msg.getKind()) {
case ERROR:
processMessage(msg, Kind.ERROR);
break;
case WARNING:
processMessage(msg, Kind.WARNING);
break;
case INFORMATION:
processMessage(msg, Kind.INFO);
break;
default:
}
}
private void processMessage(IMessage msg, Kind kind) {
myContext.processMessage(new CompilerMessage(MPSMakeConstants.BUILDER_ID, kind, msg.getText()));
}
}
private static class MakeFacetWrapper {
private final CompileContext myContext;
private final MakeSession myMakeSession;
private final GenerationPathsController myPathsController;
private ReducedMakeFacetConfiguration myMakeFacetConfiguration;
public MakeFacetWrapper(CompileContext context, MakeSession makeSession, GenerationPathsController pathsController) {
myContext = context;
myMakeSession = makeSession;
myPathsController = pathsController;
}
public IScriptController configureFacets() {
return myMakeFacetConfiguration.configureFacets(myMakeSession);
}
public ReducedMakeFacetConfiguration constructMakeFacetConfiguration() {
final boolean isMake = JavaBuilderUtil.isCompileJavaIncrementally(myContext);
myMakeFacetConfiguration = new ReducedMakeFacetConfiguration(myPathsController.getRedirects(), !isMake);
return myMakeFacetConfiguration;
}
}
}