diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java | 1225 |
1 files changed, 0 insertions, 1225 deletions
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 deleted file mode 100644 index 78d9d94e4..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java +++ /dev/null @@ -1,1225 +0,0 @@ -/* - * Copyright (C) 2010 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.build; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -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.preferences.AdtPrefs; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; -import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; -import com.android.ide.eclipse.adt.internal.sdk.ProjectState; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.prefs.AndroidLocation.AndroidLocationException; -import com.android.sdklib.BuildToolInfo; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.IAndroidTarget.IOptionalLibrary; -import com.android.sdklib.build.ApkBuilder; -import com.android.sdklib.build.ApkBuilder.JarStatus; -import com.android.sdklib.build.ApkBuilder.SigningInfo; -import com.android.sdklib.build.ApkCreationException; -import com.android.sdklib.build.DuplicateFileException; -import com.android.sdklib.build.RenderScriptProcessor; -import com.android.sdklib.build.SealedApkException; -import com.android.sdklib.internal.build.DebugKeyProvider; -import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; -import com.android.utils.GrabProcessOutput; -import com.android.utils.GrabProcessOutput.IProcessOutput; -import com.android.utils.GrabProcessOutput.Wait; -import com.google.common.base.Charsets; -import com.google.common.hash.HashCode; -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hashing; - -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.IWorkspaceRoot; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.core.IClasspathContainer; -import org.eclipse.jdt.core.IClasspathEntry; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jface.preference.IPreferenceStore; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintStream; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -/** - * Helper with methods for the last 3 steps of the generation of an APK. - * - * {@link #packageResources(IFile, IProject[], String, int, String, String)} packages the - * application resources using aapt into a zip file that is ready to be integrated into the apk. - * - * {@link #executeDx(IJavaProject, String, String, IJavaProject[])} will convert the Java byte - * code into the Dalvik bytecode. - * - * {@link #finalPackage(String, String, String, boolean, IJavaProject, IProject[], IJavaProject[], String, boolean)} - * will make the apk from all the previous components. - * - * This class only executes the 3 above actions. It does not handle the errors, and simply sends - * them back as custom exceptions. - * - * Warnings are handled by the {@link ResourceMarker} interface. - * - * Console output (verbose and non verbose) is handled through the {@link AndroidPrintStream} passed - * to the constructor. - * - */ -public class BuildHelper { - - private static final String CONSOLE_PREFIX_DX = "Dx"; //$NON-NLS-1$ - private final static String TEMP_PREFIX = "android_"; //$NON-NLS-1$ - - private static final String COMMAND_CRUNCH = "crunch"; //$NON-NLS-1$ - private static final String COMMAND_PACKAGE = "package"; //$NON-NLS-1$ - - @NonNull - private final ProjectState mProjectState; - @NonNull - private final IProject mProject; - @NonNull - private final BuildToolInfo mBuildToolInfo; - @NonNull - private final AndroidPrintStream mOutStream; - @NonNull - private final AndroidPrintStream mErrStream; - private final boolean mForceJumbo; - private final boolean mDisableDexMerger; - private final boolean mVerbose; - private final boolean mDebugMode; - - private final Set<String> mCompiledCodePaths = new HashSet<String>(); - - public static final boolean BENCHMARK_FLAG = false; - public static long sStartOverallTime = 0; - public static long sStartJavaCTime = 0; - - private final static int MILLION = 1000000; - private String mProguardFile; - - /** - * An object able to put a marker on a resource. - */ - public interface ResourceMarker { - void setWarning(IResource resource, String message); - } - - /** - * Creates a new post-compiler helper - * @param project - * @param outStream - * @param errStream - * @param debugMode whether this is a debug build - * @param verbose - * @throws CoreException - */ - public BuildHelper(@NonNull ProjectState projectState, - @NonNull BuildToolInfo buildToolInfo, - @NonNull AndroidPrintStream outStream, - @NonNull AndroidPrintStream errStream, - boolean forceJumbo, boolean disableDexMerger, boolean debugMode, - boolean verbose, ResourceMarker resMarker) throws CoreException { - mProjectState = projectState; - mProject = projectState.getProject(); - mBuildToolInfo = buildToolInfo; - mOutStream = outStream; - mErrStream = errStream; - mDebugMode = debugMode; - mVerbose = verbose; - mForceJumbo = forceJumbo; - mDisableDexMerger = disableDexMerger; - - gatherPaths(resMarker); - } - - public void updateCrunchCache() throws AaptExecException, AaptResultException { - // Benchmarking start - long startCrunchTime = 0; - if (BENCHMARK_FLAG) { - String msg = "BENCHMARK ADT: Starting Initial Packaging (.ap_)"; //$NON-NLS-1$ - AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg); - startCrunchTime = System.nanoTime(); - } - - // Get the resources folder to crunch from - IFolder resFolder = mProject.getFolder(AdtConstants.WS_RESOURCES); - List<String> resPaths = new ArrayList<String>(); - resPaths.add(resFolder.getLocation().toOSString()); - - // Get the output folder where the cache is stored. - IFolder binFolder = BaseProjectHelper.getAndroidOutputFolder(mProject); - IFolder cacheFolder = binFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_CRUNCHCACHE); - String cachePath = cacheFolder.getLocation().toOSString(); - - /* For crunching, we don't need the osManifestPath, osAssetsPath, or the configFilter - * parameters for executeAapt - */ - executeAapt(COMMAND_CRUNCH, "", resPaths, "", cachePath, "", 0); - - // Benchmarking end - if (BENCHMARK_FLAG) { - String msg = "BENCHMARK ADT: Ending Initial Package (.ap_). \nTime Elapsed: " //$NON-NLS-1$ - + ((System.nanoTime() - startCrunchTime)/MILLION) + "ms"; //$NON-NLS-1$ - AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg); - } - } - - /** - * Packages the resources of the projet into a .ap_ file. - * @param manifestFile the manifest of the project. - * @param libProjects the list of library projects that this project depends on. - * @param resFilter an optional resource filter to be used with the -c option of aapt. If null - * no filters are used. - * @param versionCode an optional versionCode to be inserted in the manifest during packaging. - * If the value is <=0, no values are inserted. - * @param outputFolder where to write the resource ap_ file. - * @param outputFilename the name of the resource ap_ file. - * @throws AaptExecException - * @throws AaptResultException - */ - public void packageResources(IFile manifestFile, List<IProject> libProjects, String resFilter, - int versionCode, String outputFolder, String outputFilename) - throws AaptExecException, AaptResultException { - - // Benchmarking start - long startPackageTime = 0; - if (BENCHMARK_FLAG) { - String msg = "BENCHMARK ADT: Starting Initial Packaging (.ap_)"; //$NON-NLS-1$ - AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg); - startPackageTime = System.nanoTime(); - } - - // need to figure out some path before we can execute aapt; - IFolder binFolder = BaseProjectHelper.getAndroidOutputFolder(mProject); - - // get the cache folder - IFolder cacheFolder = binFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_CRUNCHCACHE); - - // get the BC folder - IFolder bcFolder = binFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_BC); - - // get the resource folder - IFolder resFolder = mProject.getFolder(AdtConstants.WS_RESOURCES); - - // and the assets folder - IFolder assetsFolder = mProject.getFolder(AdtConstants.WS_ASSETS); - - // we need to make sure this one exists. - if (assetsFolder.exists() == false) { - assetsFolder = null; - } - - // list of res folder (main project + maybe libraries) - ArrayList<String> osResPaths = new ArrayList<String>(); - - IPath resLocation = resFolder.getLocation(); - IPath manifestLocation = manifestFile.getLocation(); - - if (resLocation != null && manifestLocation != null) { - - // png cache folder first. - addFolderToList(osResPaths, cacheFolder); - addFolderToList(osResPaths, bcFolder); - - // regular res folder next. - osResPaths.add(resLocation.toOSString()); - - // then libraries - if (libProjects != null) { - for (IProject lib : libProjects) { - // png cache folder first - IFolder libBinFolder = BaseProjectHelper.getAndroidOutputFolder(lib); - - IFolder libCacheFolder = libBinFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_CRUNCHCACHE); - addFolderToList(osResPaths, libCacheFolder); - - IFolder libBcFolder = libBinFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_BC); - addFolderToList(osResPaths, libBcFolder); - - // regular res folder next. - IFolder libResFolder = lib.getFolder(AdtConstants.WS_RESOURCES); - addFolderToList(osResPaths, libResFolder); - } - } - - String osManifestPath = manifestLocation.toOSString(); - - String osAssetsPath = null; - if (assetsFolder != null) { - osAssetsPath = assetsFolder.getLocation().toOSString(); - } - - // build the default resource package - executeAapt(COMMAND_PACKAGE, osManifestPath, osResPaths, osAssetsPath, - outputFolder + File.separator + outputFilename, resFilter, - versionCode); - } - - // Benchmarking end - if (BENCHMARK_FLAG) { - String msg = "BENCHMARK ADT: Ending Initial Package (.ap_). \nTime Elapsed: " //$NON-NLS-1$ - + ((System.nanoTime() - startPackageTime)/MILLION) + "ms"; //$NON-NLS-1$ - AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg); - } - } - - /** - * Adds os path of a folder to a list only if the folder actually exists. - * @param pathList - * @param folder - */ - private void addFolderToList(List<String> pathList, IFolder folder) { - // use a File instead of the IFolder API to ignore workspace refresh issue. - File testFile = new File(folder.getLocation().toOSString()); - if (testFile.isDirectory()) { - pathList.add(testFile.getAbsolutePath()); - } - } - - /** - * Makes a final package signed with the debug key. - * - * Packages the dex files, the temporary resource file into the final package file. - * - * Whether the package is a debug package is controlled with the <var>debugMode</var> parameter - * in {@link #PostCompilerHelper(IProject, PrintStream, PrintStream, boolean, boolean)} - * - * @param intermediateApk The path to the temporary resource file. - * @param dex The path to the dex file. - * @param output The path to the final package file to create. - * @param libProjects an optional list of library projects (can be null) - * @return true if success, false otherwise. - * @throws ApkCreationException - * @throws AndroidLocationException - * @throws KeytoolException - * @throws NativeLibInJarException - * @throws CoreException - * @throws DuplicateFileException - */ - public void finalDebugPackage(String intermediateApk, String dex, String output, - List<IProject> libProjects, ResourceMarker resMarker) - throws ApkCreationException, KeytoolException, AndroidLocationException, - NativeLibInJarException, DuplicateFileException, CoreException { - - AdtPlugin adt = AdtPlugin.getDefault(); - if (adt == null) { - return; - } - - // get the debug keystore to use. - IPreferenceStore store = adt.getPreferenceStore(); - String keystoreOsPath = store.getString(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE); - if (keystoreOsPath == null || new File(keystoreOsPath).isFile() == false) { - keystoreOsPath = DebugKeyProvider.getDefaultKeyStoreOsPath(); - AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, mProject, - Messages.ApkBuilder_Using_Default_Key); - } else { - AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, mProject, - String.format(Messages.ApkBuilder_Using_s_To_Sign, keystoreOsPath)); - } - - // from the keystore, get the signing info - SigningInfo info = ApkBuilder.getDebugKey(keystoreOsPath, mVerbose ? mOutStream : null); - - finalPackage(intermediateApk, dex, output, libProjects, - info != null ? info.key : null, info != null ? info.certificate : null, resMarker); - } - - /** - * Makes the final package. - * - * Packages the dex files, the temporary resource file into the final package file. - * - * Whether the package is a debug package is controlled with the <var>debugMode</var> parameter - * in {@link #PostCompilerHelper(IProject, PrintStream, PrintStream, boolean, boolean)} - * - * @param intermediateApk The path to the temporary resource file. - * @param dex The path to the dex file. - * @param output The path to the final package file to create. - * @param debugSign whether the apk must be signed with the debug key. - * @param libProjects an optional list of library projects (can be null) - * @param abiFilter an optional filter. If not null, then only the matching ABI is included in - * the final archive - * @return true if success, false otherwise. - * @throws NativeLibInJarException - * @throws ApkCreationException - * @throws CoreException - * @throws DuplicateFileException - */ - public void finalPackage(String intermediateApk, String dex, String output, - List<IProject> libProjects, - PrivateKey key, X509Certificate certificate, ResourceMarker resMarker) - throws NativeLibInJarException, ApkCreationException, DuplicateFileException, - CoreException { - - try { - ApkBuilder apkBuilder = new ApkBuilder(output, intermediateApk, dex, - key, certificate, - mVerbose ? mOutStream: null); - apkBuilder.setDebugMode(mDebugMode); - - // either use the full compiled code paths or just the proguard file - // if present - Collection<String> pathsCollection = mCompiledCodePaths; - if (mProguardFile != null) { - pathsCollection = Collections.singletonList(mProguardFile); - mProguardFile = null; - } - - // Now we write the standard resources from all the output paths. - for (String path : pathsCollection) { - File file = new File(path); - if (file.isFile()) { - JarStatus jarStatus = apkBuilder.addResourcesFromJar(file); - - // check if we found native libraries in the external library. This - // constitutes an error or warning depending on if they are in lib/ - if (jarStatus.getNativeLibs().size() > 0) { - String libName = file.getName(); - - String msg = String.format( - "Native libraries detected in '%1$s'. See console for more information.", - libName); - - ArrayList<String> consoleMsgs = new ArrayList<String>(); - - consoleMsgs.add(String.format( - "The library '%1$s' contains native libraries that will not run on the device.", - libName)); - - if (jarStatus.hasNativeLibsConflicts()) { - consoleMsgs.add("Additionally some of those libraries will interfer with the installation of the application because of their location in lib/"); - consoleMsgs.add("lib/ is reserved for NDK libraries."); - } - - consoleMsgs.add("The following libraries were found:"); - - for (String lib : jarStatus.getNativeLibs()) { - consoleMsgs.add(" - " + lib); - } - - String[] consoleStrings = consoleMsgs.toArray(new String[consoleMsgs.size()]); - - // if there's a conflict or if the prefs force error on any native code in jar - // files, throw an exception - if (jarStatus.hasNativeLibsConflicts() || - AdtPrefs.getPrefs().getBuildForceErrorOnNativeLibInJar()) { - throw new NativeLibInJarException(jarStatus, msg, libName, consoleStrings); - } else { - // otherwise, put a warning, and output to the console also. - if (resMarker != null) { - resMarker.setWarning(mProject, msg); - } - - for (String string : consoleStrings) { - mOutStream.println(string); - } - } - } - } else if (file.isDirectory()) { - // this is technically not a source folder (class folder instead) but since we - // only care about Java resources (ie non class/java files) this will do the - // same - apkBuilder.addSourceFolder(file); - } - } - - // now write the native libraries. - // First look if the lib folder is there. - IResource libFolder = mProject.findMember(SdkConstants.FD_NATIVE_LIBS); - if (libFolder != null && libFolder.exists() && - libFolder.getType() == IResource.FOLDER) { - // get a File for the folder. - apkBuilder.addNativeLibraries(libFolder.getLocation().toFile()); - } - - // next the native libraries for the renderscript support mode. - if (mProjectState.getRenderScriptSupportMode()) { - IFolder androidOutputFolder = BaseProjectHelper.getAndroidOutputFolder(mProject); - IResource rsLibFolder = androidOutputFolder.getFolder( - AdtConstants.WS_BIN_RELATIVE_RS_LIBS); - File rsLibFolderFile = rsLibFolder.getLocation().toFile(); - if (rsLibFolderFile.isDirectory()) { - apkBuilder.addNativeLibraries(rsLibFolderFile); - } - - File rsLibs = RenderScriptProcessor.getSupportNativeLibFolder( - mBuildToolInfo.getLocation().getAbsolutePath()); - if (rsLibs.isDirectory()) { - apkBuilder.addNativeLibraries(rsLibs); - } - } - - // write the native libraries for the library projects. - if (libProjects != null) { - for (IProject lib : libProjects) { - libFolder = lib.findMember(SdkConstants.FD_NATIVE_LIBS); - if (libFolder != null && libFolder.exists() && - libFolder.getType() == IResource.FOLDER) { - apkBuilder.addNativeLibraries(libFolder.getLocation().toFile()); - } - } - } - - // seal the APK. - apkBuilder.sealApk(); - } catch (SealedApkException e) { - // this won't happen as we control when the apk is sealed. - } - } - - public void setProguardOutput(String proguardFile) { - mProguardFile = proguardFile; - } - - public Collection<String> getCompiledCodePaths() { - return mCompiledCodePaths; - } - - public void runProguard(List<File> proguardConfigs, File inputJar, Collection<String> jarFiles, - File obfuscatedJar, File logOutput) - throws ProguardResultException, ProguardExecException, IOException { - IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); - - // prepare the command line for proguard - List<String> command = new ArrayList<String>(); - command.add(AdtPlugin.getOsAbsoluteProguard()); - - 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())); - for (String jarFile : jarFiles) { - sb.append(File.pathSeparatorChar); - sb.append(quotePath(jarFile)); - } - command.add(quoteWinArg(sb.toString())); - - command.add("-outjars"); //$NON-NLS-1$ - command.add(quotePath(obfuscatedJar.getAbsolutePath())); - - command.add("-libraryjars"); //$NON-NLS-1$ - sb = new StringBuilder(quotePath(target.getPath(IAndroidTarget.ANDROID_JAR))); - IOptionalLibrary[] libraries = target.getOptionalLibraries(); - if (libraries != null) { - for (IOptionalLibrary lib : libraries) { - sb.append(File.pathSeparatorChar); - sb.append(quotePath(lib.getJarPath())); - } - } - command.add(quoteWinArg(sb.toString())); - - if (logOutput != null) { - if (logOutput.isDirectory() == false) { - logOutput.mkdirs(); - } - - command.add("-dump"); //$NON-NLS-1$ - command.add(new File(logOutput, "dump.txt").getAbsolutePath()); //$NON-NLS-1$ - - command.add("-printseeds"); //$NON-NLS-1$ - command.add(new File(logOutput, "seeds.txt").getAbsolutePath()); //$NON-NLS-1$ - - command.add("-printusage"); //$NON-NLS-1$ - command.add(new File(logOutput, "usage.txt").getAbsolutePath()); //$NON-NLS-1$ - - command.add("-printmapping"); //$NON-NLS-1$ - command.add(new File(logOutput, "mapping.txt").getAbsolutePath()); //$NON-NLS-1$ - } - - String commandArray[] = null; - - if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) { - commandArray = createWindowsProguardConfig(command); - } - - if (commandArray == null) { - // For Mac & Linux, use a regular command string array. - commandArray = command.toArray(new String[command.size()]); - } - - // Define PROGUARD_HOME to point to $SDK/tools/proguard if it's not yet defined. - // The Mac/Linux proguard.sh can infer it correctly but not the proguard.bat one. - String[] envp = null; - Map<String, String> envMap = new TreeMap<String, String>(System.getenv()); - if (!envMap.containsKey("PROGUARD_HOME")) { //$NON-NLS-1$ - envMap.put("PROGUARD_HOME", Sdk.getCurrent().getSdkOsLocation() + //$NON-NLS-1$ - SdkConstants.FD_TOOLS + File.separator + - SdkConstants.FD_PROGUARD); - envp = new String[envMap.size()]; - int i = 0; - for (Map.Entry<String, String> entry : envMap.entrySet()) { - envp[i++] = String.format("%1$s=%2$s", //$NON-NLS-1$ - entry.getKey(), - entry.getValue()); - } - } - - if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { - sb = new StringBuilder(); - for (String c : commandArray) { - sb.append(c).append(' '); - } - AdtPlugin.printToConsole(mProject, sb.toString()); - } - - // launch - int execError = 1; - try { - // launch the command line process - Process process = Runtime.getRuntime().exec(commandArray, envp); - - // list to store each line of stderr - ArrayList<String> results = new ArrayList<String>(); - - // get the output and return code from the process - execError = grabProcessOutput(mProject, process, results); - - if (mVerbose) { - for (String resultString : results) { - mOutStream.println(resultString); - } - } - - if (execError != 0) { - throw new ProguardResultException(execError, - results.toArray(new String[results.size()])); - } - - } catch (IOException e) { - String msg = String.format(Messages.Proguard_Exec_Error, commandArray[0]); - throw new ProguardExecException(msg, e); - } catch (InterruptedException e) { - String msg = String.format(Messages.Proguard_Exec_Error, commandArray[0]); - throw new ProguardExecException(msg, e); - } - } - - /** - * For tools R8 up to R11, the proguard.bat launcher on Windows only accepts - * arguments %1..%9. Since we generally have about 15 arguments, we were working - * around this by generating a temporary config file for proguard and then using - * that. - * Starting with tools R12, the proguard.bat launcher has been fixed to take - * all arguments using %* so we no longer need this hack. - * - * @param command - * @return - * @throws IOException - */ - private String[] createWindowsProguardConfig(List<String> command) throws IOException { - - // Arg 0 is the proguard.bat path and arg 1 is the user config file - String launcher = AdtPlugin.readFile(new File(command.get(0))); - if (launcher.contains("%*")) { //$NON-NLS-1$ - // This is the launcher from Tools R12. Don't work around it. - return null; - } - - // On Windows, proguard.bat can only pass %1...%9 to the java -jar proguard.jar - // call, but we have at least 15 arguments here so some get dropped silently - // and quoting is a big issue. So instead we'll work around that by writing - // all the arguments to a temporary config file. - - String[] commandArray = new String[3]; - - commandArray[0] = command.get(0); - commandArray[1] = command.get(1); - - // Write all the other arguments to a config file - File argsFile = File.createTempFile(TEMP_PREFIX, ".pro"); //$NON-NLS-1$ - // TODO FIXME this may leave a lot of temp files around on a long session. - // Should have a better way to clean up e.g. before each build. - argsFile.deleteOnExit(); - - FileWriter fw = new FileWriter(argsFile); - - for (int i = 2; i < command.size(); i++) { - String s = command.get(i); - fw.write(s); - fw.write(s.startsWith("-") ? ' ' : '\n'); //$NON-NLS-1$ - } - - fw.close(); - - commandArray[2] = "@" + argsFile.getAbsolutePath(); //$NON-NLS-1$ - return commandArray; - } - - /** - * Quotes a single path for proguard to deal with spaces. - * - * @param path The path to quote. - * @return The original path if it doesn't contain a space. - * Or the original path surrounded by single quotes if it contains spaces. - */ - private String quotePath(String path) { - if (path.indexOf(' ') != -1) { - path = '\'' + path + '\''; - } - return path; - } - - /** - * Quotes a compound proguard argument to deal with spaces. - * <p/> - * Proguard takes multi-path arguments such as "path1;path2" for some options. - * When the {@link #quotePath} methods adds quotes for such a path if it contains spaces, - * the proguard shell wrapper will absorb the quotes, so we need to quote around the - * quotes. - * - * @param path The path to quote. - * @return The original path if it doesn't contain a single quote. - * Or on Windows the original path surrounded by double quotes if it contains a quote. - */ - private String quoteWinArg(String path) { - if (path.indexOf('\'') != -1 && - SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) { - path = '"' + path + '"'; - } - return path; - } - - - /** - * Execute the Dx tool for dalvik code conversion. - * @param javaProject The java project - * @param inputPaths the input paths for DX - * @param osOutFilePath the path of the dex file to create. - * - * @throws CoreException - * @throws DexException - */ - public void executeDx(IJavaProject javaProject, Collection<String> inputPaths, - String osOutFilePath) - throws CoreException, DexException { - - // get the dex wrapper - Sdk sdk = Sdk.getCurrent(); - DexWrapper wrapper = sdk.getDexWrapper(mBuildToolInfo); - - if (wrapper == null) { - throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - Messages.ApkBuilder_UnableBuild_Dex_Not_loaded)); - } - - try { - // set a temporary prefix on the print streams. - mOutStream.setPrefix(CONSOLE_PREFIX_DX); - mErrStream.setPrefix(CONSOLE_PREFIX_DX); - - IFolder binFolder = BaseProjectHelper.getAndroidOutputFolder(javaProject.getProject()); - File binFile = binFolder.getLocation().toFile(); - File dexedLibs = new File(binFile, "dexedLibs"); - if (dexedLibs.exists() == false) { - dexedLibs.mkdir(); - } - - // replace the libs by their dexed versions (dexing them if needed.) - List<String> finalInputPaths = new ArrayList<String>(inputPaths.size()); - if (mDisableDexMerger || inputPaths.size() == 1) { - // only one input, no need to put a pre-dexed version, even if this path is - // just a jar file (case for proguard'ed builds) - finalInputPaths.addAll(inputPaths); - } else { - - for (String input : inputPaths) { - File inputFile = new File(input); - if (inputFile.isDirectory()) { - finalInputPaths.add(input); - } else if (inputFile.isFile()) { - String fileName = getDexFileName(inputFile); - - File dexedLib = new File(dexedLibs, fileName); - String dexedLibPath = dexedLib.getAbsolutePath(); - - if (dexedLib.isFile() == false || - dexedLib.lastModified() < inputFile.lastModified()) { - - if (mVerbose) { - mOutStream.println( - String.format("Pre-Dexing %1$s -> %2$s", input, fileName)); - } - - if (dexedLib.isFile()) { - dexedLib.delete(); - } - - int res = wrapper.run(dexedLibPath, Collections.singleton(input), - mForceJumbo, mVerbose, mOutStream, mErrStream); - - if (res != 0) { - // output error message and mark the project. - String message = String.format(Messages.Dalvik_Error_d, res); - throw new DexException(message); - } - } else { - if (mVerbose) { - mOutStream.println( - String.format("Using Pre-Dexed %1$s <- %2$s", - fileName, input)); - } - } - - finalInputPaths.add(dexedLibPath); - } - } - } - - if (mVerbose) { - for (String input : finalInputPaths) { - mOutStream.println("Input: " + input); - } - } - - int res = wrapper.run(osOutFilePath, - finalInputPaths, - mForceJumbo, - mVerbose, - mOutStream, mErrStream); - - mOutStream.setPrefix(null); - mErrStream.setPrefix(null); - - if (res != 0) { - // output error message and marker the project. - String message = String.format(Messages.Dalvik_Error_d, res); - throw new DexException(message); - } - } catch (DexException e) { - throw e; - } catch (Throwable t) { - String message = t.getMessage(); - if (message == null) { - message = t.getClass().getCanonicalName(); - } - message = String.format(Messages.Dalvik_Error_s, message); - - throw new DexException(message, t); - } - } - - private String getDexFileName(File inputFile) { - // get the filename - String name = inputFile.getName(); - // remove the extension - int pos = name.lastIndexOf('.'); - if (pos != -1) { - name = name.substring(0, pos); - } - - // add a hash of the original file path - HashFunction hashFunction = Hashing.md5(); - HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath(), Charsets.UTF_8); - - return name + "-" + hashCode.toString() + ".jar"; - } - - /** - * Executes aapt. If any error happen, files or the project will be marked. - * @param command The command for aapt to execute. Currently supported: package and crunch - * @param osManifestPath The path to the manifest file - * @param osResPath The path to the res folder - * @param osAssetsPath The path to the assets folder. This can be null. - * @param osOutFilePath The path to the temporary resource file to create, - * or in the case of crunching the path to the cache to create/update. - * @param configFilter The configuration filter for the resources to include - * (used with -c option, for example "port,en,fr" to include portrait, English and French - * resources.) - * @param versionCode optional version code to insert in the manifest during packaging. If <=0 - * then no value is inserted - * @throws AaptExecException - * @throws AaptResultException - */ - private void executeAapt(String aaptCommand, String osManifestPath, - List<String> osResPaths, String osAssetsPath, String osOutFilePath, - String configFilter, int versionCode) throws AaptExecException, AaptResultException { - IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); - - String aapt = mBuildToolInfo.getPath(BuildToolInfo.PathId.AAPT); - - // Create the command line. - ArrayList<String> commandArray = new ArrayList<String>(); - commandArray.add(aapt); - commandArray.add(aaptCommand); - if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { - commandArray.add("-v"); //$NON-NLS-1$ - } - - // Common to all commands - for (String path : osResPaths) { - commandArray.add("-S"); //$NON-NLS-1$ - commandArray.add(path); - } - - if (aaptCommand.equals(COMMAND_PACKAGE)) { - commandArray.add("-f"); //$NON-NLS-1$ - commandArray.add("--no-crunch"); //$NON-NLS-1$ - - // if more than one res, this means there's a library (or more) and we need - // to activate the auto-add-overlay - if (osResPaths.size() > 1) { - commandArray.add("--auto-add-overlay"); //$NON-NLS-1$ - } - - if (mDebugMode) { - commandArray.add("--debug-mode"); //$NON-NLS-1$ - } - - if (versionCode > 0) { - commandArray.add("--version-code"); //$NON-NLS-1$ - commandArray.add(Integer.toString(versionCode)); - } - - if (configFilter != null) { - commandArray.add("-c"); //$NON-NLS-1$ - commandArray.add(configFilter); - } - - // never compress apks. - commandArray.add("-0"); - commandArray.add("apk"); - - commandArray.add("-M"); //$NON-NLS-1$ - commandArray.add(osManifestPath); - - if (osAssetsPath != null) { - commandArray.add("-A"); //$NON-NLS-1$ - commandArray.add(osAssetsPath); - } - - commandArray.add("-I"); //$NON-NLS-1$ - commandArray.add(target.getPath(IAndroidTarget.ANDROID_JAR)); - - commandArray.add("-F"); //$NON-NLS-1$ - commandArray.add(osOutFilePath); - } else if (aaptCommand.equals(COMMAND_CRUNCH)) { - commandArray.add("-C"); //$NON-NLS-1$ - commandArray.add(osOutFilePath); - } - - String command[] = commandArray.toArray( - new String[commandArray.size()]); - - if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) { - StringBuilder sb = new StringBuilder(); - for (String c : command) { - sb.append(c); - sb.append(' '); - } - AdtPlugin.printToConsole(mProject, sb.toString()); - } - - // Benchmarking start - long startAaptTime = 0; - if (BENCHMARK_FLAG) { - String msg = "BENCHMARK ADT: Starting " + aaptCommand //$NON-NLS-1$ - + " call to Aapt"; //$NON-NLS-1$ - AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg); - startAaptTime = System.nanoTime(); - } - - // launch - try { - // launch the command line process - Process process = Runtime.getRuntime().exec(command); - - // list to store each line of stderr - ArrayList<String> stdErr = new ArrayList<String>(); - - // get the output and return code from the process - int returnCode = grabProcessOutput(mProject, process, stdErr); - - if (mVerbose) { - for (String stdErrString : stdErr) { - mOutStream.println(stdErrString); - } - } - if (returnCode != 0) { - throw new AaptResultException(returnCode, - stdErr.toArray(new String[stdErr.size()])); - } - } catch (IOException e) { - String msg = String.format(Messages.AAPT_Exec_Error_s, command[0]); - throw new AaptExecException(msg, e); - } catch (InterruptedException e) { - String msg = String.format(Messages.AAPT_Exec_Error_s, command[0]); - throw new AaptExecException(msg, e); - } - - // Benchmarking end - if (BENCHMARK_FLAG) { - String msg = "BENCHMARK ADT: Ending " + aaptCommand //$NON-NLS-1$ - + " call to Aapt.\nBENCHMARK ADT: Time Elapsed: " //$NON-NLS-1$ - + ((System.nanoTime() - startAaptTime)/MILLION) + "ms"; //$NON-NLS-1$ - AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg); - } - } - - /** - * Computes all the project output and dependencies that must go into building the apk. - * - * @param resMarker - * @throws CoreException - */ - private void gatherPaths(ResourceMarker resMarker) - throws CoreException { - IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); - - // get a java project for the project. - IJavaProject javaProject = JavaCore.create(mProject); - - - // get the output of the main project - IPath path = javaProject.getOutputLocation(); - IResource outputResource = wsRoot.findMember(path); - if (outputResource != null && outputResource.getType() == IResource.FOLDER) { - mCompiledCodePaths.add(outputResource.getLocation().toOSString()); - } - - // we could use IJavaProject.getResolvedClasspath directly, but we actually - // want to see the containers themselves. - IClasspathEntry[] classpaths = javaProject.readRawClasspath(); - if (classpaths != null) { - for (IClasspathEntry e : classpaths) { - // ignore non exported entries, unless they're in the DEPEDENCIES container, - // in which case we always want it (there may be some older projects that - // have it as non exported). - if (e.isExported() || - (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER && - e.getPath().toString().equals(AdtConstants.CONTAINER_DEPENDENCIES))) { - handleCPE(e, javaProject, wsRoot, resMarker); - } - } - } - } - - private void handleCPE(IClasspathEntry entry, IJavaProject javaProject, - IWorkspaceRoot wsRoot, ResourceMarker resMarker) { - - // if this is a classpath variable reference, we resolve it. - if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { - entry = JavaCore.getResolvedClasspathEntry(entry); - } - - if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { - IProject refProject = wsRoot.getProject(entry.getPath().lastSegment()); - try { - // ignore if it's an Android project, or if it's not a Java Project - if (refProject.hasNature(JavaCore.NATURE_ID) && - refProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) { - IJavaProject refJavaProject = JavaCore.create(refProject); - - // get the output folder - IPath path = refJavaProject.getOutputLocation(); - IResource outputResource = wsRoot.findMember(path); - if (outputResource != null && outputResource.getType() == IResource.FOLDER) { - mCompiledCodePaths.add(outputResource.getLocation().toOSString()); - } - } - } catch (CoreException exception) { - // can't query the project nature? ignore - } - - } else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { - handleClasspathLibrary(entry, wsRoot, resMarker); - } else if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { - // get the container - try { - IClasspathContainer container = JavaCore.getClasspathContainer( - entry.getPath(), javaProject); - // ignore the system and default_system types as they represent - // libraries that are part of the runtime. - if (container != null && container.getKind() == IClasspathContainer.K_APPLICATION) { - IClasspathEntry[] entries = container.getClasspathEntries(); - for (IClasspathEntry cpe : entries) { - handleCPE(cpe, javaProject, wsRoot, resMarker); - } - } - } catch (JavaModelException jme) { - // can't resolve the container? ignore it. - AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", entry.getPath()); - } - } - } - - private void handleClasspathLibrary(IClasspathEntry e, IWorkspaceRoot wsRoot, - ResourceMarker resMarker) { - // get the IPath - IPath path = e.getPath(); - - IResource resource = wsRoot.findMember(path); - - if (resource != null && resource.getType() == IResource.PROJECT) { - // if it's a project we should just ignore it because it's going to be added - // later when we add all the referenced projects. - - } else if (SdkConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { - // case of a jar file (which could be relative to the workspace or a full path) - if (resource != null && resource.exists() && - resource.getType() == IResource.FILE) { - mCompiledCodePaths.add(resource.getLocation().toOSString()); - } else { - // if the jar path doesn't match a workspace resource, - // then we get an OSString and check if this links to a valid file. - String osFullPath = path.toOSString(); - - File f = new File(osFullPath); - if (f.isFile()) { - mCompiledCodePaths.add(osFullPath); - } else { - String message = String.format( Messages.Couldnt_Locate_s_Error, - path); - // always output to the console - mOutStream.println(message); - - // put a marker - if (resMarker != null) { - resMarker.setWarning(mProject, message); - } - } - } - } else { - // this can be the case for a class folder. - if (resource != null && resource.exists() && - resource.getType() == IResource.FOLDER) { - mCompiledCodePaths.add(resource.getLocation().toOSString()); - } else { - // if the path doesn't match a workspace resource, - // then we get an OSString and check if this links to a valid folder. - String osFullPath = path.toOSString(); - - File f = new File(osFullPath); - if (f.isDirectory()) { - mCompiledCodePaths.add(osFullPath); - } - } - } - } - - /** - * Checks a {@link IFile} to make sure it should be packaged as standard resources. - * @param file the IFile representing the file. - * @return true if the file should be packaged as standard java resources. - */ - public static boolean checkFileForPackaging(IFile file) { - String name = file.getName(); - - String ext = file.getFileExtension(); - return ApkBuilder.checkFileForPackaging(name, ext); - } - - /** - * Checks whether an {@link IFolder} and its content is valid for packaging into the .apk as - * standard Java resource. - * @param folder the {@link IFolder} to check. - */ - public static boolean checkFolderForPackaging(IFolder folder) { - String name = folder.getName(); - return ApkBuilder.checkFolderForPackaging(name); - } - - /** - * Returns a list of {@link IJavaProject} matching the provided {@link IProject} objects. - * @param projects the IProject objects. - * @return a new list object containing the IJavaProject object for the given IProject objects. - * @throws CoreException - */ - public static List<IJavaProject> getJavaProjects(List<IProject> projects) throws CoreException { - ArrayList<IJavaProject> list = new ArrayList<IJavaProject>(); - - for (IProject p : projects) { - if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) { - - list.add(JavaCore.create(p)); - } - } - - return list; - } - - /** - * Get the stderr output of a process and return when the process is done. - * @param process The process to get the output from - * @param stderr The array to store the stderr output - * @return the process return code. - * @throws InterruptedException - */ - public final static int grabProcessOutput( - final IProject project, - final Process process, - final ArrayList<String> stderr) - throws InterruptedException { - - return GrabProcessOutput.grabProcessOutput( - process, - Wait.WAIT_FOR_READERS, // we really want to make sure we get all the output! - new IProcessOutput() { - - @SuppressWarnings("unused") - @Override - public void out(@Nullable String line) { - if (line != null) { - // If benchmarking always print the lines that - // correspond to benchmarking info returned by ADT - if (BENCHMARK_FLAG && line.startsWith("BENCHMARK:")) { //$NON-NLS-1$ - AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, - project, line); - } else { - AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, - project, line); - } - } - } - - @Override - public void err(@Nullable String line) { - if (line != null) { - stderr.add(line); - if (BuildVerbosity.VERBOSE == AdtPrefs.getPrefs().getBuildVerbosity()) { - AdtPlugin.printErrorToConsole(project, line); - } - } - } - }); - } -} |