org.jetbrains.android.compiler.AndroidCompileUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.jetbrains.android.compiler.AndroidCompileUtil.java

Source

/*
 * Copyright 2000-2010 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.android.compiler;

import com.intellij.CommonBundle;
import com.intellij.compiler.CompilerConfiguration;
import com.intellij.compiler.CompilerConfigurationImpl;
import com.intellij.compiler.CompilerWorkspaceConfiguration;
import com.intellij.compiler.impl.CompileContextImpl;
import com.intellij.compiler.impl.ModuleCompileScope;
import com.intellij.compiler.options.CompileStepBeforeRun;
import com.intellij.compiler.progress.CompilerTask;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.*;
import com.intellij.openapi.compiler.Compiler;
import com.intellij.openapi.compiler.options.ExcludeEntryDescription;
import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.impl.ModifiableModelCommitter;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.artifacts.ArtifactProperties;
import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import org.jetbrains.android.compiler.artifact.*;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidFacetConfiguration;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.fileTypes.AndroidIdlFileType;
import com.android.tools.idea.fileTypes.AndroidRenderscriptFileType;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidCompilerMessageKind;
import org.jetbrains.android.util.AndroidExecutionUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;

/**
 * @author yole
 */
public class AndroidCompileUtil {
    private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidCompileUtil");

    private static final Key<Boolean> RELEASE_BUILD_KEY = new Key<Boolean>(AndroidCommonUtils.RELEASE_BUILD_OPTION);
    @NonNls
    private static final String RESOURCES_CACHE_DIR_NAME = "res-cache";
    @NonNls
    private static final String GEN_MODULE_PREFIX = "~generated_";

    @NonNls
    public static final String OLD_PROGUARD_CFG_FILE_NAME = "proguard.cfg";
    public static final String UNSIGNED_SUFFIX = ".unsigned";

    private AndroidCompileUtil() {
    }

    @NotNull
    public static <T> Map<CompilerMessageCategory, T> toCompilerMessageCategoryKeys(
            @NotNull Map<AndroidCompilerMessageKind, T> map) {
        final Map<CompilerMessageCategory, T> result = new HashMap<CompilerMessageCategory, T>();

        for (Map.Entry<AndroidCompilerMessageKind, T> entry : map.entrySet()) {
            final AndroidCompilerMessageKind key = entry.getKey();
            final T value = entry.getValue();

            switch (key) {
            case ERROR:
                result.put(CompilerMessageCategory.ERROR, value);
                break;
            case INFORMATION:
                result.put(CompilerMessageCategory.INFORMATION, value);
                break;
            case WARNING:
                result.put(CompilerMessageCategory.WARNING, value);
                break;
            }
        }
        return result;
    }

    @Nullable
    public static Pair<VirtualFile, Boolean> getDefaultProguardConfigFile(@NotNull AndroidFacet facet) {
        VirtualFile root = AndroidRootUtil.getMainContentRoot(facet);
        if (root == null) {
            return null;
        }
        final VirtualFile proguardCfg = root.findChild(AndroidCommonUtils.PROGUARD_CFG_FILE_NAME);
        if (proguardCfg != null) {
            return new Pair<VirtualFile, Boolean>(proguardCfg, true);
        }

        final VirtualFile oldProguardCfg = root.findChild(OLD_PROGUARD_CFG_FILE_NAME);
        if (oldProguardCfg != null) {
            return new Pair<VirtualFile, Boolean>(oldProguardCfg, false);
        }
        return null;
    }

    public static void addMessages(final CompileContext context,
            final Map<CompilerMessageCategory, List<String>> messages, @Nullable Module module) {
        addMessages(context, messages, null, module);
    }

    static void addMessages(final CompileContext context, final Map<CompilerMessageCategory, List<String>> messages,
            @Nullable final Map<VirtualFile, VirtualFile> presentableFilesMap, @Nullable final Module module) {
        ApplicationManager.getApplication().runReadAction(new Runnable() {
            @Override
            public void run() {
                if (context.getProject().isDisposed())
                    return;
                for (CompilerMessageCategory category : messages.keySet()) {
                    List<String> messageList = messages.get(category);
                    for (String message : messageList) {
                        String url = null;
                        int line = -1;
                        Matcher matcher = AndroidCommonUtils.COMPILER_MESSAGE_PATTERN.matcher(message);
                        if (matcher.matches()) {
                            String fileName = matcher.group(1);
                            if (new File(fileName).exists()) {
                                url = getPresentableFile("file://" + fileName, presentableFilesMap);
                                line = Integer.parseInt(matcher.group(2));
                            }
                        }
                        context.addMessage(category,
                                (module != null ? '[' + module.getName() + "] " : "") + message, url, line, -1);
                    }
                }
            }
        });
    }

    @NotNull
    private static String getPresentableFile(@NotNull String url,
            @Nullable Map<VirtualFile, VirtualFile> presentableFilesMap) {
        final VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
        if (file == null) {
            return url;
        }

        if (presentableFilesMap == null) {
            return url;
        }

        for (Map.Entry<VirtualFile, VirtualFile> entry : presentableFilesMap.entrySet()) {
            if (Comparing.equal(file, entry.getValue())) {
                return entry.getKey().getUrl();
            }
        }
        return url;
    }

    private static void collectChildrenRecursively(@NotNull VirtualFile root, @NotNull VirtualFile anchor,
            @NotNull Collection<VirtualFile> result) {
        if (Comparing.equal(root, anchor)) {
            return;
        }

        VirtualFile parent = anchor.getParent();
        if (parent == null) {
            return;
        }
        for (VirtualFile child : parent.getChildren()) {
            if (!Comparing.equal(child, anchor)) {
                result.add(child);
            }
        }
        if (!Comparing.equal(parent, root)) {
            collectChildrenRecursively(root, parent, result);
        }
    }

    private static void unexcludeRootIfNecessary(@NotNull VirtualFile root, @NotNull ModifiableRootModel model,
            @NotNull Ref<Boolean> modelChangedFlag) {
        Set<VirtualFile> excludedRoots = new HashSet<VirtualFile>(Arrays.asList(model.getExcludeRoots()));
        VirtualFile excludedRoot = root;
        while (excludedRoot != null && !excludedRoots.contains(excludedRoot)) {
            excludedRoot = excludedRoot.getParent();
        }
        if (excludedRoot == null) {
            return;
        }
        Set<VirtualFile> rootsToExclude = new HashSet<VirtualFile>();
        collectChildrenRecursively(excludedRoot, root, rootsToExclude);
        ContentEntry contentEntry = findContentEntryForRoot(model, excludedRoot);
        if (contentEntry != null) {
            ExcludeFolder excludedFolder = null;
            for (ExcludeFolder folder : contentEntry.getExcludeFolders()) {
                if (Comparing.equal(folder.getFile(), excludedRoot)) {
                    excludedFolder = folder;
                    break;
                }
            }
            if (excludedFolder != null) {
                contentEntry.removeExcludeFolder(excludedFolder);
                modelChangedFlag.set(true);
            }
            for (VirtualFile rootToExclude : rootsToExclude) {
                if (!excludedRoots.contains(rootToExclude)) {
                    contentEntry.addExcludeFolder(rootToExclude);
                    modelChangedFlag.set(true);
                }
            }
        }
    }

    @NotNull
    private static String getGenModuleName(@NotNull Module module) {
        return GEN_MODULE_PREFIX + module.getName();
    }

    @Nullable
    public static VirtualFile createSourceRootIfNotExist(@NotNull final String path,
            @NotNull final ModifiableRootModel model, @NotNull Ref<Boolean> modelChangedFlag) {
        ApplicationManager.getApplication().assertIsDispatchThread();

        final File rootFile = new File(path);
        final boolean created;
        if (!rootFile.exists()) {
            if (!rootFile.mkdirs())
                return null;
            created = true;
        } else {
            created = false;
        }
        final Module module = model.getModule();
        final Project project = module.getProject();

        if (project.isDisposed() || module.isDisposed()) {
            return null;
        }

        final VirtualFile root;
        if (created) {
            root = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(rootFile);
        } else {
            root = LocalFileSystem.getInstance().findFileByIoFile(rootFile);
        }
        if (root != null) {
            unexcludeRootIfNecessary(root, model, modelChangedFlag);

            boolean markedAsSource = false;

            for (VirtualFile existingRoot : model.getSourceRoots()) {
                if (Comparing.equal(existingRoot, root)) {
                    markedAsSource = true;
                }
            }

            if (!markedAsSource) {
                addSourceRoot(model, root);
                modelChangedFlag.set(true);
            }
        }
        return root;
    }

    private static void excludeFromCompilation(@NotNull Project project, @NotNull VirtualFile sourceRoot,
            @NotNull String aPackage) {
        final String buildConfigPath = sourceRoot.getPath() + '/' + aPackage.replace('.', '/')
                + "/BuildConfig.java";
        String url = VfsUtilCore.pathToUrl(buildConfigPath);
        final ExcludedEntriesConfiguration configuration = CompilerConfiguration.getInstance(project)
                .getExcludedEntriesConfiguration();

        for (ExcludeEntryDescription description : configuration.getExcludeEntryDescriptions()) {
            if (Comparing.equal(description.getUrl(), url)) {
                return;
            }
        }
        configuration.addExcludeEntryDescription(new ExcludeEntryDescription(url, true, false, project));
    }

    private static void excludeFromCompilation(@NotNull Project project, @NotNull VirtualFile dir) {
        final ExcludedEntriesConfiguration configuration = CompilerConfiguration.getInstance(project)
                .getExcludedEntriesConfiguration();

        for (ExcludeEntryDescription description : configuration.getExcludeEntryDescriptions()) {
            if (Comparing.equal(description.getVirtualFile(), dir)) {
                return;
            }
        }
        configuration.addExcludeEntryDescription(new ExcludeEntryDescription(dir, true, false, project));
    }

    private static void removeGenModule(@NotNull final ModifiableRootModel model,
            @NotNull Ref<Boolean> modelChangedFlag) {
        final String genModuleName = getGenModuleName(model.getModule());
        final Project project = model.getProject();
        final ModuleManager moduleManager = ModuleManager.getInstance(project);

        final Module genModule = moduleManager.findModuleByName(genModuleName);
        if (genModule == null) {
            return;
        }

        for (OrderEntry entry : model.getOrderEntries()) {
            if (entry instanceof ModuleOrderEntry
                    && genModuleName.equals(((ModuleOrderEntry) entry).getModuleName())) {
                model.removeOrderEntry(entry);
                modelChangedFlag.set(true);
            }
        }
        final VirtualFile moduleFile = genModule.getModuleFile();
        moduleManager.disposeModule(genModule);

        if (moduleFile != null) {
            ApplicationManager.getApplication().invokeLater(new Runnable() {
                @Override
                public void run() {
                    ApplicationManager.getApplication().runWriteAction(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                moduleFile.delete(project);
                            } catch (IOException e) {
                                LOG.error(e);
                            }
                        }
                    });
                }
            });
        }
    }

    public static void addSourceRoot(final ModifiableRootModel model, @NotNull final VirtualFile root) {
        ContentEntry contentEntry = findContentEntryForRoot(model, root);
        if (contentEntry == null) {
            contentEntry = model.addContentEntry(root);
        }
        contentEntry.addSourceFolder(root, false);
    }

    @Nullable
    public static ContentEntry findContentEntryForRoot(@NotNull ModifiableRootModel model,
            @NotNull VirtualFile root) {
        ContentEntry contentEntry = null;
        for (ContentEntry candidate : model.getContentEntries()) {
            VirtualFile contentRoot = candidate.getFile();
            if (contentRoot != null && VfsUtilCore.isAncestor(contentRoot, root, false)) {
                contentEntry = candidate;
            }
        }
        return contentEntry;
    }

    public static void generate(final Module module, final AndroidAutogeneratorMode mode,
            boolean withDependentModules) {
        if (withDependentModules) {
            Set<Module> modules = new HashSet<Module>();
            collectModules(module, modules, ModuleManager.getInstance(module.getProject()).getModules());
            for (Module module1 : modules) {
                generate(module1, mode);
            }
        } else {
            generate(module, mode);
        }
    }

    public static void generate(final Module module, final AndroidAutogeneratorMode mode) {
        final AndroidFacet facet = AndroidFacet.getInstance(module);

        if (facet != null) {
            facet.scheduleSourceRegenerating(mode);
        }
    }

    public static boolean doGenerate(AndroidFacet facet, final AndroidAutogeneratorMode mode) {
        assert !ApplicationManager.getApplication().isDispatchThread();
        final CompileContext[] contextWrapper = new CompileContext[1];
        final Module module = facet.getModule();
        final Project project = module.getProject();

        ApplicationManager.getApplication().runReadAction(new Runnable() {
            @Override
            public void run() {
                if (project.isDisposed())
                    return;
                CompilerTask task = new CompilerTask(project, "Android auto-generation", true, false, true, true);
                CompileScope scope = new ModuleCompileScope(module, false);
                contextWrapper[0] = new CompileContextImpl(project, task, scope, null, false, false);
            }
        });
        CompileContext context = contextWrapper[0];
        if (context == null) {
            return false;
        }
        generate(facet, mode, context);
        return context.getMessages(CompilerMessageCategory.ERROR).length == 0;
    }

    public static boolean isModuleAffected(CompileContext context, Module module) {
        return ArrayUtil.find(context.getCompileScope().getAffectedModules(), module) >= 0;
    }

    public static void generate(AndroidFacet facet, AndroidAutogeneratorMode mode, final CompileContext context) {
        if (context == null) {
            return;
        }
        AndroidAutogenerator.run(mode, facet, context);
    }

    private static void collectModules(Module module, Set<Module> result, Module[] allModules) {
        if (!result.add(module)) {
            return;
        }
        for (Module otherModule : allModules) {
            if (ModuleRootManager.getInstance(otherModule).isDependsOn(module)) {
                collectModules(otherModule, result, allModules);
            }
        }
    }

    // must be invoked in a read action!

    public static void removeDuplicatingClasses(final Module module, @NotNull final String packageName,
            @NotNull String className, @Nullable File classFile, String sourceRootPath) {
        if (sourceRootPath == null) {
            return;
        }
        VirtualFile sourceRoot = LocalFileSystem.getInstance().findFileByPath(sourceRootPath);
        if (sourceRoot == null) {
            return;
        }
        final Project project = module.getProject();
        final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
        final String interfaceQualifiedName = packageName + '.' + className;
        PsiClass[] classes = facade.findClasses(interfaceQualifiedName, GlobalSearchScope.moduleScope(module));
        final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
        for (PsiClass c : classes) {
            PsiFile psiFile = c.getContainingFile();
            if (className.equals(FileUtil.getNameWithoutExtension(psiFile.getName()))) {
                VirtualFile virtualFile = psiFile.getVirtualFile();
                if (virtualFile != null
                        && Comparing.equal(projectFileIndex.getSourceRootForFile(virtualFile), sourceRoot)) {
                    final String path = virtualFile.getPath();
                    final File f = new File(path);

                    if (!FileUtil.filesEqual(f, classFile) && f.exists()) {
                        if (f.delete()) {
                            virtualFile.refresh(true, false);
                        } else {
                            ApplicationManager.getApplication().invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    Messages.showErrorDialog(project, "Can't delete file " + path,
                                            CommonBundle.getErrorTitle());
                                }
                            }, project.getDisposed());
                        }
                    }
                }
            }
        }
    }

    @NotNull
    public static String[] collectResourceDirs(AndroidFacet facet, boolean collectResCacheDirs,
            @Nullable CompileContext context) {
        final Project project = facet.getModule().getProject();
        final IntermediateOutputCompiler pngFilesCachingCompiler = collectResCacheDirs
                ? Extensions.findExtension(Compiler.EP_NAME, project, AndroidPngFilesCachingCompiler.class)
                : null;

        if (collectResCacheDirs) {
            assert pngFilesCachingCompiler != null;
        }

        final List<String> result = new ArrayList<String>();

        doCollectResourceDirs(facet, collectResCacheDirs, result, context);

        for (AndroidFacet depFacet : AndroidUtils.getAllAndroidDependencies(facet.getModule(), true)) {
            doCollectResourceDirs(depFacet, collectResCacheDirs, result, context);
        }
        return ArrayUtil.toStringArray(result);
    }

    private static void doCollectResourceDirs(AndroidFacet facet, boolean collectResCacheDirs, List<String> result,
            CompileContext context) {
        final Module module = facet.getModule();

        if (collectResCacheDirs) {
            final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform();
            final int platformToolsRevision = platform != null ? platform.getSdkData().getPlatformToolsRevision()
                    : -1;

            if (platformToolsRevision < 0 || platformToolsRevision > 7) {
                // png cache is supported since platform-tools-r8
                final String resCacheDirOsPath = findResourcesCacheDirectory(module, false, context);
                if (resCacheDirOsPath != null) {
                    result.add(resCacheDirOsPath);
                } else {
                    LOG.info("PNG cache not found for module " + module.getName());
                }
            }
        }

        final VirtualFile resourcesDir = AndroidAptCompiler.getResourceDirForApkCompiler(facet);
        if (resourcesDir != null) {
            result.add(resourcesDir.getPath());
        }
    }

    @Nullable
    public static String findResourcesCacheDirectory(@NotNull Module module, boolean createIfNotFound,
            @Nullable CompileContext context) {
        final Project project = module.getProject();

        final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(project);
        if (extension == null) {
            if (context != null) {
                context.addMessage(CompilerMessageCategory.ERROR,
                        "Cannot get compiler settings for project " + project.getName(), null, -1, -1);
            }
            return null;
        }

        final String projectOutputDirUrl = extension.getCompilerOutputUrl();
        if (projectOutputDirUrl == null) {
            if (context != null) {
                context.addMessage(CompilerMessageCategory.ERROR,
                        "Cannot find output directory for project " + project.getName(), null, -1, -1);
            }
            return null;
        }

        final String pngCacheDirPath = VfsUtil.urlToPath(projectOutputDirUrl) + '/' + RESOURCES_CACHE_DIR_NAME + '/'
                + module.getName();
        final String pngCacheDirOsPath = FileUtil.toSystemDependentName(pngCacheDirPath);

        final File pngCacheDir = new File(pngCacheDirOsPath);
        if (pngCacheDir.exists()) {
            if (pngCacheDir.isDirectory()) {
                return pngCacheDirOsPath;
            } else {
                if (context != null) {
                    context.addMessage(CompilerMessageCategory.ERROR,
                            "Cannot create directory " + pngCacheDirOsPath + " because file already exists", null,
                            -1, -1);
                }
                return null;
            }
        }

        if (!createIfNotFound) {
            return null;
        }

        if (!pngCacheDir.mkdirs()) {
            if (context != null) {
                context.addMessage(CompilerMessageCategory.ERROR, "Cannot create directory " + pngCacheDirOsPath,
                        null, -1, -1);
            }
            return null;
        }

        return pngCacheDirOsPath;
    }

    public static boolean isFullBuild(@NotNull CompileContext context) {
        return isFullBuild(context.getCompileScope());
    }

    public static boolean isFullBuild(@NotNull CompileScope scope) {
        final RunConfiguration c = CompileStepBeforeRun.getRunConfiguration(scope);
        return c == null || !AndroidCommonUtils.isTestConfiguration(c.getType().getId());
    }

    public static boolean isReleaseBuild(@NotNull CompileContext context) {
        final Boolean value = context.getCompileScope().getUserData(RELEASE_BUILD_KEY);
        if (value != null && value.booleanValue()) {
            return true;
        }
        final Project project = context.getProject();
        final Set<Artifact> artifacts = ArtifactCompileScope.getArtifactsToBuild(project, context.getCompileScope(),
                false);

        if (artifacts != null) {
            for (Artifact artifact : artifacts) {
                final ArtifactProperties<?> properties = artifact
                        .getProperties(AndroidArtifactPropertiesProvider.getInstance());
                if (properties instanceof AndroidApplicationArtifactProperties
                        && ((AndroidApplicationArtifactProperties) properties)
                                .getSigningMode() != AndroidArtifactSigningMode.DEBUG) {
                    return true;
                }
            }
        }
        return false;
    }

    public static void setReleaseBuild(@NotNull CompileScope compileScope) {
        compileScope.putUserData(RELEASE_BUILD_KEY, Boolean.TRUE);
    }

    public static boolean createGenModulesAndSourceRoots(@NotNull AndroidFacet facet,
            @NotNull ModifiableRootModel model) {
        if (facet.isGradleProject()) {
            return false;
        }
        final Module module = facet.getModule();
        final GlobalSearchScope moduleScope = facet.getModule().getModuleScope();
        final Ref<Boolean> modelChangedFlag = Ref.create(false);

        if (facet.isLibraryProject()) {
            removeGenModule(model, modelChangedFlag);
        }
        initializeGenSourceRoot(model, AndroidRootUtil.getBuildconfigGenSourceRootPath(facet), true, true,
                modelChangedFlag);

        initializeGenSourceRoot(model, AndroidRootUtil.getRenderscriptGenSourceRootPath(facet),
                FileTypeIndex.getFiles(AndroidRenderscriptFileType.INSTANCE, moduleScope).size() > 0, true,
                modelChangedFlag);

        if (AndroidAptCompiler.isToCompileModule(module, facet.getConfiguration())) {
            initializeGenSourceRoot(model, AndroidRootUtil.getAptGenSourceRootPath(facet), true, true,
                    modelChangedFlag);
        } else {
            // we need to include generated-sources/r to compilation, because it contains R.java generated by Maven,
            // which should be used in Maven-based resource processing mode
            final VirtualFile aptSourceRoot = initializeGenSourceRoot(model,
                    AndroidRootUtil.getAptGenSourceRootPath(facet), true, false, modelChangedFlag);

            if (aptSourceRoot != null) {
                excludeAllBuildConfigsFromCompilation(facet, aptSourceRoot);
            }
            includeAaptGenSourceRootToCompilation(facet);
        }
        initializeGenSourceRoot(model, AndroidRootUtil.getAidlGenSourceRootPath(facet),
                FileTypeIndex.getFiles(AndroidIdlFileType.ourFileType, moduleScope).size() > 0, true,
                modelChangedFlag);
        return modelChangedFlag.get();
    }

    private static void excludeAllBuildConfigsFromCompilation(AndroidFacet facet, VirtualFile sourceRoot) {
        final Module module = facet.getModule();
        final Project project = module.getProject();
        final Set<String> packages = new HashSet<String>();

        final Manifest manifest = facet.getManifest();
        final String aPackage = manifest != null ? manifest.getPackage().getStringValue() : null;

        if (aPackage != null) {
            packages.add(aPackage);
        }
        packages.addAll(AndroidUtils.getDepLibsPackages(module));

        for (String p : packages) {
            excludeFromCompilation(project, sourceRoot, p);
        }
    }

    private static void includeAaptGenSourceRootToCompilation(AndroidFacet facet) {
        final Project project = facet.getModule().getProject();
        final ExcludedEntriesConfiguration configuration = ((CompilerConfigurationImpl) CompilerConfiguration
                .getInstance(project)).getExcludedEntriesConfiguration();
        final ExcludeEntryDescription[] descriptions = configuration.getExcludeEntryDescriptions();

        configuration.removeAllExcludeEntryDescriptions();

        for (ExcludeEntryDescription description : descriptions) {
            final VirtualFile vFile = description.getVirtualFile();
            if (!Comparing.equal(vFile, AndroidRootUtil.getAaptGenDir(facet))) {
                configuration.addExcludeEntryDescription(description);
            }
        }
    }

    @Nullable
    private static VirtualFile initializeGenSourceRoot(@NotNull ModifiableRootModel model,
            @Nullable String sourceRootPath, boolean createIfNotExist, boolean excludeInNonExternalMode,
            @NotNull Ref<Boolean> modelChangedFlag) {
        if (sourceRootPath == null) {
            return null;
        }
        VirtualFile sourceRoot = null;

        if (createIfNotExist) {
            final VirtualFile root = createSourceRootIfNotExist(sourceRootPath, model, modelChangedFlag);
            if (root != null) {
                sourceRoot = root;
            }
        }
        if (sourceRoot == null) {
            sourceRoot = LocalFileSystem.getInstance().findFileByPath(sourceRootPath);
        }
        if (sourceRoot != null && excludeInNonExternalMode) {
            final Module module = model.getModule();
            final CompilerWorkspaceConfiguration config = CompilerWorkspaceConfiguration
                    .getInstance(module.getProject());

            // In JPS generated roots are excluded by AndroidExcludeJavaSourceRootProvider,
            // so we don't need to mark them in the project model
            if (!config.USE_COMPILE_SERVER) {
                excludeFromCompilation(module.getProject(), sourceRoot);
            }
        }
        return sourceRoot;
    }

    @NotNull
    public static String[] toOsPaths(@NotNull VirtualFile[] classFilesDirs) {
        final String[] classFilesDirOsPaths = new String[classFilesDirs.length];

        for (int i = 0; i < classFilesDirs.length; i++) {
            classFilesDirOsPaths[i] = FileUtil.toSystemDependentName(classFilesDirs[i].getPath());
        }
        return classFilesDirOsPaths;
    }

    // can't be invoked from dispatch thread
    @NotNull
    public static Map<CompilerMessageCategory, List<String>> execute(String... argv) throws IOException {
        assert !ApplicationManager.getApplication().isDispatchThread();
        final Map<AndroidCompilerMessageKind, List<String>> messages = AndroidExecutionUtil.doExecute(argv);
        return toCompilerMessageCategoryKeys(messages);
    }

    public static String getApkName(Module module) {
        return module.getName() + ".apk";
    }

    @Nullable
    public static String getOutputPackage(@NotNull Module module) {
        VirtualFile compilerOutput = CompilerModuleExtension.getInstance(module).getCompilerOutputPath();
        if (compilerOutput == null)
            return null;
        return new File(compilerOutput.getPath(), getApkName(module)).getPath();
    }

    public static boolean isExcludedFromCompilation(@NotNull File file, @Nullable Project project) {
        final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file);
        return vFile != null && isExcludedFromCompilation(vFile, project);
    }

    public static boolean isExcludedFromCompilation(VirtualFile child, @Nullable Project project) {
        final CompilerManager compilerManager = project != null ? CompilerManager.getInstance(project) : null;

        if (compilerManager == null) {
            return false;
        }

        if (!compilerManager.isExcludedFromCompilation(child)) {
            return false;
        }

        final Module module = ModuleUtil.findModuleForFile(child, project);
        if (module == null) {
            return true;
        }

        final AndroidFacet facet = AndroidFacet.getInstance(module);
        if (facet == null || !facet.isLibraryProject()) {
            return true;
        }

        final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform();
        if (platform == null) {
            return true;
        }

        // we exclude sources of library modules automatically for tools r7 or previous
        return platform.getSdkData().getPlatformToolsRevision() > 7;
    }

    @Nullable
    public static ProguardRunningOptions getProguardConfigFilePathIfShouldRun(@NotNull AndroidFacet facet,
            CompileContext context) {
        // wizard
        String path = context.getCompileScope().getUserData(AndroidProguardCompiler.PROGUARD_CFG_PATH_KEY);
        if (path != null) {
            final Boolean includeSystemProguardFile = context.getCompileScope()
                    .getUserData(AndroidProguardCompiler.INCLUDE_SYSTEM_PROGUARD_FILE);
            return new ProguardRunningOptions(path, Boolean.TRUE.equals(includeSystemProguardFile));
        }

        // artifact
        final Project project = context.getProject();
        final Set<Artifact> artifacts = ArtifactCompileScope.getArtifactsToBuild(project, context.getCompileScope(),
                false);

        for (Artifact artifact : artifacts) {
            if (artifact.getArtifactType() instanceof AndroidApplicationArtifactType
                    && facet.equals(AndroidArtifactUtil.getPackagedFacet(project, artifact))) {
                final ArtifactProperties<?> properties = artifact
                        .getProperties(AndroidArtifactPropertiesProvider.getInstance());

                if (properties instanceof AndroidApplicationArtifactProperties) {
                    final AndroidApplicationArtifactProperties p = (AndroidApplicationArtifactProperties) properties;

                    if (p.isRunProGuard()) {
                        path = FileUtil.toSystemDependentName(VfsUtilCore.urlToPath(p.getProGuardCfgFileUrl()));
                        return new ProguardRunningOptions(path, p.isIncludeSystemProGuardCfgFile());
                    }
                }
            }
        }

        // facet
        final AndroidFacetConfiguration configuration = facet.getConfiguration();
        if (configuration.getState().RUN_PROGUARD) {
            final VirtualFile proguardCfgFile = AndroidRootUtil.getProguardCfgFile(facet);
            final String proguardCfgPath = proguardCfgFile != null
                    ? FileUtil.toSystemDependentName(proguardCfgFile.getPath())
                    : null;
            return new ProguardRunningOptions(proguardCfgPath, configuration.isIncludeSystemProguardCfgPath());
        }
        return null;
    }

    @Nullable
    public static Module findCircularDependencyOnLibraryWithSamePackage(@NotNull AndroidFacet facet) {
        final Manifest manifest = facet.getManifest();
        final String aPackage = manifest != null ? manifest.getPackage().getValue() : null;
        if (aPackage == null) {
            return null;
        }

        for (AndroidFacet depFacet : AndroidUtils.getAllAndroidDependencies(facet.getModule(), true)) {
            final Manifest depManifest = depFacet.getManifest();
            final String depPackage = depManifest != null ? depManifest.getPackage().getValue() : null;
            if (aPackage.equals(depPackage)) {
                final List<AndroidFacet> depDependencies = AndroidUtils
                        .getAllAndroidDependencies(depFacet.getModule(), false);

                if (depDependencies.contains(facet)) {
                    // circular dependency on library with the same package
                    return depFacet.getModule();
                }
            }
        }
        return null;
    }

    @NotNull
    public static String[] getLibPackages(@NotNull Module module, @NotNull String packageName) {
        final Set<String> packageSet = new HashSet<String>();
        packageSet.add(packageName);

        final List<String> result = new ArrayList<String>();

        for (String libPackage : AndroidUtils.getDepLibsPackages(module)) {
            if (packageSet.add(libPackage)) {
                result.add(libPackage);
            }
        }

        return ArrayUtil.toStringArray(result);
    }

    // support for lib<->lib and app<->lib circular dependencies
    // see IDEA-79737 for details
    public static boolean isLibraryWithBadCircularDependency(@NotNull AndroidFacet facet) {
        if (!facet.isLibraryProject()) {
            return false;
        }

        final List<AndroidFacet> dependencies = AndroidUtils.getAllAndroidDependencies(facet.getModule(), false);

        final Manifest manifest = facet.getManifest();
        if (manifest == null) {
            return false;
        }

        final String aPackage = manifest.getPackage().getValue();
        if (aPackage == null || aPackage.length() == 0) {
            return false;
        }

        for (AndroidFacet depFacet : dependencies) {
            final List<AndroidFacet> depDependencies = AndroidUtils.getAllAndroidDependencies(depFacet.getModule(),
                    true);

            if (depDependencies.contains(facet) && dependencies.contains(depFacet)
                    && (depFacet.getModule().getName().compareTo(facet.getModule().getName()) < 0
                            || !depFacet.isLibraryProject())) {
                return true;
            }
        }
        return false;
    }

    @Nullable
    public static String getUnsignedApkPath(@NotNull AndroidFacet facet) {
        final boolean useCompileServer = CompilerWorkspaceConfiguration
                .getInstance(facet.getModule().getProject()).USE_COMPILE_SERVER;
        final String apkPath = AndroidRootUtil.getApkPath(facet);

        if (apkPath != null) {
            return useCompileServer ? apkPath : AndroidCommonUtils.addSuffixToFileName(apkPath, UNSIGNED_SUFFIX);
        }
        return null;
    }

    @Nullable
    public static <T> T handleExceptionError(@NotNull CompileContext context, @NotNull String messagePrefix,
            @NotNull Exception e) {
        reportException(context, messagePrefix, e);
        return null;
    }

    public static void reportException(@NotNull CompileContext context, @NotNull String messagePrefix,
            @NotNull Exception e) {
        context.addMessage(CompilerMessageCategory.ERROR,
                messagePrefix + e.getClass().getSimpleName() + ": " + e.getMessage(), null, -1, -1);
    }

    public static void markDirtyAndRefresh(VirtualFile f, boolean recursively) {
        markDirty(f, recursively);
        f.refresh(false, recursively);
    }

    public static void markDirty(VirtualFile f, boolean recursively) {
        if (f instanceof NewVirtualFile) {
            final NewVirtualFile newF = (NewVirtualFile) f;

            if (recursively) {
                newF.markDirtyRecursively();
            } else {
                newF.markDirty();
            }
        }
    }

    @Nullable
    public static String getAaptManifestPackage(@NotNull AndroidFacet facet) {
        if (facet.getProperties().USE_CUSTOM_MANIFEST_PACKAGE) {
            return facet.getProperties().CUSTOM_MANIFEST_PACKAGE;
        }
        final Manifest manifest = facet.getManifest();

        return manifest != null ? manifest.getPackage().getStringValue() : null;
    }

    public static void createGenModulesAndSourceRoots(@NotNull Project project,
            @NotNull final Collection<AndroidFacet> facets) {
        if (project.isDisposed()) {
            return;
        }
        final ModifiableModuleModel moduleModel = ModuleManager.getInstance(project).getModifiableModel();

        ApplicationManager.getApplication().runWriteAction(new Runnable() {
            @Override
            public void run() {
                final List<ModifiableRootModel> modelsToCommit = new ArrayList<ModifiableRootModel>();

                for (final AndroidFacet facet : facets) {
                    if (facet.isGradleProject()) {
                        continue;
                    }
                    final Module module = facet.getModule();

                    if (module.isDisposed()) {
                        continue;
                    }
                    final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel();

                    if (createGenModulesAndSourceRoots(facet, model)) {
                        modelsToCommit.add(model);
                    } else {
                        model.dispose();
                    }
                }
                if (modelsToCommit.size() == 0) {
                    return;
                }

                ModifiableModelCommitter.multiCommit(
                        modelsToCommit.toArray(new ModifiableRootModel[modelsToCommit.size()]), moduleModel);
            }
        });
    }
}