OSDN Git Service

Remove hard-coded source folder name for library projects in ADT.
authorXavier Ducrohet <xav@android.com>
Fri, 25 Jun 2010 01:31:57 +0000 (18:31 -0700)
committerXavier Ducrohet <xav@android.com>
Fri, 25 Jun 2010 22:18:49 +0000 (15:18 -0700)
Previously, the library mechanism expected the library to have a single source folder called 'src'.

There can now be more than one source folder (but the 'gen' folder of the library is always excluded),
and they don't need to be directly under the project folder. For example src/java is supported.

Change-Id: If139bd69cb720c331a1e5d1543a0cd4a91b8d675

eclipse/changes.txt
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BaseBuilder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java

index 34ad261..5cf67e2 100644 (file)
@@ -1,5 +1,6 @@
 0.9.8:
 - Fixed issue with library project names containing characters that aren't compatible with Eclipse path variable. The link between the main project and the library would fail to create.
+- Added support for library projects that don't have a source folder called "src". There is now support for any number of source folder, with no name restriction. They can even be in sub folder such as "src/java".
 - added support for new resource qualifiers: car/desk, night/notnight and navexposed/navhidden
 
 0.9.7:
index 7211ac4..32092f8 100644 (file)
@@ -365,28 +365,6 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
     }
 
     /**
-     * Saves the path of a resource into the persistent storate of the project.
-     * @param propertyName the name of the property. The id of the plugin is added to this string.
-     * @param resource the resource which path is saved.
-     * @return true if the save succeeded
-     */
-    protected boolean saveProjectResourceProperty(String propertyName, IResource resource) {
-        return ProjectHelper.saveResourceProperty(getProject(), propertyName, resource);
-    }
-
-    /**
-     * Loads the path of a resource from the persistent storage of the project, and returns the
-     * corresponding IResource object.
-     * @param propertyName the name of the property. The id of the plugin is added to this string.
-     * @return The corresponding IResource object (or children interface) or null
-     */
-    protected IResource loadProjectResourceProperty(String propertyName) {
-        IProject project = getProject();
-        return ProjectHelper.loadResourceProperty(project, propertyName);
-    }
-
-
-    /**
      * Aborts the build if the SDK/project setups are broken. This does not
      * display any errors.
      *
index 44165ef..2eb5dd2 100644 (file)
@@ -87,6 +87,16 @@ public final class BaseProjectHelper {
     }
 
     /**
+     * returns a list of source classpath for a specified project
+     * @param project
+     * @return a list of path relative to the workspace root.
+     */
+    public static ArrayList<IPath> getSourceClasspaths(IProject project) {
+        IJavaProject javaProject = JavaCore.create(project);
+        return getSourceClasspaths(javaProject);
+    }
+
+    /**
      * Adds a marker to a file on a specific line. This methods catches thrown
      * {@link CoreException}, and returns null instead.
      * @param resource the resource to be marked
index ba0346c..b7ba2fe 100644 (file)
@@ -18,7 +18,6 @@ package com.android.ide.eclipse.adt.internal.project;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AndroidConstants;
-import com.android.ide.eclipse.adt.internal.project.ProjectState.LibraryState;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.sdklib.SdkConstants;
 
@@ -62,14 +61,11 @@ public class FolderDecorator implements ILightweightLabelDecorator {
                             doDecoration(decoration, null);
                         } else if (name.equals(SdkConstants.FD_GEN_SOURCES)) {
                             doDecoration(decoration, " [Generated Java Files]");
-                      } else if (name.equals(SdkConstants.FD_NATIVE_LIBS)) {
-                          doDecoration(decoration, null);
-                      } else if (folder.isLinked()) {
-                          ProjectState state = Sdk.getProjectState(project);
-                          LibraryState lib = state.getLibrary(folder.getName());
-                          if (lib != null) {
-                              doDecoration(decoration, " [Android Library]");
-                          }
+                        } else if (name.equals(SdkConstants.FD_NATIVE_LIBS)) {
+                            doDecoration(decoration, null);
+                        } else if (folder.isLinked() && Sdk.CREATOR_ADT.equals(
+                                ProjectHelper.loadStringProperty(folder, Sdk.PROP_CREATOR))) {
+                            doDecoration(decoration, " [Android Library]");
                         }
                     }
                 }
index da867e4..01479bf 100644 (file)
@@ -33,6 +33,7 @@ import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.QualifiedName;
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IJavaModel;
@@ -549,7 +550,7 @@ public final class ProjectHelper {
     /**
      * Saves a String property into the persistent storage of a resource.
      * @param resource The resource into which the string value is saved.
-     * @param propertyName the name of the property. The id of the plugin is added to this string.
+     * @param propertyName the name of the property. The id of the plug-in is added to this string.
      * @param value the value to save
      * @return true if the save succeeded.
      */
@@ -569,7 +570,7 @@ public final class ProjectHelper {
     /**
      * Loads a String property from the persistent storage of a resource.
      * @param resource The resource from which the string value is loaded.
-     * @param propertyName the name of the property. The id of the plugin is added to this string.
+     * @param propertyName the name of the property. The id of the plug-in is added to this string.
      * @return the property value or null if it was not found.
      */
     public static String loadStringProperty(IResource resource, String propertyName) {
@@ -586,7 +587,7 @@ public final class ProjectHelper {
     /**
      * Saves a property into the persistent storage of a resource.
      * @param resource The resource into which the boolean value is saved.
-     * @param propertyName the name of the property. The id of the plugin is added to this string.
+     * @param propertyName the name of the property. The id of the plug-in is added to this string.
      * @param value the value to save
      * @return true if the save succeeded.
      */
@@ -596,9 +597,9 @@ public final class ProjectHelper {
     }
 
     /**
-     * Loads a boolean property from the persistent storage of the project.
+     * Loads a boolean property from the persistent storage of a resource.
      * @param resource The resource from which the boolean value is loaded.
-     * @param propertyName the name of the property. The id of the plugin is added to this string.
+     * @param propertyName the name of the property. The id of the plug-in is added to this string.
      * @param defaultValue The default value to return if the property was not found.
      * @return the property value or the default value if the property was not found.
      */
@@ -613,9 +614,9 @@ public final class ProjectHelper {
     }
 
     /**
-     * Saves the path of a resource into the persistent storate of the project.
+     * Saves the path of a resource into the persistent storage of a resource.
      * @param resource The resource into which the resource path is saved.
-     * @param propertyName the name of the property. The id of the plugin is added to this string.
+     * @param propertyName the name of the property. The id of the plug-in is added to this string.
      * @param value The resource to save. It's its path that is actually stored. If null, an
      *      empty string is stored.
      * @return true if the save succeeded
@@ -623,7 +624,7 @@ public final class ProjectHelper {
     public static boolean saveResourceProperty(IResource resource, String propertyName,
             IResource value) {
         if (value != null) {
-            IPath iPath = value.getProjectRelativePath();
+            IPath iPath = value.getFullPath();
             return saveStringProperty(resource, propertyName, iPath.toString());
         }
 
@@ -631,17 +632,17 @@ public final class ProjectHelper {
     }
 
     /**
-     * Loads the path of a resource from the persistent storage of the project, and returns the
-     * corresponding IResource object, if it exists in the same project as <code>resource</code>.
+     * Loads the path of a resource from the persistent storage of a resource, and returns the
+     * corresponding IResource object.
      * @param resource The resource from which the resource path is loaded.
-     * @param propertyName the name of the property. The id of the plugin is added to this string.
+     * @param propertyName the name of the property. The id of the plug-in is added to this string.
      * @return The corresponding IResource object (or children interface) or null
      */
     public static IResource loadResourceProperty(IResource resource, String propertyName) {
         String value = loadStringProperty(resource, propertyName);
 
         if (value != null && value.length() > 0) {
-            return resource.getProject().findMember(value);
+            return ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(value));
         }
 
         return null;
index ff34f99..c3dc894 100644 (file)
@@ -20,6 +20,7 @@ import com.android.ddmlib.IDevice;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer;
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
 import com.android.ide.eclipse.adt.internal.project.ProjectState;
 import com.android.ide.eclipse.adt.internal.project.ProjectState.LibraryDifference;
 import com.android.ide.eclipse.adt.internal.project.ProjectState.LibraryState;
@@ -46,6 +47,7 @@ import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IProjectDescription;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.IncrementalProjectBuilder;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
@@ -83,6 +85,9 @@ import java.util.Map.Entry;
  * To get the list of platforms or add-ons present in the SDK, call {@link #getTargets()}.
  */
 public final class Sdk  {
+    private static final String PROP_LIBRARY = "_library"; //$NON-NLS-1$
+    public static final String CREATOR_ADT = "ADT";        //$NON-NLS-1$
+    public static final String PROP_CREATOR = "_creator";  //$NON-NLS-1$
     private final static Object sLock = new Object();
 
     private static Sdk sCurrentSdk = null;
@@ -1008,7 +1013,7 @@ public final class Sdk  {
             final IPath previousLibraryPath,
             boolean doInJob) {
         final IJobRunnable jobRunnable = new IJobRunnable() {
-            public IStatus run(IProgressMonitor monitor) {
+            public IStatus run(final IProgressMonitor monitor) {
                 try {
                     IProject project = projectState.getProject();
                     IProject library = libraryState.getProjectState().getProject();
@@ -1049,55 +1054,98 @@ public final class Sdk  {
                     final String libName = library.getName();
                     final String varName = getLibraryVariableName(libName);
 
-                    // create a linked resource for the library using the path var.
-                    IFolder libSrc = project.getFolder(libName);
-                    // FIXME: make sure src has not been overriden?
-                    String libSrcFolder = "src"; //$NON-NLS-1$
-                    libSrc.createLink(new Path(varName + "/" + libSrcFolder), //$NON-NLS-1$
-                            IResource.REPLACE, monitor);
-
-                    // use the folder as a source folder. get the current list first.
+                    // get the current classpath entries for the project to add the new source
+                    // folders.
                     IJavaProject javaProject = JavaCore.create(project);
                     IClasspathEntry[] entries = javaProject.getRawClasspath();
-                    ArrayList<IClasspathEntry> list = new ArrayList<IClasspathEntry>(
+                    ArrayList<IClasspathEntry> classpathEntries = new ArrayList<IClasspathEntry>(
                             Arrays.asList(entries));
 
-                    // add the source folder to the classpath entries
-                    IPath path = libSrc.getFullPath();
-                    int count = list.size();
-                    boolean foundMatch = false;
-                    for (int i = 0 ; i < count ; i++) {
-                        if (list.get(i).getPath().equals(path)) {
-                            foundMatch = true;
-                            break;
+                    IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
+
+                    // list to hold the source folder to delete, as they can't be delete before
+                    // they have been removed from the classpath entries
+                    final ArrayList<IResource> toDelete = new ArrayList<IResource>();
+
+                    // loop on the classpath entries and look for CPE_SOURCE entries that
+                    // are linked folders. If they are created by us for the given library, then
+                    // we remove them as they'll be created again later (it's easier than trying
+                    // to keep old one--if they link to the same resource)
+                    for (int i = 0 ; i < classpathEntries.size();) {
+                        IClasspathEntry classpathEntry = classpathEntries.get(i);
+                        if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+                            IPath path = classpathEntry.getPath();
+                            IResource linkedRes = wsRoot.findMember(path);
+                            if (linkedRes != null && linkedRes.isLinked() && CREATOR_ADT.equals(
+                                    ProjectHelper.loadStringProperty(linkedRes, PROP_CREATOR))) {
+                                IResource originalLibrary = ProjectHelper.loadResourceProperty(
+                                        linkedRes, PROP_LIBRARY);
+
+                                // if the library is missing, or if the library is the one being
+                                // added or the same path as the one being removed:
+                                // remove the classpath entry and delete the linked folder.
+                                if (originalLibrary == null || originalLibrary.equals(library) ||
+                                        originalLibrary.getFullPath().equals(previousLibraryPath)) {
+                                    classpathEntries.remove(i);
+                                    toDelete.add(linkedRes);
+                                    continue; // don't increment i
+                                }
+                            }
                         }
-                    }
-                    if (foundMatch == false) {
-                        list.add(JavaCore.newSourceEntry(path));
+
+                        i++;
                     }
 
-                    // remove a previous one if needed (in case of a rename)
-                    if (previousLibraryPath != null) {
-                        String oldLibName = previousLibraryPath.lastSegment();
-                        IPath oldEntryPath = new Path("/").append(project.getName()).append(oldLibName);
-                        // first remove the class path entry.
-                        count = list.size();
-                        for (int i = 0 ; i < count ; i++) {
-                            if (list.get(i).getPath().equals(oldEntryPath)) {
-                                list.remove(i);
-                                break;
-                            }
+                    // get the list of source folders for the library.
+                    ArrayList<IPath> sourceFolderPaths = BaseProjectHelper.getSourceClasspaths(
+                            library);
+
+                    // loop on all the source folder, ignoring FD_GEN and add them as linked folder
+                    for (IPath sourceFolderPath : sourceFolderPaths) {
+                        IResource sourceFolder = wsRoot.findMember(sourceFolderPath);
+                        if (sourceFolder == null) {
+                            continue;
                         }
 
-                        // then remove the folder itself.
-                        IFolder oldLinkedFolder = project.getFolder(oldLibName);
-                        oldLinkedFolder.delete(true, monitor);
+                        IPath relativePath = sourceFolder.getProjectRelativePath();
+                        if (SdkConstants.FD_GEN_SOURCES.equals(relativePath.toString())) {
+                            continue;
+                        }
+
+                        // get a string version, to make up the linked folder name
+                        String srcFolderName = relativePath.toString().replace("/", //$NON-NLS-1$
+                                "_"); //$NON-NLS-1$
+
+                        // create a linked resource for the library using the path var.
+                        IFolder libSrc = project.getFolder(libName + "_" + srcFolderName);  //$NON-NLS-1$
+
+                        libSrc.createLink(new Path(varName).append(relativePath),
+                                IResource.REPLACE, monitor);
+
+                        // if we were deleting something called exactly the same (which could
+                        // have linked to a different folder), we remove it from the list
+                        // of items to delete
+                        if (toDelete.contains(libSrc)) {
+                            toDelete.remove(libSrc);
+                        }
+
+                        // set some persistent properties on it to know that it was created by ADT.
+                        ProjectHelper.saveStringProperty(libSrc, PROP_CREATOR, CREATOR_ADT);
+                        ProjectHelper.saveResourceProperty(libSrc, PROP_LIBRARY, library);
+
+                        // add the source folder to the classpath entries
+                        classpathEntries.add(JavaCore.newSourceEntry(libSrc.getFullPath()));
                     }
 
                     // set the new list
-                    javaProject.setRawClasspath(list.toArray(new IClasspathEntry[list.size()]),
+                    javaProject.setRawClasspath(
+                            classpathEntries.toArray(new IClasspathEntry[classpathEntries.size()]),
                             monitor);
 
+                    for (IResource res : toDelete) {
+                        res.delete(true, monitor);
+                    }
+
                     return Status.OK_STATUS;
                 } catch (CoreException e) {
                     AdtPlugin.logAndPrintError(e, "Library Project", "Failed to create link between library %1$s and project %2$s: %3$s",
@@ -1173,32 +1221,54 @@ public final class Sdk  {
                     // edit the list of source folders.
                     IJavaProject javaProject = JavaCore.create(project);
                     IClasspathEntry[] entries = javaProject.getRawClasspath();
-                    ArrayList<IClasspathEntry> list = new ArrayList<IClasspathEntry>(
+                    ArrayList<IClasspathEntry> classpathEntries = new ArrayList<IClasspathEntry>(
                             Arrays.asList(entries));
 
-                    String libName = libraryProject.getName();
-                    IPath oldEntryPath = new Path("/").append(project.getName()).append(libName);
-                    // first remove the class path entry.
-                    final int count = list.size();
-                    for (int i = 0 ; i < count ; i++) {
-                        if (list.get(i).getPath().equals(oldEntryPath)) {
-                            list.remove(i);
-                            break;
+                    IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
+
+                    // list to hold the source folder to delete, as they can't be delete before
+                    // they have been removed from the classpath entries
+                    ArrayList<IResource> toDelete = new ArrayList<IResource>();
+
+                    for (int i = 0 ; i < classpathEntries.size();) {
+                        IClasspathEntry classpathEntry = classpathEntries.get(i);
+                        if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+                            IPath path = classpathEntry.getPath();
+                            IResource linkedRes = wsRoot.findMember(path);
+                            if (linkedRes != null && linkedRes.isLinked() && CREATOR_ADT.equals(
+                                    ProjectHelper.loadStringProperty(linkedRes, PROP_CREATOR))) {
+                                IResource originalLibrary = ProjectHelper.loadResourceProperty(
+                                        linkedRes, PROP_LIBRARY);
+
+                                // if the library is missing, or if the library is the one being
+                                // unlinked:
+                                // remove the classpath entry and delete the linked folder.
+                                if (originalLibrary == null ||
+                                        originalLibrary.equals(libraryProject)) {
+                                    classpathEntries.remove(i);
+                                    toDelete.add(linkedRes);
+                                    continue; // don't increment i
+                                }
+                            }
                         }
-                    }
 
-                    // then remove the folder itself.
-                    IFolder oldLinkedFolder = project.getFolder(libName);
-                    oldLinkedFolder.delete(true, monitor);
+                        i++;
+                    }
 
                     // set the new list
-                    javaProject.setRawClasspath(list.toArray(new IClasspathEntry[list.size()]),
+                    javaProject.setRawClasspath(
+                            classpathEntries.toArray(new IClasspathEntry[classpathEntries.size()]),
                             monitor);
 
+                    // delete the resources that need deleting
+                    for (IResource res : toDelete) {
+                        res.delete(true, monitor);
+                    }
+
                     return Status.OK_STATUS;
                 } catch (CoreException e) {
-                    AdtPlugin.log(e,"Failure when unlinking %1$s from %2$s",
-                            libraryProject.getName(), projectState.getProject().getName());
+                    AdtPlugin.log(e, "Failure to unlink %1$s from %2$s", libraryProject.getName(),
+                            projectState.getProject().getName());
                     return e.getStatus();
                 }
             }
index dd465aa..40b055a 100644 (file)
@@ -373,6 +373,11 @@ public final class ProjectProperties {
             BufferedReader reader = new BufferedReader(new InputStreamReader(toSave.getContents(),
                     SdkConstants.INI_CHARSET));
 
+            // since we're reading the existing file and replacing values with new ones, or skipping
+            // removed values, we need to record what properties have been visited, so that
+            // we can figure later what new properties need to be added at the end of the file.
+            HashSet<String> visitedProps = new HashSet<String>();
+
             String line = null;
             while ((line = reader.readLine()) != null) {
                 // check if this is a line containing a property.
@@ -382,6 +387,10 @@ public final class ProjectProperties {
                     if (m.matches()) {
                         String key = m.group(1);
                         String value = m.group(2);
+
+                        // record the prop
+                        visitedProps.add(key);
+
                         // check if the property still exists.
                         if (mProperties.containsKey(key)) {
                             // put the new value.
@@ -396,8 +405,7 @@ public final class ProjectProperties {
 
                         // if the value is still valid, write it down.
                         if (value != null) {
-                            value = value.replaceAll("\\\\", "\\\\\\\\");
-                            writer.write(String.format("%s=%s\n", key, value));
+                            writeValue(writer, key, value, false /*addComment*/);
                         }
                     } else  {
                         // the line was wrong, let's just ignore it so that it's removed from the
@@ -408,6 +416,17 @@ public final class ProjectProperties {
                     writer.append(line).append('\n');
                 }
             }
+
+            // now add the new properties.
+            for (Entry<String, String> entry : mProperties.entrySet()) {
+                if (visitedProps.contains(entry.getKey()) == false) {
+                    String value = entry.getValue();
+                    if (value != null) {
+                        writeValue(writer, entry.getKey(), value, true /*addComment*/);
+                    }
+                }
+            }
+
         } else {
             // new file, just write it all
             // write the header
@@ -415,14 +434,9 @@ public final class ProjectProperties {
 
             // write the properties.
             for (Entry<String, String> entry : mProperties.entrySet()) {
-                String comment = COMMENT_MAP.get(entry.getKey());
-                if (comment != null) {
-                    writer.write(comment);
-                }
                 String value = entry.getValue();
                 if (value != null) {
-                    value = value.replaceAll("\\\\", "\\\\\\\\");
-                    writer.write(String.format("%s=%s\n", entry.getKey(), value));
+                    writeValue(writer, entry.getKey(), value, true /*addComment*/);
                 }
             }
         }
@@ -435,6 +449,19 @@ public final class ProjectProperties {
         filestream.flush();
     }
 
+    private void writeValue(OutputStreamWriter writer, String key, String value,
+            boolean addComment) throws IOException {
+        if (addComment) {
+            String comment = COMMENT_MAP.get(key);
+            if (comment != null) {
+                writer.write(comment);
+            }
+        }
+
+        value = value.replaceAll("\\\\", "\\\\\\\\");
+        writer.write(String.format("%s=%s\n", key, value));
+    }
+
     /**
      * Parses a property file (using UTF-8 encoding) and returns a map of the content.
      * <p/>If the file is not present, null is returned with no error messages sent to the log.