blob: b5997b3536c4af232e9d08ff1fae2ebbc9976bec [file] [log] [blame]
package org.jetbrains.android.compiler;
import com.android.SdkConstants;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.build.BuildConfigGenerator;
import com.android.tools.idea.fileTypes.AndroidRenderscriptFileType;
import com.intellij.compiler.impl.CompilerUtil;
import com.intellij.compiler.impl.ModuleCompileScope;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import org.jetbrains.android.compiler.tools.AndroidIdl;
import org.jetbrains.android.compiler.tools.AndroidRenderscript;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.facet.AndroidFacet;
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.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidAutogenerator {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidAutogenerator");
private AndroidAutogenerator() {
}
private static boolean toRun(@NotNull AndroidAutogeneratorMode mode, @NotNull AndroidFacet facet, boolean force) {
if (!supportsAutogeneration(facet)) {
return false;
}
if (!force && !facet.getProperties().ENABLE_SOURCES_AUTOGENERATION) {
return false;
}
switch (mode) {
case AAPT:
case AIDL:
case RENDERSCRIPT:
case BUILDCONFIG:
return true;
default:
LOG.error("Unknown autogenerator mode " + mode);
return false;
}
}
public static boolean supportsAutogeneration(@NotNull AndroidFacet facet) {
// This is a cheap way to figure out that a module has the Android-Gradle facet.
// Don't generate anything if a module has an Android-Gradle facet.
return !facet.requiresAndroidModel();
}
public static void run(@NotNull AndroidAutogeneratorMode mode,
@NotNull AndroidFacet facet,
@NotNull CompileContext context,
boolean force) {
if (!toRun(mode, facet, force)) {
return;
}
final Set<String> obsoleteFiles = new HashSet<String>(facet.getAutogeneratedFiles(mode));
switch (mode) {
case AAPT:
runAapt(facet, context, force);
break;
case AIDL:
runAidl(facet, context);
break;
case RENDERSCRIPT:
runRenderscript(facet, context);
break;
case BUILDCONFIG:
runBuildConfigGenerator(facet, context);
break;
default:
LOG.error("Unknown mode" + mode);
}
obsoleteFiles.removeAll(facet.getAutogeneratedFiles(mode));
for (String path : obsoleteFiles) {
final File file = new File(path);
if (file.isFile()) {
FileUtil.delete(file);
CompilerUtil.refreshIOFile(file);
}
}
}
private static void runBuildConfigGenerator(@NotNull final AndroidFacet facet, @NotNull final CompileContext context) {
final Module module = facet.getModule();
final BuildconfigAutogenerationItem item = ApplicationManager.getApplication().runReadAction(
new Computable<BuildconfigAutogenerationItem>() {
@Nullable
@Override
public BuildconfigAutogenerationItem compute() {
if (module.isDisposed() || module.getProject().isDisposed()) {
return null;
}
final String sourceRootPath = AndroidRootUtil.getBuildconfigGenSourceRootPath(facet);
if (sourceRootPath == null) {
return null;
}
final VirtualFile manifestFile = AndroidRootUtil.getManifestFileForCompiler(facet);
if (manifestFile == null) {
context.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.manifest.not.found", module.getName()), null, -1, -1);
return null;
}
final Manifest manifest = AndroidUtils.loadDomElement(module, manifestFile, Manifest.class);
if (manifest == null) {
context.addMessage(CompilerMessageCategory.ERROR, "Cannot parse file", manifestFile.getUrl(), -1, -1);
return null;
}
String packageName = manifest.getPackage().getValue();
if (packageName != null) {
packageName = packageName.trim();
}
if (packageName == null || packageName.length() <= 0) {
context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("package.not.found.error"), manifestFile.getUrl(),
-1, -1);
return null;
}
if (!AndroidUtils.isValidAndroidPackageName(packageName)) {
context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("not.valid.package.name.error", packageName),
manifestFile.getUrl(), -1, -1);
return null;
}
return new BuildconfigAutogenerationItem(packageName, FileUtil.toSystemDependentName(sourceRootPath));
}
});
if (item == null) {
return;
}
try {
// hack for IDEA-100046: we need to avoid reporting "condition is always 'true'
// from data flow inspection, so use non-constant value here
generateStubClass(item.myPackage, new File(item.mySourceRootOsPath), "BuildConfig",
" public final static boolean DEBUG = Boolean.parseBoolean(null);\n");
final VirtualFile genSourceRoot = LocalFileSystem.getInstance().findFileByPath(item.mySourceRootOsPath);
if (genSourceRoot != null) {
genSourceRoot.refresh(false, true);
}
facet.clearAutogeneratedFiles(AndroidAutogeneratorMode.BUILDCONFIG);
final VirtualFile genFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(
item.mySourceRootOsPath + '/' + item.myPackage.replace('.', '/') + '/' + BuildConfigGenerator.BUILD_CONFIG_NAME);
if (genFile != null && genFile.exists()) {
facet.markFileAutogenerated(AndroidAutogeneratorMode.BUILDCONFIG, genFile);
}
}
catch (final IOException e) {
LOG.info(e);
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (module.getProject().isDisposed()) return;
context.addMessage(CompilerMessageCategory.ERROR, "I/O error: " + e.getMessage(), null, -1, -1);
}
});
}
}
private static void runAapt(@NotNull final AndroidFacet facet, @NotNull final CompileContext context, boolean force) {
final Module module = facet.getModule();
final AptAutogenerationItem item = ApplicationManager.getApplication().runReadAction(new Computable<AptAutogenerationItem>() {
@Nullable
@Override
public AptAutogenerationItem compute() {
if (module.isDisposed() || module.getProject().isDisposed()) {
return null;
}
final VirtualFile manifestFile = AndroidRootUtil.getManifestFileForCompiler(facet);
if (manifestFile == null) {
context.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.manifest.not.found", module.getName()), null, -1, -1);
return null;
}
final Manifest manifest = AndroidUtils.loadDomElement(module, manifestFile, Manifest.class);
if (manifest == null) {
context.addMessage(CompilerMessageCategory.ERROR, "Cannot parse file", manifestFile.getUrl(), -1, -1);
return null;
}
String packageName = manifest.getPackage().getValue();
if (packageName != null) {
packageName = packageName.trim();
}
if (packageName == null || packageName.length() <= 0) {
context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("package.not.found.error"), manifestFile.getUrl(),
-1, -1);
return null;
}
if (!AndroidUtils.isValidAndroidPackageName(packageName)) {
context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("not.valid.package.name.error", packageName),
manifestFile.getUrl(), -1, -1);
return null;
}
final String sourceRootPath = AndroidRootUtil.getAptGenSourceRootPath(facet);
if (sourceRootPath == null) {
context.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.apt.gen.not.specified", module.getName()),
null, -1, -1);
return null;
}
final Map<String, String> genFilePath2Package = new HashMap<String, String>();
final String packageDir = packageName.replace('.', '/') + '/';
genFilePath2Package.put(packageDir + AndroidCommonUtils.MANIFEST_JAVA_FILE_NAME, packageName);
genFilePath2Package.put(packageDir + AndroidCommonUtils.R_JAVA_FILENAME, packageName);
return new AptAutogenerationItem(packageName, sourceRootPath, genFilePath2Package);
}
});
if (item == null) {
return;
}
if (force) {
final Set<VirtualFile> filesToCheck = new HashSet<VirtualFile>();
for (String genFileRelPath : item.myGenFileRelPath2package.keySet()) {
final String genFileFullPath = item.myOutputDirOsPath + '/' + genFileRelPath;
if (new File(genFileFullPath).exists()) {
final VirtualFile genFile = LocalFileSystem.getInstance().findFileByPath(genFileFullPath);
if (genFile != null) {
filesToCheck.add(genFile);
}
}
}
if (!ensureFilesWritable(module.getProject(), filesToCheck)) {
return;
}
}
File tempOutDir = null;
try {
// Aapt generation can be very long, so we generate it in temp directory first
tempOutDir = FileUtil.createTempDirectory("android_apt_autogeneration", "tmp");
generateStubClasses(item.myPackage, tempOutDir,
AndroidUtils.R_CLASS_NAME,
AndroidUtils.MANIFEST_CLASS_NAME);
for (String genFileRelPath : item.myGenFileRelPath2package.keySet()) {
final File srcFile = new File(tempOutDir.getPath() + '/' + genFileRelPath);
if (srcFile.isFile()) {
final File dstFile = new File(item.myOutputDirOsPath + '/' + genFileRelPath);
if (dstFile.exists()) {
if (!force) {
continue;
}
if (!FileUtil.delete(dstFile)) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (module.isDisposed() || module.getProject().isDisposed()) {
return;
}
context.addMessage(CompilerMessageCategory.ERROR,
"Cannot delete " + FileUtil.toSystemDependentName(dstFile.getPath()), null, -1, -1);
}
});
}
}
FileUtil.rename(srcFile, dstFile);
}
}
for (Map.Entry<String, String> entry : item.myGenFileRelPath2package.entrySet()) {
final String path = item.myOutputDirOsPath + '/' + entry.getKey();
final String aPackage = entry.getValue();
final File file = new File(path);
CompilerUtil.refreshIOFile(file);
removeAllFilesWithSameName(module, file, item.myOutputDirOsPath);
removeDuplicateClasses(module, aPackage, file, item.myOutputDirOsPath);
}
final VirtualFile genSourceRoot = LocalFileSystem.getInstance().findFileByPath(item.myOutputDirOsPath);
if (genSourceRoot != null) {
genSourceRoot.refresh(false, true);
}
facet.clearAutogeneratedFiles(AndroidAutogeneratorMode.AAPT);
for (String relPath : item.myGenFileRelPath2package.keySet()) {
final VirtualFile genFile = LocalFileSystem.getInstance().findFileByPath(item.myOutputDirOsPath + '/' + relPath);
if (genFile != null && genFile.exists()) {
facet.markFileAutogenerated(AndroidAutogeneratorMode.AAPT, genFile);
}
}
}
catch (final IOException e) {
LOG.info(e);
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (module.getProject().isDisposed()) return;
context.addMessage(CompilerMessageCategory.ERROR, "I/O error: " + e.getMessage(), null, -1, -1);
}
});
}
finally {
if (tempOutDir != null) {
FileUtil.delete(tempOutDir);
}
}
}
private static void generateStubClasses(@NotNull String aPackage, @NotNull File outputDir, @NotNull String... classNames)
throws IOException {
assert aPackage.length() > 0;
for (String className : classNames) {
generateStubClass(aPackage, outputDir, className, "");
}
}
private static void generateStubClass(String aPackage, File outputDir, String className, String content) throws IOException {
final File packageDir = new File(outputDir.getPath() + '/' + aPackage.replace('.', '/'));
if (!packageDir.exists() && !packageDir.mkdirs()) {
throw new IOException("Cannot create directory " + FileUtil.toSystemDependentName(packageDir.getPath()));
}
final BufferedWriter writer = new BufferedWriter(new FileWriter(new File(packageDir, className + ".java")));
try {
writer.write(
AndroidCommonUtils.AUTOGENERATED_JAVA_FILE_HEADER +
"\n\npackage " + aPackage + ";\n\n" +
"/* This stub is only used by the IDE. It is NOT the " + className + " class actually packed into the APK */\n" +
"public final class " + className + " {\n" +
content +
"}"
);
}
finally {
writer.close();
}
}
private static void patchAndMarkGeneratedFile(@NotNull AndroidFacet facet,
@NotNull AndroidAutogeneratorMode mode,
@NotNull VirtualFile vFile) throws IOException {
final File file = new File(vFile.getPath());
final String fileText = FileUtil.loadFile(file);
FileUtil.writeToFile(file, AndroidCommonUtils.AUTOGENERATED_JAVA_FILE_HEADER + "\n\n" + fileText);
facet.markFileAutogenerated(mode, vFile);
}
private static void removeAllFilesWithSameName(@NotNull final Module module, @NotNull final File file, @NotNull String directoryPath) {
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file);
final VirtualFile genDir = LocalFileSystem.getInstance().findFileByPath(directoryPath);
if (vFile == null || genDir == null) {
return;
}
final Collection<VirtualFile> files = DumbService.getInstance(module.getProject()).runReadActionInSmartMode(new Computable<Collection<VirtualFile>>() {
@Nullable
@Override
public Collection<VirtualFile> compute() {
if (module.isDisposed() || module.getProject().isDisposed()) {
return null;
}
return FilenameIndex.getVirtualFilesByName(module.getProject(), file.getName(), module.getModuleScope(false));
}
});
if (files == null) {
return;
}
final List<VirtualFile> filesToDelete = new ArrayList<VirtualFile>();
for (final VirtualFile f : files) {
if (!Comparing.equal(f, vFile) && VfsUtilCore.isAncestor(genDir, f, true)) {
filesToDelete.add(f);
}
}
if (filesToDelete.size() == 0) {
return;
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
for (VirtualFile f : filesToDelete) {
if (f.isValid() && f.exists()) {
try {
f.delete(module.getProject());
}
catch (IOException e) {
LOG.debug(e);
}
}
}
}
});
}
});
}
private static void runAidl(@NotNull final AndroidFacet facet, @NotNull final CompileContext context) {
final Module module = facet.getModule();
final ModuleCompileScope moduleCompileScope = new ModuleCompileScope(module, false);
final VirtualFile[] files = moduleCompileScope.getFiles(AidlFileType.INSTANCE, true);
final List<IdlAutogenerationItem> items = new ArrayList<IdlAutogenerationItem>();
for (final VirtualFile file : files) {
final IdlAutogenerationItem item = ApplicationManager.getApplication().runReadAction(new Computable<IdlAutogenerationItem>() {
@Nullable
@Override
public IdlAutogenerationItem compute() {
if (module.isDisposed() || module.getProject().isDisposed()) {
return null;
}
final IAndroidTarget target = facet.getConfiguration().getAndroidTarget();
if (target == null) {
context.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.specify.platform", module.getName()), null, -1, -1);
return null;
}
final String packageName = AndroidUtils.computePackageName(module, file);
if (packageName == null) {
context.addMessage(CompilerMessageCategory.ERROR, "Cannot compute package for file", file.getUrl(), -1, -1);
return null;
}
final String sourceRootPath = AndroidRootUtil.getAidlGenSourceRootPath(facet);
if (sourceRootPath == null) {
context.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.apt.gen.not.specified", module.getName()), null, -1, -1);
return null;
}
final VirtualFile[] sourceRoots = getSourceRootsForModuleAndDependencies(module, false);
final String[] sourceRootOsPaths = AndroidCompileUtil.toOsPaths(sourceRoots);
final String outFileOsPath = FileUtil.toSystemDependentName(
sourceRootPath + '/' + packageName.replace('.', '/') + '/' + file.getNameWithoutExtension() + ".java");
return new IdlAutogenerationItem(file, target, outFileOsPath, sourceRootOsPaths, sourceRootPath, packageName);
}
});
if (item != null) {
items.add(item);
}
}
final Set<VirtualFile> filesToCheck = new HashSet<VirtualFile>();
for (IdlAutogenerationItem item : items) {
if (new File(FileUtil.toSystemDependentName(item.myFile.getPath())).exists()) {
filesToCheck.add(item.myFile);
}
}
if (!ensureFilesWritable(module.getProject(), filesToCheck)) {
return;
}
facet.clearAutogeneratedFiles(AndroidAutogeneratorMode.AIDL);
for (IdlAutogenerationItem item : items) {
final VirtualFile file = item.myFile;
final String fileOsPath = FileUtil.toSystemDependentName(file.getPath());
try {
final Map<CompilerMessageCategory, List<String>> messages = AndroidCompileUtil.toCompilerMessageCategoryKeys(
AndroidIdl.execute(item.myTarget, fileOsPath, item.myOutFileOsPath, item.mySourceRootOsPaths));
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (module.getProject().isDisposed()) return;
for (CompilerMessageCategory category : messages.keySet()) {
List<String> messageList = messages.get(category);
for (String message : messageList) {
context.addMessage(category, message, file.getUrl(), -1, -1);
}
}
}
});
removeDuplicateClasses(module, item.myPackage, new File(item.myOutFileOsPath), item.myOutDirOsPath);
final VirtualFile genDir = LocalFileSystem.getInstance().findFileByPath(item.myOutDirOsPath);
if (genDir != null) {
genDir.refresh(false, true);
}
final VirtualFile outFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(item.myOutFileOsPath);
if (outFile != null && outFile.exists()) {
patchAndMarkGeneratedFile(facet, AndroidAutogeneratorMode.AIDL, outFile);
}
}
catch (final IOException e) {
LOG.info(e);
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (module.getProject().isDisposed()) return;
context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), file.getUrl(), -1, -1);
}
});
}
}
}
private static void runRenderscript(@NotNull final AndroidFacet facet, @NotNull final CompileContext context) {
final Module module = facet.getModule();
final ModuleCompileScope moduleCompileScope = new ModuleCompileScope(module, false);
final VirtualFile[] files = moduleCompileScope.getFiles(AndroidRenderscriptFileType.INSTANCE, true);
facet.clearAutogeneratedFiles(AndroidAutogeneratorMode.RENDERSCRIPT);
for (final VirtualFile file : files) {
final RenderscriptAutogenerationItem item =
ApplicationManager.getApplication().runReadAction(new Computable<RenderscriptAutogenerationItem>() {
@Nullable
@Override
public RenderscriptAutogenerationItem compute() {
final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform();
if (platform == null) {
context.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.specify.platform", module.getName()), null, -1, -1);
return null;
}
final IAndroidTarget target = platform.getTarget();
final String sdkLocation = platform.getSdkData().getPath();
final String packageName = AndroidUtils.computePackageName(module, file);
if (packageName == null) {
context.addMessage(CompilerMessageCategory.ERROR, "Cannot compute package for file", file.getUrl(), -1, -1);
return null;
}
final String resourceDirPath = AndroidRootUtil.getResourceDirPath(facet);
assert resourceDirPath != null;
final String sourceRootPath = AndroidRootUtil.getRenderscriptGenSourceRootPath(facet);
if (sourceRootPath == null) {
return null;
}
final String rawDirPath = resourceDirPath + '/' + SdkConstants.FD_RES_RAW;
return new RenderscriptAutogenerationItem(sdkLocation, target, sourceRootPath, rawDirPath);
}
});
if (item == null) {
continue;
}
File tempOutDir = null;
try {
tempOutDir = FileUtil.createTempDirectory("android_renderscript_autogeneration", "tmp");
final VirtualFile vTempOutDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempOutDir);
final String depFolderPath =
vTempOutDir != null ? getDependencyFolder(context.getProject(), file, vTempOutDir) : null;
final Map<CompilerMessageCategory, List<String>> messages = AndroidCompileUtil.toCompilerMessageCategoryKeys(
AndroidRenderscript
.execute(item.mySdkLocation, item.myTarget, file.getPath(), tempOutDir.getPath(), depFolderPath,
item.myRawDirPath));
if (messages.get(CompilerMessageCategory.ERROR).size() == 0) {
final List<File> newFiles = new ArrayList<File>();
AndroidCommonUtils.moveAllFiles(tempOutDir, new File(item.myGenDirPath), newFiles);
for (File newFile : newFiles) {
final VirtualFile newVFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(newFile);
if (newVFile != null) {
patchAndMarkGeneratedFile(facet, AndroidAutogeneratorMode.RENDERSCRIPT, newVFile);
}
}
final File bcFile = new File(item.myRawDirPath, FileUtil.getNameWithoutExtension(file.getName()) + ".bc");
final VirtualFile vBcFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(bcFile);
if (vBcFile != null) {
facet.markFileAutogenerated(AndroidAutogeneratorMode.RENDERSCRIPT, vBcFile);
}
}
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (module.getProject().isDisposed()) {
return;
}
for (final CompilerMessageCategory category : messages.keySet()) {
final List<String> messageList = messages.get(category);
for (final String message : messageList) {
context.addMessage(category, message, file.getUrl(), -1, -1);
}
}
}
});
final VirtualFile genDir = LocalFileSystem.getInstance().findFileByPath(item.myGenDirPath);
if (genDir != null) {
genDir.refresh(false, true);
}
}
catch (final IOException e) {
LOG.info(e);
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (module.getProject().isDisposed()) return;
context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), file.getUrl(), -1, -1);
}
});
}
finally {
if (tempOutDir != null) {
FileUtil.delete(tempOutDir);
}
}
}
}
private static boolean ensureFilesWritable(@NotNull final Project project, @NotNull final Collection<VirtualFile> filesToCheck) {
if (filesToCheck.size() == 0) {
return true;
}
final boolean[] run = {false};
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
@Override
public void run() {
run[0] = !project.isDisposed() &&
ReadonlyStatusHandler.ensureFilesWritable(project, filesToCheck.toArray(new VirtualFile[filesToCheck.size()]));
}
}, ModalityState.defaultModalityState());
return run[0];
}
private static void removeDuplicateClasses(@NotNull final Module module,
@NotNull final String aPackage,
@NotNull final File generatedFile,
@NotNull final String sourceRootPath) {
if (generatedFile.exists()) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (module.getProject().isDisposed() || module.isDisposed()) {
return;
}
String className = FileUtil.getNameWithoutExtension(generatedFile);
AndroidCompileUtil.removeDuplicatingClasses(module, aPackage, className, generatedFile, sourceRootPath);
}
});
}
}
private static void fillSourceRoots(@NotNull Module module,
@NotNull Set<Module> visited,
@NotNull Set<VirtualFile> result,
boolean includingTests) {
visited.add(module);
final AndroidFacet facet = AndroidFacet.getInstance(module);
VirtualFile resDir = facet != null ? AndroidRootUtil.getResourceDir(facet) : null;
ModuleRootManager manager = ModuleRootManager.getInstance(module);
for (VirtualFile sourceRoot : manager.getSourceRoots(includingTests)) {
if (!Comparing.equal(resDir, sourceRoot)) {
result.add(sourceRoot);
}
}
for (OrderEntry entry : manager.getOrderEntries()) {
if (entry instanceof ModuleOrderEntry) {
ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)entry;
DependencyScope scope = moduleOrderEntry.getScope();
if (scope == DependencyScope.COMPILE) {
Module depModule = moduleOrderEntry.getModule();
if (depModule != null && !visited.contains(depModule)) {
fillSourceRoots(depModule, visited, result, false);
}
}
}
}
}
@NotNull
public static VirtualFile[] getSourceRootsForModuleAndDependencies(@NotNull Module module, boolean includingTests) {
Set<VirtualFile> result = new HashSet<VirtualFile>();
fillSourceRoots(module, new HashSet<Module>(), result, includingTests);
return VfsUtil.toVirtualFileArray(result);
}
@Nullable
static String getDependencyFolder(@NotNull final Project project,
@NotNull final VirtualFile sourceFile,
@NotNull final VirtualFile genFolder) {
final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex();
final VirtualFile sourceRoot = index.getSourceRootForFile(sourceFile);
if (sourceRoot == null) {
return null;
}
final VirtualFile parent = sourceFile.getParent();
if (Comparing.equal(parent, sourceRoot)) {
return genFolder.getPath();
}
final String relativePath = VfsUtilCore.getRelativePath(sourceFile.getParent(), sourceRoot, '/');
assert relativePath != null;
return genFolder.getPath() + '/' + relativePath;
}
private static class AptAutogenerationItem {
final String myPackage;
final String myOutputDirOsPath;
final Map<String, String> myGenFileRelPath2package;
private AptAutogenerationItem(@NotNull String aPackage,
@NotNull String outputDirOsPath,
@NotNull Map<String, String> genFileRelPath2package) {
myPackage = aPackage;
myOutputDirOsPath = outputDirOsPath;
myGenFileRelPath2package = genFileRelPath2package;
}
}
private static class IdlAutogenerationItem {
final VirtualFile myFile;
final IAndroidTarget myTarget;
final String myOutFileOsPath;
final String[] mySourceRootOsPaths;
final String myOutDirOsPath;
final String myPackage;
private IdlAutogenerationItem(@NotNull VirtualFile file,
@NotNull IAndroidTarget target,
@NotNull String outFileOsPath,
@NotNull String[] sourceRootOsPaths,
@NotNull String outDirOsPath,
@NotNull String aPackage) {
myFile = file;
myTarget = target;
myOutFileOsPath = outFileOsPath;
mySourceRootOsPaths = sourceRootOsPaths;
myOutDirOsPath = outDirOsPath;
myPackage = aPackage;
}
}
private static class RenderscriptAutogenerationItem {
final String mySdkLocation;
final IAndroidTarget myTarget;
final String myGenDirPath;
final String myRawDirPath;
private RenderscriptAutogenerationItem(@NotNull String sdkLocation,
@NotNull IAndroidTarget target,
@NotNull String genDirPath,
@NotNull String rawDirPath) {
mySdkLocation = sdkLocation;
myTarget = target;
myGenDirPath = genDirPath;
myRawDirPath = rawDirPath;
}
}
private static class BuildconfigAutogenerationItem {
final String myPackage;
final String mySourceRootOsPath;
private BuildconfigAutogenerationItem(@NotNull String aPackage, @NotNull String sourceRootOsPath) {
myPackage = aPackage;
mySourceRootOsPath = sourceRootOsPath;
}
}
}