Initial implementation for external build
#KT-2751 Fixed
#KT-3017 Fixed
#KT-3021 Fixed
Original commit: 9592bfd62f
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+133
@@ -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() {}
|
||||
}
|
||||
Reference in New Issue
Block a user