/* * 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.android.tools.idea.fileTypes.AndroidRenderscriptFileType; 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.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.CompileContext; import com.intellij.openapi.compiler.CompileScope; import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.compiler.CompilerMessageCategory; import com.intellij.openapi.compiler.options.ExcludeEntryDescription; import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration; import com.intellij.openapi.diagnostic.Logger; 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.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.ModifiableModelCommitter; import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable; 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.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 org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.util.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.android.model.impl.JpsAndroidModuleProperties; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.jps.model.java.JavaSourceRootProperties; import org.jetbrains.jps.model.java.JavaSourceRootType; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import javax.swing.event.HyperlinkEvent; 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"; public static Key<String> PROGUARD_CFG_PATHS_KEY = Key.create(AndroidCommonUtils.PROGUARD_CFG_PATHS_OPTION); 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) { if (contentEntry.removeExcludeFolder(excludedRoot.getUrl())) { 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) { markRootAsGenerated(model, root, modelChangedFlag); } else { addSourceRoot(model, root); modelChangedFlag.set(true); } } return root; } private static void markRootAsGenerated(ModifiableRootModel model, VirtualFile root, Ref<Boolean> modelChangedFlag) { final ContentEntry contentEntry = findContentEntryForRoot(model, root); if (contentEntry == null) { return; } for (SourceFolder sourceFolder : contentEntry.getSourceFolders()) { if (root.equals(sourceFolder.getFile())) { final JavaSourceRootProperties props = sourceFolder.getJpsElement().getProperties(JavaModuleSourceRootTypes.SOURCES); if (props != null) { props.setForGeneratedSources(true); modelChangedFlag.set(true); break; } } } } 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); } } }); } }); } } @Nullable public static SourceFolder addSourceRoot(final ModifiableRootModel model, @NotNull final VirtualFile root) { ContentEntry contentEntry = findContentEntryForRoot(model, root); if (contentEntry == null) { final Project project = model.getProject(); final String message = "Cannot mark directory '" + FileUtil.toSystemDependentName(root.getPath()) + "' as source root, because it is not located under content root of module '" + model.getModule().getName() + "'\n<a href='fix'>Open Project Structure</a>"; final Notification notification = new Notification( AndroidBundle.message("android.autogeneration.notification.group"), "Autogeneration Error", message, NotificationType.ERROR, new NotificationListener.Adapter() { @Override protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) { notification.expire(); final ProjectStructureConfigurable configurable = ProjectStructureConfigurable.getInstance(project); ShowSettingsUtil.getInstance() .editConfigurable(project, configurable, new Runnable() { @Override public void run() { final Module module = model.getModule(); final AndroidFacet facet = AndroidFacet.getInstance(module); if (facet != null) { configurable.select(facet, true); } } }); } }); Notifications.Bus.notify(notification, project); LOG.debug(message); return null; } else { return contentEntry.addSourceFolder(root, JavaSourceRootType.SOURCE, JpsJavaExtensionService.getInstance().createSourceRootProperties("", true)); } } @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) { 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, false, false); } }); CompileContext context = contextWrapper[0]; if (context == null) { return false; } generate(facet, mode, context, false); 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, boolean force) { if (context == null) { return; } AndroidAutogenerator.run(mode, facet, context, force); } // 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()); } } } } } } @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) { final AndroidArtifactSigningMode signingMode = ((AndroidApplicationArtifactProperties)properties).getSigningMode(); if (signingMode != AndroidArtifactSigningMode.DEBUG && signingMode != AndroidArtifactSigningMode.DEBUG_WITH_CUSTOM_CERTIFICATE) { 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() || !facet.getProperties().ENABLE_SOURCES_AUTOGENERATION) { 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); } final Set<String> genRootsToCreate = new HashSet<String>(); final Set<String> genRootsToInit = new HashSet<String>(); final String buildConfigGenRootPath = AndroidRootUtil.getBuildconfigGenSourceRootPath(facet); if (buildConfigGenRootPath != null) { genRootsToCreate.add(buildConfigGenRootPath); } final String renderscriptGenRootPath = AndroidRootUtil.getRenderscriptGenSourceRootPath(facet); if (renderscriptGenRootPath != null) { final boolean createIfNotExist = FileTypeIndex.getFiles(AndroidRenderscriptFileType.INSTANCE, moduleScope).size() > 0; (createIfNotExist ? genRootsToCreate : genRootsToInit).add(renderscriptGenRootPath); } final String aptGenRootPath = AndroidRootUtil.getAptGenSourceRootPath(facet); if (aptGenRootPath != null) { genRootsToCreate.add(aptGenRootPath); } final String aidlGenRootPath = AndroidRootUtil.getAidlGenSourceRootPath(facet); if (aidlGenRootPath != null) { final boolean createIfNotExist = FileTypeIndex.getFiles(AndroidIdlFileType.ourFileType, moduleScope).size() > 0; (createIfNotExist ? genRootsToCreate : genRootsToInit).add(aidlGenRootPath); } genRootsToInit.addAll(genRootsToCreate); for (String genRootPath : genRootsToInit) { initializeGenSourceRoot(model, genRootPath, genRootsToCreate.contains(genRootPath), 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()); } 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 pathsStr = context.getCompileScope().getUserData(PROGUARD_CFG_PATHS_KEY); if (pathsStr != null) { final String[] paths = pathsStr.split(File.pathSeparator); if (paths.length > 0) { return new ProguardRunningOptions(Arrays.asList(paths)); } } final AndroidPlatform platform = AndroidPlatform.getInstance(facet.getModule()); final String sdkHomePath = platform != null ? FileUtil.toCanonicalPath(platform.getSdkData().getPath()) : null; // 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()) { final List<String> paths = AndroidUtils.urlsToOsPaths(p.getProGuardCfgFiles(), sdkHomePath); return new ProguardRunningOptions(paths); } } } } // facet final AndroidFacetConfiguration configuration = facet.getConfiguration(); final JpsAndroidModuleProperties properties = configuration.getState(); if (properties != null && properties.RUN_PROGUARD) { final List<String> urls = properties.myProGuardCfgFiles; final List<String> paths = AndroidUtils.urlsToOsPaths(urls, sdkHomePath); return new ProguardRunningOptions(paths); } 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) { return AndroidRootUtil.getApkPath(facet); } 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); } @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); } }); } }