Home | History | Annotate | Download | only in ant
      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