Merge "Don't copy the whole SdkController into the SDK."
diff --git a/build/tools.atree b/build/tools.atree
index a773941..8fc262c 100644
--- a/build/tools.atree
+++ b/build/tools.atree
@@ -65,7 +65,8 @@
# sdk.git Ant templates for project build files
sdk/templates/build.template tools/lib/build.template
-sdk/files/proguard.cfg tools/lib/proguard.cfg
+sdk/files/proguard-project.txt tools/lib/proguard-project.txt
+sdk/files/proguard-android.txt tools/proguard/proguard-android.txt
# Ant Build Rules
sdk/files/ant tools/ant
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 2584933..f6fe09f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -20,7 +20,9 @@
import com.android.ide.common.log.ILogger;
import com.android.ide.common.resources.ResourceFile;
import com.android.ide.common.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler.Solution;
import com.android.ide.eclipse.adt.internal.VersionCheck;
+import com.android.ide.eclipse.adt.internal.actions.SdkManagerAction;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor;
@@ -42,6 +44,7 @@
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
+import org.eclipse.core.commands.Command;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IProject;
@@ -59,8 +62,10 @@
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.util.IPropertyChangeListener;
@@ -78,11 +83,14 @@
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWebBrowser;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.texteditor.AbstractTextEditor;
@@ -162,15 +170,23 @@
* checkSdkLocationAndId.
*/
public static abstract class CheckSdkErrorHandler {
+
+ public enum Solution {
+ NONE,
+ OPEN_SDK_MANAGER,
+ OPEN_ANDROID_PREFS,
+ OPEN_P2_UPDATE
+ }
+
/** Handle an error message during sdk location check. Returns whatever
* checkSdkLocationAndId() should returns.
*/
- public abstract boolean handleError(String message);
+ public abstract boolean handleError(Solution solution, String message);
/** Handle a warning message during sdk location check. Returns whatever
* checkSdkLocationAndId() should returns.
*/
- public abstract boolean handleWarning(String message);
+ public abstract boolean handleWarning(Solution solution, String message);
}
/**
@@ -1064,17 +1080,133 @@
}
return checkSdkLocationAndId(sdkLocation, new CheckSdkErrorHandler() {
+ private String mTitle = "Android SDK Verification";
@Override
- public boolean handleError(String message) {
- AdtPlugin.displayError("Android SDK Verification", message);
+ public boolean handleError(Solution solution, String message) {
+ displayMessage(solution, message, MessageDialog.ERROR);
return false;
}
@Override
- public boolean handleWarning(String message) {
- AdtPlugin.displayWarning("Android SDK Verification", message);
+ public boolean handleWarning(Solution solution, String message) {
+ displayMessage(solution, message, MessageDialog.WARNING);
return true;
}
+
+ private void displayMessage(
+ final Solution solution,
+ final String message,
+ final int dialogImageType) {
+ final Display disp = getDisplay();
+ disp.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ Shell shell = disp.getActiveShell();
+ if (shell == null) {
+ return;
+ }
+
+ String customLabel = null;
+ switch(solution) {
+ case OPEN_ANDROID_PREFS:
+ customLabel = "Open Preferences";
+ break;
+ case OPEN_P2_UPDATE:
+ customLabel = "Check for Updates";
+ break;
+ case OPEN_SDK_MANAGER:
+ customLabel = "Open SDK Manager";
+ break;
+ }
+
+ String btnLabels[] = new String[customLabel == null ? 1 : 2];
+ btnLabels[0] = customLabel;
+ btnLabels[btnLabels.length - 1] = IDialogConstants.CLOSE_LABEL;
+
+ MessageDialog dialog = new MessageDialog(
+ shell, // parent
+ mTitle,
+ null, // dialogTitleImage
+ message,
+ dialogImageType,
+ btnLabels,
+ btnLabels.length - 1);
+ int index = dialog.open();
+
+ if (customLabel != null && index == 0) {
+ switch(solution) {
+ case OPEN_ANDROID_PREFS:
+ openAndroidPrefs();
+ break;
+ case OPEN_P2_UPDATE:
+ openP2Update();
+ break;
+ case OPEN_SDK_MANAGER:
+ openSdkManager();
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ private void openSdkManager() {
+ // Windows only: open the standalone external SDK Manager since we know
+ // that ADT on Windows is bound to be locking some SDK folders.
+ if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {
+ if (SdkManagerAction.openExternalSdkManager()) {
+ return;
+ }
+ }
+
+ // Otherwise open the regular SDK Manager bundled within ADT
+ if (!SdkManagerAction.openAdtSdkManager()) {
+ // We failed because the SDK location is undefined. In this case
+ // let's open the preferences instead.
+ openAndroidPrefs();
+ }
+ }
+
+ private void openP2Update() {
+ Display disp = getDisplay();
+ if (disp == null) {
+ return;
+ }
+ disp.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ String cmdId = "org.eclipse.equinox.p2.ui.sdk.update"; //$NON-NLS-1$
+ IWorkbench wb = PlatformUI.getWorkbench();
+ if (wb == null) {
+ return;
+ }
+
+ ICommandService cs = (ICommandService) wb.getService(ICommandService.class);
+ IHandlerService is = (IHandlerService) wb.getService(IHandlerService.class);
+ if (cs == null || is == null) {
+ return;
+ }
+
+ Command cmd = cs.getCommand(cmdId);
+ if (cmd != null && cmd.isDefined()) {
+ try {
+ is.executeCommand(cmdId, null/*event*/);
+ } catch (Exception ignore) {
+ AdtPlugin.log(ignore, "Failed to execute command %s", cmdId);
+ }
+ }
+ }
+ });
+ }
+
+ private void openAndroidPrefs() {
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(
+ getDisplay().getActiveShell(),
+ "com.android.ide.eclipse.preferences.main", //$NON-NLS-1$ preferencePageId
+ null, // displayedIds
+ null); // data
+ dialog.open();
+ }
});
}
@@ -1093,6 +1225,7 @@
File osSdkFolder = new File(osSdkLocation);
if (osSdkFolder.isDirectory() == false) {
return errorHandler.handleError(
+ Solution.OPEN_ANDROID_PREFS,
String.format(Messages.Could_Not_Find_Folder, osSdkLocation));
}
@@ -1100,6 +1233,7 @@
File toolsFolder = new File(osTools);
if (toolsFolder.isDirectory() == false) {
return errorHandler.handleError(
+ Solution.OPEN_ANDROID_PREFS,
String.format(Messages.Could_Not_Find_Folder_In_SDK,
SdkConstants.FD_TOOLS, osSdkLocation));
}
@@ -1113,13 +1247,17 @@
// check that we have both the tools component and the platform-tools component.
String platformTools = osSdkLocation + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER;
if (checkFolder(platformTools) == false) {
- return errorHandler.handleWarning("SDK Platform Tools component is missing!\n" +
+ return errorHandler.handleWarning(
+ Solution.OPEN_SDK_MANAGER,
+ "SDK Platform Tools component is missing!\n" +
"Please use the SDK Manager to install it.");
}
String tools = osSdkLocation + SdkConstants.OS_SDK_TOOLS_FOLDER;
if (checkFolder(tools) == false) {
- return errorHandler.handleError("SDK Tools component is missing!\n" +
+ return errorHandler.handleError(
+ Solution.OPEN_SDK_MANAGER,
+ "SDK Tools component is missing!\n" +
"Please use the SDK Manager to install it.");
}
@@ -1131,7 +1269,9 @@
};
for (String file : filesToCheck) {
if (checkFile(file) == false) {
- return errorHandler.handleError(String.format(Messages.Could_Not_Find, file));
+ return errorHandler.handleError(
+ Solution.OPEN_ANDROID_PREFS,
+ String.format(Messages.Could_Not_Find, file));
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java
index 9055b29..b468c5e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java
@@ -18,6 +18,7 @@
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler.Solution;
import com.android.ide.eclipse.adt.Messages;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.repository.PkgProps;
@@ -103,7 +104,9 @@
// Failed to get the min plugin version number?
if (minMajorVersion == -1 || minMinorVersion == -1 || minMicroVersion ==-1) {
- return errorHandler.handleWarning(Messages.VersionCheck_Plugin_Version_Failed);
+ return errorHandler.handleWarning(
+ Solution.OPEN_SDK_MANAGER,
+ Messages.VersionCheck_Plugin_Version_Failed);
}
// test the plugin number
@@ -126,6 +129,7 @@
if (valid == false) {
return errorHandler.handleError(
+ Solution.OPEN_P2_UPDATE,
String.format(Messages.VersionCheck_Plugin_Too_Old,
minMajorVersion, minMinorVersion, minMicroVersion, versionString));
}
@@ -163,6 +167,7 @@
// this is a warning only as we need to parse the SDK to allow updating
// of the tools!
return errorHandler.handleWarning(
+ Solution.OPEN_SDK_MANAGER,
String.format(Messages.VersionCheck_Tools_Too_Old,
MIN_TOOLS_REV, toolsRevision));
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java
index 970bc47..f8a080a 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java
@@ -22,6 +22,7 @@
import com.android.sdklib.SdkConstants;
import com.android.sdklib.util.GrabProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
@@ -175,7 +176,7 @@
int err = GrabProcessOutput.grabProcessOutput(
process,
- true /*waitForReaders*/,
+ Wait.WAIT_FOR_READERS,
new IProcessOutput() {
@Override
public void out(String line) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
index ddf180f..0ff50b5 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
@@ -20,6 +20,11 @@
import com.android.ide.eclipse.adt.internal.build.DexWrapper;
import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.io.FileOp;
+import com.android.sdklib.util.GrabProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
import com.android.sdkuilib.repository.ISdkChangeListener;
import com.android.sdkuilib.repository.SdkUpdaterWindow;
import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext;
@@ -31,6 +36,8 @@
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+import java.io.File;
+
/**
* Delegate for the toolbar/menu action "Android SDK Manager".
* It displays the Android SDK Manager.
@@ -49,95 +56,163 @@
@Override
public void run(IAction action) {
- final Sdk sdk = Sdk.getCurrent();
- if (sdk != null) {
-
- // Runs the updater window, directing only warning/errors logs to the ADT console
- // (normal log is just dropped, which is fine since the SDK Manager has its own
- // log window now.)
-
- SdkUpdaterWindow window = new SdkUpdaterWindow(
- AdtPlugin.getDisplay().getActiveShell(),
- new AdtConsoleSdkLog() {
- @Override
- public void printf(String msgFormat, Object... args) {
- // Do not show non-error/warning log in Eclipse.
- };
- },
- sdk.getSdkLocation(),
- SdkInvocationContext.IDE);
-
- ISdkChangeListener listener = new ISdkChangeListener() {
- @Override
- public void onSdkLoaded() {
- // Ignore initial load of the SDK.
- }
-
- /**
- * Unload all we can from the SDK before new packages are installed.
- * Typically we need to get rid of references to dx from platform-tools
- * and to any platform resource data.
- * <p/>
- * {@inheritDoc}
- */
- @Override
- public void preInstallHook() {
-
- // TODO we need to unload as much of as SDK as possible. Otherwise
- // on Windows we end up with Eclipse locking some files and we can't
- // replace them.
- //
- // At this point, we know what the user wants to install so it would be
- // possible to pass in flags to know what needs to be unloaded. Typically
- // we need to:
- // - unload dex if platform-tools is going to be updated. There's a vague
- // attempt below at removing any references to dex and GCing. Seems
- // to do the trick.
- // - unload any target that is going to be updated since it may have
- // resource data used by a current layout editor (e.g. data/*.ttf
- // and various data/res/*.xml).
- //
- // Most important we need to make sure there isn't a build going on
- // and if there is one, either abort it or wait for it to complete and
- // then we want to make sure we don't get any attempt to use the SDK
- // before the postInstallHook is called.
-
- if (sdk != null) {
- sdk.unloadTargetData(true /*preventReload*/);
-
- DexWrapper dx = sdk.getDexWrapper();
- dx.unload();
- }
- }
-
- /**
- * Nothing to do. We'll reparse the SDK later in onSdkReload.
- * <p/>
- * {@inheritDoc}
- */
- @Override
- public void postInstallHook() {
- }
-
- /**
- * Reparse the SDK in case anything was add/removed.
- * <p/>
- * {@inheritDoc}
- */
- @Override
- public void onSdkReload() {
- AdtPlugin.getDefault().reparseSdk();
- }
- };
-
- window.addListener(listener);
- window.open();
- } else {
- AdtPlugin.displayError("Android SDK",
+ if (!openAdtSdkManager()) {
+ AdtPlugin.displayError(
+ "Android SDK",
"Location of the Android SDK has not been setup in the preferences.");
}
}
+ /**
+ * Opens the SDK Manager as an external application.
+ * This call is asynchronous, it doesn't wait for the manager to be closed.
+ *
+ * @return True if the application was found and executed. False if it could not
+ * be located or could not be launched.
+ */
+ public static boolean openExternalSdkManager() {
+ final Sdk sdk = Sdk.getCurrent();
+ if (sdk == null) {
+ return false;
+ }
+
+ File androidBat = FileOp.append(
+ sdk.getSdkLocation(),
+ SdkConstants.FD_TOOLS,
+ SdkConstants.androidCmdName());
+
+ if (!androidBat.exists()) {
+ return false;
+ }
+
+ try {
+ final AdtConsoleSdkLog logger = new AdtConsoleSdkLog();
+
+ String command[] = new String[] {
+ androidBat.getAbsolutePath(),
+ "sdk" //$NON-NLS-1$
+ };
+ Process process = Runtime.getRuntime().exec(command);
+ GrabProcessOutput.grabProcessOutput(
+ process,
+ Wait.ASYNC,
+ new IProcessOutput() {
+ @Override
+ public void out(String line) {
+ // Ignore stdout
+ }
+
+ @Override
+ public void err(String line) {
+ if (line != null) {
+ logger.printf("[SDK Manager] %s", line);
+ }
+ }
+ });
+ } catch (Exception ignore) {
+ }
+
+ return true;
+ }
+
+ /**
+ * Opens the SDK Manager bundled within ADT.
+ * The call is blocking and does not return till the SD Manager window is closed.
+ *
+ * @return True if the SDK location is known and the SDK Manager was started.
+ * False if the SDK location is not set and we can't open a SDK Manager to
+ * manage files in an unknown location.
+ */
+ public static boolean openAdtSdkManager() {
+ final Sdk sdk = Sdk.getCurrent();
+ if (sdk == null) {
+ return false;
+ }
+
+ // Runs the updater window, directing only warning/errors logs to the ADT console
+ // (normal log is just dropped, which is fine since the SDK Manager has its own
+ // log window now.)
+
+ SdkUpdaterWindow window = new SdkUpdaterWindow(
+ AdtPlugin.getDisplay().getActiveShell(),
+ new AdtConsoleSdkLog() {
+ @Override
+ public void printf(String msgFormat, Object... args) {
+ // Do not show non-error/warning log in Eclipse.
+ };
+ },
+ sdk.getSdkLocation(),
+ SdkInvocationContext.IDE);
+
+ ISdkChangeListener listener = new ISdkChangeListener() {
+ @Override
+ public void onSdkLoaded() {
+ // Ignore initial load of the SDK.
+ }
+
+ /**
+ * Unload all we can from the SDK before new packages are installed.
+ * Typically we need to get rid of references to dx from platform-tools
+ * and to any platform resource data.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public void preInstallHook() {
+
+ // TODO we need to unload as much of as SDK as possible. Otherwise
+ // on Windows we end up with Eclipse locking some files and we can't
+ // replace them.
+ //
+ // At this point, we know what the user wants to install so it would be
+ // possible to pass in flags to know what needs to be unloaded. Typically
+ // we need to:
+ // - unload dex if platform-tools is going to be updated. There's a vague
+ // attempt below at removing any references to dex and GCing. Seems
+ // to do the trick.
+ // - unload any target that is going to be updated since it may have
+ // resource data used by a current layout editor (e.g. data/*.ttf
+ // and various data/res/*.xml).
+ //
+ // Most important we need to make sure there isn't a build going on
+ // and if there is one, either abort it or wait for it to complete and
+ // then we want to make sure we don't get any attempt to use the SDK
+ // before the postInstallHook is called.
+
+ if (sdk != null) {
+ sdk.unloadTargetData(true /*preventReload*/);
+
+ DexWrapper dx = sdk.getDexWrapper();
+ dx.unload();
+ }
+ }
+
+ /**
+ * Nothing to do. We'll reparse the SDK later in onSdkReload.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public void postInstallHook() {
+ }
+
+ /**
+ * Reparse the SDK in case anything was add/removed.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSdkReload() {
+ AdtPlugin.getDefault().reparseSdk();
+ }
+ };
+
+ window.addListener(listener);
+ window.open();
+
+ return true;
+ }
+
@Override
public void selectionChanged(IAction action, ISelection selection) {
// nothing related to the current selection.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
index 3ae9f64..7536c2f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
@@ -41,6 +41,7 @@
import com.android.sdklib.internal.build.SignedJarBuilder;
import com.android.sdklib.util.GrabProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
@@ -512,7 +513,7 @@
return compiledPaths;
}
- public void runProguard(File proguardConfig, File inputJar, String[] jarFiles,
+ public void runProguard(List<File> proguardConfigs, File inputJar, String[] jarFiles,
File obfuscatedJar, File logOutput)
throws ProguardResultException, ProguardExecException, IOException {
IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
@@ -521,7 +522,10 @@
List<String> command = new ArrayList<String>();
command.add(AdtPlugin.getOsAbsoluteProguard());
- command.add("@" + quotePath(proguardConfig.getAbsolutePath())); //$NON-NLS-1$
+ for (File configFile : proguardConfigs) {
+ command.add("-include"); //$NON-NLS-1$
+ command.add(quotePath(configFile.getAbsolutePath()));
+ }
command.add("-injars"); //$NON-NLS-1$
StringBuilder sb = new StringBuilder(quotePath(inputJar.getAbsolutePath()));
@@ -1196,7 +1200,7 @@
return GrabProcessOutput.grabProcessOutput(
process,
- false /*waitForReaders*/,
+ Wait.WAIT_FOR_PROCESS,
new IProcessOutput() {
@SuppressWarnings("unused")
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
index 426149e..eff883b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
@@ -17,6 +17,7 @@
package com.android.ide.eclipse.adt.internal.preferences;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
import com.android.sdklib.IAndroidTarget;
@@ -140,13 +141,17 @@
boolean ok = AdtPlugin.getDefault().checkSdkLocationAndId(fileName,
new AdtPlugin.CheckSdkErrorHandler() {
@Override
- public boolean handleError(String message) {
+ public boolean handleError(
+ CheckSdkErrorHandler.Solution solution,
+ String message) {
setErrorMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$
return false; // Apply/OK must be disabled
}
@Override
- public boolean handleWarning(String message) {
+ public boolean handleWarning(
+ CheckSdkErrorHandler.Solution solution,
+ String message) {
showMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$
return true; // Apply/OK must be enabled
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
index 1e5171c..93fe43d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java
@@ -16,6 +16,8 @@
package com.android.ide.eclipse.adt.internal.project;
+import static com.android.sdklib.internal.project.ProjectProperties.PROPERTY_SDK;
+
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
@@ -25,6 +27,7 @@
import com.android.ide.eclipse.adt.internal.build.NativeLibInJarException;
import com.android.ide.eclipse.adt.internal.build.ProguardExecException;
import com.android.ide.eclipse.adt.internal.build.ProguardResultException;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.io.IFileWrapper;
@@ -58,6 +61,7 @@
import java.io.OutputStream;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
@@ -66,8 +70,10 @@
* Export helper to export release version of APKs.
*/
public final class ExportHelper {
-
- private final static String TEMP_PREFIX = "android_"; //$NON-NLS-1$
+ private static final String HOME_PROPERTY = "user.home"; //$NON-NLS-1$
+ private static final String HOME_PROPERTY_REF = "${" + HOME_PROPERTY + '}'; //$NON-NLS-1$
+ private static final String SDK_PROPERTY_REF = "${" + PROPERTY_SDK + '}'; //$NON-NLS-1$
+ private final static String TEMP_PREFIX = "android_"; //$NON-NLS-1$
/**
* Exports a release version of the application created by the given project.
@@ -75,7 +81,8 @@
* @param outputFile the file to write
* @param key the key to used for signing. Can be null.
* @param certificate the certificate used for signing. Can be null.
- * @param monitor
+ * @param monitor progress monitor
+ * @throws CoreException if an error occurs
*/
public static void exportReleaseApk(IProject project, File outputFile, PrivateKey key,
X509Certificate certificate, IProgressMonitor monitor) throws CoreException {
@@ -151,13 +158,46 @@
ProjectProperties.PROPERTY_PROGUARD_CONFIG);
boolean runProguard = false;
- File proguardConfigFile = null;
+ List<File> proguardConfigFiles = null;
if (proguardConfig != null && proguardConfig.length() > 0) {
- proguardConfigFile = new File(proguardConfig);
- if (proguardConfigFile.isAbsolute() == false) {
- proguardConfigFile = new File(project.getLocation().toFile(), proguardConfig);
+ // Be tolerant with respect to file and path separators just like
+ // Ant is. Allow "/" in the property file to mean whatever the file
+ // separator character is:
+ if (File.separatorChar != '/' && proguardConfig.indexOf('/') != -1) {
+ proguardConfig = proguardConfig.replace('/', File.separatorChar);
}
- runProguard = proguardConfigFile.isFile();
+ // Also split path: no need to convert to File.pathSeparator because we'll
+ // be splitting the path ourselves right here, so just ensure that both
+ // ':' and ';' work:
+ if (proguardConfig.indexOf(';') != -1) {
+ proguardConfig = proguardConfig.replace(';', ':');
+ }
+ String[] paths = proguardConfig.split(":"); //$NON-NLS-1$
+
+ for (String path : paths) {
+ if (path.startsWith(SDK_PROPERTY_REF)) {
+ path = AdtPrefs.getPrefs().getOsSdkFolder() +
+ path.substring(SDK_PROPERTY_REF.length());
+ } else if (path.startsWith(HOME_PROPERTY_REF)) {
+ path = System.getProperty(HOME_PROPERTY) +
+ path.substring(HOME_PROPERTY_REF.length());
+ }
+ File proguardConfigFile = new File(path);
+ if (proguardConfigFile.isAbsolute() == false) {
+ proguardConfigFile = new File(project.getLocation().toFile(), path);
+ }
+ if (proguardConfigFile.isFile()) {
+ if (proguardConfigFiles == null) {
+ proguardConfigFiles = new ArrayList<File>();
+ }
+ proguardConfigFiles.add(proguardConfigFile);
+ runProguard = true;
+ } else {
+ throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ "Invalid proguard configuration file path " + proguardConfigFile
+ + " does not exist or is not a regular file", null));
+ }
+ }
}
String[] dxInput;
@@ -188,7 +228,7 @@
obfuscatedJar.deleteOnExit();
// run proguard
- helper.runProguard(proguardConfigFile, inputJar, jarFiles, obfuscatedJar,
+ helper.runProguard(proguardConfigFiles, inputJar, jarFiles, obfuscatedJar,
new File(project.getLocation().toFile(), SdkConstants.FD_PROGUARD));
// dx input is proguard's output
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java
index 2b3e2c2..96add71 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java
@@ -17,6 +17,7 @@
package com.android.ide.eclipse.adt.internal.welcome;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.sdkstats.DdmsPreferenceStore;
import com.android.sdkstats.SdkStatsService;
@@ -83,12 +84,16 @@
boolean ok = AdtPlugin.getDefault().checkSdkLocationAndId(osSdkPath,
new AdtPlugin.CheckSdkErrorHandler() {
@Override
- public boolean handleError(String message) {
+ public boolean handleError(
+ CheckSdkErrorHandler.Solution solution,
+ String message) {
return false;
}
@Override
- public boolean handleWarning(String message) {
+ public boolean handleWarning(
+ CheckSdkErrorHandler.Solution solution,
+ String message) {
return true;
}
});
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java
index d418432..4e580fe 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.welcome;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.wizard.WizardPage;
@@ -262,14 +263,18 @@
AdtPlugin.getDefault().checkSdkLocationAndId(path,
new AdtPlugin.CheckSdkErrorHandler() {
@Override
- public boolean handleError(String message) {
+ public boolean handleError(
+ CheckSdkErrorHandler.Solution solution,
+ String message) {
message = message.replaceAll("\n", " "); //$NON-NLS-1$ //$NON-NLS-2$
errorReference.set(message);
return false; // Apply/OK must be disabled
}
@Override
- public boolean handleWarning(String message) {
+ public boolean handleWarning(
+ CheckSdkErrorHandler.Solution solution,
+ String message) {
message = message.replaceAll("\n", " "); //$NON-NLS-1$ //$NON-NLS-2$
warningReference.set(message);
return true; // Apply/OK must be enabled
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
index 4ce8230..8fcf902 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
@@ -24,6 +24,7 @@
import com.android.sdklib.internal.build.KeystoreHelper;
import com.android.sdklib.util.GrabProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
@@ -543,7 +544,7 @@
int status = GrabProcessOutput.grabProcessOutput(
process,
- true /*waitForReaders*/,
+ Wait.WAIT_FOR_READERS,
new IProcessOutput() {
@Override
public void out(String line) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java
index 97da3ab..7424d6c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java
@@ -606,7 +606,10 @@
File libFolder = new File((String) parameters.get(PARAM_SDK_TOOLS_DIR),
SdkConstants.FD_LIB);
addLocalFile(project,
- new File(libFolder, SdkConstants.FN_PROGUARD_CFG),
+ new File(libFolder, SdkConstants.FN_PROJECT_PROGUARD_FILE),
+ // Write ProGuard config files with the extension .pro which
+ // is what is used in the ProGuard documentation and samples
+ SdkConstants.FN_PROJECT_PROGUARD_FILE,
monitor);
// Set output location
@@ -1080,13 +1083,14 @@
/**
* Adds a file to the root of the project
* @param project the project to add the file to.
+ * @param destName the name to write the file as
* @param source the file to add. It'll keep the same filename once copied into the project.
* @throws FileNotFoundException
* @throws CoreException
*/
- private void addLocalFile(IProject project, File source, IProgressMonitor monitor)
- throws FileNotFoundException, CoreException {
- IFile dest = project.getFile(source.getName());
+ private void addLocalFile(IProject project, File source, String destName,
+ IProgressMonitor monitor) throws FileNotFoundException, CoreException {
+ IFile dest = project.getFile(destName);
if (dest.exists() == false) {
FileInputStream stream = new FileInputStream(source);
dest.create(stream, false /* force */, new SubProgressMonitor(monitor, 10));
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java
index d5bd895..ea78127 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java
@@ -15,6 +15,9 @@
*/
package com.android.ide.eclipse.adt.internal.wizards.newproject;
+import static com.android.sdklib.SdkConstants.FN_PROJECT_PROGUARD_FILE;
+import static com.android.sdklib.SdkConstants.OS_SDK_TOOLS_LIB_FOLDER;
+
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode;
@@ -26,6 +29,8 @@
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
+import java.io.File;
+
/**
* A "New Android Project" Wizard.
@@ -105,6 +110,16 @@
@Override
public boolean performFinish() {
+ File file = new File(AdtPlugin.getOsSdkFolder(), OS_SDK_TOOLS_LIB_FOLDER + File.separator
+ + FN_PROJECT_PROGUARD_FILE);
+ if (!file.exists()) {
+ AdtPlugin.displayError("Tools Out of Date?",
+ String.format("It looks like you do not have the latest version of the "
+ + "SDK Tools installed. Make sure you update via the SDK Manager "
+ + "first. (Could not find %1$s)", file.getPath()));
+ return false;
+ }
+
NewProjectCreator creator = new NewProjectCreator(mValues, getContainer());
if (!(creator.createAndroidProjects())) {
return false;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java
index 3db3353..6de6556 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java
@@ -18,8 +18,11 @@
import static com.android.ide.eclipse.adt.AdtUtils.capitalize;
import static com.android.ide.eclipse.adt.AdtUtils.stripWhitespace;
import static com.android.ide.eclipse.adt.internal.wizards.newproject.ApplicationInfoPage.ACTIVITY_NAME_SUFFIX;
+import static com.android.sdklib.SdkConstants.FN_PROJECT_PROGUARD_FILE;
+import static com.android.sdklib.SdkConstants.OS_SDK_TOOLS_LIB_FOLDER;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.VersionCheck;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode;
import com.android.sdklib.SdkConstants;
@@ -85,6 +88,15 @@
private Button mBrowseButton;
private Label mLocationLabel;
private WorkingSetGroup mWorkingSetGroup;
+ /**
+ * Whether we've made sure the Tools are up to date (enough that all the
+ * resources required by the New Project wizard are present -- we don't
+ * necessarily check for newer versions than that here; that's done by
+ * {@link VersionCheck}, though that check doesn't <b>enforce</b> an update
+ * since it needs to allow the user to proceed to access the SDK manager
+ * etc.)
+ */
+ private boolean mCheckedSdkUptodate;
/**
* Create the wizard.
@@ -446,6 +458,20 @@
}
}
+ if (!mCheckedSdkUptodate) {
+ // Ensure that we have a recent enough version of the Tools that the right templates
+ // are available
+ File file = new File(AdtPlugin.getOsSdkFolder(), OS_SDK_TOOLS_LIB_FOLDER
+ + File.separator + FN_PROJECT_PROGUARD_FILE);
+ if (!file.exists()) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ String.format("You do not have the latest version of the "
+ + "SDK Tools installed: Please update. (Missing %1$s)", file.getPath()));
+ } else {
+ mCheckedSdkUptodate = true;
+ }
+ }
+
// -- update UI & enable finish if there's no error
setPageComplete(status == null || status.getSeverity() != IStatus.ERROR);
if (status != null) {
diff --git a/files/ant/build.xml b/files/ant/build.xml
index 622d558..e47c3fe 100644
--- a/files/ant/build.xml
+++ b/files/ant/build.xml
@@ -766,13 +766,24 @@
</firstmatchmapper>
</pathconvert>
+ <!-- Turn the path property ${proguard.config} from an A:B:C property
+ into a series of includes: -include A -include B -include C
+ suitable for processing by the ProGuard task. Note - this does
+ not include the leading '-include "' or the closing '"'; those
+ are added under the <proguard> call below.
+ -->
+ <path id="proguard.configpath">
+ <pathelement path="${proguard.config}"/>
+ </path>
+ <pathconvert pathsep='" -include "' property="proguard.configcmd" refid="proguard.configpath"/>
+
<mkdir dir="${obfuscate.absolute.dir}" />
<delete file="${preobfuscate.jar.file}"/>
<delete file="${obfuscated.jar.file}"/>
<jar basedir="${out.classes.absolute.dir}"
destfile="${preobfuscate.jar.file}" />
<proguard>
- @${proguard.config}
+ -include "${proguard.configcmd}"
-injars ${project.jars}
-outjars "${obfuscated.jar.file}"
-libraryjars ${android.libraryjars}
diff --git a/files/proguard-android.txt b/files/proguard-android.txt
new file mode 100644
index 0000000..b3d2fe1
--- /dev/null
+++ b/files/proguard-android.txt
@@ -0,0 +1,77 @@
+# This is a configuration file for ProGuard.
+# http://proguard.sourceforge.net/index.html#manual/usage.html
+
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-verbose
+
+# Optimization is turned off by default. Dex does not like code run
+# through the ProGuard optimize and preverify steps (and performs some
+# of these optimizations on its own).
+-dontoptimize
+-dontpreverify
+
+# If you want to enable optimization, you should include the
+# following:
+# -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
+# -optimizationpasses 5
+# -allowaccessmodification
+#
+# Note that you cannot just include these flags in your own
+# configuration file; if you are including this file, optimization
+# will be turned off. You'll need to either edit this file, or
+# duplicate the contents of this file and remove the include of this
+# file from your project's proguard.config path property.
+
+-keepattributes *Annotation*
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgent
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keep public class * extends android.view.View {
+ public <init>(android.content.Context);
+ public <init>(android.content.Context, android.util.AttributeSet);
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+ public void set*(...);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
+
+-keepclassmembers class **.R$* {
+ public static <fields>;
+}
+
+# The support library contains references to newer platform versions.
+# Don't warn about those in case this app is linking against an older
+# platform version. We know about them, and they are safe.
+-dontwarn android.support.**
diff --git a/files/proguard-project.txt b/files/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/files/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
index 4ebc9ed..3f32727 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
@@ -21,7 +21,8 @@
import static com.android.tools.lint.detector.api.LintConstants.DOT_CLASS;
import static com.android.tools.lint.detector.api.LintConstants.DOT_JAR;
import static com.android.tools.lint.detector.api.LintConstants.DOT_JAVA;
-import static com.android.tools.lint.detector.api.LintConstants.PROGUARD_CFG;
+import static com.android.tools.lint.detector.api.LintConstants.OLD_PROGUARD_FILE;
+import static com.android.tools.lint.detector.api.LintConstants.PROGUARD_FILE;
import static com.android.tools.lint.detector.api.LintConstants.RES_FOLDER;
import static com.android.tools.lint.detector.api.LintConstants.SUPPRESS_ALL;
import static com.android.tools.lint.detector.api.LintConstants.SUPPRESS_LINT;
@@ -46,6 +47,8 @@
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.XmlContext;
import com.google.common.annotations.Beta;
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.io.ByteStreams;
@@ -215,7 +218,7 @@
mScope.add(Scope.MANIFEST);
} else if (name.endsWith(".xml")) {
mScope.add(Scope.RESOURCE_FILE);
- } else if (name.equals(PROGUARD_CFG)) {
+ } else if (name.equals(PROGUARD_FILE) || name.equals(OLD_PROGUARD_FILE)) {
mScope.add(Scope.PROGUARD_FILE);
} else if (name.equals(RES_FOLDER)
|| file.getParent().equals(RES_FOLDER)) {
@@ -225,6 +228,8 @@
mScope.add(Scope.JAVA_FILE);
} else if (name.endsWith(DOT_CLASS)) {
mScope.add(Scope.CLASS_FILE);
+ } else if (name.equals(OLD_PROGUARD_FILE) || name.equals(PROGUARD_FILE)) {
+ mScope.add(Scope.PROGUARD_FILE);
}
}
} else {
@@ -740,18 +745,50 @@
}
if (project == main && mScope.contains(Scope.PROGUARD_FILE)) {
- List<Detector> detectors = mScopeDetectors.get(Scope.PROGUARD_FILE);
- if (detectors != null) {
- File file = new File(project.getDir(), PROGUARD_CFG);
+ checkProGuard(project, main);
+ }
+ }
+
+ private void checkProGuard(Project project, Project main) {
+ List<Detector> detectors = mScopeDetectors.get(Scope.PROGUARD_FILE);
+ if (detectors != null) {
+ Project p = main != null ? main : project;
+ List<File> files = new ArrayList<File>();
+ String paths = p.getProguardPath();
+ if (paths != null) {
+ Splitter splitter = Splitter.on(CharMatcher.anyOf(":;")); //$NON-NLS-1$
+ for (String path : splitter.split(paths)) {
+ if (path.contains("${")) { //$NON-NLS-1$
+ // Don't analyze the global/user proguard files
+ continue;
+ }
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ file = new File(project.getDir(), path);
+ }
+ if (file.exists()) {
+ files.add(file);
+ }
+ }
+ }
+ if (files.isEmpty()) {
+ File file = new File(project.getDir(), OLD_PROGUARD_FILE);
if (file.exists()) {
- Context context = new Context(this, project, main, file);
- fireEvent(EventType.SCANNING_FILE, context);
- for (Detector detector : detectors) {
- if (detector.appliesTo(context, file)) {
- detector.beforeCheckFile(context);
- detector.run(context);
- detector.afterCheckFile(context);
- }
+ files.add(file);
+ }
+ file = new File(project.getDir(), PROGUARD_FILE);
+ if (file.exists()) {
+ files.add(file);
+ }
+ }
+ for (File file : files) {
+ Context context = new Context(this, project, main, file);
+ fireEvent(EventType.SCANNING_FILE, context);
+ for (Detector detector : detectors) {
+ if (detector.appliesTo(context, file)) {
+ detector.beforeCheckFile(context);
+ detector.run(context);
+ detector.afterCheckFile(context);
}
}
}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
index cb47f92..6788652 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
@@ -242,7 +242,9 @@
// Filenames and folder names
public static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml"; //$NON-NLS-1$
- public static final String PROGUARD_CFG = "proguard.cfg"; //$NON-NLS-1$
+ public static final String OLD_PROGUARD_FILE = "proguard.cfg"; //$NON-NLS-1$
+ public static final String PROGUARD_FILE = "proguard-project.txt"; //$NON-NLS-1$
+
public static final String RES_FOLDER = "res"; //$NON-NLS-1$
public static final String DOT_XML = ".xml"; //$NON-NLS-1$
public static final String DOT_GIF = ".gif"; //$NON-NLS-1$
@@ -283,6 +285,7 @@
// Project properties
public static final String ANDROID_LIBRARY = "android.library"; //$NON-NLS-1$
+ public static final String PROGUARD_CONFIG = "proguard.config"; //$NON-NLS-1$
public static final String ANDROID_LIBRARY_REFERENCE_FORMAT = "android.library.reference.%1$d";//$NON-NLS-1$
public static final String PROJECT_PROPERTIES = "project.properties";//$NON-NLS-1$
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java
index 910ee2f..d0de34c 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Project.java
@@ -23,6 +23,7 @@
import static com.android.tools.lint.detector.api.LintConstants.ATTR_MIN_SDK_VERSION;
import static com.android.tools.lint.detector.api.LintConstants.ATTR_PACKAGE;
import static com.android.tools.lint.detector.api.LintConstants.ATTR_TARGET_SDK_VERSION;
+import static com.android.tools.lint.detector.api.LintConstants.PROGUARD_CONFIG;
import static com.android.tools.lint.detector.api.LintConstants.PROJECT_PROPERTIES;
import static com.android.tools.lint.detector.api.LintConstants.TAG_USES_SDK;
import static com.android.tools.lint.detector.api.LintConstants.VALUE_TRUE;
@@ -71,6 +72,7 @@
private int mTargetSdk = -1;
private boolean mLibrary;
private String mName;
+ private String mProguardPath;
/** The SDK info, if any */
private SdkInfo mSdkInfo;
@@ -121,6 +123,7 @@
properties.load(is);
String value = properties.getProperty(ANDROID_LIBRARY);
mLibrary = VALUE_TRUE.equals(value);
+ mProguardPath = properties.getProperty(PROGUARD_CONFIG);
for (int i = 1; i < 1000; i++) {
String key = String.format(ANDROID_LIBRARY_REFERENCE_FORMAT, i);
@@ -545,6 +548,17 @@
}
/**
+ * Returns the proguard path configured for this project, or null if ProGuard is
+ * not configured.
+ *
+ * @return the proguard path, or null
+ */
+ @Nullable
+ public String getProguardPath() {
+ return mProguardPath;
+ }
+
+ /**
* Returns the name of the project
*
* @return the name of the project, never null
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index 258a267..3af53ad 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -53,7 +53,7 @@
private static final List<Issue> sIssues;
static {
- final int initialCapacity = 81;
+ final int initialCapacity = 82;
List<Issue> issues = new ArrayList<Issue>(initialCapacity);
issues.add(AccessibilityDetector.ISSUE);
@@ -89,7 +89,8 @@
issues.add(TranslationDetector.MISSING);
issues.add(HardcodedValuesDetector.ISSUE);
issues.add(Utf8Detector.ISSUE);
- issues.add(ProguardDetector.ISSUE);
+ issues.add(ProguardDetector.WRONGKEEP);
+ issues.add(ProguardDetector.SPLITCONFIG);
issues.add(PxUsageDetector.ISSUE);
issues.add(TextFieldDetector.ISSUE);
issues.add(TextViewDetector.ISSUE);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
index 95de739..c062507 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ProguardDetector.java
@@ -16,6 +16,9 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.detector.api.LintConstants.PROGUARD_CONFIG;
+import static com.android.tools.lint.detector.api.LintConstants.PROJECT_PROPERTIES;
+
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
@@ -34,10 +37,10 @@
public class ProguardDetector extends Detector {
/** The main issue discovered by this detector */
- public static final Issue ISSUE = Issue.create(
+ public static final Issue WRONGKEEP = Issue.create(
"Proguard", //$NON-NLS-1$
- "Looks for problems in proguard.cfg files",
- "Using -keepclasseswithmembernames in a proguard.cfg file is not " +
+ "Looks for problems in proguard config files",
+ "Using -keepclasseswithmembernames in a proguard config file is not " +
"correct; it can cause some symbols to be renamed which should not be.\n" +
"Earlier versions of ADT used to create proguard.cfg files with the " +
"wrong format. Instead of -keepclasseswithmembernames use " +
@@ -51,25 +54,105 @@
EnumSet.of(Scope.PROGUARD_FILE)).setMoreInfo(
"http://http://code.google.com/p/android/issues/detail?id=16384"); //$NON-NLS-1$
+ /** Finds ProGuard files that contain non-project specific configuration
+ * locally and suggests replacing it with an include path */
+ public static final Issue SPLITCONFIG = Issue.create(
+ "ProguardSplit", //$NON-NLS-1$
+ "Checks for old proguard.cfg files that contain generic Android rules",
+
+ "Earlier versions of the Android tools bundled a single \"proguard.cfg\" file " +
+ "containing a ProGuard configuration file suitable for Android shrinking and " +
+ "obfuscation. However, that version was copied into new projects, which " +
+ "means that it does not continue to get updated as we improve the default " +
+ "ProGuard rules for Android.\n" +
+ "\n" +
+ "In the new version of the tools, we have split the ProGuard configuration " +
+ "into two halves:\n" +
+ "* A simple configuration file containing only project-specific flags, in " +
+ "your project\n" +
+ "* A generic configuration file containing the recommended set of ProGuard " +
+ "options for Android projects. This generic file lives in the SDK install " +
+ "directory which means that it gets updated along with the tools.\n" +
+ "\n" +
+ "In order for this to work, the proguard.config property in the " +
+ "project.properties file now refers to a path, so you can reference both " +
+ "the generic file as well as your own (and any additional files too).\n" +
+ "\n" +
+ "To migrate your project to the new setup, create a new proguard-project.txt file " +
+ "in your project containing any project specific ProGuard flags as well as " +
+ "any customizations you have made, then update your project.properties file " +
+ "to contain:\n" +
+ "proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt",
+
+ Category.CORRECTNESS,
+ 3,
+ Severity.WARNING,
+ ProguardDetector.class,
+ EnumSet.of(Scope.PROGUARD_FILE));
+
@Override
public void run(Context context) {
String contents = context.getContents();
if (contents != null) {
- int index = contents.indexOf(
- // Old pattern:
- "-keepclasseswithmembernames class * {\n" + //$NON-NLS-1$
- " public <init>(android."); //$NON-NLS-1$
- if (index != -1) {
- context.report(ISSUE,
- Location.create(context.file, contents, index, index),
- "Obsolete proguard file; use -keepclasseswithmembers instead of -keepclasseswithmembernames", null);
+ if (context.isEnabled(WRONGKEEP)) {
+ int index = contents.indexOf(
+ // Old pattern:
+ "-keepclasseswithmembernames class * {\n" + //$NON-NLS-1$
+ " public <init>(android."); //$NON-NLS-1$
+ if (index != -1) {
+ context.report(WRONGKEEP,
+ Location.create(context.file, contents, index, index),
+ "Obsolete ProGuard file; use -keepclasseswithmembers instead of " +
+ "-keepclasseswithmembernames", null);
+ }
+ }
+ if (context.isEnabled(SPLITCONFIG)) {
+ int index = contents.indexOf("-keep public class * extends android.app.Activity");
+ if (index != -1) {
+ // Only complain if project.properties actually references this file;
+ // no need to bother the users who got a default proguard.cfg file
+ // when they created their projects but haven't actually hooked it up
+ // to shrinking & obfuscation.
+ File propertyFile = new File(context.file.getParentFile(), PROJECT_PROPERTIES);
+ if (!propertyFile.exists()) {
+ return;
+ }
+ String properties = context.getClient().readFile(propertyFile);
+ int i = properties.indexOf(PROGUARD_CONFIG);
+ if (i == -1) {
+ return;
+ }
+ // Make sure the entry isn't just commented out, such as
+ // # To enable ProGuard to shrink and obfuscate your code, uncomment this:
+ // #proguard.config=proguard.cfg
+ for (; i >= 0; i--) {
+ char c = properties.charAt(i);
+ if (c == '#') {
+ return;
+ }
+ if (c == '\n') {
+ break;
+ }
+ }
+ if (properties.contains(PROGUARD_CONFIG)) {
+ context.report(SPLITCONFIG,
+ Location.create(context.file, contents, index, index),
+ String.format(
+ "Local ProGuard configuration contains general Android " +
+ "configuration: Inherit these settings instead? " +
+ "Modify project.properties to define " +
+ "proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:%1$s" +
+ " and then keep only project-specific configuration here",
+ context.file.getName()), null);
+ }
+ }
}
}
}
@Override
public boolean appliesTo(Context context, File file) {
- return file.getName().equals("proguard.cfg"); //$NON-NLS-1$
+ return true;
}
@Override
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ProguardDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ProguardDetectorTest.java
index 019effd..f3d872b 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ProguardDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ProguardDetectorTest.java
@@ -27,8 +27,60 @@
public void testProguard() throws Exception {
assertEquals(
- "proguard.cfg:21: Error: Obsolete proguard file; use -keepclasseswithmembers " +
+ "proguard.cfg:21: Error: Obsolete ProGuard file; use -keepclasseswithmembers " +
"instead of -keepclasseswithmembernames",
lintFiles("proguard.cfg"));
}
+
+ public void testProguardNewPath() throws Exception {
+ assertEquals(
+ "proguard-project.txt:21: Error: Obsolete ProGuard file; use " +
+ "-keepclasseswithmembers instead of -keepclasseswithmembernames",
+ lintFiles("proguard.cfg=>proguard-project.txt"));
+ }
+
+ public void testProguardRandomName() throws Exception {
+ assertEquals(
+ "myfile.txt:21: Error: Obsolete ProGuard file; use -keepclasseswithmembers " +
+ "instead of -keepclasseswithmembernames\n" +
+ "myfile.txt:8: Warning: Local ProGuard configuration contains general " +
+ "Android configuration: Inherit these settings instead? Modify " +
+ "project.properties to define proguard.config=${sdk.dir}/tools/proguard/" +
+ "proguard-android.txt:myfile.txt and then keep only project-specific " +
+ "configuration here",
+ lintProject(
+ "proguard.cfg=>myfile.txt",
+ "proguard.properties=>project.properties"));
+ }
+
+ public void testSilent() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintFiles(
+ "proguard.pro=>proguard.cfg",
+ "project.properties1=>project.properties"));
+ }
+
+ public void testSilent2() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintFiles(
+ "proguard.pro=>proguard.cfg",
+ "project.properties3=>project.properties"));
+ }
+
+ public void testSplit() throws Exception {
+ assertEquals(
+ "proguard.cfg:14: Warning: Local ProGuard configuration contains general " +
+ "Android configuration: Inherit these settings instead? Modify " +
+ "project.properties to define " +
+ "proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard.cfg " +
+ "and then keep only project-specific configuration here",
+
+ lintFiles(
+ "proguard.pro=>proguard.cfg",
+ "project.properties2=>project.properties"));
+ }
}
diff --git a/files/proguard.cfg b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/proguard.pro
similarity index 100%
rename from files/proguard.cfg
rename to lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/proguard.pro
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/proguard.properties b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/proguard.properties
new file mode 100644
index 0000000..989c3c7
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/proguard.properties
@@ -0,0 +1,2 @@
+target=android-14
+proguard.config=${sdk.dir}/foo.cfg:${user.home}/bar.pro;myfile.txt
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties1 b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties1
new file mode 100644
index 0000000..2b783dd
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties1
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this:
+#proguard.config=proguard.cfg
+
+# Project target.
+target=android-3
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties2 b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties2
new file mode 100644
index 0000000..d9a28ec
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties2
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this:
+proguard.config=proguard.cfg
+
+# Project target.
+target=android-3
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties3 b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties3
new file mode 100644
index 0000000..3cb9d31
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties3
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-3
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties4 b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties4
new file mode 100644
index 0000000..fca9311
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/project.properties4
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this:
+proguard.config=${sdk.dir}/tools/proguard/android-defaults.pro:proguard.pro
+
+# Project target.
+target=android-3
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index 4f3ed9f..e21e14f 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -166,8 +166,10 @@
*/
public final static String FN_GDBSERVER = "gdbserver"; //$NON-NLS-1$
- /** default proguard config file */
- public final static String FN_PROGUARD_CFG = "proguard.cfg"; //$NON-NLS-1$
+ /** global Android proguard config file */
+ public final static String FN_ANDROID_PROGUARD_FILE = "proguard-android.txt"; //$NON-NLS-1$
+ /** default proguard config file with new file extension (for project specific stuff) */
+ public final static String FN_PROJECT_PROGUARD_FILE = "proguard-project.txt"; //$NON-NLS-1$
/* Folder Names for Android Projects . */
@@ -385,7 +387,6 @@
/** SDK property: default skin */
public final static String PROP_SDK_DEFAULT_SKIN = "sdk.skin.default"; //$NON-NLS-1$
-
/* Android Class Constants */
public final static String CLASS_ACTIVITY = "android.app.Activity"; //$NON-NLS-1$
public final static String CLASS_APPLICATION = "android.app.Application"; //$NON-NLS-1$
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
index 436f2e8..db3cc33 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
@@ -28,6 +28,7 @@
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.util.GrabProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
import com.android.util.Pair;
import java.io.File;
@@ -1398,7 +1399,7 @@
int status = GrabProcessOutput.grabProcessOutput(
process,
- true /*waitForReaders*/,
+ Wait.WAIT_FOR_READERS,
new IProcessOutput() {
@Override
public void out(String line) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java
index 06f5351..af5b401 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java
@@ -20,6 +20,7 @@
import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException;
import com.android.sdklib.util.GrabProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
import java.io.File;
import java.io.IOException;
@@ -107,7 +108,7 @@
Process process = Runtime.getRuntime().exec(commandArray);
result = GrabProcessOutput.grabProcessOutput(
process,
- true /*waitForReaders*/,
+ Wait.WAIT_FOR_READERS,
new IProcessOutput() {
@Override
public void out(String line) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
index c3d7993..721d165 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
@@ -369,8 +369,8 @@
keywords);
// install the proguard config file.
- installTemplate(SdkConstants.FN_PROGUARD_CFG,
- new File(projectFolder, SdkConstants.FN_PROGUARD_CFG),
+ installTemplate(SdkConstants.FN_PROJECT_PROGUARD_FILE,
+ new File(projectFolder, SdkConstants.FN_PROJECT_PROGUARD_FILE),
null /*keywords*/);
} catch (Exception e) {
mLog.error(e, null);
@@ -751,8 +751,10 @@
if (hasProguard == false) {
try {
- installTemplate(SdkConstants.FN_PROGUARD_CFG,
- new File(projectFolder, SdkConstants.FN_PROGUARD_CFG),
+ installTemplate(SdkConstants.FN_PROJECT_PROGUARD_FILE,
+ // Write ProGuard config files with the extension .pro which
+ // is what is used in the ProGuard documentation and samples
+ new File(projectFolder, SdkConstants.FN_PROJECT_PROGUARD_FILE),
null /*placeholderMap*/);
} catch (ProjectCreateException e) {
mLog.error(e, null);
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
index 3a73bef..2bb6b71 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
@@ -16,6 +16,11 @@
package com.android.sdklib.internal.project;
+import static com.android.sdklib.SdkConstants.FD_PROGUARD;
+import static com.android.sdklib.SdkConstants.FD_TOOLS;
+import static com.android.sdklib.SdkConstants.FN_ANDROID_PROGUARD_FILE;
+import static com.android.sdklib.SdkConstants.FN_PROJECT_PROGUARD_FILE;
+
import com.android.io.FolderWrapper;
import com.android.io.IAbstractFile;
import com.android.io.IAbstractFolder;
@@ -24,6 +29,7 @@
import com.android.sdklib.SdkConstants;
import java.io.BufferedReader;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -163,7 +169,7 @@
"# This file is automatically generated by Android Tools.\n" +
"# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
"#\n" +
- "# This file must *NOT* be checked in Version Control Systems,\n" +
+ "# This file must *NOT* be checked into Version Control Systems,\n" +
"# as it contains information specific to your local configuration.\n" +
"\n";
@@ -174,16 +180,22 @@
"#\n" +
"# This file must be checked in Version Control Systems.\n" +
"#\n" +
- "# To customize properties used by the Ant build system use,\n" +
+ "# To customize properties used by the Ant build system edit\n" +
"# \"ant.properties\", and override values to adapt the script to your\n" +
"# project structure.\n" +
+ "#\n" +
+ "# To enable ProGuard to shrink and obfuscate your code, uncomment this "
+ + "(available properties: sdk.dir, user.home):\n" +
+ "#" + PROPERTY_PROGUARD_CONFIG + "=${" + PROPERTY_SDK +"}" + File.separator
+ + FD_TOOLS + File.separator + FD_PROGUARD + File.separator
+ + FN_ANDROID_PROGUARD_FILE + ':' + FN_PROJECT_PROGUARD_FILE +'\n' +
"\n";
private final static String BUILD_HEADER =
// 1-------10--------20--------30--------40--------50--------60--------70--------80
"# This file is used to override default values used by the Ant build system.\n" +
"#\n" +
- "# This file must be checked in Version Control Systems, as it is\n" +
+ "# This file must be checked into Version Control Systems, as it is\n" +
"# integral to the build system of your project.\n" +
"\n" +
"# This file is only used by the Ant script.\n" +
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
index 9e50430..2e2396f 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
@@ -25,6 +25,7 @@
import com.android.sdklib.repository.RepoConstants;
import com.android.sdklib.util.GrabProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
@@ -598,7 +599,7 @@
Process process = Runtime.getRuntime().exec(command);
int retCode = GrabProcessOutput.grabProcessOutput(
process,
- true /*waitForReaders*/,
+ Wait.WAIT_FOR_READERS,
new IProcessOutput() {
@Override
public void out(String line) {
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 ffd561f..3ddacb4 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
@@ -25,6 +25,7 @@
import com.android.sdklib.repository.SdkRepoConstants;
import com.android.sdklib.util.GrabProcessOutput;
import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
import org.w3c.dom.Node;
@@ -276,7 +277,7 @@
final String tag = scriptName;
status = GrabProcessOutput.grabProcessOutput(
proc,
- false /*waitForReaders*/,
+ Wait.WAIT_FOR_PROCESS,
new IProcessOutput() {
@Override
public void out(String line) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java
index b470153..2935493 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java
@@ -25,6 +25,35 @@
public class GrabProcessOutput {
+ public enum Wait {
+ /**
+ * Doesn't wait for the exec to complete.
+ * This still monitors the output but does not wait for the process to finish.
+ * In this mode the process return code is unknown and always 0.
+ */
+ ASYNC,
+ /**
+ * This waits for the process to finish.
+ * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the
+ * error code from the process.
+ * In some rare cases and depending on the OS, the process might not have
+ * finished dumping data into stdout/stderr.
+ * <p/>
+ * Use this when you don't particularly care for the output but instead
+ * care for the return code of the executed process.
+ */
+ WAIT_FOR_PROCESS,
+ /**
+ * This waits for the process to finish <em>and</em> for the stdout/stderr
+ * threads to complete.
+ * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the
+ * error code from the process.
+ * <p/>
+ * Use this one when capturing all the output from the process is important.
+ */
+ WAIT_FOR_READERS,
+ }
+
public interface IProcessOutput {
/**
* Processes an stdout message line.
@@ -46,13 +75,13 @@
* @param output Optional object to capture stdout/stderr.
* Note that on Windows capturing the output is not optional. If output is null
* the stdout/stderr will be captured and discarded.
- * @param waitForReaders True to wait for the reader threads to finish.
+ * @param waitMode Whether to wait for the process and/or the readers to finish.
* @return the process return code.
* @throws InterruptedException if {@link Process#waitFor()} was interrupted.
*/
public static int grabProcessOutput(
@NonNull final Process process,
- boolean waitForReaders,
+ Wait waitMode,
@Nullable final IProcessOutput output) throws InterruptedException {
// read the lines as they come. if null is returned, it's
// because the process finished
@@ -104,10 +133,14 @@
threadErr.start();
threadOut.start();
+ if (waitMode == Wait.ASYNC) {
+ return 0;
+ }
+
// it looks like on windows process#waitFor() can return
// before the thread have filled the arrays, so we wait for both threads and the
// process itself.
- if (waitForReaders) {
+ if (waitMode == Wait.WAIT_FOR_READERS) {
try {
threadErr.join();
} catch (InterruptedException e) {