blob: 4f1ae404ad4fde8ef3220923227833b177669ea6 [file] [log] [blame]
/*
* 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();
}
}