aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2013-05-31 18:31:22 -0700
committerXavier Ducrohet <xav@android.com>2013-06-27 12:36:58 -0700
commitc2a6c07d08cc39e28a3d4aa63c7c850c700bb831 (patch)
treea467d9d1450fb6dc7444d7b2582bf4e3251abc25
parent504bf5c42ed86a7d053fe6f9e015999ae0d2e3d5 (diff)
downloadsdk-c2a6c07d08cc39e28a3d4aa63c7c850c700bb831.zip
sdk-c2a6c07d08cc39e28a3d4aa63c7c850c700bb831.tar.gz
sdk-c2a6c07d08cc39e28a3d4aa63c7c850c700bb831.tar.bz2
Improves Gradle export.
Splits figuring out the modules and their props from the code writing the new files. The project selection page now uses a ProjectSetupBuilder and updates it on every project selection or unselection. This provides real-time feedback on missing dependencies or broken ones. Creates a new page to allow confirmation of project root and list of modules. Will also allow review errors when some are detected during modules gathering. A final page, is shown after the export takes place to review the result. Change-Id: Ia67acc88f390f661518ed13818f25750be181c78
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/BuildFileCreator.java420
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ConfirmationPage.java275
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportMessages.properties4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportStatus.java54
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/FinalPage.java112
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportPage.java459
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportWizard.java72
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleModule.java90
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSelectionPage.java275
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java425
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportGradleTest.java50
11 files changed, 1504 insertions, 732 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/BuildFileCreator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/BuildFileCreator.java
index 3b9c39d..d6e2276 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/BuildFileCreator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/BuildFileCreator.java
@@ -17,20 +17,17 @@
package com.android.ide.eclipse.adt.internal.wizards.exportgradle;
import com.android.SdkConstants;
-import com.android.ide.eclipse.adt.AdtConstants;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
+import com.android.annotations.NonNull;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
-import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
import com.android.io.IAbstractFile;
import com.android.sdklib.io.FileOp;
-import com.android.utils.Pair;
import com.android.xml.AndroidManifest;
+import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import com.google.common.io.Files;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -41,12 +38,8 @@ import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
-import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
@@ -60,7 +53,6 @@ import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
-import java.util.regex.Pattern;
/**
* Creates build.gradle and settings.gradle files for a set of projects.
@@ -68,13 +60,13 @@ import java.util.regex.Pattern;
* Based on {@link org.eclipse.ant.internal.ui.datatransfer.BuildFileCreator}
*/
public class BuildFileCreator {
- private static final String BUILD_FILE = "build.gradle"; //$NON-NLS-1$
- private static final String SETTINGS_FILE = "settings.gradle"; //$NON-NLS-1$
+ static final String BUILD_FILE = "build.gradle"; //$NON-NLS-1$
+ static final String SETTINGS_FILE = "settings.gradle"; //$NON-NLS-1$
private static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
private static final String GRADLE_WRAPPER_LOCATION =
"tools/templates/gradle/wrapper"; //$NON-NLS-1$
static final String PLUGIN_CLASSPATH =
- "classpath 'com.android.tools.build:gradle:0.4'"; //$NON-NLS-1$
+ "classpath 'com.android.tools.build:gradle:0.4.+'"; //$NON-NLS-1$
static final String MAVEN_REPOSITORY = "mavenCentral()"; //$NON-NLS-1$
private static final String[] GRADLE_WRAPPER_FILES = new String[] {
@@ -91,27 +83,24 @@ public class BuildFileCreator {
}
};
- private StringBuilder buildfile;
- private IJavaProject project;
- private String projectName;
- private String projectRoot;
- private final IPath commonRoot;
+ private final GradleModule mModule;
+ private final StringBuilder mBuildFile = new StringBuilder();
/**
- * Create buildfile for the given projects.
+ * Create buildfile for the projects.
*
- * @param projects create buildfiles for these <code>IJavaProject</code>
- * objects
* @param shell parent instance for dialogs
* @return project names for which buildfiles were created
* @throws InterruptedException thrown when user cancels task
*/
- public static List<String> createBuildFiles(Set<IJavaProject> projects, Shell shell,
- IProgressMonitor pm)
- throws JavaModelException, IOException, CoreException, InterruptedException {
+ public static void createBuildFiles(
+ @NonNull ProjectSetupBuilder builder,
+ @NonNull Shell shell,
+ @NonNull IProgressMonitor pm) {
+
File gradleLocation = new File(Sdk.getCurrent().getSdkLocation(), GRADLE_WRAPPER_LOCATION);
- List<String> res = new ArrayList<String>();
SubMonitor localmonitor = null;
+
try {
// See if we have a Gradle wrapper in the SDK templates directory. If so, we can copy
// it over.
@@ -122,42 +111,22 @@ public class BuildFileCreator {
}
}
- Set<IJavaProject> fullProjectSet = Sets.newHashSet();
-
- // build a list of all projects that must be included. This is in case
- // some dependencies have not been included in the selected projects. We also include
- // parent projects so that the full multi-project setup is correct.
- // Note that if two projects are selected that are not related, both will be added
- // in the same multi-project anyway.
- for (IJavaProject javaProject : projects) {
- fullProjectSet.add(javaProject);
- ProjectState projectState = Sdk.getProjectState(javaProject.getProject());
-
- // add dependencies
- List<IProject> dependencies = projectState.getFullLibraryProjects();
- for (IProject dependency : dependencies) {
- fullProjectSet.add(JavaCore.create(dependency));
- }
-
- Collection<ProjectState> parents = projectState.getFullParentProjects();
- for (ProjectState parent : parents) {
- fullProjectSet.add(JavaCore.create(parent.getProject()));
- }
- }
+ Collection<GradleModule> modules = builder.getModules();
+ boolean multiModules = modules.size() > 1;
// determine files to create/change
List<IFile> files = new ArrayList<IFile>();
- // add the build.gradle file for all projects.
- for (IJavaProject project : fullProjectSet) {
+ // add the build.gradle file for all modules.
+ for (GradleModule module : modules) {
// build.gradle file
- IFile file = project.getProject().getFile(BuildFileCreator.BUILD_FILE);
+ IFile file = module.getProject().getFile(BuildFileCreator.BUILD_FILE);
files.add(file);
}
- // get the commonRoot for all projects. If only one project, this returns the path
+ // get the commonRoot for all modules. If only one module, this returns the path
// of the project.
- IPath commonRoot = determineCommonRoot(fullProjectSet);
+ IPath commonRoot = builder.getCommonRoot();
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IPath workspaceLocation = workspaceRoot.getLocation();
@@ -167,8 +136,8 @@ public class BuildFileCreator {
File settingsFile = new File(commonRoot.toFile(), SETTINGS_FILE);
- // more than one project -> generate settings.gradle
- if (fullProjectSet.size() > 1 && rootInWorkspace) {
+ // more than one modules -> generate settings.gradle
+ if (multiModules && rootInWorkspace) {
// Locate the settings.gradle file and add it to the changed files list
IPath settingsGradle = Path.fromOSString(settingsFile.getAbsolutePath());
@@ -193,26 +162,38 @@ public class BuildFileCreator {
}
}
+ ExportStatus status = new ExportStatus();
+ builder.setStatus(status);
+
// Trigger checkout of changed files
- Set<IFile> confirmedFiles = validateEdit(shell, files);
+ Set<IFile> confirmedFiles = validateEdit(files, status, shell);
+
+ if (status.hasError()) {
+ return;
+ }
- // Now iterate over all the projects and generate the build files.
+ // Now iterate over all the modules and generate the build files.
localmonitor = SubMonitor.convert(pm, ExportMessages.PageTitle,
confirmedFiles.size());
List<String> projectSettingsPath = Lists.newArrayList();
- for (IJavaProject currentProject : fullProjectSet) {
- IFile file = currentProject.getProject().getFile(BuildFileCreator.BUILD_FILE);
+ for (GradleModule currentModule : modules) {
+ IProject moduleProject = currentModule.getProject();
+
+ IFile file = moduleProject.getFile(BuildFileCreator.BUILD_FILE);
if (!confirmedFiles.contains(file)) {
continue;
}
localmonitor.setTaskName(NLS.bind(ExportMessages.FileStatusMessage,
- currentProject.getProject().getName()));
+ moduleProject.getName()));
- ProjectState projectState = Sdk.getProjectState(currentProject.getProject());
- BuildFileCreator instance = new BuildFileCreator(currentProject, commonRoot, shell);
+ ProjectState projectState = Sdk.getProjectState(moduleProject);
+ BuildFileCreator instance = new BuildFileCreator(currentModule, shell);
if (projectState != null) {
// This is an Android project
+ if (!multiModules) {
+ instance.appendBuildScript();
+ }
instance.appendHeader(projectState.isLibrary());
instance.appendDependencies();
instance.startAndroidTask(projectState);
@@ -225,35 +206,58 @@ public class BuildFileCreator {
instance.createJavaSourceSets();
}
- // Write the build file
- String buildfile = instance.buildfile.toString();
- InputStream is =
- new ByteArrayInputStream(buildfile.getBytes("UTF-8")); //$NON-NLS-1$
- if (file.exists()) {
- file.setContents(is, true, true, null);
- } else {
- file.create(is, true, null);
- }
- if (localmonitor.isCanceled()) {
- return res;
- }
- localmonitor.worked(1);
- res.add(instance.projectName);
+ try {
+ // Write the build file
+ String buildfile = instance.mBuildFile.toString();
+ InputStream is =
+ new ByteArrayInputStream(buildfile.getBytes("UTF-8")); //$NON-NLS-1$
+ if (file.exists()) {
+ file.setContents(is, true, true, null);
+ } else {
+ file.create(is, true, null);
+ }
+ } catch (Exception e) {
+ status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE,
+ file.getLocation().toFile());
+ status.setErrorMessage(e.getMessage());
+ return;
+ }
+
+ if (localmonitor.isCanceled()) {
+ return;
+ }
+ localmonitor.worked(1);
// get the project path to add it to the settings.gradle.
- projectSettingsPath.add(instance.getRelativeGradleProjectPath());
+ projectSettingsPath.add(currentModule.getPath());
}
// write the settings file.
- if (fullProjectSet.size() > 1) {
- writeGradleSettingsFile(settingsFile, projectSettingsPath);
- writeRootBuildGradle(new File(commonRoot.toFile(), BUILD_FILE));
+ if (multiModules) {
+ try {
+ writeGradleSettingsFile(settingsFile, projectSettingsPath);
+ } catch (IOException e) {
+ status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, settingsFile);
+ status.setErrorMessage(e.getMessage());
+ return;
+ }
+ File mainBuildFile = new File(commonRoot.toFile(), BUILD_FILE);
+ try {
+ writeRootBuildGradle(mainBuildFile);
+ } catch (IOException e) {
+ status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, mainBuildFile);
+ status.setErrorMessage(e.getMessage());
+ return;
+ }
}
// finally write the wrapper
// TODO check we can based on where it is
if (hasGradleWrapper) {
- copyGradleWrapper(gradleLocation, commonRoot.toFile());
+ copyGradleWrapper(gradleLocation, commonRoot.toFile(), status);
+ if (status.hasError()) {
+ return;
+ }
}
} finally {
@@ -264,19 +268,14 @@ public class BuildFileCreator {
pm.done();
}
}
- return res;
}
/**
- * @param project create buildfile for this project
+ * @param GradleModule create buildfile for this project
* @param shell parent instance for dialogs
*/
- private BuildFileCreator(IJavaProject project, IPath commonRoot, Shell shell) {
- this.project = project;
- this.projectName = project.getProject().getName();
- this.buildfile = new StringBuilder();
- this.projectRoot = project.getResource().getLocation().toString();
- this.commonRoot = commonRoot;
+ private BuildFileCreator(GradleModule module, Shell shell) {
+ mModule = module;
}
/**
@@ -295,145 +294,71 @@ public class BuildFileCreator {
/**
* Copy the Gradle wrapper files from one directory to another.
*/
- private static void copyGradleWrapper(File from, File to) throws IOException {
+ private static void copyGradleWrapper(File from, File to, ExportStatus status) {
for (String file : GRADLE_WRAPPER_FILES) {
File dest = new File(to, file);
- if (dest.exists()) {
- // Don't clobber an existing file. The user may have modified it already.
- continue;
+ try {
+ File src = new File(from, file);
+ dest.getParentFile().mkdirs();
+ new FileOp().copyFile(src, dest);
+ dest.setExecutable(src.canExecute());
+ status.addFileStatus(ExportStatus.FileStatus.OK, dest);
+ } catch (IOException e) {
+ status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, dest);
+ return;
}
- File src = new File(from, file);
- dest.getParentFile().mkdirs();
- new FileOp().copyFile(src, dest);
- dest.setExecutable(src.canExecute());
}
}
/**
- * Finds the common parent directory shared by this project and all its dependencies.
- * If there's only one project, returns the single project's folder.
+ * Outputs boilerplate buildscript information common to all Gradle build files.
*/
- private static IPath determineCommonRoot(Collection<IJavaProject> projects) {
- IPath commonRoot = null;
- for (IJavaProject javaProject : projects) {
- if (commonRoot == null) {
- commonRoot = javaProject.getProject().getLocation();
- } else {
- commonRoot = findCommonRoot(commonRoot, javaProject.getProject().getLocation());
- }
- }
+ private void appendBuildScript() {
+ appendBuildScript(mBuildFile);
+ }
- return commonRoot;
+ /**
+ * Outputs boilerplate header information common to all Gradle build files.
+ */
+ private static void appendBuildScript(StringBuilder builder) {
+ builder.append("buildscript {\n"); //$NON-NLS-1$
+ builder.append(" repositories {\n"); //$NON-NLS-1$
+ builder.append(" " + MAVEN_REPOSITORY + "\n"); //$NON-NLS-1$
+ builder.append(" }\n"); //$NON-NLS-1$
+ builder.append(" dependencies {\n"); //$NON-NLS-1$
+ builder.append(" " + PLUGIN_CLASSPATH + "\n"); //$NON-NLS-1$
+ builder.append(" }\n"); //$NON-NLS-1$
+ builder.append("}\n"); //$NON-NLS-1$
}
/**
* Outputs boilerplate header information common to all Gradle build files.
*/
private void appendHeader(boolean isLibrary) {
- buildfile.append("buildscript {\n"); //$NON-NLS-1$
- buildfile.append(" repositories {\n"); //$NON-NLS-1$
- buildfile.append(" " + MAVEN_REPOSITORY + "\n"); //$NON-NLS-1$
- buildfile.append(" }\n"); //$NON-NLS-1$
- buildfile.append(" dependencies {\n"); //$NON-NLS-1$
- buildfile.append(" " + PLUGIN_CLASSPATH + "\n"); //$NON-NLS-1$
- buildfile.append(" }\n"); //$NON-NLS-1$
- buildfile.append("}\n"); //$NON-NLS-1$
if (isLibrary) {
- buildfile.append("apply plugin: 'android-library'\n"); //$NON-NLS-1$
+ mBuildFile.append("apply plugin: 'android-library'\n"); //$NON-NLS-1$
} else {
- buildfile.append("apply plugin: 'android'\n"); //$NON-NLS-1$
+ mBuildFile.append("apply plugin: 'android'\n"); //$NON-NLS-1$
}
- buildfile.append("\n"); //$NON-NLS-1$
+ mBuildFile.append("\n"); //$NON-NLS-1$
}
/**
* Outputs a block which sets up library and project dependencies.
*/
private void appendDependencies() {
- if (project == null) {
- AdtPlugin.log(IStatus.WARNING, "project is not loaded in workspace"); //$NON-NLS-1$
- return;
- }
- buildfile.append("dependencies {\n"); //$NON-NLS-1$
+ mBuildFile.append("dependencies {\n"); //$NON-NLS-1$
// first the local jars.
- buildfile.append(" compile fileTree(dir: 'libs', include: '*.jar')\n");
-
- // then the project dependencies.
- // First the Android libraries.
- ProjectState state = Sdk.getProjectState(project.getProject());
+ // TODO: Fix
+ mBuildFile.append(" compile fileTree(dir: 'libs', include: '*.jar')\n"); //$NON-NLS-1$
- for (LibraryState libState : state.getLibraries()) {
- String path = getRelativeGradleProjectPath(
- libState.getProjectState().getProject().getLocation(), commonRoot);
-
- buildfile.append(" compile project('" + path + "')\n"); //$NON-NLS-1$
+ for (GradleModule dep : mModule.getDependencies()) {
+ mBuildFile.append(" compile project('" + dep.getPath() + "')\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
- // Next, the other non-Android projects.
- for (IClasspathEntry entry : project.readRawClasspath()) {
- if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
- IProject cpProject = getNonAndroidProjectByName(entry.getPath().toString());
- if (cpProject != null) {
- String path = getRelativeGradleProjectPath(
- cpProject.getLocation(), commonRoot);
-
- buildfile.append(" compile project('" + path + "')\n"); //$NON-NLS-1$
- }
- }
- }
-
- buildfile.append("}\n"); //$NON-NLS-1$
- buildfile.append("\n"); //$NON-NLS-1$
- }
-
- /**
- * Given two IPaths, finds the parent directory of both of them.
- */
- private static IPath findCommonRoot(IPath path1, IPath path2) {
- // TODO: detect paths on different disk drive on Windows!
- if (path1 != null && path1.getDevice() != null) {
- assert path1.getDevice().equals(path2.getDevice());
- }
- IPath result = path1.uptoSegment(0);
-
- final int count = Math.min(path1.segmentCount(), path2.segmentCount());
- for (int i = 0; i < count; i++) {
- if (path1.segment(i).equals(path2.segment(i))) {
- result = result.append(Path.SEPARATOR + path2.segment(i));
- }
- }
- return result;
- }
-
- private String getRelativeGradleProjectPath() {
- return getRelativeGradleProjectPath(project.getProject().getLocation(), commonRoot);
- }
-
- /**
- * Converts the given path to be relative to the given root path, and converts it to
- * Gradle project notation, such as is used in the settings.gradle file.
- */
- private String getRelativeGradleProjectPath(IPath path, IPath root) {
- IPath relativePath = path.makeRelativeTo(root);
- String relativeString = relativePath.toOSString();
- return ":" + relativeString.replaceAll(Pattern.quote(File.separator), ":"); //$NON-NLS-1$
- }
-
- /**
- * Finds the workspace project with the given name
- */
- private static IProject getNonAndroidProjectByName(String name) {
- try {
- IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
- if (project.exists() && !project.hasNature(AdtConstants.NATURE_DEFAULT)) {
- return project;
- }
- } catch (IllegalArgumentException iae) {
- } catch (CoreException e) {
- }
-
- return null;
+ mBuildFile.append("}\n"); //$NON-NLS-1$
+ mBuildFile.append("\n"); //$NON-NLS-1$
}
/**
@@ -442,24 +367,10 @@ public class BuildFileCreator {
private void startAndroidTask(ProjectState projectState) {
int buildApi = projectState.getTarget().getVersion().getApiLevel();
String toolsVersion = projectState.getTarget().getBuildToolInfo().getRevision().toString();
- buildfile.append("android {\n"); //$NON-NLS-1$
- buildfile.append(" compileSdkVersion " + buildApi + "\n"); //$NON-NLS-1$
- buildfile.append(" buildToolsVersion \"" + toolsVersion + "\"\n"); //$NON-NLS-1$
- buildfile.append("\n"); //$NON-NLS-1$
- }
-
- /**
- * Outputs the defaultConfig block in the Android task.
- */
- private void appendDefaultConfig() {
- Pair<Integer, Integer> v = ManifestInfo.computeSdkVersions(project.getProject());
- int minApi = v.getFirst();
- int targetApi = v.getSecond();
-
- buildfile.append(" defaultConfig {\n"); //$NON-NLS-1$
- buildfile.append(" minSdkVersion " + minApi + "\n"); //$NON-NLS-1$
- buildfile.append(" targetSdkVersion " + targetApi + "\n"); //$NON-NLS-1$
- buildfile.append(" }\n"); //$NON-NLS-1$
+ mBuildFile.append("android {\n"); //$NON-NLS-1$
+ mBuildFile.append(" compileSdkVersion " + buildApi + "\n"); //$NON-NLS-1$
+ mBuildFile.append(" buildToolsVersion \"" + toolsVersion + "\"\n"); //$NON-NLS-1$
+ mBuildFile.append("\n"); //$NON-NLS-1$
}
/**
@@ -467,13 +378,13 @@ public class BuildFileCreator {
* subdirectories in the project.
*/
private void createAndroidSourceSets() {
- IFolderWrapper projectFolder = new IFolderWrapper(project.getProject());
+ IFolderWrapper projectFolder = new IFolderWrapper(mModule.getProject());
IAbstractFile mManifestFile = AndroidManifest.getManifest(projectFolder);
if (mManifestFile == null) {
return;
}
List<String> srcDirs = new ArrayList<String>();
- for (IClasspathEntry entry : project.readRawClasspath()) {
+ for (IClasspathEntry entry : mModule.getJavaProject().readRawClasspath()) {
if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE ||
SdkConstants.FD_GEN_SOURCES.equals(entry.getPath().lastSegment())) {
continue;
@@ -484,33 +395,33 @@ public class BuildFileCreator {
String srcPaths = Joiner.on(",").join(srcDirs);
- buildfile.append(" sourceSets {\n"); //$NON-NLS-1$
- buildfile.append(" main {\n"); //$NON-NLS-1$
- buildfile.append(" manifest.srcFile '" + SdkConstants.FN_ANDROID_MANIFEST_XML + "'\n"); //$NON-NLS-1$
- buildfile.append(" java.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
- buildfile.append(" resources.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
- buildfile.append(" aidl.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
- buildfile.append(" renderscript.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
- buildfile.append(" res.srcDirs = ['res']\n"); //$NON-NLS-1$
- buildfile.append(" assets.srcDirs = ['assets']\n"); //$NON-NLS-1$
- buildfile.append(" }\n"); //$NON-NLS-1$
- buildfile.append("\n"); //$NON-NLS-1$
- buildfile.append(" instrumentTest.setRoot('tests')\n"); //$NON-NLS-1$
- buildfile.append(" }\n"); //$NON-NLS-1$
+ mBuildFile.append(" sourceSets {\n"); //$NON-NLS-1$
+ mBuildFile.append(" main {\n"); //$NON-NLS-1$
+ mBuildFile.append(" manifest.srcFile '" + SdkConstants.FN_ANDROID_MANIFEST_XML + "'\n"); //$NON-NLS-1$
+ mBuildFile.append(" java.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
+ mBuildFile.append(" resources.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
+ mBuildFile.append(" aidl.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
+ mBuildFile.append(" renderscript.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
+ mBuildFile.append(" res.srcDirs = ['res']\n"); //$NON-NLS-1$
+ mBuildFile.append(" assets.srcDirs = ['assets']\n"); //$NON-NLS-1$
+ mBuildFile.append(" }\n"); //$NON-NLS-1$
+ mBuildFile.append("\n"); //$NON-NLS-1$
+ mBuildFile.append(" instrumentTest.setRoot('tests')\n"); //$NON-NLS-1$
+ mBuildFile.append(" }\n"); //$NON-NLS-1$
}
/**
* Outputs the completion of the Android task in the build file.
*/
private void finishAndroidTask() {
- buildfile.append("}\n"); //$NON-NLS-1$
+ mBuildFile.append("}\n"); //$NON-NLS-1$
}
/**
* Outputs a boilerplate header for non-Android projects
*/
private void appendJavaHeader() {
- buildfile.append("apply plugin: 'java'\n"); //$NON-NLS-1$
+ mBuildFile.append("apply plugin: 'java'\n"); //$NON-NLS-1$
}
/**
@@ -518,7 +429,7 @@ public class BuildFileCreator {
*/
private void createJavaSourceSets() {
List<String> dirs = new ArrayList<String>();
- for (IClasspathEntry entry : project.readRawClasspath()) {
+ for (IClasspathEntry entry : mModule.getJavaProject().readRawClasspath()) {
if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
continue;
}
@@ -528,30 +439,36 @@ public class BuildFileCreator {
String srcPaths = Joiner.on(",").join(dirs);
- buildfile.append("sourceSets {\n"); //$NON-NLS-1$
- buildfile.append(" main.java.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
- buildfile.append(" main.resources.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
- buildfile.append(" test.java.srcDirs = ['tests/java']\n"); //$NON-NLS-1$
- buildfile.append(" test.resources.srcDirs = ['tests/resources']\n"); //$NON-NLS-1$
- buildfile.append("}\n"); //$NON-NLS-1$
+ mBuildFile.append("sourceSets {\n"); //$NON-NLS-1$
+ mBuildFile.append(" main.java.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
+ mBuildFile.append(" main.resources.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
+ mBuildFile.append(" test.java.srcDirs = ['tests/java']\n"); //$NON-NLS-1$
+ mBuildFile.append(" test.resources.srcDirs = ['tests/resources']\n"); //$NON-NLS-1$
+ mBuildFile.append("}\n"); //$NON-NLS-1$
}
/**
* Merges the new subproject dependencies into the settings.gradle file if it already exists,
* and creates one if it does not.
+ * @throws IOException
*/
- private static void writeGradleSettingsFile(File settingsFile, List<String> projectPaths) {
+ private static void writeGradleSettingsFile(File settingsFile, List<String> projectPaths)
+ throws IOException {
StringBuilder contents = new StringBuilder();
for (String path : projectPaths) {
contents.append("include '").append(path).append("'\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
- AdtPlugin.writeFile(settingsFile, contents.toString());
+ Files.write(contents.toString(), settingsFile, Charsets.UTF_8);
}
- private static void writeRootBuildGradle(File buildFile) {
- AdtPlugin.writeFile(buildFile,
+ private static void writeRootBuildGradle(File buildFile) throws IOException {
+ StringBuilder sb = new StringBuilder(
"// Top-level build file where you can add configuration options common to all sub-projects/modules.\n");
+
+ appendBuildScript(sb);
+
+ Files.write(sb.toString(), buildFile, Charsets.UTF_8);
}
/**
@@ -564,7 +481,10 @@ public class BuildFileCreator {
* @throws CoreException
* thrown if project is under version control, but not connected
*/
- static Set<IFile> validateEdit(Shell shell, List<IFile> files) throws CoreException {
+ static Set<IFile> validateEdit(
+ @NonNull List<IFile> files,
+ @NonNull ExportStatus exportStatus,
+ @NonNull Shell shell) {
Set<IFile> confirmedFiles = new TreeSet<IFile>(FILE_COMPARATOR);
if (files.size() == 0) {
return confirmedFiles;
@@ -576,6 +496,10 @@ public class BuildFileCreator {
IStatus statusChild = status.getChildren()[i];
if (statusChild.isOK()) {
confirmedFiles.add(files.get(i));
+ } else {
+ exportStatus.addFileStatus(
+ ExportStatus.FileStatus.VCS_FAILURE,
+ files.get(i).getLocation().toFile());
}
}
} else if (status.isOK()) {
@@ -593,8 +517,8 @@ public class BuildFileCreator {
message.append(statusChild.getMessage() + NEWLINE);
}
}
- throw new CoreException(new Status(IStatus.ERROR,
- AdtPlugin.PLUGIN_ID, 0, message.toString(), null));
+ String s = message.toString();
+ exportStatus.setErrorMessage(s);
}
return confirmedFiles;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ConfirmationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ConfirmationPage.java
new file mode 100644
index 0000000..1f236fb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ConfirmationPage.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 com.android.ide.eclipse.adt.internal.wizards.exportgradle;
+
+import com.google.common.collect.Lists;
+
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableLayout;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Confirmation page to review the actual project export
+ * list and see warning about existing files.
+ *
+ */
+public class ConfirmationPage extends WizardPage {
+
+ private final ProjectSetupBuilder mBuilder;
+ private TableViewer mTableViewer;
+ private Label mModuleDescription1;
+ private Label mModuleDescription2;
+ private Label mModuleDescription3;
+ private Label mProjectRootLabel;
+ private Label mProjectRootWarning;
+ private List<IJavaProject> mOverrideProjects;
+ private boolean mOverrideWarning;
+ private Button mForceOverride;
+
+ public ConfirmationPage(ProjectSetupBuilder builder) {
+ super("ConfirmationPage"); //$NON-NLS-1$
+ mBuilder = builder;
+ setPageComplete(false);
+ setTitle(ExportMessages.PageTitle);
+ setDescription(ExportMessages.PageDescription);
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ initializeDialogUnits(parent);
+ GridData data;
+
+ Composite workArea = new Composite(parent, SWT.NONE);
+ setControl(workArea);
+
+ workArea.setLayout(new GridLayout());
+ workArea.setLayoutData(new GridData(GridData.FILL_BOTH
+ | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
+
+ Label title = new Label(workArea, SWT.NONE);
+ title.setText("Please review the export options.");
+
+ Group group = new Group(workArea, SWT.NONE);
+ group.setText("Project root");
+ group.setLayout(new GridLayout());
+ group.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+
+ mProjectRootLabel = new Label(group, SWT.NONE);
+ mProjectRootLabel.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+
+ mProjectRootWarning = new Label(group, SWT.NONE);
+ mProjectRootWarning.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+
+ Group group2 = new Group(workArea, SWT.NONE);
+ group2.setText("Exported Modules");
+ group2.setLayout(new GridLayout());
+ group2.setLayoutData(data = new GridData(SWT.FILL, SWT.FILL, true, true));
+ data.heightHint = 300;
+
+ Table table = new Table(group2, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+ mTableViewer = new TableViewer(table);
+ table.setLayout(new TableLayout());
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ mTableViewer.setContentProvider(new IStructuredContentProvider() {
+ @Override
+ public Object[] getElements(Object inputElement) {
+ if (inputElement instanceof ProjectSetupBuilder) {
+ ProjectSetupBuilder builder = (ProjectSetupBuilder) inputElement;
+ Collection<GradleModule> modules = builder.getModules();
+ Object[] array = new Object[modules.size()];
+ int i = 0;
+ for (GradleModule module : modules) {
+ array[i++] = module.getJavaProject();
+ }
+
+ return array;
+ }
+
+ return null;
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+
+ });
+ mTableViewer.setLabelProvider(new WorkbenchLabelProvider() {
+ @Override
+ protected String decorateText(String input, Object element) {
+ if (element instanceof IJavaProject) {
+ IJavaProject javaProject = (IJavaProject) element;
+ StringBuilder sb = new StringBuilder(input);
+ if (!mBuilder.isOriginalProject(javaProject)) {
+ sb.append('*');
+ }
+ // TODO: decorate icon instead?
+ if (mOverrideProjects.contains(javaProject)) {
+ sb.append(" (1 warning)");
+ }
+
+ return sb.toString();
+ }
+
+ return input;
+ }
+ });
+ mTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection selection = (IStructuredSelection) event.getSelection();
+ Object firstElement = selection.getFirstElement();
+ if (firstElement instanceof IJavaProject) {
+ GradleModule module = mBuilder.getModule((IJavaProject) firstElement);
+ if (mBuilder.getOriginalModules().contains(module)) {
+ mModuleDescription1.setText("Exported because selected in previous page.");
+ } else {
+ List<GradleModule> list = mBuilder.getShortestDependencyTo(module);
+ StringBuilder sb = new StringBuilder();
+ for (GradleModule m : list) {
+ if (sb.length() > 0) {
+ sb.append(" > ");
+ }
+ sb.append(m.getJavaProject().getProject().getName());
+ }
+ mModuleDescription1.setText("Dependency chain: " + sb);
+ }
+ mModuleDescription2.setText("Path: " + module.getPath());
+
+ if (mOverrideProjects.contains(module.getJavaProject())) {
+ mModuleDescription3.setText(
+ "WARNING: build.gradle already exists for this project");
+ } else {
+ mModuleDescription3.setText("");
+ }
+ } else {
+ mModuleDescription1.setText("");
+ mModuleDescription2.setText("");
+ mModuleDescription3.setText("");
+ }
+ }
+ });
+
+ mModuleDescription1 = new Label(group2, SWT.NONE);
+ mModuleDescription1.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+ mModuleDescription2 = new Label(group2, SWT.NONE);
+ mModuleDescription2.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+ mModuleDescription3 = new Label(group2, SWT.NONE);
+ mModuleDescription3.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+
+ mForceOverride = new Button(workArea, SWT.CHECK);
+ mForceOverride.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+ mForceOverride.setText("Force overriding of existing files");
+ mForceOverride.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ updateEnablement();
+ }
+ });
+
+ setControl(workArea);
+ Dialog.applyDialogFont(parent);
+ }
+
+ /**
+ * Get list of projects which have already a buildfile.
+ *
+ * @param javaProjects list of IJavaProject objects
+ * @return set of project names
+ */
+ private void computeOverride(String commonRoot) {
+ mOverrideProjects = Lists.newArrayList();
+ for (GradleModule module : mBuilder.getModules()) {
+ if (new File(module.getProject().getLocation().toFile(),
+ BuildFileCreator.BUILD_FILE).exists()) {
+ mOverrideProjects.add(module.getJavaProject());
+ }
+ }
+
+ // also check on the root settings.gradle/build.gradle
+ boolean settingsFile = new File(commonRoot, BuildFileCreator.SETTINGS_FILE).exists();
+ boolean buildFile = new File(commonRoot, BuildFileCreator.BUILD_FILE).exists();
+ if (settingsFile && buildFile) {
+ mProjectRootWarning.setText(
+ "WARNING: build.gradle/settings.gradle already exists at this location.");
+ } else if (settingsFile) {
+ mProjectRootWarning.setText(
+ "WARNING: settings.gradle already exists at this location.");
+ } else if (buildFile) {
+ mProjectRootWarning.setText("WARNING: build.gradle already exists at this location.");
+ }
+
+ mOverrideWarning = mOverrideProjects.size() > 0 || settingsFile || buildFile;
+ }
+
+ /**
+ * Enables/disables the finish button on the wizard and displays error messages as needed.
+ */
+ private void updateEnablement() {
+ if (mOverrideWarning && !mForceOverride.getSelection()) {
+ setErrorMessage("Enable overriding of existing files before clicking Finish");
+ mBuilder.setCanGenerate(false);
+ } else {
+ setErrorMessage(null);
+ mBuilder.setCanGenerate(true);
+ }
+ setPageComplete(false);
+ getContainer().updateButtons();
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ if (visible) {
+ mProjectRootWarning.setText("");
+
+ String commonRoot = mBuilder.getCommonRoot().toOSString();
+ computeOverride(commonRoot);
+ mProjectRootLabel.setText(commonRoot);
+ mTableViewer.setInput(mBuilder);
+ mTableViewer.getTable().setFocus();
+ mBuilder.setCanFinish(false);
+ mBuilder.setCanGenerate(true);
+ updateEnablement();
+ }
+ }
+} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportMessages.properties b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportMessages.properties
index 38d7771..1a6dbb1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportMessages.properties
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportMessages.properties
@@ -12,8 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-PageTitle=Generate Gradle Buildfiles
-PageDescription=Generates Gradle buildfiles based on the configuration of the Java projects
+PageTitle=Generate Gradle Build files
+PageDescription=Generates Gradle build files based on the configuration of the Java projects
SelectProjects=Select the projects to use to &generate the Gradle buildfiles:
ConfirmOverwrite=Are you sure you want to overwrite the buildfiles for these projects?
ConfirmOverwriteTitle=Overwrite Buildfiles?
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportStatus.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportStatus.java
new file mode 100644
index 0000000..3459235
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportStatus.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 com.android.ide.eclipse.adt.internal.wizards.exportgradle;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
+import java.io.File;
+
+public class ExportStatus {
+
+ public static enum FileStatus { OK, VCS_FAILURE, IO_FAILURE; }
+
+ private String mMainError = null;
+ private final Multimap<FileStatus, File> mFileStatus = ArrayListMultimap.create();
+
+ void addFileStatus(@NonNull FileStatus status, @NonNull File file) {
+ mFileStatus.put(status, file);
+ }
+
+ boolean hasError() {
+ return mMainError != null || !mFileStatus.isEmpty();
+ }
+
+ public void setErrorMessage(String error) {
+ mMainError = error;
+ }
+
+ @Nullable
+ public String getErrorMessage() {
+ return mMainError;
+ }
+
+ @NonNull
+ public Multimap<FileStatus, File> getFileStatus() {
+ return mFileStatus;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/FinalPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/FinalPage.java
new file mode 100644
index 0000000..9e976aa
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/FinalPage.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 com.android.ide.eclipse.adt.internal.wizards.exportgradle;
+
+import com.android.ide.eclipse.adt.internal.wizards.exportgradle.ExportStatus.FileStatus;
+import com.google.common.collect.Multimap;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+
+import java.io.File;
+import java.util.Collection;
+
+/**
+ * Final page to review the result of the export.
+ */
+public class FinalPage extends WizardPage {
+
+ private final ProjectSetupBuilder mBuilder;
+ private ExportStatus mStatus;
+
+ private Text mText;
+
+ public FinalPage(ProjectSetupBuilder builder) {
+ super("FinalPage"); //$NON-NLS-1$
+ mBuilder = builder;
+ setPageComplete(true);
+ setTitle(ExportMessages.PageTitle);
+ setDescription(ExportMessages.PageDescription);
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ initializeDialogUnits(parent);
+
+ mText = new Text(parent, SWT.MULTI | SWT.READ_ONLY);
+ mText.setLayoutData(new GridData(GridData.FILL_BOTH
+ | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
+
+ setControl(mText);
+ Dialog.applyDialogFont(parent);
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ if (visible) {
+ mStatus = mBuilder.getStatus();
+ mBuilder.setCanFinish(!mStatus.hasError());
+ mBuilder.setCanGenerate(false);
+
+ StringBuilder sb = new StringBuilder();
+ if (mStatus.hasError()) {
+ sb.append("There was an error!").append("\n\n");
+
+ String errorMsg = mStatus.getErrorMessage();
+ if (errorMsg != null) {
+ sb.append(errorMsg);
+ }
+
+ Multimap<FileStatus, File> fileStatusMap = mStatus.getFileStatus();
+ Collection<File> files = fileStatusMap.values();
+ if (files != null) {
+ sb.append("\n\n").append("Error on files:").append('\n');
+ for (File file : files) {
+ sb.append("\n").append(file.getAbsolutePath());
+ }
+ }
+ } else {
+ sb.append("Export successful.\n\n");
+
+ int count = mBuilder.getModuleCount();
+ if (count > 1) {
+ sb.append(String.format("Exported %s modules", count)).append('\n');
+ sb.append(String.format(
+ "Root folder: %s", mBuilder.getCommonRoot().toOSString()));
+ } else {
+ sb.append("Exported project: ").append(mBuilder.getCommonRoot().toOSString());
+ }
+
+ sb.append("\n\n").append("Choose 'import project' in Android Studio").append('\n');
+ sb.append("and select the following file:").append("\n\t");
+
+ File bGradle = new File(
+ mBuilder.getCommonRoot().toFile(), BuildFileCreator.BUILD_FILE);
+ sb.append(bGradle.getAbsolutePath());
+
+ sb.append("\n\n").append("Do NOT import the Eclipse project itself!");
+ }
+
+ mText.setText(sb.toString());
+ }
+ }
+} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportPage.java
deleted file mode 100644
index 551b709..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportPage.java
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
- *
- * 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 com.android.ide.eclipse.adt.internal.wizards.exportgradle;
-
-import org.eclipse.core.resources.IMarker;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IWorkspaceRoot;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.core.runtime.SubMonitor;
-import org.eclipse.jdt.core.IClasspathEntry;
-import org.eclipse.jdt.core.IJavaModel;
-import org.eclipse.jdt.core.IJavaModelMarker;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IPackageFragmentRoot;
-import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.operation.IRunnableWithProgress;
-import org.eclipse.jface.viewers.CheckStateChangedEvent;
-import org.eclipse.jface.viewers.CheckboxTableViewer;
-import org.eclipse.jface.viewers.ICheckStateListener;
-import org.eclipse.jface.viewers.TableLayout;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.ui.model.WorkbenchContentProvider;
-import org.eclipse.ui.model.WorkbenchLabelProvider;
-
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.google.common.base.Joiner;
-import com.ibm.icu.text.MessageFormat;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * Displays a wizard page that lets the user choose the projects for which to create Gradle build
- * files.
- * <p>
- * Based on {@link org.eclipse.ant.internal.ui.datatransfer.AntBuildfileExportPage}
- */
-public class GradleExportPage extends WizardPage {
- private static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
-
- private static final Comparator<IJavaProject> PROJECT_COMPARATOR =
- new Comparator<IJavaProject>() {
- @Override
- public int compare(IJavaProject o1, IJavaProject o2) {
- return o1.getProject().getName().compareTo(o2.getProject().getName());
- }
- };
-
- private CheckboxTableViewer mTableViewer;
- private List<IJavaProject> mSelectedJavaProjects = new ArrayList<IJavaProject>();
-
- public GradleExportPage() {
- super("GradleExportPage"); //$NON-NLS-1$
- setPageComplete(false);
- setTitle(ExportMessages.PageTitle);
- setDescription(ExportMessages.PageDescription);
- }
-
- @Override
- public void createControl(Composite parent) {
- initializeDialogUnits(parent);
-
- Composite workArea = new Composite(parent, SWT.NONE);
- setControl(workArea);
-
- workArea.setLayout(new GridLayout());
- workArea.setLayoutData(new GridData(GridData.FILL_BOTH
- | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
-
- Label title = new Label(workArea, SWT.NONE);
- title.setText(ExportMessages.SelectProjects);
-
- Composite listComposite = new Composite(workArea, SWT.NONE);
- GridLayout layout = new GridLayout();
- layout.numColumns = 2;
- layout.marginWidth = 0;
- layout.makeColumnsEqualWidth = false;
- listComposite.setLayout(layout);
-
- listComposite.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
- | GridData.GRAB_VERTICAL | GridData.FILL_BOTH));
-
- Table table = new Table(listComposite,
- SWT.CHECK | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
- mTableViewer = new CheckboxTableViewer(table);
- table.setLayout(new TableLayout());
- GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
- data.heightHint = 300;
- table.setLayoutData(data);
- mTableViewer.setContentProvider(new WorkbenchContentProvider() {
- @Override
- public Object[] getElements(Object element) {
- if (element instanceof IJavaProject[]) {
- return (IJavaProject[]) element;
- }
- return null;
- }
- });
- mTableViewer.setLabelProvider(new WorkbenchLabelProvider());
- mTableViewer.addCheckStateListener(new ICheckStateListener() {
- @Override
- public void checkStateChanged(CheckStateChangedEvent event) {
- if (event.getChecked()) {
- mSelectedJavaProjects.add((IJavaProject) event.getElement());
- } else {
- mSelectedJavaProjects.remove(event.getElement());
- }
- updateEnablement();
- }
- });
-
- initializeProjects();
- createSelectionButtons(listComposite);
- setControl(workArea);
- updateEnablement();
- Dialog.applyDialogFont(parent);
- }
-
- /**
- * Creates select all/deselect all buttons.
- */
- private void createSelectionButtons(Composite composite) {
- Composite buttonsComposite = new Composite(composite, SWT.NONE);
- GridLayout layout = new GridLayout();
- layout.marginWidth = 0;
- layout.marginHeight = 0;
- buttonsComposite.setLayout(layout);
-
- buttonsComposite.setLayoutData(new GridData(
- GridData.VERTICAL_ALIGN_BEGINNING));
-
- Button selectAll = new Button(buttonsComposite, SWT.PUSH);
- selectAll.setText(ExportMessages.SelectAll);
- selectAll.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- for (int i = 0; i < mTableViewer.getTable().getItemCount(); i++) {
- mSelectedJavaProjects.add((IJavaProject) mTableViewer.getElementAt(i));
- }
- mTableViewer.setAllChecked(true);
- updateEnablement();
- }
- });
- setButtonLayoutData(selectAll);
-
- Button deselectAll = new Button(buttonsComposite, SWT.PUSH);
- deselectAll.setText(ExportMessages.DeselectAll);
- deselectAll.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- mSelectedJavaProjects.clear();
- mTableViewer.setAllChecked(false);
- updateEnablement();
- }
- });
- setButtonLayoutData(deselectAll);
- }
-
- /**
- * Populates the list with all the eligible projects in the workspace.
- */
- private void initializeProjects() {
- IWorkspaceRoot rootWorkspace = ResourcesPlugin.getWorkspace().getRoot();
- IJavaModel javaModel = JavaCore.create(rootWorkspace);
- IJavaProject[] javaProjects;
- try {
- javaProjects = javaModel.getJavaProjects();
- } catch (JavaModelException e) {
- javaProjects = new IJavaProject[0];
- }
- mTableViewer.setInput(javaProjects);
- // Check any necessary projects
- if (mSelectedJavaProjects != null) {
- mTableViewer.setCheckedElements(mSelectedJavaProjects.toArray(
- new IJavaProject[mSelectedJavaProjects.size()]));
- }
- }
-
- /**
- * Enables/disables the finish button on the wizard and displays error messages as needed.
- */
- private void updateEnablement() {
- boolean complete = true;
- if (mSelectedJavaProjects.size() == 0) {
- setErrorMessage(ExportMessages.NoProjectsError);
- complete = false;
- }
- List<String> cyclicProjects;
- try {
- cyclicProjects = getCyclicProjects(getProjects(false));
- if (cyclicProjects.size() > 0) {
- setErrorMessage(MessageFormat.format(ExportMessages.CyclicProjectsError,
- new Object[] { Joiner.on(", ").join(cyclicProjects) })); //$NON-NLS-1$
- complete = false;
- }
- } catch (CoreException e) {}
- if (complete) {
- setErrorMessage(null);
- }
- setPageComplete(complete);
- }
-
- @Override
- public void setVisible(boolean visible) {
- super.setVisible(visible);
- if (visible) {
- mTableViewer.getTable().setFocus();
- }
- }
-
- /**
- * Converts Eclipse Java projects to Gradle build files. Displays error dialogs.
- */
- public boolean generateBuildfiles() {
- setErrorMessage(null);
- final List<String> projectNames = new ArrayList<String>();
- final Set<IJavaProject> projects;
- try {
- projects = getProjects(true);
- if (projects.size() == 0) {
- return false;
- }
- } catch (JavaModelException e) {
- AdtPlugin.log(e, null);
- setErrorMessage(MessageFormat.format(
- ExportMessages.ExportFailedError, new Object[] { e.toString() }));
- return false;
- }
- IRunnableWithProgress runnable = new IRunnableWithProgress() {
- @Override
- public void run(IProgressMonitor pm) throws InterruptedException {
- SubMonitor localmonitor = SubMonitor.convert(pm, ExportMessages.StatusMessage,
- projects.size());
- Exception problem = null;
- try {
- projectNames.addAll(BuildFileCreator.createBuildFiles(projects, getShell(),
- localmonitor.newChild(projects.size())));
- } catch (JavaModelException e) {
- problem = e;
- } catch (IOException e) {
- problem = e;
- } catch (CoreException e) {
- problem = e;
- }
-
- if (problem != null) {
- AdtPlugin.log(problem, null);
- setErrorMessage(MessageFormat.format(ExportMessages.ExportFailedError,
- new Object[] { problem.toString() }));
- }
- }
- };
-
- try {
- getContainer().run(false, false, runnable);
- } catch (InvocationTargetException e) {
- AdtPlugin.log(e, null);
- return false;
- } catch (InterruptedException e) {
- AdtPlugin.log(e, null);
- return false;
- }
- if (getErrorMessage() != null) {
- return false;
- }
- return true;
- }
-
- /**
- * Get projects to write buildfiles for. Opens confirmation dialog.
- *
- * @param displayConfirmation if set to true a dialog prompts for
- * confirmation before overwriting files
- * @return set of project names
- */
- private Set<IJavaProject> getProjects(boolean displayConfirmation) throws JavaModelException {
- // collect all projects to create buildfiles for
- Set<IJavaProject> projects = new TreeSet<IJavaProject>(PROJECT_COMPARATOR);
- for (IJavaProject javaProject : mSelectedJavaProjects) {
- projects.addAll(getClasspathProjectsRecursive(javaProject));
- projects.add(javaProject);
- }
-
- // confirm overwrite
- List<String> confirmOverwrite = getConfirmOverwriteSet(projects);
- if (displayConfirmation && confirmOverwrite.size() > 0) {
- String message = ExportMessages.ConfirmOverwrite + NEWLINE +
- Joiner.on(NEWLINE).join(confirmOverwrite);
- if (!MessageDialog.openQuestion(getShell(),
- ExportMessages.ConfirmOverwriteTitle, message)) {
- return new TreeSet<IJavaProject>(PROJECT_COMPARATOR);
- }
- }
- return projects;
- }
-
- /**
- * Returns given projects that have cyclic dependencies.
- *
- * @param javaProjects list of IJavaProject objects
- * @return set of project names
- */
- private List<String> getCyclicProjects(Set<IJavaProject> projects) throws CoreException {
-
- List<String> cyclicProjects = new ArrayList<String>();
- for (IJavaProject javaProject : projects) {
- if (hasCyclicDependency(javaProject)) {
- cyclicProjects.add(javaProject.getProject().getName());
- }
- }
- return cyclicProjects;
- }
-
- /**
- * Check if given project has a cyclic dependency.
- * <p>
- * See {@link org.eclipse.jdt.core.tests.model.ClasspathTests.numberOfCycleMarkers}
- */
- public static boolean hasCyclicDependency(IJavaProject javaProject)
- throws CoreException {
- IMarker[] markers = javaProject.getProject().findMarkers(
- IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false,
- IResource.DEPTH_ONE);
- for (int i = 0; i < markers.length; i++) {
- IMarker marker = markers[i];
- String cycleAttr = (String) marker
- .getAttribute(IJavaModelMarker.CYCLE_DETECTED);
- if (cycleAttr != null && cycleAttr.equals("true")) { //$NON-NLS-1$
- return true;
- }
- }
- return false;
- }
-
- /**
- * Get list of projects which have already a buildfile.
- *
- * @param javaProjects list of IJavaProject objects
- * @return set of project names
- */
- private List<String> getConfirmOverwriteSet(Set<IJavaProject> javaProjects)
- {
- List<String> result = new ArrayList<String>(javaProjects.size());
- for (IJavaProject project : javaProjects) {
- String projectRoot = project.getResource().getLocation().toString();
- if (new File(projectRoot, "build.gradle").exists()) {
- result.add(project.getProject().getName());
- }
- }
- return result;
- }
-
- /**
- * Get Java project from resource.
- */
- private static IJavaProject getJavaProjectByName(String name) {
- try {
- IProject project = ResourcesPlugin.getWorkspace().getRoot()
- .getProject(name);
- if (project.exists()) {
- return JavaCore.create(project);
- }
- } catch (IllegalArgumentException iae) {
- }
- return null;
- }
-
- /**
- * Get Java project for given root.
- */
- private static IJavaProject getJavaProject(String root) {
- IPath path = new Path(root);
- if (path.segmentCount() == 1) {
- return getJavaProjectByName(root);
- }
- IResource resource = ResourcesPlugin.getWorkspace().getRoot()
- .findMember(path);
- if (resource != null && resource.getType() == IResource.PROJECT) {
- if (resource.exists()) {
- return (IJavaProject) JavaCore.create(resource);
- }
- }
- return null;
- }
-
- /**
- * Get for given project all directly and indirectly dependent projects.
- *
- * @return set of IJavaProject objects
- */
- private static List<IJavaProject> getClasspathProjectsRecursive(IJavaProject project)
- throws JavaModelException {
- LinkedList<IJavaProject> result = new LinkedList<IJavaProject>();
- getClasspathProjectsRecursive(project, result);
- return result;
- }
-
- private static void getClasspathProjectsRecursive(IJavaProject project,
- LinkedList<IJavaProject> result) throws JavaModelException {
- List<IJavaProject> projects = new ArrayList<IJavaProject>();
- IClasspathEntry entries[] = project.getRawClasspath();
- for (IClasspathEntry classpathEntry : entries) {
- if (classpathEntry.getContentKind() == IPackageFragmentRoot.K_SOURCE
- && classpathEntry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
- // found required project on build path
- String subProjectRoot = classpathEntry.getPath().toString();
- IJavaProject subProject = getJavaProject(subProjectRoot);
- // is project available in workspace
- if (subProject != null) {
- projects.add(subProject);
- }
- }
- }
- for (IJavaProject javaProject : projects) {
- if (!result.contains(javaProject)) {
- result.addFirst(javaProject);
- getClasspathProjectsRecursive(javaProject, result); // recursion
- }
- }
- }
-} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportWizard.java
index 5ff4c53..29f8802 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleExportWizard.java
@@ -16,26 +16,50 @@
package com.android.ide.eclipse.adt.internal.wizards.exportgradle;
+import com.android.ide.eclipse.adt.AdtPlugin;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.ui.IExportWizard;
import org.eclipse.ui.IWorkbench;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+
public class GradleExportWizard extends Wizard implements IExportWizard {
- private GradleExportPage mMainPage;
+
+ private ProjectSetupBuilder mBuilder = new ProjectSetupBuilder();
+
+ private ProjectSelectionPage mFirstPage;
+ private ConfirmationPage mSecondPage;
+ private FinalPage mFinalPage;
/**
* Creates buildfile.
*/
@Override
public boolean performFinish() {
- return mMainPage.generateBuildfiles();
+ if (mBuilder.canGenerate()) {
+ generateBuildfiles(mSecondPage);
+ getContainer().showPage(mFinalPage);
+ return false;
+ }
+
+ return true;
}
@Override
public void addPages() {
- mMainPage = new GradleExportPage();
- addPage(mMainPage);
+ mFirstPage = new ProjectSelectionPage(mBuilder);
+ addPage(mFirstPage);
+ mSecondPage = new ConfirmationPage(mBuilder);
+ addPage(mSecondPage);
+ mFinalPage = new FinalPage(mBuilder);
+ addPage(mFinalPage);
}
@Override
@@ -43,4 +67,44 @@ public class GradleExportWizard extends Wizard implements IExportWizard {
setWindowTitle(ExportMessages.WindowTitle);
setNeedsProgressMonitor(true);
}
+
+ @Override
+ public boolean canFinish() {
+ return mBuilder.canFinish() || mBuilder.canGenerate();
+ }
+
+ /**
+ * Converts Eclipse Java projects to Gradle build files. Displays error dialogs.
+ */
+ public boolean generateBuildfiles(final WizardPage page) {
+ IRunnableWithProgress runnable = new IRunnableWithProgress() {
+ @Override
+ public void run(IProgressMonitor pm) throws InterruptedException {
+ Collection<GradleModule> modules = mBuilder.getModules();
+ final int count = modules.size();
+
+ SubMonitor localmonitor = SubMonitor.convert(pm, ExportMessages.StatusMessage,
+ count);
+ BuildFileCreator.createBuildFiles(
+ mBuilder,
+ page.getShell(),
+ localmonitor.newChild(count));
+ }
+ };
+
+ try {
+ getContainer().run(false, false, runnable);
+ } catch (InvocationTargetException e) {
+ AdtPlugin.log(e, null);
+ return false;
+ } catch (InterruptedException e) {
+ AdtPlugin.log(e, null);
+ return false;
+ }
+ if (page.getErrorMessage() != null) {
+ return false;
+ }
+ return true;
+ }
+
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleModule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleModule.java
new file mode 100644
index 0000000..684f03b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/GradleModule.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 com.android.ide.eclipse.adt.internal.wizards.exportgradle;
+
+import com.android.annotations.NonNull;
+import com.google.common.collect.Lists;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jdt.core.IJavaProject;
+
+import java.util.List;
+
+/**
+ * A configured Gradle module for export. This includes gradle path, dependency, type, etc...
+ */
+public class GradleModule {
+
+ @NonNull
+ private final IJavaProject mJavaProject;
+
+ private String mPath;
+ private Type mType;
+
+ private final List<GradleModule> mDependencies = Lists.newArrayList();
+
+ public static enum Type { ANDROID, JAVA };
+
+ GradleModule(@NonNull IJavaProject javaProject) {
+ mJavaProject = javaProject;
+ }
+
+ @NonNull
+ public IJavaProject getJavaProject() {
+ return mJavaProject;
+ }
+
+ @NonNull
+ public IProject getProject() {
+ return mJavaProject.getProject();
+ }
+
+ boolean isConfigured() {
+ return mType != null;
+ }
+
+ public void setType(Type type) {
+ mType = type;
+ }
+
+ public Type getType() {
+ return mType;
+ }
+
+ public void addDependency(GradleModule module) {
+ mDependencies.add(module);
+ }
+
+ public List<GradleModule> getDependencies() {
+ return mDependencies;
+ }
+
+ public void setPath(String path) {
+ mPath = path;
+ }
+
+ public String getPath() {
+ return mPath;
+ }
+
+ @Override
+ public String toString() {
+ return "GradleModule [mJavaProject=" + mJavaProject + ", mPath=" + mPath + ", mType="
+ + mType + ", mDependencies=" + mDependencies + "]";
+ }
+}
+
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSelectionPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSelectionPage.java
new file mode 100644
index 0000000..81c7a73
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSelectionPage.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 com.android.ide.eclipse.adt.internal.wizards.exportgradle;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.ibm.icu.text.MessageFormat;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaModelMarker;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.TableLayout;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Displays a wizard page that lets the user choose the projects for which to create Gradle build
+ * files.
+ * <p>
+ * Based on {@link org.eclipse.ant.internal.ui.datatransfer.AntBuildfileExportPage}
+ */
+public class ProjectSelectionPage extends WizardPage {
+
+ private final ProjectSetupBuilder mBuilder;
+ private CheckboxTableViewer mTableViewer;
+ private List<IJavaProject> mSelectedJavaProjects = Lists.newArrayList();
+
+ public ProjectSelectionPage(ProjectSetupBuilder builder) {
+ super("GradleExportPage"); //$NON-NLS-1$
+ mBuilder = builder;
+ setPageComplete(false);
+ setTitle(ExportMessages.PageTitle);
+ setDescription(ExportMessages.PageDescription);
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ initializeDialogUnits(parent);
+
+ Composite workArea = new Composite(parent, SWT.NONE);
+ setControl(workArea);
+
+ workArea.setLayout(new GridLayout());
+ workArea.setLayoutData(new GridData(GridData.FILL_BOTH
+ | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
+
+ Label title = new Label(workArea, SWT.NONE);
+ title.setText(ExportMessages.SelectProjects);
+
+ Composite listComposite = new Composite(workArea, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ layout.marginWidth = 0;
+ layout.makeColumnsEqualWidth = false;
+ listComposite.setLayout(layout);
+
+ listComposite.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
+ | GridData.GRAB_VERTICAL | GridData.FILL_BOTH));
+
+ Table table = new Table(listComposite,
+ SWT.CHECK | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+ mTableViewer = new CheckboxTableViewer(table);
+ table.setLayout(new TableLayout());
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.heightHint = 300;
+ table.setLayoutData(data);
+ mTableViewer.setContentProvider(new WorkbenchContentProvider() {
+ @Override
+ public Object[] getElements(Object element) {
+ if (element instanceof IJavaProject[]) {
+ return (IJavaProject[]) element;
+ }
+ return null;
+ }
+ });
+ mTableViewer.setLabelProvider(new WorkbenchLabelProvider());
+ mTableViewer.addCheckStateListener(new ICheckStateListener() {
+ @Override
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ if (event.getChecked()) {
+ mSelectedJavaProjects.add((IJavaProject) event.getElement());
+ } else {
+ mSelectedJavaProjects.remove(event.getElement());
+ }
+ updateEnablement();
+ }
+ });
+
+ initializeProjects();
+ createSelectionButtons(listComposite);
+ setControl(workArea);
+ updateEnablement();
+ Dialog.applyDialogFont(parent);
+ }
+
+ /**
+ * Creates select all/deselect all buttons.
+ */
+ private void createSelectionButtons(Composite composite) {
+ Composite buttonsComposite = new Composite(composite, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ buttonsComposite.setLayout(layout);
+
+ buttonsComposite.setLayoutData(new GridData(
+ GridData.VERTICAL_ALIGN_BEGINNING));
+
+ Button selectAll = new Button(buttonsComposite, SWT.PUSH);
+ selectAll.setText(ExportMessages.SelectAll);
+ selectAll.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ for (int i = 0; i < mTableViewer.getTable().getItemCount(); i++) {
+ mSelectedJavaProjects.add((IJavaProject) mTableViewer.getElementAt(i));
+ }
+ mTableViewer.setAllChecked(true);
+ updateEnablement();
+ }
+ });
+ setButtonLayoutData(selectAll);
+
+ Button deselectAll = new Button(buttonsComposite, SWT.PUSH);
+ deselectAll.setText(ExportMessages.DeselectAll);
+ deselectAll.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ mSelectedJavaProjects.clear();
+ mTableViewer.setAllChecked(false);
+ updateEnablement();
+ }
+ });
+ setButtonLayoutData(deselectAll);
+ }
+
+ /**
+ * Populates the list with all the eligible projects in the workspace.
+ */
+ private void initializeProjects() {
+ IWorkspaceRoot rootWorkspace = ResourcesPlugin.getWorkspace().getRoot();
+ IJavaModel javaModel = JavaCore.create(rootWorkspace);
+ IJavaProject[] javaProjects;
+ try {
+ javaProjects = javaModel.getJavaProjects();
+ } catch (JavaModelException e) {
+ javaProjects = new IJavaProject[0];
+ }
+ mTableViewer.setInput(javaProjects);
+ // Check any necessary projects
+ if (mSelectedJavaProjects != null) {
+ mTableViewer.setCheckedElements(mSelectedJavaProjects.toArray(
+ new IJavaProject[mSelectedJavaProjects.size()]));
+ }
+ }
+
+ /**
+ * Enables/disables the finish button on the wizard and displays error messages as needed.
+ */
+ private void updateEnablement() {
+ String error = null;
+ try {
+ if (mSelectedJavaProjects.size() == 0) {
+ error = ExportMessages.NoProjectsError;
+ return;
+ }
+
+ List<String> cyclicProjects;
+ try {
+ cyclicProjects = getCyclicProjects(mSelectedJavaProjects);
+ if (cyclicProjects.size() > 0) {
+ error = MessageFormat.format(ExportMessages.CyclicProjectsError,
+ new Object[] { Joiner.on(", ").join(cyclicProjects) }); //$NON-NLS-1$
+ return;
+ }
+
+ error = mBuilder.setProject(mSelectedJavaProjects);
+ if (error != null) {
+ return;
+ }
+
+ } catch (CoreException ignored) {
+ // TODO: do something?
+ }
+ } finally {
+ setErrorMessage(error);
+ setPageComplete(error == null);
+ getContainer().updateButtons();
+ }
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ if (visible) {
+ mTableViewer.getTable().setFocus();
+ mBuilder.setCanFinish(false);
+ mBuilder.setCanGenerate(false);
+ }
+ }
+
+ /**
+ * Returns given projects that have cyclic dependencies.
+ *
+ * @param javaProjects list of IJavaProject objects
+ * @return set of project names
+ */
+ private List<String> getCyclicProjects(List<IJavaProject> projects) throws CoreException {
+
+ List<String> cyclicProjects = new ArrayList<String>();
+ for (IJavaProject javaProject : projects) {
+ if (hasCyclicDependency(javaProject)) {
+ cyclicProjects.add(javaProject.getProject().getName());
+ }
+ }
+ return cyclicProjects;
+ }
+
+ /**
+ * Check if given project has a cyclic dependency.
+ * <p>
+ * See {@link org.eclipse.jdt.core.tests.model.ClasspathTests.numberOfCycleMarkers}
+ */
+ private static boolean hasCyclicDependency(IJavaProject javaProject)
+ throws CoreException {
+ IMarker[] markers = javaProject.getProject().findMarkers(
+ IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false,
+ IResource.DEPTH_ONE);
+ for (IMarker marker : markers) {
+ String cycleAttr = (String) marker
+ .getAttribute(IJavaModelMarker.CYCLE_DETECTED);
+ if (cycleAttr != null && cycleAttr.equals("true")) { //$NON-NLS-1$
+ return true;
+ }
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java
new file mode 100644
index 0000000..0df28ea
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ProjectSetupBuilder.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 com.android.ide.eclipse.adt.internal.wizards.exportgradle;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Class to setup the project and its modules.
+ */
+public class ProjectSetupBuilder {
+
+ private final static class InternalException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ InternalException(String message) {
+ super(message);
+ }
+ }
+
+ private boolean mCanFinish = false;
+ private boolean mCanGenerate = false;
+ private final List<GradleModule> mOriginalModules = Lists.newArrayList();
+ private final Map<IJavaProject, GradleModule> mModules = Maps.newHashMap();
+ private IPath mCommonRoot;
+ private ExportStatus mStatus;
+
+ public ProjectSetupBuilder() {
+
+ }
+
+ public void setCanGenerate(boolean generate) {
+ mCanGenerate = generate;
+ }
+
+ public void setCanFinish(boolean canFinish) {
+ mCanFinish = canFinish;
+ }
+
+ public boolean canFinish() {
+ return mCanFinish;
+ }
+
+ public boolean canGenerate() {
+ return mCanGenerate;
+ }
+
+ public void setStatus(ExportStatus status) {
+ mStatus = status;
+ }
+
+ public ExportStatus getStatus() {
+ return mStatus;
+ }
+
+ @NonNull
+ public String setProject(@NonNull List<IJavaProject> selectedProjects)
+ throws CoreException {
+ mModules.clear();
+
+ // build a list of all projects that must be included. This is in case
+ // some dependencies have not been included in the selected projects. We also include
+ // parent projects so that the full multi-project setup is correct.
+ // Note that if two projects are selected that are not related, both will be added
+ // in the same multi-project anyway.
+ try {
+ for (IJavaProject javaProject : selectedProjects) {
+ GradleModule module;
+
+ if (javaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT)) {
+ module = processAndroidProject(javaProject);
+ } else {
+ module = processJavaProject(javaProject);
+ }
+
+ mOriginalModules.add(module);
+ }
+
+ Collection<GradleModule> modules = mModules.values();
+ computeRootAndPaths(modules);
+
+ return null;
+ } catch (InternalException e) {
+ return e.getMessage();
+ }
+ }
+
+ @NonNull
+ public Collection<GradleModule> getModules() {
+ return mModules.values();
+ }
+
+ public int getModuleCount() {
+ return mModules.size();
+ }
+
+ @Nullable
+ public IPath getCommonRoot() {
+ return mCommonRoot;
+ }
+
+ @Nullable
+ public GradleModule getModule(IJavaProject javaProject) {
+ return mModules.get(javaProject);
+ }
+
+ public boolean isOriginalProject(@NonNull IJavaProject javaProject) {
+ GradleModule module = mModules.get(javaProject);
+ return mOriginalModules.contains(module);
+ }
+
+ @NonNull
+ public List<GradleModule> getOriginalModules() {
+ return mOriginalModules;
+ }
+
+ @Nullable
+ public List<GradleModule> getShortestDependencyTo(GradleModule module) {
+ return findModule(module, mOriginalModules);
+ }
+
+ @Nullable
+ public List<GradleModule> findModule(GradleModule toFind, GradleModule rootModule) {
+ if (toFind == rootModule) {
+ List<GradleModule> list = Lists.newArrayList();
+ list.add(toFind);
+ return list;
+ }
+
+ List<GradleModule> shortestChain = findModule(toFind, rootModule.getDependencies());
+
+ if (shortestChain != null) {
+ shortestChain.add(0, rootModule);
+ }
+
+ return shortestChain;
+ }
+
+ @Nullable
+ public List<GradleModule> findModule(GradleModule toFind, List<GradleModule> modules) {
+ List<GradleModule> currentChain = null;
+
+ for (GradleModule child : modules) {
+ List<GradleModule> newChain = findModule(toFind, child);
+ if (currentChain == null) {
+ currentChain = newChain;
+ } else if (newChain != null) {
+ if (currentChain.size() > newChain.size()) {
+ currentChain = newChain;
+ }
+ }
+ }
+
+ return currentChain;
+ }
+
+ @NonNull
+ private GradleModule processAndroidProject(@NonNull IJavaProject javaProject)
+ throws InternalException, CoreException {
+
+ // get/create the module
+ GradleModule module = createModuleOnDemand(javaProject);
+ if (module.isConfigured()) {
+ return module;
+ }
+
+ module.setType(GradleModule.Type.ANDROID);
+
+ ProjectState projectState = Sdk.getProjectState(javaProject.getProject());
+ assert projectState != null;
+
+ // add library project dependencies
+ List<LibraryState> libraryProjects = projectState.getLibraries();
+ for (LibraryState libraryState : libraryProjects) {
+ ProjectState libProjectState = libraryState.getProjectState();
+ if (libProjectState != null) {
+ IJavaProject javaLib = getJavaProject(libProjectState);
+ if (javaLib != null) {
+ GradleModule libModule = processAndroidProject(javaLib);
+ module.addDependency(libModule);
+ } else {
+ throw new InternalException(String.format(
+ "Project %1$s is missing. Needed by %2$s.\n" +
+ "Make sure all dependencies are opened.",
+ libraryState.getRelativePath(),
+ javaProject.getProject().getName()));
+ }
+ } else {
+ throw new InternalException(String.format(
+ "Project %1$s is missing. Needed by %2$s.\n" +
+ "Make sure all dependencies are opened.",
+ libraryState.getRelativePath(),
+ javaProject.getProject().getName()));
+ }
+ }
+
+ // add java project dependencies
+ List<IJavaProject> javaDepProjects = getReferencedProjects(javaProject);
+ for (IJavaProject javaDep : javaDepProjects) {
+ GradleModule libModule = processJavaProject(javaDep);
+ module.addDependency(libModule);
+ }
+
+ return module;
+ }
+
+ @NonNull
+ private GradleModule processJavaProject(@NonNull IJavaProject javaProject)
+ throws InternalException, CoreException {
+ // get/create the module
+ GradleModule module = createModuleOnDemand(javaProject);
+
+ if (module.isConfigured()) {
+ return module;
+ }
+
+ module.setType(GradleModule.Type.JAVA);
+
+ // add java project dependencies
+ List<IJavaProject> javaDepProjects = getReferencedProjects(javaProject);
+ for (IJavaProject javaDep : javaDepProjects) {
+ // Java project should not reference Android project!
+ if (javaDep.getProject().hasNature(AdtConstants.NATURE_DEFAULT)) {
+ throw new InternalException(String.format(
+ "Java project %1$s depends on Android project %2$s!\n" +
+ "This is not a valid dependency",
+ javaProject.getProject().getName(), javaDep.getProject().getName()));
+ }
+ GradleModule libModule = processJavaProject(javaDep);
+ module.addDependency(libModule);
+ }
+
+ return module;
+ }
+
+ private void computeRootAndPaths(Collection<GradleModule> modules) throws InternalException {
+ // compute the common root.
+ mCommonRoot = determineCommonRoot(modules);
+
+ // compute all the relative paths.
+ for (GradleModule module : modules) {
+ String path = getGradlePath(module.getJavaProject().getProject().getLocation(),
+ mCommonRoot);
+
+ module.setPath(path);
+ }
+ }
+
+ /**
+ * Finds the common parent directory shared by this project and all its dependencies.
+ * If there's only one project, returns the single project's folder.
+ * @throws InternalException
+ */
+ @NonNull
+ private static IPath determineCommonRoot(Collection<GradleModule> modules)
+ throws InternalException {
+ IPath commonRoot = null;
+ for (GradleModule module : modules) {
+ if (commonRoot == null) {
+ commonRoot = module.getJavaProject().getProject().getLocation();
+ } else {
+ commonRoot = findCommonRoot(commonRoot,
+ module.getJavaProject().getProject().getLocation());
+ }
+ }
+
+ return commonRoot;
+ }
+
+ /**
+ * Converts the given path to be relative to the given root path, and converts it to
+ * Gradle project notation, such as is used in the settings.gradle file.
+ */
+ @NonNull
+ private static String getGradlePath(IPath path, IPath root) {
+ IPath relativePath = path.makeRelativeTo(root);
+ String relativeString = relativePath.toOSString();
+ return ":" + relativeString.replaceAll(Pattern.quote(File.separator), ":"); //$NON-NLS-1$
+ }
+
+ /**
+ * Given two IPaths, finds the parent directory of both of them.
+ * @throws InternalException
+ */
+ @NonNull
+ private static IPath findCommonRoot(@NonNull IPath path1, @NonNull IPath path2)
+ throws InternalException {
+ if (path1.getDevice() != null && path1.getDevice().equals(path2.getDevice())) {
+ throw new InternalException(
+ "Different modules have been detected on different drives.\n" +
+ "This prevents finding a common root to all modules.");
+ }
+
+ IPath result = path1.uptoSegment(0);
+
+ final int count = Math.min(path1.segmentCount(), path2.segmentCount());
+ for (int i = 0; i < count; i++) {
+ if (path1.segment(i).equals(path2.segment(i))) {
+ result = result.append(Path.SEPARATOR + path2.segment(i));
+ }
+ }
+ return result;
+ }
+
+ @Nullable
+ private IJavaProject getJavaProject(ProjectState projectState) {
+ try {
+ return BaseProjectHelper.getJavaProject(projectState.getProject());
+ } catch (CoreException e) {
+ return null;
+ }
+ }
+
+ @NonNull
+ private GradleModule createModuleOnDemand(@NonNull IJavaProject javaProject) {
+ GradleModule module = mModules.get(javaProject);
+ if (module == null) {
+ module = new GradleModule(javaProject);
+ mModules.put(javaProject, module);
+ }
+
+ return module;
+ }
+
+ @NonNull
+ private static List<IJavaProject> getReferencedProjects(IJavaProject javaProject)
+ throws JavaModelException, InternalException {
+
+ List<IJavaProject> projects = Lists.newArrayList();
+
+ IClasspathEntry entries[] = javaProject.getRawClasspath();
+ for (IClasspathEntry classpathEntry : entries) {
+ if (classpathEntry.getContentKind() == IPackageFragmentRoot.K_SOURCE
+ && classpathEntry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
+ // found required project on build path
+ String subProjectRoot = classpathEntry.getPath().toString();
+ IJavaProject subProject = getJavaProject(subProjectRoot);
+ // is project available in workspace?
+ if (subProject != null) {
+ projects.add(subProject);
+ } else {
+ throw new InternalException(String.format(
+ "Project '%s' is missing project dependency '%s' in Eclipse workspace.\n" +
+ "Make sure all dependencies are opened.",
+ javaProject.getProject().getName(),
+ classpathEntry.getPath().toString()));
+ }
+ }
+ }
+
+ return projects;
+ }
+
+ /**
+ * Get Java project for given root.
+ */
+ @Nullable
+ private static IJavaProject getJavaProject(String root) {
+ IPath path = new Path(root);
+ if (path.segmentCount() == 1) {
+ return getJavaProjectByName(root);
+ }
+ IResource resource = ResourcesPlugin.getWorkspace().getRoot()
+ .findMember(path);
+ if (resource != null && resource.getType() == IResource.PROJECT) {
+ if (resource.exists()) {
+ return (IJavaProject) JavaCore.create(resource);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get Java project from resource.
+ */
+ private static IJavaProject getJavaProjectByName(String name) {
+ try {
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
+ if (project.exists()) {
+ return JavaCore.create(project);
+ }
+ } catch (IllegalArgumentException iae) {
+ }
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportGradleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportGradleTest.java
index 2cbde5a..f5cc58b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportGradleTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/wizards/exportgradle/ExportGradleTest.java
@@ -17,17 +17,6 @@ package com.android.ide.eclipse.adt.internal.wizards.exportgradle;
import static com.android.sdklib.internal.project.ProjectProperties.PROPERTY_LIBRARY;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.QualifiedName;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jface.operation.IRunnableContext;
-import org.eclipse.jface.operation.IRunnableWithProgress;
-
import com.android.SdkConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
@@ -40,11 +29,22 @@ import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardS
import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode;
import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
import com.google.common.base.Charsets;
-import com.google.common.collect.Sets;
import com.google.common.io.Files;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+
import java.io.File;
import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
public class ExportGradleTest extends AdtProjectTest {
private QualifiedName ERROR_KEY = new QualifiedName(AdtPlugin.PLUGIN_ID, "JobErrorKey");
@@ -64,12 +64,17 @@ public class ExportGradleTest extends AdtProjectTest {
public void testSimpleAndroidApp() throws Throwable {
IProject project = getProject("simple-app");
final IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
+
+ final ProjectSetupBuilder builder = new ProjectSetupBuilder();
+ builder.setProject(Collections.singletonList(javaProject));
+
Job job = new Job("Validate project") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
- BuildFileCreator.createBuildFiles(Sets.newHashSet(javaProject), null, null);
- File buildfile = new File(javaProject.getResource().getLocation().toString(), "build.gradle");
+ BuildFileCreator.createBuildFiles(builder, null, monitor);
+ File buildfile = new File(javaProject.getResource().getLocation().toString(),
+ BuildFileCreator.BUILD_FILE);
assertTrue(buildfile.exists());
String contents = Files.toString(buildfile, Charsets.UTF_8);
String expectedContents =
@@ -134,14 +139,19 @@ public class ExportGradleTest extends AdtProjectTest {
if (projectProp != null) {
projectProp.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
}
+
Job job = new Job("Validate project") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
- BuildFileCreator.createBuildFiles(Sets.newHashSet(javaProject), null, null);
- File buildfile = new File(javaProject.getResource().getLocation().toString(), "build.gradle");
+ final ProjectSetupBuilder builder = new ProjectSetupBuilder();
+ builder.setProject(Collections.singletonList(javaProject));
+
+ BuildFileCreator.createBuildFiles(builder, null, monitor);
+ File buildfile = new File(javaProject.getResource().getLocation().toString(),
+ BuildFileCreator.BUILD_FILE);
assertTrue(buildfile.exists());
String contents = Files.toString(buildfile, Charsets.UTF_8);
String expectedContents =
@@ -199,7 +209,11 @@ public class ExportGradleTest extends AdtProjectTest {
public void testPlainJavaProject() throws Throwable {
IProject project = getJavaProject("simple-java");
final IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
- BuildFileCreator.createBuildFiles(Sets.newHashSet(javaProject), null, null);
+
+ final ProjectSetupBuilder builder = new ProjectSetupBuilder();
+ builder.setProject(Collections.singletonList(javaProject));
+
+ BuildFileCreator.createBuildFiles(builder, null, null);
Job job = new Job("Validate project") {
@Override
protected IStatus run(IProgressMonitor monitor) {
@@ -273,8 +287,6 @@ public class ExportGradleTest extends AdtProjectTest {
return validateProjectExists(name);
}
-
-
/**
* Compares two strings, disregarding whitespace. This makes the test less brittle with respect
* to insignificant changes.