• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.AdtPlugin;
20 import com.android.ide.eclipse.adt.AndroidConstants;
21 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
22 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
23 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
24 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
25 import com.android.ide.eclipse.adt.internal.sdk.DexWrapper;
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.build.ApkBuilder;
31 import com.android.sdklib.build.ApkCreationException;
32 import com.android.sdklib.build.DuplicateFileException;
33 import com.android.sdklib.build.SealedApkException;
34 import com.android.sdklib.build.ApkBuilder.JarStatus;
35 import com.android.sdklib.internal.build.DebugKeyProvider;
36 import com.android.sdklib.internal.build.SignedJarBuilder;
37 import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException;
38 
39 import org.eclipse.core.resources.IFile;
40 import org.eclipse.core.resources.IFolder;
41 import org.eclipse.core.resources.IMarker;
42 import org.eclipse.core.resources.IProject;
43 import org.eclipse.core.resources.IResource;
44 import org.eclipse.core.resources.IWorkspace;
45 import org.eclipse.core.resources.IWorkspaceRoot;
46 import org.eclipse.core.resources.ResourcesPlugin;
47 import org.eclipse.core.runtime.CoreException;
48 import org.eclipse.core.runtime.IPath;
49 import org.eclipse.core.runtime.IStatus;
50 import org.eclipse.core.runtime.Status;
51 import org.eclipse.jdt.core.IClasspathEntry;
52 import org.eclipse.jdt.core.IJavaProject;
53 import org.eclipse.jdt.core.JavaCore;
54 import org.eclipse.jdt.core.JavaModelException;
55 import org.eclipse.jface.preference.IPreferenceStore;
56 
57 import java.io.File;
58 import java.io.IOException;
59 import java.io.PrintStream;
60 import java.util.ArrayList;
61 import java.util.List;
62 
63 /**
64  * Helper with methods for the last 3 steps of the generation of an APK.
65  *
66  * {@link #packageResources(IFile, IProject[], String, int, String, String)} packages the
67  * application resources using aapt into a zip file that is ready to be integrated into the apk.
68  *
69  * {@link #executeDx(IJavaProject, String, String, IJavaProject[])} will convert the Java byte
70  * code into the Dalvik bytecode.
71  *
72  * {@link #finalPackage(String, String, String, boolean, IJavaProject, IProject[], IJavaProject[], String, boolean)}
73  * will make the apk from all the previous components.
74  *
75  */
76 public class PostCompilerHelper {
77 
78     private final IProject mProject;
79     private final PrintStream mOutStream;
80     private final PrintStream mErrStream;
81 
PostCompilerHelper(IProject project, PrintStream outStream, PrintStream errStream)82     public PostCompilerHelper(IProject project, PrintStream outStream, PrintStream errStream) {
83         mProject = project;
84         mOutStream = outStream;
85         mErrStream = errStream;
86     }
87 
88     /**
89      * Packages the resources of the projet into a .ap_ file.
90      * @param manifestFile the manifest of the project.
91      * @param libProjects the list of library projects that this project depends on.
92      * @param resFilter an optional resource filter to be used with the -c option of aapt. If null
93      * no filters are used.
94      * @param versionCode an optional versionCode to be inserted in the manifest during packaging.
95      * If the value is <=0, no values are inserted.
96      * @param outputFolder where to write the resource ap_ file.
97      * @param outputFilename the name of the resource ap_ file.
98      * @return true if success.
99      */
packageResources(IFile manifestFile, IProject[] libProjects, String resFilter, int versionCode, String outputFolder, String outputFilename)100     public boolean packageResources(IFile manifestFile, IProject[] libProjects, String resFilter,
101             int versionCode, String outputFolder, String outputFilename) {
102         // need to figure out some path before we can execute aapt;
103 
104         // get the resource folder
105         IFolder resFolder = mProject.getFolder(AndroidConstants.WS_RESOURCES);
106 
107         // and the assets folder
108         IFolder assetsFolder = mProject.getFolder(AndroidConstants.WS_ASSETS);
109 
110         // we need to make sure this one exists.
111         if (assetsFolder.exists() == false) {
112             assetsFolder = null;
113         }
114 
115         IPath resLocation = resFolder.getLocation();
116         IPath manifestLocation = manifestFile.getLocation();
117 
118         if (resLocation != null && manifestLocation != null) {
119             // list of res folder (main project + maybe libraries)
120             ArrayList<String> osResPaths = new ArrayList<String>();
121             osResPaths.add(resLocation.toOSString()); //main project
122 
123             // libraries?
124             if (libProjects != null) {
125                 for (IProject lib : libProjects) {
126                     IFolder libResFolder = lib.getFolder(SdkConstants.FD_RES);
127                     if (libResFolder.exists()) {
128                         osResPaths.add(libResFolder.getLocation().toOSString());
129                     }
130                 }
131             }
132 
133             String osManifestPath = manifestLocation.toOSString();
134 
135             String osAssetsPath = null;
136             if (assetsFolder != null) {
137                 osAssetsPath = assetsFolder.getLocation().toOSString();
138             }
139 
140             // build the default resource package
141             if (executeAapt(osManifestPath, osResPaths, osAssetsPath,
142                     outputFolder + File.separator + outputFilename, resFilter,
143                     versionCode) == false) {
144                 // aapt failed. Whatever files that needed to be marked
145                 // have already been marked. We just return.
146                 return false;
147             }
148         }
149 
150         return true;
151     }
152 
153     /**
154      * Makes the final package. Package the dex files, the temporary resource file into the final
155      * package file.
156      * @param intermediateApk The path to the temporary resource file.
157      * @param dex The path to the dex file.
158      * @param output The path to the final package file to create.
159      * @param debugSign whether the apk must be signed with the debug key.
160      * @param javaProject the java project being compiled
161      * @param libProjects an optional list of library projects (can be null)
162      * @param referencedJavaProjects referenced projects.
163      * @param abiFilter an optional filter. If not null, then only the matching ABI is included in
164      * the final archive
165      * @param debuggable whether the project manifest has debuggable==true. If true, any gdbserver
166      * executables will be packaged with the native libraries.
167      * @return true if success, false otherwise.
168      */
finalPackage(String intermediateApk, String dex, String output, boolean debugSign, final IJavaProject javaProject, IProject[] libProjects, IJavaProject[] referencedJavaProjects, String abiFilter, boolean debuggable)169     public boolean finalPackage(String intermediateApk, String dex, String output,
170             boolean debugSign, final IJavaProject javaProject, IProject[] libProjects,
171             IJavaProject[] referencedJavaProjects, String abiFilter, boolean debuggable) {
172 
173         IProject project = javaProject.getProject();
174 
175         String keystoreOsPath = null;
176         if (debugSign) {
177             IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
178             keystoreOsPath = store.getString(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE);
179             if (keystoreOsPath == null || new File(keystoreOsPath).isFile() == false) {
180                 try {
181                     keystoreOsPath = DebugKeyProvider.getDefaultKeyStoreOsPath();
182                     AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, mProject,
183                             Messages.ApkBuilder_Using_Default_Key);
184                 } catch (KeytoolException e) {
185                     String eMessage = e.getMessage();
186 
187                     // mark the project with the standard message
188                     String msg = String.format(Messages.Final_Archive_Error_s, eMessage);
189                     BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING, msg,
190                             IMarker.SEVERITY_ERROR);
191 
192                     // output more info in the console
193                     AdtPlugin.printErrorToConsole(mProject,
194                             msg,
195                             String.format(Messages.ApkBuilder_JAVA_HOME_is_s, e.getJavaHome()),
196                             Messages.ApkBuilder_Update_or_Execute_manually_s,
197                             e.getCommandLine());
198 
199                     return false;
200                 } catch (AndroidLocationException e) {
201                     String eMessage = e.getMessage();
202 
203                     // mark the project with the standard message
204                     String msg = String.format(Messages.Final_Archive_Error_s, eMessage);
205                     BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING, msg,
206                             IMarker.SEVERITY_ERROR);
207 
208                     return false;
209                 }
210             } else {
211                 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, mProject,
212                         String.format(Messages.ApkBuilder_Using_s_To_Sign, keystoreOsPath));
213             }
214         }
215 
216 
217         try {
218             ApkBuilder apkBuilder = new ApkBuilder(output, intermediateApk, dex, keystoreOsPath,
219                     AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE ?
220                             AdtPlugin.getOutPrintStream(project, null): null);
221             apkBuilder.setDebugMode(debuggable);
222 
223             // Now we write the standard resources from the project and the referenced projects.
224             writeStandardResources(apkBuilder, javaProject, referencedJavaProjects);
225 
226             // Now we write the standard resources from the external jars
227             for (String libraryOsPath : getExternalJars()) {
228                 JarStatus status = apkBuilder.addResourcesFromJar(new File(libraryOsPath));
229 
230                 // check if we found native libraries in the external library. This
231                 // constitutes an error or warning depending on if they are in lib/
232                 if (status.getNativeLibs().size() > 0) {
233                     String libName = new File(libraryOsPath).getName();
234                     String msg = String.format(
235                             "Native libraries detected in '%1$s'. See console for more information.",
236                             libName);
237 
238                     BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING,
239                             msg,
240                             status.hasNativeLibsConflicts() ||
241                                     AdtPrefs.getPrefs().getBuildForceErrorOnNativeLibInJar() ?
242                                     IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING);
243 
244                     ArrayList<String> consoleMsgs = new ArrayList<String>();
245                     consoleMsgs.add(String.format(
246                             "The library '%1$s' contains native libraries that will not run on the device.",
247                             libName));
248                     if (status.hasNativeLibsConflicts()) {
249                         consoleMsgs.add("Additionally some of those libraries will interfer with the installation of the application because of their location in lib/");
250                         consoleMsgs.add("lib/ is reserved for NDK libraries.");
251                     }
252                     consoleMsgs.add("The following libraries were found:");
253                     for (String lib : status.getNativeLibs()) {
254                         consoleMsgs.add(" - " + lib);
255                     }
256                     AdtPlugin.printErrorToConsole(mProject,
257                             consoleMsgs.toArray());
258 
259                     return false;
260                 }
261             }
262 
263             // now write the native libraries.
264             // First look if the lib folder is there.
265             IResource libFolder = mProject.findMember(SdkConstants.FD_NATIVE_LIBS);
266             if (libFolder != null && libFolder.exists() &&
267                     libFolder.getType() == IResource.FOLDER) {
268                 // get a File for the folder.
269                 apkBuilder.addNativeLibraries(libFolder.getLocation().toFile(), abiFilter);
270             }
271 
272             // write the native libraries for the library projects.
273             if (libProjects != null) {
274                 for (IProject lib : libProjects) {
275                     libFolder = lib.findMember(SdkConstants.FD_NATIVE_LIBS);
276                     if (libFolder != null && libFolder.exists() &&
277                             libFolder.getType() == IResource.FOLDER) {
278                         apkBuilder.addNativeLibraries(libFolder.getLocation().toFile(), abiFilter);
279                     }
280                 }
281             }
282 
283             // seal the APK.
284             apkBuilder.sealApk();
285             return true;
286         } catch (CoreException e) {
287             // mark project and return
288             String msg = String.format(Messages.Final_Archive_Error_s, e.getMessage());
289             AdtPlugin.printErrorToConsole(mProject, msg);
290             BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING, msg,
291                     IMarker.SEVERITY_ERROR);
292         } catch (ApkCreationException e) {
293             // mark project and return
294             String msg = String.format(Messages.Final_Archive_Error_s, e.getMessage());
295             AdtPlugin.printErrorToConsole(mProject, msg);
296             BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING, msg,
297                     IMarker.SEVERITY_ERROR);
298         } catch (DuplicateFileException e) {
299             String msg1 = String.format(
300                     "Found duplicate file for APK: %1$s\nOrigin 1: %2$s\nOrigin 2: %3$s",
301                     e.getArchivePath(), e.getFile1(), e.getFile2());
302             String msg2 = String.format(Messages.Final_Archive_Error_s, msg1);
303             AdtPlugin.printErrorToConsole(mProject, msg2);
304             BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING, msg2,
305                     IMarker.SEVERITY_ERROR);
306         } catch (SealedApkException e) {
307             // this won't happen as we control when the apk is sealed.
308         } catch (Exception e) {
309             // try to catch other exception to actually display an error. This will be useful
310             // if we get an NPE or something so that we can at least notify the user that something
311             // went wrong (otherwise the build appears to succeed but the zip archive is not closed
312             // and therefore invalid.
313             String msg = e.getMessage();
314             if (msg == null) {
315                 msg = e.getClass().getCanonicalName();
316             }
317 
318             msg = String.format("Unknown error: %1$s", msg);
319             AdtPlugin.printErrorToConsole(mProject, msg);
320             BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING, msg,
321                     IMarker.SEVERITY_ERROR);
322         }
323 
324         return false;
325     }
326 
327     /**
328      * Execute the Dx tool for dalvik code conversion.
329      * @param javaProject The java project
330      * @param osBinPath the path to the output folder of the project
331      * @param osOutFilePath the path of the dex file to create.
332      * @param referencedJavaProjects the list of referenced projects for this project.
333      *
334      * @throws CoreException
335      */
executeDx(IJavaProject javaProject, String osBinPath, String osOutFilePath, IJavaProject[] referencedJavaProjects)336     boolean executeDx(IJavaProject javaProject, String osBinPath, String osOutFilePath,
337             IJavaProject[] referencedJavaProjects) throws CoreException {
338 
339         IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
340         AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
341         if (targetData == null) {
342             throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
343                     Messages.ApkBuilder_UnableBuild_Dex_Not_loaded));
344         }
345 
346         // get the dex wrapper
347         DexWrapper wrapper = targetData.getDexWrapper();
348 
349         if (wrapper == null) {
350             throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
351                     Messages.ApkBuilder_UnableBuild_Dex_Not_loaded));
352         }
353 
354         try {
355             // get the list of libraries to include with the source code
356             String[] libraries = getExternalJars();
357 
358             // get the list of referenced projects output to add
359             String[] projectOutputs = getProjectOutputs(referencedJavaProjects);
360 
361             String[] fileNames = new String[1 + projectOutputs.length + libraries.length];
362 
363             // first this project output
364             fileNames[0] = osBinPath;
365 
366             // then other project output
367             System.arraycopy(projectOutputs, 0, fileNames, 1, projectOutputs.length);
368 
369             // then external jars.
370             System.arraycopy(libraries, 0, fileNames, 1 + projectOutputs.length, libraries.length);
371 
372             int res = wrapper.run(osOutFilePath, fileNames,
373                     AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE,
374                     mOutStream, mErrStream);
375 
376             if (res != 0) {
377                 // output error message and marker the project.
378                 String message = String.format(Messages.Dalvik_Error_d,
379                         res);
380                 AdtPlugin.printErrorToConsole(mProject, message);
381                 BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING,
382                         message, IMarker.SEVERITY_ERROR);
383                 return false;
384             }
385         } catch (Throwable ex) {
386             String message = ex.getMessage();
387             if (message == null) {
388                 message = ex.getClass().getCanonicalName();
389             }
390             message = String.format(Messages.Dalvik_Error_s, message);
391             AdtPlugin.printErrorToConsole(mProject, message);
392             BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING,
393                     message, IMarker.SEVERITY_ERROR);
394             if ((ex instanceof NoClassDefFoundError)
395                     || (ex instanceof NoSuchMethodError)) {
396                 AdtPlugin.printErrorToConsole(mProject, Messages.Incompatible_VM_Warning,
397                         Messages.Requires_1_5_Error);
398             }
399             return false;
400         }
401 
402         return true;
403     }
404 
405 
406     /**
407      * Executes aapt. If any error happen, files or the project will be marked.
408      * @param osManifestPath The path to the manifest file
409      * @param osResPath The path to the res folder
410      * @param osAssetsPath The path to the assets folder. This can be null.
411      * @param osOutFilePath The path to the temporary resource file to create.
412      * @param configFilter The configuration filter for the resources to include
413      * (used with -c option, for example "port,en,fr" to include portrait, English and French
414      * resources.)
415      * @param versionCode optional version code to insert in the manifest during packaging. If <=0
416      * then no value is inserted
417      * @return true if success, false otherwise.
418      */
executeAapt(String osManifestPath, List<String> osResPaths, String osAssetsPath, String osOutFilePath, String configFilter, int versionCode)419     private boolean executeAapt(String osManifestPath,
420             List<String> osResPaths, String osAssetsPath, String osOutFilePath,
421             String configFilter, int versionCode) {
422         IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
423 
424         // Create the command line.
425         ArrayList<String> commandArray = new ArrayList<String>();
426         commandArray.add(target.getPath(IAndroidTarget.AAPT));
427         commandArray.add("package"); //$NON-NLS-1$
428         commandArray.add("-f");//$NON-NLS-1$
429         if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) {
430             commandArray.add("-v"); //$NON-NLS-1$
431         }
432 
433         // if more than one res, this means there's a library (or more) and we need
434         // to activate the auto-add-overlay
435         if (osResPaths.size() > 1) {
436             commandArray.add("--auto-add-overlay"); //$NON-NLS-1$
437         }
438 
439         if (versionCode > 0) {
440             commandArray.add("--version-code"); //$NON-NLS-1$
441             commandArray.add(Integer.toString(versionCode));
442         }
443 
444         if (configFilter != null) {
445             commandArray.add("-c"); //$NON-NLS-1$
446             commandArray.add(configFilter);
447         }
448 
449         commandArray.add("-M"); //$NON-NLS-1$
450         commandArray.add(osManifestPath);
451 
452         for (String path : osResPaths) {
453             commandArray.add("-S"); //$NON-NLS-1$
454             commandArray.add(path);
455         }
456 
457         if (osAssetsPath != null) {
458             commandArray.add("-A"); //$NON-NLS-1$
459             commandArray.add(osAssetsPath);
460         }
461 
462         commandArray.add("-I"); //$NON-NLS-1$
463         commandArray.add(target.getPath(IAndroidTarget.ANDROID_JAR));
464 
465         commandArray.add("-F"); //$NON-NLS-1$
466         commandArray.add(osOutFilePath);
467 
468         String command[] = commandArray.toArray(
469                 new String[commandArray.size()]);
470 
471         if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) {
472             StringBuilder sb = new StringBuilder();
473             for (String c : command) {
474                 sb.append(c);
475                 sb.append(' ');
476             }
477             AdtPlugin.printToConsole(mProject, sb.toString());
478         }
479 
480         // launch
481         int execError = 1;
482         try {
483             // launch the command line process
484             Process process = Runtime.getRuntime().exec(command);
485 
486             // list to store each line of stderr
487             ArrayList<String> results = new ArrayList<String>();
488 
489             // get the output and return code from the process
490             execError = BaseBuilder.grabProcessOutput(mProject, process, results);
491 
492             // attempt to parse the error output
493             boolean parsingError = AaptParser.parseOutput(results, mProject);
494 
495             // if we couldn't parse the output we display it in the console.
496             if (parsingError) {
497                 if (execError != 0) {
498                     AdtPlugin.printErrorToConsole(mProject, results.toArray());
499                 } else {
500                     AdtPlugin.printBuildToConsole(BuildVerbosity.ALWAYS, mProject,
501                             results.toArray());
502                 }
503             }
504 
505             // We need to abort if the exec failed.
506             if (execError != 0) {
507                 // if the exec failed, and we couldn't parse the error output (and therefore
508                 // not all files that should have been marked, were marked), we put a generic
509                 // marker on the project and abort.
510                 if (parsingError) {
511                     BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING,
512                             Messages.Unparsed_AAPT_Errors,
513                             IMarker.SEVERITY_ERROR);
514                 }
515 
516                 // abort if exec failed.
517                 return false;
518             }
519         } catch (IOException e1) {
520             String msg = String.format(Messages.AAPT_Exec_Error, command[0]);
521             BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING, msg,
522                     IMarker.SEVERITY_ERROR);
523             return false;
524         } catch (InterruptedException e) {
525             String msg = String.format(Messages.AAPT_Exec_Error, command[0]);
526             BaseProjectHelper.markResource(mProject, AndroidConstants.MARKER_PACKAGING, msg,
527                     IMarker.SEVERITY_ERROR);
528             return false;
529         }
530 
531         return true;
532     }
533 
534 
535     /**
536      * Writes the standard resources of a project and its referenced projects
537      * into a {@link SignedJarBuilder}.
538      * Standard resources are non java/aidl files placed in the java package folders.
539      * @param apkBuilder the {@link ApkBuilder}.
540      * @param javaProject the javaProject object.
541      * @param referencedJavaProjects the java projects that this project references.
542      * @throws ApkCreationException if an error occurred
543      * @throws SealedApkException if the APK is already sealed.
544      * @throws DuplicateFileException if a file conflicts with another already added to the APK
545      *                                   at the same location inside the APK archive.
546      * @throws CoreException
547      */
writeStandardResources(ApkBuilder apkBuilder, IJavaProject javaProject, IJavaProject[] referencedJavaProjects)548     private void writeStandardResources(ApkBuilder apkBuilder, IJavaProject javaProject,
549             IJavaProject[] referencedJavaProjects)
550             throws DuplicateFileException, ApkCreationException, SealedApkException,
551             CoreException  {
552         IWorkspace ws = ResourcesPlugin.getWorkspace();
553         IWorkspaceRoot wsRoot = ws.getRoot();
554 
555         // create a list of path already put into the archive, in order to detect conflict
556         ArrayList<String> list = new ArrayList<String>();
557 
558         writeStandardProjectResources(apkBuilder, javaProject, wsRoot, list);
559 
560         for (IJavaProject referencedJavaProject : referencedJavaProjects) {
561             // only include output from non android referenced project
562             // (This is to handle the case of reference Android projects in the context of
563             // instrumentation projects that need to reference the projects to be tested).
564             if (referencedJavaProject.getProject().hasNature(
565                     AndroidConstants.NATURE_DEFAULT) == false) {
566                 writeStandardProjectResources(apkBuilder, referencedJavaProject, wsRoot, list);
567             }
568         }
569     }
570 
571     /**
572      * Writes the standard resources of a {@link IJavaProject} into a {@link SignedJarBuilder}.
573      * Standard resources are non java/aidl files placed in the java package folders.
574      * @param jarBuilder the {@link ApkBuilder}.
575      * @param javaProject the javaProject object.
576      * @param wsRoot the {@link IWorkspaceRoot}.
577      * @param list a list of files already added to the archive, to detect conflicts.
578      * @throws ApkCreationException if an error occurred
579      * @throws SealedApkException if the APK is already sealed.
580      * @throws DuplicateFileException if a file conflicts with another already added to the APK
581      *                                   at the same location inside the APK archive.
582      */
writeStandardProjectResources(ApkBuilder apkBuilder, IJavaProject javaProject, IWorkspaceRoot wsRoot, ArrayList<String> list)583     private void writeStandardProjectResources(ApkBuilder apkBuilder,
584             IJavaProject javaProject, IWorkspaceRoot wsRoot, ArrayList<String> list)
585             throws DuplicateFileException, ApkCreationException, SealedApkException {
586         // get the source pathes
587         ArrayList<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject);
588 
589         // loop on them and then recursively go through the content looking for matching files.
590         for (IPath sourcePath : sourceFolders) {
591             IResource sourceResource = wsRoot.findMember(sourcePath);
592             if (sourceResource != null && sourceResource.getType() == IResource.FOLDER) {
593                 // get a File from the IResource
594                 apkBuilder.addSourceFolder(sourceResource.getLocation().toFile());
595             }
596         }
597     }
598 
599     /**
600      * Returns an array of external jar files used by the project.
601      * @return an array of OS-specific absolute file paths
602      */
getExternalJars()603     private final String[] getExternalJars() {
604         // get a java project from it
605         IJavaProject javaProject = JavaCore.create(mProject);
606 
607         IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
608 
609         ArrayList<String> oslibraryList = new ArrayList<String>();
610         IClasspathEntry[] classpaths = javaProject.readRawClasspath();
611         if (classpaths != null) {
612             for (IClasspathEntry e : classpaths) {
613                 if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY ||
614                         e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
615                     // if this is a classpath variable reference, we resolve it.
616                     if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
617                         e = JavaCore.getResolvedClasspathEntry(e);
618                     }
619 
620                     // get the IPath
621                     IPath path = e.getPath();
622 
623                     // check the name ends with .jar
624                     if (AndroidConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
625                         IResource resource = wsRoot.findMember(path);
626                         if (resource != null && resource.exists() &&
627                                 resource.getType() == IResource.FILE) {
628                             oslibraryList.add(resource.getLocation().toOSString());
629                         } else {
630                             // if the jar path doesn't match a workspace resource,
631                             // then we get an OSString and check if this links to a valid file.
632                             String osFullPath = path.toOSString();
633 
634                             File f = new File(osFullPath);
635                             if (f.exists()) {
636                                 oslibraryList.add(osFullPath);
637                             } else {
638                                 String message = String.format( Messages.Couldnt_Locate_s_Error,
639                                         path);
640                                 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE,
641                                         mProject, message);
642 
643                                 // Also put a warning marker on the project
644                                 BaseProjectHelper.markResource(mProject,
645                                         AndroidConstants.MARKER_PACKAGING, message,
646                                         IMarker.SEVERITY_WARNING);
647                             }
648                         }
649                     }
650                 }
651             }
652         }
653 
654         return oslibraryList.toArray(new String[oslibraryList.size()]);
655     }
656 
657     /**
658      * Returns the list of the output folders for the specified {@link IJavaProject} objects, if
659      * they are Android projects.
660      *
661      * @param referencedJavaProjects the java projects.
662      * @return an array, always. Can be empty.
663      * @throws CoreException
664      */
getProjectOutputs(IJavaProject[] referencedJavaProjects)665     private String[] getProjectOutputs(IJavaProject[] referencedJavaProjects) throws CoreException {
666         ArrayList<String> list = new ArrayList<String>();
667 
668         IWorkspace ws = ResourcesPlugin.getWorkspace();
669         IWorkspaceRoot wsRoot = ws.getRoot();
670 
671         for (IJavaProject javaProject : referencedJavaProjects) {
672             // only include output from non android referenced project
673             // (This is to handle the case of reference Android projects in the context of
674             // instrumentation projects that need to reference the projects to be tested).
675             if (javaProject.getProject().hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
676                 // get the output folder
677                 IPath path = null;
678                 try {
679                     path = javaProject.getOutputLocation();
680                 } catch (JavaModelException e) {
681                     continue;
682                 }
683 
684                 IResource outputResource = wsRoot.findMember(path);
685                 if (outputResource != null && outputResource.getType() == IResource.FOLDER) {
686                     String outputOsPath = outputResource.getLocation().toOSString();
687 
688                     list.add(outputOsPath);
689                 }
690             }
691         }
692 
693         return list.toArray(new String[list.size()]);
694     }
695 
696     /**
697      * Checks a {@link IFile} to make sure it should be packaged as standard resources.
698      * @param file the IFile representing the file.
699      * @return true if the file should be packaged as standard java resources.
700      */
checkFileForPackaging(IFile file)701     static boolean checkFileForPackaging(IFile file) {
702         String name = file.getName();
703 
704         String ext = file.getFileExtension();
705         return ApkBuilder.checkFileForPackaging(name, ext);
706     }
707 
708     /**
709      * Checks whether an {@link IFolder} and its content is valid for packaging into the .apk as
710      * standard Java resource.
711      * @param folder the {@link IFolder} to check.
712      */
checkFolderForPackaging(IFolder folder)713     static boolean checkFolderForPackaging(IFolder folder) {
714         String name = folder.getName();
715         return ApkBuilder.checkFolderForPackaging(name);
716     }
717 
718     /**
719      * Returns an array of {@link IJavaProject} matching the provided {@link IProject} objects.
720      * @param projects the IProject objects.
721      * @return an array, always. Can be empty.
722      * @throws CoreException
723      */
getJavaProjects(IProject[] projects)724     public static IJavaProject[] getJavaProjects(IProject[] projects) throws CoreException {
725         ArrayList<IJavaProject> list = new ArrayList<IJavaProject>();
726 
727         for (IProject p : projects) {
728             if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) {
729 
730                 list.add(JavaCore.create(p));
731             }
732         }
733 
734         return list.toArray(new IJavaProject[list.size()]);
735     }
736 
737 
738 }
739