| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ant; |
| |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.types.FileSet; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.types.PatternSet.NameEntry; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| class MultiFilesTask extends BuildTypedTask { |
| |
| static enum DisplayType { |
| FOUND, COMPILING, REMOVE_OUTPUT, REMOVE_DEP; |
| } |
| |
| interface SourceProcessor { |
| Set<String> getSourceFileExtensions(); |
| void process(String filePath, String sourceFolder, |
| List<String> sourceFolders, Project taskProject); |
| void displayMessage(DisplayType type, int count); |
| } |
| |
| protected void processFiles(SourceProcessor processor, List<Path> paths, String genFolder) { |
| |
| Project taskProject = getProject(); |
| |
| Set<String> extensions = processor.getSourceFileExtensions(); |
| |
| // build a list of all the source folders |
| ArrayList<String> sourceFolders = new ArrayList<String>(); |
| for (Path p : paths) { |
| String[] values = p.list(); |
| if (values != null) { |
| sourceFolders.addAll(Arrays.asList(values)); |
| } |
| } |
| |
| ArrayList<String> includePatterns = new ArrayList<String>(extensions.size()); |
| for (String extension : extensions) { |
| includePatterns.add("**/*." + extension); |
| } |
| |
| // gather all the source files from all the source folders. |
| Map<String, String> sourceFiles = getFilesByNameEntryFilter(sourceFolders, |
| includePatterns.toArray(new String[includePatterns.size()])); |
| if (sourceFiles.size() > 0) { |
| processor.displayMessage(DisplayType.FOUND, sourceFiles.size()); |
| } |
| |
| // go look for all dependency files in the gen folder. This will have all dependency |
| // files but we can filter them based on the first pre-req file. |
| Iterator<?> depFiles = getFilesByNameEntryFilter(genFolder, "**/*.d"); |
| |
| // parse all the dep files and keep the ones that are of the proper type and check if |
| // they require compilation again. |
| Map<String, String> toCompile = new HashMap<String, String>(); |
| ArrayList<File> toRemove = new ArrayList<File>(); |
| ArrayList<String> depsToRemove = new ArrayList<String>(); |
| while (depFiles.hasNext()) { |
| String depFile = depFiles.next().toString(); |
| DependencyGraph graph = new DependencyGraph(depFile, null /*watchPaths*/); |
| |
| // get the source file. it's the first item in the pre-reqs |
| File sourceFile = graph.getFirstPrereq(); |
| String sourceFilePath = sourceFile.getAbsolutePath(); |
| |
| // The gen folder may contain other dependency files not generated by this particular |
| // processor. |
| // We only care if the first pre-rep is of the right extension. |
| String fileExtension = sourceFilePath.substring(sourceFilePath.lastIndexOf('.') + 1); |
| if (extensions.contains(fileExtension.toLowerCase(Locale.US))) { |
| // remove from the list of sourceFiles to mark as "processed" (but not compiled |
| // yet, that'll be done by adding it to toCompile) |
| String sourceFolder = sourceFiles.get(sourceFilePath); |
| if (sourceFolder == null) { |
| // looks like the source file does not exist anymore! |
| // we'll have to remove the output! |
| Set<File> outputFiles = graph.getTargets(); |
| toRemove.addAll(outputFiles); |
| |
| // also need to remove the dep file. |
| depsToRemove.add(depFile); |
| } else { |
| // Source file is present. remove it from the list as being processed. |
| sourceFiles.remove(sourceFilePath); |
| |
| // check if it needs to be recompiled. |
| if (hasBuildTypeChanged() || |
| graph.dependenciesHaveChanged(false /*printStatus*/)) { |
| toCompile.put(sourceFilePath, sourceFolder); |
| } |
| } |
| } |
| } |
| |
| // add to the list of files to compile, whatever is left in sourceFiles. Those are |
| // new files that have never been compiled. |
| toCompile.putAll(sourceFiles); |
| |
| processor.displayMessage(DisplayType.COMPILING, toCompile.size()); |
| if (toCompile.size() > 0) { |
| for (Entry<String, String> toCompilePath : toCompile.entrySet()) { |
| processor.process(toCompilePath.getKey(), toCompilePath.getValue(), |
| sourceFolders, taskProject); |
| } |
| } |
| |
| if (toRemove.size() > 0) { |
| processor.displayMessage(DisplayType.REMOVE_OUTPUT, 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) { |
| processor.displayMessage(DisplayType.REMOVE_DEP, toRemove.size()); |
| |
| for (String path : depsToRemove) { |
| if (new File(path).delete() == false) { |
| System.err.println("Failed to remove " + path); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns a list of files found in given folders, all matching a given filter. |
| * The result is a map of (file, folder). |
| * @param folders the folders to search |
| * @param filter the filter for the files. Typically a glob. |
| * @return a map of (file, folder) |
| */ |
| private Map<String, String> getFilesByNameEntryFilter(List<String> folders, String[] filters) { |
| Map<String, String> sourceFiles = new HashMap<String, String>(); |
| |
| for (String folder : folders) { |
| Iterator<?> iterator = getFilesByNameEntryFilter(folder, filters); |
| |
| while (iterator.hasNext()) { |
| sourceFiles.put(iterator.next().toString(), folder); |
| } |
| } |
| |
| return sourceFiles; |
| } |
| |
| /** |
| * Returns a list of files found in a given folder, matching a given filter. |
| * @param folder the folder to search |
| * @param filter the filter for the files. Typically a glob. |
| * @return an iterator. |
| */ |
| private Iterator<?> getFilesByNameEntryFilter(String folder, String... filters) { |
| Project taskProject = getProject(); |
| |
| // create a fileset to find all the files in the folder |
| FileSet fs = new FileSet(); |
| fs.setProject(taskProject); |
| fs.setDir(new File(folder)); |
| for (String filter : filters) { |
| NameEntry include = fs.createInclude(); |
| include.setName(filter); |
| } |
| |
| // loop through the results of the file set |
| return fs.iterator(); |
| } |
| } |