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