Support circular dependencies

We generate a module script with information on all modules in the chunk, then build the whole chunk as "one big module"
This commit is contained in:
Andrey Breslav
2013-10-09 22:13:39 +04:00
parent d5b08d4f1c
commit 60425b15e6
20 changed files with 599 additions and 321 deletions
@@ -39,9 +39,13 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.*;
import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.EXCEPTION;
import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.INFO;
import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.WARNING;
public class KotlinBuilder extends ModuleLevelBuilder {
@@ -81,29 +85,12 @@ public class KotlinBuilder extends ModuleLevelBuilder {
messageCollector.report(INFO, "Kotlin JPS plugin version " + KotlinVersion.VERSION, CompilerMessageLocation.NO_LOCATION);
if (chunk.getModules().size() > 1) {
// We do not support circular dependencies, but if they are present, we should not break the build,
// so we simply yield a warning and report NOTHING_DONE
messageCollector.report(
WARNING, "Circular dependencies are not supported. " +
"The following modules depend on each other: " + StringUtil.join(chunk.getModules(), MODULE_NAME, ", ") + ". " +
"Kotlin is not compiled for these modules",
CompilerMessageLocation.NO_LOCATION);
return ExitCode.NOTHING_DONE;
}
ModuleBuildTarget representativeTarget = chunk.representativeTarget();
// For non-incremental build: take all sources
if (!dirtyFilesHolder.hasDirtyFiles()) {
return ExitCode.NOTHING_DONE;
}
List<File> sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(representativeTarget);
//List<File> sourceFiles = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder);
if (sourceFiles.isEmpty()) {
return ExitCode.NOTHING_DONE;
}
File outputDir = representativeTarget.getOutputDir();
@@ -118,6 +105,24 @@ public class KotlinBuilder extends ModuleLevelBuilder {
OutputItemsCollectorImpl outputItemCollector = new OutputItemsCollectorImpl(outputDir);
if (JpsUtils.isJsKotlinModule(representativeTarget)) {
if (chunk.getModules().size() > 1) {
// We do not support circular dependencies, but if they are present, we do our best should not break the build,
// so we simply yield a warning and report NOTHING_DONE
messageCollector.report(
WARNING, "Circular dependencies are not supported. " +
"The following JS modules depend on each other: " + StringUtil.join(chunk.getModules(), MODULE_NAME, ", ") + ". " +
"Kotlin is not compiled for these modules",
CompilerMessageLocation.NO_LOCATION);
return ExitCode.NOTHING_DONE;
}
List<File> sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(representativeTarget);
//List<File> sourceFiles = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder);
if (sourceFiles.isEmpty()) {
return ExitCode.NOTHING_DONE;
}
File outputFile = new File(outputDir, representativeTarget.getModule().getName() + ".js");
KotlinCompilerRunner.runK2JsCompiler(
@@ -129,7 +134,19 @@ public class KotlinBuilder extends ModuleLevelBuilder {
outputFile);
}
else {
File moduleFile = KotlinBuilderModuleScriptGenerator.generateModuleDescription(context, representativeTarget, sourceFiles);
if (chunk.getModules().size() > 1) {
messageCollector.report(
WARNING, "Circular dependencies are only partially supported. " +
"The following modules depend on each other: " + StringUtil.join(chunk.getModules(), MODULE_NAME, ", ") + ". " +
"Kotlin will compile them, but some strange effect may happen",
CompilerMessageLocation.NO_LOCATION);
}
File moduleFile = KotlinBuilderModuleScriptGenerator.generateModuleDescription(context, chunk);
if (moduleFile == null) {
// No Kotlin sources found
return ExitCode.NOTHING_DONE;
}
KotlinCompilerRunner.runK2JvmCompiler(
messageCollector,
@@ -20,14 +20,17 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionGenerator;
import org.jetbrains.jet.compiler.runner.KotlinModuleXmlGenerator;
import org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionBuilder;
import org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionBuilderFactory;
import org.jetbrains.jet.compiler.runner.KotlinModuleXmlBuilderFactory;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ModuleBuildTarget;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.model.java.*;
import org.jetbrains.jps.model.java.JpsAnnotationRootType;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.library.JpsLibrary;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.model.library.sdk.JpsSdkType;
@@ -42,42 +45,61 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionGenerator.DependencyProvider;
import static org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionBuilder.DependencyProcessor;
import static org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionBuilder.DependencyProvider;
import static org.jetbrains.jet.jps.build.JpsUtils.getAllDependencies;
public class KotlinBuilderModuleScriptGenerator {
public static final KotlinModuleDescriptionGenerator GENERATOR = KotlinModuleXmlGenerator.INSTANCE;
public static final KotlinModuleDescriptionBuilderFactory FACTORY = KotlinModuleXmlBuilderFactory.INSTANCE;
public static File generateModuleDescription(CompileContext context, ModuleBuildTarget target, List<File> sourceFiles)
@Nullable
public static File generateModuleDescription(CompileContext context, ModuleChunk chunk)
throws IOException
{
KotlinModuleDescriptionBuilder builder = FACTORY.create();
boolean noSources = true;
for (ModuleBuildTarget target : chunk.getTargets()) {
File outputDir = getOutputDir(target);
List<File> sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(target);
noSources &= sourceFiles.isEmpty();
builder.addModule(
target.getId(),
outputDir.getAbsolutePath(),
getKotlinModuleDependencies(context, target),
sourceFiles,
target.isTests(),
// this excludes the output directory from the class path, to be removed for true incremental compilation
Collections.singleton(outputDir)
);
}
if (noSources) return null;
File scriptFile = new File(getOutputDir(chunk.representativeTarget()), "script." + FACTORY.getFileExtension());
writeScriptToFile(context, builder.asText(), scriptFile);
return scriptFile;
}
@NotNull
private static File getOutputDir(@NotNull ModuleBuildTarget target) {
File outputDir = target.getOutputDir();
if (outputDir == null) {
throw new IllegalStateException("No output directory found for " + target);
}
CharSequence moduleScriptText = GENERATOR.generateModuleScript(
target.getId(),
outputDir.getAbsolutePath(),
getKotlinModuleDependencies(context, target),
sourceFiles,
target.isTests(),
// this excludes the output directory from the class path, to be removed for true incremental compilation
Collections.singleton(outputDir)
);
File scriptFile = new File(outputDir, "script." + GENERATOR.getFileExtension());
writeScriptToFile(context, moduleScriptText, scriptFile);
return scriptFile;
return outputDir;
}
private static DependencyProvider getKotlinModuleDependencies(final CompileContext context, final ModuleBuildTarget target) {
return new DependencyProvider() {
@Override
public void processClassPath(@NotNull KotlinModuleDescriptionGenerator.DependencyProcessor processor) {
public void processClassPath(@NotNull DependencyProcessor processor) {
processor.processClassPathSection("Classpath", findClassPathRoots(target));
processor.processClassPathSection("Java Source Roots", findSourceRoots(context, target));
processor.processAnnotationRoots(findAnnotationRoots(target));