diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties
index a16ee9b54454..f47bb5e9c89a 100644
--- a/plugins/android/resources/messages/AndroidBundle.properties
+++ b/plugins/android/resources/messages/AndroidBundle.properties
@@ -293,4 +293,5 @@ android.logcat.edit.logcat.filter.button=Edit selected logcat filter
android.logcat.remove.logcat.filter.button=Remove selected logcat filter
android.logcat.add.logcat.filter.button=Add a new logcat filter
android.logcat.new.logcat.dialog.label=Filter logcat messages by different parameters.
Empty fields will match all messages.
-android.logcat.new.filter.dialog.name.busy.error=Filter {0} already exists
\ No newline at end of file
+android.logcat.new.filter.dialog.name.busy.error=Filter {0} already exists
+android.compile.messages.processing.external.apklib.dependencies=Processing external apklib dependencies
\ No newline at end of file
diff --git a/plugins/android/src/META-INF/maven-support.xml b/plugins/android/src/META-INF/maven-support.xml
index f46d7735745c..fbf44ffad17a 100644
--- a/plugins/android/src/META-INF/maven-support.xml
+++ b/plugins/android/src/META-INF/maven-support.xml
@@ -5,6 +5,7 @@
+
diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml
index 2e4fb9f08796..cc004a844ce9 100644
--- a/plugins/android/src/META-INF/plugin.xml
+++ b/plugins/android/src/META-INF/plugin.xml
@@ -163,6 +163,8 @@
serviceImplementation="org.jetbrains.android.uipreview.AndroidLayoutPreviewToolWindowSettings"/>
+
diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java
index ecc453d26461..41b4dcb3bf9a 100644
--- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java
+++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java
@@ -369,11 +369,11 @@ public class AndroidCompileUtil {
}
@Nullable
- private static ContentEntry findContentEntryForRoot(@NotNull ModifiableRootModel model, @NotNull VirtualFile root) {
+ 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 && VfsUtil.isAncestor(contentRoot, root, false)) {
+ if (contentRoot != null && VfsUtilCore.isAncestor(contentRoot, root, false)) {
contentEntry = candidate;
}
}
diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidExternalApklibExtractingCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidExternalApklibExtractingCompiler.java
new file mode 100644
index 000000000000..f6559841db51
--- /dev/null
+++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidExternalApklibExtractingCompiler.java
@@ -0,0 +1,246 @@
+package org.jetbrains.android.compiler;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.HashSet;
+import com.intellij.util.io.ZipUtil;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.android.maven.AndroidExternalApklibDependenciesManager;
+import org.jetbrains.android.maven.AndroidMavenUtil;
+import org.jetbrains.android.util.AndroidBundle;
+import org.jetbrains.android.util.AndroidUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.DataInput;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class AndroidExternalApklibExtractingCompiler implements SourceGeneratingCompiler {
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidExternalApklibExtractingCompiler");
+
+ @Nullable
+ @Override
+ public VirtualFile getPresentableFile(CompileContext context, Module module, VirtualFile outputRoot, VirtualFile generatedFile) {
+ return null;
+ }
+
+ @Override
+ public GenerationItem[] getGenerationItems(final CompileContext context) {
+ return ApplicationManager.getApplication().runReadAction(new Computable() {
+ @Override
+ public GenerationItem[] compute() {
+ final List result = new ArrayList();
+
+ for (Module module : ModuleManager.getInstance(context.getProject()).getModules()) {
+ final AndroidFacet facet = AndroidFacet.getInstance(module);
+ if (facet == null || !facet.getConfiguration().LIBRARY_PROJECT) {
+ continue;
+ }
+
+ final String mavenIdStr = AndroidMavenUtil.getMavenIdStringByExtApklibModule(facet.getModule());
+ if (mavenIdStr == null) {
+ continue;
+ }
+
+ final AndroidExternalApklibDependenciesManager manager =
+ AndroidExternalApklibDependenciesManager.getInstance(context.getProject());
+ final String artifactFilePath = manager.getArtifactFilePath(mavenIdStr);
+
+ if (artifactFilePath == null) {
+ context.addMessage(CompilerMessageCategory.ERROR,
+ "Cannot find artifact file for generated module " + module.getName() + ". Try to force reimport Maven model",
+ null, -1, -1);
+ continue;
+ }
+
+ final VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots();
+ if (roots.length == 0) {
+ context.addMessage(CompilerMessageCategory.ERROR, "Cannot find any content root in generated module " +
+ module.getName() +
+ ". Try to force reimport Maven model", null, -1, -1);
+ continue;
+ }
+
+ final VirtualFile root = roots[0];
+
+ result.add(new MyGenerationItem(module, FileUtil.toSystemDependentName(root.getPath()),
+ FileUtil.toSystemDependentName(artifactFilePath)));
+ }
+
+ return result.toArray(new GenerationItem[result.size()]);
+ }
+ });
+ }
+
+ @Override
+ public GenerationItem[] generate(CompileContext context,
+ GenerationItem[] items,
+ VirtualFile outputRootDirectory) {
+ if (items == null || items.length == 0) {
+ return new GenerationItem[0];
+ }
+
+ context.getProgressIndicator().setText(AndroidBundle.message("android.compile.messages.processing.external.apklib.dependencies"));
+
+ final GenerationItem[] generatedItems = doGenerate(context, items);
+ final Set generatedVFiles = new HashSet();
+ final HashSet visited = new HashSet();
+
+ for (GenerationItem item : generatedItems) {
+ final MyGenerationItem genItem = (MyGenerationItem)item;
+
+ final File srcRoot = new File(genItem.getGenContentRootPath() + '/' +
+ AndroidMavenUtil.APK_LIB_ARTIFACT_SOURCE_ROOT);
+
+ final VirtualFile vSrcRoot = LocalFileSystem.getInstance().findFileByIoFile(srcRoot);
+ if (vSrcRoot != null) {
+ vSrcRoot.getParent().refresh(false, true);
+ AndroidUtils.collectFiles(vSrcRoot, visited, generatedVFiles);
+ }
+ }
+
+ if (context instanceof CompileContextEx) {
+ ((CompileContextEx)context).markGenerated(generatedVFiles);
+ }
+
+ return generatedItems;
+ }
+
+ private static GenerationItem[] doGenerate(CompileContext context, GenerationItem[] items) {
+ final List result = new ArrayList();
+
+ for (GenerationItem item : items) {
+ final Module module = item.getModule();
+
+ if (!AndroidCompileUtil.isModuleAffected(context, module)) {
+ continue;
+ }
+
+ final MyGenerationItem genItem = (MyGenerationItem)item;
+
+ final String artifactPath = genItem.getArtifactPath();
+ final File artifactFile = new File(artifactPath);
+
+ if (!artifactFile.exists()) {
+ context.addMessage(CompilerMessageCategory.ERROR, "Cannot find file " + artifactPath, null, -1, -1);
+ continue;
+ }
+
+ if (!artifactFile.isFile()) {
+ context.addMessage(CompilerMessageCategory.ERROR, artifactPath + " is not file", null, -1, -1);
+ continue;
+ }
+
+ final String genContentRootPath = ((MyGenerationItem)item).getGenContentRootPath();
+ final File genContentRootFile = new File(genContentRootPath);
+
+ if (genContentRootFile.exists()) {
+ if (!FileUtil.delete(genContentRootFile)) {
+ context.addMessage(CompilerMessageCategory.ERROR, "Cannot delete old directory: " + genContentRootPath, null, -1, -1);
+ continue;
+ }
+ }
+
+ if (!genContentRootFile.mkdir()) {
+ context.addMessage(CompilerMessageCategory.ERROR, "Cannot create directory " + genContentRootPath, null, -1, -1);
+ continue;
+ }
+
+ try {
+ ZipUtil.extract(artifactFile, genContentRootFile, null);
+ }
+ catch (IOException e) {
+ final String message = e.getMessage();
+ if (message == null) {
+ context.addMessage(CompilerMessageCategory.ERROR, "Unknown I/O error", null, -1, -1);
+ LOG.error(e);
+ }
+ else {
+ context.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
+ LOG.info(e);
+ }
+ continue;
+ }
+
+ result.add(genItem);
+ }
+
+ return result.toArray(new GenerationItem[result.size()]);
+ }
+
+ @NotNull
+ @Override
+ public String getDescription() {
+ return "Android External Apklib Extracting Compiler";
+ }
+
+ @Override
+ public boolean validateConfiguration(CompileScope scope) {
+ return true;
+ }
+
+ @Override
+ public ValidityState createValidityState(DataInput in) throws IOException {
+ return TimestampValidityState.load(in);
+ }
+
+ private static class MyGenerationItem implements GenerationItem {
+ private final Module myModule;
+ private final String myGenContentRootPath;
+ private final String myArtifactPath;
+
+ private MyGenerationItem(@NotNull Module module, @NotNull String genContentRootPath, @NotNull String artifactPath) {
+ myModule = module;
+ myGenContentRootPath = genContentRootPath;
+ myArtifactPath = artifactPath;
+ }
+
+ @Nullable
+ @Override
+ public String getPath() {
+ return myGenContentRootPath;
+ }
+
+ @Override
+ public ValidityState getValidityState() {
+ return new TimestampValidityState(new File(myArtifactPath).lastModified());
+ }
+
+ @Override
+ @NotNull
+ public Module getModule() {
+ return myModule;
+ }
+
+ @Override
+ public boolean isTestSource() {
+ return false;
+ }
+
+ @NotNull
+ public String getGenContentRootPath() {
+ return myGenContentRootPath;
+ }
+
+ @NotNull
+ public String getArtifactPath() {
+ return myArtifactPath;
+ }
+ }
+}
diff --git a/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApt.java b/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApt.java
index fa560796acb3..8f6d9a6522e3 100644
--- a/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApt.java
+++ b/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApt.java
@@ -56,6 +56,13 @@ public final class AndroidApt {
@NotNull String[] resourceDirsOsPaths,
@NotNull String[] libPackages,
boolean isLibrary) throws IOException {
+ final File outOsDir = new File(outDirOsPath);
+ if (!outOsDir.exists()) {
+ if (!outOsDir.mkdirs()) {
+ LOG.error("Unable to create directory " + outDirOsPath);
+ }
+ }
+
final String packageFolderOsPath = FileUtil.toSystemDependentName(outDirOsPath + '/' + aPackage.replace('.', '/'));
/* We actually need to delete the manifest.java as it may become empty and
diff --git a/plugins/android/src/org/jetbrains/android/maven/AndroidExternalApklibDependenciesManager.java b/plugins/android/src/org/jetbrains/android/maven/AndroidExternalApklibDependenciesManager.java
new file mode 100644
index 000000000000..0767bfa8116c
--- /dev/null
+++ b/plugins/android/src/org/jetbrains/android/maven/AndroidExternalApklibDependenciesManager.java
@@ -0,0 +1,194 @@
+package org.jetbrains.android.maven;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.containers.HashMap;
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.MapAnnotation;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.maven.model.MavenId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+@State(
+ name = "AndroidExternalApklibDependenciesManager",
+ storages = {
+ @Storage(file = "$WORKSPACE_FILE$")
+ }
+)
+public class AndroidExternalApklibDependenciesManager implements PersistentStateComponent {
+ private State myState = new State();
+
+ public static AndroidExternalApklibDependenciesManager getInstance(@NotNull Project project) {
+ return ServiceManager.getService(project, AndroidExternalApklibDependenciesManager.class);
+ }
+
+ @Override
+ public AndroidExternalApklibDependenciesManager.State getState() {
+ return myState;
+ }
+
+ @Override
+ public void loadState(AndroidExternalApklibDependenciesManager.State state) {
+ myState = state;
+ }
+
+ @Nullable
+ public MyResolvedInfo getResolvedInfoForArtifact(@NotNull MavenId mavenId) {
+ final String key = AndroidMavenUtil.getMavenIdStringForFileName(mavenId);
+ return myState.getResolvedInfoMap().get(key);
+ }
+
+ @Nullable
+ public String getArtifactFilePath(@NotNull String mavenIdStr) {
+ return myState.getArtifactFilesMap().get(mavenIdStr);
+ }
+
+ public void setSdkInfoForArtifact(@NotNull MavenId mavenId, @NotNull MyResolvedInfo info) {
+ final String key = AndroidMavenUtil.getMavenIdStringForFileName(mavenId);
+ myState.getResolvedInfoMap().put(key, info);
+ }
+
+ public void setArtifactFilePath(@NotNull MavenId mavenId, @NotNull String path) {
+ final String key = AndroidMavenUtil.getMavenIdStringForFileName(mavenId);
+ myState.getArtifactFilesMap().put(key, path);
+ }
+
+ public static class State {
+ private Map myArtifactFilesMap = new HashMap();
+ private Map myResolvedInfoMap = new HashMap();
+
+ @Tag("sdk-infos")
+ @MapAnnotation(surroundWithTag = false)
+ public Map getResolvedInfoMap() {
+ return myResolvedInfoMap;
+ }
+
+ @Tag("artifacts")
+ @MapAnnotation(surroundWithTag = false)
+ public Map getArtifactFilesMap() {
+ return myArtifactFilesMap;
+ }
+
+ public void setArtifactFilesMap(Map artifactFilesMap) {
+ myArtifactFilesMap = artifactFilesMap;
+ }
+
+ public void setResolvedInfoMap(Map artifactId2SdkData) {
+ myResolvedInfoMap = artifactId2SdkData;
+ }
+ }
+
+ @Tag("resolved-info")
+ public static class MyResolvedInfo {
+ private String mySdkPath;
+ private String myApiLevel;
+ private List myApklibDependencies = new ArrayList();
+
+ public MyResolvedInfo(String sdkPath, String apiLevel, Collection dependencyInfos) {
+ mySdkPath = sdkPath;
+ myApiLevel = apiLevel;
+
+ myApklibDependencies = new ArrayList(dependencyInfos);
+ }
+
+ public MyResolvedInfo() {
+ }
+
+ public String getSdkPath() {
+ return mySdkPath;
+ }
+
+ public String getApiLevel() {
+ return myApiLevel;
+ }
+
+ @Tag("dependencies")
+ @AbstractCollection(surroundWithTag = false)
+ public List getApklibDependencies() {
+ return myApklibDependencies;
+ }
+
+ public void setApklibDependencies(List apklibDependencies) {
+ myApklibDependencies = apklibDependencies;
+ }
+
+ public void setSdkPath(String sdkPath) {
+ mySdkPath = sdkPath;
+ }
+
+ public void setApiLevel(String apiLevel) {
+ myApiLevel = apiLevel;
+ }
+ }
+
+ public static class MavenDependencyInfo {
+ private String myGroupId;
+ private String myArtifactId;
+ private String myVersion;
+ private String myType;
+ private String myLibraryName;
+
+ public MavenDependencyInfo() {
+ }
+
+ public MavenDependencyInfo(@NotNull MavenId mavenId, @NotNull String type, @NotNull String libraryName) {
+ myGroupId = mavenId.getGroupId();
+ myArtifactId = mavenId.getArtifactId();
+ myVersion = mavenId.getVersion();
+ myType = type;
+ myLibraryName = libraryName;
+ }
+
+ public String getGroupId() {
+ return myGroupId;
+ }
+
+ public String getArtifactId() {
+ return myArtifactId;
+ }
+
+ public String getVersion() {
+ return myVersion;
+ }
+
+ public String getType() {
+ return myType;
+ }
+
+ public String getLibraryName() {
+ return myLibraryName;
+ }
+
+ public void setGroupId(String groupId) {
+ myGroupId = groupId;
+ }
+
+ public void setArtifactId(String artifactId) {
+ myArtifactId = artifactId;
+ }
+
+ public void setVersion(String version) {
+ myVersion = version;
+ }
+
+ public void setType(String type) {
+ myType = type;
+ }
+
+ public void setLibraryName(String libraryName) {
+ myLibraryName = libraryName;
+ }
+ }
+}
diff --git a/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporterBase.java b/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporterBase.java
index 6c23177ed8f8..260e565e4e2c 100644
--- a/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporterBase.java
+++ b/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporterBase.java
@@ -17,14 +17,29 @@ package org.jetbrains.android.maven;
import com.android.sdklib.IAndroidTarget;
import com.intellij.facet.FacetType;
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.openapi.application.ApplicationManager;
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.StdModuleTypes;
+import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.HashSet;
+import com.intellij.util.io.ZipUtil;
import org.jdom.Element;
+import org.jetbrains.android.compiler.AndroidCompileUtil;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidFacetConfiguration;
import org.jetbrains.android.facet.AndroidFacetType;
@@ -36,9 +51,16 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.importing.FacetImporter;
import org.jetbrains.idea.maven.importing.MavenModifiableModelsProvider;
import org.jetbrains.idea.maven.importing.MavenRootModelAdapter;
+import org.jetbrains.idea.maven.model.MavenArtifact;
+import org.jetbrains.idea.maven.model.MavenId;
import org.jetbrains.idea.maven.project.*;
+import org.jetbrains.idea.maven.server.MavenEmbedderWrapper;
+import org.jetbrains.idea.maven.server.NativeMavenProjectHolder;
+import org.jetbrains.idea.maven.utils.MavenProcessCanceledException;
+import org.jetbrains.idea.maven.utils.MavenProgressIndicator;
import java.io.File;
+import java.io.IOException;
import java.util.*;
@@ -47,6 +69,9 @@ import java.util.*;
*/
public abstract class AndroidFacetImporterBase extends FacetImporter {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.maven.AndroidFacetImporterBase");
+
+ private static final Key MODULE_IMPORTED = Key.create("ANDROID_NEWLY_CREATED_KEY");
+ private static final Key NEED_TO_DELETE_OBSOLETE_MODULES_KEY = Key.create("NEED_TO_DELETE_OBSOLETE_MODULES_KEY");
public AndroidFacetImporterBase(@NotNull String pluginId) {
super("com.jayway.maven.plugins.android.generation2", pluginId, FacetType.findInstance(AndroidFacetType.class), "Android");
@@ -66,7 +91,7 @@ public abstract class AndroidFacetImporterBase extends FacetImporter postTasks) {
configurePaths(facet, mavenProject);
configureAndroidPlatform(facet, mavenProject, modelsProvider);
+ importExternalApklibDependencies(module.getProject(), rootModel, modelsProvider, mavenTree, mavenProject, postTasks);
+
+ if (hasApklibDependencies(mavenProject) &&
+ MavenProjectsManager.getInstance(module.getProject()).getImportingSettings().isUseMavenOutput()) {
+ // IDEA's apklibs building model is different from Maven's one, so we cannot use the same
+ rootModel.useModuleOutput(mavenProject.getBuildDirectory() + "/idea-classes",
+ mavenProject.getBuildDirectory() + "/idea-test-classes");
+ }
+
+ postTasks.add(new MyDeleteObsoleteApklibModulesTask(module.getProject(), mavenProject, mavenTree));
+ }
+
+ private static boolean hasApklibDependencies(@NotNull MavenProject mavenProject) {
+ for (MavenArtifact depArtifact : mavenProject.getDependencies()) {
+ if (AndroidMavenUtil.APKLIB_DEPENDENCY_AND_PACKAGING_TYPE.equals(depArtifact.getType())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void importExternalApklibDependencies(Project project,
+ MavenRootModelAdapter rootModelAdapter,
+ MavenModifiableModelsProvider modelsProvider,
+ MavenProjectsTree mavenTree,
+ MavenProject mavenProject,
+ List tasks) {
+ final ModifiableRootModel rootModel = rootModelAdapter.getRootModel();
+ removeExtApklibDependencies(rootModel);
+
+ for (MavenArtifact depArtifact : mavenProject.getDependencies()) {
+ if (AndroidMavenUtil.APKLIB_DEPENDENCY_AND_PACKAGING_TYPE.equals(depArtifact.getType()) &&
+ mavenTree.findProject(depArtifact) == null) {
+
+ final ModifiableModuleModel moduleModel = modelsProvider.getModuleModel();
+ final String apklibModuleName = AndroidMavenUtil.getModuleNameForExtApklibArtifact(depArtifact.getMavenId());
+ Module apklibModule = moduleModel.findModuleByName(apklibModuleName);
+
+ if (apklibModule == null || apklibModule.getUserData(MODULE_IMPORTED) == null) {
+ apklibModule =
+ importExternalApklibArtifact(project, rootModelAdapter, apklibModule, modelsProvider, mavenProject, mavenTree, depArtifact,
+ moduleModel);
+ if (apklibModule != null) {
+ apklibModule.putUserData(MODULE_IMPORTED, Boolean.TRUE);
+ }
+ }
+
+ if (apklibModule != null) {
+ final Module finalGenModule = apklibModule;
+ tasks.add(new MavenProjectsProcessorTask() {
+ @Override
+ public void perform(Project project,
+ MavenEmbeddersManager embeddersManager,
+ MavenConsole console,
+ MavenProgressIndicator indicator)
+ throws MavenProcessCanceledException {
+ finalGenModule.putUserData(MODULE_IMPORTED, null);
+ }
+ });
+ }
+
+ if (ArrayUtil.find(rootModel.getDependencyModuleNames(), apklibModuleName) < 0) {
+ addCompileModuleDependency(modelsProvider, rootModel, apklibModuleName);
+ }
+ }
+ }
+ }
+
+ private static void removeExtApklibDependencies(ModifiableRootModel modifiableRootModel) {
+ for (OrderEntry entry : modifiableRootModel.getOrderEntries()) {
+ if (entry instanceof ModuleOrderEntry) {
+ final Module depModule = ((ModuleOrderEntry)entry).getModule();
+ if (depModule != null && AndroidMavenUtil.isExtApklibModule(depModule)) {
+ modifiableRootModel.removeOrderEntry(entry);
+ }
+ }
+ }
+ }
+
+ private static void addCompileModuleDependency(@NotNull MavenModifiableModelsProvider modelsProvider,
+ @NotNull ModifiableRootModel rootModel,
+ @NotNull final String moduleName) {
+ if (findModuleDependency(rootModel, moduleName) != null) {
+ return;
+ }
+
+ final Module module = modelsProvider.getModuleModel().findModuleByName(moduleName);
+
+ final ModuleOrderEntry entry = module != null
+ ? rootModel.addModuleOrderEntry(module)
+ : rootModel.addInvalidModuleEntry(moduleName);
+ entry.setScope(DependencyScope.COMPILE);
+ }
+
+ private static ModuleOrderEntry findModuleDependency(ModifiableRootModel rootModel, final String moduleName) {
+ final Ref result = Ref.create(null);
+
+ rootModel.orderEntries().forEach(new Processor() {
+ @Override
+ public boolean process(OrderEntry entry) {
+ if (entry instanceof ModuleOrderEntry) {
+ final ModuleOrderEntry moduleEntry = (ModuleOrderEntry)entry;
+ final String name = moduleEntry.getModuleName();
+ if (moduleName.equals(name)) {
+ result.set(moduleEntry);
+ }
+ }
+ return true;
+ }
+ });
+
+ return result.get();
+ }
+
+ @Nullable
+ private static Module importExternalApklibArtifact(Project project,
+ MavenRootModelAdapter rootModelAdapter,
+ Module apklibModule,
+ MavenModifiableModelsProvider modelsProvider,
+ MavenProject mavenProject,
+ MavenProjectsTree mavenTree,
+ MavenArtifact artifact,
+ ModifiableModuleModel moduleModel) {
+ final MavenId artifactMavenId = artifact.getMavenId();
+
+ final String genModuleName = AndroidMavenUtil.getModuleNameForExtApklibArtifact(artifactMavenId);
+
+ String targetDirPath =
+ AndroidMavenUtil.computePathToUnpackExtApklibArtifact(artifactMavenId, mavenProject, mavenTree.getProjects());
+
+ if (targetDirPath == null) {
+ return null;
+ }
+
+ final File targetDir = new File(targetDirPath);
+ if (targetDir.exists()) {
+ if (!FileUtil.delete(targetDir)) {
+ LOG.error("Cannot delete old " + targetDirPath);
+ return null;
+ }
+ }
+
+ if (!targetDir.mkdirs()) {
+ LOG.error("Cannot create directory " + targetDirPath);
+ return null;
+ }
+
+ final File artifactFile = artifact.getFile();
+
+ final AndroidExternalApklibDependenciesManager adm = AndroidExternalApklibDependenciesManager.getInstance(project);
+
+ adm.setArtifactFilePath(artifactMavenId, FileUtil.toSystemIndependentName(artifactFile.getPath()));
+
+ if (artifactFile.exists()) {
+ try {
+ ZipUtil.extract(artifactFile, targetDir, null);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ return null;
+ }
+ }
+ else {
+ LOG.info("Cannot find file " + artifactFile.getPath());
+ }
+
+ final VirtualFile vApklibDir = LocalFileSystem.getInstance().refreshAndFindFileByPath(targetDirPath);
+ if (vApklibDir == null) {
+ LOG.error("Cannot find file " + targetDirPath + " in VFS");
+ return null;
+ }
+
+ if (apklibModule == null) {
+ final String genModuleFilePath = vApklibDir.getPath() + '/' + genModuleName + ModuleFileType.DOT_DEFAULT_EXTENSION;
+ apklibModule = moduleModel.newModule(genModuleFilePath, StdModuleTypes.JAVA);
+ }
+
+ final ModifiableRootModel apklibModuleModel = modelsProvider.getRootModel(apklibModule);
+ final ContentEntry contentEntry = apklibModuleModel.addContentEntry(vApklibDir);
+
+ final VirtualFile sourceRoot = vApklibDir.findChild(AndroidMavenUtil.APK_LIB_ARTIFACT_SOURCE_ROOT);
+ if (sourceRoot != null) {
+ contentEntry.addSourceFolder(sourceRoot, false);
+ }
+ else {
+ LOG.info("Cannot find " + AndroidMavenUtil.APK_LIB_ARTIFACT_SOURCE_ROOT + " directory in " + vApklibDir.getPath());
+ }
+
+ final AndroidFacet facet = AndroidUtils.addAndroidFacet(apklibModuleModel, vApklibDir, true);
+
+ final AndroidFacetConfiguration configuration = facet.getConfiguration();
+ configuration.RES_FOLDER_RELATIVE_PATH = '/' + AndroidMavenUtil.APK_LIB_ARTIFACT_RES_DIR;
+ configuration.LIBS_FOLDER_RELATIVE_PATH = '/' + AndroidMavenUtil.APK_LIB_ARTIFACT_NATIVE_LIBS_DIR;
+ configuration.MANIFEST_FILE_RELATIVE_PATH = '/' + AndroidMavenUtil.APK_LIB_ARTIFACT_MANIFEST_FILE;
+
+ importSdkAndDependenciesForApklibArtifact(project, rootModelAdapter, apklibModuleModel, modelsProvider, mavenTree, artifact);
+
+ return apklibModule;
+ }
+
+ private static void importSdkAndDependenciesForApklibArtifact(Project project,
+ MavenRootModelAdapter rootModelAdapter,
+ ModifiableRootModel apklibModuleModel,
+ MavenModifiableModelsProvider modelsProvider,
+ MavenProjectsTree mavenTree,
+ MavenArtifact artifact) {
+ final AndroidExternalApklibDependenciesManager adm = AndroidExternalApklibDependenciesManager.getInstance(project);
+ final AndroidExternalApklibDependenciesManager.MyResolvedInfo resolvedInfo =
+ adm.getResolvedInfoForArtifact(artifact.getMavenId());
+
+ for (OrderEntry entry : apklibModuleModel.getOrderEntries()) {
+ if (entry instanceof ModuleOrderEntry || entry instanceof LibraryOrderEntry) {
+ apklibModuleModel.removeOrderEntry(entry);
+ }
+ }
+
+ if (resolvedInfo != null) {
+ final Sdk sdk = findOrCreateAndroidPlatform(resolvedInfo.getSdkPath(), resolvedInfo.getApiLevel());
+
+ if (sdk != null) {
+ apklibModuleModel.setSdk(sdk);
+ }
+
+ for (AndroidExternalApklibDependenciesManager.MavenDependencyInfo depArtifactInfo : resolvedInfo.getApklibDependencies()) {
+ final MavenId depMavenId = new MavenId(depArtifactInfo.getGroupId(), depArtifactInfo.getArtifactId(),
+ depArtifactInfo.getVersion());
+
+ final String type = depArtifactInfo.getType();
+
+ if (AndroidMavenUtil.APKLIB_DEPENDENCY_AND_PACKAGING_TYPE.equals(type)) {
+ final MavenProject depProject = mavenTree.findProject(depMavenId);
+
+ if (depProject != null) {
+ final Module depModule = MavenProjectsManager.getInstance(project).findModule(depProject);
+ if (depModule != null && findModuleDependency(apklibModuleModel, depModule.getName()) == null) {
+ apklibModuleModel.addModuleOrderEntry(depModule);
+ }
+ }
+ else {
+ final String depApklibGenModuleName = AndroidMavenUtil.getModuleNameForExtApklibArtifact(depMavenId);
+ addCompileModuleDependency(modelsProvider, apklibModuleModel, depApklibGenModuleName);
+ }
+ }
+ else {
+ final String libraryName = depArtifactInfo.getLibraryName();
+ final ModifiableRootModel rootModel = rootModelAdapter.getRootModel();
+ final LibraryOrderEntry libEntry = findLibraryByName(rootModel, libraryName);
+
+ if (libEntry != null) {
+ final Library library = libEntry.getLibrary();
+
+ if (library != null && apklibModuleModel.findLibraryOrderEntry(library) == null) {
+ final LibraryOrderEntry newLibEntry = apklibModuleModel.addLibraryEntry(library);
+ newLibEntry.setExported(libEntry.isExported());
+ newLibEntry.setScope(libEntry.getScope());
+ }
+ }
+ }
+ }
+ }
+ else {
+ LOG.info("Cannot find sdk info for artifact " + artifact.getMavenId().getKey());
+ }
+ }
+
+ @Nullable
+ private static LibraryOrderEntry findLibraryByName(ModifiableRootModel model, final String libraryName) {
+ final Ref result = Ref.create(null);
+ model.orderEntries().forEach(new Processor() {
+ @Override
+ public boolean process(OrderEntry entry) {
+ if (entry instanceof LibraryOrderEntry) {
+ final LibraryOrderEntry libOrderEntry = (LibraryOrderEntry)entry;
+ final Library library = libOrderEntry.getLibrary();
+
+ if (library != null && libraryName.equals(library.getName())) {
+ result.set(libOrderEntry);
+ }
+ }
+ return true;
+ }
+ });
+ return result.get();
+ }
+
+ @Override
+ public void resolve(final Project project,
+ MavenProject mavenProject,
+ NativeMavenProjectHolder nativeMavenProject,
+ MavenEmbedderWrapper embedder)
+ throws MavenProcessCanceledException {
+
+ for (MavenArtifact depArtifact : mavenProject.getDependencies()) {
+ final MavenProjectsManager mavenProjectsManager = MavenProjectsManager.getInstance(project);
+
+ if (AndroidMavenUtil.APKLIB_DEPENDENCY_AND_PACKAGING_TYPE.equals(depArtifact.getType()) &&
+ mavenProjectsManager.findProject(depArtifact) == null) {
+
+ doResolveApklibArtifact(project, depArtifact, embedder, mavenProjectsManager);
+ }
+ }
+ }
+
+ private void doResolveApklibArtifact(Project project,
+ MavenArtifact artifact,
+ MavenEmbedderWrapper embedder,
+ MavenProjectsManager mavenProjectsManager) throws MavenProcessCanceledException {
+ final File depArtifacetFile = new File(FileUtil.getNameWithoutExtension(artifact.getPath()) + ".pom");
+ if (!depArtifacetFile.exists()) {
+ LOG.info("Cannot find file " + depArtifacetFile.getPath());
+ return;
+ }
+
+ final VirtualFile vDepArtifactFile = LocalFileSystem.getInstance().findFileByIoFile(depArtifacetFile);
+ if (vDepArtifactFile == null) {
+ LOG.error("Cannot find file " + depArtifacetFile.getPath() + " in VFS");
+ return;
+ }
+
+ final MavenProject projectForExternalApklib = new MavenProject(vDepArtifactFile);
+ final MavenGeneralSettings generalSettings = mavenProjectsManager.getGeneralSettings();
+ final MavenProjectReader mavenProjectReader = new MavenProjectReader();
+
+ final MavenProjectReaderProjectLocator locator = new MavenProjectReaderProjectLocator() {
+ @Nullable
+ @Override
+ public VirtualFile findProjectFile(MavenId coordinates) {
+ return null;
+ }
+ };
+
+ projectForExternalApklib.read(generalSettings, mavenProjectsManager.getAvailableProfiles(), mavenProjectReader, locator);
+ projectForExternalApklib.resolve(project, generalSettings, embedder, mavenProjectReader, locator);
+
+ final String apiLevel = getPlatformFromConfig(projectForExternalApklib);
+ final String sdkPath = getSdkPathFromConfig(projectForExternalApklib);
+
+ final List dependencies =
+ new ArrayList();
+
+ for (MavenArtifact depArtifact : projectForExternalApklib.getDependencies()) {
+ final String depArtifactLibraryName = MavenRootModelAdapter.makeLibraryName(depArtifact);
+
+ dependencies.add(new AndroidExternalApklibDependenciesManager.MavenDependencyInfo(
+ depArtifact.getMavenId(), depArtifact.getType(), depArtifactLibraryName));
+ }
+
+ final AndroidExternalApklibDependenciesManager apklibDependenciesManager =
+ AndroidExternalApklibDependenciesManager.getInstance(project);
+
+ final AndroidExternalApklibDependenciesManager.MyResolvedInfo info = new AndroidExternalApklibDependenciesManager.MyResolvedInfo(
+ apiLevel != null ? apiLevel : "",
+ sdkPath != null ? sdkPath : "",
+ dependencies
+ );
+
+ apklibDependenciesManager.setSdkInfoForArtifact(artifact.getMavenId(), info);
}
private void configureAndroidPlatform(AndroidFacet facet, MavenProject project, MavenModifiableModelsProvider modelsProvider) {
- final Sdk currentSdk = modelsProvider.getRootModel(facet.getModule()).getSdk();
+ final ModifiableRootModel model = modelsProvider.getRootModel(facet.getModule());
+ configureAndroidPlatform(project, model);
+ }
+
+ private void configureAndroidPlatform(MavenProject project, ModifiableRootModel model) {
+ final Sdk currentSdk = model.getSdk();
if (currentSdk != null && isAppropriateSdk(currentSdk, project)) {
return;
}
Sdk platformLib = findOrCreateAndroidPlatform(project);
if (platformLib != null) {
- modelsProvider.getRootModel(facet.getModule()).setSdk(platformLib);
+ model.setSdk(platformLib);
}
}
@@ -139,11 +526,25 @@ public abstract class AndroidFacetImporterBase extends FacetImporter referredModules = new HashSet();
+
+ for (Module module : moduleModel.getModules()) {
+ if (!AndroidMavenUtil.isExtApklibModule(module) &&
+ !AndroidCompileUtil.isGenModule(module)) {
+ collectDependenciesRecursively(module, referredModules);
+ }
+ }
+
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ for (final Module module : moduleModel.getModules()) {
+ if (AndroidMavenUtil.isExtApklibModule(module) && !referredModules.contains(module)) {
+ final VirtualFile[] contentRoots = ModuleRootManager.getInstance(module).getContentRoots();
+
+ if (contentRoots.length > 0) {
+ final VirtualFile contentRoot = contentRoots[0];
+ try {
+ contentRoot.delete(myProject);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+
+ final Module genModule = AndroidCompileUtil.getGenModule(module);
+ if (genModule != null) {
+ moduleModel.disposeModule(genModule);
+ }
+
+ moduleModel.disposeModule(module);
+ }
+ }
+
+ moduleModel.commit();
+ }
+ });
+ }
+ });
+ }
+
+ private static void collectDependenciesRecursively(@NotNull Module root, @NotNull Set result) {
+ if (!result.add(root)) {
+ return;
+ }
+
+ for (Module depModule : ModuleRootManager.getInstance(root).getDependencies()) {
+ collectDependenciesRecursively(depModule, result);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ MyDeleteObsoleteApklibModulesTask task = (MyDeleteObsoleteApklibModulesTask)o;
+
+ if (!myProject.equals(task.myProject)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + myProject.hashCode();
+ return result;
+ }
+ }
}
diff --git a/plugins/android/src/org/jetbrains/android/maven/AndroidMavenUtil.java b/plugins/android/src/org/jetbrains/android/maven/AndroidMavenUtil.java
index 8d74784502c6..5b7191db51a2 100644
--- a/plugins/android/src/org/jetbrains/android/maven/AndroidMavenUtil.java
+++ b/plugins/android/src/org/jetbrains/android/maven/AndroidMavenUtil.java
@@ -15,22 +15,92 @@
*/
package org.jetbrains.android.maven;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.maven.model.MavenId;
+import org.jetbrains.idea.maven.project.MavenProject;
+
+import java.util.Collection;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidMavenUtil {
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.maven.AndroidMavenUtil");
+
@NonNls public static final String APKSOURCES_DEPENDENCY_TYPE = "apksources";
@NonNls public static final String APKLIB_DEPENDENCY_AND_PACKAGING_TYPE = "apklib";
+ @NonNls public static final String APK_PACKAGING_TYPE = "apk";
+
+ @NonNls public static final String APK_LIB_ARTIFACT_SOURCE_ROOT = "src";
+ @NonNls public static final String APK_LIB_ARTIFACT_RES_DIR = "res";
+ public static final String APK_LIB_ARTIFACT_NATIVE_LIBS_DIR = "libs";
+ public static final String APK_LIB_ARTIFACT_MANIFEST_FILE = "AndroidManifest.xml";
+ @NonNls private static final String APKLIB_MODULE_PREFIX = "~apklib-";
private AndroidMavenUtil() {
}
+ @Nullable
+ public static String computePathToUnpackExtApklibArtifact(@NotNull MavenId mavenId,
+ @NotNull MavenProject project,
+ @NotNull Collection allProjects) {
+ String path = null;
+ boolean resultUnderApp = false;
+
+ for (MavenProject p : allProjects) {
+ if (p.findDependencies(mavenId).size() > 0) {
+ final VirtualFile projectDir = p.getDirectoryFile();
+ final boolean app = APK_PACKAGING_TYPE.equals(p.getPackaging());
+ if (path == null || !resultUnderApp && app) {
+ path = projectDir.getPath() + '/' + getDirNameToUnpackExtApklibArtifact(mavenId);
+ resultUnderApp = app;
+ }
+ }
+ }
+
+ if (path == null) {
+ path = project.getDirectoryFile().getPath() + '/' + getDirNameToUnpackExtApklibArtifact(mavenId);
+ }
+ return path;
+ }
+
+ @NotNull
+ private static String getDirNameToUnpackExtApklibArtifact(@NotNull MavenId mavenId) {
+ return "gen-external-apklibs/" + getMavenIdStringForFileName(mavenId);
+ }
+
+ @NotNull
+ public static String getModuleNameForExtApklibArtifact(MavenId mavenId) {
+ return APKLIB_MODULE_PREFIX + getMavenIdStringForFileName(mavenId);
+ }
+
+ @Nullable
+ public static String getMavenIdStringByExtApklibModule(@NotNull Module module) {
+ final String moduleName = module.getName();
+
+ if (!moduleName.startsWith(APKLIB_MODULE_PREFIX)) {
+ return null;
+ }
+
+ return moduleName.substring(APKLIB_MODULE_PREFIX.length());
+ }
+
+ public static boolean isExtApklibModule(@NotNull Module module) {
+ return module.getName().startsWith(APKLIB_MODULE_PREFIX);
+ }
+
+ @NotNull
+ public static String getMavenIdStringForFileName(@NotNull MavenId mavenId) {
+ final String artifactId = mavenId.getKey().replace(':', '_');
+ return artifactId != null ? artifactId : "null";
+ }
+
public static boolean isMavenizedModule(@NotNull Module module) {
AndroidMavenProvider mavenProxy = getMavenProvider();
return mavenProxy != null && mavenProxy.isMavenizedModule(module);