1 /* 2 * Copyright (C) 2011 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.Project; 20 import org.apache.tools.ant.types.FileSet; 21 import org.apache.tools.ant.types.Path; 22 import org.apache.tools.ant.types.PatternSet.NameEntry; 23 24 import java.io.File; 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.HashMap; 28 import java.util.Iterator; 29 import java.util.List; 30 import java.util.Locale; 31 import java.util.Map; 32 import java.util.Map.Entry; 33 import java.util.Set; 34 35 class MultiFilesTask extends BuildTypedTask { 36 37 static enum DisplayType { 38 FOUND, COMPILING, REMOVE_OUTPUT, REMOVE_DEP; 39 } 40 41 interface SourceProcessor { 42 String getSourceFileExtension(); 43 void process(String filePath, String sourceFolder, 44 List<String> sourceFolders, Project taskProject); 45 void displayMessage(DisplayType type, int count); 46 } 47 48 protected void processFiles(SourceProcessor processor, List<Path> paths, String genFolder) { 49 50 Project taskProject = getProject(); 51 52 String extension = processor.getSourceFileExtension(); 53 54 // build a list of all the source folders 55 ArrayList<String> sourceFolders = new ArrayList<String>(); 56 for (Path p : paths) { 57 String[] values = p.list(); 58 if (values != null) { 59 sourceFolders.addAll(Arrays.asList(values)); 60 } 61 } 62 63 // gather all the source files from all the source folders. 64 Map<String, String> sourceFiles = getFileListByExtension(taskProject, sourceFolders, 65 "**/*." + extension); 66 if (sourceFiles.size() > 0) { 67 processor.displayMessage(DisplayType.FOUND, sourceFiles.size()); 68 } 69 70 // go look for all dependency files in the gen folder. This will have all dependency 71 // files but we can filter them based on the first pre-req file. 72 Map<String, String> depFiles = getFileListByExtension(taskProject, genFolder, "**/*.d"); 73 74 // parse all the dep files and keep the ones that are of the proper type and check if 75 // they require compilation again. 76 Map<String, String> toCompile = new HashMap<String, String>(); 77 ArrayList<File> toRemove = new ArrayList<File>(); 78 ArrayList<String> depsToRemove = new ArrayList<String>(); 79 for (String depFile : depFiles.keySet()) { 80 DependencyGraph graph = new DependencyGraph(depFile, null /*watchPaths*/); 81 82 // get the source file. it's the first item in the pre-reqs 83 File sourceFile = graph.getFirstPrereq(); 84 String sourceFilePath = sourceFile.getAbsolutePath(); 85 86 // The gen folder may contain other dependency files not generated by this particular 87 // processor. 88 // We only care if the first pre-rep is of the right extension. 89 if (sourceFilePath.toLowerCase(Locale.US).endsWith("." + extension)) { 90 // remove from the list of sourceFiles to mark as "processed" (but not compiled 91 // yet, that'll be done by adding it to toCompile) 92 String sourceFolder = sourceFiles.get(sourceFilePath); 93 if (sourceFolder == null) { 94 // looks like the source file does not exist anymore! 95 // we'll have to remove the output! 96 Set<File> outputFiles = graph.getTargets(); 97 toRemove.addAll(outputFiles); 98 99 // also need to remove the dep file. 100 depsToRemove.add(depFile); 101 } else { 102 // Source file is present. remove it from the list as being processed. 103 sourceFiles.remove(sourceFilePath); 104 105 // check if it needs to be recompiled. 106 if (hasBuildTypeChanged() || 107 graph.dependenciesHaveChanged(false /*printStatus*/)) { 108 toCompile.put(sourceFilePath, sourceFolder); 109 } 110 } 111 } 112 } 113 114 // add to the list of files to compile, whatever is left in sourceFiles. Those are 115 // new files that have never been compiled. 116 toCompile.putAll(sourceFiles); 117 118 processor.displayMessage(DisplayType.COMPILING, toCompile.size()); 119 if (toCompile.size() > 0) { 120 for (Entry<String, String> toCompilePath : toCompile.entrySet()) { 121 processor.process(toCompilePath.getKey(), toCompilePath.getValue(), 122 sourceFolders, taskProject); 123 } 124 } 125 126 if (toRemove.size() > 0) { 127 processor.displayMessage(DisplayType.REMOVE_OUTPUT, toRemove.size()); 128 129 for (File toRemoveFile : toRemove) { 130 if (toRemoveFile.delete() == false) { 131 System.err.println("Failed to remove " + toRemoveFile.getAbsolutePath()); 132 } 133 } 134 } 135 136 // remove the dependency files that are obsolete 137 if (depsToRemove.size() > 0) { 138 processor.displayMessage(DisplayType.REMOVE_DEP, toRemove.size()); 139 140 for (String path : depsToRemove) { 141 if (new File(path).delete() == false) { 142 System.err.println("Failed to remove " + path); 143 } 144 } 145 } 146 } 147 148 private Map<String, String> getFileListByExtension(Project taskProject, 149 List<String> sourceFolders, String filter) { 150 HashMap<String, String> sourceFiles = new HashMap<String, String>(); 151 for (String sourceFolder : sourceFolders) { 152 sourceFiles.putAll(getFileListByExtension(taskProject, sourceFolder, filter)); 153 } 154 155 return sourceFiles; 156 } 157 158 private Map<String, String> getFileListByExtension(Project taskProject, 159 String sourceFolder, String filter) { 160 HashMap<String, String> sourceFiles = new HashMap<String, String>(); 161 162 // create a fileset to find all the files in the folder 163 FileSet fs = new FileSet(); 164 fs.setProject(taskProject); 165 fs.setDir(new File(sourceFolder)); 166 NameEntry include = fs.createInclude(); 167 include.setName(filter); 168 169 // loop through the results of the file set 170 Iterator<?> iter = fs.iterator(); 171 while (iter.hasNext()) { 172 sourceFiles.put(iter.next().toString(), sourceFolder); 173 } 174 175 return sourceFiles; 176 } 177 178 } 179