blob: ffc77cc9fe06c9e81fc1b22de5dbd1e258f494cb [file] [log] [blame]
/*
* 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.ExcludesConfiguration;
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 com.android.tools.idea.lang.aidl.AidlFileType;
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 ExcludesConfiguration 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 ExcludesConfiguration 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.requiresAndroidModel() || !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(AidlFileType.INSTANCE, 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 ExcludesConfiguration 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.requiresAndroidModel()) {
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);
}
});
}
}