OSDN Git Service

SDK Updater: Better guess for the addon folder name.
authorRaphael <raphael@google.com>
Thu, 18 Jun 2009 03:02:56 +0000 (20:02 -0700)
committerRaphael <raphael@google.com>
Thu, 18 Jun 2009 04:28:51 +0000 (21:28 -0700)
- Always unzip the archives first and get their root zip dir.
- Try to reusing an existing addon folder first.
- Or the root zip dir.
- Or come up with a better name for the addon folder.

tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java
tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java

index 6d39098..c9372de 100755 (executable)
@@ -182,25 +182,46 @@ public class AddonPackage extends Package {
      * has this add-ons installed, we'll use that one.\r
      *\r
      * @param osSdkRoot The OS path of the SDK root folder.\r
+     * @param suggestedDir A suggestion for the installation folder name, based on the root\r
+     *                     folder used in the zip archive.\r
+     * @param sdkManager An existing SDK manager to list current platforms and addons.\r
      * @return A new {@link File} corresponding to the directory to use to install this package.\r
      */\r
     @Override\r
-    public File getInstallFolder(String osSdkRoot) {\r
+    public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {\r
         File addons = new File(osSdkRoot, SdkConstants.FD_ADDONS);\r
 \r
-        String name = String.format("%s-%d", getName(), getApiLevel()); // $NON-NLS-1$\r
+        // First find if this add-on is already installed. If so, reuse the same directory.\r
+        for (IAndroidTarget target : sdkManager.getTargets()) {\r
+            if (!target.isPlatform() &&\r
+                    target.getApiVersionNumber() == getApiLevel() &&\r
+                    target.getName().equals(getName()) &&\r
+                    target.getVendor().equals(getVendor())) {\r
+                return new File(target.getLocation());\r
+            }\r
+        }\r
+\r
+        // Otherwise, see about reusing the suggestedDir. It must not be already used or\r
+        // add some index to it, or we try to make up one.\r
+        String name = suggestedDir;\r
 \r
-        // FIXME this will fail if the name is not ASCII compatible. This could easily\r
-        // happen: a Chinese or Japanese name etc for example,\r
-        // to name a few.\r
-        name = name.toLowerCase();\r
-        name = name.replaceAll("[^a-zA-Z0-9_-]+", "_");                 // $NON-NLS-1$\r
-        name = name.replaceAll("_+", "_");                              // $NON-NLS-1$\r
+        if (suggestedDir == null || suggestedDir.length() == 0) {\r
+            name = String.format("addon-%s-%s-%d", getName(), getVendor(), getApiLevel()); //$NON-NLS-1$\r
+            name = name.toLowerCase();\r
+            name = name.replaceAll("[^a-z0-9_-]+", "_");      //$NON-NLS-1$ //$NON-NLS-2$\r
+            name = name.replaceAll("_+", "_");                //$NON-NLS-1$ //$NON-NLS-2$\r
+        }\r
 \r
-        File folder = new File(addons, name);\r
+        for (int i = 0; i < 100; i++) {\r
+            String name2 = i == 0 ? name : String.format("%s-%d", name, i); //$NON-NLS-1$\r
+            File folder = new File(addons, name2);\r
+            if (!folder.exists()) {\r
+                return folder;\r
+            }\r
+        }\r
 \r
-        // TODO find similar existing addon in addons folder\r
-        return folder;\r
+        // We shouldn't really get here. I mean, seriously, we tried hard enough.\r
+        return null;\r
     }\r
 \r
     /**\r
index 08a536b..826d629 100755 (executable)
@@ -16,6 +16,8 @@
 \r
 package com.android.sdklib.internal.repository;\r
 \r
+import com.android.sdklib.SdkManager;\r
+\r
 import java.io.File;\r
 import java.io.FileInputStream;\r
 import java.io.FileOutputStream;\r
@@ -341,7 +343,10 @@ public class Archive implements IDescription {
      *\r
      * @return True if the archive was installed, false otherwise.\r
      */\r
-    public boolean install(String osSdkRoot, boolean forceHttp, ITaskMonitor monitor) {\r
+    public boolean install(String osSdkRoot,\r
+            boolean forceHttp,\r
+            SdkManager sdkManager,\r
+            ITaskMonitor monitor) {\r
 \r
         File archiveFile = null;\r
         try {\r
@@ -364,7 +369,7 @@ public class Archive implements IDescription {
 \r
             archiveFile = downloadFile(monitor, forceHttp);\r
             if (archiveFile != null) {\r
-                if (unarchive(osSdkRoot, archiveFile, monitor)) {\r
+                if (unarchive(osSdkRoot, archiveFile, sdkManager, monitor)) {\r
                     monitor.setResult("Installed: %1$s", name);\r
                     return true;\r
                 }\r
@@ -568,48 +573,63 @@ public class Archive implements IDescription {
     /**\r
      * Install the given archive in the given folder.\r
      */\r
-    private boolean unarchive(String osSdkRoot, File archiveFile, ITaskMonitor monitor) {\r
-        String name = getParentPackage().getShortDescription();\r
-        String desc = String.format("Installing %1$s", name);\r
-        monitor.setDescription(desc);\r
+    private boolean unarchive(String osSdkRoot,\r
+            File archiveFile,\r
+            SdkManager sdkManager,\r
+            ITaskMonitor monitor) {\r
+        String pkgName = getParentPackage().getShortDescription();\r
+        String pkgDesc = String.format("Installing %1$s", pkgName);\r
+        monitor.setDescription(pkgDesc);\r
+\r
+        // We always unzip in a temp folder which name depends on the package type\r
+        // (e.g. addon, tools, etc.) and then move the folder to the destination folder.\r
+        // If the destination folder exists, it will be renamed and deleted at the very\r
+        // end if everything succeeded.\r
 \r
-        File destFolder = getParentPackage().getInstallFolder(osSdkRoot);\r
+        String pkgKind = getParentPackage().getClass().getSimpleName();\r
 \r
-        File unzipDestFolder = destFolder;\r
+        File destFolder = null;\r
+        File unzipDestFolder = null;\r
         File renamedDestFolder = null;\r
 \r
         try {\r
-            // If this folder already exists, unzip in a temporary folder and then move/unlink.\r
-            if (destFolder.exists()) {\r
-                // Find a new temp folder that doesn't exist yet\r
-                unzipDestFolder = findTempFolder(destFolder, "new");  //$NON-NLS-1$\r
+            // Find a new temp folder that doesn't exist yet\r
+            unzipDestFolder = findTempFolder(osSdkRoot, pkgKind, "new");  //$NON-NLS-1$\r
 \r
-                if (unzipDestFolder == null) {\r
-                    // this should not seriously happen.\r
-                    monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",\r
-                            destFolder.getPath());\r
-                    return false;\r
-                }\r
+            if (unzipDestFolder == null) {\r
+                // this should not seriously happen.\r
+                monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot);\r
+                return false;\r
             }\r
 \r
             if (!unzipDestFolder.mkdirs()) {\r
-                monitor.setResult("Failed to create directory %1$s",\r
-                        unzipDestFolder.getPath());\r
+                monitor.setResult("Failed to create directory %1$s", unzipDestFolder.getPath());\r
+                return false;\r
+            }\r
+\r
+            String[] zipRootFolder = new String[] { null };\r
+            if (!unzipFolder(archiveFile, getSize(),\r
+                    unzipDestFolder, pkgDesc,\r
+                    zipRootFolder, monitor)) {\r
                 return false;\r
             }\r
 \r
-            if (!unzipFolder(archiveFile, getSize(), unzipDestFolder, desc, monitor)) {\r
+            // Compute destination directory\r
+            destFolder = getParentPackage().getInstallFolder(\r
+                    osSdkRoot, zipRootFolder[0], sdkManager);\r
+\r
+            if (destFolder == null) {\r
+                // this should not seriously happen.\r
+                monitor.setResult("Failed to compute installation directory for %1$s.", pkgName);\r
                 return false;\r
             }\r
 \r
-            if (unzipDestFolder != destFolder) {\r
-                // Swap the old folder by the new one.\r
-                // Both original folders will be deleted in the finally clause below.\r
-                renamedDestFolder = findTempFolder(destFolder, "old");  //$NON-NLS-1$\r
+            // Swap the old folder by the new one.\r
+            if (destFolder.isDirectory()) {\r
+                renamedDestFolder = findTempFolder(osSdkRoot, pkgKind, "old");  //$NON-NLS-1$\r
                 if (renamedDestFolder == null) {\r
                     // this should not seriously happen.\r
-                    monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",\r
-                            destFolder.getPath());\r
+                    monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot);\r
                     return false;\r
                 }\r
 \r
@@ -619,28 +639,36 @@ public class Archive implements IDescription {
                     return false;\r
 \r
                 }\r
-                if (!unzipDestFolder.renameTo(destFolder)) {\r
-                    monitor.setResult("Failed to rename directory %1$s to %2$s",\r
-                            unzipDestFolder.getPath(), destFolder.getPath());\r
-                    return false;\r
-                }\r
             }\r
 \r
+            if (!unzipDestFolder.renameTo(destFolder)) {\r
+                monitor.setResult("Failed to rename directory %1$s to %2$s",\r
+                        unzipDestFolder.getPath(), destFolder.getPath());\r
+                return false;\r
+            }\r
+\r
+            unzipDestFolder = null;\r
             return true;\r
 \r
         } finally {\r
             // Cleanup if the unzip folder is still set.\r
             deleteFileOrFolder(renamedDestFolder);\r
-            if (unzipDestFolder != destFolder) {\r
-                deleteFileOrFolder(unzipDestFolder);\r
-            }\r
+            deleteFileOrFolder(unzipDestFolder);\r
         }\r
     }\r
 \r
+    /**\r
+     * Unzips a zip file into the given destination directory.\r
+     *\r
+     * The archive file MUST have a unique "root" folder. This root folder is skipped when\r
+     * unarchiving. However we return that root folder name to the caller, as it can be used\r
+     * as a template to know what destination directory to use in the Add-on case.\r
+     */\r
     private boolean unzipFolder(File archiveFile,\r
             long compressedSize,\r
             File unzipDestFolder,\r
             String description,\r
+            String[] outZipRootFolder,\r
             ITaskMonitor monitor) {\r
 \r
         description += " (%1$d%%)";\r
@@ -678,6 +706,9 @@ public class Archive implements IDescription {
                 if (pos < 0 || pos == name.length() - 1) {\r
                     continue;\r
                 } else {\r
+                    if (outZipRootFolder[0] == null && pos > 0) {\r
+                        outZipRootFolder[0] = name.substring(0, pos);\r
+                    }\r
                     name = name.substring(pos + 1);\r
                 }\r
 \r
@@ -762,21 +793,27 @@ public class Archive implements IDescription {
     }\r
 \r
     /**\r
-     * Finds a temp folder which name is similar to the one of the ideal folder\r
-     * and with a ".tmpN" appended.\r
+     * Finds a temp folder in the form of osBasePath/temp/prefix.suffixNNN.\r
      * <p/>\r
      * This operation is not atomic so there's no guarantee the folder can't get\r
      * created in between. This is however unlikely and the caller can assume the\r
      * returned folder does not exist yet.\r
      * <p/>\r
-     * Returns null if no such folder can be found (e.g. if all candidates exist),\r
-     * which is rather unlikely.\r
+     * Returns null if no such folder can be found (e.g. if all candidates exist,\r
+     * which is rather unlikely) or if the base temp folder cannot be created.\r
      */\r
-    private File findTempFolder(File idealFolder, String suffix) {\r
-        String basePath = idealFolder.getPath();\r
+    private File findTempFolder(String osBasePath, String prefix, String suffix) {\r
+        File baseTempFolder = new File(osBasePath, "temp");\r
+\r
+        if (!baseTempFolder.isDirectory()) {\r
+            if (!baseTempFolder.mkdirs()) {\r
+                return null;\r
+            }\r
+        }\r
 \r
         for (int i = 1; i < 100; i++) {\r
-            File folder = new File(String.format("%1$s.%2$s%3$02d", basePath, suffix, i));  //$NON-NLS-1$\r
+            File folder = new File(baseTempFolder,\r
+                    String.format("%1$s.%2$s%3$02d", prefix, suffix, i));  //$NON-NLS-1$\r
             if (!folder.exists()) {\r
                 return folder;\r
             }\r
index 66e3255..0e94717 100755 (executable)
@@ -17,6 +17,7 @@
 package com.android.sdklib.internal.repository;\r
 \r
 import com.android.sdklib.SdkConstants;\r
+import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
 import com.android.sdklib.repository.SdkRepository;\r
@@ -98,10 +99,13 @@ public class DocPackage extends Package {
      * A "doc" package should always be located in SDK/docs.\r
      *\r
      * @param osSdkRoot The OS path of the SDK root folder.\r
+     * @param suggestedDir A suggestion for the installation folder name, based on the root\r
+     *                     folder used in the zip archive.\r
+     * @param sdkManager An existing SDK manager to list current platforms and addons.\r
      * @return A new {@link File} corresponding to the directory to use to install this package.\r
      */\r
     @Override\r
-    public File getInstallFolder(String osSdkRoot) {\r
+    public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {\r
         return new File(osSdkRoot, SdkConstants.FD_DOCS);\r
     }\r
 \r
index 4ff8fb8..10f297e 100755 (executable)
@@ -17,7 +17,6 @@
 package com.android.sdklib.internal.repository;\r
 \r
 import com.android.sdklib.IAndroidTarget;\r
-import com.android.sdklib.ISdkLog;\r
 import com.android.sdklib.SdkConstants;\r
 import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
@@ -63,7 +62,7 @@ public class LocalSdkParser {
     }\r
 \r
     /**\r
-     * Returns the packages found by the last call to {@link #parseSdk(String)}.\r
+     * Returns the packages found by the last call to {@link #parseSdk(String, SdkManager)}.\r
      */\r
     public Package[] getPackages() {\r
         return mPackages;\r
@@ -71,7 +70,7 @@ public class LocalSdkParser {
 \r
     /**\r
      * Clear the internal packages list. After this call, {@link #getPackages()} will return\r
-     * null till {@link #parseSdk(String)} is called.\r
+     * null till {@link #parseSdk(String, SdkManager)} is called.\r
      */\r
     public void clearPackages() {\r
         mPackages = null;\r
@@ -84,9 +83,10 @@ public class LocalSdkParser {
      * at any time later.\r
      *\r
      * @param osSdkRoot The path to the SDK folder.\r
+     * @param sdkManager An existing SDK manager to list current platforms and addons.\r
      * @return The packages found. Can be retrieved later using {@link #getPackages()}.\r
      */\r
-    public Package[] parseSdk(String osSdkRoot) {\r
+    public Package[] parseSdk(String osSdkRoot, SdkManager sdkManager) {\r
         ArrayList<Package> packages = new ArrayList<Package>();\r
 \r
         Package pkg = scanDoc(new File(osSdkRoot, SdkConstants.FD_DOCS));\r
@@ -100,20 +100,7 @@ public class LocalSdkParser {
         }\r
 \r
         // for platforms and add-ons, rely on the SdkManager parser\r
-        SdkManager sdkman = SdkManager.createManager(osSdkRoot, new ISdkLog() {\r
-            // A dummy sdk logger that doesn't log anything.\r
-            public void error(Throwable t, String errorFormat, Object... args) {\r
-                // pass\r
-            }\r
-            public void printf(String msgFormat, Object... args) {\r
-                // pass\r
-            }\r
-            public void warning(String warningFormat, Object... args) {\r
-                // pass\r
-            }\r
-        });\r
-\r
-        for(IAndroidTarget target : sdkman.getTargets()) {\r
+        for(IAndroidTarget target : sdkManager.getTargets()) {\r
             pkg = null;\r
 \r
             if (target.isPlatform()) {\r
index cdac3c1..bd6ccc0 100755 (executable)
@@ -16,6 +16,7 @@
 \r
 package com.android.sdklib.internal.repository;\r
 \r
+import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
 import com.android.sdklib.repository.SdkRepository;\r
@@ -198,9 +199,13 @@ public abstract class Package implements IDescription {
      * existing or new folder depending on the current content of the SDK.\r
      *\r
      * @param osSdkRoot The OS path of the SDK root folder.\r
+     * @param suggestedDir A suggestion for the installation folder name, based on the root\r
+     *                     folder used in the zip archive.\r
+     * @param sdkManager An existing SDK manager to list current platforms and addons.\r
      * @return A new {@link File} corresponding to the directory to use to install this package.\r
      */\r
-    public abstract File getInstallFolder(String osSdkRoot);\r
+    public abstract File getInstallFolder(\r
+            String osSdkRoot, String suggestedDir, SdkManager sdkManager);\r
 \r
     /**\r
      * Computes whether the given package is a suitable update for the current package.\r
index b4b8cf3..66dd529 100755 (executable)
@@ -102,10 +102,23 @@ public class PlatformPackage extends Package {
      * has this platform version installed, we'll use that one.\r
      *\r
      * @param osSdkRoot The OS path of the SDK root folder.\r
+     * @param suggestedDir A suggestion for the installation folder name, based on the root\r
+     *                     folder used in the zip archive.\r
+     * @param sdkManager An existing SDK manager to list current platforms and addons.\r
      * @return A new {@link File} corresponding to the directory to use to install this package.\r
      */\r
     @Override\r
-    public File getInstallFolder(String osSdkRoot) {\r
+    public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {\r
+\r
+        // First find if this add-on is already installed. If so, reuse the same directory.\r
+        for (IAndroidTarget target : sdkManager.getTargets()) {\r
+            if (target.isPlatform() &&\r
+                    target.getApiVersionNumber() == getApiLevel() &&\r
+                    target.getApiVersionName().equals(getVersion())) {\r
+                return new File(target.getLocation());\r
+            }\r
+        }\r
+\r
         File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS);\r
         File folder = new File(platforms, String.format("android-%s", getVersion())); //$NON-NLS-1$\r
         // TODO find similar existing platform in platforms folder\r
index b355b93..b6d830c 100755 (executable)
@@ -17,6 +17,7 @@
 package com.android.sdklib.internal.repository;\r
 \r
 import com.android.sdklib.SdkConstants;\r
+import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
 \r
@@ -82,10 +83,13 @@ public class ToolPackage extends Package {
      * A "tool" package should always be located in SDK/tools.\r
      *\r
      * @param osSdkRoot The OS path of the SDK root folder.\r
+     * @param suggestedDir A suggestion for the installation folder name, based on the root\r
+     *                     folder used in the zip archive.\r
+     * @param sdkManager An existing SDK manager to list current platforms and addons.\r
      * @return A new {@link File} corresponding to the directory to use to install this package.\r
      */\r
     @Override\r
-    public File getInstallFolder(String osSdkRoot) {\r
+    public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {\r
         return new File(osSdkRoot, SdkConstants.FD_TOOLS);\r
     }\r
 \r
index 05e0b06..a92129f 100755 (executable)
@@ -101,7 +101,8 @@ class LocalSdkAdapter  {
 \r
                 if (packages == null) {\r
                     // load on demand the first time\r
-                    packages = parser.parseSdk(mUpdaterData.getOsSdkRoot());\r
+                    packages = parser.parseSdk(mUpdaterData.getOsSdkRoot(),\r
+                            mUpdaterData.getSdkManager());\r
                 }\r
 \r
                 if (packages != null) {\r
index f8373dd..455abcf 100755 (executable)
@@ -273,7 +273,7 @@ class UpdaterData {
                             break;\r
                         }\r
 \r
-                        if (archive.install(mOsSdkRoot, forceHttp, monitor)) {\r
+                        if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) {\r
                             numInstalled++;\r
                         }\r
 \r