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