pull out code out of KotlinCompiler and into a testable CompileEnvironment; smoke test for CompileEnvironment
This commit is contained in:
@@ -0,0 +1,311 @@
|
|||||||
|
package org.jetbrains.jet.compiler;
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable;
|
||||||
|
import com.intellij.openapi.application.PathManager;
|
||||||
|
import com.intellij.openapi.util.Disposer;
|
||||||
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import com.intellij.util.Processor;
|
||||||
|
import jet.modules.IModuleBuilder;
|
||||||
|
import jet.modules.IModuleSetBuilder;
|
||||||
|
import org.jetbrains.jet.JetCoreEnvironment;
|
||||||
|
import org.jetbrains.jet.codegen.ClassFileFactory;
|
||||||
|
import org.jetbrains.jet.codegen.GeneratedClassLoader;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetNamespace;
|
||||||
|
import org.jetbrains.jet.plugin.JetMainDetector;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.jar.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The environment for compiling a bunch of source files or
|
||||||
|
*
|
||||||
|
* @author yole
|
||||||
|
*/
|
||||||
|
public class CompileEnvironment {
|
||||||
|
private JetCoreEnvironment myEnvironment;
|
||||||
|
private final Disposable myRootDisposable;
|
||||||
|
private PrintStream myErrorStream = System.out;
|
||||||
|
|
||||||
|
public CompileEnvironment() {
|
||||||
|
myRootDisposable = new Disposable() {
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
myEnvironment = new JetCoreEnvironment(myRootDisposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMyErrorStream(PrintStream errorStream) {
|
||||||
|
myErrorStream = errorStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
Disposer.dispose(myRootDisposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean initializeKotlinRuntime() {
|
||||||
|
final File unpackedRuntimePath = getUnpackedRuntimePath();
|
||||||
|
if (unpackedRuntimePath != null) {
|
||||||
|
myEnvironment.addToClasspath(unpackedRuntimePath);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final File runtimeJarPath = getRuntimeJarPath();
|
||||||
|
if (runtimeJarPath != null && runtimeJarPath.exists()) {
|
||||||
|
myEnvironment.addToClasspath(runtimeJarPath);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getUnpackedRuntimePath() {
|
||||||
|
URL url = CompileEnvironment.class.getClassLoader().getResource("jet/JetObject.class");
|
||||||
|
if (url != null && url.getProtocol().equals("file")) {
|
||||||
|
return new File(url.getPath()).getParentFile().getParentFile();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getRuntimeJarPath() {
|
||||||
|
URL url = CompileEnvironment.class.getClassLoader().getResource("jet/JetObject.class");
|
||||||
|
if (url != null && url.getProtocol().equals("jar")) {
|
||||||
|
String path = url.getPath();
|
||||||
|
return new File(path.substring(path.indexOf(":") + 1, path.indexOf("!/")));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJavaRuntime(File rtJarPath) {
|
||||||
|
myEnvironment.addToClasspath(rtJarPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File findActiveRtJar() {
|
||||||
|
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
|
||||||
|
if (systemClassLoader instanceof URLClassLoader) {
|
||||||
|
URLClassLoader loader = (URLClassLoader) systemClassLoader;
|
||||||
|
for (URL url: loader.getURLs()) {
|
||||||
|
if("file".equals(url.getProtocol())) {
|
||||||
|
if(url.getFile().endsWith("/lib/rt.jar")) {
|
||||||
|
return new File(url.getFile());
|
||||||
|
}
|
||||||
|
if(url.getFile().endsWith("/Classes/classes.jar")) {
|
||||||
|
return new File(url.getFile()).getAbsoluteFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void compileModuleScript(String moduleFile) {
|
||||||
|
final IModuleSetBuilder moduleSetBuilder = loadModuleScript(moduleFile);
|
||||||
|
if (moduleSetBuilder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String directory = new File(moduleFile).getParent();
|
||||||
|
for (IModuleBuilder moduleBuilder : moduleSetBuilder.getModules()) {
|
||||||
|
ClassFileFactory moduleFactory = compileModule(moduleBuilder, directory);
|
||||||
|
writeToJar(moduleFactory, new File(directory, moduleBuilder.getModuleName() + ".jar").getPath(), null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IModuleSetBuilder loadModuleScript(String moduleFile) {
|
||||||
|
CompileSession scriptCompileSession = new CompileSession(myEnvironment);
|
||||||
|
scriptCompileSession.addSources(moduleFile);
|
||||||
|
|
||||||
|
URL url = CompileEnvironment.class.getClassLoader().getResource("ModuleBuilder.kt");
|
||||||
|
if (url != null) {
|
||||||
|
String path = url.getPath();
|
||||||
|
if (path.startsWith("file:")) {
|
||||||
|
path = path.substring(5);
|
||||||
|
}
|
||||||
|
final VirtualFile vFile = myEnvironment.getJarFileSystem().findFileByPath(path);
|
||||||
|
if (vFile == null) {
|
||||||
|
throw new CompileEnvironmentException("Couldn't load ModuleBuilder.kt from runtime jar: "+ url);
|
||||||
|
}
|
||||||
|
scriptCompileSession.addSources(vFile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// building from source
|
||||||
|
final String homeDirectory = getHomeDirectory();
|
||||||
|
final File file = new File(homeDirectory, "stdlib/ktSrc/ModuleBuilder.kt");
|
||||||
|
scriptCompileSession.addSources(myEnvironment.getLocalFileSystem().findFileByPath(file.getPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scriptCompileSession.analyze(myErrorStream)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final ClassFileFactory factory = scriptCompileSession.generate();
|
||||||
|
|
||||||
|
return runDefineModules(moduleFile, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IModuleSetBuilder runDefineModules(String moduleFile, ClassFileFactory factory) {
|
||||||
|
GeneratedClassLoader loader = new GeneratedClassLoader(factory);
|
||||||
|
try {
|
||||||
|
Class moduleSetBuilderClass = loader.loadClass("kotlin.modules.ModuleSetBuilder");
|
||||||
|
final IModuleSetBuilder moduleSetBuilder = (IModuleSetBuilder) moduleSetBuilderClass.newInstance();
|
||||||
|
|
||||||
|
Class namespaceClass = loader.loadClass("namespace");
|
||||||
|
final Method[] methods = namespaceClass.getMethods();
|
||||||
|
boolean modulesDefined = false;
|
||||||
|
for (Method method : methods) {
|
||||||
|
if (method.getName().equals("defineModules")) {
|
||||||
|
method.invoke(null, moduleSetBuilder);
|
||||||
|
modulesDefined = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!modulesDefined) {
|
||||||
|
throw new CompileEnvironmentException("Module script " + moduleFile + " must define a defineModules() method");
|
||||||
|
}
|
||||||
|
return moduleSetBuilder;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CompileEnvironmentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassFileFactory compileModule(IModuleBuilder moduleBuilder, String directory) {
|
||||||
|
CompileSession moduleCompileSession = new CompileSession(myEnvironment);
|
||||||
|
for (String sourceFile : moduleBuilder.getSourceFiles()) {
|
||||||
|
moduleCompileSession.addSources(new File(directory, sourceFile).getPath());
|
||||||
|
}
|
||||||
|
for (String classpathRoot : moduleBuilder.getClasspathRoots()) {
|
||||||
|
myEnvironment.addToClasspath(new File(classpathRoot));
|
||||||
|
}
|
||||||
|
if (!moduleCompileSession.analyze(myErrorStream)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return moduleCompileSession.generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getHomeDirectory() {
|
||||||
|
return new File(PathManager.getResourceRoot(CompileEnvironment.class, "/org/jetbrains/jet/compiler/CompileEnvironment.class")).getParentFile().getParentFile().getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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) {
|
||||||
|
throw new CompileEnvironmentException("Failed to generate jar file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeRuntimeToJar(final JarOutputStream stream) throws IOException {
|
||||||
|
final File unpackedRuntimePath = getUnpackedRuntimePath();
|
||||||
|
if (unpackedRuntimePath != null) {
|
||||||
|
FileUtil.processFilesRecursively(unpackedRuntimePath, new Processor<File>() {
|
||||||
|
@Override
|
||||||
|
public boolean process(File file) {
|
||||||
|
if (file.isDirectory()) return true;
|
||||||
|
final String relativePath = FileUtil.getRelativePath(unpackedRuntimePath, 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 {
|
||||||
|
File runtimeJarPath = getRuntimeJarPath();
|
||||||
|
if (runtimeJarPath != null) {
|
||||||
|
JarInputStream jis = new JarInputStream(new FileInputStream(runtimeJarPath));
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
JarEntry e = jis.getNextJarEntry();
|
||||||
|
if (e == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (FileUtil.getExtension(e.getName()).equals("class")) {
|
||||||
|
stream.putNextEntry(e);
|
||||||
|
FileUtil.copy(jis, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
jis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new CompileEnvironmentException("Couldn't find runtime library");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void compileBunchOfSources(String sourceFileOrDir, String jar, String outputDir) {
|
||||||
|
CompileSession session = new CompileSession(myEnvironment);
|
||||||
|
session.addSources(sourceFileOrDir);
|
||||||
|
|
||||||
|
String mainClass = null;
|
||||||
|
for (JetNamespace namespace : session.getSourceFileNamespaces()) {
|
||||||
|
if (JetMainDetector.hasMain(namespace.getDeclarations())) {
|
||||||
|
mainClass = namespace.getFQName() + ".namespace";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!session.analyze(myErrorStream)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFileFactory factory = session.generate();
|
||||||
|
if (jar != null) {
|
||||||
|
writeToJar(factory, jar, mainClass, true);
|
||||||
|
}
|
||||||
|
else if (outputDir != null) {
|
||||||
|
writeToOutputDirectory(factory, outputDir);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new CompileEnvironmentException("Output directory or jar file is not specified - no files will be saved to the disk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompileEnvironmentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.jetbrains.jet.compiler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yole
|
||||||
|
*/
|
||||||
|
public class CompileEnvironmentException extends RuntimeException {
|
||||||
|
public CompileEnvironmentException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompileEnvironmentException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompileEnvironmentException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-4
@@ -1,4 +1,4 @@
|
|||||||
package org.jetbrains.jet.cli;
|
package org.jetbrains.jet.compiler;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
@@ -15,10 +15,13 @@ 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.JetFileType;
|
import org.jetbrains.jet.plugin.JetFileType;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The session which handles analyzing and compiling a single module.
|
||||||
|
*
|
||||||
* @author yole
|
* @author yole
|
||||||
*/
|
*/
|
||||||
public class CompileSession {
|
public class CompileSession {
|
||||||
@@ -73,10 +76,10 @@ public class CompileSession {
|
|||||||
return mySourceFileNamespaces;
|
return mySourceFileNamespaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean analyze() {
|
public boolean analyze(final PrintStream out) {
|
||||||
if (!myErrors.isEmpty()) {
|
if (!myErrors.isEmpty()) {
|
||||||
for (String error : myErrors) {
|
for (String error : myErrors) {
|
||||||
System.out.println(error);
|
out.println(error);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -85,7 +88,7 @@ public class CompileSession {
|
|||||||
allNamespaces.addAll(myLibrarySourceFileNamespaces);
|
allNamespaces.addAll(myLibrarySourceFileNamespaces);
|
||||||
myBindingContext = instance.analyzeNamespaces(myEnvironment.getProject(), allNamespaces, Predicates.<PsiFile>alwaysTrue(), JetControlFlowDataTraceFactory.EMPTY);
|
myBindingContext = instance.analyzeNamespaces(myEnvironment.getProject(), allNamespaces, Predicates.<PsiFile>alwaysTrue(), JetControlFlowDataTraceFactory.EMPTY);
|
||||||
ErrorCollector errorCollector = new ErrorCollector(myBindingContext);
|
ErrorCollector errorCollector = new ErrorCollector(myBindingContext);
|
||||||
errorCollector.report();
|
errorCollector.report(out);
|
||||||
return !errorCollector.hasErrors;
|
return !errorCollector.hasErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
+5
-4
@@ -1,4 +1,4 @@
|
|||||||
package org.jetbrains.jet.cli;
|
package org.jetbrains.jet.compiler;
|
||||||
|
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
@@ -9,6 +9,7 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticWithTextRange;
|
|||||||
import org.jetbrains.jet.lang.diagnostics.Severity;
|
import org.jetbrains.jet.lang.diagnostics.Severity;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,14 +37,14 @@ class ErrorCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void report() {
|
void report(final PrintStream out) {
|
||||||
if(!maps.isEmpty()) {
|
if(!maps.isEmpty()) {
|
||||||
for (PsiFile psiFile : maps.keySet()) {
|
for (PsiFile psiFile : maps.keySet()) {
|
||||||
System.out.println(psiFile.getVirtualFile().getPath());
|
out.println(psiFile.getVirtualFile().getPath());
|
||||||
Collection<DiagnosticWithTextRange> diagnosticWithTextRanges = maps.get(psiFile);
|
Collection<DiagnosticWithTextRange> diagnosticWithTextRanges = maps.get(psiFile);
|
||||||
for (DiagnosticWithTextRange diagnosticWithTextRange : diagnosticWithTextRanges) {
|
for (DiagnosticWithTextRange diagnosticWithTextRange : diagnosticWithTextRanges) {
|
||||||
String position = DiagnosticUtils.formatPosition(diagnosticWithTextRange);
|
String position = DiagnosticUtils.formatPosition(diagnosticWithTextRange);
|
||||||
System.out.println("\t" + diagnosticWithTextRange.getSeverity().toString() + ": " + position + " " + diagnosticWithTextRange.getMessage());
|
out.println("\t" + diagnosticWithTextRange.getSeverity().toString() + ": " + position + " " + diagnosticWithTextRange.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,29 +1,11 @@
|
|||||||
package org.jetbrains.jet.cli;
|
package org.jetbrains.jet.cli;
|
||||||
|
|
||||||
import com.intellij.openapi.Disposable;
|
|
||||||
import com.intellij.openapi.application.PathManager;
|
|
||||||
import com.intellij.openapi.util.io.FileUtil;
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
|
||||||
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 jet.modules.IModuleBuilder;
|
import org.jetbrains.jet.compiler.CompileEnvironment;
|
||||||
import jet.modules.IModuleSetBuilder;
|
import org.jetbrains.jet.compiler.CompileEnvironmentException;
|
||||||
import org.jetbrains.jet.JetCoreEnvironment;
|
|
||||||
import org.jetbrains.jet.codegen.ClassFileFactory;
|
|
||||||
import org.jetbrains.jet.codegen.GeneratedClassLoader;
|
|
||||||
import org.jetbrains.jet.lang.psi.JetNamespace;
|
|
||||||
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.lang.reflect.Method;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.jar.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author yole
|
* @author yole
|
||||||
@@ -54,273 +36,35 @@ public class KotlinCompiler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Disposable root = new Disposable() {
|
CompileEnvironment environment = new CompileEnvironment();
|
||||||
@Override
|
|
||||||
public void dispose() {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
JetCoreEnvironment environment = new JetCoreEnvironment(root);
|
|
||||||
|
|
||||||
File rtJar = initJdk();
|
File rtJar = initJdk();
|
||||||
if (rtJar == null) return;
|
if (rtJar == null) return;
|
||||||
|
|
||||||
environment.addToClasspath(rtJar);
|
try {
|
||||||
final File unpackedRuntimePath = getUnpackedRuntimePath();
|
environment.setJavaRuntime(rtJar);
|
||||||
if (unpackedRuntimePath != null) {
|
if (!environment.initializeKotlinRuntime()) {
|
||||||
environment.addToClasspath(unpackedRuntimePath);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final File runtimeJarPath = getRuntimeJarPath();
|
|
||||||
if (runtimeJarPath != null && runtimeJarPath.exists()) {
|
|
||||||
environment.addToClasspath(runtimeJarPath);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.out.println("No runtime library found");
|
System.out.println("No runtime library found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments.module != null) {
|
if (arguments.module != null) {
|
||||||
compileModuleScript(environment, arguments.module);
|
environment.compileModuleScript(arguments.module);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompileSession session = new CompileSession(environment);
|
|
||||||
session.addSources(arguments.src);
|
|
||||||
|
|
||||||
String mainClass = null;
|
|
||||||
for (JetNamespace namespace : session.getSourceFileNamespaces()) {
|
|
||||||
if (JetMainDetector.hasMain(namespace.getDeclarations())) {
|
|
||||||
mainClass = namespace.getFQName() + ".namespace";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!session.analyze()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassFileFactory factory = session.generate();
|
|
||||||
if (arguments.jar != null) {
|
|
||||||
writeToJar(factory, arguments.jar, mainClass, true);
|
|
||||||
}
|
|
||||||
else if (arguments.outputDir != null) {
|
|
||||||
writeToOutputDirectory(factory, arguments.outputDir);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.out.println("Output directory or jar file is not specified - no files will be saved to the disk");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void compileModuleScript(JetCoreEnvironment environment, String moduleFile) {
|
|
||||||
CompileSession scriptCompileSession = new CompileSession(environment);
|
|
||||||
scriptCompileSession.addSources(moduleFile);
|
|
||||||
|
|
||||||
URL url = KotlinCompiler.class.getClassLoader().getResource("ModuleBuilder.kt");
|
|
||||||
if (url != null) {
|
|
||||||
String path = url.getPath();
|
|
||||||
if (path.startsWith("file:")) {
|
|
||||||
path = path.substring(5);
|
|
||||||
}
|
|
||||||
final VirtualFile vFile = environment.getJarFileSystem().findFileByPath(path);
|
|
||||||
if (vFile == null) {
|
|
||||||
System.out.println("Couldn't load ModuleBuilder.kt from runtime jar: "+ url);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scriptCompileSession.addSources(vFile);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// building from source
|
|
||||||
final String homeDirectory = getHomeDirectory();
|
|
||||||
final File file = new File(homeDirectory, "stdlib/ktSrc/ModuleBuilder.kt");
|
|
||||||
scriptCompileSession.addSources(environment.getLocalFileSystem().findFileByPath(file.getPath()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scriptCompileSession.analyze()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final ClassFileFactory factory = scriptCompileSession.generate();
|
|
||||||
|
|
||||||
final IModuleSetBuilder moduleSetBuilder = runDefineModules(moduleFile, factory);
|
|
||||||
|
|
||||||
for (IModuleBuilder moduleBuilder : moduleSetBuilder.getModules()) {
|
|
||||||
compileModule(environment, moduleBuilder, new File(moduleFile).getParent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IModuleSetBuilder runDefineModules(String moduleFile, ClassFileFactory factory) {
|
|
||||||
GeneratedClassLoader loader = new GeneratedClassLoader(factory);
|
|
||||||
try {
|
|
||||||
Class moduleSetBuilderClass = loader.loadClass("kotlin.modules.ModuleSetBuilder");
|
|
||||||
final IModuleSetBuilder moduleSetBuilder = (IModuleSetBuilder) moduleSetBuilderClass.newInstance();
|
|
||||||
|
|
||||||
Class namespaceClass = loader.loadClass("namespace");
|
|
||||||
final Method[] methods = namespaceClass.getMethods();
|
|
||||||
boolean modulesDefined = false;
|
|
||||||
for (Method method : methods) {
|
|
||||||
if (method.getName().equals("defineModules")) {
|
|
||||||
method.invoke(null, moduleSetBuilder);
|
|
||||||
modulesDefined = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!modulesDefined) {
|
|
||||||
System.out.println("Module script " + moduleFile + " must define a defineModules() method");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return moduleSetBuilder;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void compileModule(JetCoreEnvironment environment, IModuleBuilder moduleBuilder, String directory) {
|
|
||||||
CompileSession moduleCompileSession = new CompileSession(environment);
|
|
||||||
for (String sourceFile : moduleBuilder.getSourceFiles()) {
|
|
||||||
moduleCompileSession.addSources(new File(directory, sourceFile).getPath());
|
|
||||||
}
|
|
||||||
for (String classpathRoot : moduleBuilder.getClasspathRoots()) {
|
|
||||||
environment.addToClasspath(new File(classpathRoot));
|
|
||||||
}
|
|
||||||
if (!moduleCompileSession.analyze()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ClassFileFactory factory = moduleCompileSession.generate();
|
|
||||||
writeToJar(factory, new File(directory, moduleBuilder.getModuleName() + ".jar").getPath(), null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getHomeDirectory() {
|
|
||||||
return new File(PathManager.getResourceRoot(KotlinCompiler.class, "/org/jetbrains/jet/cli/KotlinCompiler.class")).getParentFile().getParentFile().getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 File getUnpackedRuntimePath() {
|
|
||||||
URL url = KotlinCompiler.class.getClassLoader().getResource("jet/JetObject.class");
|
|
||||||
if (url != null && url.getProtocol().equals("file")) {
|
|
||||||
return new File(url.getPath()).getParentFile().getParentFile();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File getRuntimeJarPath() {
|
|
||||||
URL url = KotlinCompiler.class.getClassLoader().getResource("jet/JetObject.class");
|
|
||||||
if (url != null && url.getProtocol().equals("jar")) {
|
|
||||||
String path = url.getPath();
|
|
||||||
return new File(path.substring(path.indexOf(":") + 1, path.indexOf("!/")));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeRuntimeToJar(final JarOutputStream stream) throws IOException {
|
|
||||||
final File unpackedRuntimePath = getUnpackedRuntimePath();
|
|
||||||
if (unpackedRuntimePath != null) {
|
|
||||||
FileUtil.processFilesRecursively(unpackedRuntimePath, new Processor<File>() {
|
|
||||||
@Override
|
|
||||||
public boolean process(File file) {
|
|
||||||
if (file.isDirectory()) return true;
|
|
||||||
final String relativePath = FileUtil.getRelativePath(unpackedRuntimePath, 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 {
|
|
||||||
File runtimeJarPath = getRuntimeJarPath();
|
|
||||||
if (runtimeJarPath != null) {
|
|
||||||
JarInputStream jis = new JarInputStream(new FileInputStream(runtimeJarPath));
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
JarEntry e = jis.getNextJarEntry();
|
|
||||||
if (e == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (FileUtil.getExtension(e.getName()).equals("class")) {
|
|
||||||
stream.putNextEntry(e);
|
|
||||||
FileUtil.copy(jis, stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
jis.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
System.out.println("Couldn't find runtime library");
|
environment.compileBunchOfSources(arguments.src, arguments.jar, arguments.outputDir);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
} catch (CompileEnvironmentException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File initJdk() {
|
private static File initJdk() {
|
||||||
String javaHome = System.getenv("JAVA_HOME");
|
String javaHome = System.getenv("JAVA_HOME");
|
||||||
File rtJar = null;
|
File rtJar;
|
||||||
if (javaHome == null) {
|
if (javaHome == null) {
|
||||||
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
|
rtJar = CompileEnvironment.findActiveRtJar();
|
||||||
if(systemClassLoader instanceof URLClassLoader) {
|
|
||||||
URLClassLoader loader = (URLClassLoader) systemClassLoader;
|
|
||||||
for(URL url: loader.getURLs()) {
|
|
||||||
if("file".equals(url.getProtocol())) {
|
|
||||||
if(url.getFile().endsWith("/lib/rt.jar")) {
|
|
||||||
rtJar = new File(url.getFile());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(url.getFile().endsWith("/Classes/classes.jar")) {
|
|
||||||
rtJar = new File(url.getFile()).getAbsoluteFile();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rtJar == null) {
|
if(rtJar == null) {
|
||||||
System.out.println("JAVA_HOME environment variable needs to be defined");
|
System.out.println("JAVA_HOME environment variable needs to be defined");
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
namespace Smoke
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import kotlin.modules.ModuleSetBuilder
|
||||||
|
|
||||||
|
fun ModuleSetBuilder.defineModules() {
|
||||||
|
module("smoke") {
|
||||||
|
source files "Smoke.kt"
|
||||||
|
jar name System.getProperty("java.io.tmpdir") + "/smoke.jar"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.jetbrains.jet.compiler;
|
||||||
|
|
||||||
|
import jet.modules.IModuleBuilder;
|
||||||
|
import jet.modules.IModuleSetBuilder;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.jetbrains.jet.codegen.ClassFileFactory;
|
||||||
|
import org.jetbrains.jet.parsing.JetParsingTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yole
|
||||||
|
*/
|
||||||
|
public class CompileEnvironmentTest extends TestCase {
|
||||||
|
public void testSmoke() {
|
||||||
|
CompileEnvironment environment = new CompileEnvironment();
|
||||||
|
environment.setJavaRuntime(CompileEnvironment.findActiveRtJar());
|
||||||
|
environment.initializeKotlinRuntime();
|
||||||
|
final String testDataDir = JetParsingTest.getTestDataDir() + "/compiler/smoke/";
|
||||||
|
final IModuleSetBuilder setBuilder = environment.loadModuleScript(testDataDir + "Smoke.kts");
|
||||||
|
assertEquals(1, setBuilder.getModules().size());
|
||||||
|
final IModuleBuilder moduleBuilder = setBuilder.getModules().get(0);
|
||||||
|
final ClassFileFactory factory = environment.compileModule(moduleBuilder, testDataDir);
|
||||||
|
assertNotNull(factory);
|
||||||
|
assertNotNull(factory.asBytes("Smoke/namespace.class"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,9 +29,16 @@ class ClasspathBuilder(val parent: ModuleBuilder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class JarBuilder(val parent: ModuleBuilder) {
|
||||||
|
fun name(jarName: String) {
|
||||||
|
parent.setJarName(jarName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ModuleBuilder(val name: String): IModuleBuilder {
|
class ModuleBuilder(val name: String): IModuleBuilder {
|
||||||
val sourceFiles: ArrayList<String?> = ArrayList<String?>()
|
val sourceFiles: ArrayList<String?> = ArrayList<String?>()
|
||||||
val classpathRoots: ArrayList<String?> = ArrayList<String?>()
|
val classpathRoots: ArrayList<String?> = ArrayList<String?>()
|
||||||
|
var _jarName: String? = null
|
||||||
|
|
||||||
val source: SourcesBuilder
|
val source: SourcesBuilder
|
||||||
get() = SourcesBuilder(this)
|
get() = SourcesBuilder(this)
|
||||||
@@ -39,6 +46,9 @@ class ModuleBuilder(val name: String): IModuleBuilder {
|
|||||||
val classpath: ClasspathBuilder
|
val classpath: ClasspathBuilder
|
||||||
get() = ClasspathBuilder(this)
|
get() = ClasspathBuilder(this)
|
||||||
|
|
||||||
|
val jar: JarBuilder
|
||||||
|
get() = JarBuilder(this)
|
||||||
|
|
||||||
fun addSourceFiles(pattern: String) {
|
fun addSourceFiles(pattern: String) {
|
||||||
sourceFiles.add(pattern)
|
sourceFiles.add(pattern)
|
||||||
}
|
}
|
||||||
@@ -47,9 +57,13 @@ class ModuleBuilder(val name: String): IModuleBuilder {
|
|||||||
classpathRoots.add(name)
|
classpathRoots.add(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setJarName(name: String) {
|
||||||
|
_jarName = name
|
||||||
|
}
|
||||||
|
|
||||||
override fun getSourceFiles(): List<String?>? = sourceFiles
|
override fun getSourceFiles(): List<String?>? = sourceFiles
|
||||||
override fun getClasspathRoots(): List<String?>? = classpathRoots
|
override fun getClasspathRoots(): List<String?>? = classpathRoots
|
||||||
override fun getModuleName(): String? = name
|
override fun getModuleName(): String? = name
|
||||||
}
|
override fun getJarName(): String? = _jarName
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -9,4 +9,5 @@ public interface IModuleBuilder {
|
|||||||
String getModuleName();
|
String getModuleName();
|
||||||
List<String> getSourceFiles();
|
List<String> getSourceFiles();
|
||||||
List<String> getClasspathRoots();
|
List<String> getClasspathRoots();
|
||||||
|
String getJarName();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user