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