]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - Tools/Java/Source/GenBuild/org/tianocore/build/FfsProcess.java
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
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
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
22import java.util.Vector;\r
23\r
24import javax.xml.namespace.QName;\r
25\r
26import org.apache.tools.ant.BuildException;\r
27import org.apache.tools.ant.Project;\r
28import org.apache.xmlbeans.XmlCursor;\r
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
33import org.tianocore.common.definitions.EdkDefinitions;\r
34import org.tianocore.common.logger.EdkLog;\r
35import org.w3c.dom.Document;\r
36import org.w3c.dom.Element;\r
37\r
38/** \r
39 <p><code>FfsProcess</code> is a class to find the corresponding FFS layout. </p>\r
40 \r
41 <p>The FFS Layout is like following: </p>\r
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
61 private BuildOptionsDocument.BuildOptions.Ffs ffsXmlObject;\r
62\r
63 private Project project = null;\r
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
92 public static final String[][] sectionExt = EdkDefinitions.SectionTypeExtensions;\r
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
113 Find the corresponding FFS layout in <code>FPD</code>. \r
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
119 If can't find FFS Layout in FPD.\r
120 **/\r
121 public boolean initSections(String buildType, Project project, FpdModuleIdentification fpdModuleId) throws BuildException {\r
122 this.project = project;\r
123 //\r
124 // Try to find Ffs layout from FPD file\r
125 //\r
126 SurfaceAreaQuery saq = new SurfaceAreaQuery(GlobalData.getFpdBuildOptionsMap());\r
127 BuildOptionsDocument.BuildOptions.Ffs[] ffsArray = saq.getFpdFfs();\r
128 for (int i = 0; i < ffsArray.length; i++) {\r
129 if (isMatch(ffsArray[i].getFfsKey(), buildType)) {\r
130 ffsXmlObject = ffsArray[i];\r
131 genDigest();\r
132 return true;\r
133 }\r
134 }\r
135 \r
136 //\r
137 // If FfsFormatKey is not null, report exception and fail build\r
138 // Otherwise report warning message\r
139 //\r
140 if (buildType == null) {\r
141 EdkLog.log(EdkLog.EDK_WARNING, "Warning: this module doesn't specify a FfsFormatKey. ");\r
142 } else {\r
143 throw new BuildException("Can't find the FfsFormatKey [" + buildType + "] attribute in the FPD file!"); \r
144 }\r
145\r
146 return false;\r
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
160 if (ffsXmlObject == null) {\r
161 return new String[0];\r
162 }\r
163 Vector<String> sectionList = new Vector<String>();\r
164 XmlCursor cursor = null;\r
165\r
166 cursor = ffsXmlObject.newCursor();\r
167\r
168 int mode = MODE_NONE;\r
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
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
180 genffsfileEle.setAttribute(changeAttributeName(name), value);\r
181 } else if (cursor.getName().getLocalPart().equalsIgnoreCase("Section")) {\r
182 cursor.push();\r
183 dealSection(mode, document, genffsfileEle, cursor, sectionList);\r
184 cursor.pop();\r
185 } else if (cursor.getName().getLocalPart().equalsIgnoreCase("Sections")) {\r
186 cursor.push();\r
187 dealSections(mode, document, genffsfileEle, cursor, sectionList);\r
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
197 Vector<String> sections = new Vector<String>();\r
198 for (int i = 0; i < sectionList.size(); i++) {\r
199 String section = (String) sectionList.get(i);\r
200 if (isSectionType(section)) {\r
201 sections.addElement(section);\r
202 }\r
203 Element pathEle = document.createElement("file");\r
204 pathEle.setAttribute("name", getSectionFile(basename, section));\r
205 sourceEle.appendChild(pathEle);\r
206 }\r
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
214 String[] result = sections.toArray(new String[sections.size()]);\r
215\r
216 outofdateEle.appendChild(sourceEle);\r
217 Element targetEle = document.createElement("targetfiles");\r
218 Element fileEle = document.createElement("file");\r
219 fileEle.setAttribute("name", "${BIN_DIR}" + File.separatorChar + targetFilename);\r
220 targetEle.appendChild(fileEle);\r
221 outofdateEle.appendChild(targetEle);\r
222 Element sequentialEle = document.createElement("sequential");\r
223 sequentialEle.appendChild(genffsfileEle);\r
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
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
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
279 Element toolEle = null;\r
280 if (type.equalsIgnoreCase("COMPRESS") && (toolName == null || toolName.equalsIgnoreCase(""))) {\r
281 mode = MODE_COMPRESS;\r
282 //\r
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
288 } else {\r
289 mode = MODE_GUID_DEFINED;\r
290 //\r
291 // <gensection sectiontype="EFI_SECTION_GUID_DEFINED">\r
292 // \r
293 ele = doc.createElement("gensection");\r
294 if (type != null) {\r
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
301 } else {\r
302 ele.setAttribute("sectiontype", sectType);\r
303 }\r
304 //\r
305 // <tool toolName="${OEMTOOLPATH}\toolname"\r
306 // outputPath = "${DEST_DIR_OUTPUT}">\r
307 //\r
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
311 + File.separatorChar + "GenCRC32Section");\r
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
327 }\r
328 if (cursor.toFirstChild()) {\r
329 do {\r
330 if (cursor.getName().getLocalPart().equalsIgnoreCase("Section")) {\r
331 cursor.push();\r
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
338 cursor.pop();\r
339 } else if (cursor.getName().getLocalPart().equalsIgnoreCase("Sections")) {\r
340 cursor.push();\r
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
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
365 String alignment = cursor.getAttributeText(new QName("Alignment"));\r
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
393 } else {\r
394 list.addElement(fileName);\r
395 }\r
396\r
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
402 if (fileName == null) {\r
403 ele.setAttribute("file", getSectionFile(basename, type));\r
404 } else {\r
405 ele.setAttribute("file", fileName);\r
406 }\r
407 root.appendChild(ele);\r
408 } else {\r
409 //\r
410 // <sectFile fileName= "..."/>\r
411 //\r
412 Element ele = doc.createElement("sectFile");\r
413 if (fileName == null) {\r
414 ele.setAttribute("fileName", getSectionFile(basename, type));\r
415 } else {\r
416 ele.setAttribute("fileName", fileName);\r
417 }\r
418 if (alignment != null) {\r
419 ele.setAttribute("Alignment", alignment);\r
420 }\r
421 root.appendChild(ele);\r
422 }\r
423 }\r
424\r
425 /**\r
426 Get the corresponding section file suffix.\r
427 \r
428 @param type Section type\r
429 @return Corresponding section file extension\r
430 **/\r
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
441 for (int i = 0; i < sectionExt.length; i++) {\r
442 if (sectionExt[i][0].equalsIgnoreCase(type)) {\r
443 return true;\r
444 }\r
445 }\r
446 return false;\r
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
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
526}\r