diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java | 448 |
1 files changed, 0 insertions, 448 deletions
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 deleted file mode 100644 index 56e0c0938..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.project; - -import static com.android.sdklib.internal.project.ProjectProperties.PROPERTY_SDK; - -import com.android.SdkConstants; -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AndroidPrintStream; -import com.android.ide.eclipse.adt.internal.build.BuildHelper; -import com.android.ide.eclipse.adt.internal.build.DexException; -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; -import com.android.sdklib.BuildToolInfo; -import com.android.sdklib.build.ApkCreationException; -import com.android.sdklib.build.DuplicateFileException; -import com.android.sdklib.internal.project.ProjectProperties; -import com.android.tools.lint.detector.api.LintUtils; -import com.android.xml.AndroidManifest; - -import org.eclipse.core.resources.IFile; -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.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Shell; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; - -/** - * Export helper to export release version of APKs. - */ -public final class ExportHelper { - 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. - * @param project the project to export - * @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 progress monitor - * @throws CoreException if an error occurs - */ - public static void exportReleaseApk(IProject project, File outputFile, PrivateKey key, - X509Certificate certificate, IProgressMonitor monitor) throws CoreException { - - // the export, takes the output of the precompiler & Java builders so it's - // important to call build in case the auto-build option of the workspace is disabled. - // Also enable dependency building to make sure everything is up to date. - // However do not package the APK since we're going to do it manually here, using a - // different output location. - ProjectHelper.compileInReleaseMode(project, monitor); - - // if either key or certificate is null, ensure the other is null. - if (key == null) { - certificate = null; - } else if (certificate == null) { - key = null; - } - - try { - // check if the manifest declares debuggable as true. While this is a release build, - // debuggable in the manifest will override this and generate a debug build - IResource manifestResource = project.findMember(SdkConstants.FN_ANDROID_MANIFEST_XML); - if (manifestResource.getType() != IResource.FILE) { - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - String.format("%1$s missing.", SdkConstants.FN_ANDROID_MANIFEST_XML))); - } - - IFileWrapper manifestFile = new IFileWrapper((IFile) manifestResource); - boolean debugMode = AndroidManifest.getDebuggable(manifestFile); - - AndroidPrintStream fakeStream = new AndroidPrintStream(null, null, new OutputStream() { - @Override - public void write(int b) throws IOException { - // do nothing - } - }); - - ProjectState projectState = Sdk.getProjectState(project); - - // get the jumbo mode option - String forceJumboStr = projectState.getProperty(AdtConstants.DEX_OPTIONS_FORCEJUMBO); - Boolean jumbo = Boolean.valueOf(forceJumboStr); - - String dexMergerStr = projectState.getProperty(AdtConstants.DEX_OPTIONS_DISABLE_MERGER); - Boolean dexMerger = Boolean.valueOf(dexMergerStr); - - BuildToolInfo buildToolInfo = getBuildTools(projectState); - - BuildHelper helper = new BuildHelper( - projectState, - buildToolInfo, - fakeStream, fakeStream, - jumbo.booleanValue(), - dexMerger.booleanValue(), - debugMode, false /*verbose*/, - null /*resourceMarker*/); - - // get the list of library projects - List<IProject> libProjects = projectState.getFullLibraryProjects(); - - // Step 1. Package the resources. - - // tmp file for the packaged resource file. To not disturb the incremental builders - // output, all intermediary files are created in tmp files. - File resourceFile = File.createTempFile(TEMP_PREFIX, SdkConstants.DOT_RES); - resourceFile.deleteOnExit(); - - // Make sure the PNG crunch cache is up to date - helper.updateCrunchCache(); - - // get the merged manifest - IFolder androidOutputFolder = BaseProjectHelper.getAndroidOutputFolder(project); - IFile mergedManifestFile = androidOutputFolder.getFile( - SdkConstants.FN_ANDROID_MANIFEST_XML); - - - // package the resources. - helper.packageResources( - mergedManifestFile, - libProjects, - null, // res filter - 0, // versionCode - resourceFile.getParent(), - resourceFile.getName()); - - // Step 2. Convert the byte code to Dalvik bytecode - - // tmp file for the packaged resource file. - File dexFile = File.createTempFile(TEMP_PREFIX, SdkConstants.DOT_DEX); - dexFile.deleteOnExit(); - - ProjectState state = Sdk.getProjectState(project); - String proguardConfig = state.getProperties().getProperty( - ProjectProperties.PROPERTY_PROGUARD_CONFIG); - - boolean runProguard = false; - List<File> proguardConfigFiles = null; - if (proguardConfig != null && proguardConfig.length() > 0) { - // 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); - } - - Iterable<String> paths = LintUtils.splitPath(proguardConfig); - 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)); - } - } - - // get the proguard file output by aapt - if (proguardConfigFiles != null) { - IFile proguardFile = androidOutputFolder.getFile(AdtConstants.FN_AAPT_PROGUARD); - proguardConfigFiles.add(proguardFile.getLocation().toFile()); - } - } - - Collection<String> dxInput; - - if (runProguard) { - // get all the compiled code paths. This will contain both project output - // folder and jar files. - Collection<String> paths = helper.getCompiledCodePaths(); - - // create a jar file containing all the project output (as proguard cannot - // process folders of .class files). - File inputJar = File.createTempFile(TEMP_PREFIX, SdkConstants.DOT_JAR); - inputJar.deleteOnExit(); - JarOutputStream jos = new JarOutputStream(new FileOutputStream(inputJar)); - - // a list of the other paths (jar files.) - List<String> jars = new ArrayList<String>(); - - for (String path : paths) { - File root = new File(path); - if (root.isDirectory()) { - addFileToJar(jos, root, root); - } else if (root.isFile()) { - jars.add(path); - } - } - jos.close(); - - // destination file for proguard - File obfuscatedJar = File.createTempFile(TEMP_PREFIX, SdkConstants.DOT_JAR); - obfuscatedJar.deleteOnExit(); - - // run proguard - helper.runProguard(proguardConfigFiles, inputJar, jars, obfuscatedJar, - new File(project.getLocation().toFile(), SdkConstants.FD_PROGUARD)); - - helper.setProguardOutput(obfuscatedJar.getAbsolutePath()); - - // dx input is proguard's output - dxInput = Collections.singletonList(obfuscatedJar.getAbsolutePath()); - } else { - // no proguard, simply get all the compiled code path: project output(s) + - // jar file(s) - dxInput = helper.getCompiledCodePaths(); - } - - IJavaProject javaProject = JavaCore.create(project); - - helper.executeDx(javaProject, dxInput, dexFile.getAbsolutePath()); - - // Step 3. Final package - - helper.finalPackage( - resourceFile.getAbsolutePath(), - dexFile.getAbsolutePath(), - outputFile.getAbsolutePath(), - libProjects, - key, - certificate, - null); //resourceMarker - - // success! - } catch (CoreException e) { - throw e; - } catch (ProguardResultException e) { - String msg = String.format("Proguard returned with error code %d. See console", - e.getErrorCode()); - AdtPlugin.printErrorToConsole(project, msg); - AdtPlugin.printErrorToConsole(project, (Object[]) e.getOutput()); - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - msg, e)); - } catch (ProguardExecException e) { - String msg = String.format("Failed to run proguard: %s", e.getMessage()); - AdtPlugin.printErrorToConsole(project, msg); - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - msg, e)); - } catch (DuplicateFileException e) { - String msg = String.format( - "Found duplicate file for APK: %1$s\nOrigin 1: %2$s\nOrigin 2: %3$s", - e.getArchivePath(), e.getFile1(), e.getFile2()); - AdtPlugin.printErrorToConsole(project, msg); - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - e.getMessage(), e)); - } catch (NativeLibInJarException e) { - String msg = e.getMessage(); - - AdtPlugin.printErrorToConsole(project, msg); - AdtPlugin.printErrorToConsole(project, (Object[]) e.getAdditionalInfo()); - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - e.getMessage(), e)); - } catch (DexException e) { - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - e.getMessage(), e)); - } catch (ApkCreationException e) { - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - e.getMessage(), e)); - } catch (Exception e) { - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - "Failed to export application", e)); - } finally { - // move back to a debug build. - // By using a normal build, we'll simply rebuild the debug version, and let the - // builder decide whether to build the full package or not. - ProjectHelper.buildWithDeps(project, IncrementalProjectBuilder.FULL_BUILD, monitor); - project.refreshLocal(IResource.DEPTH_INFINITE, monitor); - } - } - - public static BuildToolInfo getBuildTools(ProjectState projectState) - throws CoreException { - BuildToolInfo buildToolInfo = projectState.getBuildToolInfo(); - if (buildToolInfo == null) { - buildToolInfo = Sdk.getCurrent().getLatestBuildTool(); - } - - if (buildToolInfo == null) { - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - "No Build Tools installed in the SDK.")); - } - return buildToolInfo; - } - - /** - * Exports an unsigned release APK after prompting the user for a location. - * - * <strong>Must be called from the UI thread.</strong> - * - * @param project the project to export - */ - public static void exportUnsignedReleaseApk(final IProject project) { - Shell shell = Display.getCurrent().getActiveShell(); - - // create a default file name for the apk. - String fileName = project.getName() + SdkConstants.DOT_ANDROID_PACKAGE; - - // Pop up the file save window to get the file location - FileDialog fileDialog = new FileDialog(shell, SWT.SAVE); - - fileDialog.setText("Export Project"); - fileDialog.setFileName(fileName); - - final String saveLocation = fileDialog.open(); - if (saveLocation != null) { - new Job("Android Release Export") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - exportReleaseApk(project, - new File(saveLocation), - null, //key - null, //certificate - monitor); - - // this is unsigned export. Let's tell the developers to run zip align - AdtPlugin.displayWarning("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)); - - return Status.OK_STATUS; - } catch (CoreException e) { - AdtPlugin.displayError("Android IDE Plug-in", String.format( - "Error exporting application:\n\n%1$s", e.getMessage())); - return e.getStatus(); - } - } - }.schedule(); - } - } - - /** - * Adds a file to a jar file. - * The <var>rootDirectory</var> dictates the path of the file inside the jar file. It must be - * a parent of <var>file</var>. - * @param jar the jar to add the file to - * @param file the file to add - * @param rootDirectory the rootDirectory. - * @throws IOException - */ - private static void addFileToJar(JarOutputStream jar, File file, File rootDirectory) - throws IOException { - if (file.isDirectory()) { - if (file.getName().equals("META-INF") == false) { - for (File child: file.listFiles()) { - addFileToJar(jar, child, rootDirectory); - } - } - } else if (file.isFile()) { - String rootPath = rootDirectory.getAbsolutePath(); - String path = file.getAbsolutePath(); - path = path.substring(rootPath.length()).replace("\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$ - if (path.charAt(0) == '/') { - path = path.substring(1); - } - - JarEntry entry = new JarEntry(path); - entry.setTime(file.lastModified()); - jar.putNextEntry(entry); - - // put the content of the file. - byte[] buffer = new byte[1024]; - int count; - BufferedInputStream bis = null; - try { - bis = new BufferedInputStream(new FileInputStream(file)); - while ((count = bis.read(buffer)) != -1) { - jar.write(buffer, 0, count); - } - } finally { - if (bis != null) { - try { - bis.close(); - } catch (IOException ignore) { - } - } - } - jar.closeEntry(); - } - } -} |