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