Corrected and simplified compilation of module chunk.

This commit is contained in:
Evgeny Gerashchenko
2014-06-03 21:51:46 +04:00
parent ad8affe07c
commit 601a691e18
14 changed files with 243 additions and 159 deletions
@@ -1,27 +0,0 @@
/*
* Copyright 2010-2013 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.jet.cli.common.output
import java.io.File
public trait OutputDirector {
public fun getOutputDirectory(sourceFiles: Collection<File>): File
}
public class SingleDirectoryDirector(private val dir: File): OutputDirector {
override public fun getOutputDirectory(sourceFiles : Collection<File>) : File = dir
}
@@ -21,15 +21,13 @@ import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation
import org.jetbrains.jet.cli.common.messages.OutputMessageUtil import org.jetbrains.jet.cli.common.messages.OutputMessageUtil
import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.jet.cli.common.messages.MessageCollector import org.jetbrains.jet.cli.common.messages.MessageCollector
import org.jetbrains.jet.cli.common.output.OutputDirector
import java.io.File import java.io.File
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.jet.cli.common.output.SingleDirectoryDirector
public fun OutputFileCollection.writeAll(outputDirector: OutputDirector, report: (sources: List<File>, output: File) -> Unit) { public fun OutputFileCollection.writeAll(outputDir: File, report: (sources: List<File>, output: File) -> Unit) {
for (file in asList()) { for (file in asList()) {
val sources = file.sourceFiles val sources = file.sourceFiles
val output = File(outputDirector.getOutputDirectory(sources), file.relativePath) val output = File(outputDir, file.relativePath)
report(sources, output) report(sources, output)
FileUtil.writeToFile(output, file.asByteArray()) FileUtil.writeToFile(output, file.asByteArray())
} }
@@ -38,11 +36,11 @@ public fun OutputFileCollection.writeAll(outputDirector: OutputDirector, report:
private val REPORT_NOTHING = { (sources: List<File>, output: File) -> } private val REPORT_NOTHING = { (sources: List<File>, output: File) -> }
public fun OutputFileCollection.writeAllTo(outputDir: File) { public fun OutputFileCollection.writeAllTo(outputDir: File) {
writeAll(SingleDirectoryDirector(outputDir), REPORT_NOTHING) writeAll(outputDir, REPORT_NOTHING)
} }
public fun OutputFileCollection.writeAll(outputDirector: OutputDirector, messageCollector: MessageCollector) { public fun OutputFileCollection.writeAll(outputDir: File, messageCollector: MessageCollector) {
writeAll(outputDirector) { sources, output -> writeAll(outputDir) { sources, output ->
messageCollector.report(CompilerMessageSeverity.OUTPUT, OutputMessageUtil.formatOutputMessage(sources, output), CompilerMessageLocation.NO_LOCATION) messageCollector.report(CompilerMessageSeverity.OUTPUT, OutputMessageUtil.formatOutputMessage(sources, output), CompilerMessageLocation.NO_LOCATION)
} }
} }
@@ -38,8 +38,6 @@ import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport;
import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
import org.jetbrains.jet.cli.common.messages.MessageCollector; import org.jetbrains.jet.cli.common.messages.MessageCollector;
import org.jetbrains.jet.cli.common.output.OutputDirector;
import org.jetbrains.jet.cli.common.output.SingleDirectoryDirector;
import org.jetbrains.jet.cli.common.output.outputUtils.OutputUtilsPackage; import org.jetbrains.jet.cli.common.output.outputUtils.OutputUtilsPackage;
import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.config.CommonConfigurationKeys; import org.jetbrains.jet.config.CommonConfigurationKeys;
@@ -136,8 +134,7 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
OutputFileCollection outputFiles = translate(mainCallParameters, config, sourcesFiles, outputFile, outputPrefixFile, outputPostfixFile); OutputFileCollection outputFiles = translate(mainCallParameters, config, sourcesFiles, outputFile, outputPrefixFile, outputPostfixFile);
OutputDirector outputDirector = new SingleDirectoryDirector(outputFile.getParentFile()); OutputUtilsPackage.writeAll(outputFiles, outputFile.getParentFile(), messageCollector);
OutputUtilsPackage.writeAll(outputFiles, outputDirector, messageCollector);
return OK; return OK;
} }
@@ -19,7 +19,11 @@ package org.jetbrains.jet.cli.jvm.compiler
import kotlin.modules.Module import kotlin.modules.Module
class ChunkAsOneModule(private val chunk: ModuleChunk) : Module { class ChunkAsOneModule(private val chunk: ModuleChunk) : Module {
override fun getModuleName(): String = "chunk" + chunk.getModules().map { it.getModuleName() }.toString() fun getModules(): List<Module> {
return chunk.getModules()
}
override fun getModuleName(): String = "chunk" + getModules().map { it.getModuleName() }.toString()
override fun getOutputDirectory(): String { override fun getOutputDirectory(): String {
throw UnsupportedOperationException("Each module in a chunk has its own output directory") throw UnsupportedOperationException("Each module in a chunk has its own output directory")
@@ -32,7 +32,6 @@ import org.jetbrains.jet.cli.common.messages.MessageCollector;
import org.jetbrains.jet.cli.common.messages.MessageRenderer; import org.jetbrains.jet.cli.common.messages.MessageRenderer;
import org.jetbrains.jet.cli.common.modules.ModuleDescription; import org.jetbrains.jet.cli.common.modules.ModuleDescription;
import org.jetbrains.jet.cli.common.modules.ModuleXmlParser; import org.jetbrains.jet.cli.common.modules.ModuleXmlParser;
import org.jetbrains.jet.cli.common.output.OutputDirector;
import org.jetbrains.jet.cli.common.output.outputUtils.OutputUtilsPackage; import org.jetbrains.jet.cli.common.output.outputUtils.OutputUtilsPackage;
import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys; import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys;
import org.jetbrains.jet.codegen.ClassFileFactory; import org.jetbrains.jet.codegen.ClassFileFactory;
@@ -249,7 +248,7 @@ public class CompileEnvironmentUtil {
static void writeOutputToDirOrJar( static void writeOutputToDirOrJar(
@Nullable File jar, @Nullable File jar,
@Nullable OutputDirector outputDir, @Nullable File outputDir,
boolean includeRuntime, boolean includeRuntime,
@Nullable FqName mainClass, @Nullable FqName mainClass,
@NotNull ClassFileFactory outputFiles, @NotNull ClassFileFactory outputFiles,
@@ -16,6 +16,7 @@
package org.jetbrains.jet.cli.jvm.compiler; package org.jetbrains.jet.cli.jvm.compiler;
import com.google.common.collect.Lists;
import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.core.CoreApplicationEnvironment; import com.intellij.core.CoreApplicationEnvironment;
import com.intellij.core.CoreJavaFileManager; import com.intellij.core.CoreJavaFileManager;
@@ -31,13 +32,15 @@ import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElementFinder; import com.intellij.psi.PsiElementFinder;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager; import com.intellij.psi.PsiManager;
import com.intellij.psi.compiled.ClassFileDecompilers; import com.intellij.psi.compiled.ClassFileDecompilers;
import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy; import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy;
import com.intellij.psi.impl.file.impl.JavaFileManager; import com.intellij.psi.impl.file.impl.JavaFileManager;
import kotlin.Function1;
import kotlin.Unit;
import kotlin.io.IoPackage;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly; import org.jetbrains.annotations.TestOnly;
import org.jetbrains.jet.CompilerModeProvider; import org.jetbrains.jet.CompilerModeProvider;
@@ -203,9 +206,14 @@ public class JetCoreEnvironment {
for (File path : configuration.getList(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY)) { for (File path : configuration.getList(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY)) {
addExternalAnnotationsRoot(path); addExternalAnnotationsRoot(path);
} }
for (String path : configuration.getList(CommonConfigurationKeys.SOURCE_ROOTS_KEY)) { sourceFiles.addAll(
addSources(path); getJetFiles(this, configuration.getList(CommonConfigurationKeys.SOURCE_ROOTS_KEY), new Function1<String, Unit>() {
} @Override
public Unit invoke(String s) {
report(ERROR, s);
return null;
}
}));
JetScriptDefinitionProvider.getInstance(project).addScriptDefinitions( JetScriptDefinitionProvider.getInstance(project).addScriptDefinitions(
configuration.getList(CommonConfigurationKeys.SCRIPT_DEFINITIONS_KEY)); configuration.getList(CommonConfigurationKeys.SCRIPT_DEFINITIONS_KEY));
@@ -240,42 +248,43 @@ public class JetCoreEnvironment {
annotationsManager.addExternalAnnotationsRoot(PathUtil.jarFileOrDirectoryToVirtualFile(path)); annotationsManager.addExternalAnnotationsRoot(PathUtil.jarFileOrDirectoryToVirtualFile(path));
} }
private void addSources(File file) { @NotNull
if (file.isDirectory()) { public static List<JetFile> getJetFiles(@NotNull final JetCoreEnvironment environment, @NotNull List<String> sourceRoots, @NotNull Function1<String, Unit> reportError) {
File[] files = file.listFiles(); final List<JetFile> result = Lists.newArrayList();
if (files != null) {
for (File child : files) { for (String sourceRootPath : sourceRoots) {
addSources(child); if (sourceRootPath == null) {
} continue;
} }
}
else { VirtualFile vFile = environment.getMyApplicationEnvironment().getLocalFileSystem().findFileByPath(sourceRootPath);
VirtualFile fileByPath = getMyApplicationEnvironment().getLocalFileSystem().findFileByPath(file.getAbsolutePath()); if (vFile == null) {
if (fileByPath != null) { reportError.invoke("Source file or directory not found: " + sourceRootPath);
PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(fileByPath); continue;
if (psiFile instanceof JetFile) { }
sourceFiles.add((JetFile) psiFile); if (!vFile.isDirectory() && vFile.getFileType() != JetFileType.INSTANCE) {
} reportError.invoke("Source entry is not a Kotlin file: " + sourceRootPath);
continue;
} }
}
}
private void addSources(String path) { IoPackage.recurse(new File(sourceRootPath), new Function1<File, Unit>() {
if (path == null) { @Override
return; public Unit invoke(File file) {
if (file.isFile()) {
VirtualFile fileByPath = environment.getMyApplicationEnvironment().getLocalFileSystem().findFileByPath(file.getAbsolutePath());
if (fileByPath != null) {
PsiFile psiFile = PsiManager.getInstance(environment.getProject()).findFile(fileByPath);
if (psiFile instanceof JetFile) {
result.add((JetFile) psiFile);
}
}
}
return null;
}
});
} }
VirtualFile vFile = getMyApplicationEnvironment().getLocalFileSystem().findFileByPath(path); return result;
if (vFile == null) {
report(ERROR, "Source file or directory not found: " + path);
return;
}
if (!vFile.isDirectory() && vFile.getFileType() != JetFileType.INSTANCE) {
report(ERROR, "Source entry is not a Kotlin file: " + path);
return;
}
addSources(new File(path));
} }
private void addToClasspath(File path) { private void addToClasspath(File path) {
@@ -17,10 +17,14 @@
package org.jetbrains.jet.cli.jvm.compiler; package org.jetbrains.jet.cli.jvm.compiler;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Disposer;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import kotlin.Function0; import kotlin.Function0;
import kotlin.Function1;
import kotlin.Unit;
import kotlin.modules.AllModules; import kotlin.modules.AllModules;
import kotlin.modules.Module; import kotlin.modules.Module;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -31,8 +35,6 @@ import org.jetbrains.jet.cli.common.CompilerPlugin;
import org.jetbrains.jet.cli.common.CompilerPluginContext; import org.jetbrains.jet.cli.common.CompilerPluginContext;
import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport; import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport;
import org.jetbrains.jet.cli.common.messages.MessageCollector; import org.jetbrains.jet.cli.common.messages.MessageCollector;
import org.jetbrains.jet.cli.common.output.OutputDirector;
import org.jetbrains.jet.cli.common.output.SingleDirectoryDirector;
import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys; import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys;
import org.jetbrains.jet.codegen.*; import org.jetbrains.jet.codegen.*;
import org.jetbrains.jet.codegen.inline.InlineCodegenUtil; import org.jetbrains.jet.codegen.inline.InlineCodegenUtil;
@@ -56,26 +58,73 @@ import org.jetbrains.jet.utils.KotlinPaths;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
public class KotlinToJVMBytecodeCompiler { public class KotlinToJVMBytecodeCompiler {
private static final boolean COMPILE_CHUNK_AS_ONE_MODULE = true;
private KotlinToJVMBytecodeCompiler() { private KotlinToJVMBytecodeCompiler() {
} }
@Nullable @Nullable
public static ClassFileFactory compileModule(CompilerConfiguration configuration, Module module, File directory) { public static Map<Module, ClassFileFactory> compileModule(
List<String> sourceFiles = module.getSourceFiles(); @NotNull CompilerConfiguration configuration,
if (sourceFiles.isEmpty()) { @NotNull List<Module> modules,
throw new CompileEnvironmentException("No source files where defined in module " + module.getModuleName()); @NotNull File directory
) {
CompilerConfiguration compilerConfiguration = configuration.copy();
for (Module module : modules) {
compilerConfiguration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, getAbsolutePaths(directory, module));
for (String classpathRoot : module.getClasspathRoots()) {
compilerConfiguration.add(JVMConfigurationKeys.CLASSPATH_KEY, new File(classpathRoot));
}
for (String annotationsRoot : module.getAnnotationsRoots()) {
compilerConfiguration.add(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, new File(annotationsRoot));
}
} }
CompilerConfiguration compilerConfiguration = configuration.copy(); Disposable parentDisposable = Disposer.newDisposable();
for (String sourceFile : sourceFiles) { JetCoreEnvironment moduleEnvironment = null;
try {
moduleEnvironment = JetCoreEnvironment.createForProduction(parentDisposable, compilerConfiguration);
AnalyzeExhaust exhaust = analyze(moduleEnvironment);
if (exhaust == null) {
return null;
}
exhaust.throwIfError();
Map<Module, ClassFileFactory> result = Maps.newHashMap();
for (Module module : modules) {
List<JetFile> jetFiles = JetCoreEnvironment.getJetFiles(
moduleEnvironment, getAbsolutePaths(directory, module), new Function1<String, Unit>() {
@Override
public Unit invoke(String s) {
return null;
}
});
GenerationState generationState = generate(moduleEnvironment, exhaust, jetFiles);
result.put(module, generationState.getFactory());
}
return result;
}
finally {
if (moduleEnvironment != null) {
Disposer.dispose(parentDisposable);
}
}
}
@NotNull
private static List<String> getAbsolutePaths(@NotNull File directory, @NotNull Module module) {
List<String> result = Lists.newArrayList();
for (String sourceFile : module.getSourceFiles()) {
File source = new File(sourceFile); File source = new File(sourceFile);
if (!source.isAbsolute()) { if (!source.isAbsolute()) {
source = new File(directory, sourceFile); source = new File(directory, sourceFile);
@@ -85,39 +134,15 @@ public class KotlinToJVMBytecodeCompiler {
throw new CompileEnvironmentException("'" + source + "' does not exist in module " + module.getModuleName()); throw new CompileEnvironmentException("'" + source + "' does not exist in module " + module.getModuleName());
} }
compilerConfiguration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, source.getPath()); result.add(source.getAbsolutePath());
}
for (String classpathRoot : module.getClasspathRoots()) {
compilerConfiguration.add(JVMConfigurationKeys.CLASSPATH_KEY, new File(classpathRoot));
}
for (String annotationsRoot : module.getAnnotationsRoots()) {
compilerConfiguration.add(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, new File(annotationsRoot));
}
Disposable parentDisposable = Disposer.newDisposable();
JetCoreEnvironment moduleEnvironment = null;
try {
moduleEnvironment = JetCoreEnvironment.createForProduction(parentDisposable, compilerConfiguration);
GenerationState generationState = analyzeAndGenerate(moduleEnvironment);
if (generationState == null) {
return null;
}
return generationState.getFactory();
} finally {
if (moduleEnvironment != null) {
Disposer.dispose(parentDisposable);
}
} }
return result;
} }
private static void writeOutput( private static void writeOutput(
CompilerConfiguration configuration, CompilerConfiguration configuration,
ClassFileFactory outputFiles, ClassFileFactory outputFiles,
OutputDirector outputDir, File outputDir,
File jarPath, File jarPath,
boolean jarRuntime, boolean jarRuntime,
FqName mainClass FqName mainClass
@@ -127,37 +152,20 @@ public class KotlinToJVMBytecodeCompiler {
} }
public static boolean compileModules( public static boolean compileModules(
CompilerConfiguration configuration, @NotNull CompilerConfiguration configuration,
@NotNull final ModuleChunk chunk, @NotNull ModuleChunk chunk,
@NotNull File directory, @NotNull File directory,
@Nullable File jarPath, @Nullable File jarPath,
boolean jarRuntime boolean jarRuntime
) { ) {
List<Module> modules = chunk.getModules(); List<Module> modules = chunk.getModules();
if (COMPILE_CHUNK_AS_ONE_MODULE && modules.size() > 1) {
modules = Collections.<Module>singletonList(new ChunkAsOneModule(chunk)); Map<Module, ClassFileFactory> outputFiles = compileModule(configuration, modules, directory);
if (outputFiles == null) {
return false;
} }
for (Module module : modules) { for (Module module : modules) {
ClassFileFactory outputFiles = compileModule(configuration, module, directory); writeOutput(configuration, outputFiles.get(module), new File(module.getOutputDirectory()), jarPath, jarRuntime, null);
if (outputFiles == null) {
return false;
}
OutputDirector outputDir = new OutputDirector() {
@NotNull
@Override
public File getOutputDirectory(@NotNull Collection<? extends File> sourceFiles) {
for (File sourceFile : sourceFiles) {
// Note that here we track original modules:
Module module = chunk.findModuleBySourceFile(sourceFile);
if (module != null) {
return new File(module.getOutputDirectory());
}
}
throw new IllegalStateException("No module found for source files: " + sourceFiles);
}
};
writeOutput(configuration, outputFiles, outputDir, jarPath, jarRuntime, null);
} }
return true; return true;
} }
@@ -194,8 +202,7 @@ public class KotlinToJVMBytecodeCompiler {
FqName mainClass = findMainClass(generationState, environment.getSourceFiles()); FqName mainClass = findMainClass(generationState, environment.getSourceFiles());
try { try {
OutputDirector outputDirector = outputDir != null ? new SingleDirectoryDirector(outputDir) : null; writeOutput(environment.getConfiguration(), generationState.getFactory(), outputDir, jar, includeRuntime, mainClass);
writeOutput(environment.getConfiguration(), generationState.getFactory(), outputDirector, jar, includeRuntime, mainClass);
return true; return true;
} }
finally { finally {
@@ -262,7 +269,7 @@ public class KotlinToJVMBytecodeCompiler {
exhaust.throwIfError(); exhaust.throwIfError();
return generate(environment, exhaust); return generate(environment, exhaust, environment.getSourceFiles());
} }
@Nullable @Nullable
@@ -301,11 +308,15 @@ public class KotlinToJVMBytecodeCompiler {
} }
@NotNull @NotNull
private static GenerationState generate(@NotNull JetCoreEnvironment environment, @NotNull AnalyzeExhaust exhaust) { private static GenerationState generate(
@NotNull JetCoreEnvironment environment,
@NotNull AnalyzeExhaust exhaust,
@NotNull List<JetFile> sourceFiles
) {
CompilerConfiguration configuration = environment.getConfiguration(); CompilerConfiguration configuration = environment.getConfiguration();
GenerationState generationState = new GenerationState( GenerationState generationState = new GenerationState(
environment.getProject(), ClassBuilderFactories.BINARIES, Progress.DEAF, environment.getProject(), ClassBuilderFactories.BINARIES, Progress.DEAF,
exhaust.getModuleDescriptor(), exhaust.getBindingContext(), environment.getSourceFiles(), exhaust.getModuleDescriptor(), exhaust.getBindingContext(), sourceFiles,
configuration.get(JVMConfigurationKeys.GENERATE_NOT_NULL_ASSERTIONS, false), configuration.get(JVMConfigurationKeys.GENERATE_NOT_NULL_ASSERTIONS, false),
configuration.get(JVMConfigurationKeys.GENERATE_NOT_NULL_PARAMETER_ASSERTIONS, false), configuration.get(JVMConfigurationKeys.GENERATE_NOT_NULL_PARAMETER_ASSERTIONS, false),
GenerationState.GenerateClassFilter.GENERATE_ALL, GenerationState.GenerateClassFilter.GENERATE_ALL,
@@ -16,39 +16,24 @@
package org.jetbrains.jet.cli.jvm.compiler; package org.jetbrains.jet.cli.jvm.compiler;
import com.google.common.collect.Maps;
import kotlin.modules.Module; import kotlin.modules.Module;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
public class ModuleChunk { public class ModuleChunk {
public static final ModuleChunk EMPTY = new ModuleChunk(Collections.<Module>emptyList()); public static final ModuleChunk EMPTY = new ModuleChunk(Collections.<Module>emptyList());
private final List<Module> modules; private final List<Module> modules;
private final Map<File, Module> sourceFileToModule = Maps.newHashMap();
public ModuleChunk(@NotNull List<Module> modules) { public ModuleChunk(@NotNull List<Module> modules) {
this.modules = modules; this.modules = modules;
for (Module module : modules) {
for (String file : module.getSourceFiles()) {
sourceFileToModule.put(new File(file).getAbsoluteFile(), module);
}
}
} }
@NotNull @NotNull
public List<Module> getModules() { public List<Module> getModules() {
return modules; return modules;
} }
@Nullable
public Module findModuleBySourceFile(@NotNull File sourceFile) {
return sourceFileToModule.get(sourceFile.getAbsoluteFile());
}
} }
@@ -29,9 +29,15 @@ import org.jetbrains.jps.model.java.JpsJavaDependencyScope;
import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.module.JpsModule; import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.util.JpsPathUtil; import org.jetbrains.jps.util.JpsPathUtil;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
public class KotlinJpsBuildTest extends AbstractKotlinJpsBuildTestCase { public class KotlinJpsBuildTest extends AbstractKotlinJpsBuildTestCase {
private static final String PROJECT_NAME = "kotlinProject"; private static final String PROJECT_NAME = "kotlinProject";
@@ -204,6 +210,34 @@ public class KotlinJpsBuildTest extends AbstractKotlinJpsBuildTestCase {
checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module2", "module2/src/kt1.kt", "kt1.Kt1Package"); checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module2", "module2/src/kt1.kt", "kt1.Kt1Package");
} }
public void testCircularDependenciesSamePackage() throws IOException {
initProject();
BuildResult result = makeAll();
result.assertSuccessful();
// Check that outputs are located properly
File facadeWithA = findFileInOutputDir(findModule("module1"), "test/TestPackage.class");
File facadeWithB = findFileInOutputDir(findModule("module2"), "test/TestPackage.class");
assertSameElements(getMethodsOfClass(facadeWithA), "a", "getA");
assertSameElements(getMethodsOfClass(facadeWithB), "b", "getB", "setB");
checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module1", "module1/src/a.kt", "test.TestPackage");
checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module2", "module2/src/b.kt", "test.TestPackage");
}
@NotNull
private static Set<String> getMethodsOfClass(@NotNull File classFile) throws IOException {
final Set<String> result = new TreeSet<String>();
new ClassReader(FileUtil.loadFileBytes(classFile)).accept(new ClassVisitor(Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
result.add(name);
return null;
}
}, 0);
return result;
}
public void testReexportedDependency() { public void testReexportedDependency() {
initProject(); initProject();
addKotlinRuntimeDependency(JpsJavaDependencyScope.COMPILE, addKotlinRuntimeDependency(JpsJavaDependencyScope.COMPILE,
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<resourceExtensions />
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
<component name="CopyrightManager" default="">
<module2copyright />
</component>
<component name="DependencyValidationManager">
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</component>
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/module1/module1.iml" filepath="$PROJECT_DIR$/module1/module1.iml" />
<module fileurl="file://$PROJECT_DIR$/module2/module2.iml" filepath="$PROJECT_DIR$/module2/module2.iml" />
</modules>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA_JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="xUTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="IDEA_JDK" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="module2" />
<orderEntry type="library" name="KotlinRuntime" level="project" />
</component>
</module>
@@ -0,0 +1,7 @@
package test
fun a() {
}
val a = ""
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="IDEA_JDK" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="module1" />
<orderEntry type="library" name="KotlinRuntime" level="project" />
</component>
</module>
@@ -0,0 +1,7 @@
package test
fun b() {
}
var b = ""