]> git.proxmox.com Git - mirror_edk2.git/blobdiff - Tools/Java/Source/Cpptasks/net/sf/antcontrib/cpptasks/TargetHistoryTable.java
Restructuring for better separation of Tool packages.
[mirror_edk2.git] / Tools / Java / Source / Cpptasks / net / sf / antcontrib / cpptasks / TargetHistoryTable.java
diff --git a/Tools/Java/Source/Cpptasks/net/sf/antcontrib/cpptasks/TargetHistoryTable.java b/Tools/Java/Source/Cpptasks/net/sf/antcontrib/cpptasks/TargetHistoryTable.java
new file mode 100644 (file)
index 0000000..9fb0a7b
--- /dev/null
@@ -0,0 +1,426 @@
+/*\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