From 6c8ec1d6479f51e269138633aedceb0bed9d2340 Mon Sep 17 00:00:00 2001 From: Raphael Date: Wed, 17 Jun 2009 20:02:56 -0700 Subject: [PATCH] SDK Updater: Better guess for the addon folder name. - 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. --- .../sdklib/internal/repository/AddonPackage.java | 43 ++++++-- .../sdklib/internal/repository/Archive.java | 121 ++++++++++++++------- .../sdklib/internal/repository/DocPackage.java | 6 +- .../sdklib/internal/repository/LocalSdkParser.java | 23 +--- .../sdklib/internal/repository/Package.java | 7 +- .../internal/repository/PlatformPackage.java | 15 ++- .../sdklib/internal/repository/ToolPackage.java | 6 +- .../internal/repository/LocalSdkAdapter.java | 3 +- .../sdkuilib/internal/repository/UpdaterData.java | 2 +- 9 files changed, 149 insertions(+), 77 deletions(-) diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java index 6d3909832..c9372de7c 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java @@ -182,25 +182,46 @@ public class AddonPackage extends Package { * has this add-ons installed, we'll use that one. * * @param osSdkRoot The OS path of the SDK root folder. + * @param suggestedDir A suggestion for the installation folder name, based on the root + * folder used in the zip archive. + * @param sdkManager An existing SDK manager to list current platforms and addons. * @return A new {@link File} corresponding to the directory to use to install this package. */ @Override - public File getInstallFolder(String osSdkRoot) { + public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) { File addons = new File(osSdkRoot, SdkConstants.FD_ADDONS); - String name = String.format("%s-%d", getName(), getApiLevel()); // $NON-NLS-1$ + // First find if this add-on is already installed. If so, reuse the same directory. + for (IAndroidTarget target : sdkManager.getTargets()) { + if (!target.isPlatform() && + target.getApiVersionNumber() == getApiLevel() && + target.getName().equals(getName()) && + target.getVendor().equals(getVendor())) { + return new File(target.getLocation()); + } + } + + // Otherwise, see about reusing the suggestedDir. It must not be already used or + // add some index to it, or we try to make up one. + String name = suggestedDir; - // FIXME this will fail if the name is not ASCII compatible. This could easily - // happen: a Chinese or Japanese name etc for example, - // to name a few. - name = name.toLowerCase(); - name = name.replaceAll("[^a-zA-Z0-9_-]+", "_"); // $NON-NLS-1$ - name = name.replaceAll("_+", "_"); // $NON-NLS-1$ + if (suggestedDir == null || suggestedDir.length() == 0) { + name = String.format("addon-%s-%s-%d", getName(), getVendor(), getApiLevel()); //$NON-NLS-1$ + name = name.toLowerCase(); + name = name.replaceAll("[^a-z0-9_-]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$ + name = name.replaceAll("_+", "_"); //$NON-NLS-1$ //$NON-NLS-2$ + } - File folder = new File(addons, name); + for (int i = 0; i < 100; i++) { + String name2 = i == 0 ? name : String.format("%s-%d", name, i); //$NON-NLS-1$ + File folder = new File(addons, name2); + if (!folder.exists()) { + return folder; + } + } - // TODO find similar existing addon in addons folder - return folder; + // We shouldn't really get here. I mean, seriously, we tried hard enough. + return null; } /** diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java index 08a536b51..826d629ef 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java @@ -16,6 +16,8 @@ package com.android.sdklib.internal.repository; +import com.android.sdklib.SdkManager; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -341,7 +343,10 @@ public class Archive implements IDescription { * * @return True if the archive was installed, false otherwise. */ - public boolean install(String osSdkRoot, boolean forceHttp, ITaskMonitor monitor) { + public boolean install(String osSdkRoot, + boolean forceHttp, + SdkManager sdkManager, + ITaskMonitor monitor) { File archiveFile = null; try { @@ -364,7 +369,7 @@ public class Archive implements IDescription { archiveFile = downloadFile(monitor, forceHttp); if (archiveFile != null) { - if (unarchive(osSdkRoot, archiveFile, monitor)) { + if (unarchive(osSdkRoot, archiveFile, sdkManager, monitor)) { monitor.setResult("Installed: %1$s", name); return true; } @@ -568,48 +573,63 @@ public class Archive implements IDescription { /** * Install the given archive in the given folder. */ - private boolean unarchive(String osSdkRoot, File archiveFile, ITaskMonitor monitor) { - String name = getParentPackage().getShortDescription(); - String desc = String.format("Installing %1$s", name); - monitor.setDescription(desc); + private boolean unarchive(String osSdkRoot, + File archiveFile, + SdkManager sdkManager, + ITaskMonitor monitor) { + String pkgName = getParentPackage().getShortDescription(); + String pkgDesc = String.format("Installing %1$s", pkgName); + monitor.setDescription(pkgDesc); + + // We always unzip in a temp folder which name depends on the package type + // (e.g. addon, tools, etc.) and then move the folder to the destination folder. + // If the destination folder exists, it will be renamed and deleted at the very + // end if everything succeeded. - File destFolder = getParentPackage().getInstallFolder(osSdkRoot); + String pkgKind = getParentPackage().getClass().getSimpleName(); - File unzipDestFolder = destFolder; + File destFolder = null; + File unzipDestFolder = null; File renamedDestFolder = null; try { - // If this folder already exists, unzip in a temporary folder and then move/unlink. - if (destFolder.exists()) { - // Find a new temp folder that doesn't exist yet - unzipDestFolder = findTempFolder(destFolder, "new"); //$NON-NLS-1$ + // Find a new temp folder that doesn't exist yet + unzipDestFolder = findTempFolder(osSdkRoot, pkgKind, "new"); //$NON-NLS-1$ - if (unzipDestFolder == null) { - // this should not seriously happen. - monitor.setResult("Failed to find a suitable temp directory similar to %1$s.", - destFolder.getPath()); - return false; - } + if (unzipDestFolder == null) { + // this should not seriously happen. + monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot); + return false; } if (!unzipDestFolder.mkdirs()) { - monitor.setResult("Failed to create directory %1$s", - unzipDestFolder.getPath()); + monitor.setResult("Failed to create directory %1$s", unzipDestFolder.getPath()); + return false; + } + + String[] zipRootFolder = new String[] { null }; + if (!unzipFolder(archiveFile, getSize(), + unzipDestFolder, pkgDesc, + zipRootFolder, monitor)) { return false; } - if (!unzipFolder(archiveFile, getSize(), unzipDestFolder, desc, monitor)) { + // Compute destination directory + destFolder = getParentPackage().getInstallFolder( + osSdkRoot, zipRootFolder[0], sdkManager); + + if (destFolder == null) { + // this should not seriously happen. + monitor.setResult("Failed to compute installation directory for %1$s.", pkgName); return false; } - if (unzipDestFolder != destFolder) { - // Swap the old folder by the new one. - // Both original folders will be deleted in the finally clause below. - renamedDestFolder = findTempFolder(destFolder, "old"); //$NON-NLS-1$ + // Swap the old folder by the new one. + if (destFolder.isDirectory()) { + renamedDestFolder = findTempFolder(osSdkRoot, pkgKind, "old"); //$NON-NLS-1$ if (renamedDestFolder == null) { // this should not seriously happen. - monitor.setResult("Failed to find a suitable temp directory similar to %1$s.", - destFolder.getPath()); + monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot); return false; } @@ -619,28 +639,36 @@ public class Archive implements IDescription { return false; } - if (!unzipDestFolder.renameTo(destFolder)) { - monitor.setResult("Failed to rename directory %1$s to %2$s", - unzipDestFolder.getPath(), destFolder.getPath()); - return false; - } } + if (!unzipDestFolder.renameTo(destFolder)) { + monitor.setResult("Failed to rename directory %1$s to %2$s", + unzipDestFolder.getPath(), destFolder.getPath()); + return false; + } + + unzipDestFolder = null; return true; } finally { // Cleanup if the unzip folder is still set. deleteFileOrFolder(renamedDestFolder); - if (unzipDestFolder != destFolder) { - deleteFileOrFolder(unzipDestFolder); - } + deleteFileOrFolder(unzipDestFolder); } } + /** + * Unzips a zip file into the given destination directory. + * + * The archive file MUST have a unique "root" folder. This root folder is skipped when + * unarchiving. However we return that root folder name to the caller, as it can be used + * as a template to know what destination directory to use in the Add-on case. + */ private boolean unzipFolder(File archiveFile, long compressedSize, File unzipDestFolder, String description, + String[] outZipRootFolder, ITaskMonitor monitor) { description += " (%1$d%%)"; @@ -678,6 +706,9 @@ public class Archive implements IDescription { if (pos < 0 || pos == name.length() - 1) { continue; } else { + if (outZipRootFolder[0] == null && pos > 0) { + outZipRootFolder[0] = name.substring(0, pos); + } name = name.substring(pos + 1); } @@ -762,21 +793,27 @@ public class Archive implements IDescription { } /** - * Finds a temp folder which name is similar to the one of the ideal folder - * and with a ".tmpN" appended. + * Finds a temp folder in the form of osBasePath/temp/prefix.suffixNNN. *

* This operation is not atomic so there's no guarantee the folder can't get * created in between. This is however unlikely and the caller can assume the * returned folder does not exist yet. *

- * Returns null if no such folder can be found (e.g. if all candidates exist), - * which is rather unlikely. + * Returns null if no such folder can be found (e.g. if all candidates exist, + * which is rather unlikely) or if the base temp folder cannot be created. */ - private File findTempFolder(File idealFolder, String suffix) { - String basePath = idealFolder.getPath(); + private File findTempFolder(String osBasePath, String prefix, String suffix) { + File baseTempFolder = new File(osBasePath, "temp"); + + if (!baseTempFolder.isDirectory()) { + if (!baseTempFolder.mkdirs()) { + return null; + } + } for (int i = 1; i < 100; i++) { - File folder = new File(String.format("%1$s.%2$s%3$02d", basePath, suffix, i)); //$NON-NLS-1$ + File folder = new File(baseTempFolder, + String.format("%1$s.%2$s%3$02d", prefix, suffix, i)); //$NON-NLS-1$ if (!folder.exists()) { return folder; } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java index 66e32558a..0e9471740 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java @@ -17,6 +17,7 @@ package com.android.sdklib.internal.repository; import com.android.sdklib.SdkConstants; +import com.android.sdklib.SdkManager; import com.android.sdklib.internal.repository.Archive.Arch; import com.android.sdklib.internal.repository.Archive.Os; import com.android.sdklib.repository.SdkRepository; @@ -98,10 +99,13 @@ public class DocPackage extends Package { * A "doc" package should always be located in SDK/docs. * * @param osSdkRoot The OS path of the SDK root folder. + * @param suggestedDir A suggestion for the installation folder name, based on the root + * folder used in the zip archive. + * @param sdkManager An existing SDK manager to list current platforms and addons. * @return A new {@link File} corresponding to the directory to use to install this package. */ @Override - public File getInstallFolder(String osSdkRoot) { + public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) { return new File(osSdkRoot, SdkConstants.FD_DOCS); } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java index 4ff8fb869..10f297e53 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java @@ -17,7 +17,6 @@ package com.android.sdklib.internal.repository; import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; import com.android.sdklib.internal.repository.Archive.Arch; @@ -63,7 +62,7 @@ public class LocalSdkParser { } /** - * Returns the packages found by the last call to {@link #parseSdk(String)}. + * Returns the packages found by the last call to {@link #parseSdk(String, SdkManager)}. */ public Package[] getPackages() { return mPackages; @@ -71,7 +70,7 @@ public class LocalSdkParser { /** * Clear the internal packages list. After this call, {@link #getPackages()} will return - * null till {@link #parseSdk(String)} is called. + * null till {@link #parseSdk(String, SdkManager)} is called. */ public void clearPackages() { mPackages = null; @@ -84,9 +83,10 @@ public class LocalSdkParser { * at any time later. * * @param osSdkRoot The path to the SDK folder. + * @param sdkManager An existing SDK manager to list current platforms and addons. * @return The packages found. Can be retrieved later using {@link #getPackages()}. */ - public Package[] parseSdk(String osSdkRoot) { + public Package[] parseSdk(String osSdkRoot, SdkManager sdkManager) { ArrayList packages = new ArrayList(); Package pkg = scanDoc(new File(osSdkRoot, SdkConstants.FD_DOCS)); @@ -100,20 +100,7 @@ public class LocalSdkParser { } // for platforms and add-ons, rely on the SdkManager parser - SdkManager sdkman = SdkManager.createManager(osSdkRoot, new ISdkLog() { - // A dummy sdk logger that doesn't log anything. - public void error(Throwable t, String errorFormat, Object... args) { - // pass - } - public void printf(String msgFormat, Object... args) { - // pass - } - public void warning(String warningFormat, Object... args) { - // pass - } - }); - - for(IAndroidTarget target : sdkman.getTargets()) { + for(IAndroidTarget target : sdkManager.getTargets()) { pkg = null; if (target.isPlatform()) { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java index cdac3c11a..bd6ccc046 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java @@ -16,6 +16,7 @@ package com.android.sdklib.internal.repository; +import com.android.sdklib.SdkManager; import com.android.sdklib.internal.repository.Archive.Arch; import com.android.sdklib.internal.repository.Archive.Os; import com.android.sdklib.repository.SdkRepository; @@ -198,9 +199,13 @@ public abstract class Package implements IDescription { * existing or new folder depending on the current content of the SDK. * * @param osSdkRoot The OS path of the SDK root folder. + * @param suggestedDir A suggestion for the installation folder name, based on the root + * folder used in the zip archive. + * @param sdkManager An existing SDK manager to list current platforms and addons. * @return A new {@link File} corresponding to the directory to use to install this package. */ - public abstract File getInstallFolder(String osSdkRoot); + public abstract File getInstallFolder( + String osSdkRoot, String suggestedDir, SdkManager sdkManager); /** * Computes whether the given package is a suitable update for the current package. diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java index b4b8cf37e..66dd529c2 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java @@ -102,10 +102,23 @@ public class PlatformPackage extends Package { * has this platform version installed, we'll use that one. * * @param osSdkRoot The OS path of the SDK root folder. + * @param suggestedDir A suggestion for the installation folder name, based on the root + * folder used in the zip archive. + * @param sdkManager An existing SDK manager to list current platforms and addons. * @return A new {@link File} corresponding to the directory to use to install this package. */ @Override - public File getInstallFolder(String osSdkRoot) { + public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) { + + // First find if this add-on is already installed. If so, reuse the same directory. + for (IAndroidTarget target : sdkManager.getTargets()) { + if (target.isPlatform() && + target.getApiVersionNumber() == getApiLevel() && + target.getApiVersionName().equals(getVersion())) { + return new File(target.getLocation()); + } + } + File platforms = new File(osSdkRoot, SdkConstants.FD_PLATFORMS); File folder = new File(platforms, String.format("android-%s", getVersion())); //$NON-NLS-1$ // TODO find similar existing platform in platforms folder diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java index b355b933c..b6d830c1b 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java @@ -17,6 +17,7 @@ package com.android.sdklib.internal.repository; import com.android.sdklib.SdkConstants; +import com.android.sdklib.SdkManager; import com.android.sdklib.internal.repository.Archive.Arch; import com.android.sdklib.internal.repository.Archive.Os; @@ -82,10 +83,13 @@ public class ToolPackage extends Package { * A "tool" package should always be located in SDK/tools. * * @param osSdkRoot The OS path of the SDK root folder. + * @param suggestedDir A suggestion for the installation folder name, based on the root + * folder used in the zip archive. + * @param sdkManager An existing SDK manager to list current platforms and addons. * @return A new {@link File} corresponding to the directory to use to install this package. */ @Override - public File getInstallFolder(String osSdkRoot) { + public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) { return new File(osSdkRoot, SdkConstants.FD_TOOLS); } diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java index 05e0b06bb..a92129f53 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java @@ -101,7 +101,8 @@ class LocalSdkAdapter { if (packages == null) { // load on demand the first time - packages = parser.parseSdk(mUpdaterData.getOsSdkRoot()); + packages = parser.parseSdk(mUpdaterData.getOsSdkRoot(), + mUpdaterData.getSdkManager()); } if (packages != null) { diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java index f8373dd6d..455abcf65 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java @@ -273,7 +273,7 @@ class UpdaterData { break; } - if (archive.install(mOsSdkRoot, forceHttp, monitor)) { + if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) { numInstalled++; } -- 2.11.0