android-jps: support circular dependencies between android libs (IDEA-79737), create generated dirs before compilation if necessary, clear them when rebuild

This commit is contained in:
Eugene Kudelevsky
2012-03-13 20:59:54 +04:00
parent 3a6100e495
commit 0f13b66bc3
3 changed files with 113 additions and 33 deletions
@@ -1,6 +1,5 @@
package org.jetbrains.jps.android;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.idea.AdditionalRootsProviderService;
@@ -13,7 +12,6 @@ import java.util.List;
* @author Eugene.Kudelevsky
*/
public class AndroidAdditionalRootProviderService extends AdditionalRootsProviderService {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.android.AndroidAdditionalRootProviderService");
@NotNull
@Override
@@ -21,19 +19,10 @@ public class AndroidAdditionalRootProviderService extends AdditionalRootsProvide
final File generatedSourcesRoot = AndroidJpsUtil.getGeneratedSourcesStorage(module);
final List<String> result = new ArrayList<String>();
result.add(createSubdir(generatedSourcesRoot, AndroidJpsUtil.AAPT_GENERATED_SOURCE_ROOT_NAME));
result.add(createSubdir(generatedSourcesRoot, AndroidJpsUtil.AIDL_GENERATED_SOURCE_ROOT_NAME));
result.add(createSubdir(generatedSourcesRoot, AndroidJpsUtil.RENDERSCRIPT_GENERATED_SOURCE_ROOT_NAME));
result.add(new File(generatedSourcesRoot, AndroidJpsUtil.AAPT_GENERATED_SOURCE_ROOT_NAME).getPath());
result.add(new File(generatedSourcesRoot, AndroidJpsUtil.AIDL_GENERATED_SOURCE_ROOT_NAME).getPath());
result.add(new File(generatedSourcesRoot, AndroidJpsUtil.RENDERSCRIPT_GENERATED_SOURCE_ROOT_NAME).getPath());
return result;
}
@NotNull
private static String createSubdir(@NotNull File dir, @NotNull String name) {
final File aaptSourceRoot = new File(dir, name);
if (!aaptSourceRoot.exists() && !aaptSourceRoot.mkdirs()) {
LOG.info("Cannot create folder " + aaptSourceRoot.getPath());
}
return aaptSourceRoot.getPath();
}
}
@@ -63,7 +63,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
if (context.isCompilingTests() || !AndroidJpsUtil.containsAndroidFacet(chunk)) {
return ModuleLevelBuilder.ExitCode.OK;
}
try {
return doBuild(context, chunk);
}
@@ -104,6 +104,26 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
}
boolean success = true;
if (context.isProjectRebuild()) {
for (Module module : moduleDataMap.keySet()) {
final File generatedSourcesStorage = AndroidJpsUtil.getGeneratedSourcesStorage(module);
if (generatedSourcesStorage.exists() &&
!deleteAndMarkRecursively(generatedSourcesStorage, context)) {
success = false;
}
final File generatedResourcesStorage = AndroidJpsUtil.getGeneratedResourcesStorage(module);
if (generatedResourcesStorage.exists() &&
!deleteAndMarkRecursively(generatedResourcesStorage, context)) {
success = false;
}
}
}
if (!success) {
return ExitCode.ABORT;
}
if (!runAidlCompiler(context, idlFilesToCompile, moduleDataMap)) {
success = false;
}
@@ -148,13 +168,21 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
}
final File generatedSourcesDir = AndroidJpsUtil.getGeneratedSourcesStorage(module);
final File aidlOutputDirectory = new File(generatedSourcesDir, AndroidJpsUtil.AIDL_GENERATED_SOURCE_ROOT_NAME);
if (!aidlOutputDirectory.exists() && !aidlOutputDirectory.mkdirs()) {
context.processMessage(
new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot create directory " + aidlOutputDirectory.getPath()));
success = false;
continue;
}
final IAndroidTarget target = moduleData.getAndroidTarget();
try {
final File[] sourceRoots = AndroidJpsUtil.getSourceRootsForModuleAndDependencies(module);
final String[] sourceRootPaths = AndroidJpsUtil.toPaths(sourceRoots);
final String packageName = computePackageForFile(context, file);
if (packageName == null) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
AndroidJpsBundle.message("android.jps.errors.cannot.compute.package", filePath)));
@@ -210,7 +238,21 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
final File generatedSourcesDir = AndroidJpsUtil.getGeneratedSourcesStorage(module);
final File rsOutputDirectory = new File(generatedSourcesDir, AndroidJpsUtil.RENDERSCRIPT_GENERATED_SOURCE_ROOT_NAME);
if (!rsOutputDirectory.exists() && !rsOutputDirectory.mkdirs()) {
context.processMessage(
new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot create directory " + rsOutputDirectory.getPath()));
success = false;
continue;
}
final File generatedResourcesDir = AndroidJpsUtil.getGeneratedResourcesStorage(module);
final File rawDir = new File(generatedResourcesDir, "raw");
if (!rawDir.exists() && !rawDir.mkdirs()) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot create directory " + rawDir.getPath()));
success = false;
continue;
}
final IAndroidTarget target = moduleData.getAndroidTarget();
final String sdkLocation = moduleData.getSdkLocation();
@@ -221,7 +263,6 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
try {
tmpOutputDirectory = FileUtil.createTempDirectory("generated-rs-temp", null);
final String depFolderPath = getDependencyFolder(context, file, tmpOutputDirectory);
final File rawDir = new File(generatedResourcesDir, "raw");
final Map<AndroidCompilerMessageKind, List<String>> messages =
AndroidRenderscript.execute(sdkLocation, target, filePath, tmpOutputDirectory.getPath(), depFolderPath, rawDir.getPath());
@@ -299,7 +340,15 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
continue;
}
final Set<String> depLibPackagesSet = getDepLibPackages(module);
final Map<Module, String> depLibPackageMap = getDepLibPackages(module);
final Map<Module, String> packageMap = new HashMap<Module, String>(depLibPackageMap);
packageMap.put(module, packageName);
if (hasBadCircularDependencies(facet, packageMap)) {
continue;
}
final Set<String> depLibPackagesSet = new HashSet<String>(depLibPackageMap.values());
depLibPackagesSet.remove(packageName);
final Set<ResourceEntry> resources = collectResources(resPaths);
@@ -315,14 +364,17 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
final File generatedSourcesDir = AndroidJpsUtil.getGeneratedSourcesStorage(module);
final File aptOutputDirectory = new File(generatedSourcesDir, AndroidJpsUtil.AAPT_GENERATED_SOURCE_ROOT_NAME);
if (aptOutputDirectory.exists()) {
// clear directory, because it may contain obsolete files (ex. if package name was changed)
final List<File> filesToDelete = collectJavaFilesRecursively(aptOutputDirectory);
FileUtil.delete(aptOutputDirectory);
// clear directory, because it may contain obsolete files (ex. if package name was changed)
if (!deleteAndMarkRecursively(aptOutputDirectory, context)) {
success = false;
continue;
}
for (File file : filesToDelete) {
context.markDeleted(file);
}
if (!aptOutputDirectory.mkdirs()) {
context.processMessage(
new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot create directory " + aptOutputDirectory.getPath()));
success = false;
continue;
}
context.processMessage(new ProgressMessage(AndroidJpsBundle.message("android.jps.progress.aapt", module.getName())));
@@ -350,6 +402,21 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
return success;
}
private static boolean deleteAndMarkRecursively(@NotNull File dir, @NotNull CompileContext context) throws IOException {
if (dir.exists()) {
final List<File> filesToDelete = collectJavaFilesRecursively(dir);
if (!FileUtil.delete(dir)) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot delete " + dir.getPath()));
return false;
}
for (File file : filesToDelete) {
context.markDeleted(file);
}
}
return true;
}
private static boolean markDirtyRecursively(@NotNull File dir, @NotNull final CompileContext context) {
final Ref<Boolean> success = Ref.create(true);
@@ -389,8 +456,8 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
}
@NotNull
private static Set<String> getDepLibPackages(@NotNull Module module) throws IOException {
final Set<String> result = new HashSet<String>();
private static Map<Module, String> getDepLibPackages(@NotNull Module module) throws IOException {
final Map<Module, String> result = new HashMap<Module, String>();
for (AndroidFacet depFacet : AndroidJpsUtil.getAllDependentAndroidLibraries(module)) {
final File depManifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(depFacet);
@@ -399,7 +466,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
final String packageName = parsePackageNameFromManifestFile(depManifestFile);
if (packageName != null) {
result.add(packageName);
result.put(depFacet.getModule(), packageName);
}
}
}
@@ -562,7 +629,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
assert relativePath != null;
return genFolder.getPath() + '/' + relativePath;
}
@Nullable
private static Map<Module, MyModuleData> computeModuleDatas(@NotNull Collection<Module> modules, @NotNull CompileContext context) {
final Map<Module, MyModuleData> moduleDataMap = new HashMap<Module, MyModuleData>();
@@ -575,7 +642,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
continue;
}
final Pair<AndroidSdk,IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
final Pair<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
if (pair == null) {
success = false;
continue;
@@ -600,7 +667,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
if (relPath == null) {
return null;
}
return FileUtil.toSystemIndependentName(relPath).replace('/', '.');
}
@@ -609,6 +676,32 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
return "Android Source Generating Builder";
}
// see IDEA-79737 for details
private static boolean hasBadCircularDependencies(@NotNull AndroidFacet facet, @NotNull Map<Module, String> packages) throws IOException {
final String aPackage = packages.get(facet.getModule());
if (aPackage == null || aPackage.length() == 0) {
return false;
}
final List<AndroidFacet> dependencies = AndroidJpsUtil.getAllDependentAndroidLibraries(facet.getModule());
for (AndroidFacet depFacet : dependencies) {
final String depPackage = packages.get(depFacet.getModule());
if (!aPackage.equals(depPackage)) {
continue;
}
final List<AndroidFacet> depDependencies = AndroidJpsUtil.getAllDependentAndroidLibraries(depFacet.getModule());
if (depDependencies.contains(facet) &&
dependencies.contains(depFacet) &&
depFacet.getModule().getName().compareTo(facet.getModule().getName()) < 0) {
return true;
}
}
return false;
}
private static void addMessages(@NotNull CompileContext context,
@NotNull Map<AndroidCompilerMessageKind, List<String>> messages,
@NotNull String sourcePath,
@@ -260,8 +260,6 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler {
return items.toArray(new GenerationItem[items.size()]);
}
// todo: support this in android-jps
// see IDEA-79737 for details
private static boolean hasBadCircularDependencies(@NotNull AndroidFacet facet) {
final List<AndroidFacet> dependencies = AndroidUtils.getAllAndroidDependencies(facet.getModule(), true);