Initial implementation for external build

#KT-2751 Fixed
 #KT-3017 Fixed
 #KT-3021 Fixed
This commit is contained in:
Andrey Breslav
2012-11-09 16:00:22 +04:00
parent f7a6c6f6fd
commit 9592bfd62f
5 changed files with 342 additions and 23 deletions
+4 -1
View File
@@ -27,8 +27,11 @@
<element id="archive" name="kotlin-jps-plugin.jar">
<element id="module-output" name="jps-plugin" />
<element id="directory" name="META-INF">
<element id="dir-copy" path="$Tests$/jps-plugin/src/META-INF" />
<element id="dir-copy" path="$PROJECT_DIR$/jps-plugin/src/META-INF" />
</element>
<element id="module-output" name="ide-compiler-runner" />
<element id="module-output" name="cli-common" />
<element id="module-output" name="util" />
</element>
</element>
</element>
+2
View File
@@ -8,6 +8,8 @@
<orderEntry type="jdk" jdkName="1.6" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="idea-full" level="project" />
<orderEntry type="module" module-name="ide-compiler-runner" />
<orderEntry type="module" module-name="cli-common" />
</component>
</module>
@@ -16,50 +16,149 @@
package org.jetbrains.jet.jps.build;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
import org.jetbrains.jet.cli.common.messages.MessageCollector;
import org.jetbrains.jet.compiler.runner.*;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.builders.ChunkBuildOutputConsumer;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.builders.FileProcessor;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERROR;
public class KotlinBuilder extends ModuleLevelBuilder {
private static final String KOTLIN_COMPILER_NAME = "Kotlin";
private static final String KOTLIN_BUILDER_NAME = "Kotlin Builder";
protected KotlinBuilder() {
super(BuilderCategory.SOURCE_PROCESSOR);
}
@NotNull
@Override
public String getPresentableName() {
return KOTLIN_BUILDER_NAME;
}
@Override
public ExitCode build(
CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder
) throws ProjectBuildException {
try {
dirtyFilesHolder.processDirtyFiles(new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
@Override
public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor root) throws IOException {
if (file.getName().endsWith(".kt") || file.getName().endsWith(".java")) {
CompileContext context,
ModuleChunk chunk,
DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
ChunkBuildOutputConsumer outputConsumer
) throws ProjectBuildException, IOException {
}
return true;
}
});
MessageCollector messageCollector = new MessageCollectorAdapter(context);
if (chunk.getModules().size() > 1) {
messageCollector.report(
ERROR, "Circular dependencies are not supported: " + chunk.getModules(),
CompilerMessageLocation.NO_LOCATION);
return ExitCode.ABORT;
}
catch (IOException e) {
throw new ProjectBuildException(e);
ModuleBuildTarget representativeTarget = chunk.representativeTarget();
// For non-incremental build: take all sources
List<File> sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(representativeTarget);
//List<File> sourceFiles = getDirtySourceFiles(dirtyFilesHolder);
if (sourceFiles.isEmpty()) {
return ExitCode.NOTHING_DONE;
}
//context.processMessage(new CompilerMessage());
//JpsJavaExtensionService.dependencies().recursively().productionOnly().classes().getRoots()
File scriptFile = KotlinBuilderModuleScriptGenerator.generateModuleScript(context, representativeTarget, sourceFiles);
File outputDir = representativeTarget.getOutputDir();
CompilerEnvironment environment = CompilerEnvironment.getEnvironmentFor(outputDir);
if (!environment.success()) {
environment.reportErrorsTo(messageCollector);
return ExitCode.ABORT;
}
assert outputDir != null : "CompilerEnvironment must have checked for outputDir to be not null, but it didn't";
OutputItemsCollectorImpl outputItemCollector = new OutputItemsCollectorImpl(outputDir);
KotlinCompilerRunner.runCompiler(
messageCollector,
environment,
scriptFile,
outputItemCollector,
/*runOutOfProcess = */false);
for (SimpleOutputItem outputItem : outputItemCollector.getOutputs()) {
outputConsumer.registerOutputFile(
representativeTarget,
outputItem.getOutputFile().getPath(),
paths(outputItem.getSourceFiles()));
}
return ExitCode.OK;
}
@Override
public String getName() {
return "KotlinBuilder";
private static Collection<String> paths(Collection<File> files) {
Collection<String> result = ContainerUtil.newArrayList();
for (File file : files) {
result.add(file.getPath());
}
return result;
}
@Override
public String getDescription() {
return "KotlinBuilder";
public static class MessageCollectorAdapter implements MessageCollector {
private final CompileContext context;
public MessageCollectorAdapter(@NotNull CompileContext context) {
this.context = context;
}
@Override
public void report(
@NotNull CompilerMessageSeverity severity,
@NotNull String message,
@NotNull CompilerMessageLocation location
) {
context.processMessage(new CompilerMessage(
KOTLIN_COMPILER_NAME,
kind(severity),
message,
location.getPath(),
-1, -1, -1,
location.getLine(),
location.getColumn()
));
}
@NotNull
private static BuildMessage.Kind kind(@NotNull CompilerMessageSeverity severity) {
switch (severity) {
case INFO:
return BuildMessage.Kind.INFO;
case ERROR:
case EXCEPTION:
return BuildMessage.Kind.ERROR;
case WARNING:
return BuildMessage.Kind.WARNING;
case LOGGING:
return BuildMessage.Kind.PROGRESS;
default:
throw new IllegalArgumentException("Unsupported severity: " + severity);
}
}
}
}
@@ -0,0 +1,133 @@
/*
* Copyright 2010-2012 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.jps.build;
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.KotlinModuleScriptGenerator;
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.JpsAnnotationRootType;
import org.jetbrains.jps.model.java.JpsJavaClasspathKind;
import org.jetbrains.jps.model.java.JpsJavaDependenciesEnumerator;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.library.JpsLibrary;
import org.jetbrains.jps.model.library.JpsLibraryRoot;
import org.jetbrains.jps.model.module.JpsDependencyElement;
import org.jetbrains.jps.model.module.JpsLibraryDependency;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.model.module.JpsSdkDependency;
import org.jetbrains.jps.util.JpsPathUtil;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.compiler.runner.KotlinModuleScriptGenerator.DependencyProvider;
public class KotlinBuilderModuleScriptGenerator {
public static File generateModuleScript(CompileContext context, ModuleBuildTarget target, List<File> sourceFiles)
throws IOException
{
CharSequence moduleScriptText = KotlinModuleScriptGenerator.generateModuleScript(
target.getModuleName(),
getKotlinModuleDependencies(target),
sourceFiles,
target.isTests(),
// this excludes the output directory from the class path, to be removed for true incremental compilation
Collections.singleton(target.getOutputDir())
);
File scriptFile = new File(target.getOutputDir(), "script.kts");
writeScriptToFile(context, moduleScriptText, scriptFile);
return scriptFile;
}
private static DependencyProvider getKotlinModuleDependencies(final ModuleBuildTarget target) {
return new DependencyProvider() {
@Override
public void processClassPath(@NotNull KotlinModuleScriptGenerator.DependencyProcessor processor) {
processor.processClassPathSection("All", findClassPathRoots(target));
processor.processAnnotationRoots(findAnnotationRoots(target));
}
};
}
private static void writeScriptToFile(CompileContext context, CharSequence moduleScriptText, File scriptFile) throws IOException {
FileUtil.writeToFile(scriptFile, moduleScriptText.toString());
context.processMessage(new CompilerMessage(
"Kotlin",
BuildMessage.Kind.INFO,
"Created script file: " + scriptFile
));
}
@NotNull
private static Collection<File> findClassPathRoots(@NotNull ModuleBuildTarget target) {
JpsModule module = target.getModule();
JpsJavaDependenciesEnumerator dependencies = JpsJavaExtensionService.dependencies(module)
.includedIn(JpsJavaClasspathKind.compile(target.isTests()));
return dependencies.classes().getRoots();
}
@NotNull
private static List<File> findAnnotationRoots(@NotNull ModuleBuildTarget target) {
JpsModule module = target.getModule();
List<JpsDependencyElement> dependencies = module.getDependenciesList().getDependencies();
List<File> annotationRootFiles = ContainerUtil.newArrayList();
for (JpsDependencyElement dependencyElement : dependencies) {
JpsLibrary library = getLibrary(dependencyElement);
if (library == null) continue;
List<JpsLibraryRoot> annotationRoots = library.getRoots(JpsAnnotationRootType.INSTANCE);
for (JpsLibraryRoot root : annotationRoots) {
File file = new File(JpsPathUtil.urlToPath(root.getUrl()));
annotationRootFiles.add(file);
}
}
return annotationRootFiles;
}
@Nullable
private static JpsLibrary getLibrary(@NotNull JpsDependencyElement dependencyElement) {
if (dependencyElement instanceof JpsSdkDependency) {
JpsSdkDependency sdkDependency = (JpsSdkDependency) dependencyElement;
return sdkDependency.resolveSdk();
}
if (dependencyElement instanceof JpsLibraryDependency) {
JpsLibraryDependency libraryDependency = (JpsLibraryDependency) dependencyElement;
return libraryDependency.getLibrary();
}
return null;
}
private KotlinBuilderModuleScriptGenerator() {}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2012 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.jps.build;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.builders.FileProcessor;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.incremental.ModuleBuildTarget;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class KotlinSourceFileCollector {
// For incremental compilation
public static List<File> getDirtySourceFiles(DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder)
throws IOException
{
final List<File> sourceFiles = ContainerUtil.newArrayList();
dirtyFilesHolder.processDirtyFiles(new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
@Override
public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor root) throws IOException {
if (isKotlinSourceFile(file)) {
sourceFiles.add(file);
}
return true;
}
});
return sourceFiles;
}
@NotNull
public static List<File> getAllKotlinSourceFiles(@NotNull ModuleBuildTarget target) {
final List<File> result = ContainerUtil.newArrayList();
for (JpsModuleSourceRoot sourceRoot : getRelevantSourceRoots(target)) {
FileUtil.processFilesRecursively(sourceRoot.getFile(), new Processor<File>() {
@Override
public boolean process(File file) {
if (file.isFile() && isKotlinSourceFile(file)) {
result.add(file);
}
return true;
}
});
}
return result;
}
private static Iterable<JpsModuleSourceRoot> getRelevantSourceRoots(ModuleBuildTarget target) {
JavaSourceRootType sourceRootType = target.isTests() ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE;
//noinspection unchecked
return (Iterable) target.getModule().getSourceRoots(sourceRootType);
}
private static boolean isKotlinSourceFile(File file) {
return file.getPath().endsWith(".kt");
}
private KotlinSourceFileCollector() {}
}