git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			427 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			427 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * 
 | |
|  * Copyright 2002-2004 The Ant-Contrib 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 net.sf.antcontrib.cpptasks;
 | |
| import java.io.BufferedWriter;
 | |
| import java.io.File;
 | |
| import java.io.FileOutputStream;
 | |
| import java.io.IOException;
 | |
| import java.io.OutputStreamWriter;
 | |
| import java.io.UnsupportedEncodingException;
 | |
| import java.util.Enumeration;
 | |
| import java.util.Hashtable;
 | |
| import java.util.Vector;
 | |
| 
 | |
| import javax.xml.parsers.SAXParser;
 | |
| import javax.xml.parsers.SAXParserFactory;
 | |
| 
 | |
| import net.sf.antcontrib.cpptasks.compiler.ProcessorConfiguration;
 | |
| 
 | |
| import org.apache.tools.ant.BuildException;
 | |
| import org.xml.sax.Attributes;
 | |
| import org.xml.sax.SAXException;
 | |
| import org.xml.sax.helpers.DefaultHandler;
 | |
| /**
 | |
|  * A history of the compiler and linker settings used to build the files in the
 | |
|  * same directory as the history.
 | |
|  * 
 | |
|  * @author Curt Arnold
 | |
|  */
 | |
| public final class TargetHistoryTable {
 | |
|     /**
 | |
|      * This class handles populates the TargetHistory hashtable in response to
 | |
|      * SAX parse events
 | |
|      */
 | |
|     private class TargetHistoryTableHandler extends DefaultHandler {
 | |
|         private final File baseDir;
 | |
|         private String config;
 | |
|         private final Hashtable history;
 | |
|         private String output;
 | |
|         private long outputLastModified;
 | |
|         private final Vector sources = new Vector();
 | |
|         /**
 | |
|          * Constructor
 | |
|          * 
 | |
|          * @param history
 | |
|          *            hashtable of TargetHistory keyed by output name
 | |
|          * @param outputFiles
 | |
|          *            existing files in output directory
 | |
|          */
 | |
|         private TargetHistoryTableHandler(Hashtable history, File baseDir) {
 | |
|             this.history = history;
 | |
|             config = null;
 | |
|             output = null;
 | |
|             this.baseDir = baseDir;
 | |
|         }
 | |
|         public void endElement(String namespaceURI, String localName,
 | |
|                 String qName) throws SAXException {
 | |
|             //
 | |
|             //   if </target> then
 | |
|             //       create TargetHistory object and add to hashtable
 | |
|             //           if corresponding output file exists and
 | |
|             //           has the same timestamp
 | |
|             //
 | |
|             if (qName.equals("target")) {
 | |
|                 if (config != null && output != null) {
 | |
|                     File existingFile = new File(baseDir, output);
 | |
|                     //
 | |
|                     //   if the corresponding files doesn't exist or has a
 | |
|                     // different
 | |
|                     //      modification time, then discard this record
 | |
|                     if (existingFile.exists()) {
 | |
|                         long existingLastModified = existingFile.lastModified();
 | |
|                         //
 | |
|                         //   would have expected exact time stamps
 | |
|                         //      but have observed slight differences
 | |
|                         //      in return value for multiple evaluations of
 | |
|                         //      lastModified(). Check if times are within
 | |
|                         //      a second
 | |
|                         long diff = outputLastModified - existingLastModified;
 | |
|                         if (diff >= -500 && diff <= 500) {
 | |
|                             SourceHistory[] sourcesArray = new SourceHistory[sources
 | |
|                                     .size()];
 | |
|                             sources.copyInto(sourcesArray);
 | |
|                             TargetHistory targetHistory = new TargetHistory(
 | |
|                                     config, output, outputLastModified,
 | |
|                                     sourcesArray);
 | |
|                             history.put(output, targetHistory);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 output = null;
 | |
|                 sources.setSize(0);
 | |
|             } else {
 | |
|                 //
 | |
|                 //   reset config so targets not within a processor element
 | |
|                 //      don't pick up a previous processors signature
 | |
|                 //
 | |
|                 if (qName.equals("processor")) {
 | |
|                     config = null;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         /**
 | |
|          * startElement handler
 | |
|          */
 | |
|         public void startElement(String namespaceURI, String localName,
 | |
|                 String qName, Attributes atts) throws SAXException {
 | |
|             //
 | |
|             //   if sourceElement
 | |
|             //
 | |
|             if (qName.equals("source")) {
 | |
|                 String sourceFile = atts.getValue("file");
 | |
|                 long sourceLastModified = Long.parseLong(atts
 | |
|                         .getValue("lastModified"), 16);
 | |
|                 sources.addElement(new SourceHistory(sourceFile,
 | |
|                         sourceLastModified));
 | |
|             } else {
 | |
|                 //
 | |
|                 //   if <target> element,
 | |
|                 //      grab file name and lastModified values
 | |
|                 //      TargetHistory object will be created in endElement
 | |
|                 //
 | |
|                 if (qName.equals("target")) {
 | |
|                     sources.setSize(0);
 | |
|                     output = atts.getValue("file");
 | |
|                     outputLastModified = Long.parseLong(atts
 | |
|                             .getValue("lastModified"), 16);
 | |
|                 } else {
 | |
|                     //
 | |
|                     //   if <processor> element,
 | |
|                     //       grab signature attribute
 | |
|                     //
 | |
|                     if (qName.equals("processor")) {
 | |
|                         config = atts.getValue("signature");
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     /** Flag indicating whether the cache should be written back to file. */
 | |
|     private boolean dirty;
 | |
|     /**
 | |
|      * a hashtable of TargetHistory's keyed by output file name
 | |
|      */
 | |
|     private final Hashtable history = new Hashtable();
 | |
|     /** The file the cache was loaded from. */
 | |
|     private/* final */File historyFile;
 | |
|     private/* final */File outputDir;
 | |
|     private String outputDirPath;
 | |
|     /**
 | |
|      * Creates a target history table from history.xml in the output directory,
 | |
|      * if it exists. Otherwise, initializes the history table empty.
 | |
|      * 
 | |
|      * @param task
 | |
|      *            task used for logging history load errors
 | |
|      * @param outputDir
 | |
|      *            output directory for task
 | |
|      */
 | |
|     public TargetHistoryTable(CCTask task, File outputDir)
 | |
|             throws BuildException {
 | |
|         if (outputDir == null) {
 | |
|             throw new NullPointerException("outputDir");
 | |
|         }
 | |
|         if (!outputDir.isDirectory()) {
 | |
|             throw new BuildException("Output directory is not a directory");
 | |
|         }
 | |
|         if (!outputDir.exists()) {
 | |
|             throw new BuildException("Output directory does not exist");
 | |
|         }
 | |
|         this.outputDir = outputDir;
 | |
|         try {
 | |
|             outputDirPath = outputDir.getCanonicalPath();
 | |
|         } catch (IOException ex) {
 | |
|             outputDirPath = outputDir.toString();
 | |
|         }
 | |
|         //
 | |
|         //   load any existing history from file
 | |
|         //       suppressing any records whose corresponding
 | |
|         //       file does not exist, is zero-length or
 | |
|         //          last modified dates differ
 | |
|         historyFile = new File(outputDir, "history.xml");
 | |
|         if (historyFile.exists()) {
 | |
|             SAXParserFactory factory = SAXParserFactory.newInstance();
 | |
|             factory.setValidating(false);
 | |
|             try {
 | |
|                 SAXParser parser = factory.newSAXParser();
 | |
|                 parser.parse(historyFile, new TargetHistoryTableHandler(
 | |
|                         history, outputDir));
 | |
|             } catch (Exception ex) {
 | |
|                 //
 | |
|                 //   a failure on loading this history is not critical
 | |
|                 //       but should be logged
 | |
|                 task.log("Error reading history.xml: " + ex.toString());
 | |
|             }
 | |
|         } else {
 | |
|             //
 | |
|             // create empty history file for identifying new files by last
 | |
|             // modified
 | |
|             //   timestamp comperation (to compare with
 | |
|             //   System.currentTimeMillis() don't work on Unix, because it
 | |
|             //   maesure timestamps only in seconds).
 | |
|             //
 | |
|             try {
 | |
|                 FileOutputStream outputStream = new FileOutputStream(
 | |
|                         historyFile);
 | |
|                 byte[] historyElement = new byte[]{0x3C, 0x68, 0x69, 0x73,
 | |
|                         0x74, 0x6F, 0x72, 0x79, 0x2F, 0x3E};
 | |
|                 outputStream.write(historyElement);
 | |
|                 outputStream.close();
 | |
|             } catch (IOException ex) {
 | |
|                 throw new BuildException("Can't create history file", ex);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     public void commit() throws IOException {
 | |
|         //
 | |
|         //   if not dirty, no need to update file
 | |
|         //
 | |
|         if (dirty) {
 | |
|             //
 | |
|             //   build (small) hashtable of config id's in history
 | |
|             //
 | |
|             Hashtable configs = new Hashtable(20);
 | |
|             Enumeration elements = history.elements();
 | |
|             while (elements.hasMoreElements()) {
 | |
|                 TargetHistory targetHistory = (TargetHistory) elements
 | |
|                         .nextElement();
 | |
|                 String configId = targetHistory.getProcessorConfiguration();
 | |
|                 if (configs.get(configId) == null) {
 | |
|                     configs.put(configId, configId);
 | |
|                 }
 | |
|             }
 | |
|             FileOutputStream outStream = new FileOutputStream(historyFile);
 | |
|             OutputStreamWriter outWriter;
 | |
|             //
 | |
|             //   early VM's don't support UTF-8 encoding
 | |
|             //       try and fallback to the default encoding
 | |
|             //           otherwise
 | |
|             String encodingName = "UTF-8";
 | |
|             try {
 | |
|                 outWriter = new OutputStreamWriter(outStream, "UTF-8");
 | |
|             } catch (UnsupportedEncodingException ex) {
 | |
|                 outWriter = new OutputStreamWriter(outStream);
 | |
|                 encodingName = outWriter.getEncoding();
 | |
|             }
 | |
|             BufferedWriter writer = new BufferedWriter(outWriter);
 | |
|             writer.write("<?xml version='1.0' encoding='");
 | |
|             writer.write(encodingName);
 | |
|             writer.write("'?>\n");
 | |
|             writer.write("<history>\n");
 | |
|             StringBuffer buf = new StringBuffer(200);
 | |
|             Enumeration configEnum = configs.elements();
 | |
|             while (configEnum.hasMoreElements()) {
 | |
|                 String configId = (String) configEnum.nextElement();
 | |
|                 buf.setLength(0);
 | |
|                 buf.append("   <processor signature=\"");
 | |
|                 buf.append(CUtil.xmlAttribEncode(configId));
 | |
|                 buf.append("\">\n");
 | |
|                 writer.write(buf.toString());
 | |
|                 elements = history.elements();
 | |
|                 while (elements.hasMoreElements()) {
 | |
|                     TargetHistory targetHistory = (TargetHistory) elements
 | |
|                             .nextElement();
 | |
|                     if (targetHistory.getProcessorConfiguration().equals(
 | |
|                             configId)) {
 | |
|                         buf.setLength(0);
 | |
|                         buf.append("      <target file=\"");
 | |
|                         buf.append(CUtil.xmlAttribEncode(targetHistory
 | |
|                                 .getOutput()));
 | |
|                         buf.append("\" lastModified=\"");
 | |
|                         buf.append(Long.toHexString(targetHistory
 | |
|                                 .getOutputLastModified()));
 | |
|                         buf.append("\">\n");
 | |
|                         writer.write(buf.toString());
 | |
|                         SourceHistory[] sourceHistories = targetHistory
 | |
|                                 .getSources();
 | |
|                         for (int i = 0; i < sourceHistories.length; i++) {
 | |
|                             buf.setLength(0);
 | |
|                             buf.append("         <source file=\"");
 | |
|                             buf.append(CUtil.xmlAttribEncode(sourceHistories[i]
 | |
|                                     .getRelativePath()));
 | |
|                             buf.append("\" lastModified=\"");
 | |
|                             buf.append(Long.toHexString(sourceHistories[i]
 | |
|                                     .getLastModified()));
 | |
|                             buf.append("\"/>\n");
 | |
|                             writer.write(buf.toString());
 | |
|                         }
 | |
|                         writer.write("      </target>\n");
 | |
|                     }
 | |
|                 }
 | |
|                 writer.write("   </processor>\n");
 | |
|             }
 | |
|             writer.write("</history>\n");
 | |
|             writer.close();
 | |
|             dirty = false;
 | |
|         }
 | |
|     }
 | |
|     public TargetHistory get(String configId, String outputName) {
 | |
|         TargetHistory targetHistory = (TargetHistory) history.get(outputName);
 | |
|         if (targetHistory != null) {
 | |
|             if (!targetHistory.getProcessorConfiguration().equals(configId)) {
 | |
|                 targetHistory = null;
 | |
|             }
 | |
|         }
 | |
|         return targetHistory;
 | |
|     }
 | |
|     public void markForRebuild(Hashtable targetInfos) {
 | |
|         Enumeration targetInfoEnum = targetInfos.elements();
 | |
|         while (targetInfoEnum.hasMoreElements()) {
 | |
|             markForRebuild((TargetInfo) targetInfoEnum.nextElement());
 | |
|         }
 | |
|     }
 | |
|     public void markForRebuild(TargetInfo targetInfo) {
 | |
|         //
 | |
|         //     if it must already be rebuilt, no need to check further
 | |
|         //
 | |
|         if (!targetInfo.getRebuild()) {
 | |
|             TargetHistory history = get(targetInfo.getConfiguration()
 | |
|                     .toString(), targetInfo.getOutput().getName());
 | |
|             if (history == null) {
 | |
|                 targetInfo.mustRebuild();
 | |
|             } else {
 | |
|                 SourceHistory[] sourceHistories = history.getSources();
 | |
|                 File[] sources = targetInfo.getSources();
 | |
|                 if (sourceHistories.length != sources.length) {
 | |
|                     targetInfo.mustRebuild();
 | |
|                 } else {
 | |
|                     for (int i = 0; i < sourceHistories.length
 | |
|                             && !targetInfo.getRebuild(); i++) {
 | |
|                         //
 | |
|                         //   relative file name, must absolutize it on output
 | |
|                         // directory
 | |
|                         //
 | |
|                         boolean foundMatch = false;
 | |
|                         String historySourcePath = sourceHistories[i]
 | |
|                                 .getAbsolutePath(outputDir);
 | |
|                         for (int j = 0; j < sources.length; j++) {
 | |
|                             File targetSource = sources[j];
 | |
|                             String targetSourcePath = targetSource
 | |
|                                     .getAbsolutePath();
 | |
|                             if (targetSourcePath.equals(historySourcePath)) {
 | |
|                                 foundMatch = true;
 | |
|                                 if (targetSource.lastModified() != sourceHistories[i]
 | |
|                                         .getLastModified()) {
 | |
|                                     targetInfo.mustRebuild();
 | |
|                                     break;
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                         if (!foundMatch) {
 | |
|                             targetInfo.mustRebuild();
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     public void update(ProcessorConfiguration config, String[] sources) {
 | |
|         String configId = config.getIdentifier();
 | |
|         String[] onesource = new String[1];
 | |
|         String outputName;
 | |
|         for (int i = 0; i < sources.length; i++) {
 | |
|             onesource[0] = sources[i];
 | |
|             outputName = config.getOutputFileName(sources[i]);
 | |
|             update(configId, outputName, onesource);
 | |
|         }
 | |
|     }
 | |
|     private void update(String configId, String outputName, String[] sources) {
 | |
|         File outputFile = new File(outputDir, outputName);
 | |
|         //
 | |
|         //   if output file doesn't exist or predates the start of the
 | |
|         //        compile step (most likely a compilation error) then
 | |
|         //        do not write add a history entry
 | |
|         //
 | |
|         if (outputFile.exists()
 | |
|                 && outputFile.lastModified() >= historyFile.lastModified()) {
 | |
|             dirty = true;
 | |
|             history.remove(outputName);
 | |
|             SourceHistory[] sourceHistories = new SourceHistory[sources.length];
 | |
|             for (int i = 0; i < sources.length; i++) {
 | |
|                 File sourceFile = new File(sources[i]);
 | |
|                 long lastModified = sourceFile.lastModified();
 | |
|                 String relativePath = CUtil.getRelativePath(outputDirPath,
 | |
|                         sourceFile);
 | |
|                 sourceHistories[i] = new SourceHistory(relativePath,
 | |
|                         lastModified);
 | |
|             }
 | |
|             TargetHistory newHistory = new TargetHistory(configId, outputName,
 | |
|                     outputFile.lastModified(), sourceHistories);
 | |
|             history.put(outputName, newHistory);
 | |
|         }
 | |
|     }
 | |
|     public void update(TargetInfo linkTarget) {
 | |
|         File outputFile = linkTarget.getOutput();
 | |
|         String outputName = outputFile.getName();
 | |
|         //
 | |
|         //   if output file doesn't exist or predates the start of the
 | |
|         //        compile or link step (most likely a compilation error) then
 | |
|         //        do not write add a history entry
 | |
|         //
 | |
|         if (outputFile.exists()
 | |
|                 && outputFile.lastModified() >= historyFile.lastModified()) {
 | |
|             dirty = true;
 | |
|             history.remove(outputName);
 | |
|             SourceHistory[] sourceHistories = linkTarget
 | |
|                     .getSourceHistories(outputDirPath);
 | |
|             TargetHistory newHistory = new TargetHistory(linkTarget
 | |
|                     .getConfiguration().getIdentifier(), outputName, outputFile
 | |
|                     .lastModified(), sourceHistories);
 | |
|             history.put(outputName, newHistory);
 | |
|         }
 | |
|     }
 | |
| }
 |