Home | History | Annotate | Download | only in build
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.build;
     18 
     19 import com.android.ide.eclipse.adt.AdtConstants;
     20 import com.android.ide.eclipse.adt.AdtPlugin;
     21 import com.android.ide.eclipse.adt.AndroidPrintStream;
     22 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
     23 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
     24 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     25 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
     26 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
     27 import com.android.prefs.AndroidLocation.AndroidLocationException;
     28 import com.android.sdklib.IAndroidTarget;
     29 import com.android.sdklib.SdkConstants;
     30 import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
     31 import com.android.sdklib.build.ApkBuilder;
     32 import com.android.sdklib.build.ApkCreationException;
     33 import com.android.sdklib.build.DuplicateFileException;
     34 import com.android.sdklib.build.IArchiveBuilder;
     35 import com.android.sdklib.build.SealedApkException;
     36 import com.android.sdklib.build.ApkBuilder.JarStatus;
     37 import com.android.sdklib.build.ApkBuilder.SigningInfo;
     38 import com.android.sdklib.internal.build.DebugKeyProvider;
     39 import com.android.sdklib.internal.build.SignedJarBuilder;
     40 import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException;
     41 
     42 import org.eclipse.core.resources.IFile;
     43 import org.eclipse.core.resources.IFolder;
     44 import org.eclipse.core.resources.IProject;
     45 import org.eclipse.core.resources.IResource;
     46 import org.eclipse.core.resources.IResourceProxy;
     47 import org.eclipse.core.resources.IResourceProxyVisitor;
     48 import org.eclipse.core.resources.IWorkspace;
     49 import org.eclipse.core.resources.IWorkspaceRoot;
     50 import org.eclipse.core.resources.ResourcesPlugin;
     51 import org.eclipse.core.runtime.CoreException;
     52 import org.eclipse.core.runtime.IPath;
     53 import org.eclipse.core.runtime.IStatus;
     54 import org.eclipse.core.runtime.Status;
     55 import org.eclipse.jdt.core.IClasspathContainer;
     56 import org.eclipse.jdt.core.IClasspathEntry;
     57 import org.eclipse.jdt.core.IJavaProject;
     58 import org.eclipse.jdt.core.JavaCore;
     59 import org.eclipse.jdt.core.JavaModelException;
     60 import org.eclipse.jface.preference.IPreferenceStore;
     61 
     62 import java.io.BufferedReader;
     63 import java.io.File;
     64 import java.io.FileWriter;
     65 import java.io.IOException;
     66 import java.io.InputStreamReader;
     67 import java.io.PrintStream;
     68 import java.security.PrivateKey;
     69 import java.security.cert.X509Certificate;
     70 import java.util.ArrayList;
     71 import java.util.List;
     72 import java.util.Map;
     73 import java.util.TreeMap;
     74 
     75 /**
     76  * Helper with methods for the last 3 steps of the generation of an APK.
     77  *
     78  * {@link #packageResources(IFile, IProject[], String, int, String, String)} packages the
     79  * application resources using aapt into a zip file that is ready to be integrated into the apk.
     80  *
     81  * {@link #executeDx(IJavaProject, String, String, IJavaProject[])} will convert the Java byte
     82  * code into the Dalvik bytecode.
     83  *
     84  * {@link #finalPackage(String, String, String, boolean, IJavaProject, IProject[], IJavaProject[], String, boolean)}
     85  * will make the apk from all the previous components.
     86  *
     87  * This class only executes the 3 above actions. It does not handle the errors, and simply sends
     88  * them back as custom exceptions.
     89  *
     90  * Warnings are handled by the {@link ResourceMarker} interface.
     91  *
     92  * Console output (verbose and non verbose) is handled through the {@link AndroidPrintStream} passed
     93  * to the constructor.
     94  *
     95  */
     96 public class BuildHelper {
     97 
     98     private static final String CONSOLE_PREFIX_DX = "Dx";   //$NON-NLS-1$
     99     private final static String TEMP_PREFIX = "android_";   //$NON-NLS-1$
    100 
    101     private static final String COMMAND_CRUNCH = "crunch";  //$NON-NLS-1$
    102     private static final String COMMAND_PACKAGE = "package"; //$NON-NLS-1$
    103 
    104     private final IProject mProject;
    105     private final AndroidPrintStream mOutStream;
    106     private final AndroidPrintStream mErrStream;
    107     private final boolean mVerbose;
    108     private final boolean mDebugMode;
    109 
    110     public static final boolean BENCHMARK_FLAG = false;
    111     public static long sStartOverallTime = 0;
    112     public static long sStartJavaCTime = 0;
    113 
    114     private final static int MILLION = 1000000;
    115 
    116     /**
    117      * An object able to put a marker on a resource.
    118      */
    119     public interface ResourceMarker {
    120         void setWarning(IResource resource, String message);
    121     }
    122 
    123     /**
    124      * Creates a new post-compiler helper
    125      * @param project
    126      * @param outStream
    127      * @param errStream
    128      * @param debugMode whether this is a debug build
    129      * @param verbose
    130      */
    131     public BuildHelper(IProject project, AndroidPrintStream outStream,
    132             AndroidPrintStream errStream, boolean debugMode, boolean verbose) {
    133         mProject = project;
    134         mOutStream = outStream;
    135         mErrStream = errStream;
    136         mDebugMode = debugMode;
    137         mVerbose = verbose;
    138     }
    139 
    140     public void updateCrunchCache() throws AaptExecException, AaptResultException {
    141         // Benchmarking start
    142         long startCrunchTime = 0;
    143         if (BENCHMARK_FLAG) {
    144             String msg = "BENCHMARK ADT: Starting Initial Packaging (.ap_)"; //$NON-NLS-1$
    145             AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg);
    146             startCrunchTime = System.nanoTime();
    147         }
    148 
    149         // Get the resources folder to crunch from
    150         IFolder resFolder = mProject.getFolder(AdtConstants.WS_RESOURCES);
    151         List<String> resPaths = new ArrayList<String>();
    152         resPaths.add(resFolder.getLocation().toOSString());
    153 
    154         // Get the output folder where the cache is stored.
    155         IFolder cacheFolder = mProject.getFolder(AdtConstants.WS_CRUNCHCACHE);
    156         String cachePath = cacheFolder.getLocation().toOSString();
    157 
    158         /* For crunching, we don't need the osManifestPath, osAssetsPath, or the configFilter
    159          * parameters for executeAapt
    160          */
    161         executeAapt(COMMAND_CRUNCH, "", resPaths, "", cachePath, "", 0);
    162 
    163         // Benchmarking end
    164         if (BENCHMARK_FLAG) {
    165             String msg = "BENCHMARK ADT: Ending Initial Package (.ap_). \nTime Elapsed: " //$NON-NLS-1$
    166                             + ((System.nanoTime() - startCrunchTime)/MILLION) + "ms";     //$NON-NLS-1$
    167             AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg);
    168         }
    169     }
    170 
    171     public static void writeResources(IArchiveBuilder builder, IJavaProject javaProject)
    172             throws DuplicateFileException, ApkCreationException, SealedApkException, CoreException {
    173         writeStandardResources(builder, javaProject, null);
    174     }
    175 
    176     /**
    177      * Packages the resources of the projet into a .ap_ file.
    178      * @param manifestFile the manifest of the project.
    179      * @param libProjects the list of library projects that this project depends on.
    180      * @param resFilter an optional resource filter to be used with the -c option of aapt. If null
    181      * no filters are used.
    182      * @param versionCode an optional versionCode to be inserted in the manifest during packaging.
    183      * If the value is <=0, no values are inserted.
    184      * @param outputFolder where to write the resource ap_ file.
    185      * @param outputFilename the name of the resource ap_ file.
    186      * @throws AaptExecException
    187      * @throws AaptResultException
    188      */
    189     public void packageResources(IFile manifestFile, List<IProject> libProjects, String resFilter,
    190             int versionCode, String outputFolder, String outputFilename)
    191             throws AaptExecException, AaptResultException {
    192 
    193         // Benchmarking start
    194         long startPackageTime = 0;
    195         if (BENCHMARK_FLAG) {
    196             String msg = "BENCHMARK ADT: Starting Initial Packaging (.ap_)";    //$NON-NLS-1$
    197             AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg);
    198             startPackageTime = System.nanoTime();
    199         }
    200 
    201         // need to figure out some path before we can execute aapt;
    202 
    203         // get the cache folder
    204         IFolder cacheFolder = mProject.getFolder(AdtConstants.WS_CRUNCHCACHE);
    205 
    206         // get the resource folder
    207         IFolder resFolder = mProject.getFolder(AdtConstants.WS_RESOURCES);
    208 
    209         // and the assets folder
    210         IFolder assetsFolder = mProject.getFolder(AdtConstants.WS_ASSETS);
    211 
    212         // we need to make sure this one exists.
    213         if (assetsFolder.exists() == false) {
    214             assetsFolder = null;
    215         }
    216 
    217         IPath cacheLocation = cacheFolder.getLocation();
    218         IPath resLocation = resFolder.getLocation();
    219         IPath manifestLocation = manifestFile.getLocation();
    220 
    221         if (resLocation != null && manifestLocation != null) {
    222             // list of res folder (main project + maybe libraries)
    223             ArrayList<String> osResPaths = new ArrayList<String>();
    224             osResPaths.add(cacheLocation.toOSString()); // PNG crunch cache
    225             osResPaths.add(resLocation.toOSString()); //main project
    226 
    227             // libraries?
    228             if (libProjects != null) {
    229                 for (IProject lib : libProjects) {
    230                     IFolder libCacheFolder = lib.getFolder(AdtConstants.WS_CRUNCHCACHE);
    231                     if (libCacheFolder.exists()) {
    232                         osResPaths.add(libCacheFolder.getLocation().toOSString());
    233                     }
    234                     IFolder libResFolder = lib.getFolder(AdtConstants.WS_RESOURCES);
    235                     if (libResFolder.exists()) {
    236                         osResPaths.add(libResFolder.getLocation().toOSString());
    237                     }
    238                 }
    239             }
    240 
    241             String osManifestPath = manifestLocation.toOSString();
    242 
    243             String osAssetsPath = null;
    244             if (assetsFolder != null) {
    245                 osAssetsPath = assetsFolder.getLocation().toOSString();
    246             }
    247 
    248             // build the default resource package
    249             executeAapt(COMMAND_PACKAGE, osManifestPath, osResPaths, osAssetsPath,
    250                     outputFolder + File.separator + outputFilename, resFilter,
    251                     versionCode);
    252         }
    253 
    254         // Benchmarking end
    255         if (BENCHMARK_FLAG) {
    256             String msg = "BENCHMARK ADT: Ending Initial Package (.ap_). \nTime Elapsed: " //$NON-NLS-1$
    257                             + ((System.nanoTime() - startPackageTime)/MILLION) + "ms";    //$NON-NLS-1$
    258             AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg);
    259         }
    260     }
    261 
    262     /**
    263      * Makes a final package signed with the debug key.
    264      *
    265      * Packages the dex files, the temporary resource file into the final package file.
    266      *
    267      * Whether the package is a debug package is controlled with the <var>debugMode</var> parameter
    268      * in {@link #PostCompilerHelper(IProject, PrintStream, PrintStream, boolean, boolean)}
    269      *
    270      * @param intermediateApk The path to the temporary resource file.
    271      * @param dex The path to the dex file.
    272      * @param output The path to the final package file to create.
    273      * @param javaProject the java project being compiled
    274      * @param libProjects an optional list of library projects (can be null)
    275      * @param referencedJavaProjects referenced projects.
    276      * @return true if success, false otherwise.
    277      * @throws ApkCreationException
    278      * @throws AndroidLocationException
    279      * @throws KeytoolException
    280      * @throws NativeLibInJarException
    281      * @throws CoreException
    282      * @throws DuplicateFileException
    283      */
    284     public void finalDebugPackage(String intermediateApk, String dex, String output,
    285             final IJavaProject javaProject, List<IProject> libProjects,
    286             List<IJavaProject> referencedJavaProjects, ResourceMarker resMarker)
    287             throws ApkCreationException, KeytoolException, AndroidLocationException,
    288             NativeLibInJarException, DuplicateFileException, CoreException {
    289 
    290         AdtPlugin adt = AdtPlugin.getDefault();
    291         if (adt == null) {
    292             return;
    293         }
    294 
    295         // get the debug keystore to use.
    296         IPreferenceStore store = adt.getPreferenceStore();
    297         String keystoreOsPath = store.getString(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE);
    298         if (keystoreOsPath == null || new File(keystoreOsPath).isFile() == false) {
    299             keystoreOsPath = DebugKeyProvider.getDefaultKeyStoreOsPath();
    300             AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, mProject,
    301                     Messages.ApkBuilder_Using_Default_Key);
    302         } else {
    303             AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, mProject,
    304                     String.format(Messages.ApkBuilder_Using_s_To_Sign, keystoreOsPath));
    305         }
    306 
    307         // from the keystore, get the signing info
    308         SigningInfo info = ApkBuilder.getDebugKey(keystoreOsPath, mVerbose ? mOutStream : null);
    309 
    310         finalPackage(intermediateApk, dex, output, javaProject, libProjects,
    311                 referencedJavaProjects,
    312                 info != null ? info.key : null, info != null ? info.certificate : null, resMarker);
    313     }
    314 
    315     /**
    316      * Makes the final package.
    317      *
    318      * Packages the dex files, the temporary resource file into the final package file.
    319      *
    320      * Whether the package is a debug package is controlled with the <var>debugMode</var> parameter
    321      * in {@link #PostCompilerHelper(IProject, PrintStream, PrintStream, boolean, boolean)}
    322      *
    323      * @param intermediateApk The path to the temporary resource file.
    324      * @param dex The path to the dex file.
    325      * @param output The path to the final package file to create.
    326      * @param debugSign whether the apk must be signed with the debug key.
    327      * @param javaProject the java project being compiled
    328      * @param libProjects an optional list of library projects (can be null)
    329      * @param referencedJavaProjects referenced projects.
    330      * @param abiFilter an optional filter. If not null, then only the matching ABI is included in
    331      * the final archive
    332      * @return true if success, false otherwise.
    333      * @throws NativeLibInJarException
    334      * @throws ApkCreationException
    335      * @throws CoreException
    336      * @throws DuplicateFileException
    337      */
    338     public void finalPackage(String intermediateApk, String dex, String output,
    339             final IJavaProject javaProject, List<IProject> libProjects,
    340             List<IJavaProject> referencedJavaProjects, PrivateKey key,
    341             X509Certificate certificate, ResourceMarker resMarker)
    342             throws NativeLibInJarException, ApkCreationException, DuplicateFileException,
    343             CoreException {
    344 
    345         try {
    346             ApkBuilder apkBuilder = new ApkBuilder(output, intermediateApk, dex,
    347                     key, certificate,
    348                     mVerbose ? mOutStream: null);
    349             apkBuilder.setDebugMode(mDebugMode);
    350 
    351             // Now we write the standard resources from the project and the referenced projects.
    352             writeStandardResources(apkBuilder, javaProject, referencedJavaProjects);
    353 
    354             // Now we write the standard resources from the external jars
    355             for (String libraryOsPath : getExternalDependencies(resMarker)) {
    356                 File libFile = new File(libraryOsPath);
    357                 if (libFile.isFile()) {
    358                     JarStatus jarStatus = apkBuilder.addResourcesFromJar(new File(libraryOsPath));
    359 
    360                     // check if we found native libraries in the external library. This
    361                     // constitutes an error or warning depending on if they are in lib/
    362                     if (jarStatus.getNativeLibs().size() > 0) {
    363                         String libName = new File(libraryOsPath).getName();
    364 
    365                         String msg = String.format(
    366                                 "Native libraries detected in '%1$s'. See console for more information.",
    367                                 libName);
    368 
    369                         ArrayList<String> consoleMsgs = new ArrayList<String>();
    370 
    371                         consoleMsgs.add(String.format(
    372                                 "The library '%1$s' contains native libraries that will not run on the device.",
    373                                 libName));
    374 
    375                         if (jarStatus.hasNativeLibsConflicts()) {
    376                             consoleMsgs.add("Additionally some of those libraries will interfer with the installation of the application because of their location in lib/");
    377                             consoleMsgs.add("lib/ is reserved for NDK libraries.");
    378                         }
    379 
    380                         consoleMsgs.add("The following libraries were found:");
    381 
    382                         for (String lib : jarStatus.getNativeLibs()) {
    383                             consoleMsgs.add(" - " + lib);
    384                         }
    385 
    386                         String[] consoleStrings = consoleMsgs.toArray(new String[consoleMsgs.size()]);
    387 
    388                         // if there's a conflict or if the prefs force error on any native code in jar
    389                         // files, throw an exception
    390                         if (jarStatus.hasNativeLibsConflicts() ||
    391                                 AdtPrefs.getPrefs().getBuildForceErrorOnNativeLibInJar()) {
    392                             throw new NativeLibInJarException(jarStatus, msg, libName, consoleStrings);
    393                         } else {
    394                             // otherwise, put a warning, and output to the console also.
    395                             if (resMarker != null) {
    396                                 resMarker.setWarning(mProject, msg);
    397                             }
    398 
    399                             for (String string : consoleStrings) {
    400                                 mOutStream.println(string);
    401                             }
    402                         }
    403                     }
    404                 } else if (libFile.isDirectory()) {
    405                     // this is technically not a source folder (class folder instead) but since we
    406                     // only care about Java resources (ie non class/java files) this will do the
    407                     // same
    408                     apkBuilder.addSourceFolder(libFile);
    409                 }
    410             }
    411 
    412             // now write the native libraries.
    413             // First look if the lib folder is there.
    414             IResource libFolder = mProject.findMember(SdkConstants.FD_NATIVE_LIBS);
    415             if (libFolder != null && libFolder.exists() &&
    416                     libFolder.getType() == IResource.FOLDER) {
    417                 // get a File for the folder.
    418                 apkBuilder.addNativeLibraries(libFolder.getLocation().toFile());
    419             }
    420 
    421             // write the native libraries for the library projects.
    422             if (libProjects != null) {
    423                 for (IProject lib : libProjects) {
    424                     libFolder = lib.findMember(SdkConstants.FD_NATIVE_LIBS);
    425                     if (libFolder != null && libFolder.exists() &&
    426                             libFolder.getType() == IResource.FOLDER) {
    427                         apkBuilder.addNativeLibraries(libFolder.getLocation().toFile());
    428                     }
    429                 }
    430             }
    431 
    432             // seal the APK.
    433             apkBuilder.sealApk();
    434         } catch (SealedApkException e) {
    435             // this won't happen as we control when the apk is sealed.
    436         }
    437     }
    438 
    439     /**
    440      * Return a list of the project output for compiled Java code.
    441      * @return
    442      * @throws CoreException
    443      */
    444     public String[] getProjectJavaOutputs() throws CoreException {
    445         IFolder outputFolder = BaseProjectHelper.getJavaOutputFolder(mProject);
    446 
    447         // get the list of referenced projects output to add
    448         List<IProject> javaProjects = ProjectHelper.getReferencedProjects(mProject);
    449         List<IJavaProject> referencedJavaProjects = BuildHelper.getJavaProjects(javaProjects);
    450 
    451         // get the project output, and since it's a new list object, just add the outputFolder
    452         // of the project directly to it.
    453         List<String> projectOutputs = getProjectJavaOutputs(referencedJavaProjects);
    454 
    455         projectOutputs.add(0, outputFolder.getLocation().toOSString());
    456 
    457         return projectOutputs.toArray(new String[projectOutputs.size()]);
    458     }
    459 
    460     /**
    461      * Returns an array for all the compiled code for the project. This can include the
    462      * code compiled by Eclipse for the main project and dependencies (Java only projects), as well
    463      * as external jars used by the project or its library.
    464      *
    465      * This array of paths is compatible with the input for dx and can be passed as is to
    466      * {@link #executeDx(IJavaProject, String[], String)}.
    467      *
    468      * @param resMarker
    469      * @return a array (never empty) containing paths to compiled code.
    470      * @throws CoreException
    471      */
    472     public String[] getCompiledCodePaths(boolean includeProjectOutputs, ResourceMarker resMarker)
    473             throws CoreException {
    474 
    475         // get the list of libraries to include with the source code
    476         String[] libraries = getExternalDependencies(resMarker);
    477 
    478         int startIndex = 0;
    479 
    480         String[] compiledPaths;
    481 
    482         if (includeProjectOutputs) {
    483             String[] projectOutputs = getProjectJavaOutputs();
    484 
    485             compiledPaths = new String[libraries.length + projectOutputs.length];
    486 
    487             System.arraycopy(projectOutputs, 0, compiledPaths, 0, projectOutputs.length);
    488             startIndex = projectOutputs.length;
    489         } else {
    490             compiledPaths = new String[libraries.length];
    491         }
    492 
    493         System.arraycopy(libraries, 0, compiledPaths, startIndex, libraries.length);
    494 
    495         return compiledPaths;
    496     }
    497 
    498     public void runProguard(File proguardConfig, File inputJar, String[] jarFiles,
    499                             File obfuscatedJar, File logOutput)
    500             throws ProguardResultException, ProguardExecException, IOException {
    501         IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
    502 
    503         // prepare the command line for proguard
    504         List<String> command = new ArrayList<String>();
    505         command.add(AdtPlugin.getOsAbsoluteProguard());
    506 
    507         command.add("@" + quotePath(proguardConfig.getAbsolutePath())); //$NON-NLS-1$
    508 
    509         command.add("-injars"); //$NON-NLS-1$
    510         StringBuilder sb = new StringBuilder(quotePath(inputJar.getAbsolutePath()));
    511         for (String jarFile : jarFiles) {
    512             sb.append(File.pathSeparatorChar);
    513             sb.append(quotePath(jarFile));
    514         }
    515         command.add(quoteWinArg(sb.toString()));
    516 
    517         command.add("-outjars"); //$NON-NLS-1$
    518         command.add(quotePath(obfuscatedJar.getAbsolutePath()));
    519 
    520         command.add("-libraryjars"); //$NON-NLS-1$
    521         sb = new StringBuilder(quotePath(target.getPath(IAndroidTarget.ANDROID_JAR)));
    522         IOptionalLibrary[] libraries = target.getOptionalLibraries();
    523         if (libraries != null) {
    524             for (IOptionalLibrary lib : libraries) {
    525                 sb.append(File.pathSeparatorChar);
    526                 sb.append(quotePath(lib.getJarPath()));
    527             }
    528         }
    529         command.add(quoteWinArg(sb.toString()));
    530 
    531         if (logOutput != null) {
    532             if (logOutput.isDirectory() == false) {
    533                 logOutput.mkdirs();
    534             }
    535 
    536             command.add("-dump");                                              //$NON-NLS-1$
    537             command.add(new File(logOutput, "dump.txt").getAbsolutePath());    //$NON-NLS-1$
    538 
    539             command.add("-printseeds");                                        //$NON-NLS-1$
    540             command.add(new File(logOutput, "seeds.txt").getAbsolutePath());   //$NON-NLS-1$
    541 
    542             command.add("-printusage");                                        //$NON-NLS-1$
    543             command.add(new File(logOutput, "usage.txt").getAbsolutePath());   //$NON-NLS-1$
    544 
    545             command.add("-printmapping");                                      //$NON-NLS-1$
    546             command.add(new File(logOutput, "mapping.txt").getAbsolutePath()); //$NON-NLS-1$
    547         }
    548 
    549         String commandArray[] = null;
    550 
    551         if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) {
    552             commandArray = createWindowsProguardConfig(command);
    553         }
    554 
    555         if (commandArray == null) {
    556             // For Mac & Linux, use a regular command string array.
    557             commandArray = command.toArray(new String[command.size()]);
    558         }
    559 
    560         // Define PROGUARD_HOME to point to $SDK/tools/proguard if it's not yet defined.
    561         // The Mac/Linux proguard.sh can infer it correctly but not the proguard.bat one.
    562         String[] envp = null;
    563         Map<String, String> envMap = new TreeMap<String, String>(System.getenv());
    564         if (!envMap.containsKey("PROGUARD_HOME")) {                                    //$NON-NLS-1$
    565             envMap.put("PROGUARD_HOME",    Sdk.getCurrent().getSdkLocation() +         //$NON-NLS-1$
    566                                             SdkConstants.FD_TOOLS + File.separator +
    567                                             SdkConstants.FD_PROGUARD);
    568             envp = new String[envMap.size()];
    569             int i = 0;
    570             for (Map.Entry<String, String> entry : envMap.entrySet()) {
    571                 envp[i++] = String.format("%1$s=%2$s",                                 //$NON-NLS-1$
    572                                           entry.getKey(),
    573                                           entry.getValue());
    574             }
    575         }
    576 
    577         if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) {
    578             sb = new StringBuilder();
    579             for (String c : commandArray) {
    580                 sb.append(c).append(' ');
    581             }
    582             AdtPlugin.printToConsole(mProject, sb.toString());
    583         }
    584 
    585         // launch
    586         int execError = 1;
    587         try {
    588             // launch the command line process
    589             Process process = Runtime.getRuntime().exec(commandArray, envp);
    590 
    591             // list to store each line of stderr
    592             ArrayList<String> results = new ArrayList<String>();
    593 
    594             // get the output and return code from the process
    595             execError = grabProcessOutput(mProject, process, results);
    596 
    597             if (mVerbose) {
    598                 for (String resultString : results) {
    599                     mOutStream.println(resultString);
    600                 }
    601             }
    602 
    603             if (execError != 0) {
    604                 throw new ProguardResultException(execError,
    605                         results.toArray(new String[results.size()]));
    606             }
    607 
    608         } catch (IOException e) {
    609             String msg = String.format(Messages.Proguard_Exec_Error, commandArray[0]);
    610             throw new ProguardExecException(msg, e);
    611         } catch (InterruptedException e) {
    612             String msg = String.format(Messages.Proguard_Exec_Error, commandArray[0]);
    613             throw new ProguardExecException(msg, e);
    614         }
    615     }
    616 
    617     /**
    618      * For tools R8 up to R11, the proguard.bat launcher on Windows only accepts
    619      * arguments %1..%9. Since we generally have about 15 arguments, we were working
    620      * around this by generating a temporary config file for proguard and then using
    621      * that.
    622      * Starting with tools R12, the proguard.bat launcher has been fixed to take
    623      * all arguments using %* so we no longer need this hack.
    624      *
    625      * @param command
    626      * @return
    627      * @throws IOException
    628      */
    629     private String[] createWindowsProguardConfig(List<String> command) throws IOException {
    630 
    631         // Arg 0 is the proguard.bat path and arg 1 is the user config file
    632         String launcher = AdtPlugin.readFile(new File(command.get(0)));
    633         if (launcher.contains("%*")) {                                      //$NON-NLS-1$
    634             // This is the launcher from Tools R12. Don't work around it.
    635             return null;
    636         }
    637 
    638         // On Windows, proguard.bat can only pass %1...%9 to the java -jar proguard.jar
    639         // call, but we have at least 15 arguments here so some get dropped silently
    640         // and quoting is a big issue. So instead we'll work around that by writing
    641         // all the arguments to a temporary config file.
    642 
    643         String[] commandArray = new String[3];
    644 
    645         commandArray[0] = command.get(0);
    646         commandArray[1] = command.get(1);
    647 
    648         // Write all the other arguments to a config file
    649         File argsFile = File.createTempFile(TEMP_PREFIX, ".pro");           //$NON-NLS-1$
    650         // TODO FIXME this may leave a lot of temp files around on a long session.
    651         // Should have a better way to clean up e.g. before each build.
    652         argsFile.deleteOnExit();
    653 
    654         FileWriter fw = new FileWriter(argsFile);
    655 
    656         for (int i = 2; i < command.size(); i++) {
    657             String s = command.get(i);
    658             fw.write(s);
    659             fw.write(s.startsWith("-") ? ' ' : '\n');                       //$NON-NLS-1$
    660         }
    661 
    662         fw.close();
    663 
    664         commandArray[2] = "@" + argsFile.getAbsolutePath();                 //$NON-NLS-1$
    665         return commandArray;
    666     }
    667 
    668     /**
    669      * Quotes a single path for proguard to deal with spaces.
    670      *
    671      * @param path The path to quote.
    672      * @return The original path if it doesn't contain a space.
    673      *   Or the original path surrounded by single quotes if it contains spaces.
    674      */
    675     private String quotePath(String path) {
    676         if (path.indexOf(' ') != -1) {
    677             path = '\'' + path + '\'';
    678         }
    679         return path;
    680     }
    681 
    682     /**
    683      * Quotes a compound proguard argument to deal with spaces.
    684      * <p/>
    685      * Proguard takes multi-path arguments such as "path1;path2" for some options.
    686      * When the {@link #quotePath} methods adds quotes for such a path if it contains spaces,
    687      * the proguard shell wrapper will absorb the quotes, so we need to quote around the
    688      * quotes.
    689      *
    690      * @param path The path to quote.
    691      * @return The original path if it doesn't contain a single quote.
    692      *   Or on Windows the original path surrounded by double quotes if it contains a quote.
    693      */
    694     private String quoteWinArg(String path) {
    695         if (path.indexOf('\'') != -1 &&
    696                 SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) {
    697             path = '"' + path + '"';
    698         }
    699         return path;
    700     }
    701 
    702 
    703     /**
    704      * Execute the Dx tool for dalvik code conversion.
    705      * @param javaProject The java project
    706      * @param inputPath the path to the main input of dex
    707      * @param osOutFilePath the path of the dex file to create.
    708      *
    709      * @throws CoreException
    710      * @throws DexException
    711      */
    712     public void executeDx(IJavaProject javaProject, String[] inputPaths, String osOutFilePath)
    713             throws CoreException, DexException {
    714 
    715         // get the dex wrapper
    716         Sdk sdk = Sdk.getCurrent();
    717         DexWrapper wrapper = sdk.getDexWrapper();
    718 
    719         if (wrapper == null) {
    720             throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    721                     Messages.ApkBuilder_UnableBuild_Dex_Not_loaded));
    722         }
    723 
    724         try {
    725             // set a temporary prefix on the print streams.
    726             mOutStream.setPrefix(CONSOLE_PREFIX_DX);
    727             mErrStream.setPrefix(CONSOLE_PREFIX_DX);
    728 
    729             int res = wrapper.run(osOutFilePath,
    730                     inputPaths,
    731                     mVerbose,
    732                     mOutStream, mErrStream);
    733 
    734             mOutStream.setPrefix(null);
    735             mErrStream.setPrefix(null);
    736 
    737             if (res != 0) {
    738                 // output error message and marker the project.
    739                 String message = String.format(Messages.Dalvik_Error_d, res);
    740                 throw new DexException(message);
    741             }
    742         } catch (DexException e) {
    743             throw e;
    744         } catch (Throwable t) {
    745             String message = t.getMessage();
    746             if (message == null) {
    747                 message = t.getClass().getCanonicalName();
    748             }
    749             message = String.format(Messages.Dalvik_Error_s, message);
    750 
    751             throw new DexException(message, t);
    752         }
    753     }
    754 
    755     /**
    756      * Executes aapt. If any error happen, files or the project will be marked.
    757      * @param command The command for aapt to execute. Currently supported: package and crunch
    758      * @param osManifestPath The path to the manifest file
    759      * @param osResPath The path to the res folder
    760      * @param osAssetsPath The path to the assets folder. This can be null.
    761      * @param osOutFilePath The path to the temporary resource file to create,
    762      *   or in the case of crunching the path to the cache to create/update.
    763      * @param configFilter The configuration filter for the resources to include
    764      * (used with -c option, for example "port,en,fr" to include portrait, English and French
    765      * resources.)
    766      * @param versionCode optional version code to insert in the manifest during packaging. If <=0
    767      * then no value is inserted
    768      * @throws AaptExecException
    769      * @throws AaptResultException
    770      */
    771     private void executeAapt(String aaptCommand, String osManifestPath,
    772             List<String> osResPaths, String osAssetsPath, String osOutFilePath,
    773             String configFilter, int versionCode) throws AaptExecException, AaptResultException {
    774         IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
    775 
    776         // Create the command line.
    777         ArrayList<String> commandArray = new ArrayList<String>();
    778         commandArray.add(target.getPath(IAndroidTarget.AAPT));
    779         commandArray.add(aaptCommand);
    780         if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) {
    781             commandArray.add("-v"); //$NON-NLS-1$
    782         }
    783 
    784         // Common to all commands
    785         for (String path : osResPaths) {
    786             commandArray.add("-S"); //$NON-NLS-1$
    787             commandArray.add(path);
    788         }
    789 
    790         if (aaptCommand.equals(COMMAND_PACKAGE)) {
    791             commandArray.add("-f");          //$NON-NLS-1$
    792             commandArray.add("--no-crunch"); //$NON-NLS-1$
    793 
    794             // if more than one res, this means there's a library (or more) and we need
    795             // to activate the auto-add-overlay
    796             if (osResPaths.size() > 1) {
    797                 commandArray.add("--auto-add-overlay"); //$NON-NLS-1$
    798             }
    799 
    800             if (mDebugMode) {
    801                 commandArray.add("--debug-mode"); //$NON-NLS-1$
    802             }
    803 
    804             if (versionCode > 0) {
    805                 commandArray.add("--version-code"); //$NON-NLS-1$
    806                 commandArray.add(Integer.toString(versionCode));
    807             }
    808 
    809             if (configFilter != null) {
    810                 commandArray.add("-c"); //$NON-NLS-1$
    811                 commandArray.add(configFilter);
    812             }
    813 
    814             commandArray.add("-M"); //$NON-NLS-1$
    815             commandArray.add(osManifestPath);
    816 
    817             if (osAssetsPath != null) {
    818                 commandArray.add("-A"); //$NON-NLS-1$
    819                 commandArray.add(osAssetsPath);
    820             }
    821 
    822             commandArray.add("-I"); //$NON-NLS-1$
    823             commandArray.add(target.getPath(IAndroidTarget.ANDROID_JAR));
    824 
    825             commandArray.add("-F"); //$NON-NLS-1$
    826             commandArray.add(osOutFilePath);
    827         } else if (aaptCommand.equals(COMMAND_CRUNCH)) {
    828             commandArray.add("-C"); //$NON-NLS-1$
    829             commandArray.add(osOutFilePath);
    830         }
    831 
    832         String command[] = commandArray.toArray(
    833                 new String[commandArray.size()]);
    834 
    835         if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) {
    836             StringBuilder sb = new StringBuilder();
    837             for (String c : command) {
    838                 sb.append(c);
    839                 sb.append(' ');
    840             }
    841             AdtPlugin.printToConsole(mProject, sb.toString());
    842         }
    843 
    844         // Benchmarking start
    845         long startAaptTime = 0;
    846         if (BENCHMARK_FLAG) {
    847             String msg = "BENCHMARK ADT: Starting " + aaptCommand  //$NON-NLS-1$
    848                          + " call to Aapt";                        //$NON-NLS-1$
    849             AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg);
    850             startAaptTime = System.nanoTime();
    851         }
    852 
    853         // launch
    854         int execError = 1;
    855         try {
    856             // launch the command line process
    857             Process process = Runtime.getRuntime().exec(command);
    858 
    859             // list to store each line of stderr
    860             ArrayList<String> results = new ArrayList<String>();
    861 
    862             // get the output and return code from the process
    863             execError = grabProcessOutput(mProject, process, results);
    864 
    865             if (mVerbose) {
    866                 for (String resultString : results) {
    867                     mOutStream.println(resultString);
    868                 }
    869             }
    870             if (execError != 0) {
    871                 throw new AaptResultException(execError,
    872                         results.toArray(new String[results.size()]));
    873             }
    874         } catch (IOException e) {
    875             String msg = String.format(Messages.AAPT_Exec_Error, command[0]);
    876             throw new AaptExecException(msg, e);
    877         } catch (InterruptedException e) {
    878             String msg = String.format(Messages.AAPT_Exec_Error, command[0]);
    879             throw new AaptExecException(msg, e);
    880         }
    881 
    882         // Benchmarking end
    883         if (BENCHMARK_FLAG) {
    884             String msg = "BENCHMARK ADT: Ending " + aaptCommand                  //$NON-NLS-1$
    885                          + " call to Aapt.\nBENCHMARK ADT: Time Elapsed: "       //$NON-NLS-1$
    886                          + ((System.nanoTime() - startAaptTime)/MILLION) + "ms"; //$NON-NLS-1$
    887             AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject, msg);
    888         }
    889     }
    890 
    891     /**
    892      * Writes the standard resources of a project and its referenced projects
    893      * into a {@link SignedJarBuilder}.
    894      * Standard resources are non java/aidl files placed in the java package folders.
    895      * @param builder the archive builder.
    896      * @param javaProject the javaProject object.
    897      * @param referencedJavaProjects the java projects that this project references.
    898      * @throws ApkCreationException if an error occurred
    899      * @throws SealedApkException if the APK is already sealed.
    900      * @throws DuplicateFileException if a file conflicts with another already added to the APK
    901      *                                   at the same location inside the APK archive.
    902      * @throws CoreException
    903      */
    904     private static void writeStandardResources(IArchiveBuilder builder, IJavaProject javaProject,
    905             List<IJavaProject> referencedJavaProjects)
    906             throws DuplicateFileException, ApkCreationException, SealedApkException,
    907             CoreException  {
    908         IWorkspace ws = ResourcesPlugin.getWorkspace();
    909         IWorkspaceRoot wsRoot = ws.getRoot();
    910 
    911         writeStandardProjectResources(builder, javaProject, wsRoot);
    912 
    913         if (referencedJavaProjects != null) {
    914             for (IJavaProject referencedJavaProject : referencedJavaProjects) {
    915                 // only include output from non android referenced project
    916                 // (This is to handle the case of reference Android projects in the context of
    917                 // instrumentation projects that need to reference the projects to be tested).
    918                 if (referencedJavaProject.getProject().hasNature(
    919                         AdtConstants.NATURE_DEFAULT) == false) {
    920                     writeStandardProjectResources(builder, referencedJavaProject, wsRoot);
    921                 }
    922             }
    923         }
    924     }
    925 
    926     /**
    927      * Writes the standard resources of a {@link IJavaProject} into a {@link SignedJarBuilder}.
    928      * Standard resources are non java/aidl files placed in the java package folders.
    929      * @param jarBuilder the {@link ApkBuilder}.
    930      * @param javaProject the javaProject object.
    931      * @param wsRoot the {@link IWorkspaceRoot}.
    932      * @throws ApkCreationException if an error occurred
    933      * @throws SealedApkException if the APK is already sealed.
    934      * @throws DuplicateFileException if a file conflicts with another already added to the APK
    935      *                                   at the same location inside the APK archive.
    936      * @throws CoreException
    937      */
    938     private static void writeStandardProjectResources(IArchiveBuilder builder,
    939             IJavaProject javaProject, IWorkspaceRoot wsRoot)
    940             throws DuplicateFileException, ApkCreationException, SealedApkException, CoreException {
    941         // get the source pathes
    942         List<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject);
    943 
    944         // loop on them and then recursively go through the content looking for matching files.
    945         for (IPath sourcePath : sourceFolders) {
    946             IResource sourceResource = wsRoot.findMember(sourcePath);
    947             if (sourceResource != null && sourceResource.getType() == IResource.FOLDER) {
    948                 writeFolderResources(builder, javaProject, (IFolder) sourceResource);
    949             }
    950         }
    951     }
    952 
    953     private static void writeFolderResources(IArchiveBuilder builder,
    954             final IJavaProject javaProject, IFolder root) throws CoreException,
    955             ApkCreationException, SealedApkException, DuplicateFileException {
    956         final List<IPath> pathsToPackage = new ArrayList<IPath>();
    957         root.accept(new IResourceProxyVisitor() {
    958             public boolean visit(IResourceProxy proxy) throws CoreException {
    959                 if (proxy.getType() == IResource.FOLDER) {
    960                     // If this folder isn't wanted, don't traverse into it.
    961                     return ApkBuilder.checkFolderForPackaging(proxy.getName());
    962                 }
    963                 // If it's not a folder, it must be a file.  We won't see any other resource type.
    964                 if (!ApkBuilder.checkFileForPackaging(proxy.getName())) {
    965                     return true;
    966                 }
    967                 IResource res = proxy.requestResource();
    968                 if (!javaProject.isOnClasspath(res)) {
    969                     return true;
    970                 }
    971                 // Just record that we need to package this.  Packaging here throws
    972                 // inappropriate checked exceptions.
    973                 IPath location = res.getLocation();
    974                 pathsToPackage.add(location);
    975                 return true;
    976             }
    977         }, 0);
    978         IPath rootLocation = root.getLocation();
    979         for (IPath path : pathsToPackage) {
    980             IPath archivePath = path.makeRelativeTo(rootLocation);
    981             builder.addFile(path.toFile(), archivePath.toString());
    982         }
    983     }
    984 
    985     /**
    986      * Returns an array of external dependencies used the project. This can be paths to jar files
    987      * or to source folders.
    988      *
    989      * @param resMarker if non null, used to put Resource marker on problem files.
    990      * @return an array of OS-specific absolute file paths
    991      */
    992     private final String[] getExternalDependencies(ResourceMarker resMarker) {
    993         // get a java project from it
    994         IJavaProject javaProject = JavaCore.create(mProject);
    995 
    996         IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
    997 
    998         ArrayList<String> oslibraryList = new ArrayList<String>();
    999 
   1000         // we could use IJavaProject.getResolvedClasspath directly, but we actually
   1001         // want to see the containers themselves.
   1002         IClasspathEntry[] classpaths = javaProject.readRawClasspath();
   1003         if (classpaths != null) {
   1004             for (IClasspathEntry e : classpaths) {
   1005                 // if this is a classpath variable reference, we resolve it.
   1006                 if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
   1007                     e = JavaCore.getResolvedClasspathEntry(e);
   1008                 }
   1009 
   1010                 if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
   1011                     handleClasspathEntry(e, wsRoot, oslibraryList, resMarker);
   1012                 } else if (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
   1013                     // get the container
   1014                     try {
   1015                         IClasspathContainer container = JavaCore.getClasspathContainer(
   1016                                 e.getPath(), javaProject);
   1017                         // ignore the system and default_system types as they represent
   1018                         // libraries that are part of the runtime.
   1019                         if (container.getKind() == IClasspathContainer.K_APPLICATION) {
   1020                             IClasspathEntry[] entries = container.getClasspathEntries();
   1021                             for (IClasspathEntry entry : entries) {
   1022                                 handleClasspathEntry(entry, wsRoot, oslibraryList, resMarker);
   1023                             }
   1024                         }
   1025                     } catch (JavaModelException jme) {
   1026                         // can't resolve the container? ignore it.
   1027                         AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", e.getPath());
   1028                     }
   1029                 }
   1030             }
   1031         }
   1032 
   1033         return oslibraryList.toArray(new String[oslibraryList.size()]);
   1034     }
   1035 
   1036     private void handleClasspathEntry(IClasspathEntry e, IWorkspaceRoot wsRoot,
   1037             ArrayList<String> oslibraryList, ResourceMarker resMarker) {
   1038         // get the IPath
   1039         IPath path = e.getPath();
   1040 
   1041         IResource resource = wsRoot.findMember(path);
   1042         // case of a jar file (which could be relative to the workspace or a full path)
   1043         if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
   1044             if (resource != null && resource.exists() &&
   1045                     resource.getType() == IResource.FILE) {
   1046                 oslibraryList.add(resource.getLocation().toOSString());
   1047             } else {
   1048                 // if the jar path doesn't match a workspace resource,
   1049                 // then we get an OSString and check if this links to a valid file.
   1050                 String osFullPath = path.toOSString();
   1051 
   1052                 File f = new File(osFullPath);
   1053                 if (f.isFile()) {
   1054                     oslibraryList.add(osFullPath);
   1055                 } else {
   1056                     String message = String.format( Messages.Couldnt_Locate_s_Error,
   1057                             path);
   1058                     // always output to the console
   1059                     mOutStream.println(message);
   1060 
   1061                     // put a marker
   1062                     if (resMarker != null) {
   1063                         resMarker.setWarning(mProject, message);
   1064                     }
   1065                 }
   1066             }
   1067         } else {
   1068             // this can be the case for a class folder.
   1069             if (resource != null && resource.exists() &&
   1070                     resource.getType() == IResource.FOLDER) {
   1071                 oslibraryList.add(resource.getLocation().toOSString());
   1072             } else {
   1073                 // if the path doesn't match a workspace resource,
   1074                 // then we get an OSString and check if this links to a valid folder.
   1075                 String osFullPath = path.toOSString();
   1076 
   1077                 File f = new File(osFullPath);
   1078                 if (f.isDirectory()) {
   1079                     oslibraryList.add(osFullPath);
   1080                 }
   1081             }
   1082         }
   1083     }
   1084 
   1085     /**
   1086      * Returns the list of the output folders for the specified {@link IJavaProject} objects, if
   1087      * they are Android projects.
   1088      *
   1089      * @param referencedJavaProjects the java projects.
   1090      * @return a new list object containing the output folder paths.
   1091      * @throws CoreException
   1092      */
   1093     private List<String> getProjectJavaOutputs(List<IJavaProject> referencedJavaProjects)
   1094             throws CoreException {
   1095         ArrayList<String> list = new ArrayList<String>();
   1096 
   1097         IWorkspace ws = ResourcesPlugin.getWorkspace();
   1098         IWorkspaceRoot wsRoot = ws.getRoot();
   1099 
   1100         for (IJavaProject javaProject : referencedJavaProjects) {
   1101             // only include output from non android referenced project
   1102             // (This is to handle the case of reference Android projects in the context of
   1103             // instrumentation projects that need to reference the projects to be tested).
   1104             if (javaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT) == false) {
   1105                 // get the output folder
   1106                 IPath path = null;
   1107                 try {
   1108                     path = javaProject.getOutputLocation();
   1109                 } catch (JavaModelException e) {
   1110                     continue;
   1111                 }
   1112 
   1113                 IResource outputResource = wsRoot.findMember(path);
   1114                 if (outputResource != null && outputResource.getType() == IResource.FOLDER) {
   1115                     String outputOsPath = outputResource.getLocation().toOSString();
   1116 
   1117                     list.add(outputOsPath);
   1118                 }
   1119             }
   1120         }
   1121 
   1122         return list;
   1123     }
   1124 
   1125     /**
   1126      * Checks a {@link IFile} to make sure it should be packaged as standard resources.
   1127      * @param file the IFile representing the file.
   1128      * @return true if the file should be packaged as standard java resources.
   1129      */
   1130     public static boolean checkFileForPackaging(IFile file) {
   1131         String name = file.getName();
   1132 
   1133         String ext = file.getFileExtension();
   1134         return ApkBuilder.checkFileForPackaging(name, ext);
   1135     }
   1136 
   1137     /**
   1138      * Checks whether an {@link IFolder} and its content is valid for packaging into the .apk as
   1139      * standard Java resource.
   1140      * @param folder the {@link IFolder} to check.
   1141      */
   1142     public static boolean checkFolderForPackaging(IFolder folder) {
   1143         String name = folder.getName();
   1144         return ApkBuilder.checkFolderForPackaging(name);
   1145     }
   1146 
   1147     /**
   1148      * Returns a list of {@link IJavaProject} matching the provided {@link IProject} objects.
   1149      * @param projects the IProject objects.
   1150      * @return a new list object containing the IJavaProject object for the given IProject objects.
   1151      * @throws CoreException
   1152      */
   1153     public static List<IJavaProject> getJavaProjects(List<IProject> projects) throws CoreException {
   1154         ArrayList<IJavaProject> list = new ArrayList<IJavaProject>();
   1155 
   1156         for (IProject p : projects) {
   1157             if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) {
   1158 
   1159                 list.add(JavaCore.create(p));
   1160             }
   1161         }
   1162 
   1163         return list;
   1164     }
   1165 
   1166     /**
   1167      * Get the stderr output of a process and return when the process is done.
   1168      * @param process The process to get the ouput from
   1169      * @param results The array to store the stderr output
   1170      * @return the process return code.
   1171      * @throws InterruptedException
   1172      */
   1173     public final static int grabProcessOutput(final IProject project, final Process process,
   1174             final ArrayList<String> results)
   1175             throws InterruptedException {
   1176         // Due to the limited buffer size on windows for the standard io (stderr, stdout), we
   1177         // *need* to read both stdout and stderr all the time. If we don't and a process output
   1178         // a large amount, this could deadlock the process.
   1179 
   1180         // read the lines as they come. if null is returned, it's
   1181         // because the process finished
   1182         new Thread("") { //$NON-NLS-1$
   1183             @Override
   1184             public void run() {
   1185                 // create a buffer to read the stderr output
   1186                 InputStreamReader is = new InputStreamReader(process.getErrorStream());
   1187                 BufferedReader errReader = new BufferedReader(is);
   1188 
   1189                 try {
   1190                     while (true) {
   1191                         String line = errReader.readLine();
   1192                         if (line != null) {
   1193                             results.add(line);
   1194                         } else {
   1195                             break;
   1196                         }
   1197                     }
   1198                 } catch (IOException e) {
   1199                     // do nothing.
   1200                 }
   1201             }
   1202         }.start();
   1203 
   1204         new Thread("") { //$NON-NLS-1$
   1205             @Override
   1206             public void run() {
   1207                 InputStreamReader is = new InputStreamReader(process.getInputStream());
   1208                 BufferedReader outReader = new BufferedReader(is);
   1209 
   1210                 try {
   1211                     while (true) {
   1212                         String line = outReader.readLine();
   1213                         if (line != null) {
   1214                             // If benchmarking always print the lines that
   1215                             // correspond to benchmarking info returned by ADT
   1216                             if (BENCHMARK_FLAG && line.startsWith("BENCHMARK:")) {    //$NON-NLS-1$
   1217                                 AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS,
   1218                                         project, line);
   1219                             } else {
   1220                                 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE,
   1221                                         project, line);
   1222                             }
   1223                         } else {
   1224                             break;
   1225                         }
   1226                     }
   1227                 } catch (IOException e) {
   1228                     // do nothing.
   1229                 }
   1230             }
   1231 
   1232         }.start();
   1233 
   1234         // get the return code from the process
   1235         return process.waitFor();
   1236     }
   1237 }
   1238