Home | History | Annotate | Download | only in ant
      1 /*
      2  * Copyright (C) 2010 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 org.apache.tools.ant.BuildException;
     20 import org.apache.tools.ant.Project;
     21 import org.apache.tools.ant.Task;
     22 import org.apache.tools.ant.taskdefs.ExecTask;
     23 import org.apache.tools.ant.types.FileSet;
     24 import org.apache.tools.ant.types.Path;
     25 import org.apache.tools.ant.types.PatternSet.NameEntry;
     26 
     27 import java.io.File;
     28 import java.util.ArrayList;
     29 import java.util.Arrays;
     30 import java.util.HashSet;
     31 import java.util.Iterator;
     32 import java.util.List;
     33 import java.util.Set;
     34 
     35 /**
     36  * Task to execute aidl.
     37  * <p>
     38  * It expects 3 attributes:<br>
     39  * 'executable' ({@link Path} with a single path) for the location of the aidl executable<br>
     40  * 'framework' ({@link Path} with a single path) for the "preprocessed" file containing all the
     41  *     parcelables exported by the framework<br>
     42  * 'genFolder' ({@link Path} with a single path) for the location of the gen folder.
     43  *
     44  * It also expects one or more inner elements called "source" which are identical to {@link Path}
     45  * elements.
     46  */
     47 public class AidlExecTask extends Task {
     48 
     49     private String mExecutable;
     50     private String mFramework;
     51     private String mGenFolder;
     52     private final ArrayList<Path> mPaths = new ArrayList<Path>();
     53 
     54     /**
     55      * Sets the value of the "executable" attribute.
     56      * @param executable the value.
     57      */
     58     public void setExecutable(Path executable) {
     59         mExecutable = TaskHelper.checkSinglePath("executable", executable);
     60     }
     61 
     62     public void setFramework(Path value) {
     63         mFramework = TaskHelper.checkSinglePath("framework", value);
     64     }
     65 
     66     public void setGenFolder(Path value) {
     67         mGenFolder = TaskHelper.checkSinglePath("genFolder", value);
     68     }
     69 
     70     public Path createSource() {
     71         Path p = new Path(getProject());
     72         mPaths.add(p);
     73         return p;
     74     }
     75 
     76     @Override
     77     public void execute() throws BuildException {
     78         if (mExecutable == null) {
     79             throw new BuildException("AidlExecTask's 'executable' is required.");
     80         }
     81         if (mFramework == null) {
     82             throw new BuildException("AidlExecTask's 'framework' is required.");
     83         }
     84         if (mGenFolder == null) {
     85             throw new BuildException("AidlExecTask's 'genFolder' is required.");
     86         }
     87 
     88         Project taskProject = getProject();
     89 
     90         // build a list of all the source folders
     91         ArrayList<String> sourceFolders = new ArrayList<String>();
     92         for (Path p : mPaths) {
     93             String[] values = p.list();
     94             if (values != null) {
     95                 sourceFolders.addAll(Arrays.asList(values));
     96             }
     97         }
     98 
     99         // gather all the aidl files from all the source folders.
    100         Set<String> sourceFiles = getFileListByExtension(taskProject, sourceFolders, "**/*.aidl");
    101         if (sourceFiles.size() > 0) {
    102             System.out.println(String.format("Found %d aidl files.", sourceFiles.size()));
    103         }
    104 
    105         // go look for all dependency files in the gen folder.
    106         Set<String> depFiles = getFileListByExtension(taskProject, mGenFolder, "**/*.d");
    107 
    108         // parse all the dep files and keep the ones that are aidl and check if they require
    109         // compilation again.
    110         ArrayList<String> toCompile = new ArrayList<String>();
    111         ArrayList<File> toRemove = new ArrayList<File>();
    112         ArrayList<String> depsToRemove = new ArrayList<String>();
    113         for (String depFile : depFiles) {
    114             DependencyGraph graph = new DependencyGraph(depFile, null /*watchPaths*/);
    115 
    116             // get the source file. it's the first item in the pre-reqs
    117             File sourceFile = graph.getFirstPrereq();
    118             String sourceFilePath = sourceFile.getAbsolutePath();
    119 
    120             // The gen folder may contain other dependency files not generated by aidl.
    121             // We only care if the first pre-rep is an aidl file.
    122             if (sourceFilePath.toLowerCase().endsWith(".aidl")) {
    123                 // remove from the list of sourceFiles to mark as "processed" (but not compiled
    124                 // yet, that'll be done by adding it to toCompile)
    125                 if (sourceFiles.remove(sourceFilePath) == false) {
    126                     // looks like the source file does not exist anymore!
    127                     // we'll have to remove the output!
    128                     Set<File> outputFiles = graph.getTargets();
    129                     toRemove.addAll(outputFiles);
    130 
    131                     // also need to remove the dep file.
    132                     depsToRemove.add(depFile);
    133                 } else if (graph.dependenciesHaveChanged(false /*printStatus*/)) {
    134                     // need to recompile!
    135                     toCompile.add(sourceFilePath);
    136                 }
    137             }
    138         }
    139 
    140         // add to the list of files to compile, whatever is left in sourceFiles. Those are
    141         // new files that have never been compiled.
    142         toCompile.addAll(sourceFiles);
    143 
    144         if (toCompile.size() > 0) {
    145             System.out.println(String.format("Compiling %d aidl files.", toCompile.size()));
    146 
    147             for (String toCompilePath : toCompile) {
    148                 ExecTask task = new ExecTask();
    149                 task.setProject(taskProject);
    150                 task.setOwningTarget(getOwningTarget());
    151                 task.setExecutable(mExecutable);
    152                 task.setTaskName("aidl");
    153                 task.setFailonerror(true);
    154 
    155                 task.createArg().setValue("-p" + mFramework);
    156                 task.createArg().setValue("-o" + mGenFolder);
    157                 // add all the source folders as import in case an aidl file in a source folder
    158                 // imports a parcelable from another source folder.
    159                 for (String importFolder : sourceFolders) {
    160                     task.createArg().setValue("-I" + importFolder);
    161                 }
    162 
    163                 // set auto dependency file creation
    164                 task.createArg().setValue("-a");
    165 
    166                 task.createArg().setValue(toCompilePath);
    167 
    168                 // execute it.
    169                 task.execute();
    170             }
    171         } else {
    172             System.out.println(String.format("No aidl files to compile."));
    173         }
    174 
    175         if (toRemove.size() > 0) {
    176             System.out.println(String.format("%d obsolete output files to remove.",
    177                     toRemove.size()));
    178             for (File toRemoveFile : toRemove) {
    179                 if (toRemoveFile.delete() == false) {
    180                     System.err.println("Failed to remove " + toRemoveFile.getAbsolutePath());
    181                 }
    182             }
    183         }
    184 
    185         // remove the dependency files that are obsolete
    186         if (depsToRemove.size() > 0) {
    187             System.out.println(String.format("%d obsolete dependency files to remove.",
    188                     depsToRemove.size()));
    189             for (String path : depsToRemove) {
    190                 if (new File(path).delete() == false) {
    191                     System.err.println("Failed to remove " + path);
    192                 }
    193             }
    194         }
    195     }
    196 
    197     private Set<String> getFileListByExtension(Project taskProject,
    198             List<String> sourceFolders, String filter) {
    199         HashSet<String> sourceFiles = new HashSet<String>();
    200         for (String sourceFolder : sourceFolders) {
    201             sourceFiles.addAll(getFileListByExtension(taskProject, sourceFolder, filter));
    202         }
    203 
    204         return sourceFiles;
    205     }
    206 
    207     private Set<String> getFileListByExtension(Project taskProject,
    208             String sourceFolder, String filter) {
    209         HashSet<String> sourceFiles = new HashSet<String>();
    210 
    211         // create a fileset to find all the files in the folder
    212         FileSet fs = new FileSet();
    213         fs.setProject(taskProject);
    214         fs.setDir(new File(sourceFolder));
    215         NameEntry include = fs.createInclude();
    216         include.setName(filter);
    217 
    218         // loop through the results of the file set
    219         Iterator<?> iter = fs.iterator();
    220         while (iter.hasNext()) {
    221             sourceFiles.add(iter.next().toString());
    222         }
    223 
    224         return sourceFiles;
    225     }
    226 
    227 
    228 }
    229