K2JSCompiler cli:

Refactor.
Support zipped library sources as parameter.
Support output file path as parameter.
This commit is contained in:
pTalanov
2012-05-04 22:48:27 +04:00
parent d23cca223d
commit 397928c4e4
6 changed files with 264 additions and 144 deletions
@@ -18,19 +18,24 @@ package org.jetbrains.jet.cli.js;
import com.google.common.base.Predicates;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import jet.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.analyzer.AnalyzeExhaust;
import org.jetbrains.jet.cli.common.CLICompiler;
import org.jetbrains.jet.cli.common.ExitCode;
import org.jetbrains.jet.cli.common.messages.*;
import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport;
import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
import org.jetbrains.jet.cli.common.messages.PrintingMessageCollector;
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
import org.jetbrains.k2js.config.Config;
import org.jetbrains.k2js.config.ZippedLibrarySourcesConfig;
import org.jetbrains.k2js.facade.K2JSTranslator;
import java.util.List;
@@ -62,11 +67,46 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments, K2JSCompile
return ExitCode.INTERNAL_ERROR;
}
final JetCoreEnvironment environmentForJS = getEnvironment(arguments, rootDisposable);
final Config config = getConfig(arguments, environmentForJS.getProject());
if (analyzeAndReportErrors(messageCollector, environmentForJS.getSourceFiles(), config)) {
return ExitCode.COMPILATION_ERROR;
}
String outputFile = arguments.outputFile;
if (outputFile == null) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Specify output file via -output", CompilerMessageLocation.NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
}
return translateAndGenerateOutputFile(messageCollector, environmentForJS, config, outputFile);
}
@NotNull
private static JetCoreEnvironment getEnvironment(K2JSCompilerArguments arguments, Disposable rootDisposable) {
final JetCoreEnvironment environmentForJS = JetCoreEnvironment.getCoreEnvironmentForJS(rootDisposable);
environmentForJS.addSources(arguments.srcdir);
return environmentForJS;
}
@NotNull
private static ExitCode translateAndGenerateOutputFile(@NotNull PrintingMessageCollector messageCollector,
@NotNull JetCoreEnvironment environmentForJS, @NotNull Config config, @NotNull String outputFile) {
try {
K2JSTranslator.translateWithCallToMainAndSaveToFile(environmentForJS.getSourceFiles(), outputFile, config,
environmentForJS.getProject());
}
catch (Exception e) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Exception while translating:\n" + e.getMessage(),
CompilerMessageLocation.NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
}
return ExitCode.OK;
}
private static boolean analyzeAndReportErrors(@NotNull PrintingMessageCollector messageCollector,
@NotNull final List<JetFile> sources, @NotNull final Config config) {
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector);
final List<JetFile> sources = environmentForJS.getSourceFiles();
final Config config = Config.getEmptyConfig(environmentForJS.getProject());
analyzerWithCompilerReport.analyzeAndReport(new Function0<AnalyzeExhaust>() {
@Override
public AnalyzeExhaust invoke() {
@@ -75,23 +115,14 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments, K2JSCompile
return AnalyzeExhaust.success(context, JetStandardLibrary.getInstance());
}
}, sources);
if (analyzerWithCompilerReport.hasErrors()) {
return ExitCode.COMPILATION_ERROR;
}
return analyzerWithCompilerReport.hasErrors();
}
if (arguments.outputDir != null) {
try {
K2JSTranslator.translateWithCallToMainAndSaveToFile(sources, arguments.outputDir, config, environmentForJS.getProject());
}
catch (Exception e) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Exception while translating:\n" + e.getMessage(),
CompilerMessageLocation.NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
}
} else {
messageCollector.report(CompilerMessageSeverity.ERROR, "Specify output file via -output", CompilerMessageLocation.NO_LOCATION);
return ExitCode.INTERNAL_ERROR;
@NotNull
private static Config getConfig(@NotNull K2JSCompilerArguments arguments, @NotNull Project project) {
if (arguments.libzip == null) {
return Config.getEmptyConfig(project);
}
return ExitCode.OK;
return new ZippedLibrarySourcesConfig(project, arguments.libzip);
}
}
@@ -22,10 +22,16 @@ import org.jetbrains.jet.cli.common.CompilerArguments;
/**
* @author Pavel Talanov
*/
public class K2JSCompilerArguments extends CompilerArguments {
@Argument(value = "output", description = "Output directory")
public String outputDir;
/**
* NOTE: for now K2JSCompiler supports only minimal amount of parameters required to launch it from the plugin.
* You can specify only one source folder, path to the file where generated file will be stored, path to zipped library sources.
*/
public class K2JSCompilerArguments extends CompilerArguments {
@Argument(value = "output", description = "Output file path")
public String outputFile;
//NOTE: may well be a subject to change soon
@Argument(value = "libzip", description = "Path to zipped lib sources")
public String libzip;
@@ -16,6 +16,7 @@
package org.jetbrains.jet.plugin.compiler;
import com.google.common.collect.Lists;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileScope;
import com.intellij.openapi.compiler.CompilerMessageCategory;
@@ -24,14 +25,17 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Chunk;
import jet.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.plugin.JetFileType;
import org.jetbrains.jet.plugin.k2jsrun.K2JSRunnerUtils;
import org.jetbrains.jet.plugin.project.JsModuleDetector;
import java.io.PrintStream;
import java.util.ArrayList;
import static org.jetbrains.jet.plugin.compiler.CompilerUtils.invokeExecMethod;
import static org.jetbrains.jet.plugin.compiler.CompilerUtils.outputCompilerMessagesAndHandleExitCode;
@@ -59,18 +63,22 @@ public final class K2JSCompiler implements TranslatingCompiler {
return;
}
if (moduleChunk.getNodes().size() != 1) {
context.addMessage(CompilerMessageCategory.ERROR, "K2JSCompiler does not support multiple modules.", null, -1, -1);
final Module module = getModule(context, moduleChunk);
if (module == null) {
return;
}
final Module module = moduleChunk.getNodes().iterator().next();
final CompilerEnvironment environment = CompilerEnvironment.getEnvironmentFor(context, module, /*tests = */ false);
if (!environment.success()) {
environment.reportErrorsTo(context);
return;
}
doCompile(context, sink, module, environment);
}
private static void doCompile(@NotNull final CompileContext context, @NotNull OutputSink sink, @NotNull final Module module,
@NotNull final CompilerEnvironment environment) {
CompilerUtils.OutputItemsCollectorImpl collector = new CompilerUtils.OutputItemsCollectorImpl(environment.getOutput().getPath());
outputCompilerMessagesAndHandleExitCode(context, collector, new Function1<PrintStream, Integer>() {
@Override
@@ -81,31 +89,70 @@ public final class K2JSCompiler implements TranslatingCompiler {
sink.add(environment.getOutput().getPath(), collector.getOutputs(), collector.getSources().toArray(VirtualFile.EMPTY_ARRAY));
}
@Nullable
private static Module getModule(@NotNull CompileContext context, @NotNull Chunk<Module> moduleChunk) {
if (moduleChunk.getNodes().size() != 1) {
context.addMessage(CompilerMessageCategory.ERROR, "K2JSCompiler does not support multiple modules.", null, -1, -1);
return null;
}
return moduleChunk.getNodes().iterator().next();
}
@NotNull
private static Integer execInProcess(@NotNull CompileContext context,
@NotNull CompilerEnvironment environment, @NotNull PrintStream out, @NotNull Module module) {
try {
VirtualFile[] roots = ModuleRootManager.getInstance(module).getSourceRoots();
if (roots.length != 1) {
context.addMessage(CompilerMessageCategory.ERROR, "K2JSCompiler does not support multiple module source roots.", null, -1,
-1);
return -1;
}
String[] commandLineArgs = constructArguments(context.getProject(), context.getModuleOutputDirectory(module), roots[0]);
Object rc = invokeExecMethod(environment, out, context, commandLineArgs, "org.jetbrains.jet.cli.js.K2JSCompiler");
return CompilerUtils.getReturnCodeFromObject(rc);
return doExec(context, environment, out, module);
}
catch (Throwable e) {
context.addMessage(CompilerMessageCategory.ERROR, "Exception while executing compiler: " + e.getMessage(), null, -1, -1);
context.addMessage(CompilerMessageCategory.ERROR, "Exception while executing compiler:\n" + e.getMessage(), null, -1, -1);
}
return -1;
}
private static String[] constructArguments(@NotNull Project project, @NotNull VirtualFile outDir, @NotNull VirtualFile srcDir) {
@NotNull
private static Integer doExec(@NotNull CompileContext context, @NotNull CompilerEnvironment environment, @NotNull PrintStream out,
@NotNull Module module) throws Exception {
VirtualFile[] roots = ModuleRootManager.getInstance(module).getSourceRoots();
if (roots.length != 1) {
context.addMessage(CompilerMessageCategory.ERROR, "K2JSCompiler does not support multiple module source roots.", null, -1, -1);
return -1;
}
String[] commandLineArgs = constructArguments(context.getProject(), context.getModuleOutputDirectory(module), roots[0]);
Object rc = invokeExecMethod(environment, out, context, commandLineArgs, "org.jetbrains.jet.cli.js.K2JSCompiler");
return CompilerUtils.getReturnCodeFromObject(rc);
}
@NotNull
private static String[] constructArguments(@NotNull Project project, @Nullable VirtualFile outDir, @NotNull VirtualFile srcDir) {
ArrayList<String> args = Lists.newArrayList("-tags", "-verbose", "-version");
addPathToSourcesDir(args, srcDir);
addOutputPath(project, outDir, args);
addPathToZippedLib(project, args);
return ArrayUtil.toStringArray(args);
}
private static void addPathToSourcesDir(@NotNull ArrayList<String> args, @NotNull VirtualFile srcDir) {
String srcPath = srcDir.getPath();
String outPath = outDir.getPath();
String outFile = K2JSRunnerUtils.constructPathToGeneratedFile(project, outPath);
return new String[] {"-tags", "-verbose", "-version", "-srcdir", srcPath, "-output", outFile};
args.add("-srcdir");
args.add(srcPath);
}
private static void addOutputPath(@NotNull Project project, @Nullable VirtualFile outDir, @NotNull ArrayList<String> args) {
if (outDir != null) {
String outPath = outDir.getPath();
String outFile = K2JSRunnerUtils.constructPathToGeneratedFile(project, outPath);
args.add("-output");
args.add(outFile);
}
}
private static void addPathToZippedLib(@NotNull Project project, @NotNull ArrayList<String> args) {
String libLocationForProject = JsModuleDetector.getLibLocationForProject(project);
if (libLocationForProject != null) {
args.add("-libzip");
args.add(libLocationForProject);
}
}
@NotNull
@@ -16,113 +16,18 @@
package org.jetbrains.jet.plugin.project;
import com.google.common.collect.Lists;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.k2js.config.Config;
import org.jetbrains.k2js.utils.JetFileUtils;
import org.jetbrains.k2js.config.ZippedLibrarySourcesConfig;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static org.jetbrains.jet.plugin.project.JsModuleDetector.getLibLocationForProject;
/**
* @author Pavel Talanov
*/
public final class IDEAConfig extends Config {
@Nullable
private final String pathToLibZip;
public final class IDEAConfig extends ZippedLibrarySourcesConfig {
public IDEAConfig(@NotNull Project project) {
super(project);
this.pathToLibZip = getLibLocationForProject(project);
}
//TODO: refactor
@Nullable
private static String getLibLocationForProject(@NotNull Project project) {
VirtualFile indicationFile = JsModuleDetector.findIndicationFileInContextRoots(project);
if (indicationFile == null) {
return null;
}
try {
InputStream stream = indicationFile.getInputStream();
String path = FileUtil.loadTextAndClose(stream);
String pathToLibFile = getFirstLine(path);
if (pathToLibFile == null) {
return null;
}
try {
URI pathToLibFileUri = new URI(pathToLibFile);
URI pathToIndicationFileUri = new URI(indicationFile.getPath());
return pathToIndicationFileUri.resolve(pathToLibFileUri).toString();
}
catch (URISyntaxException e) {
return null;
}
}
catch (IOException e) {
return null;
}
}
//TODO: util
@Nullable
private static String getFirstLine(@NotNull String path) throws IOException {
BufferedReader reader = new BufferedReader(new StringReader(path));
try {
return reader.readLine();
}
finally {
reader.close();
}
}
@NotNull
@Override
public List<JetFile> generateLibFiles() {
if (pathToLibZip == null) {
return Collections.emptyList();
}
try {
File file = new File(pathToLibZip);
ZipFile zipFile = new ZipFile(file);
try {
return traverseArchive(zipFile);
}
finally {
zipFile.close();
}
}
catch (IOException e) {
return Collections.emptyList();
}
}
@NotNull
private List<JetFile> traverseArchive(@NotNull ZipFile file) throws IOException {
List<JetFile> result = Lists.newArrayList();
Enumeration<? extends ZipEntry> zipEntries = file.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry entry = zipEntries.nextElement();
if (!entry.isDirectory()) {
InputStream stream = file.getInputStream(entry);
String text = FileUtil.loadTextAndClose(stream);
JetFile jetFile = JetFileUtils.createPsiFile(entry.getName(), text, getProject());
result.add(jetFile);
}
}
return result;
super(project, getLibLocationForProject(project));
}
}
@@ -16,17 +16,24 @@
package org.jetbrains.jet.plugin.project;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
/**
* @author Pavel Talanov
*
* This class has utility functions to determine whether the project (or module) is js project.
* <p/>
* This class has utility functions to determine whether the project (or module) is js project.
*/
public final class JsModuleDetector {
@@ -36,19 +43,59 @@ public final class JsModuleDetector {
}
public static boolean isJsProject(@NotNull Project project) {
return findIndicationFileInContextRoots(project) != null;
return findIndicationFileInContentRoots(project) != null;
}
@Nullable
public static VirtualFile findIndicationFileInContextRoots(@NotNull Project project) {
public static VirtualFile findIndicationFileInContentRoots(@NotNull Project project) {
VirtualFile[] roots = ProjectRootManager.getInstance(project).getContentRoots();
for (VirtualFile root : roots) {
for (VirtualFile child : root.getChildren()) {
if (child.getFileType().equals(FileTypes.PLAIN_TEXT) && child.getName().equals(INDICATION_FILE_NAME)) {
if (child.getName().equals(INDICATION_FILE_NAME)) {
return child;
}
}
}
return null;
}
//TODO: refactor
@Nullable
public static String getLibLocationForProject(@NotNull Project project) {
VirtualFile indicationFile = findIndicationFileInContentRoots(project);
if (indicationFile == null) {
return null;
}
try {
InputStream stream = indicationFile.getInputStream();
String path = FileUtil.loadTextAndClose(stream);
String pathToLibFile = getFirstLine(path);
if (pathToLibFile == null) {
return null;
}
try {
URI pathToLibFileUri = new URI(pathToLibFile);
URI pathToIndicationFileUri = new URI(indicationFile.getPath());
return pathToIndicationFileUri.resolve(pathToLibFileUri).toString();
}
catch (URISyntaxException e) {
return null;
}
}
catch (IOException e) {
return null;
}
}
@Nullable
private static String getFirstLine(@NotNull String path) throws IOException {
BufferedReader reader = new BufferedReader(new StringReader(path));
try {
return reader.readLine();
}
finally {
reader.close();
}
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2010-2012 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 org.jetbrains.k2js.config;
import com.google.common.collect.Lists;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.k2js.utils.JetFileUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* @author Pavel Talanov
*/
public class ZippedLibrarySourcesConfig extends Config {
@Nullable
protected final String pathToLibZip;
public ZippedLibrarySourcesConfig(@NotNull Project project, @Nullable String pathToZip) {
super(project);
pathToLibZip = pathToZip;
}
@NotNull
@Override
public List<JetFile> generateLibFiles() {
if (pathToLibZip == null) {
return Collections.emptyList();
}
try {
File file = new File(pathToLibZip);
ZipFile zipFile = new ZipFile(file);
try {
return traverseArchive(zipFile);
}
finally {
zipFile.close();
}
}
catch (IOException e) {
return Collections.emptyList();
}
}
@NotNull
private List<JetFile> traverseArchive(@NotNull ZipFile file) throws IOException {
List<JetFile> result = Lists.newArrayList();
Enumeration<? extends ZipEntry> zipEntries = file.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry entry = zipEntries.nextElement();
if (!entry.isDirectory()) {
InputStream stream = file.getInputStream(entry);
String text = FileUtil.loadTextAndClose(stream);
JetFile jetFile = JetFileUtils.createPsiFile(entry.getName(), text, getProject());
result.add(jetFile);
}
}
return result;
}
}