/* * 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 myCompilers = new ArrayList(); private final List myBeforeTasks = new ArrayList(); private final List myAfterTasks = new ArrayList(); private final Set myCompilableTypes = new HashSet(); private final CompilationStatusListener myEventPublisher; private final Semaphore myCompilationSemaphore = new Semaphore(1, true); private final Set myValidationDisabledModuleTypes = new HashSet(); private final Set 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 inputTypes, Set outputTypes) { // empty } public final void removeCompiler(@NotNull Compiler compiler) { for (List tasks : Arrays.asList(myBeforeTasks, myAfterTasks)) { for (Iterator iterator = tasks.iterator(); iterator.hasNext(); ) { CompileTask task = iterator.next(); if (task instanceof FileProcessingCompilerAdapterTask && ((FileProcessingCompilerAdapterTask)task).getCompiler() == compiler) { iterator.remove(); } } } } @NotNull public T[] getCompilers(@NotNull Class compilerClass) { return getCompilers(compilerClass, CompilerFilter.ALL); } @NotNull public T[] getCompilers(@NotNull Class compilerClass, CompilerFilter filter) { final List compilers = new ArrayList(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 taskList, CompileTaskBean.CompileTaskExecutionPhase phase) { List beforeTasks = new ArrayList(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 myListenerAdapters = new HashMap(); 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) { myEventPublisher.compilationFinished(aborted, errors, warnings, compileContext); if (myDelegate != null) { myDelegate.finished(aborted, errors, warnings, compileContext); } } } }