cdd88147c3e9bf83b5a955def6e8f8ceea93845a
[mirror_edk2.git] / Tools / Source / GenBuild / org / tianocore / build / ModuleBuildFileGenerator.java
1 /** @file
2
3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 **/
12 package org.tianocore.build;
13
14 import java.io.File;
15 import java.util.LinkedHashMap;
16 import java.util.LinkedHashSet;
17 import java.util.Map;
18 import java.util.Set;
19 import java.io.FileOutputStream;
20 import java.io.OutputStreamWriter;
21
22 import javax.xml.parsers.DocumentBuilder;
23 import javax.xml.parsers.DocumentBuilderFactory;
24 import javax.xml.transform.OutputKeys;
25 import javax.xml.transform.Result;
26 import javax.xml.transform.Source;
27 import javax.xml.transform.Transformer;
28 import javax.xml.transform.TransformerFactory;
29 import javax.xml.transform.dom.DOMSource;
30 import javax.xml.transform.stream.StreamResult;
31
32 import org.apache.tools.ant.BuildException;
33 import org.apache.tools.ant.Project;
34 import org.tianocore.build.fpd.FpdParserTask;
35 import org.tianocore.build.global.GlobalData;
36 import org.tianocore.build.global.SurfaceAreaQuery;
37 import org.tianocore.build.id.FpdModuleIdentification;
38 import org.tianocore.build.id.ModuleIdentification;
39 import org.tianocore.build.id.PackageIdentification;
40 import org.w3c.dom.Comment;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Element;
43 import org.w3c.dom.Node;
44
45 public class ModuleBuildFileGenerator {
46
47 ///
48 /// Pass: TARGET, TOOLCHAIN, ARCH
49 /// PACKAGE, PACKAGE_GUID, PACKAGE_VERSION
50 ///
51 String[] inheritProperties = {"ARCH", "MODULE_GUID", "MODULE_VERSION", "PLATFORM_FILE", "PACKAGE_GUID", "PACKAGE_VERSION"};
52
53 ///
54 /// The information at the header of <em>build.xml</em>.
55 ///
56 private String info = "DO NOT EDIT \n"
57 + "File auto-generated by build utility\n"
58 + "\n"
59 + "Abstract:\n"
60 + "Auto-generated ANT build file for building of EFI Modules/Platforms\n";
61
62 private FpdModuleIdentification fpdModuleId;
63
64 private Project project;
65
66 private String ffsKeyword;
67
68 public ModuleBuildFileGenerator(Project project, String ffsKeyword, FpdModuleIdentification fpdModuleId) {
69 this.project = project;
70 this.fpdModuleId = fpdModuleId;
71 this.ffsKeyword = ffsKeyword;
72 }
73
74 /**
75 The whole BaseName_build.xml is composed of seven part.
76 <ul>
77 <li> ANT properties; </li>
78 <li> Dependent module (dependent library instances in most case); </li>
79 <li> Source files; </li>
80 <li> Sections if module is not library; </li>
81 <li> Output (different for library module and driver module); </li>
82 <li> Clean; </li>
83 <li> Clean all. </li>
84 </ul>
85
86 @throws BuildException
87 Error throws during BaseName_build.xml generating.
88 **/
89 public void genBuildFile(String buildFilename) throws BuildException {
90 FfsProcess fp = new FfsProcess();
91 DocumentBuilderFactory domfac = DocumentBuilderFactory.newInstance();
92 try {
93 DocumentBuilder dombuilder = domfac.newDocumentBuilder();
94 Document document = dombuilder.newDocument();
95 Comment rootComment = document.createComment(info);
96
97 //
98 // create root element and its attributes
99 //
100 Element root = document.createElement("project");
101 root.setAttribute("name", fpdModuleId.getModule().getName());
102 root.setAttribute("default", "all");
103 root.setAttribute("basedir", ".");
104
105 //
106 // element for External ANT tasks
107 //
108 root.appendChild(document.createComment("Apply external ANT tasks"));
109 Element ele = document.createElement("taskdef");
110 ele.setAttribute("resource", "frameworktasks.tasks");
111 root.appendChild(ele);
112 ele = document.createElement("taskdef");
113 ele.setAttribute("resource", "cpptasks.tasks");
114 root.appendChild(ele);
115 ele = document.createElement("typedef");
116 ele.setAttribute("resource", "cpptasks.types");
117 root.appendChild(ele);
118 ele = document.createElement("taskdef");
119 ele.setAttribute("resource", "net/sf/antcontrib/antlib.xml");
120 root.appendChild(ele);
121
122 //
123 // Generate the default target,
124 // which depends on init, sections and output target
125 //
126 root.appendChild(document.createComment("Default target"));
127 ele = document.createElement("target");
128 ele.setAttribute("name", "all");
129 ele.setAttribute("depends", "libraries, sourcefiles, sections, output");
130 root.appendChild(ele);
131
132 //
133 // compile all source files
134 //
135 root.appendChild(document.createComment("Compile all dependency Library instances."));
136 ele = document.createElement("target");
137 ele.setAttribute("name", "libraries");
138
139 //
140 // Parse all sourfiles but files specified in sections
141 //
142 applyLibraryInstance(document, ele);
143 root.appendChild(ele);
144
145 //
146 // compile all source files
147 //
148 root.appendChild(document.createComment("sourcefiles target"));
149 ele = document.createElement("target");
150 ele.setAttribute("name", "sourcefiles");
151
152 //
153 // Parse all sourfiles but files specified in sections
154 //
155 applyCompileElement(document, ele);
156 root.appendChild(ele);
157
158 //
159 // generate the init target
160 // main purpose is create all nessary pathes
161 // generate the sections target
162 //
163 root.appendChild(document.createComment("sections target"));
164 ele = document.createElement("target");
165 ele.setAttribute("name", "sections");
166 applySectionsElement(document, ele, fp);
167 root.appendChild(ele);
168
169 //
170 // generate the output target
171 //
172 root.appendChild(document.createComment("output target"));
173 ele = document.createElement("target");
174 ele.setAttribute("name", "output");
175 applyOutputElement(document, ele, fp);
176 root.appendChild(ele);
177
178
179 //
180 // generate the clean target
181 //
182 root.appendChild(document.createComment("clean target"));
183 ele = document.createElement("target");
184 ele.setAttribute("name", "clean");
185 applyCleanElement(document, ele);
186 root.appendChild(ele);
187
188 //
189 // generate the Clean All target
190 //
191 root.appendChild(document.createComment("Clean All target"));
192 ele = document.createElement("target");
193 ele.setAttribute("name", "cleanall");
194 applyDeepcleanElement(document, ele);
195 root.appendChild(ele);
196
197 //
198 // add the root element to the document
199 //
200 document.appendChild(rootComment);
201 document.appendChild(root);
202 //
203 // Prepare the DOM document for writing
204 //
205 Source source = new DOMSource(document);
206
207 //
208 // Prepare the output file
209 //
210 File file = new File(buildFilename);
211
212 //
213 // generate all directory path
214 //
215 (new File(file.getParent())).mkdirs();
216 FileOutputStream outputStream = new FileOutputStream(file);
217 Result result = new StreamResult(new OutputStreamWriter(outputStream));
218
219 //
220 // Write the DOM document to the file
221 //
222 Transformer xformer = TransformerFactory.newInstance().newTransformer();
223 xformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
224 xformer.setOutputProperty(OutputKeys.INDENT, "yes");
225 xformer.transform(source, result);
226 } catch (Exception ex) {
227 throw new BuildException("Module [" + fpdModuleId.getModule().getName() + "] generating build file failed.\n" + ex.getMessage());
228 }
229 }
230
231 /**
232 Generate the clean elements for BaseName_build.xml.
233
234 @param document current BaseName_build.xml XML document
235 @param root Root element for current
236 **/
237 private void applyCleanElement(Document document, Node root) {
238 //
239 // <delete includeemptydirs="true">
240 // <fileset dir="${DEST_DIR_OUTPUT}" includes="" excludes="" />
241 // </delete>
242 //
243 Element deleteEle = document.createElement("delete");
244 deleteEle.setAttribute("includeemptydirs", "true");
245 Element filesetEle = document.createElement("fileset");
246 filesetEle.setAttribute("dir", "${DEST_DIR_OUTPUT}");
247 filesetEle.setAttribute("includes", "**/*");
248 filesetEle.setAttribute("excludes", "*.xml");
249 deleteEle.appendChild(filesetEle);
250 root.appendChild(deleteEle);
251 }
252
253 /**
254 Generate the cleanall elements for BaseName_build.xml.
255
256 @param document current BaseName_build.xml XML document
257 @param root Root element for current
258 **/
259 private void applyDeepcleanElement(Document document, Node root) {
260 //
261 // <delete includeemptydirs="true">
262 // <fileset dir="${DEST_DIR_OUTPUT}" includes="" excludes="" />
263 // </delete>
264 //
265 Element deleteEle = document.createElement("delete");
266 deleteEle.setAttribute("includeemptydirs", "true");
267 Element filesetEle = document.createElement("fileset");
268 filesetEle.setAttribute("dir", "${DEST_DIR_OUTPUT}");
269 filesetEle.setAttribute("includes", "**/*");
270 filesetEle.setAttribute("excludes", "*.xml");
271 deleteEle.appendChild(filesetEle);
272 root.appendChild(deleteEle);
273
274 //
275 // <delete includeemptydirs="true">
276 // <fileset dir="${DEST_DIR_DEBUG}" includes="" />
277 // </delete>
278 //
279 deleteEle = document.createElement("delete");
280 deleteEle.setAttribute("includeemptydirs", "true");
281 filesetEle = document.createElement("fileset");
282 filesetEle.setAttribute("dir", "${DEST_DIR_DEBUG}");
283 filesetEle.setAttribute("includes", "**/*");
284 deleteEle.appendChild(filesetEle);
285 root.appendChild(deleteEle);
286 }
287
288 /**
289 Generate the dependent library instances elements for BaseName_build.xml.
290
291 @param document current BaseName_build.xml XML document
292 @param root Root element for current
293 **/
294 private void applyLibraryInstance(Document document, Node root) {
295 ModuleIdentification[] libinstances = SurfaceAreaQuery.getLibraryInstance(fpdModuleId.getArch());
296 for (int i = 0; i < libinstances.length; i++) {
297 //
298 // Put package file path to module identification
299 //
300 PackageIdentification packageId = libinstances[i].getPackage();
301
302 //
303 // Generate ANT script to build library instances
304 //
305 Element ele = document.createElement("GenBuild");
306 ele.setAttribute("type", "build");
307
308 //
309 // Prepare pass down information
310 //
311 Map<String, String> passDownMap = new LinkedHashMap<String, String>();
312 for (int j = 0; j < inheritProperties.length; j ++){
313 passDownMap.put(inheritProperties[j], "${" + inheritProperties[j] + "}");
314 }
315
316 passDownMap.put("MODULE_GUID", libinstances[i].getGuid());
317 passDownMap.put("MODULE_VERSION", libinstances[i].getVersion());
318
319 passDownMap.put("PACKAGE_GUID", packageId.getGuid());
320 passDownMap.put("PACKAGE_VERSION", packageId.getVersion());
321
322 for (int j = 0; j < inheritProperties.length; j ++){
323 Element property = document.createElement("property");
324 property.setAttribute("name", inheritProperties[j]);
325 property.setAttribute("value", passDownMap.get(inheritProperties[j]));
326 ele.appendChild(property);
327 }
328
329 root.appendChild(ele);
330 }
331 }
332
333 /**
334 Return the name of the directory that corresponds to the architecture.
335 This is a translation from the XML Schema tag to a directory that
336 corresponds to our directory name coding convention.
337
338 **/
339 private String archDir(String arch) {
340 return arch.replaceFirst("X64", "x64")
341 .replaceFirst("IPF", "Ipf")
342 .replaceFirst("IA32", "Ia32")
343 .replaceFirst("ARM", "Arm")
344 .replaceFirst("EBC", "Ebc");
345 }
346
347 /**
348 Generate the build source files elements for BaseName_build.xml.
349
350 @param document current BaseName_build.xml XML document
351 @param root Root element for current
352 **/
353 private void applyCompileElement(Document document, Node root) {
354 //
355 // Prepare the includes: PackageDependencies and Output debug direactory
356 //
357 Set<String> includes = new LinkedHashSet<String>();
358 String arch = project.getProperty("ARCH");
359
360 //
361 // WORKSPACE
362 //
363 includes.add("${WORKSPACE_DIR}" + File.separatorChar);
364
365 //
366 // Module iteself
367 //
368 includes.add("${MODULE_DIR}");
369 includes.add("${MODULE_DIR}" + File.separatorChar + archDir(arch));
370
371 //
372 // Packages in PackageDenpendencies
373 //
374 PackageIdentification[] packageDependencies = SurfaceAreaQuery.getDependencePkg(fpdModuleId.getArch());
375 for (int i = 0; i < packageDependencies.length; i++) {
376 GlobalData.refreshPackageIdentification(packageDependencies[i]);
377 File packageFile = packageDependencies[i].getSpdFile();
378 includes.add(packageFile.getParent() + File.separatorChar + "Include");
379 includes.add(packageFile.getParent() + File.separatorChar + "Include" + File.separatorChar + archDir(arch));
380 }
381
382 //
383 // All Dependency Library Instance's PackageDependencies
384 //
385 ModuleIdentification[] libinstances = SurfaceAreaQuery.getLibraryInstance(fpdModuleId.getArch());
386 for (int i = 0; i < libinstances.length; i++) {
387 SurfaceAreaQuery.push(GlobalData.getDoc(libinstances[i], fpdModuleId.getArch()));
388 PackageIdentification[] libraryPackageDependencies = SurfaceAreaQuery.getDependencePkg(fpdModuleId.getArch());
389 for (int j = 0; j < libraryPackageDependencies.length; j++) {
390 GlobalData.refreshPackageIdentification(libraryPackageDependencies[j]);
391 File packageFile = libraryPackageDependencies[j].getSpdFile();
392 includes.add(packageFile.getParent() + File.separatorChar + "Include");
393 includes.add(packageFile.getParent() + File.separatorChar + "Include" + File.separatorChar + archDir(arch));
394 }
395 SurfaceAreaQuery.pop();
396 }
397
398
399 //
400 // The package which the module belongs to
401 // TBD
402 includes.add(fpdModuleId.getModule().getPackage().getPackageDir() + File.separatorChar + "Include");
403 includes.add(fpdModuleId.getModule().getPackage().getPackageDir() + File.separatorChar + "Include" + File.separatorChar + archDir(arch));
404
405 //
406 // Debug files output directory
407 //
408 includes.add("${DEST_DIR_DEBUG}");
409
410 //
411 // sourceFiles[][0] is FileType, [][1] is File name relative to Module_Dir
412 //
413 String[][] sourceFiles = SurfaceAreaQuery.getSourceFiles(fpdModuleId.getArch());
414
415 FileProcess fileProcess = new FileProcess();
416 fileProcess.init(project, includes, document);
417
418 //
419 // Initialize some properties by user
420 //
421 Element initEle = document.createElement("Build_Init");
422 root.appendChild(initEle);
423
424 String moduleDir = project.getProperty("MODULE_DIR");
425 //
426 // Parse all Unicode files
427 //
428 for (int i = 0; i < sourceFiles.length; i++) {
429 //
430 // Go through all source files. Add MODULE_DIR to preffix
431 //
432 File sourceFile = new File(moduleDir + File.separatorChar + sourceFiles[i][1]);
433 sourceFiles[i][1] = sourceFile.getPath();
434 String filetype = sourceFiles[i][0];
435 if (filetype != null) {
436 fileProcess.parseFile(sourceFiles[i][1], filetype, root, true);
437 } else {
438 fileProcess.parseFile(sourceFiles[i][1], root, true);
439 }
440 }
441
442 //
443 // If exist Unicode file
444 //
445 if (fileProcess.isUnicodeExist()) {
446 Element ele = document.createElement("Build_Unicode_Database");
447 ele.setAttribute("FILEPATH", ".");
448 ele.setAttribute("FILENAME", "${BASE_NAME}");
449 String[] includePaths = includes.toArray(new String[includes.size()]);
450 Element includesEle = document.createElement("EXTRA.INC");
451 for (int i = 0; i < includePaths.length; i++) {
452 Element includeEle = document.createElement("includepath");
453 includeEle.setAttribute("path", includePaths[i]);
454 includesEle.appendChild(includeEle);
455 }
456 ele.appendChild(includesEle);
457 root.appendChild(ele);
458 }
459
460 //
461 // Parse AutoGen.c & AutoGen.h
462 //
463 if ( ! fpdModuleId.getModule().getName().equalsIgnoreCase("Shell")) {
464 fileProcess.parseFile(project.getProperty("DEST_DIR_DEBUG") + File.separatorChar + "AutoGen.c", root, false);
465 }
466
467 //
468 // Parse all source files but Unicode files
469 //
470 for (int i = 0; i < sourceFiles.length; i++) {
471 String filetype = sourceFiles[i][0];
472 if (filetype != null) {
473 fileProcess.parseFile(sourceFiles[i][1], filetype, root, false);
474 } else {
475 fileProcess.parseFile(sourceFiles[i][1], root, false);
476 }
477 }
478
479 //
480 // Initialize SOURCE_FILES for dependcy check use
481 //
482 String str = "";
483 for (int i = 0; i < sourceFiles.length; i++) {
484 str += " " + sourceFiles[i][1];
485 }
486 project.setProperty("SOURCE_FILES", str.replaceAll("(\\\\)", "/"));
487 }
488
489 /**
490 Generate the section elements for BaseName_build.xml. Library module will
491 skip this process.
492
493 @param document current BaseName_build.xml XML document
494 @param root Root element for current
495 **/
496 private void applySectionsElement(Document document, Node root, FfsProcess fp) {
497 if (fpdModuleId.getModule().isLibrary()) {
498 return ;
499 }
500 if (fp.initSections(ffsKeyword, project, fpdModuleId)) {
501 String targetFilename = fpdModuleId.getModule().getGuid() + "-" + "${BASE_NAME}" + FpdParserTask.getSuffix(fpdModuleId.getModule().getModuleType());
502 String[] list = fp.getGenSectionElements(document, "${BASE_NAME}", fpdModuleId.getModule().getGuid(), targetFilename);
503
504 for (int i = 0; i < list.length; i++) {
505 Element ele = document.createElement(list[i]);
506 ele.setAttribute("FILEPATH", ".");
507 ele.setAttribute("FILENAME", "${BASE_NAME}");
508 root.appendChild(ele);
509 }
510 }
511 }
512
513 /**
514 Generate the output elements for BaseName_build.xml. If module is library,
515 call the <em>LIB</em> command, else call the <em>GenFfs</em> command.
516
517 @param document current BaseName_build.xml XML document
518 @param root Root element for current
519 **/
520 private void applyOutputElement(Document document, Node root, FfsProcess fp) {
521 if (fpdModuleId.getModule().isLibrary()) {
522 //
523 // call Lib command
524 //
525 Element cc = document.createElement("Build_Library");
526 cc.setAttribute("FILENAME", fpdModuleId.getModule().getName());
527 root.appendChild(cc);
528 }
529 //
530 // if it is a module but library
531 //
532 else {
533 if (fp.getFfsNode() != null) {
534 root.appendChild(fp.getFfsNode());
535 }
536 }
537 }
538
539 }