aboutsummaryrefslogtreecommitdiffstats
path: root/anttasks/src/com/android/ant/DependencyHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'anttasks/src/com/android/ant/DependencyHelper.java')
-rw-r--r--anttasks/src/com/android/ant/DependencyHelper.java301
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;
+ }
+
+}