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
@@ -0,0 +1,27 @@
package org.jetbrains.jet.compiler.runner;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public interface KotlinModuleDescriptionBuilder {
KotlinModuleDescriptionBuilder addModule(
String moduleName,
String outputDir,
DependencyProvider dependencyProvider,
List<File> sourceFiles,
boolean tests,
Set<File> directoriesToFilterOut);
CharSequence asText();
interface DependencyProvider {
void processClassPath(DependencyProcessor processor);
}
interface DependencyProcessor {
void processClassPathSection(String sectionDescription, Collection<File> files);
void processAnnotationRoots(List<File> files);
}
}
@@ -0,0 +1,22 @@
/*
* 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.compiler.runner;
public interface KotlinModuleDescriptionBuilderFactory {
KotlinModuleDescriptionBuilder create();
String getFileExtension();
}
@@ -1,46 +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.compiler.runner;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public interface KotlinModuleDescriptionGenerator {
CharSequence generateModuleScript(
String moduleName,
String outputDir,
DependencyProvider dependencyProvider,
List<File> sourceFiles,
boolean tests,
Set<File> directoriesToFilterOut
);
String getFileExtension();
interface DependencyProvider {
void processClassPath(@NotNull DependencyProcessor processor);
}
interface DependencyProcessor {
void processClassPathSection(@NotNull String sectionDescription, @NotNull Collection<File> files);
void processAnnotationRoots(@NotNull List<File> files);
}
}
@@ -0,0 +1,117 @@
/*
* 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.compiler.runner;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
public class KotlinModuleScriptBuilderFactory implements KotlinModuleDescriptionBuilderFactory {
public static final KotlinModuleScriptBuilderFactory INSTANCE = new KotlinModuleScriptBuilderFactory();
private KotlinModuleScriptBuilderFactory() {}
@Override
public KotlinModuleDescriptionBuilder create() {
return new Builder();
}
@Override
public String getFileExtension() {
return "kts";
}
private static class Builder implements KotlinModuleDescriptionBuilder {
private final StringBuilder script = new StringBuilder();
private boolean done = false;
{
script.append("import kotlin.modules.*\n");
script.append("fun project() {\n");
}
@Override
public KotlinModuleDescriptionBuilder addModule(
String moduleName,
String outputDir,
DependencyProvider dependencyProvider,
List<File> sourceFiles,
boolean tests,
final Set<File> directoriesToFilterOut
) {
assert !done : "Already done";
if (tests) {
script.append("// Module script for tests\n");
}
else {
script.append("// Module script for production\n");
}
script.append(" module(\"" + moduleName + "\", outputDir = \"" + toSystemIndependentName(outputDir) + "\") {\n");
for (File sourceFile : sourceFiles) {
script.append(" sources += \"" + toSystemIndependentName(sourceFile.getPath()) + "\"\n");
}
dependencyProvider.processClassPath(new DependencyProcessor() {
@Override
public void processClassPathSection(@NotNull String sectionDescription, @NotNull Collection<File> files) {
script.append(" // " + sectionDescription + "\n");
for (File file : files) {
if (directoriesToFilterOut.contains(file)) {
// For IDEA's make (incremental compilation) purposes, output directories of the current module and its dependencies
// appear on the class path, so we are at risk of seeing the results of the previous build, i.e. if some class was
// removed in the sources, it may still be there in binaries. Thus, we delete these entries from the classpath.
script.append(" // Output directory, commented out\n");
script.append(" // ");
}
script.append(" classpath += \"" + toSystemIndependentName(file.getPath()) + "\"\n");
}
}
@Override
public void processAnnotationRoots(@NotNull List<File> files) {
script.append(" // External annotations\n");
for (File file : files) {
script.append(" annotationsPath += \"").append(toSystemIndependentName(file.getPath())).append("\"\n");
}
}
});
script.append(" }\n");
return this;
}
@Override
public CharSequence asText() {
if (!done) {
script.append("}\n");
done = true;
}
return script;
}
}
}
@@ -1,93 +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.compiler.runner;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
public class KotlinModuleScriptGenerator implements KotlinModuleDescriptionGenerator {
public static final KotlinModuleScriptGenerator INSTANCE = new KotlinModuleScriptGenerator();
private KotlinModuleScriptGenerator() {}
@Override
public CharSequence generateModuleScript(
String moduleName,
String outputDir, DependencyProvider dependencyProvider,
List<File> sourceFiles,
boolean tests,
final Set<File> directoriesToFilterOut
) {
final StringBuilder script = new StringBuilder();
if (tests) {
script.append("// Module script for tests\n");
}
else {
script.append("// Module script for production\n");
}
script.append("import kotlin.modules.*\n");
script.append("fun project() {\n");
script.append(" module(\"" + moduleName + "\", outputDir = \"" + toSystemIndependentName(outputDir) + "\") {\n");
for (File sourceFile : sourceFiles) {
script.append(" sources += \"" + toSystemIndependentName(sourceFile.getPath()) + "\"\n");
}
dependencyProvider.processClassPath(new DependencyProcessor() {
@Override
public void processClassPathSection(@NotNull String sectionDescription, @NotNull Collection<File> files) {
script.append(" // " + sectionDescription + "\n");
for (File file : files) {
if (directoriesToFilterOut.contains(file)) {
// For IDEA's make (incremental compilation) purposes, output directories of the current module and its dependencies
// appear on the class path, so we are at risk of seeing the results of the previous build, i.e. if some class was
// removed in the sources, it may still be there in binaries. Thus, we delete these entries from the classpath.
script.append(" // Output directory, commented out\n");
script.append(" // ");
}
script.append(" classpath += \"" + toSystemIndependentName(file.getPath()) + "\"\n");
}
}
@Override
public void processAnnotationRoots(@NotNull List<File> files) {
script.append(" // External annotations\n");
for (File file : files) {
script.append(" annotationsPath += \"").append(toSystemIndependentName(file.getPath())).append("\"\n");
}
}
});
script.append(" }\n");
script.append("}\n");
return script;
}
@Override
public String getFileExtension() {
return "kts";
}
}
@@ -0,0 +1,143 @@
/*
* 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.compiler.runner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.utils.Printer;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
import static com.intellij.openapi.util.text.StringUtil.escapeXml;
import static org.jetbrains.jet.cli.common.modules.ModuleXmlParser.*;
public class KotlinModuleXmlBuilderFactory implements KotlinModuleDescriptionBuilderFactory {
public static final KotlinModuleXmlBuilderFactory INSTANCE = new KotlinModuleXmlBuilderFactory();
private KotlinModuleXmlBuilderFactory() {}
@Override
public KotlinModuleDescriptionBuilder create() {
return new Builder();
}
@Override
public String getFileExtension() {
return "xml";
}
private static class Builder implements KotlinModuleDescriptionBuilder {
private final StringBuilder xml = new StringBuilder();
private final Printer p = new Printer(xml);
private boolean done = false;
{
openTag(p, MODULES);
}
@Override
public KotlinModuleDescriptionBuilder addModule(
String moduleName,
String outputDir,
DependencyProvider dependencyProvider,
List<File> sourceFiles,
boolean tests,
final Set<File> directoriesToFilterOut
) {
assert !done : "Already done";
if (tests) {
p.println("<!-- Module script for tests -->");
}
else {
p.println("<!-- Module script for production -->");
}
p.println("<", MODULE, " ",
NAME, "=\"", escapeXml(moduleName), "\" ",
OUTPUT_DIR, "=\"", getEscapedPath(new File(outputDir)), "\">");
p.pushIndent();
for (File sourceFile : sourceFiles) {
p.println("<", SOURCES, " ", PATH, "=\"", getEscapedPath(sourceFile), "\"/>");
}
dependencyProvider.processClassPath(new DependencyProcessor() {
@Override
public void processClassPathSection(@NotNull String sectionDescription, @NotNull Collection<File> files) {
p.println("<!-- ", sectionDescription, " -->");
for (File file : files) {
boolean isOutput = directoriesToFilterOut.contains(file);
if (isOutput) {
// For IDEA's make (incremental compilation) purposes, output directories of the current module and its dependencies
// appear on the class path, so we are at risk of seeing the results of the previous build, i.e. if some class was
// removed in the sources, it may still be there in binaries. Thus, we delete these entries from the classpath.
p.println("<!-- Output directory, commented out -->");
p.println("<!-- ");
p.pushIndent();
}
p.println("<", CLASSPATH, " ", PATH, "=\"", getEscapedPath(file), "\"/>");
if (isOutput) {
p.popIndent();
p.println("-->");
}
}
}
@Override
public void processAnnotationRoots(@NotNull List<File> files) {
p.println("<!-- External annotations -->");
for (File file : files) {
p.println("<", EXTERNAL_ANNOTATIONS, " ", PATH, "= \"", getEscapedPath(file), "\"/>");
}
}
});
closeTag(p, MODULE);
return this;
}
@Override
public CharSequence asText() {
if (!done) {
closeTag(p, MODULES);
done = true;
}
return xml;
}
}
private static void openTag(Printer p, String tag) {
p.println("<" + tag + ">");
p.pushIndent();
}
private static void closeTag(Printer p, String tag) {
p.popIndent();
p.println("</" + tag + ">");
}
private static String getEscapedPath(File sourceFile) {
return escapeXml(toSystemIndependentName(sourceFile.getPath()));
}
}
@@ -1,123 +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.compiler.runner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.utils.Printer;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
import static com.intellij.openapi.util.text.StringUtil.escapeXml;
import static org.jetbrains.jet.cli.common.modules.ModuleXmlParser.*;
public class KotlinModuleXmlGenerator implements KotlinModuleDescriptionGenerator {
public static final KotlinModuleXmlGenerator INSTANCE = new KotlinModuleXmlGenerator();
private KotlinModuleXmlGenerator() {}
@Override
public CharSequence generateModuleScript(
String moduleName,
String outputDir, DependencyProvider dependencyProvider,
List<File> sourceFiles,
boolean tests,
final Set<File> directoriesToFilterOut
) {
StringBuilder xml = new StringBuilder();
final Printer p = new Printer(xml);
openTag(p, MODULES);
if (tests) {
p.println("<!-- Module script for tests -->");
}
else {
p.println("<!-- Module script for production -->");
}
p.println("<", MODULE, " ",
NAME, "=\"", escapeXml(moduleName), "\" ",
OUTPUT_DIR, "=\"", getEscapedPath(new File(outputDir)), "\">");
p.pushIndent();
for (File sourceFile : sourceFiles) {
p.println("<", SOURCES, " ", PATH, "=\"", getEscapedPath(sourceFile), "\"/>");
}
dependencyProvider.processClassPath(new DependencyProcessor() {
@Override
public void processClassPathSection(@NotNull String sectionDescription, @NotNull Collection<File> files) {
p.println("<!-- ", sectionDescription, " -->");
for (File file : files) {
boolean isOutput = directoriesToFilterOut.contains(file);
if (isOutput) {
// For IDEA's make (incremental compilation) purposes, output directories of the current module and its dependencies
// appear on the class path, so we are at risk of seeing the results of the previous build, i.e. if some class was
// removed in the sources, it may still be there in binaries. Thus, we delete these entries from the classpath.
p.println("<!-- Output directory, commented out -->");
p.println("<!-- ");
p.pushIndent();
}
p.println("<", CLASSPATH, " ", PATH, "=\"", getEscapedPath(file), "\"/>");
if (isOutput) {
p.popIndent();
p.println("-->");
}
}
}
@Override
public void processAnnotationRoots(@NotNull List<File> files) {
p.println("<!-- External annotations -->");
for (File file : files) {
p.println("<", EXTERNAL_ANNOTATIONS, " ", PATH, "= \"", getEscapedPath(file), "\"/>");
}
}
});
closeTag(p, MODULE);
closeTag(p, MODULES);
return xml;
}
@Override
public String getFileExtension() {
return "xml";
}
private static void openTag(Printer p, String tag) {
p.println("<" + tag + ">");
p.pushIndent();
}
private static void closeTag(Printer p, String tag) {
p.popIndent();
p.println("</" + tag + ">");
}
private static String getEscapedPath(File sourceFile) {
return escapeXml(toSystemIndependentName(sourceFile.getPath()));
}
}