]> git.proxmox.com Git - mirror_edk2.git/blame - Tools/Java/Source/Cpptasks/net/sf/antcontrib/cpptasks/TargetHistoryTable.java
Added code to check if "cmd" attribute is valid or not. This is to make error report...
[mirror_edk2.git] / Tools / Java / Source / Cpptasks / net / sf / antcontrib / cpptasks / TargetHistoryTable.java
CommitLineData
878ddf1f 1/*\r
2 * \r
3 * Copyright 2002-2004 The Ant-Contrib project\r
4 *\r
5 * Licensed under the Apache License, Version 2.0 (the "License");\r
6 * you may not use this file except in compliance with the License.\r
7 * You may obtain a copy of the License at\r
8 *\r
9 * http://www.apache.org/licenses/LICENSE-2.0\r
10 *\r
11 * Unless required by applicable law or agreed to in writing, software\r
12 * distributed under the License is distributed on an "AS IS" BASIS,\r
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
14 * See the License for the specific language governing permissions and\r
15 * limitations under the License.\r
16 */\r
17package net.sf.antcontrib.cpptasks;\r
18import java.io.BufferedWriter;\r
19import java.io.File;\r
20import java.io.FileOutputStream;\r
21import java.io.IOException;\r
22import java.io.OutputStreamWriter;\r
23import java.io.UnsupportedEncodingException;\r
24import java.util.Enumeration;\r
25import java.util.Hashtable;\r
26import java.util.Vector;\r
27\r
28import javax.xml.parsers.SAXParser;\r
29import javax.xml.parsers.SAXParserFactory;\r
30\r
31import net.sf.antcontrib.cpptasks.compiler.ProcessorConfiguration;\r
32\r
33import org.apache.tools.ant.BuildException;\r
34import org.xml.sax.Attributes;\r
35import org.xml.sax.SAXException;\r
36import org.xml.sax.helpers.DefaultHandler;\r
37/**\r
38 * A history of the compiler and linker settings used to build the files in the\r
39 * same directory as the history.\r
40 * \r
41 * @author Curt Arnold\r
42 */\r
43public final class TargetHistoryTable {\r
44 /**\r
45 * This class handles populates the TargetHistory hashtable in response to\r
46 * SAX parse events\r
47 */\r
48 private class TargetHistoryTableHandler extends DefaultHandler {\r
49 private final File baseDir;\r
50 private String config;\r
51 private final Hashtable history;\r
52 private String output;\r
53 private long outputLastModified;\r
54 private final Vector sources = new Vector();\r
55 /**\r
56 * Constructor\r
57 * \r
58 * @param history\r
59 * hashtable of TargetHistory keyed by output name\r
60 * @param outputFiles\r
61 * existing files in output directory\r
62 */\r
63 private TargetHistoryTableHandler(Hashtable history, File baseDir) {\r
64 this.history = history;\r
65 config = null;\r
66 output = null;\r
67 this.baseDir = baseDir;\r
68 }\r
69 public void endElement(String namespaceURI, String localName,\r
70 String qName) throws SAXException {\r
71 //\r
72 // if </target> then\r
73 // create TargetHistory object and add to hashtable\r
74 // if corresponding output file exists and\r
75 // has the same timestamp\r
76 //\r
77 if (qName.equals("target")) {\r
78 if (config != null && output != null) {\r
79 File existingFile = new File(baseDir, output);\r
80 //\r
81 // if the corresponding files doesn't exist or has a\r
82 // different\r
83 // modification time, then discard this record\r
84 if (existingFile.exists()) {\r
85 long existingLastModified = existingFile.lastModified();\r
86 //\r
87 // would have expected exact time stamps\r
88 // but have observed slight differences\r
89 // in return value for multiple evaluations of\r
90 // lastModified(). Check if times are within\r
91 // a second\r
92 long diff = outputLastModified - existingLastModified;\r
93 if (diff >= -500 && diff <= 500) {\r
94 SourceHistory[] sourcesArray = new SourceHistory[sources\r
95 .size()];\r
96 sources.copyInto(sourcesArray);\r
97 TargetHistory targetHistory = new TargetHistory(\r
98 config, output, outputLastModified,\r
99 sourcesArray);\r
100 history.put(output, targetHistory);\r
101 }\r
102 }\r
103 }\r
104 output = null;\r
105 sources.setSize(0);\r
106 } else {\r
107 //\r
108 // reset config so targets not within a processor element\r
109 // don't pick up a previous processors signature\r
110 //\r
111 if (qName.equals("processor")) {\r
112 config = null;\r
113 }\r
114 }\r
115 }\r
116 /**\r
117 * startElement handler\r
118 */\r
119 public void startElement(String namespaceURI, String localName,\r
120 String qName, Attributes atts) throws SAXException {\r
121 //\r
122 // if sourceElement\r
123 //\r
124 if (qName.equals("source")) {\r
125 String sourceFile = atts.getValue("file");\r
126 long sourceLastModified = Long.parseLong(atts\r
127 .getValue("lastModified"), 16);\r
128 sources.addElement(new SourceHistory(sourceFile,\r
129 sourceLastModified));\r
130 } else {\r
131 //\r
132 // if <target> element,\r
133 // grab file name and lastModified values\r
134 // TargetHistory object will be created in endElement\r
135 //\r
136 if (qName.equals("target")) {\r
137 sources.setSize(0);\r
138 output = atts.getValue("file");\r
139 outputLastModified = Long.parseLong(atts\r
140 .getValue("lastModified"), 16);\r
141 } else {\r
142 //\r
143 // if <processor> element,\r
144 // grab signature attribute\r
145 //\r
146 if (qName.equals("processor")) {\r
147 config = atts.getValue("signature");\r
148 }\r
149 }\r
150 }\r
151 }\r
152 }\r
153 /** Flag indicating whether the cache should be written back to file. */\r
154 private boolean dirty;\r
155 /**\r
156 * a hashtable of TargetHistory's keyed by output file name\r
157 */\r
158 private final Hashtable history = new Hashtable();\r
159 /** The file the cache was loaded from. */\r
160 private/* final */File historyFile;\r
161 private/* final */File outputDir;\r
162 private String outputDirPath;\r
163 /**\r
164 * Creates a target history table from history.xml in the output directory,\r
165 * if it exists. Otherwise, initializes the history table empty.\r
166 * \r
167 * @param task\r
168 * task used for logging history load errors\r
169 * @param outputDir\r
170 * output directory for task\r
171 */\r
172 public TargetHistoryTable(CCTask task, File outputDir)\r
173 throws BuildException {\r
174 if (outputDir == null) {\r
175 throw new NullPointerException("outputDir");\r
176 }\r
177 if (!outputDir.isDirectory()) {\r
178 throw new BuildException("Output directory is not a directory");\r
179 }\r
180 if (!outputDir.exists()) {\r
181 throw new BuildException("Output directory does not exist");\r
182 }\r
183 this.outputDir = outputDir;\r
184 try {\r
185 outputDirPath = outputDir.getCanonicalPath();\r
186 } catch (IOException ex) {\r
187 outputDirPath = outputDir.toString();\r
188 }\r
189 //\r
190 // load any existing history from file\r
191 // suppressing any records whose corresponding\r
192 // file does not exist, is zero-length or\r
193 // last modified dates differ\r
194 historyFile = new File(outputDir, "history.xml");\r
195 if (historyFile.exists()) {\r
196 SAXParserFactory factory = SAXParserFactory.newInstance();\r
197 factory.setValidating(false);\r
198 try {\r
199 SAXParser parser = factory.newSAXParser();\r
200 parser.parse(historyFile, new TargetHistoryTableHandler(\r
201 history, outputDir));\r
202 } catch (Exception ex) {\r
203 //\r
204 // a failure on loading this history is not critical\r
205 // but should be logged\r
206 task.log("Error reading history.xml: " + ex.toString());\r
207 }\r
208 } else {\r
209 //\r
210 // create empty history file for identifying new files by last\r
211 // modified\r
212 // timestamp comperation (to compare with\r
213 // System.currentTimeMillis() don't work on Unix, because it\r
214 // maesure timestamps only in seconds).\r
215 //\r
216 try {\r
217 FileOutputStream outputStream = new FileOutputStream(\r
218 historyFile);\r
219 byte[] historyElement = new byte[]{0x3C, 0x68, 0x69, 0x73,\r
220 0x74, 0x6F, 0x72, 0x79, 0x2F, 0x3E};\r
221 outputStream.write(historyElement);\r
222 outputStream.close();\r
223 } catch (IOException ex) {\r
224 throw new BuildException("Can't create history file", ex);\r
225 }\r
226 }\r
227 }\r
228 public void commit() throws IOException {\r
229 //\r
230 // if not dirty, no need to update file\r
231 //\r
232 if (dirty) {\r
233 //\r
234 // build (small) hashtable of config id's in history\r
235 //\r
236 Hashtable configs = new Hashtable(20);\r
237 Enumeration elements = history.elements();\r
238 while (elements.hasMoreElements()) {\r
239 TargetHistory targetHistory = (TargetHistory) elements\r
240 .nextElement();\r
241 String configId = targetHistory.getProcessorConfiguration();\r
242 if (configs.get(configId) == null) {\r
243 configs.put(configId, configId);\r
244 }\r
245 }\r
246 FileOutputStream outStream = new FileOutputStream(historyFile);\r
247 OutputStreamWriter outWriter;\r
248 //\r
249 // early VM's don't support UTF-8 encoding\r
250 // try and fallback to the default encoding\r
251 // otherwise\r
252 String encodingName = "UTF-8";\r
253 try {\r
254 outWriter = new OutputStreamWriter(outStream, "UTF-8");\r
255 } catch (UnsupportedEncodingException ex) {\r
256 outWriter = new OutputStreamWriter(outStream);\r
257 encodingName = outWriter.getEncoding();\r
258 }\r
259 BufferedWriter writer = new BufferedWriter(outWriter);\r
260 writer.write("<?xml version='1.0' encoding='");\r
261 writer.write(encodingName);\r
262 writer.write("'?>\n");\r
263 writer.write("<history>\n");\r
264 StringBuffer buf = new StringBuffer(200);\r
265 Enumeration configEnum = configs.elements();\r
266 while (configEnum.hasMoreElements()) {\r
267 String configId = (String) configEnum.nextElement();\r
268 buf.setLength(0);\r
269 buf.append(" <processor signature=\"");\r
270 buf.append(CUtil.xmlAttribEncode(configId));\r
271 buf.append("\">\n");\r
272 writer.write(buf.toString());\r
273 elements = history.elements();\r
274 while (elements.hasMoreElements()) {\r
275 TargetHistory targetHistory = (TargetHistory) elements\r
276 .nextElement();\r
277 if (targetHistory.getProcessorConfiguration().equals(\r
278 configId)) {\r
279 buf.setLength(0);\r
280 buf.append(" <target file=\"");\r
281 buf.append(CUtil.xmlAttribEncode(targetHistory\r
282 .getOutput()));\r
283 buf.append("\" lastModified=\"");\r
284 buf.append(Long.toHexString(targetHistory\r
285 .getOutputLastModified()));\r
286 buf.append("\">\n");\r
287 writer.write(buf.toString());\r
288 SourceHistory[] sourceHistories = targetHistory\r
289 .getSources();\r
290 for (int i = 0; i < sourceHistories.length; i++) {\r
291 buf.setLength(0);\r
292 buf.append(" <source file=\"");\r
293 buf.append(CUtil.xmlAttribEncode(sourceHistories[i]\r
294 .getRelativePath()));\r
295 buf.append("\" lastModified=\"");\r
296 buf.append(Long.toHexString(sourceHistories[i]\r
297 .getLastModified()));\r
298 buf.append("\"/>\n");\r
299 writer.write(buf.toString());\r
300 }\r
301 writer.write(" </target>\n");\r
302 }\r
303 }\r
304 writer.write(" </processor>\n");\r
305 }\r
306 writer.write("</history>\n");\r
307 writer.close();\r
308 dirty = false;\r
309 }\r
310 }\r
311 public TargetHistory get(String configId, String outputName) {\r
312 TargetHistory targetHistory = (TargetHistory) history.get(outputName);\r
313 if (targetHistory != null) {\r
314 if (!targetHistory.getProcessorConfiguration().equals(configId)) {\r
315 targetHistory = null;\r
316 }\r
317 }\r
318 return targetHistory;\r
319 }\r
320 public void markForRebuild(Hashtable targetInfos) {\r
321 Enumeration targetInfoEnum = targetInfos.elements();\r
322 while (targetInfoEnum.hasMoreElements()) {\r
323 markForRebuild((TargetInfo) targetInfoEnum.nextElement());\r
324 }\r
325 }\r
326 public void markForRebuild(TargetInfo targetInfo) {\r
327 //\r
328 // if it must already be rebuilt, no need to check further\r
329 //\r
330 if (!targetInfo.getRebuild()) {\r
331 TargetHistory history = get(targetInfo.getConfiguration()\r
332 .toString(), targetInfo.getOutput().getName());\r
333 if (history == null) {\r
334 targetInfo.mustRebuild();\r
335 } else {\r
336 SourceHistory[] sourceHistories = history.getSources();\r
337 File[] sources = targetInfo.getSources();\r
338 if (sourceHistories.length != sources.length) {\r
339 targetInfo.mustRebuild();\r
340 } else {\r
341 for (int i = 0; i < sourceHistories.length\r
342 && !targetInfo.getRebuild(); i++) {\r
343 //\r
344 // relative file name, must absolutize it on output\r
345 // directory\r
346 //\r
347 boolean foundMatch = false;\r
348 String historySourcePath = sourceHistories[i]\r
349 .getAbsolutePath(outputDir);\r
350 for (int j = 0; j < sources.length; j++) {\r
351 File targetSource = sources[j];\r
352 String targetSourcePath = targetSource\r
353 .getAbsolutePath();\r
354 if (targetSourcePath.equals(historySourcePath)) {\r
355 foundMatch = true;\r
356 if (targetSource.lastModified() != sourceHistories[i]\r
357 .getLastModified()) {\r
358 targetInfo.mustRebuild();\r
359 break;\r
360 }\r
361 }\r
362 }\r
363 if (!foundMatch) {\r
364 targetInfo.mustRebuild();\r
365 }\r
366 }\r
367 }\r
368 }\r
369 }\r
370 }\r
371 public void update(ProcessorConfiguration config, String[] sources) {\r
372 String configId = config.getIdentifier();\r
373 String[] onesource = new String[1];\r
374 String outputName;\r
375 for (int i = 0; i < sources.length; i++) {\r
376 onesource[0] = sources[i];\r
377 outputName = config.getOutputFileName(sources[i]);\r
378 update(configId, outputName, onesource);\r
379 }\r
380 }\r
381 private void update(String configId, String outputName, String[] sources) {\r
382 File outputFile = new File(outputDir, outputName);\r
383 //\r
384 // if output file doesn't exist or predates the start of the\r
385 // compile step (most likely a compilation error) then\r
386 // do not write add a history entry\r
387 //\r
388 if (outputFile.exists()\r
389 && outputFile.lastModified() >= historyFile.lastModified()) {\r
390 dirty = true;\r
391 history.remove(outputName);\r
392 SourceHistory[] sourceHistories = new SourceHistory[sources.length];\r
393 for (int i = 0; i < sources.length; i++) {\r
394 File sourceFile = new File(sources[i]);\r
395 long lastModified = sourceFile.lastModified();\r
396 String relativePath = CUtil.getRelativePath(outputDirPath,\r
397 sourceFile);\r
398 sourceHistories[i] = new SourceHistory(relativePath,\r
399 lastModified);\r
400 }\r
401 TargetHistory newHistory = new TargetHistory(configId, outputName,\r
402 outputFile.lastModified(), sourceHistories);\r
403 history.put(outputName, newHistory);\r
404 }\r
405 }\r
406 public void update(TargetInfo linkTarget) {\r
407 File outputFile = linkTarget.getOutput();\r
408 String outputName = outputFile.getName();\r
409 //\r
410 // if output file doesn't exist or predates the start of the\r
411 // compile or link step (most likely a compilation error) then\r
412 // do not write add a history entry\r
413 //\r
414 if (outputFile.exists()\r
415 && outputFile.lastModified() >= historyFile.lastModified()) {\r
416 dirty = true;\r
417 history.remove(outputName);\r
418 SourceHistory[] sourceHistories = linkTarget\r
419 .getSourceHistories(outputDirPath);\r
420 TargetHistory newHistory = new TargetHistory(linkTarget\r
421 .getConfiguration().getIdentifier(), outputName, outputFile\r
422 .lastModified(), sourceHistories);\r
423 history.put(outputName, newHistory);\r
424 }\r
425 }\r
426}\r