• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
setExecutable(Path executable)58     public void setExecutable(Path executable) {
59         mExecutable = TaskHelper.checkSinglePath("executable", executable);
60     }
61 
setFramework(Path value)62     public void setFramework(Path value) {
63         mFramework = TaskHelper.checkSinglePath("framework", value);
64     }
65 
setGenFolder(Path value)66     public void setGenFolder(Path value) {
67         mGenFolder = TaskHelper.checkSinglePath("genFolder", value);
68     }
69 
createSource()70     public Path createSource() {
71         Path p = new Path(getProject());
72         mPaths.add(p);
73         return p;
74     }
75 
76     @Override
execute()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 
getFileListByExtension(Project taskProject, List<String> sourceFolders, String filter)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 
getFileListByExtension(Project taskProject, String sourceFolder, String filter)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