diff options
Diffstat (limited to 'anttasks/src/com/android/ant/DependencyHelper.java')
-rw-r--r-- | anttasks/src/com/android/ant/DependencyHelper.java | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/anttasks/src/com/android/ant/DependencyHelper.java b/anttasks/src/com/android/ant/DependencyHelper.java new file mode 100644 index 0000000..c2a6694 --- /dev/null +++ b/anttasks/src/com/android/ant/DependencyHelper.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ant; + +import com.android.io.FolderWrapper; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.build.JarListSanitizer; +import com.android.sdklib.build.JarListSanitizer.DifferentLibException; +import com.android.sdklib.build.JarListSanitizer.Sha1Exception; +import com.android.sdklib.internal.project.IPropertySource; +import com.android.sdklib.internal.project.ProjectProperties; +import com.android.sdklib.internal.project.ProjectProperties.PropertyType; + +import org.apache.tools.ant.BuildException; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Helper class to manage dependency for projects. + * + */ +public class DependencyHelper { + + private final boolean mVerbose; + private final File mProjectFolder; + private final IPropertySource mProperties; + private final List<File> mLibraries = new ArrayList<File>(); + + /** + * A Library Processor. Used in {@link DependencyHelper#processLibraries(LibraryProcessor)} + * + */ + protected interface LibraryProcessor { + void processLibrary(String libRootPath); + } + + /** + * Basic implementation of {@link LibraryProcessor} that builds a list of sanitized list + * of 3rd party jar files from all the Library Projects. + */ + public static class LibraryProcessorFor3rdPartyJars implements LibraryProcessor { + + private final List<File> mJars = new ArrayList<File>(); + + private final FilenameFilter mFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase(Locale.US).endsWith(".jar"); + } + }; + + public List<File> getJars() { + return mJars; + } + + public FilenameFilter getFilter() { + return mFilter; + } + + @Override + public void processLibrary(String libRootPath) { + // get the library output + // FIXME: support renamed folder. + mJars.add(new File(libRootPath + "/" + SdkConstants.FD_OUTPUT + + "/" + SdkConstants.FN_CLASSES_JAR)); + + // Get the 3rd party jar files. + // FIXME: support renamed folder. + File libsFolder = new File(libRootPath, SdkConstants.FD_NATIVE_LIBS); + File[] jarFiles = libsFolder.listFiles(mFilter); + if (jarFiles != null) { + for (File jarFile : jarFiles) { + mJars.add(jarFile); + } + } + } + } + + public static List<File> sanitizePaths(File projectFolder, IPropertySource properties, + List<File> paths) { + // first get the non-files. + List<File> results = new ArrayList<File>(); + for (int i = 0 ; i < paths.size() ;) { + File f = paths.get(i); + // TEMP WORKAROUND: ignore classes.jar as all the output of libraries are + // called the same (in Ant) but are not actually the same jar file. + // TODO: Be aware of library output vs. regular jar dependency. + if (f.isFile() && f.getName().equals(SdkConstants.FN_CLASSES_JAR) == false) { + i++; + } else { + results.add(f); + paths.remove(i); + } + } + + + File outputFile = new File(projectFolder, getOutDir(properties)); + JarListSanitizer sanitizer = new JarListSanitizer(outputFile); + + try { + results.addAll(sanitizer.sanitize(paths)); + } catch (DifferentLibException e) { + String[] details = e.getDetails(); + for (String s : details) { + System.err.println(s); + } + throw new BuildException(e.getMessage(), e); + } catch (Sha1Exception e) { + throw new BuildException( + "Failed to compute sha1 for " + e.getJarFile().getAbsolutePath(), e); + } + + return results; + } + + /** + * + * @param projectFolder the project root folder. + */ + public DependencyHelper(File projectFolder, boolean verbose) { + mProjectFolder = projectFolder; + mVerbose = verbose; + + ProjectProperties properties = ProjectProperties.load(projectFolder.getAbsolutePath(), + PropertyType.ANT); + + if (properties == null) { + properties = ProjectProperties.load(projectFolder.getAbsolutePath(), + PropertyType.PROJECT); + } else { + properties.makeWorkingCopy().merge(PropertyType.PROJECT); + } + + mProperties = properties; + + init(projectFolder); + } + + /** + * + * @param projectFolder the project root folder. + * @param source an {@link IPropertySource} that can provide the project properties values. + */ + public DependencyHelper(File projectFolder, IPropertySource properties, boolean verbose) { + mProjectFolder = projectFolder; + mProperties = properties; + mVerbose = verbose; + + init(projectFolder); + } + + private void init(File projectFolder) { + // get the top level list of library dependencies. + List<File> topLevelLibraries = getDirectDependencies(projectFolder, mProperties); + + // process the libraries in case they depend on other libraries. + resolveFullLibraryDependencies(topLevelLibraries, mLibraries); + } + + public List<File> getLibraries() { + return mLibraries; + } + + public int getLibraryCount() { + return mLibraries.size(); + } + + public String getProperty(String name) { + return mProperties.getProperty(name); + } + + public void processLibraries(LibraryProcessor processor) { + // use that same order to process the libraries. + for (File library : mLibraries) { + // get the root path. + String libRootPath = library.getAbsolutePath(); + if (mVerbose) { + System.out.println(libRootPath); + } + + if (processor != null) { + processor.processLibrary(libRootPath); + } + } + } + + public List<File> sanitizePaths(List<File> paths) { + return sanitizePaths(mProjectFolder, mProperties, paths); + } + + public String getOutDir() { + return getOutDir(mProperties); + } + + + /** + * Returns the top level library dependencies of a given <var>source</var> representing a + * project properties. + * @param baseFolder the base folder of the project (to resolve relative paths) + * @param properties a source of project properties. + */ + private List<File> getDirectDependencies(File baseFolder, IPropertySource properties) { + ArrayList<File> libraries = new ArrayList<File>(); + + // first build the list. they are ordered highest priority first. + int index = 1; + while (true) { + String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++); + String rootPath = properties.getProperty(propName); + + if (rootPath == null) { + break; + } + + try { + File library = new File(baseFolder, rootPath).getCanonicalFile(); + + // check for validity + File projectProp = new File(library, PropertyType.PROJECT.getFilename()); + if (projectProp.isFile() == false) { + // error! + throw new BuildException(String.format( + "%1$s resolve to a path with no %2$s file for project %3$s", rootPath, + PropertyType.PROJECT.getFilename(), baseFolder.getAbsolutePath())); + } + + if (libraries.contains(library) == false) { + if (mVerbose) { + System.out.println(String.format("%1$s: %2$s => %3$s", + baseFolder.getAbsolutePath(), rootPath, library.getAbsolutePath())); + } + + libraries.add(library); + } + } catch (IOException e) { + throw new BuildException("Failed to resolve library path: " + rootPath, e); + } + } + + return libraries; + } + + /** + * Resolves a given list of libraries, finds out if they depend on other libraries, and + * returns a full list of all the direct and indirect dependencies in the proper order (first + * is higher priority when calling aapt). + * @param inLibraries the libraries to resolve + * @param outLibraries where to store all the libraries. + */ + private void resolveFullLibraryDependencies(List<File> inLibraries, List<File> outLibraries) { + // loop in the inverse order to resolve dependencies on the libraries, so that if a library + // is required by two higher level libraries it can be inserted in the correct place + for (int i = inLibraries.size() - 1 ; i >= 0 ; i--) { + File library = inLibraries.get(i); + + // get the default.property file for it + final ProjectProperties projectProp = ProjectProperties.load( + new FolderWrapper(library), PropertyType.PROJECT); + + // get its libraries + List<File> dependencies = getDirectDependencies(library, projectProp); + + // resolve the dependencies for those libraries + resolveFullLibraryDependencies(dependencies, outLibraries); + + // and add the current one (if needed) in front (higher priority) + if (outLibraries.contains(library) == false) { + outLibraries.add(0, library); + } + } + } + + private static String getOutDir(IPropertySource properties) { + String bin = properties.getProperty("out.dir"); + if (bin == null) { + return SdkConstants.FD_OUTPUT; + } + + return bin; + } + +} |