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