Add genDigest() method to class FfsProcess to generate MD5 digest value for the FFS...
[mirror_edk2.git] / Tools / Java / Source / GenBuild / org / tianocore / build / FfsProcess.java
1 /** @file
2 File is FfsProcess class which is used to get the corresponding FFS layout
3 information for driver module.
4
5 Copyright (c) 2006, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 **/
14 package org.tianocore.build;
15
16 import java.io.File;
17 import java.io.FileInputStream;
18 import java.io.FileOutputStream;
19 import java.io.FileReader;
20 import java.io.FileWriter;
21 import java.security.MessageDigest;
22 import java.util.Vector;
23
24 import javax.xml.namespace.QName;
25
26 import org.apache.tools.ant.BuildException;
27 import org.apache.tools.ant.Project;
28 import org.apache.xmlbeans.XmlCursor;
29 import org.tianocore.BuildOptionsDocument;
30 import org.tianocore.build.global.GlobalData;
31 import org.tianocore.build.global.SurfaceAreaQuery;
32 import org.tianocore.build.id.FpdModuleIdentification;
33 import org.tianocore.common.definitions.EdkDefinitions;
34 import org.tianocore.common.logger.EdkLog;
35 import org.w3c.dom.Document;
36 import org.w3c.dom.Element;
37
38 /**
39 <p><code>FfsProcess</code> is a class to find the corresponding FFS layout. </p>
40
41 <p>The FFS Layout is like following: </p>
42
43 <pre>
44 &lt;Ffs type="APPLICATION"&gt;
45 &lt;Attribute Name="FFS_FILETYPE" Value="EFI_FV_FILETYPE_APPLICATION" /&gt;
46 &lt;Attribute Name="FFS_ATTRIB_CHECKSUM" Value="TRUE" /&gt;
47 &lt;Sections EncapsulationType="Compress"&gt;
48 &lt;Sections EncapsulationType="Guid-Defined"&gt;
49 &lt;Section SectionType="EFI_SECTION_PE32" /&gt;
50 &lt;Section SectionType="EFI_SECTION_USER_INTERFACE" /&gt;
51 &lt;Section SectionType="EFI_SECTION_VERSION" /&gt;
52 &lt;/Sections&gt;
53 &lt;/Sections&gt;
54 &lt;/Ffs&gt;
55 </pre>
56
57 @since GenBuild 1.0
58 **/
59 public class FfsProcess {
60
61 private BuildOptionsDocument.BuildOptions.Ffs ffsXmlObject;
62
63 private Project project = null;
64 ///
65 /// ANT script to call GenFfs
66 ///
67 private Element ffsNode = null;
68
69 ///
70 /// Module base name
71 ///
72 private String basename;
73
74 ///
75 /// Sections type: normal
76 ///
77 private static int MODE_NONE = 0;
78
79 ///
80 /// Sections type: compress
81 ///
82 private static int MODE_COMPRESS = 1;
83
84 ///
85 /// Sections type: guid-define
86 ///
87 private static int MODE_GUID_DEFINED = 2;
88
89 ///
90 /// mapping from section type to section output file extension
91 ///
92 public static final String[][] sectionExt = EdkDefinitions.SectionTypeExtensions;
93
94 /**
95 search in the type, if componentType is listed in type, return true;
96 otherwise return false.
97
98 @param type a list supported component type separated by comma
99 @param componentType current module component type
100 @return whether componentType is one of type
101 **/
102 private boolean isMatch(String type, String componentType) {
103 String[] items = type.split("[ \t]*,[ \t]*");
104 for (int i = 0; i < items.length; i++) {
105 if (items[i].equalsIgnoreCase(componentType)) {
106 return true;
107 }
108 }
109 return false;
110 }
111
112 /**
113 Find the corresponding FFS layout in <code>FPD</code>.
114
115 @param buildType Current module's component type
116 @param project Ant project
117 @return whether find the corresponding FFS layout
118 @throws BuildException
119 If can't find FFS Layout in FPD.
120 **/
121 public boolean initSections(String buildType, Project project, FpdModuleIdentification fpdModuleId) throws BuildException {
122 this.project = project;
123 //
124 // Try to find Ffs layout from FPD file
125 //
126 SurfaceAreaQuery saq = new SurfaceAreaQuery(GlobalData.getFpdBuildOptionsMap());
127 BuildOptionsDocument.BuildOptions.Ffs[] ffsArray = saq.getFpdFfs();
128 for (int i = 0; i < ffsArray.length; i++) {
129 if (isMatch(ffsArray[i].getFfsKey(), buildType)) {
130 ffsXmlObject = ffsArray[i];
131 genDigest();
132 return true;
133 }
134 }
135
136 //
137 // If FfsFormatKey is not null, report exception and fail build
138 // Otherwise report warning message
139 //
140 if (buildType == null) {
141 EdkLog.log(EdkLog.EDK_WARNING, "Warning: this module doesn't specify a FfsFormatKey. ");
142 } else {
143 throw new BuildException("Can't find the FfsFormatKey [" + buildType + "] attribute in the FPD file!");
144 }
145
146 return false;
147 }
148
149 /**
150 Recursive parse the FFS layout. Find out all section type here used.
151
152 @param document BaseName_build.xml Xml document
153 @param basename Module's base name
154 @param guid Module's GUID
155 @param targetFilename Module's final file name (GUID-BaseName.APP)
156 @return List of section type
157 **/
158 public String[] getGenSectionElements(Document document, String basename, String guid, String targetFilename) {
159 this.basename = basename;
160 if (ffsXmlObject == null) {
161 return new String[0];
162 }
163 Vector<String> sectionList = new Vector<String>();
164 XmlCursor cursor = null;
165
166 cursor = ffsXmlObject.newCursor();
167
168 int mode = MODE_NONE;
169 Element genffsfileEle = document.createElement("genffsfile");
170 genffsfileEle.setAttribute("outputDir", "${BIN_DIR}");
171 genffsfileEle.setAttribute("moduleType", "${MODULE_TYPE}");
172 genffsfileEle.setAttribute("BaseName", basename);
173 genffsfileEle.setAttribute("fileGuid", guid);
174
175 if (cursor.toFirstChild()) {
176 do {
177 if (cursor.getName().getLocalPart().equalsIgnoreCase("Attribute")) {
178 String name = cursor.getAttributeText(new QName("Name"));
179 String value = cursor.getAttributeText(new QName("Value"));
180 genffsfileEle.setAttribute(changeAttributeName(name), value);
181 } else if (cursor.getName().getLocalPart().equalsIgnoreCase("Section")) {
182 cursor.push();
183 dealSection(mode, document, genffsfileEle, cursor, sectionList);
184 cursor.pop();
185 } else if (cursor.getName().getLocalPart().equalsIgnoreCase("Sections")) {
186 cursor.push();
187 dealSections(mode, document, genffsfileEle, cursor, sectionList);
188 cursor.pop();
189 }
190 } while (cursor.toNextSibling());
191 }
192 //
193 // Check dependency
194 //
195 Element outofdateEle = document.createElement("OnDependency");
196 Element sourceEle = document.createElement("sourcefiles");
197 Vector<String> sections = new Vector<String>();
198 for (int i = 0; i < sectionList.size(); i++) {
199 String section = (String) sectionList.get(i);
200 if (isSectionType(section)) {
201 sections.addElement(section);
202 }
203 Element pathEle = document.createElement("file");
204 pathEle.setAttribute("name", getSectionFile(basename, section));
205 sourceEle.appendChild(pathEle);
206 }
207 //
208 // add FFS layout digest into the source file list
209 //
210 Element pathEle = document.createElement("file");
211 pathEle.setAttribute("name", "${DEST_DIR_OUTPUT}" + File.separator + "ffs.md5");
212 sourceEle.appendChild(pathEle);
213
214 String[] result = sections.toArray(new String[sections.size()]);
215
216 outofdateEle.appendChild(sourceEle);
217 Element targetEle = document.createElement("targetfiles");
218 Element fileEle = document.createElement("file");
219 fileEle.setAttribute("name", "${BIN_DIR}" + File.separatorChar + targetFilename);
220 targetEle.appendChild(fileEle);
221 outofdateEle.appendChild(targetEle);
222 Element sequentialEle = document.createElement("sequential");
223 sequentialEle.appendChild(genffsfileEle);
224 outofdateEle.appendChild(sequentialEle);
225 ffsNode = outofdateEle;
226 return result;
227 }
228
229 /**
230 Change the attribute name. For example:
231
232 <pre>
233 Before change: FFS_ATTRIB_CHECKSUM
234 After change: ffsATTRIBCHECKSUM
235 </pre>
236
237 @param name Original attribute name
238 @return Changed attribute name
239 **/
240 private String changeAttributeName(String name) {
241 String[] strs = name.split("_");
242 String str = strs[0].toLowerCase();
243 for (int j = 1; j < strs.length; j++) {
244 str += strs[j];
245 }
246 return str;
247 }
248
249 /**
250 Recursively deal with Sections. If sections does not specify a type, then omit it.
251
252 @param mode Current node mode (MODE_NONE | MODE_COMPREE | MODE_GUID_DEFINED)
253 @param doc Xml Document
254 @param root Root Node
255 @param cursor Current FFS layout cursor
256 @param list List of section type here used
257 **/
258 private void dealSections(int mode, Document doc, Element root, XmlCursor cursor, Vector<String> list) {
259 String type = cursor.getAttributeText(new QName("EncapsulationType"));
260 String toolName = cursor.getAttributeText(new QName("ToolName"));
261 String sectType = cursor.getAttributeText(new QName("SectionType"));
262 if (type == null && sectType == null) {
263 if (cursor.toFirstChild()) {
264 do {
265 if (cursor.getName().getLocalPart().equalsIgnoreCase("Section")) {
266 cursor.push();
267 dealSection(mode, doc, root, cursor, list);
268 cursor.pop();
269 } else if (cursor.getName().getLocalPart().equalsIgnoreCase("Sections")) {
270 cursor.push();
271 dealSections(mode, doc, root, cursor, list);
272 cursor.pop();
273 }
274 } while (cursor.toNextSibling());
275 }
276 return;
277 }
278 Element ele;
279 Element toolEle = null;
280 if (type.equalsIgnoreCase("COMPRESS") && (toolName == null || toolName.equalsIgnoreCase(""))) {
281 mode = MODE_COMPRESS;
282 //
283 // <gensection sectiontype="EFI_SECTION_COMPRESSION">
284 //
285 ele = doc.createElement("gensection");
286 ele.setAttribute("sectionType", "EFI_SECTION_COMPRESSION");
287
288 } else {
289 mode = MODE_GUID_DEFINED;
290 //
291 // <gensection sectiontype="EFI_SECTION_GUID_DEFINED">
292 //
293 ele = doc.createElement("gensection");
294 if (type != null) {
295 if (type.equalsIgnoreCase("COMPRESS")) {
296 ele.setAttribute("sectionType", "EFI_SECTION_COMPRESSION");
297 }else {
298 ele.setAttribute("sectiontype", "EFI_SECTION_GUID_DEFINED");
299 }
300
301 } else {
302 ele.setAttribute("sectiontype", sectType);
303 }
304 //
305 // <tool toolName="${OEMTOOLPATH}\toolname"
306 // outputPath = "${DEST_DIR_OUTPUT}">
307 //
308 toolEle = doc.createElement("tool");
309 if (toolName == null || toolName.equalsIgnoreCase("")) {
310 toolEle.setAttribute("toolName", "${WORKSPACE_DIR}" + File.separatorChar + "Tools" + File.separatorChar + "bin"
311 + File.separatorChar + "GenCRC32Section");
312 }else{
313 File toolExe = new File(toolName);
314 //
315 // If <Tool> element exist, add sub element under <tool> .
316 //
317 if (toolExe.isAbsolute()) {
318 toolEle.setAttribute("toolName", toolName);
319 } else {
320 toolEle.setAttribute("toolName", "${WORKSPACE_DIR}" + File.separatorChar + "Tools" + File.separatorChar + "bin"
321 + File.separatorChar + toolName);
322 }
323 }
324
325 toolEle.setAttribute("outputPath", "${DEST_DIR_OUTPUT}");
326 ele.appendChild(toolEle);
327 }
328 if (cursor.toFirstChild()) {
329 do {
330 if (cursor.getName().getLocalPart().equalsIgnoreCase("Section")) {
331 cursor.push();
332 if (toolEle == null) {
333 dealSection(mode, doc, ele, cursor, list);
334 } else {
335 dealSection(mode, doc, toolEle, cursor, list);
336 }
337
338 cursor.pop();
339 } else if (cursor.getName().getLocalPart().equalsIgnoreCase("Sections")) {
340 cursor.push();
341 if (toolEle == null) {
342 dealSections(mode, doc, ele, cursor, list);
343 } else {
344 dealSections(mode, doc, toolEle, cursor, list);
345 }
346
347 cursor.pop();
348 }
349 } while (cursor.toNextSibling());
350 }
351 root.appendChild(ele);
352 }
353
354 /**
355 Recursively deal with section.
356
357 @param mode Current node mode (MODE_NONE | MODE_COMPREE | MODE_GUID_DEFINED)
358 @param doc Xml Document
359 @param root Root Node
360 @param cursor Current FFS layout cursor
361 @param list List of section type here used
362 **/
363 private void dealSection(int mode, Document doc, Element root, XmlCursor cursor, Vector<String> list) {
364 String type = cursor.getAttributeText(new QName("SectionType"));
365 String alignment = cursor.getAttributeText(new QName("Alignment"));
366
367 //
368 // Judge if file is specified? Yes, just use the file, else call Build Macro
369 // If fileName is null, means without FileNames specify in FPD file
370 //
371 String fileName = null;
372 cursor.push();
373 if (cursor.toFirstChild()) {
374 do {
375 if (cursor.getName().getLocalPart().equalsIgnoreCase("Filenames")) {
376 cursor.push();
377 if (cursor.toFirstChild()) {
378 do {
379 if (cursor.getName().getLocalPart().equalsIgnoreCase("Filename")) {
380 fileName = cursor.getTextValue();
381 }
382 } while (cursor.toNextSibling());
383 }
384 cursor.pop();
385 }
386 } while (cursor.toNextSibling());
387 }
388
389 cursor.pop();
390
391 if (fileName == null) {
392 list.addElement(type);
393 } else {
394 list.addElement(fileName);
395 }
396
397 if (mode == MODE_GUID_DEFINED) {
398 //
399 // <input file="${DEST_DIR_OUTPUT}\Bds.pe32"/>
400 //
401 Element ele = doc.createElement("input");
402 if (fileName == null) {
403 ele.setAttribute("file", getSectionFile(basename, type));
404 } else {
405 ele.setAttribute("file", fileName);
406 }
407 root.appendChild(ele);
408 } else {
409 //
410 // <sectFile fileName= "..."/>
411 //
412 Element ele = doc.createElement("sectFile");
413 if (fileName == null) {
414 ele.setAttribute("fileName", getSectionFile(basename, type));
415 } else {
416 ele.setAttribute("fileName", fileName);
417 }
418 if (alignment != null) {
419 ele.setAttribute("Alignment", alignment);
420 }
421 root.appendChild(ele);
422 }
423 }
424
425 /**
426 Get the corresponding section file suffix.
427
428 @param type Section type
429 @return Corresponding section file extension
430 **/
431 private String getSectionFile(String basename, String type) {
432 for (int i = 0; i < sectionExt.length; i++) {
433 if (sectionExt[i][0].equalsIgnoreCase(type)) {
434 return "${DEST_DIR_OUTPUT}" + File.separatorChar + basename + sectionExt[i][1];
435 }
436 }
437 return type;
438 }
439
440 private boolean isSectionType(String type) {
441 for (int i = 0; i < sectionExt.length; i++) {
442 if (sectionExt[i][0].equalsIgnoreCase(type)) {
443 return true;
444 }
445 }
446 return false;
447 }
448
449 /**
450 Return the ANT script to call GenFfs Tool.
451
452 @return ANT script to call GenFfs Tool
453 **/
454 public Element getFfsNode() {
455 return ffsNode;
456 }
457
458 private void genDigest() {
459 String digestFilePath = project.getProperty("DEST_DIR_OUTPUT");
460 if (digestFilePath == null) {
461 EdkLog.log(EdkLog.EDK_WARNING, "Warning: cannot get DEST_DIR_OUTPUT!");
462 return;
463 }
464
465 //
466 // use MD5 algorithm
467 //
468 MessageDigest md5 = null;
469 try {
470 md5 = MessageDigest.getInstance("MD5");
471 //
472 // convert the FFS layout XML DOM tree into string and use it to
473 // calculate its MD5 digest value
474 //
475 md5.update(ffsXmlObject.xmlText().getBytes());
476 } catch (Exception e) {
477 EdkLog.log(EdkLog.EDK_WARNING, "Warning: " + e.getMessage());
478 return;
479 }
480
481 //
482 // get the MD5 digest value
483 //
484 byte[] digest = md5.digest();
485
486 //
487 // put the digest in a file named "ffs.md5" if it doesn't exist, otherwise
488 // we will compare the digest in the file with the one just calculated
489 //
490 digestFilePath += File.separator + "ffs.md5";
491 File digestFile = new File(digestFilePath);
492 if (digestFile.exists()) {
493 byte[] oldDigest = new byte[digest.length];
494 try {
495 FileInputStream fIn = new FileInputStream(digestFile);
496 fIn.read(oldDigest);
497 fIn.close();
498 } catch (Exception e) {
499 throw new BuildException(e.getMessage());
500 }
501
502 boolean noChange = true;
503 for (int i = 0; i < oldDigest.length; ++i) {
504 if (digest[i] != oldDigest[i]) {
505 noChange = false;
506 break;
507 }
508 }
509
510 if (noChange) {
511 return;
512 }
513 }
514
515 //
516 // update the "ffs.md5" file content with new digest value
517 //
518 try {
519 FileOutputStream fOut = new FileOutputStream(digestFile);
520 fOut.write(digest);
521 fOut.close();
522 } catch (Exception e) {
523 throw new BuildException(e.getMessage());
524 }
525 }
526 }