OSDN Git Service

Add support for proper dependency detection in the aidl ant compilation step.
authorXavier Ducrohet <xav@android.com>
Tue, 23 Aug 2011 17:18:17 +0000 (10:18 -0700)
committerXavier Ducrohet <xav@android.com>
Thu, 1 Sep 2011 21:10:28 +0000 (14:10 -0700)
Aidl compilation now generates dependency files that are reused on further
compilations to only recompile files that needs it based on source files
modifications.

Also clean up output (and dependency) files when a source file is deleted.

Change-Id: I3131463fd7939ffc4b5bbdfa49940e03f0249a28

anttasks/src/com/android/ant/AaptExecTask.java [moved from anttasks/src/com/android/ant/AaptExecLoopTask.java with 99% similarity]
anttasks/src/com/android/ant/AidlExecTask.java
anttasks/src/com/android/ant/BaseTask.java
anttasks/src/com/android/ant/DependencyGraph.java
anttasks/src/com/android/ant/RenderScriptTask.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java
files/ant/build.xml

@@ -50,7 +50,7 @@ import java.util.ArrayList;
  * <tr><td></td><td></td><td></td></tr>
  * </table>
  */
-public final class AaptExecLoopTask extends BaseTask {
+public final class AaptExecTask extends BaseTask {
 
     /**
      * Class representing a &lt;nocompress&gt; node in the main task XML.
@@ -352,6 +352,8 @@ public final class AaptExecLoopTask extends BaseTask {
                               && dependenciesHaveChanged() == false) {
                 System.out.println("No changed resources. R.java and Manifest.java untouched.");
                 return;
+            } else {
+                System.out.println("Generating resource IDs...");
             }
         } else {
             // Find our dependency file. It should have the same name as our target .ap_ but
index 5fa1f30..98cac9b 100644 (file)
@@ -27,7 +27,10 @@ import org.apache.tools.ant.types.PatternSet.NameEntry;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Task to execute aidl.
@@ -93,21 +96,57 @@ public class AidlExecTask extends Task {
             }
         }
 
-        // now loop on all the source folders to find all the aidl to compile
-        // and compile them
-        for (String sourceFolder : sourceFolders) {
-            // create a fileset to find all the aidl files in the current source folder
-            FileSet fs = new FileSet();
-            fs.setProject(taskProject);
-            fs.setDir(new File(sourceFolder));
-            NameEntry include = fs.createInclude();
-            include.setName("**/*.aidl");
-
-            // loop through the results of the file set
-            Iterator<?> iter = fs.iterator();
-            while (iter.hasNext()) {
-                Object next = iter.next();
+        // gather all the aidl files from all the source folders.
+        Set<String> sourceFiles = getFileListByExtension(taskProject, sourceFolders, "**/*.aidl");
+        if (sourceFiles.size() > 0) {
+            System.out.println(String.format("Found %d aidl files.", sourceFiles.size()));
+        }
+
+        // go look for all dependency files in the gen folder.
+        Set<String> depFiles = getFileListByExtension(taskProject, mGenFolder, "**/*.d");
+
+        // parse all the dep files and keep the ones that are aidl and check if they require
+        // compilation again.
+        ArrayList<String> toCompile = new ArrayList<String>();
+        ArrayList<File> toRemove = new ArrayList<File>();
+        ArrayList<String> depsToRemove = new ArrayList<String>();
+        for (String depFile : depFiles) {
+            DependencyGraph graph = new DependencyGraph(depFile, null /*watchPaths*/);
+
+            // get the source file. it's the first item in the pre-reqs
+            List<File> preReqs = graph.getPrereqs();
+            File sourceFile = preReqs.get(0);
+            String sourceFilePath = sourceFile.getAbsolutePath();
+
+            // The gen folder may contain other dependency files not generated by aidl.
+            // We only care if the first pre-rep is an aidl file.
+            if (sourceFilePath.toLowerCase().endsWith(".aidl")) {
+                // remove from the list of sourceFiles to mark as "processed" (but not compiled
+                // yet, that'll be done by adding it to toCompile)
+                if (sourceFiles.remove(sourceFilePath) == false) {
+                    // looks like the source file does not exist anymore!
+                    // we'll have to remove the output!
+                    List<File> outputFiles = graph.getTargets();
+                    toRemove.addAll(outputFiles);
+
+                    // also need to remove the dep file.
+                    depsToRemove.add(depFile);
+                } else if (graph.dependenciesHaveChanged(null /*extensionsToCheck*/,
+                        false /*printStatus*/)) {
+                    // need to recompile!
+                    toCompile.add(sourceFilePath);
+                }
+            }
+        }
+
+        // add to the list of files to compile, whatever is left in sourceFiles. Those are
+        // new files that have never been compiled.
+        toCompile.addAll(sourceFiles);
 
+        if (toCompile.size() > 0) {
+            System.out.println(String.format("Compiling %d aidl files.", toCompile.size()));
+
+            for (String toCompilePath : toCompile) {
                 ExecTask task = new ExecTask();
                 task.setProject(taskProject);
                 task.setOwningTarget(getOwningTarget());
@@ -123,11 +162,69 @@ public class AidlExecTask extends Task {
                     task.createArg().setValue("-I" + importFolder);
                 }
 
-                task.createArg().setValue(next.toString());
+                // set auto dependency file creation
+                task.createArg().setValue("-a");
+
+                task.createArg().setValue(toCompilePath);
 
                 // execute it.
                 task.execute();
             }
+        } else {
+            System.out.println(String.format("No aidl files to compile."));
+        }
+
+        if (toRemove.size() > 0) {
+            System.out.println(String.format("%d obsolete output files to remove.",
+                    toRemove.size()));
+            for (File toRemoveFile : toRemove) {
+                if (toRemoveFile.delete() == false) {
+                    System.err.println("Failed to remove " + toRemoveFile.getAbsolutePath());
+                }
+            }
+        }
+
+        // remove the dependency files that are obsolete
+        if (depsToRemove.size() > 0) {
+            System.out.println(String.format("%d obsolete dependency files to remove.",
+                    depsToRemove.size()));
+            for (String path : depsToRemove) {
+                if (new File(path).delete() == false) {
+                    System.err.println("Failed to remove " + path);
+                }
+            }
+        }
+    }
+
+    private Set<String> getFileListByExtension(Project taskProject,
+            List<String> sourceFolders, String filter) {
+        HashSet<String> sourceFiles = new HashSet<String>();
+        for (String sourceFolder : sourceFolders) {
+            sourceFiles.addAll(getFileListByExtension(taskProject, sourceFolder, filter));
         }
+
+        return sourceFiles;
     }
+
+    private Set<String> getFileListByExtension(Project taskProject,
+            String sourceFolder, String filter) {
+        HashSet<String> sourceFiles = new HashSet<String>();
+
+        // create a fileset to find all the files in the folder
+        FileSet fs = new FileSet();
+        fs.setProject(taskProject);
+        fs.setDir(new File(sourceFolder));
+        NameEntry include = fs.createInclude();
+        include.setName(filter);
+
+        // loop through the results of the file set
+        Iterator<?> iter = fs.iterator();
+        while (iter.hasNext()) {
+            sourceFiles.add(iter.next().toString());
+        }
+
+        return sourceFiles;
+    }
+
+
 }
index 0ff7bf1..68dd6b5 100644 (file)
@@ -102,6 +102,7 @@ public abstract class BaseTask extends Task {
         }
 
         assert mDependencies != null : "Dependencies have not been initialized";
-        return mDependencies.dependenciesHaveChanged(mRestrictTouchedExtensionsTo);
+        return mDependencies.dependenciesHaveChanged(mRestrictTouchedExtensionsTo,
+                true /*printStatus*/);
     }
 }
index 4c85860..11c09e4 100644 (file)
@@ -23,7 +23,7 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -34,8 +34,10 @@ import java.util.Set;
 public class DependencyGraph {
 
     // Files that we know about from the dependency file
-    private Set<File> mTargets = Collections.emptySet();
-    private Set<File> mPrereqs = mTargets;
+    private List<File> mTargets = Collections.emptyList();
+    private List<File> mPrereqs = mTargets;
+    private boolean mMissingDepFile = false;
+    private long mDepFileLastModified;
     private final ArrayList<File> mWatchPaths;
 
     public DependencyGraph(String dependencyFilePath, ArrayList<File> watchPaths) {
@@ -48,34 +50,45 @@ public class DependencyGraph {
      * @param extensionsToCheck a set of extensions. Only files with an extension in this set will
      *        be considered for a modification check. All deleted/created files will still be
      *        checked. If this is null, all files will be checked for modification date
+     * @param printStatus will print to {@link System#out} the dependencies status.
      * @return true if new prerequisites have appeared, target files are missing or if
      *         prerequisite files have been modified since the last target generation.
      */
-    public boolean dependenciesHaveChanged(Set<String> extensionsToCheck) {
-        boolean noFile = (mTargets.size() == 0);
+    public boolean dependenciesHaveChanged(Set<String> extensionsToCheck, boolean printStatus) {
         boolean missingPrereq = missingPrereqFile();
         boolean newPrereq = newPrereqFile();
         boolean missingTarget = missingTargetFile();
         boolean modPrereq = modifiedPrereq(extensionsToCheck);
 
-        if (noFile) {
-            System.out.println("No Dependency File Found");
-        }
-        if (missingPrereq) {
-            System.out.println("Found Deleted Prereq File");
-        }
-        if (newPrereq) {
-            System.out.println("Found New Prereq File");
-        }
-        if (missingTarget) {
-            System.out.println("Found Deleted Target File");
-        }
-        if (modPrereq) {
-            System.out.println("Found Modified Prereq File");
+        if (printStatus) {
+            if (mMissingDepFile) {
+                System.out.println("No Dependency File Found");
+            }
+            if (missingPrereq) {
+                System.out.println("Found Deleted Prereq File");
+            }
+            if (newPrereq) {
+                System.out.println("Found New Prereq File");
+            }
+            if (missingTarget) {
+                System.out.println("Found Deleted Target File");
+            }
+            if (modPrereq) {
+                System.out.println("Found Modified Prereq File");
+            }
         }
+
         // If no dependency file has been set up, then we'll just return true
         // if we have a dependency file, we'll check to see what's been changed
-        return noFile || missingPrereq || newPrereq || missingTarget || modPrereq;
+        return mMissingDepFile || missingPrereq || newPrereq || missingTarget || modPrereq;
+    }
+
+    public List<File> getTargets() {
+        return mTargets;
+    }
+
+    public List<File> getPrereqs() {
+        return mPrereqs;
     }
 
     /**
@@ -84,6 +97,16 @@ public class DependencyGraph {
      * @param dependencyFilePath the dependency file
      */
     private void parseDependencyFile(String dependencyFilePath) {
+        // first check if the dependency file is here.
+        File depFile = new File(dependencyFilePath);
+        if (depFile.isFile() == false) {
+            mMissingDepFile = true;
+            return;
+        }
+
+        // get the modification time of the dep file as we may need it later
+        mDepFileLastModified = depFile.lastModified();
+
         // Read in our dependency file
         String content = readFile(dependencyFilePath);
         if (content == null) {
@@ -121,14 +144,20 @@ public class DependencyGraph {
             prereqs = files[1].trim().split(" ");
         }
 
-        mTargets = new HashSet<File>(targets.length);
+        mTargets = new ArrayList<File>(targets.length);
         for (String path : targets) {
-            mTargets.add(new File(path));
+            if (path.length() > 0) {
+                mTargets.add(new File(path));
+            }
         }
-        mPrereqs = new HashSet<File>(prereqs.length);
+        mTargets = Collections.unmodifiableList(mTargets);
+        mPrereqs = new ArrayList<File>(prereqs.length);
         for (String path : prereqs) {
-            mPrereqs.add(new File(path));
+            if (path.length() > 0) {
+                mPrereqs.add(new File(path));
+            }
         }
+        mPrereqs = Collections.unmodifiableList(mPrereqs);
     }
 
     /**
@@ -137,9 +166,11 @@ public class DependencyGraph {
      * @return true if a new file is encountered in the dependency folders
      */
     private boolean newPrereqFile() {
-        for (File dir : mWatchPaths) {
-            if (newFileInTree(dir)) {
-                return true;
+        if (mWatchPaths != null) {
+            for (File dir : mWatchPaths) {
+                if (newFileInTree(dir)) {
+                    return true;
+                }
             }
         }
         // If we make it all the way through our directories we're good.
@@ -212,9 +243,14 @@ public class DependencyGraph {
     private boolean modifiedPrereq(Set<String> extensionsToCheck) {
         // Find the oldest target
         long oldestTarget = Long.MAX_VALUE;
-        for (File target : mTargets) {
-            if (target.lastModified() < oldestTarget) {
-                oldestTarget = target.lastModified();
+        // if there's no output, then compare to the time of the dependency file.
+        if (mTargets.size() == 0) {
+            oldestTarget = mDepFileLastModified;
+        } else {
+            for (File target : mTargets) {
+                if (target.lastModified() < oldestTarget) {
+                    oldestTarget = target.lastModified();
+                }
             }
         }
 
@@ -273,4 +309,5 @@ public class DependencyGraph {
         // Don't include the leading '.' in the extension
         return filename.substring(filename.lastIndexOf('.') + 1);
     }
+
 }
index 5aa6612..08eeeed 100644 (file)
@@ -105,6 +105,8 @@ public class RenderScriptTask extends Task {
         File exe = new File(mExecutable);
         String execTaskName = exe.getName();
 
+        int count = 0;
+
         // now loop on all the source folders to find all the renderscript to compile
         // and compile them
         for (String sourceFolder : sourceFolders) {
@@ -145,5 +147,12 @@ public class RenderScriptTask extends Task {
                 task.execute();
             }
         }
+
+        if (count > 0) {
+            System.out.println(String.format("Compiled %d renderscript files.", count));
+        } else {
+            System.out.println("No renderscript files to compile.");
+        }
+
     }
 }
index f13e201..84fa2d7 100644 (file)
@@ -72,8 +72,8 @@ public class RenderScriptProcessor extends SourceProcessor {
             if (r == false &&
                     kind == IResourceDelta.REMOVED &&
                     AdtConstants.EXT_DEP.equalsIgnoreCase(file.getFileExtension())) {
-                // This looks to be an extension file.
-                // For futureproofness let's make sure this dependency file was generated by
+                // This looks to be a dependency file.
+                // For future-proofness let's make sure this dependency file was generated by
                 // this processor even if it's the only processor using them for now.
 
                 // look for the original file.
index e128ad8..e379d95 100644 (file)
@@ -66,7 +66,7 @@
             classpathref="android.antlibs" />
 
     <taskdef name="aapt"
-            classname="com.android.ant.AaptExecLoopTask"
+            classname="com.android.ant.AaptExecTask"
             classpathref="android.antlibs" />
 
     <taskdef name="aidl"
     <target name="-code-gen">
         <do-only-if-manifest-hasCode
                 elseText="hasCode = false. Skipping aidl/renderscript/R.java">
-            <echo>Compiling aidl files into Java classes...</echo>
+            <echo>----------</echo>
+            <echo>Handling aidl files...</echo>
             <aidl executable="${aidl}" framework="${android.aidl}"
                     genFolder="${gen.absolute.dir}">
                 <source path="${source.absolute.dir}"/>
             </aidl>
 
             <!-- renderscript generates resources so it must be called before aapt -->
-            <echo>Compiling RenderScript files into Java classes and RenderScript bytecode...</echo>
+            <echo>----------</echo>
+            <echo>Handling RenderScript files...</echo>
             <renderscript executable="${renderscript}"
                     framework="${android.rs}"
                     genFolder="${gen.absolute.dir}"
                 <source path="${source.absolute.dir}"/>
             </renderscript>
 
-            <echo>Generating R.java / Manifest.java from the resources...</echo>
+            <echo>----------</echo>
+            <echo>Handling Resources...</echo>
             <aapt executable="${aapt}"
                     command="package"
                     verbose="${verbose}"