package jetbrains.mps.testbench;
/*Generated by MPS */
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import jetbrains.mps.project.Project;
import org.jetbrains.mps.openapi.module.SModule;
import java.util.Map;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import java.io.File;
import java.io.IOException;
import jetbrains.mps.generator.GenerationOptions;
import jetbrains.mps.make.script.IResult;
import jetbrains.mps.make.script.IScript;
import jetbrains.mps.make.MakeSession;
import jetbrains.mps.internal.make.cfg.TextGenFacetInitializer;
import jetbrains.mps.internal.make.cfg.MakeFacetInitializer;
import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes;
import jetbrains.mps.vfs.IFile;
import jetbrains.mps.internal.make.cfg.GenerateFacetInitializer;
import jetbrains.mps.make.script.IScriptController;
import jetbrains.mps.progress.EmptyProgressMonitor;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.internal.collections.runtime.IMapping;
import jetbrains.mps.vfs.FileSystem;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.internal.collections.runtime.IVisitor;
import difflib.Patch;
import difflib.DiffUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import jetbrains.mps.util.FileUtil;
import java.util.Queue;
import jetbrains.mps.internal.collections.runtime.QueueSequence;
import java.util.LinkedList;
import jetbrains.mps.smodel.ModelAccess;
import jetbrains.mps.util.Computable;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.util.SNodeOperations;
import jetbrains.mps.make.script.ScriptBuilder;
import jetbrains.mps.make.facet.IFacet;
import jetbrains.mps.make.facet.ITarget;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.internal.collections.runtime.ITranslator2;
import jetbrains.mps.make.resources.IResource;
import jetbrains.mps.smodel.ModelAccessHelper;
import java.util.Collections;
import jetbrains.mps.generator.GenerationFacade;
import jetbrains.mps.smodel.resources.ModelsToResources;
import jetbrains.mps.messages.IMessageHandler;
import jetbrains.mps.messages.IMessage;
import java.io.StringWriter;
import java.io.PrintWriter;
public class ModuleGenerationHolder {
private static String[] BINARY_FILE_EXTENSIONS = new String[]{".png", ".gif"};
private Set<String> ignoredFiles = SetSequence.fromSetAndArray(new HashSet<String>(), "generated", "dependencies", "exports");
private final Project project;
private final SModule module;
private String tmpPath;
private Map<String, String> path2tmp = MapSequence.fromMap(new HashMap<String, String>());
private final ModuleGenerationHolder.MyMessageHandler myMessageHandler = new ModuleGenerationHolder.MyMessageHandler();
private boolean isSuccessful;
public ModuleGenerationHolder(SModule module, Project project) {
this.module = module;
this.project = project;
File tmpDir;
try {
tmpDir = File.createTempFile("projecttest", "tmp");
tmpDir.delete();
tmpDir.mkdir();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
this.tmpPath = tmpDir.getAbsolutePath();
}
public void build() throws Exception {
if (!(needsGeneration())) {
isSuccessful = true;
return;
}
// sanity check build() doesn't come after diff() (due to broken test method ordering)
assert tmpPath != null;
final GenerationOptions.OptionsBuilder optBuilder = GenerationOptions.getDefaults();
boolean isParallel = "true".equalsIgnoreCase(System.getProperty("parallel.generation"));
if (isParallel) {
optBuilder.strictMode(true).generateInParallel(isParallel, 8);
}
IResult result;
IScript scr = ModuleGenerationHolder.defaultScriptBuilder().toScript();
final MakeSession session = new MakeSession(project, myMessageHandler, true);
// trace.info is useless for tests, however we do keep these files in repo, and diffModule test
// fails if we don't generate one here
TextGenFacetInitializer tgfi = new TextGenFacetInitializer().generateDebugInfo(true);
MakeFacetInitializer mfi = new MakeFacetInitializer().setPathToFile(new _FunctionTypes._return_P1_E0<IFile, String>() {
public IFile invoke(String path) {
return tmpFile(path);
}
});
GenerateFacetInitializer gfi = new GenerateFacetInitializer().setGenerationOptions(optBuilder);
IScriptController ctl = new IScriptController.Stub2(session, tgfi, mfi, gfi);
result = new TestMakeService().make(session, ModuleGenerationHolder.collectResources(project, module), scr, ctl, new EmptyProgressMonitor()).get();
isSuccessful = result != null && result.isSucessful();
}
public boolean isBuildSuccessful() {
return isSuccessful;
}
public List<String> buildErrors() {
return myMessageHandler.getGenerationErrors();
}
public List<String> buildWarns() {
return myMessageHandler.getGenerationWarnings();
}
/*package*/ boolean hasFilesGenerated() {
return !(MapSequence.fromMap(path2tmp).isEmpty());
}
public List<String> diff() {
List<String> diffs = ListSequence.fromList(new ArrayList<String>());
for (IMapping<String, String> p2t : MapSequence.fromMap(path2tmp).mappingsSet()) {
File orig = new File(p2t.key());
File revd = new File(p2t.value());
if (orig.exists() && revd.exists() && orig.isDirectory() && revd.isDirectory()) {
diffDirs(orig, revd, diffs);
} else if (!(orig.exists()) && !(revd.exists())) {
ListSequence.fromList(diffs).addElement("None exists: " + orig + " or " + revd);
} else if (!(orig.exists())) {
ListSequence.fromList(diffs).addElement("Created: " + revd);
} else if (!(revd.exists())) {
ListSequence.fromList(diffs).addElement("Removed: " + orig);
} else {
ListSequence.fromList(diffs).addElement("Something weird here: " + orig + " or here " + revd);
}
}
return diffs;
}
private IFile tmpFile(String path) {
if (MapSequence.fromMap(path2tmp).containsKey(path)) {
return FileSystem.getInstance().getFileByPath(MapSequence.fromMap(path2tmp).get(path));
}
int idx = path.indexOf("/");
idx = (idx < 0 ? path.indexOf(File.separator) : idx);
String tmp = tmpPath + "/" + ((idx < 0 ? path.replace(':', '_') : path.substring(idx + 1)));
MapSequence.fromMap(path2tmp).put(path, tmp);
return FileSystem.getInstance().getFileByPath(tmp);
}
private void diffDirs(final File orig, File revd, final List<String> diffs) {
Iterable<String> onames = Sequence.fromArray(orig.list());
Iterable<String> rnames = Sequence.fromArray(revd.list());
if (Sequence.fromIterable(onames).disjunction(Sequence.fromIterable(rnames)).isNotEmpty()) {
Sequence.fromIterable(onames).subtract(Sequence.fromIterable(rnames)).visitAll(new IVisitor<String>() {
public void visit(String it) {
if (ignoredFile(it)) {
return;
}
ListSequence.fromList(diffs).addElement("Removed: " + new File(orig, it));
}
});
Sequence.fromIterable(rnames).subtract(Sequence.fromIterable(onames)).visitAll(new IVisitor<String>() {
public void visit(String it) {
if (ignoredFile(it)) {
return;
}
ListSequence.fromList(diffs).addElement("Created: " + new File(orig, it));
}
});
}
for (String name : Sequence.fromIterable(onames).intersect(Sequence.fromIterable(rnames))) {
if (ignoredFile(name)) {
continue;
}
File onext = new File(orig, name);
File rnext = new File(revd, name);
if (onext.isDirectory() == rnext.isDirectory()) {
if (!(onext.isDirectory())) {
if (isBinary(name)) {
continue;
}
List<String> olines = fileToStrings(onext);
Patch patch = DiffUtils.diff(olines, fileToStrings(rnext));
if (!(patch.getDeltas().isEmpty())) {
ListSequence.fromList(diffs).addSequence(ListSequence.fromList(DiffUtils.generateUnifiedDiff(onext.getPath(), rnext.getPath(), olines, patch, 5)));
}
} else {
diffDirs(onext, rnext, diffs);
}
} else {
ListSequence.fromList(diffs).addElement("Something weird here: " + onext + " or here " + rnext);
}
}
}
private boolean ignoredFile(String fileName) {
return SetSequence.fromSet(ignoredFiles).contains(fileName) || (fileName != null && fileName.startsWith(".hash"));
}
private boolean isBinary(String filename) {
for (String nextExt : BINARY_FILE_EXTENSIONS) {
if (filename.endsWith(nextExt)) {
return true;
}
}
return false;
}
private List<String> fileToStrings(File f) {
List<String> result = ListSequence.fromList(new ArrayList<String>());
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream(f), FileUtil.DEFAULT_CHARSET));
String line;
while ((line = in.readLine()) != null) {
ListSequence.fromList(result).addElement(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignore) {
}
}
}
return result;
}
public void cleanUp() {
for (Queue<File> dirs = QueueSequence.fromQueueAndArray(new LinkedList<File>(), new File(tmpPath)); QueueSequence.fromQueue(dirs).isNotEmpty();) {
File dir = QueueSequence.fromQueue(dirs).removeFirstElement();
dir.deleteOnExit();
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
QueueSequence.fromQueue(dirs).addLastElement(f);
} else {
f.deleteOnExit();
}
}
}
this.tmpPath = null;
MapSequence.fromMap(path2tmp).clear();
myMessageHandler.cleanUp();
}
/*package*/ boolean needsGeneration() {
return ModelAccess.instance().runReadAction(new Computable<Boolean>() {
public Boolean compute() {
for (SModel descriptor : module.getModels()) {
if (SNodeOperations.isGeneratable(descriptor)) {
return true;
}
}
return false;
}
});
}
private static ScriptBuilder defaultScriptBuilder() {
return new ScriptBuilder().withFacetNames(new IFacet.Name("jetbrains.mps.lang.resources.Binaries"), new IFacet.Name("jetbrains.mps.lang.core.Generate"), new IFacet.Name("jetbrains.mps.lang.core.TextGen"), new IFacet.Name("jetbrains.mps.make.facets.Make"), new IFacet.Name("jetbrains.mps.lang.editor.imageGen.GenerateImages")).withFinalTarget(new ITarget.Name("jetbrains.mps.make.facets.Make.make"));
}
private static Iterable<SModule> withGenerators(Iterable<SModule> modules) {
return Sequence.fromIterable(modules).concat(Sequence.fromIterable(modules).where(new IWhereFilter<SModule>() {
public boolean accept(SModule it) {
return it instanceof Language;
}
}).translate(new ITranslator2<SModule, SModule>() {
public Iterable<SModule> translate(SModule it) {
return (List<SModule>) (List) ((Language) it).getGenerators();
}
}));
}
private static Iterable<IResource> collectResources(Project project, final SModule module) {
return new ModelAccessHelper(project.getModelAccess()).runReadAction(new Computable<Iterable<IResource>>() {
public Iterable<IResource> compute() {
Iterable<SModel> models = Sequence.fromIterable(withGenerators(Collections.singletonList(module))).translate(new ITranslator2<SModule, SModel>() {
public Iterable<SModel> translate(SModule mod) {
return mod.getModels();
}
}).where(new IWhereFilter<SModel>() {
public boolean accept(SModel it) {
return GenerationFacade.canGenerate(it);
}
});
return new ModelsToResources(models).resources(false);
}
});
}
private static class MyMessageHandler implements IMessageHandler {
private final List<String> myGenerationErrors = new ArrayList<String>();
private final List<String> myGenerationWarnings = new ArrayList<String>();
private MyMessageHandler() {
}
@Override
public void handle(IMessage msg) {
switch (msg.getKind()) {
case ERROR:
if (msg.getException() != null) {
StringWriter writer = new StringWriter();
msg.getException().printStackTrace(new PrintWriter(writer));
myGenerationErrors.add(msg.getText() + ": " + writer.getBuffer().toString());
} else {
myGenerationErrors.add(msg.getText());
}
break;
case WARNING:
myGenerationWarnings.add(msg.getText());
break;
case INFORMATION:
break;
default:
}
}
public List<String> getGenerationErrors() {
return myGenerationErrors;
}
public List<String> getGenerationWarnings() {
return myGenerationWarnings;
}
public void cleanUp() {
myGenerationErrors.clear();
myGenerationWarnings.clear();
}
}
}