Merge change 21617 into donut
* changes: ADT Export wizard now calls ZipAlign
This commit is contained in:
@@ -454,12 +454,17 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
|
||||
/** Returns the adb path relative to the sdk folder */
|
||||
public static String getOsRelativeAdb() {
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB;
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB;
|
||||
}
|
||||
|
||||
/** Returns the zipalign path relative to the sdk folder */
|
||||
public static String getOsRelativeZipAlign() {
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ZIPALIGN;
|
||||
}
|
||||
|
||||
/** Returns the emulator path relative to the sdk folder */
|
||||
public static String getOsRelativeEmulator() {
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR;
|
||||
return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_EMULATOR;
|
||||
}
|
||||
|
||||
/** Returns the absolute adb path */
|
||||
@@ -467,6 +472,11 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
return getOsSdkFolder() + getOsRelativeAdb();
|
||||
}
|
||||
|
||||
/** Returns the absolute zipalign path */
|
||||
public static String getOsAbsoluteZipAlign() {
|
||||
return getOsSdkFolder() + getOsRelativeZipAlign();
|
||||
}
|
||||
|
||||
/** Returns the absolute traceview path */
|
||||
public static String getOsAbsoluteTraceview() {
|
||||
return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER +
|
||||
|
||||
@@ -106,10 +106,6 @@ public class AndroidConstants {
|
||||
public final static Pattern PATTERN_RESOURCES_S_AP_ =
|
||||
Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
|
||||
|
||||
public final static String FN_ADB = SdkConstants.FN_ADB;
|
||||
|
||||
public final static String FN_EMULATOR = SdkConstants.FN_EMULATOR;
|
||||
|
||||
public final static String FN_TRACEVIEW =
|
||||
(SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
|
||||
"traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
@@ -76,7 +76,7 @@ public final class ExportHelper {
|
||||
File file = new File(binLocation.toOSString() + File.separator + fileName);
|
||||
|
||||
if (file.exists() == false || file.isFile() == false) {
|
||||
MessageDialog.openInformation(Display.getCurrent().getActiveShell(),
|
||||
MessageDialog.openError(Display.getCurrent().getActiveShell(),
|
||||
"Android IDE Plug-in",
|
||||
String.format("Failed to export %1$s: %2$s doesn't exist!",
|
||||
project.getName(), file.getPath()));
|
||||
@@ -117,7 +117,7 @@ public final class ExportHelper {
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog.openInformation(shell, "Android IDE Plug-in",
|
||||
MessageDialog.openError(shell, "Android IDE Plug-in",
|
||||
String.format("Failed to export %1$s: %2$s doesn't exist!",
|
||||
project.getName(), file.getPath()));
|
||||
return;
|
||||
@@ -159,11 +159,10 @@ public final class ExportHelper {
|
||||
// close the entry for this file
|
||||
zos.closeEntry();
|
||||
zis.closeEntry();
|
||||
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
MessageDialog.openInformation(shell, "Android IDE Plug-in",
|
||||
MessageDialog.openError(shell, "Android IDE Plug-in",
|
||||
String.format("Failed to export %1$s: %2$s",
|
||||
project.getName(), e.getMessage()));
|
||||
} finally {
|
||||
@@ -178,9 +177,19 @@ public final class ExportHelper {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
// this is unsigned export. Let's tell the developers to run zip align
|
||||
MessageDialog.openWarning(shell, "Android IDE Plug-in", String.format(
|
||||
"An unsigned package of the application was saved at\n%1$s\n\n" +
|
||||
"Before publishing the application you will need to:\n" +
|
||||
"- Sign the application with your release key,\n" +
|
||||
"- run zipalign on the signed package. ZipAlign is located in <SDK>/tools/\n\n" +
|
||||
"Aligning applications allows Android to use application resources\n" +
|
||||
"more efficiently.", saveLocation));
|
||||
|
||||
}
|
||||
} else {
|
||||
MessageDialog.openInformation(shell, "Android IDE Plug-in",
|
||||
MessageDialog.openError(shell, "Android IDE Plug-in",
|
||||
String.format("Failed to export %1$s: Could not get project output location",
|
||||
project.getName()));
|
||||
}
|
||||
|
||||
@@ -16,19 +16,18 @@
|
||||
|
||||
package com.android.ide.eclipse.adt.internal.wizards.export;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
|
||||
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
|
||||
import com.android.jarutils.KeystoreHelper;
|
||||
import com.android.jarutils.SignedJarBuilder;
|
||||
import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
|
||||
import com.android.jarutils.DebugKeyProvider.KeytoolException;
|
||||
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IncrementalProjectBuilder;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.jface.operation.IRunnableWithProgress;
|
||||
@@ -43,15 +42,16 @@ import org.eclipse.ui.IExportWizard;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.KeyStore.PrivateKeyEntry;
|
||||
import java.security.cert.X509Certificate;
|
||||
@@ -300,13 +300,25 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
||||
|
||||
// now generate the packages.
|
||||
Set<Entry<String, String[]>> set = mApkMap.entrySet();
|
||||
|
||||
boolean runZipAlign = false;
|
||||
String path = AdtPlugin.getOsAbsoluteZipAlign();
|
||||
File zipalign = new File(path);
|
||||
runZipAlign = zipalign.isFile();
|
||||
|
||||
for (Entry<String, String[]> entry : set) {
|
||||
String[] defaultApk = entry.getValue();
|
||||
String srcFilename = defaultApk[APK_FILE_SOURCE];
|
||||
String destFilename = defaultApk[APK_FILE_DEST];
|
||||
File destFile;
|
||||
if (runZipAlign) {
|
||||
destFile = File.createTempFile("android", ".apk");
|
||||
} else {
|
||||
destFile = new File(mDestinationParentFolder, destFilename);
|
||||
}
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(
|
||||
new File(mDestinationParentFolder, destFilename));
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(destFile);
|
||||
SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate);
|
||||
|
||||
// get the input file.
|
||||
@@ -316,8 +328,19 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
||||
// the same time.
|
||||
try {
|
||||
builder.writeZip(fis, null /* filter */);
|
||||
// close the builder: write the final signature files, and close the archive.
|
||||
// close the builder: write the final signature files,
|
||||
// and close the archive.
|
||||
builder.close();
|
||||
|
||||
// now zipalign the result
|
||||
if (runZipAlign) {
|
||||
File realDestFile = new File(mDestinationParentFolder, destFilename);
|
||||
String message = zipAlign(path, destFile, realDestFile);
|
||||
if (message != null) {
|
||||
displayError(message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
fis.close();
|
||||
@@ -326,20 +349,21 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// export success. In case we didn't run ZipAlign we display a warning
|
||||
if (runZipAlign == false) {
|
||||
AdtPlugin.displayWarning("Export Wizard",
|
||||
"The zipalign tool was not found in the SDK.\n\n" +
|
||||
"Please update to the latest SDK and re-export your application\n" +
|
||||
"or run zipalign manually.\n\n" +
|
||||
"Aligning applications allows Android to use application resources\n" +
|
||||
"more efficiently.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
displayError(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
displayError(e);
|
||||
} catch (IOException e) {
|
||||
displayError(e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
displayError(e);
|
||||
} catch (KeytoolException e) {
|
||||
displayError(e);
|
||||
} catch (CoreException e) {
|
||||
displayError(e);
|
||||
} catch (Throwable t) {
|
||||
displayError(t);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -540,13 +564,118 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
||||
AdtPlugin.displayError("Export Wizard", message);
|
||||
}
|
||||
|
||||
private void displayError(Exception e) {
|
||||
String message = getExceptionMessage(e);
|
||||
private void displayError(Throwable t) {
|
||||
String message = getExceptionMessage(t);
|
||||
displayError(message);
|
||||
|
||||
AdtPlugin.log(e, "Export Wizard Error");
|
||||
AdtPlugin.log(t, "Export Wizard Error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes zipalign
|
||||
* @param zipAlignPath location of the zipalign too
|
||||
* @param source file to zipalign
|
||||
* @param destination where to write the resulting file
|
||||
* @return null if success, the error otherwise
|
||||
* @throws IOException
|
||||
*/
|
||||
private String zipAlign(String zipAlignPath, File source, File destination) throws IOException {
|
||||
// command line: zipaling -f 4 tmp destination
|
||||
String[] command = new String[5];
|
||||
command[0] = zipAlignPath;
|
||||
command[1] = "-f"; //$NON-NLS-1$
|
||||
command[2] = "4"; //$NON-NLS-1$
|
||||
command[3] = source.getAbsolutePath();
|
||||
command[4] = destination.getAbsolutePath();
|
||||
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
ArrayList<String> output = new ArrayList<String>();
|
||||
try {
|
||||
if (grabProcessOutput(process, output) != 0) {
|
||||
// build a single message from the array list
|
||||
StringBuilder sb = new StringBuilder("Error while running zipalign:");
|
||||
for (String msg : output) {
|
||||
sb.append('\n');
|
||||
sb.append(msg);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// ?
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stderr output of a process and return when the process is done.
|
||||
* @param process The process to get the ouput from
|
||||
* @param results The array to store the stderr output
|
||||
* @return the process return code.
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private final int grabProcessOutput(final Process process,
|
||||
final ArrayList<String> results)
|
||||
throws InterruptedException {
|
||||
// Due to the limited buffer size on windows for the standard io (stderr, stdout), we
|
||||
// *need* to read both stdout and stderr all the time. If we don't and a process output
|
||||
// a large amount, this could deadlock the process.
|
||||
|
||||
// read the lines as they come. if null is returned, it's
|
||||
// because the process finished
|
||||
new Thread("") { //$NON-NLS-1$
|
||||
@Override
|
||||
public void run() {
|
||||
// create a buffer to read the stderr output
|
||||
InputStreamReader is = new InputStreamReader(process.getErrorStream());
|
||||
BufferedReader errReader = new BufferedReader(is);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
String line = errReader.readLine();
|
||||
if (line != null) {
|
||||
results.add(line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
new Thread("") { //$NON-NLS-1$
|
||||
@Override
|
||||
public void run() {
|
||||
InputStreamReader is = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader outReader = new BufferedReader(is);
|
||||
|
||||
IProject project = getProject();
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
String line = outReader.readLine();
|
||||
if (line != null) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
|
||||
project, line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
}.start();
|
||||
|
||||
// get the return code from the process
|
||||
return process.waitFor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link Throwable#getMessage()}. If the {@link Throwable#getMessage()} returns
|
||||
* <code>null</code>, the method is called again on the cause of the Throwable object.
|
||||
@@ -556,13 +685,11 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
||||
static String getExceptionMessage(Throwable t) {
|
||||
String message = t.getMessage();
|
||||
if (message == null) {
|
||||
Throwable cause = t.getCause();
|
||||
if (cause != null) {
|
||||
return getExceptionMessage(cause);
|
||||
}
|
||||
|
||||
// no more cause and still no message. display the first exception.
|
||||
return t.getClass().getCanonicalName();
|
||||
// no error info? get the stack call to display it
|
||||
// At least that'll give us a better bug report.
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
t.printStackTrace(new PrintStream(baos));
|
||||
message = baos.toString();
|
||||
}
|
||||
|
||||
return message;
|
||||
|
||||
@@ -107,6 +107,10 @@ public final class SdkConstants {
|
||||
public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/** zipalign executable (with extension for the current OS) */
|
||||
public final static String FN_ZIPALIGN = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
|
||||
"zipalign.exe" : "zipalign"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
/* Folder Names for Android Projects . */
|
||||
|
||||
/** Resources folder name, i.e. "res". */
|
||||
|
||||
Reference in New Issue
Block a user