blob: ceebef6ac8f0e1a00c162febacd51593400e5b82 [file] [log] [blame]
/*
* Copyright 2000-2009 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 com.intellij.compiler;
import com.intellij.compiler.impl.*;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.*;
import com.intellij.openapi.compiler.Compiler;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.Semaphore;
public class CompilerManagerImpl extends CompilerManager {
private final Project myProject;
private final List<Compiler> myCompilers = new ArrayList<Compiler>();
private final List<CompileTask> myBeforeTasks = new ArrayList<CompileTask>();
private final List<CompileTask> myAfterTasks = new ArrayList<CompileTask>();
private final Set<FileType> myCompilableTypes = new HashSet<FileType>();
private final CompilationStatusListener myEventPublisher;
private final Semaphore myCompilationSemaphore = new Semaphore(1, true);
private final Set<ModuleType> myValidationDisabledModuleTypes = new HashSet<ModuleType>();
private final Set<LocalFileSystem.WatchRequest> myWatchRoots;
public CompilerManagerImpl(final Project project, MessageBus messageBus) {
myProject = project;
myEventPublisher = messageBus.syncPublisher(CompilerTopics.COMPILATION_STATUS);
// predefined compilers
for(Compiler compiler: Extensions.getExtensions(Compiler.EP_NAME, myProject)) {
addCompiler(compiler);
}
for(CompilerFactory factory: Extensions.getExtensions(CompilerFactory.EP_NAME, myProject)) {
Compiler[] compilers = factory.createCompilers(this);
for (Compiler compiler : compilers) {
addCompiler(compiler);
}
}
addCompilableFileType(StdFileTypes.JAVA);
final File projectGeneratedSrcRoot = CompilerPaths.getGeneratedDataDirectory(project);
projectGeneratedSrcRoot.mkdirs();
final LocalFileSystem lfs = LocalFileSystem.getInstance();
myWatchRoots = lfs.addRootsToWatch(Collections.singletonList(FileUtil.toCanonicalPath(projectGeneratedSrcRoot.getPath())), true);
Disposer.register(project, new Disposable() {
public void dispose() {
lfs.removeWatchedRoots(myWatchRoots);
if (ApplicationManager.getApplication().isUnitTestMode()) { // force cleanup for created compiler system directory with generated sources
FileUtil.delete(CompilerPaths.getCompilerSystemDirectory(project));
}
}
});
}
public Semaphore getCompilationSemaphore() {
return myCompilationSemaphore;
}
public boolean isCompilationActive() {
return myCompilationSemaphore.availablePermits() == 0;
}
public final void addCompiler(@NotNull Compiler compiler) {
myCompilers.add(compiler);
// supporting file instrumenting compilers and validators for external build
// Since these compilers are IDE-specific and use PSI, it is ok to run them before and after the build in the IDE
if (compiler instanceof SourceInstrumentingCompiler) {
addBeforeTask(new FileProcessingCompilerAdapterTask((FileProcessingCompiler)compiler));
}
else if (compiler instanceof Validator) {
addAfterTask(new FileProcessingCompilerAdapterTask((FileProcessingCompiler)compiler));
}
}
@Deprecated
public void addTranslatingCompiler(@NotNull TranslatingCompiler compiler, Set<FileType> inputTypes, Set<FileType> outputTypes) {
// empty
}
public final void removeCompiler(@NotNull Compiler compiler) {
for (List<CompileTask> tasks : Arrays.asList(myBeforeTasks, myAfterTasks)) {
for (Iterator<CompileTask> iterator = tasks.iterator(); iterator.hasNext(); ) {
CompileTask task = iterator.next();
if (task instanceof FileProcessingCompilerAdapterTask && ((FileProcessingCompilerAdapterTask)task).getCompiler() == compiler) {
iterator.remove();
}
}
}
}
@NotNull
public <T extends Compiler> T[] getCompilers(@NotNull Class<T> compilerClass) {
return getCompilers(compilerClass, CompilerFilter.ALL);
}
@NotNull
public <T extends Compiler> T[] getCompilers(@NotNull Class<T> compilerClass, CompilerFilter filter) {
final List<T> compilers = new ArrayList<T>(myCompilers.size());
for (final Compiler item : myCompilers) {
if (compilerClass.isAssignableFrom(item.getClass()) && filter.acceptCompiler(item)) {
compilers.add((T)item);
}
}
final T[] array = (T[])Array.newInstance(compilerClass, compilers.size());
return compilers.toArray(array);
}
public void addCompilableFileType(@NotNull FileType type) {
myCompilableTypes.add(type);
}
public void removeCompilableFileType(@NotNull FileType type) {
myCompilableTypes.remove(type);
}
public boolean isCompilableFileType(@NotNull FileType type) {
return myCompilableTypes.contains(type);
}
public final void addBeforeTask(@NotNull CompileTask task) {
myBeforeTasks.add(task);
}
public final void addAfterTask(@NotNull CompileTask task) {
myAfterTasks.add(task);
}
@NotNull
public CompileTask[] getBeforeTasks() {
return getCompileTasks(myBeforeTasks, CompileTaskBean.CompileTaskExecutionPhase.BEFORE);
}
private CompileTask[] getCompileTasks(List<CompileTask> taskList, CompileTaskBean.CompileTaskExecutionPhase phase) {
List<CompileTask> beforeTasks = new ArrayList<CompileTask>(taskList);
for (CompileTaskBean extension : CompileTaskBean.EP_NAME.getExtensions(myProject)) {
if (extension.myExecutionPhase == phase) {
beforeTasks.add(extension.getTaskInstance());
}
}
return beforeTasks.toArray(new CompileTask[beforeTasks.size()]);
}
@NotNull
public CompileTask[] getAfterTasks() {
return getCompileTasks(myAfterTasks, CompileTaskBean.CompileTaskExecutionPhase.AFTER);
}
public void compile(@NotNull VirtualFile[] files, CompileStatusNotification callback) {
compile(createFilesCompileScope(files), callback);
}
public void compile(@NotNull Module module, CompileStatusNotification callback) {
new CompileDriver(myProject).compile(createModuleCompileScope(module, false), new ListenerNotificator(callback));
}
public void compile(@NotNull CompileScope scope, CompileStatusNotification callback) {
new CompileDriver(myProject).compile(scope, new ListenerNotificator(callback));
}
public void make(CompileStatusNotification callback) {
new CompileDriver(myProject).make(createProjectCompileScope(myProject), new ListenerNotificator(callback));
}
public void make(@NotNull Module module, CompileStatusNotification callback) {
new CompileDriver(myProject).make(createModuleCompileScope(module, true), new ListenerNotificator(callback));
}
public void make(@NotNull Project project, @NotNull Module[] modules, CompileStatusNotification callback) {
new CompileDriver(myProject).make(createModuleGroupCompileScope(project, modules, true), new ListenerNotificator(callback));
}
public void make(@NotNull CompileScope scope, CompileStatusNotification callback) {
new CompileDriver(myProject).make(scope, new ListenerNotificator(callback));
}
public void make(@NotNull CompileScope scope, CompilerFilter filter, @Nullable CompileStatusNotification callback) {
final CompileDriver compileDriver = new CompileDriver(myProject);
compileDriver.setCompilerFilter(filter);
compileDriver.make(scope, new ListenerNotificator(callback));
}
public boolean isUpToDate(@NotNull final CompileScope scope) {
return new CompileDriver(myProject).isUpToDate(scope);
}
public void rebuild(CompileStatusNotification callback) {
new CompileDriver(myProject).rebuild(new ListenerNotificator(callback));
}
public void executeTask(@NotNull CompileTask task, @NotNull CompileScope scope, String contentName, Runnable onTaskFinished) {
final CompileDriver compileDriver = new CompileDriver(myProject);
compileDriver.executeCompileTask(task, scope, contentName, onTaskFinished);
}
private final Map<CompilationStatusListener, MessageBusConnection> myListenerAdapters = new HashMap<CompilationStatusListener, MessageBusConnection>();
public void addCompilationStatusListener(@NotNull final CompilationStatusListener listener) {
final MessageBusConnection connection = myProject.getMessageBus().connect();
myListenerAdapters.put(listener, connection);
connection.subscribe(CompilerTopics.COMPILATION_STATUS, listener);
}
@Override
public void addCompilationStatusListener(@NotNull CompilationStatusListener listener, @NotNull Disposable parentDisposable) {
final MessageBusConnection connection = myProject.getMessageBus().connect(parentDisposable);
connection.subscribe(CompilerTopics.COMPILATION_STATUS, listener);
}
public void removeCompilationStatusListener(@NotNull final CompilationStatusListener listener) {
final MessageBusConnection connection = myListenerAdapters.remove(listener);
if (connection != null) {
connection.disconnect();
}
}
public boolean isExcludedFromCompilation(@NotNull VirtualFile file) {
return CompilerConfiguration.getInstance(myProject).isExcludedFromCompilation(file);
}
@NotNull
public CompileScope createFilesCompileScope(@NotNull final VirtualFile[] files) {
CompileScope[] scopes = new CompileScope[files.length];
for(int i = 0; i < files.length; i++){
scopes[i] = new OneProjectItemCompileScope(myProject, files[i]);
}
return new CompositeScope(scopes);
}
@NotNull
public CompileScope createModuleCompileScope(@NotNull final Module module, final boolean includeDependentModules) {
return createModulesCompileScope(new Module[] {module}, includeDependentModules);
}
@NotNull
public CompileScope createModulesCompileScope(@NotNull final Module[] modules, final boolean includeDependentModules) {
return createModulesCompileScope(modules, includeDependentModules, false);
}
@NotNull
public CompileScope createModulesCompileScope(@NotNull Module[] modules, boolean includeDependentModules, boolean includeRuntimeDependencies) {
return new ModuleCompileScope(myProject, modules, includeDependentModules, includeRuntimeDependencies);
}
@NotNull
public CompileScope createModuleGroupCompileScope(@NotNull final Project project, @NotNull final Module[] modules, final boolean includeDependentModules) {
return new ModuleCompileScope(project, modules, includeDependentModules);
}
@NotNull
public CompileScope createProjectCompileScope(@NotNull final Project project) {
return new ProjectCompileScope(project);
}
@Override
public void setValidationEnabled(ModuleType moduleType, boolean enabled) {
if (enabled) {
myValidationDisabledModuleTypes.remove(moduleType);
}
else {
myValidationDisabledModuleTypes.add(moduleType);
}
}
@Override
public boolean isValidationEnabled(Module module) {
if (myValidationDisabledModuleTypes.isEmpty()) {
return true; // optimization
}
return !myValidationDisabledModuleTypes.contains(ModuleType.get(module));
}
private class ListenerNotificator implements CompileStatusNotification {
private final @Nullable CompileStatusNotification myDelegate;
private ListenerNotificator(@Nullable CompileStatusNotification delegate) {
myDelegate = delegate;
}
public void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext) {
if (!myProject.isDisposed()) {
myEventPublisher.compilationFinished(aborted, errors, warnings, compileContext);
}
if (myDelegate != null) {
myDelegate.finished(aborted, errors, warnings, compileContext);
}
}
}
}