KotlinCompiler can build a jar file
This commit is contained in:
@@ -10,6 +10,7 @@ import com.intellij.openapi.util.io.FileUtil;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.psi.PsiManager;
|
import com.intellij.psi.PsiManager;
|
||||||
|
import com.intellij.util.Processor;
|
||||||
import com.sampullara.cli.Args;
|
import com.sampullara.cli.Args;
|
||||||
import com.sampullara.cli.Argument;
|
import com.sampullara.cli.Argument;
|
||||||
import org.jetbrains.jet.JetCoreEnvironment;
|
import org.jetbrains.jet.JetCoreEnvironment;
|
||||||
@@ -25,22 +26,29 @@ import org.jetbrains.jet.lang.psi.JetNamespace;
|
|||||||
import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
|
import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JavaDefaultImports;
|
import org.jetbrains.jet.lang.resolve.java.JavaDefaultImports;
|
||||||
|
import org.jetbrains.jet.plugin.JetMainDetector;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.jar.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author yole
|
* @author yole
|
||||||
* @author alex.tkachman
|
* @author alex.tkachman
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||||
public class KotlinCompiler {
|
public class KotlinCompiler {
|
||||||
public static class Arguments {
|
public static class Arguments {
|
||||||
@Argument(value = "output", description = "output directory")
|
@Argument(value = "output", description = "output directory")
|
||||||
public String outputDir;
|
public String outputDir;
|
||||||
|
@Argument(value = "jar", description = "jar file name")
|
||||||
|
public String jar;
|
||||||
@Argument(value = "src", description = "source file or directory", required = true)
|
@Argument(value = "src", description = "source file or directory", required = true)
|
||||||
public String src;
|
public String src;
|
||||||
}
|
}
|
||||||
@@ -52,7 +60,7 @@ public class KotlinCompiler {
|
|||||||
Args.parse(arguments, args);
|
Args.parse(arguments, args);
|
||||||
}
|
}
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
System.out.println("Usage: KotlinCompiler -output <outputDir> -src <filename or dirname>");
|
System.out.println("Usage: KotlinCompiler [-output <outputDir>|-jar <jarFileName>] -src <filename or dirname>");
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -78,6 +86,7 @@ public class KotlinCompiler {
|
|||||||
Project project = environment.getProject();
|
Project project = environment.getProject();
|
||||||
GenerationState generationState = new GenerationState(project, false);
|
GenerationState generationState = new GenerationState(project, false);
|
||||||
List<JetNamespace> namespaces = Lists.newArrayList();
|
List<JetNamespace> namespaces = Lists.newArrayList();
|
||||||
|
String mainClass = null;
|
||||||
if(vFile.isDirectory()) {
|
if(vFile.isDirectory()) {
|
||||||
File dir = new File(vFile.getPath());
|
File dir = new File(vFile.getPath());
|
||||||
addFiles(environment, project, namespaces, dir);
|
addFiles(environment, project, namespaces, dir);
|
||||||
@@ -85,7 +94,11 @@ public class KotlinCompiler {
|
|||||||
else {
|
else {
|
||||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile);
|
PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile);
|
||||||
if (psiFile instanceof JetFile) {
|
if (psiFile instanceof JetFile) {
|
||||||
namespaces.add(((JetFile) psiFile).getRootNamespace());
|
final JetNamespace namespace = ((JetFile) psiFile).getRootNamespace();
|
||||||
|
if (JetMainDetector.hasMain(namespace.getDeclarations())) {
|
||||||
|
mainClass = namespace.getFQName() + ".namespace";
|
||||||
|
}
|
||||||
|
namespaces.add(namespace);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
System.out.print("Not a Kotlin file: " + vFile.getPath());
|
System.out.print("Not a Kotlin file: " + vFile.getPath());
|
||||||
@@ -102,25 +115,113 @@ public class KotlinCompiler {
|
|||||||
generationState.compileCorrectNamespaces(bindingContext, namespaces);
|
generationState.compileCorrectNamespaces(bindingContext, namespaces);
|
||||||
|
|
||||||
final ClassFileFactory factory = generationState.getFactory();
|
final ClassFileFactory factory = generationState.getFactory();
|
||||||
if(arguments.outputDir == null) {
|
if (arguments.jar != null) {
|
||||||
System.out.println("Output directory is not specified - no files will be saved to the disk");
|
writeToJar(factory, arguments.jar, mainClass, true);
|
||||||
|
}
|
||||||
|
else if (arguments.outputDir != null) {
|
||||||
|
writeToOutputDirectory(factory, arguments.outputDir);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
List<String> files = factory.files();
|
System.out.println("Output directory or jar file is not specified - no files will be saved to the disk");
|
||||||
for (String file : files) {
|
|
||||||
File target = new File(arguments.outputDir, file);
|
|
||||||
try {
|
|
||||||
FileUtil.writeToFile(target, factory.asBytes(file));
|
|
||||||
System.out.println("Generated classfile: " + target);
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.out.println(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void writeToJar(ClassFileFactory factory, String jar, String mainClass, boolean includeRuntime) {
|
||||||
|
try {
|
||||||
|
Manifest manifest = new Manifest();
|
||||||
|
final Attributes mainAttributes = manifest.getMainAttributes();
|
||||||
|
mainAttributes.putValue("Manifest-Version", "1.0");
|
||||||
|
mainAttributes.putValue("Created-By", "JetBrains Kotlin");
|
||||||
|
if (mainClass != null) {
|
||||||
|
mainAttributes.putValue("Main-Class", mainClass);
|
||||||
|
}
|
||||||
|
FileOutputStream fos = new FileOutputStream(jar);
|
||||||
|
JarOutputStream stream = new JarOutputStream(fos, manifest);
|
||||||
|
try {
|
||||||
|
for (String file : factory.files()) {
|
||||||
|
stream.putNextEntry(new JarEntry(file));
|
||||||
|
stream.write(factory.asBytes(file));
|
||||||
|
}
|
||||||
|
if (includeRuntime) {
|
||||||
|
writeRuntimeToJar(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
stream.close();
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Failed to generate jar file: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeRuntimeToJar(final JarOutputStream stream) throws IOException {
|
||||||
|
URL url = KotlinCompiler.class.getClassLoader().getResource("jet/JetObject.class");
|
||||||
|
if (url == null) {
|
||||||
|
System.out.println("Couldn't find runtime library");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String protocol = url.getProtocol();
|
||||||
|
final String path = url.getPath();
|
||||||
|
if (protocol.equals("file")) { // unpacked runtime
|
||||||
|
final File stdlibDir = new File(path).getParentFile().getParentFile();
|
||||||
|
FileUtil.processFilesRecursively(stdlibDir, new Processor<File>() {
|
||||||
|
@Override
|
||||||
|
public boolean process(File file) {
|
||||||
|
if (file.isDirectory()) return true;
|
||||||
|
final String relativePath = FileUtil.getRelativePath(stdlibDir, file);
|
||||||
|
try {
|
||||||
|
stream.putNextEntry(new JarEntry(FileUtil.toSystemIndependentName(relativePath)));
|
||||||
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
try {
|
||||||
|
FileUtil.copy(fis, stream);
|
||||||
|
} finally {
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (protocol.equals("jar")) {
|
||||||
|
File jar = new File(path.substring(path.indexOf(":") + 1, path.indexOf("!/")));
|
||||||
|
JarInputStream jis = new JarInputStream(new FileInputStream(jar));
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
JarEntry e = jis.getNextJarEntry();
|
||||||
|
if (e == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stream.putNextEntry(e);
|
||||||
|
FileUtil.copy(jis, stream);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
jis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.out.println("Couldn't copy runtime library from " + url + ", protocol " + protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeToOutputDirectory(ClassFileFactory factory, final String outputDir) {
|
||||||
|
List<String> files = factory.files();
|
||||||
|
for (String file : files) {
|
||||||
|
File target = new File(outputDir, file);
|
||||||
|
try {
|
||||||
|
FileUtil.writeToFile(target, factory.asBytes(file));
|
||||||
|
System.out.println("Generated classfile: " + target);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class ErrorCollector {
|
private static class ErrorCollector {
|
||||||
Multimap<PsiFile,DiagnosticWithTextRange> maps = LinkedHashMultimap.<PsiFile, DiagnosticWithTextRange>create();
|
Multimap<PsiFile,DiagnosticWithTextRange> maps = LinkedHashMultimap.<PsiFile, DiagnosticWithTextRange>create();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.jetbrains.jet.plugin;
|
||||||
|
|
||||||
|
import org.jetbrains.jet.lang.psi.JetDeclaration;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetNamedFunction;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetParameter;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetTypeReference;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yole
|
||||||
|
*/
|
||||||
|
public class JetMainDetector {
|
||||||
|
private JetMainDetector() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasMain(List<JetDeclaration> declarations) {
|
||||||
|
for (JetDeclaration declaration : declarations) {
|
||||||
|
if (declaration instanceof JetNamedFunction) {
|
||||||
|
JetNamedFunction function = (JetNamedFunction) declaration;
|
||||||
|
if ("main".equals(function.getName())) {
|
||||||
|
List<JetParameter> parameters = function.getValueParameters();
|
||||||
|
if (parameters.size() == 1) {
|
||||||
|
JetTypeReference reference = parameters.get(0).getTypeReference();
|
||||||
|
if (reference != null && reference.getText().equals("Array<String>")) { // TODO correct check
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,8 +9,7 @@ import com.intellij.openapi.util.text.StringUtil;
|
|||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
|
import org.jetbrains.jet.plugin.JetMainDetector;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author yole
|
* @author yole
|
||||||
@@ -30,14 +29,14 @@ public class JetRunConfigurationProducer extends RuntimeConfigurationProducer im
|
|||||||
@Override
|
@Override
|
||||||
protected RunnerAndConfigurationSettings createConfigurationByElement(Location location, ConfigurationContext configurationContext) {
|
protected RunnerAndConfigurationSettings createConfigurationByElement(Location location, ConfigurationContext configurationContext) {
|
||||||
JetClass containingClass = (JetClass) location.getParentElement(JetClass.class);
|
JetClass containingClass = (JetClass) location.getParentElement(JetClass.class);
|
||||||
if (containingClass != null && hasMain(containingClass.getDeclarations())) {
|
if (containingClass != null && JetMainDetector.hasMain(containingClass.getDeclarations())) {
|
||||||
mySourceElement = containingClass;
|
mySourceElement = containingClass;
|
||||||
return createConfigurationByQName(location.getModule(), configurationContext, containingClass.getFQName());
|
return createConfigurationByQName(location.getModule(), configurationContext, containingClass.getFQName());
|
||||||
}
|
}
|
||||||
PsiFile psiFile = location.getPsiElement().getContainingFile();
|
PsiFile psiFile = location.getPsiElement().getContainingFile();
|
||||||
if (psiFile instanceof JetFile) {
|
if (psiFile instanceof JetFile) {
|
||||||
JetNamespace namespace = ((JetFile) psiFile).getRootNamespace();
|
JetNamespace namespace = ((JetFile) psiFile).getRootNamespace();
|
||||||
if (hasMain(namespace.getDeclarations())) {
|
if (JetMainDetector.hasMain(namespace.getDeclarations())) {
|
||||||
mySourceElement = namespace;
|
mySourceElement = namespace;
|
||||||
return createConfigurationByQName(location.getModule(), configurationContext, namespace.getFQName() + ".namespace");
|
return createConfigurationByQName(location.getModule(), configurationContext, namespace.getFQName() + ".namespace");
|
||||||
}
|
}
|
||||||
@@ -45,25 +44,6 @@ public class JetRunConfigurationProducer extends RuntimeConfigurationProducer im
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasMain(List<JetDeclaration> declarations) {
|
|
||||||
for (JetDeclaration declaration : declarations) {
|
|
||||||
if (declaration instanceof JetNamedFunction) {
|
|
||||||
JetNamedFunction function = (JetNamedFunction) declaration;
|
|
||||||
if ("main".equals(function.getName())) {
|
|
||||||
List<JetParameter> parameters = function.getValueParameters();
|
|
||||||
if (parameters.size() == 1) {
|
|
||||||
JetTypeReference reference = parameters.get(0).getTypeReference();
|
|
||||||
if (reference != null && reference.getText().equals("Array<String>")) { // TODO correct check
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RunnerAndConfigurationSettings createConfigurationByQName(Module module, ConfigurationContext context, String fqName) {
|
private RunnerAndConfigurationSettings createConfigurationByQName(Module module, ConfigurationContext context, String fqName) {
|
||||||
RunnerAndConfigurationSettings settings = cloneTemplateConfiguration(module.getProject(), context);
|
RunnerAndConfigurationSettings settings = cloneTemplateConfiguration(module.getProject(), context);
|
||||||
JetRunConfiguration configuration = (JetRunConfiguration) settings.getConfiguration();
|
JetRunConfiguration configuration = (JetRunConfiguration) settings.getConfiguration();
|
||||||
|
|||||||
Reference in New Issue
Block a user