mirror of https://github.com/acidanthera/audk.git
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);
|
|
}
|
|
}
|
|
}
|