• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.ant;
18 
19 import com.android.io.FolderWrapper;
20 import com.android.sdklib.SdkConstants;
21 import com.android.sdklib.build.JarListSanitizer;
22 import com.android.sdklib.build.JarListSanitizer.DifferentLibException;
23 import com.android.sdklib.build.JarListSanitizer.Sha1Exception;
24 import com.android.sdklib.internal.project.IPropertySource;
25 import com.android.sdklib.internal.project.ProjectProperties;
26 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
27 
28 import org.apache.tools.ant.BuildException;
29 
30 import java.io.File;
31 import java.io.FilenameFilter;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Locale;
36 
37 /**
38  * Helper class to manage dependency for projects.
39  *
40  */
41 public class DependencyHelper {
42 
43     private final boolean mVerbose;
44     private final File mProjectFolder;
45     private final IPropertySource mProperties;
46     private final List<File> mLibraries = new ArrayList<File>();
47 
48     /**
49      * A Library Processor. Used in {@link DependencyHelper#processLibraries(LibraryProcessor)}
50      *
51      */
52     protected interface LibraryProcessor {
processLibrary(String libRootPath)53         void processLibrary(String libRootPath);
54     }
55 
56     /**
57      * Basic implementation of {@link LibraryProcessor} that builds a list of sanitized list
58      * of 3rd party jar files from all the Library Projects.
59      */
60     public static class LibraryProcessorFor3rdPartyJars implements LibraryProcessor {
61 
62         private final List<File> mJars = new ArrayList<File>();
63 
64         private final FilenameFilter mFilter = new FilenameFilter() {
65             @Override
66             public boolean accept(File dir, String name) {
67                 return name.toLowerCase(Locale.US).endsWith(".jar");
68             }
69         };
70 
getJars()71         public List<File> getJars() {
72             return mJars;
73         }
74 
getFilter()75         public FilenameFilter getFilter() {
76             return mFilter;
77         }
78 
79         @Override
processLibrary(String libRootPath)80         public void processLibrary(String libRootPath) {
81             // get the library output
82             // FIXME: support renamed folder.
83             mJars.add(new File(libRootPath + "/" + SdkConstants.FD_OUTPUT +
84                     "/" + SdkConstants.FN_CLASSES_JAR));
85 
86             // Get the 3rd party jar files.
87             // FIXME: support renamed folder.
88             File libsFolder = new File(libRootPath, SdkConstants.FD_NATIVE_LIBS);
89             File[] jarFiles = libsFolder.listFiles(mFilter);
90             if (jarFiles != null) {
91                 for (File jarFile : jarFiles) {
92                     mJars.add(jarFile);
93                 }
94             }
95         }
96     }
97 
sanitizePaths(File projectFolder, IPropertySource properties, List<File> paths)98     public static List<File> sanitizePaths(File projectFolder, IPropertySource properties,
99             List<File> paths) {
100         // first get the non-files.
101         List<File> results = new ArrayList<File>();
102         for (int i = 0 ; i < paths.size() ;) {
103             File f = paths.get(i);
104             // TEMP WORKAROUND: ignore classes.jar as all the output of libraries are
105             // called the same (in Ant) but are not actually the same jar file.
106             // TODO: Be aware of library output vs. regular jar dependency.
107             if (f.isFile() && f.getName().equals(SdkConstants.FN_CLASSES_JAR) == false) {
108                 i++;
109             } else {
110                 results.add(f);
111                 paths.remove(i);
112             }
113         }
114 
115 
116         File outputFile = new File(projectFolder, getOutDir(properties));
117         JarListSanitizer sanitizer = new JarListSanitizer(outputFile);
118 
119         try {
120             results.addAll(sanitizer.sanitize(paths));
121         } catch (DifferentLibException e) {
122             String[] details = e.getDetails();
123             for (String s : details) {
124                 System.err.println(s);
125             }
126             throw new BuildException(e.getMessage(), e);
127         } catch (Sha1Exception e) {
128             throw new BuildException(
129                     "Failed to compute sha1 for " + e.getJarFile().getAbsolutePath(), e);
130         }
131 
132         return results;
133     }
134 
135     /**
136      *
137      * @param projectFolder the project root folder.
138      */
DependencyHelper(File projectFolder, boolean verbose)139     public DependencyHelper(File projectFolder, boolean verbose) {
140         mProjectFolder = projectFolder;
141         mVerbose = verbose;
142 
143         ProjectProperties properties = ProjectProperties.load(projectFolder.getAbsolutePath(),
144                 PropertyType.ANT);
145 
146         if (properties == null) {
147             properties = ProjectProperties.load(projectFolder.getAbsolutePath(),
148                     PropertyType.PROJECT);
149         } else {
150             properties.makeWorkingCopy().merge(PropertyType.PROJECT);
151         }
152 
153         mProperties = properties;
154 
155         init(projectFolder);
156     }
157 
158     /**
159      *
160      * @param projectFolder the project root folder.
161      * @param source an {@link IPropertySource} that can provide the project properties values.
162      */
DependencyHelper(File projectFolder, IPropertySource properties, boolean verbose)163     public DependencyHelper(File projectFolder, IPropertySource properties, boolean verbose) {
164         mProjectFolder = projectFolder;
165         mProperties = properties;
166         mVerbose = verbose;
167 
168         init(projectFolder);
169     }
170 
init(File projectFolder)171     private void init(File projectFolder) {
172         // get the top level list of library dependencies.
173         List<File> topLevelLibraries = getDirectDependencies(projectFolder, mProperties);
174 
175         // process the libraries in case they depend on other libraries.
176         resolveFullLibraryDependencies(topLevelLibraries, mLibraries);
177     }
178 
getLibraries()179     public List<File> getLibraries() {
180         return mLibraries;
181     }
182 
getLibraryCount()183     public int getLibraryCount() {
184         return mLibraries.size();
185     }
186 
getProperty(String name)187     public String getProperty(String name) {
188         return mProperties.getProperty(name);
189     }
190 
processLibraries(LibraryProcessor processor)191     public void processLibraries(LibraryProcessor processor) {
192         // use that same order to process the libraries.
193         for (File library : mLibraries) {
194             // get the root path.
195             String libRootPath = library.getAbsolutePath();
196             if (mVerbose) {
197                 System.out.println(libRootPath);
198             }
199 
200             if (processor != null) {
201                 processor.processLibrary(libRootPath);
202             }
203         }
204     }
205 
sanitizePaths(List<File> paths)206     public List<File> sanitizePaths(List<File> paths) {
207         return sanitizePaths(mProjectFolder, mProperties, paths);
208     }
209 
getOutDir()210     public String getOutDir() {
211         return getOutDir(mProperties);
212     }
213 
214 
215     /**
216      * Returns the top level library dependencies of a given <var>source</var> representing a
217      * project properties.
218      * @param baseFolder the base folder of the project (to resolve relative paths)
219      * @param properties a source of project properties.
220      */
getDirectDependencies(File baseFolder, IPropertySource properties)221     private List<File> getDirectDependencies(File baseFolder, IPropertySource properties) {
222         ArrayList<File> libraries = new ArrayList<File>();
223 
224         // first build the list. they are ordered highest priority first.
225         int index = 1;
226         while (true) {
227             String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++);
228             String rootPath = properties.getProperty(propName);
229 
230             if (rootPath == null) {
231                 break;
232             }
233 
234             try {
235                 File library = new File(baseFolder, rootPath).getCanonicalFile();
236 
237                 // check for validity
238                 File projectProp = new File(library, PropertyType.PROJECT.getFilename());
239                 if (projectProp.isFile() == false) {
240                     // error!
241                     throw new BuildException(String.format(
242                             "%1$s resolve to a path with no %2$s file for project %3$s", rootPath,
243                             PropertyType.PROJECT.getFilename(), baseFolder.getAbsolutePath()));
244                 }
245 
246                 if (libraries.contains(library) == false) {
247                     if (mVerbose) {
248                         System.out.println(String.format("%1$s: %2$s => %3$s",
249                                 baseFolder.getAbsolutePath(), rootPath, library.getAbsolutePath()));
250                     }
251 
252                     libraries.add(library);
253                 }
254             } catch (IOException e) {
255                 throw new BuildException("Failed to resolve library path: " + rootPath, e);
256             }
257         }
258 
259         return libraries;
260     }
261 
262     /**
263      * Resolves a given list of libraries, finds out if they depend on other libraries, and
264      * returns a full list of all the direct and indirect dependencies in the proper order (first
265      * is higher priority when calling aapt).
266      * @param inLibraries the libraries to resolve
267      * @param outLibraries where to store all the libraries.
268      */
resolveFullLibraryDependencies(List<File> inLibraries, List<File> outLibraries)269     private void resolveFullLibraryDependencies(List<File> inLibraries, List<File> outLibraries) {
270         // loop in the inverse order to resolve dependencies on the libraries, so that if a library
271         // is required by two higher level libraries it can be inserted in the correct place
272         for (int i = inLibraries.size() - 1  ; i >= 0 ; i--) {
273             File library = inLibraries.get(i);
274 
275             // get the default.property file for it
276             final ProjectProperties projectProp = ProjectProperties.load(
277                     new FolderWrapper(library), PropertyType.PROJECT);
278 
279             // get its libraries
280             List<File> dependencies = getDirectDependencies(library, projectProp);
281 
282             // resolve the dependencies for those libraries
283             resolveFullLibraryDependencies(dependencies, outLibraries);
284 
285             // and add the current one (if needed) in front (higher priority)
286             if (outLibraries.contains(library) == false) {
287                 outLibraries.add(0, library);
288             }
289         }
290     }
291 
getOutDir(IPropertySource properties)292     private static String getOutDir(IPropertySource properties) {
293         String bin = properties.getProperty("out.dir");
294         if (bin == null) {
295             return SdkConstants.FD_OUTPUT;
296         }
297 
298         return bin;
299     }
300 
301 }
302