]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/build/build.py
BaseTools/build/build: refactor and move functions
[mirror_edk2.git] / BaseTools / Source / Python / build / build.py
1 ## @file
2 # build a platform or a module
3 #
4 # Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>
5 # Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
6 # Copyright (c) 2018, Hewlett Packard Enterprise Development, L.P.<BR>
7 #
8 # This program and the accompanying materials
9 # are licensed and made available under the terms and conditions of the BSD License
10 # which accompanies this distribution. The full text of the license may be found at
11 # http://opensource.org/licenses/bsd-license.php
12 #
13 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #
16
17 ##
18 # Import Modules
19 #
20 from __future__ import print_function
21 import Common.LongFilePathOs as os
22 import re
23 from io import BytesIO
24 import sys
25 import glob
26 import time
27 import platform
28 import traceback
29 import encodings.ascii
30 import itertools
31 import multiprocessing
32
33 from struct import *
34 from threading import *
35 import threading
36 from optparse import OptionParser
37 from subprocess import *
38 from Common import Misc as Utils
39
40 from Common.LongFilePathSupport import OpenLongFilePath as open
41 from Common.TargetTxtClassObject import TargetTxtClassObject
42 from Common.ToolDefClassObject import ToolDefClassObject
43 from Common.DataType import *
44 from Common.BuildVersion import gBUILD_VERSION
45 from AutoGen.AutoGen import *
46 from Common.BuildToolError import *
47 from Workspace.WorkspaceDatabase import WorkspaceDatabase
48 from Common.MultipleWorkspace import MultipleWorkspace as mws
49
50 from BuildReport import BuildReport
51 from GenPatchPcdTable.GenPatchPcdTable import *
52 from PatchPcdValue.PatchPcdValue import *
53
54 import Common.EdkLogger
55 import Common.GlobalData as GlobalData
56 from GenFds.GenFds import GenFds, GenFdsApi
57
58 from collections import OrderedDict, defaultdict
59
60 # Version and Copyright
61 VersionNumber = "0.60" + ' ' + gBUILD_VERSION
62 __version__ = "%prog Version " + VersionNumber
63 __copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation All rights reserved."
64
65 ## standard targets of build command
66 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']
67
68 ## build configuration file
69 gBuildConfiguration = "target.txt"
70 gToolsDefinition = "tools_def.txt"
71
72 TemporaryTablePattern = re.compile(r'^_\d+_\d+_[a-fA-F0-9]+$')
73 TmpTableDict = {}
74
75 ## Make a Python object persistent on file system
76 #
77 # @param Data The object to be stored in file
78 # @param File The path of file to store the object
79 #
80 def _DataDump(Data, File):
81 Fd = None
82 try:
83 Fd = open(File, 'wb')
84 pickle.dump(Data, Fd, pickle.HIGHEST_PROTOCOL)
85 except:
86 EdkLogger.error("", FILE_OPEN_FAILURE, ExtraData=File, RaiseError=False)
87 finally:
88 if Fd is not None:
89 Fd.close()
90
91 ## Restore a Python object from a file
92 #
93 # @param File The path of file stored the object
94 #
95 # @retval object A python object
96 # @retval None If failure in file operation
97 #
98 def _DataRestore(File):
99 Data = None
100 Fd = None
101 try:
102 Fd = open(File, 'rb')
103 Data = pickle.load(Fd)
104 except Exception as e:
105 EdkLogger.verbose("Failed to load [%s]\n\t%s" % (File, str(e)))
106 Data = None
107 finally:
108 if Fd is not None:
109 Fd.close()
110 return Data
111
112 ## Check environment PATH variable to make sure the specified tool is found
113 #
114 # If the tool is found in the PATH, then True is returned
115 # Otherwise, False is returned
116 #
117 def IsToolInPath(tool):
118 if 'PATHEXT' in os.environ:
119 extns = os.environ['PATHEXT'].split(os.path.pathsep)
120 else:
121 extns = ('',)
122 for pathDir in os.environ['PATH'].split(os.path.pathsep):
123 for ext in extns:
124 if os.path.exists(os.path.join(pathDir, tool + ext)):
125 return True
126 return False
127
128 ## Check environment variables
129 #
130 # Check environment variables that must be set for build. Currently they are
131 #
132 # WORKSPACE The directory all packages/platforms start from
133 # EDK_TOOLS_PATH The directory contains all tools needed by the build
134 # PATH $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH
135 #
136 # If any of above environment variable is not set or has error, the build
137 # will be broken.
138 #
139 def CheckEnvVariable():
140 # check WORKSPACE
141 if "WORKSPACE" not in os.environ:
142 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
143 ExtraData="WORKSPACE")
144
145 WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))
146 if not os.path.exists(WorkspaceDir):
147 EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData=WorkspaceDir)
148 elif ' ' in WorkspaceDir:
149 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",
150 ExtraData=WorkspaceDir)
151 os.environ["WORKSPACE"] = WorkspaceDir
152
153 # set multiple workspace
154 PackagesPath = os.getenv("PACKAGES_PATH")
155 mws.setWs(WorkspaceDir, PackagesPath)
156 if mws.PACKAGES_PATH:
157 for Path in mws.PACKAGES_PATH:
158 if not os.path.exists(Path):
159 EdkLogger.error("build", FILE_NOT_FOUND, "One Path in PACKAGES_PATH doesn't exist", ExtraData=Path)
160 elif ' ' in Path:
161 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in PACKAGES_PATH", ExtraData=Path)
162
163
164 os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])
165
166 # check EDK_TOOLS_PATH
167 if "EDK_TOOLS_PATH" not in os.environ:
168 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
169 ExtraData="EDK_TOOLS_PATH")
170
171 # check PATH
172 if "PATH" not in os.environ:
173 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
174 ExtraData="PATH")
175
176 GlobalData.gWorkspace = WorkspaceDir
177
178 GlobalData.gGlobalDefines["WORKSPACE"] = WorkspaceDir
179 GlobalData.gGlobalDefines["EDK_TOOLS_PATH"] = os.environ["EDK_TOOLS_PATH"]
180
181 ## Get normalized file path
182 #
183 # Convert the path to be local format, and remove the WORKSPACE path at the
184 # beginning if the file path is given in full path.
185 #
186 # @param FilePath File path to be normalized
187 # @param Workspace Workspace path which the FilePath will be checked against
188 #
189 # @retval string The normalized file path
190 #
191 def NormFile(FilePath, Workspace):
192 # check if the path is absolute or relative
193 if os.path.isabs(FilePath):
194 FileFullPath = os.path.normpath(FilePath)
195 else:
196 FileFullPath = os.path.normpath(mws.join(Workspace, FilePath))
197 Workspace = mws.getWs(Workspace, FilePath)
198
199 # check if the file path exists or not
200 if not os.path.isfile(FileFullPath):
201 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath)
202
203 # remove workspace directory from the beginning part of the file path
204 if Workspace[-1] in ["\\", "/"]:
205 return FileFullPath[len(Workspace):]
206 else:
207 return FileFullPath[(len(Workspace) + 1):]
208
209 ## Get the output of an external program
210 #
211 # This is the entrance method of thread reading output of an external program and
212 # putting them in STDOUT/STDERR of current program.
213 #
214 # @param From The stream message read from
215 # @param To The stream message put on
216 # @param ExitFlag The flag used to indicate stopping reading
217 #
218 def ReadMessage(From, To, ExitFlag):
219 while True:
220 # read one line a time
221 Line = From.readline()
222 # empty string means "end"
223 if Line is not None and Line != "":
224 To(Line.rstrip())
225 else:
226 break
227 if ExitFlag.isSet():
228 break
229
230 ## Launch an external program
231 #
232 # This method will call subprocess.Popen to execute an external program with
233 # given options in specified directory. Because of the dead-lock issue during
234 # redirecting output of the external program, threads are used to to do the
235 # redirection work.
236 #
237 # @param Command A list or string containing the call of the program
238 # @param WorkingDir The directory in which the program will be running
239 #
240 def LaunchCommand(Command, WorkingDir):
241 BeginTime = time.time()
242 # if working directory doesn't exist, Popen() will raise an exception
243 if not os.path.isdir(WorkingDir):
244 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)
245
246 # Command is used as the first Argument in following Popen().
247 # It could be a string or sequence. We find that if command is a string in following Popen(),
248 # ubuntu may fail with an error message that the command is not found.
249 # So here we may need convert command from string to list instance.
250 if platform.system() != 'Windows':
251 if not isinstance(Command, list):
252 Command = Command.split()
253 Command = ' '.join(Command)
254
255 Proc = None
256 EndOfProcedure = None
257 try:
258 # launch the command
259 Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1, shell=True)
260
261 # launch two threads to read the STDOUT and STDERR
262 EndOfProcedure = Event()
263 EndOfProcedure.clear()
264 if Proc.stdout:
265 StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))
266 StdOutThread.setName("STDOUT-Redirector")
267 StdOutThread.setDaemon(False)
268 StdOutThread.start()
269
270 if Proc.stderr:
271 StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))
272 StdErrThread.setName("STDERR-Redirector")
273 StdErrThread.setDaemon(False)
274 StdErrThread.start()
275
276 # waiting for program exit
277 Proc.wait()
278 except: # in case of aborting
279 # terminate the threads redirecting the program output
280 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
281 if EndOfProcedure is not None:
282 EndOfProcedure.set()
283 if Proc is None:
284 if not isinstance(Command, type("")):
285 Command = " ".join(Command)
286 EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))
287
288 if Proc.stdout:
289 StdOutThread.join()
290 if Proc.stderr:
291 StdErrThread.join()
292
293 # check the return code of the program
294 if Proc.returncode != 0:
295 if not isinstance(Command, type("")):
296 Command = " ".join(Command)
297 # print out the Response file and its content when make failure
298 RespFile = os.path.join(WorkingDir, 'OUTPUT', 'respfilelist.txt')
299 if os.path.isfile(RespFile):
300 f = open(RespFile)
301 RespContent = f.read()
302 f.close()
303 EdkLogger.info(RespContent)
304
305 EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))
306 return "%dms" % (int(round((time.time() - BeginTime) * 1000)))
307
308 ## The smallest unit that can be built in multi-thread build mode
309 #
310 # This is the base class of build unit. The "Obj" parameter must provide
311 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units
312 # missing build.
313 #
314 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.
315 #
316 class BuildUnit:
317 ## The constructor
318 #
319 # @param self The object pointer
320 # @param Obj The object the build is working on
321 # @param Target The build target name, one of gSupportedTarget
322 # @param Dependency The BuildUnit(s) which must be completed in advance
323 # @param WorkingDir The directory build command starts in
324 #
325 def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):
326 self.BuildObject = Obj
327 self.Dependency = Dependency
328 self.WorkingDir = WorkingDir
329 self.Target = Target
330 self.BuildCommand = BuildCommand
331 if not BuildCommand:
332 EdkLogger.error("build", OPTION_MISSING,
333 "No build command found for this module. "
334 "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
335 (Obj.BuildTarget, Obj.ToolChain, Obj.Arch),
336 ExtraData=str(Obj))
337
338
339 ## str() method
340 #
341 # It just returns the string representation of self.BuildObject
342 #
343 # @param self The object pointer
344 #
345 def __str__(self):
346 return str(self.BuildObject)
347
348 ## "==" operator method
349 #
350 # It just compares self.BuildObject with "Other". So self.BuildObject must
351 # provide its own __eq__() method.
352 #
353 # @param self The object pointer
354 # @param Other The other BuildUnit object compared to
355 #
356 def __eq__(self, Other):
357 return Other and self.BuildObject == Other.BuildObject \
358 and Other.BuildObject \
359 and self.BuildObject.Arch == Other.BuildObject.Arch
360
361 ## hash() method
362 #
363 # It just returns the hash value of self.BuildObject which must be hashable.
364 #
365 # @param self The object pointer
366 #
367 def __hash__(self):
368 return hash(self.BuildObject) + hash(self.BuildObject.Arch)
369
370 def __repr__(self):
371 return repr(self.BuildObject)
372
373 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode
374 #
375 # This class is for module build by nmake/make build system. The "Obj" parameter
376 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
377 # be make units missing build.
378 #
379 # Currently the "Obj" should be only ModuleAutoGen object.
380 #
381 class ModuleMakeUnit(BuildUnit):
382 ## The constructor
383 #
384 # @param self The object pointer
385 # @param Obj The ModuleAutoGen object the build is working on
386 # @param Target The build target name, one of gSupportedTarget
387 #
388 def __init__(self, Obj, Target):
389 Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]
390 BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)
391 if Target in [None, "", "all"]:
392 self.Target = "tbuild"
393
394 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode
395 #
396 # This class is for platform build by nmake/make build system. The "Obj" parameter
397 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
398 # be make units missing build.
399 #
400 # Currently the "Obj" should be only PlatformAutoGen object.
401 #
402 class PlatformMakeUnit(BuildUnit):
403 ## The constructor
404 #
405 # @param self The object pointer
406 # @param Obj The PlatformAutoGen object the build is working on
407 # @param Target The build target name, one of gSupportedTarget
408 #
409 def __init__(self, Obj, Target):
410 Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]
411 Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])
412 BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)
413
414 ## The class representing the task of a module build or platform build
415 #
416 # This class manages the build tasks in multi-thread build mode. Its jobs include
417 # scheduling thread running, catching thread error, monitor the thread status, etc.
418 #
419 class BuildTask:
420 # queue for tasks waiting for schedule
421 _PendingQueue = OrderedDict()
422 _PendingQueueLock = threading.Lock()
423
424 # queue for tasks ready for running
425 _ReadyQueue = OrderedDict()
426 _ReadyQueueLock = threading.Lock()
427
428 # queue for run tasks
429 _RunningQueue = OrderedDict()
430 _RunningQueueLock = threading.Lock()
431
432 # queue containing all build tasks, in case duplicate build
433 _TaskQueue = OrderedDict()
434
435 # flag indicating error occurs in a running thread
436 _ErrorFlag = threading.Event()
437 _ErrorFlag.clear()
438 _ErrorMessage = ""
439
440 # BoundedSemaphore object used to control the number of running threads
441 _Thread = None
442
443 # flag indicating if the scheduler is started or not
444 _SchedulerStopped = threading.Event()
445 _SchedulerStopped.set()
446
447 ## Start the task scheduler thread
448 #
449 # @param MaxThreadNumber The maximum thread number
450 # @param ExitFlag Flag used to end the scheduler
451 #
452 @staticmethod
453 def StartScheduler(MaxThreadNumber, ExitFlag):
454 SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))
455 SchedulerThread.setName("Build-Task-Scheduler")
456 SchedulerThread.setDaemon(False)
457 SchedulerThread.start()
458 # wait for the scheduler to be started, especially useful in Linux
459 while not BuildTask.IsOnGoing():
460 time.sleep(0.01)
461
462 ## Scheduler method
463 #
464 # @param MaxThreadNumber The maximum thread number
465 # @param ExitFlag Flag used to end the scheduler
466 #
467 @staticmethod
468 def Scheduler(MaxThreadNumber, ExitFlag):
469 BuildTask._SchedulerStopped.clear()
470 try:
471 # use BoundedSemaphore to control the maximum running threads
472 BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)
473 #
474 # scheduling loop, which will exits when no pending/ready task and
475 # indicated to do so, or there's error in running thread
476 #
477 while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \
478 or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():
479 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"
480 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))
481
482 # get all pending tasks
483 BuildTask._PendingQueueLock.acquire()
484 BuildObjectList = BuildTask._PendingQueue.keys()
485 #
486 # check if their dependency is resolved, and if true, move them
487 # into ready queue
488 #
489 for BuildObject in BuildObjectList:
490 Bt = BuildTask._PendingQueue[BuildObject]
491 if Bt.IsReady():
492 BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)
493 BuildTask._PendingQueueLock.release()
494
495 # launch build thread until the maximum number of threads is reached
496 while not BuildTask._ErrorFlag.isSet():
497 # empty ready queue, do nothing further
498 if len(BuildTask._ReadyQueue) == 0:
499 break
500
501 # wait for active thread(s) exit
502 BuildTask._Thread.acquire(True)
503
504 # start a new build thread
505 Bo, Bt = BuildTask._ReadyQueue.popitem()
506
507 # move into running queue
508 BuildTask._RunningQueueLock.acquire()
509 BuildTask._RunningQueue[Bo] = Bt
510 BuildTask._RunningQueueLock.release()
511
512 Bt.Start()
513 # avoid tense loop
514 time.sleep(0.01)
515
516 # avoid tense loop
517 time.sleep(0.01)
518
519 # wait for all running threads exit
520 if BuildTask._ErrorFlag.isSet():
521 EdkLogger.quiet("\nWaiting for all build threads exit...")
522 # while not BuildTask._ErrorFlag.isSet() and \
523 while len(BuildTask._RunningQueue) > 0:
524 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))
525 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join(Th.getName() for Th in threading.enumerate()))
526 # avoid tense loop
527 time.sleep(0.1)
528 except BaseException as X:
529 #
530 # TRICK: hide the output of threads left runing, so that the user can
531 # catch the error message easily
532 #
533 EdkLogger.SetLevel(EdkLogger.ERROR)
534 BuildTask._ErrorFlag.set()
535 BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)
536
537 BuildTask._PendingQueue.clear()
538 BuildTask._ReadyQueue.clear()
539 BuildTask._RunningQueue.clear()
540 BuildTask._TaskQueue.clear()
541 BuildTask._SchedulerStopped.set()
542
543 ## Wait for all running method exit
544 #
545 @staticmethod
546 def WaitForComplete():
547 BuildTask._SchedulerStopped.wait()
548
549 ## Check if the scheduler is running or not
550 #
551 @staticmethod
552 def IsOnGoing():
553 return not BuildTask._SchedulerStopped.isSet()
554
555 ## Abort the build
556 @staticmethod
557 def Abort():
558 if BuildTask.IsOnGoing():
559 BuildTask._ErrorFlag.set()
560 BuildTask.WaitForComplete()
561
562 ## Check if there's error in running thread
563 #
564 # Since the main thread cannot catch exceptions in other thread, we have to
565 # use threading.Event to communicate this formation to main thread.
566 #
567 @staticmethod
568 def HasError():
569 return BuildTask._ErrorFlag.isSet()
570
571 ## Get error message in running thread
572 #
573 # Since the main thread cannot catch exceptions in other thread, we have to
574 # use a static variable to communicate this message to main thread.
575 #
576 @staticmethod
577 def GetErrorMessage():
578 return BuildTask._ErrorMessage
579
580 ## Factory method to create a BuildTask object
581 #
582 # This method will check if a module is building or has been built. And if
583 # true, just return the associated BuildTask object in the _TaskQueue. If
584 # not, create and return a new BuildTask object. The new BuildTask object
585 # will be appended to the _PendingQueue for scheduling later.
586 #
587 # @param BuildItem A BuildUnit object representing a build object
588 # @param Dependency The dependent build object of BuildItem
589 #
590 @staticmethod
591 def New(BuildItem, Dependency=None):
592 if BuildItem in BuildTask._TaskQueue:
593 Bt = BuildTask._TaskQueue[BuildItem]
594 return Bt
595
596 Bt = BuildTask()
597 Bt._Init(BuildItem, Dependency)
598 BuildTask._TaskQueue[BuildItem] = Bt
599
600 BuildTask._PendingQueueLock.acquire()
601 BuildTask._PendingQueue[BuildItem] = Bt
602 BuildTask._PendingQueueLock.release()
603
604 return Bt
605
606 ## The real constructor of BuildTask
607 #
608 # @param BuildItem A BuildUnit object representing a build object
609 # @param Dependency The dependent build object of BuildItem
610 #
611 def _Init(self, BuildItem, Dependency=None):
612 self.BuildItem = BuildItem
613
614 self.DependencyList = []
615 if Dependency is None:
616 Dependency = BuildItem.Dependency
617 else:
618 Dependency.extend(BuildItem.Dependency)
619 self.AddDependency(Dependency)
620 # flag indicating build completes, used to avoid unnecessary re-build
621 self.CompleteFlag = False
622
623 ## Check if all dependent build tasks are completed or not
624 #
625 def IsReady(self):
626 ReadyFlag = True
627 for Dep in self.DependencyList:
628 if Dep.CompleteFlag == True:
629 continue
630 ReadyFlag = False
631 break
632
633 return ReadyFlag
634
635 ## Add dependent build task
636 #
637 # @param Dependency The list of dependent build objects
638 #
639 def AddDependency(self, Dependency):
640 for Dep in Dependency:
641 if not Dep.BuildObject.IsBinaryModule:
642 self.DependencyList.append(BuildTask.New(Dep)) # BuildTask list
643
644 ## The thread wrapper of LaunchCommand function
645 #
646 # @param Command A list or string contains the call of the command
647 # @param WorkingDir The directory in which the program will be running
648 #
649 def _CommandThread(self, Command, WorkingDir):
650 try:
651 self.BuildItem.BuildObject.BuildTime = LaunchCommand(Command, WorkingDir)
652 self.CompleteFlag = True
653 except:
654 #
655 # TRICK: hide the output of threads left runing, so that the user can
656 # catch the error message easily
657 #
658 if not BuildTask._ErrorFlag.isSet():
659 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),
660 self.BuildItem.BuildObject.Arch,
661 self.BuildItem.BuildObject.ToolChain,
662 self.BuildItem.BuildObject.BuildTarget
663 )
664 EdkLogger.SetLevel(EdkLogger.ERROR)
665 BuildTask._ErrorFlag.set()
666 BuildTask._ErrorMessage = "%s broken\n %s [%s]" % \
667 (threading.currentThread().getName(), Command, WorkingDir)
668 # indicate there's a thread is available for another build task
669 BuildTask._RunningQueueLock.acquire()
670 BuildTask._RunningQueue.pop(self.BuildItem)
671 BuildTask._RunningQueueLock.release()
672 BuildTask._Thread.release()
673
674 ## Start build task thread
675 #
676 def Start(self):
677 EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))
678 Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]
679 self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))
680 self.BuildTread.setName("build thread")
681 self.BuildTread.setDaemon(False)
682 self.BuildTread.start()
683
684 ## The class contains the information related to EFI image
685 #
686 class PeImageInfo():
687 ## Constructor
688 #
689 # Constructor will load all required image information.
690 #
691 # @param BaseName The full file path of image.
692 # @param Guid The GUID for image.
693 # @param Arch Arch of this image.
694 # @param OutputDir The output directory for image.
695 # @param DebugDir The debug directory for image.
696 # @param ImageClass PeImage Information
697 #
698 def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass):
699 self.BaseName = BaseName
700 self.Guid = Guid
701 self.Arch = Arch
702 self.OutputDir = OutputDir
703 self.DebugDir = DebugDir
704 self.Image = ImageClass
705 self.Image.Size = (self.Image.Size / 0x1000 + 1) * 0x1000
706
707 ## The class implementing the EDK2 build process
708 #
709 # The build process includes:
710 # 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf
711 # 2. Parse DSC file of active platform
712 # 3. Parse FDF file if any
713 # 4. Establish build database, including parse all other files (module, package)
714 # 5. Create AutoGen files (C code file, depex file, makefile) if necessary
715 # 6. Call build command
716 #
717 class Build():
718 ## Constructor
719 #
720 # Constructor will load all necessary configurations, parse platform, modules
721 # and packages and the establish a database for AutoGen.
722 #
723 # @param Target The build command target, one of gSupportedTarget
724 # @param WorkspaceDir The directory of workspace
725 # @param BuildOptions Build options passed from command line
726 #
727 def __init__(self, Target, WorkspaceDir, BuildOptions):
728 self.WorkspaceDir = WorkspaceDir
729 self.Target = Target
730 self.PlatformFile = BuildOptions.PlatformFile
731 self.ModuleFile = BuildOptions.ModuleFile
732 self.ArchList = BuildOptions.TargetArch
733 self.ToolChainList = BuildOptions.ToolChain
734 self.BuildTargetList= BuildOptions.BuildTarget
735 self.Fdf = BuildOptions.FdfFile
736 self.FdList = BuildOptions.RomImage
737 self.FvList = BuildOptions.FvImage
738 self.CapList = BuildOptions.CapName
739 self.SilentMode = BuildOptions.SilentMode
740 self.ThreadNumber = BuildOptions.ThreadNumber
741 self.SkipAutoGen = BuildOptions.SkipAutoGen
742 self.Reparse = BuildOptions.Reparse
743 self.SkuId = BuildOptions.SkuId
744 if self.SkuId:
745 GlobalData.gSKUID_CMD = self.SkuId
746 self.ConfDirectory = BuildOptions.ConfDirectory
747 self.SpawnMode = True
748 self.BuildReport = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType)
749 self.TargetTxt = TargetTxtClassObject()
750 self.ToolDef = ToolDefClassObject()
751 self.AutoGenTime = 0
752 self.MakeTime = 0
753 self.GenFdsTime = 0
754 GlobalData.BuildOptionPcd = BuildOptions.OptionPcd if BuildOptions.OptionPcd else []
755 #Set global flag for build mode
756 GlobalData.gIgnoreSource = BuildOptions.IgnoreSources
757 GlobalData.gUseHashCache = BuildOptions.UseHashCache
758 GlobalData.gBinCacheDest = BuildOptions.BinCacheDest
759 GlobalData.gBinCacheSource = BuildOptions.BinCacheSource
760 GlobalData.gEnableGenfdsMultiThread = BuildOptions.GenfdsMultiThread
761
762 if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache:
763 EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination must be used together with --hash.")
764
765 if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache:
766 EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-source must be used together with --hash.")
767
768 if GlobalData.gBinCacheDest and GlobalData.gBinCacheSource:
769 EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination can not be used together with --binary-source.")
770
771 if GlobalData.gBinCacheSource:
772 BinCacheSource = os.path.normpath(GlobalData.gBinCacheSource)
773 if not os.path.isabs(BinCacheSource):
774 BinCacheSource = mws.join(self.WorkspaceDir, BinCacheSource)
775 GlobalData.gBinCacheSource = BinCacheSource
776 else:
777 if GlobalData.gBinCacheSource is not None:
778 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-source.")
779
780 if GlobalData.gBinCacheDest:
781 BinCacheDest = os.path.normpath(GlobalData.gBinCacheDest)
782 if not os.path.isabs(BinCacheDest):
783 BinCacheDest = mws.join(self.WorkspaceDir, BinCacheDest)
784 GlobalData.gBinCacheDest = BinCacheDest
785 else:
786 if GlobalData.gBinCacheDest is not None:
787 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.")
788
789 if self.ConfDirectory:
790 # Get alternate Conf location, if it is absolute, then just use the absolute directory name
791 ConfDirectoryPath = os.path.normpath(self.ConfDirectory)
792
793 if not os.path.isabs(ConfDirectoryPath):
794 # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE
795 # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf
796 ConfDirectoryPath = mws.join(self.WorkspaceDir, ConfDirectoryPath)
797 else:
798 if "CONF_PATH" in os.environ:
799 ConfDirectoryPath = os.path.normcase(os.path.normpath(os.environ["CONF_PATH"]))
800 else:
801 # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf
802 ConfDirectoryPath = mws.join(self.WorkspaceDir, 'Conf')
803 GlobalData.gConfDirectory = ConfDirectoryPath
804 GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath))
805
806 self.Db = WorkspaceDatabase()
807 self.BuildDatabase = self.Db.BuildObject
808 self.Platform = None
809 self.ToolChainFamily = None
810 self.LoadFixAddress = 0
811 self.UniFlag = BuildOptions.Flag
812 self.BuildModules = []
813 self.HashSkipModules = []
814 self.Db_Flag = False
815 self.LaunchPrebuildFlag = False
816 self.PlatformBuildPath = os.path.join(GlobalData.gConfDirectory, '.cache', '.PlatformBuild')
817 if BuildOptions.CommandLength:
818 GlobalData.gCommandMaxLength = BuildOptions.CommandLength
819
820 # print dot character during doing some time-consuming work
821 self.Progress = Utils.Progressor()
822 # print current build environment and configuration
823 EdkLogger.quiet("%-16s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))
824 if "PACKAGES_PATH" in os.environ:
825 # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env.
826 EdkLogger.quiet("%-16s = %s" % ("PACKAGES_PATH", os.path.normcase(os.path.normpath(os.environ["PACKAGES_PATH"]))))
827 EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))
828 if "EDK_TOOLS_BIN" in os.environ:
829 # Print the same path style with WORKSPACE env.
830 EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os.path.normcase(os.path.normpath(os.environ["EDK_TOOLS_BIN"]))))
831 EdkLogger.quiet("%-16s = %s" % ("CONF_PATH", GlobalData.gConfDirectory))
832 self.InitPreBuild()
833 self.InitPostBuild()
834 if self.Prebuild:
835 EdkLogger.quiet("%-16s = %s" % ("PREBUILD", self.Prebuild))
836 if self.Postbuild:
837 EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.Postbuild))
838 if self.Prebuild:
839 self.LaunchPrebuild()
840 self.TargetTxt = TargetTxtClassObject()
841 self.ToolDef = ToolDefClassObject()
842 if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)):
843 self.InitBuild()
844
845 EdkLogger.info("")
846 os.chdir(self.WorkspaceDir)
847
848 ## Load configuration
849 #
850 # This method will parse target.txt and get the build configurations.
851 #
852 def LoadConfiguration(self):
853 #
854 # Check target.txt and tools_def.txt and Init them
855 #
856 BuildConfigurationFile = os.path.normpath(os.path.join(GlobalData.gConfDirectory, gBuildConfiguration))
857 if os.path.isfile(BuildConfigurationFile) == True:
858 StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)
859
860 ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_CONF]
861 if ToolDefinitionFile == '':
862 ToolDefinitionFile = gToolsDefinition
863 ToolDefinitionFile = os.path.normpath(mws.join(self.WorkspaceDir, 'Conf', ToolDefinitionFile))
864 if os.path.isfile(ToolDefinitionFile) == True:
865 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)
866 else:
867 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)
868 else:
869 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)
870
871 # if no ARCH given in command line, get it from target.txt
872 if not self.ArchList:
873 self.ArchList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET_ARCH]
874 self.ArchList = tuple(self.ArchList)
875
876 # if no build target given in command line, get it from target.txt
877 if not self.BuildTargetList:
878 self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET]
879
880 # if no tool chain given in command line, get it from target.txt
881 if not self.ToolChainList:
882 self.ToolChainList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_TAG]
883 if self.ToolChainList is None or len(self.ToolChainList) == 0:
884 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")
885
886 # check if the tool chains are defined or not
887 NewToolChainList = []
888 for ToolChain in self.ToolChainList:
889 if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:
890 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)
891 else:
892 NewToolChainList.append(ToolChain)
893 # if no tool chain available, break the build
894 if len(NewToolChainList) == 0:
895 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
896 ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))
897 else:
898 self.ToolChainList = NewToolChainList
899
900 ToolChainFamily = []
901 ToolDefinition = self.ToolDef.ToolsDefTxtDatabase
902 for Tool in self.ToolChainList:
903 if TAB_TOD_DEFINES_FAMILY not in ToolDefinition or Tool not in ToolDefinition[TAB_TOD_DEFINES_FAMILY] \
904 or not ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]:
905 EdkLogger.warn("build", "No tool chain family found in configuration for %s. Default to MSFT." % Tool)
906 ToolChainFamily.append(TAB_COMPILER_MSFT)
907 else:
908 ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool])
909 self.ToolChainFamily = ToolChainFamily
910
911 if self.ThreadNumber is None:
912 self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]
913 if self.ThreadNumber == '':
914 self.ThreadNumber = 0
915 else:
916 self.ThreadNumber = int(self.ThreadNumber, 0)
917
918 if self.ThreadNumber == 0:
919 try:
920 self.ThreadNumber = multiprocessing.cpu_count()
921 except (ImportError, NotImplementedError):
922 self.ThreadNumber = 1
923
924 if not self.PlatformFile:
925 PlatformFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_ACTIVE_PLATFORM]
926 if not PlatformFile:
927 # Try to find one in current directory
928 WorkingDirectory = os.getcwd()
929 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))
930 FileNum = len(FileList)
931 if FileNum >= 2:
932 EdkLogger.error("build", OPTION_MISSING,
933 ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))
934 elif FileNum == 1:
935 PlatformFile = FileList[0]
936 else:
937 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
938 ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")
939
940 self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)
941
942 ## Initialize build configuration
943 #
944 # This method will parse DSC file and merge the configurations from
945 # command line and target.txt, then get the final build configurations.
946 #
947 def InitBuild(self):
948 # parse target.txt, tools_def.txt, and platform file
949 self.LoadConfiguration()
950
951 # Allow case-insensitive for those from command line or configuration file
952 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
953 if ErrorCode != 0:
954 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
955
956
957 def InitPreBuild(self):
958 self.LoadConfiguration()
959 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
960 if ErrorCode != 0:
961 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
962 if self.BuildTargetList:
963 GlobalData.gGlobalDefines['TARGET'] = self.BuildTargetList[0]
964 if self.ArchList:
965 GlobalData.gGlobalDefines['ARCH'] = self.ArchList[0]
966 if self.ToolChainList:
967 GlobalData.gGlobalDefines['TOOLCHAIN'] = self.ToolChainList[0]
968 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = self.ToolChainList[0]
969 if self.ToolChainFamily:
970 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[0]
971 if 'PREBUILD' in GlobalData.gCommandLineDefines:
972 self.Prebuild = GlobalData.gCommandLineDefines.get('PREBUILD')
973 else:
974 self.Db_Flag = True
975 Platform = self.Db.MapPlatform(str(self.PlatformFile))
976 self.Prebuild = str(Platform.Prebuild)
977 if self.Prebuild:
978 PrebuildList = []
979 #
980 # Evaluate all arguments and convert arguments that are WORKSPACE
981 # relative paths to absolute paths. Filter arguments that look like
982 # flags or do not follow the file/dir naming rules to avoid false
983 # positives on this conversion.
984 #
985 for Arg in self.Prebuild.split():
986 #
987 # Do not modify Arg if it looks like a flag or an absolute file path
988 #
989 if Arg.startswith('-') or os.path.isabs(Arg):
990 PrebuildList.append(Arg)
991 continue
992 #
993 # Do not modify Arg if it does not look like a Workspace relative
994 # path that starts with a valid package directory name
995 #
996 if not Arg[0].isalpha() or os.path.dirname(Arg) == '':
997 PrebuildList.append(Arg)
998 continue
999 #
1000 # If Arg looks like a WORKSPACE relative path, then convert to an
1001 # absolute path and check to see if the file exists.
1002 #
1003 Temp = mws.join(self.WorkspaceDir, Arg)
1004 if os.path.isfile(Temp):
1005 Arg = Temp
1006 PrebuildList.append(Arg)
1007 self.Prebuild = ' '.join(PrebuildList)
1008 self.Prebuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)
1009
1010 def InitPostBuild(self):
1011 if 'POSTBUILD' in GlobalData.gCommandLineDefines:
1012 self.Postbuild = GlobalData.gCommandLineDefines.get('POSTBUILD')
1013 else:
1014 Platform = self.Db.MapPlatform(str(self.PlatformFile))
1015 self.Postbuild = str(Platform.Postbuild)
1016 if self.Postbuild:
1017 PostbuildList = []
1018 #
1019 # Evaluate all arguments and convert arguments that are WORKSPACE
1020 # relative paths to absolute paths. Filter arguments that look like
1021 # flags or do not follow the file/dir naming rules to avoid false
1022 # positives on this conversion.
1023 #
1024 for Arg in self.Postbuild.split():
1025 #
1026 # Do not modify Arg if it looks like a flag or an absolute file path
1027 #
1028 if Arg.startswith('-') or os.path.isabs(Arg):
1029 PostbuildList.append(Arg)
1030 continue
1031 #
1032 # Do not modify Arg if it does not look like a Workspace relative
1033 # path that starts with a valid package directory name
1034 #
1035 if not Arg[0].isalpha() or os.path.dirname(Arg) == '':
1036 PostbuildList.append(Arg)
1037 continue
1038 #
1039 # If Arg looks like a WORKSPACE relative path, then convert to an
1040 # absolute path and check to see if the file exists.
1041 #
1042 Temp = mws.join(self.WorkspaceDir, Arg)
1043 if os.path.isfile(Temp):
1044 Arg = Temp
1045 PostbuildList.append(Arg)
1046 self.Postbuild = ' '.join(PostbuildList)
1047 self.Postbuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)
1048
1049 def PassCommandOption(self, BuildTarget, TargetArch, ToolChain, PlatformFile, Target):
1050 BuildStr = ''
1051 if GlobalData.gCommand and isinstance(GlobalData.gCommand, list):
1052 BuildStr += ' ' + ' '.join(GlobalData.gCommand)
1053 TargetFlag = False
1054 ArchFlag = False
1055 ToolChainFlag = False
1056 PlatformFileFlag = False
1057
1058 if GlobalData.gOptions and not GlobalData.gOptions.BuildTarget:
1059 TargetFlag = True
1060 if GlobalData.gOptions and not GlobalData.gOptions.TargetArch:
1061 ArchFlag = True
1062 if GlobalData.gOptions and not GlobalData.gOptions.ToolChain:
1063 ToolChainFlag = True
1064 if GlobalData.gOptions and not GlobalData.gOptions.PlatformFile:
1065 PlatformFileFlag = True
1066
1067 if TargetFlag and BuildTarget:
1068 if isinstance(BuildTarget, list) or isinstance(BuildTarget, tuple):
1069 BuildStr += ' -b ' + ' -b '.join(BuildTarget)
1070 elif isinstance(BuildTarget, str):
1071 BuildStr += ' -b ' + BuildTarget
1072 if ArchFlag and TargetArch:
1073 if isinstance(TargetArch, list) or isinstance(TargetArch, tuple):
1074 BuildStr += ' -a ' + ' -a '.join(TargetArch)
1075 elif isinstance(TargetArch, str):
1076 BuildStr += ' -a ' + TargetArch
1077 if ToolChainFlag and ToolChain:
1078 if isinstance(ToolChain, list) or isinstance(ToolChain, tuple):
1079 BuildStr += ' -t ' + ' -t '.join(ToolChain)
1080 elif isinstance(ToolChain, str):
1081 BuildStr += ' -t ' + ToolChain
1082 if PlatformFileFlag and PlatformFile:
1083 if isinstance(PlatformFile, list) or isinstance(PlatformFile, tuple):
1084 BuildStr += ' -p ' + ' -p '.join(PlatformFile)
1085 elif isinstance(PlatformFile, str):
1086 BuildStr += ' -p' + PlatformFile
1087 BuildStr += ' --conf=' + GlobalData.gConfDirectory
1088 if Target:
1089 BuildStr += ' ' + Target
1090
1091 return BuildStr
1092
1093 def LaunchPrebuild(self):
1094 if self.Prebuild:
1095 EdkLogger.info("\n- Prebuild Start -\n")
1096 self.LaunchPrebuildFlag = True
1097 #
1098 # The purpose of .PrebuildEnv file is capture environment variable settings set by the prebuild script
1099 # and preserve them for the rest of the main build step, because the child process environment will
1100 # evaporate as soon as it exits, we cannot get it in build step.
1101 #
1102 PrebuildEnvFile = os.path.join(GlobalData.gConfDirectory, '.cache', '.PrebuildEnv')
1103 if os.path.isfile(PrebuildEnvFile):
1104 os.remove(PrebuildEnvFile)
1105 if os.path.isfile(self.PlatformBuildPath):
1106 os.remove(self.PlatformBuildPath)
1107 if sys.platform == "win32":
1108 args = ' && '.join((self.Prebuild, 'set > ' + PrebuildEnvFile))
1109 Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)
1110 else:
1111 args = ' && '.join((self.Prebuild, 'env > ' + PrebuildEnvFile))
1112 Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)
1113
1114 # launch two threads to read the STDOUT and STDERR
1115 EndOfProcedure = Event()
1116 EndOfProcedure.clear()
1117 if Process.stdout:
1118 StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))
1119 StdOutThread.setName("STDOUT-Redirector")
1120 StdOutThread.setDaemon(False)
1121 StdOutThread.start()
1122
1123 if Process.stderr:
1124 StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))
1125 StdErrThread.setName("STDERR-Redirector")
1126 StdErrThread.setDaemon(False)
1127 StdErrThread.start()
1128 # waiting for program exit
1129 Process.wait()
1130
1131 if Process.stdout:
1132 StdOutThread.join()
1133 if Process.stderr:
1134 StdErrThread.join()
1135 if Process.returncode != 0 :
1136 EdkLogger.error("Prebuild", PREBUILD_ERROR, 'Prebuild process is not success!')
1137
1138 if os.path.exists(PrebuildEnvFile):
1139 f = open(PrebuildEnvFile)
1140 envs = f.readlines()
1141 f.close()
1142 envs = itertools.imap(lambda l: l.split('=', 1), envs)
1143 envs = itertools.ifilter(lambda l: len(l) == 2, envs)
1144 envs = itertools.imap(lambda l: [i.strip() for i in l], envs)
1145 os.environ.update(dict(envs))
1146 EdkLogger.info("\n- Prebuild Done -\n")
1147
1148 def LaunchPostbuild(self):
1149 if self.Postbuild:
1150 EdkLogger.info("\n- Postbuild Start -\n")
1151 if sys.platform == "win32":
1152 Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)
1153 else:
1154 Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)
1155 # launch two threads to read the STDOUT and STDERR
1156 EndOfProcedure = Event()
1157 EndOfProcedure.clear()
1158 if Process.stdout:
1159 StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))
1160 StdOutThread.setName("STDOUT-Redirector")
1161 StdOutThread.setDaemon(False)
1162 StdOutThread.start()
1163
1164 if Process.stderr:
1165 StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))
1166 StdErrThread.setName("STDERR-Redirector")
1167 StdErrThread.setDaemon(False)
1168 StdErrThread.start()
1169 # waiting for program exit
1170 Process.wait()
1171
1172 if Process.stdout:
1173 StdOutThread.join()
1174 if Process.stderr:
1175 StdErrThread.join()
1176 if Process.returncode != 0 :
1177 EdkLogger.error("Postbuild", POSTBUILD_ERROR, 'Postbuild process is not success!')
1178 EdkLogger.info("\n- Postbuild Done -\n")
1179 ## Build a module or platform
1180 #
1181 # Create autogen code and makefile for a module or platform, and the launch
1182 # "make" command to build it
1183 #
1184 # @param Target The target of build command
1185 # @param Platform The platform file
1186 # @param Module The module file
1187 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
1188 # @param ToolChain The name of toolchain to build
1189 # @param Arch The arch of the module/platform
1190 # @param CreateDepModuleCodeFile Flag used to indicate creating code
1191 # for dependent modules/Libraries
1192 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
1193 # for dependent modules/Libraries
1194 #
1195 def _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand={}):
1196 if AutoGenObject is None:
1197 return False
1198
1199 # skip file generation for cleanxxx targets, run and fds target
1200 if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1201 # for target which must generate AutoGen code and makefile
1202 if not self.SkipAutoGen or Target == 'genc':
1203 self.Progress.Start("Generating code")
1204 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)
1205 self.Progress.Stop("done!")
1206 if Target == "genc":
1207 return True
1208
1209 if not self.SkipAutoGen or Target == 'genmake':
1210 self.Progress.Start("Generating makefile")
1211 AutoGenObject.CreateMakeFile(CreateDepsMakeFile, FfsCommand)
1212 self.Progress.Stop("done!")
1213 if Target == "genmake":
1214 return True
1215 else:
1216 # always recreate top/platform makefile when clean, just in case of inconsistency
1217 AutoGenObject.CreateCodeFile(False)
1218 AutoGenObject.CreateMakeFile(False)
1219
1220 if EdkLogger.GetLevel() == EdkLogger.QUIET:
1221 EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
1222
1223 BuildCommand = AutoGenObject.BuildCommand
1224 if BuildCommand is None or len(BuildCommand) == 0:
1225 EdkLogger.error("build", OPTION_MISSING,
1226 "No build command found for this module. "
1227 "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
1228 (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),
1229 ExtraData=str(AutoGenObject))
1230
1231 makefile = GenMake.BuildFile(AutoGenObject)._FILE_NAME_[GenMake.gMakeType]
1232
1233 # run
1234 if Target == 'run':
1235 RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))
1236 Command = '.\SecMain'
1237 os.chdir(RunDir)
1238 LaunchCommand(Command, RunDir)
1239 return True
1240
1241 # build modules
1242 if BuildModule:
1243 BuildCommand = BuildCommand + [Target]
1244 LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
1245 self.CreateAsBuiltInf()
1246 return True
1247
1248 # build library
1249 if Target == 'libraries':
1250 for Lib in AutoGenObject.LibraryBuildDirectoryList:
1251 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']
1252 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1253 return True
1254
1255 # build module
1256 if Target == 'modules':
1257 for Lib in AutoGenObject.LibraryBuildDirectoryList:
1258 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']
1259 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1260 for Mod in AutoGenObject.ModuleBuildDirectoryList:
1261 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, makefile)), 'pbuild']
1262 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1263 self.CreateAsBuiltInf()
1264 return True
1265
1266 # cleanlib
1267 if Target == 'cleanlib':
1268 for Lib in AutoGenObject.LibraryBuildDirectoryList:
1269 LibMakefile = os.path.normpath(os.path.join(Lib, makefile))
1270 if os.path.exists(LibMakefile):
1271 NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']
1272 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1273 return True
1274
1275 # clean
1276 if Target == 'clean':
1277 for Mod in AutoGenObject.ModuleBuildDirectoryList:
1278 ModMakefile = os.path.normpath(os.path.join(Mod, makefile))
1279 if os.path.exists(ModMakefile):
1280 NewBuildCommand = BuildCommand + ['-f', ModMakefile, 'cleanall']
1281 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1282 for Lib in AutoGenObject.LibraryBuildDirectoryList:
1283 LibMakefile = os.path.normpath(os.path.join(Lib, makefile))
1284 if os.path.exists(LibMakefile):
1285 NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']
1286 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1287 return True
1288
1289 # cleanall
1290 if Target == 'cleanall':
1291 try:
1292 #os.rmdir(AutoGenObject.BuildDir)
1293 RemoveDirectory(AutoGenObject.BuildDir, True)
1294 except WindowsError as X:
1295 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
1296 return True
1297
1298 ## Build a module or platform
1299 #
1300 # Create autogen code and makefile for a module or platform, and the launch
1301 # "make" command to build it
1302 #
1303 # @param Target The target of build command
1304 # @param Platform The platform file
1305 # @param Module The module file
1306 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
1307 # @param ToolChain The name of toolchain to build
1308 # @param Arch The arch of the module/platform
1309 # @param CreateDepModuleCodeFile Flag used to indicate creating code
1310 # for dependent modules/Libraries
1311 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
1312 # for dependent modules/Libraries
1313 #
1314 def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False):
1315 if AutoGenObject is None:
1316 return False
1317
1318 # skip file generation for cleanxxx targets, run and fds target
1319 if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1320 # for target which must generate AutoGen code and makefile
1321 if not self.SkipAutoGen or Target == 'genc':
1322 self.Progress.Start("Generating code")
1323 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)
1324 self.Progress.Stop("done!")
1325 if Target == "genc":
1326 return True
1327
1328 if not self.SkipAutoGen or Target == 'genmake':
1329 self.Progress.Start("Generating makefile")
1330 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)
1331 #AutoGenObject.CreateAsBuiltInf()
1332 self.Progress.Stop("done!")
1333 if Target == "genmake":
1334 return True
1335 else:
1336 # always recreate top/platform makefile when clean, just in case of inconsistency
1337 AutoGenObject.CreateCodeFile(False)
1338 AutoGenObject.CreateMakeFile(False)
1339
1340 if EdkLogger.GetLevel() == EdkLogger.QUIET:
1341 EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
1342
1343 BuildCommand = AutoGenObject.BuildCommand
1344 if BuildCommand is None or len(BuildCommand) == 0:
1345 EdkLogger.error("build", OPTION_MISSING,
1346 "No build command found for this module. "
1347 "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
1348 (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),
1349 ExtraData=str(AutoGenObject))
1350
1351 # build modules
1352 if BuildModule:
1353 if Target != 'fds':
1354 BuildCommand = BuildCommand + [Target]
1355 AutoGenObject.BuildTime = LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
1356 self.CreateAsBuiltInf()
1357 return True
1358
1359 # genfds
1360 if Target == 'fds':
1361 if GenFdsApi(AutoGenObject.GenFdsCommandDict, self.Db):
1362 EdkLogger.error("build", COMMAND_FAILURE)
1363 return True
1364
1365 # run
1366 if Target == 'run':
1367 RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))
1368 Command = '.\SecMain'
1369 os.chdir(RunDir)
1370 LaunchCommand(Command, RunDir)
1371 return True
1372
1373 # build library
1374 if Target == 'libraries':
1375 pass
1376
1377 # not build modules
1378
1379
1380 # cleanall
1381 if Target == 'cleanall':
1382 try:
1383 #os.rmdir(AutoGenObject.BuildDir)
1384 RemoveDirectory(AutoGenObject.BuildDir, True)
1385 except WindowsError as X:
1386 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
1387 return True
1388
1389 ## Rebase module image and Get function address for the input module list.
1390 #
1391 def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):
1392 if ModeIsSmm:
1393 AddrIsOffset = False
1394 for InfFile in ModuleList:
1395 sys.stdout.write (".")
1396 sys.stdout.flush()
1397 ModuleInfo = ModuleList[InfFile]
1398 ModuleName = ModuleInfo.BaseName
1399 ModuleOutputImage = ModuleInfo.Image.FileName
1400 ModuleDebugImage = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi')
1401 ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1402 if not ModeIsSmm:
1403 BaseAddress = BaseAddress - ModuleInfo.Image.Size
1404 #
1405 # Update Image to new BaseAddress by GenFw tool
1406 #
1407 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)
1408 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)
1409 else:
1410 #
1411 # Set new address to the section header only for SMM driver.
1412 #
1413 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)
1414 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)
1415 #
1416 # Collect funtion address from Map file
1417 #
1418 ImageMapTable = ModuleOutputImage.replace('.efi', '.map')
1419 FunctionList = []
1420 if os.path.exists(ImageMapTable):
1421 OrigImageBaseAddress = 0
1422 ImageMap = open(ImageMapTable, 'r')
1423 for LinStr in ImageMap:
1424 if len (LinStr.strip()) == 0:
1425 continue
1426 #
1427 # Get the preferred address set on link time.
1428 #
1429 if LinStr.find ('Preferred load address is') != -1:
1430 StrList = LinStr.split()
1431 OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)
1432
1433 StrList = LinStr.split()
1434 if len (StrList) > 4:
1435 if StrList[3] == 'f' or StrList[3] == 'F':
1436 Name = StrList[1]
1437 RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress
1438 FunctionList.append ((Name, RelativeAddress))
1439
1440 ImageMap.close()
1441 #
1442 # Add general information.
1443 #
1444 if ModeIsSmm:
1445 MapBuffer.write('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))
1446 elif AddrIsOffset:
1447 MapBuffer.write('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))
1448 else:
1449 MapBuffer.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))
1450 #
1451 # Add guid and general seciton section.
1452 #
1453 TextSectionAddress = 0
1454 DataSectionAddress = 0
1455 for SectionHeader in ModuleInfo.Image.SectionHeaderList:
1456 if SectionHeader[0] == '.text':
1457 TextSectionAddress = SectionHeader[1]
1458 elif SectionHeader[0] in ['.data', '.sdata']:
1459 DataSectionAddress = SectionHeader[1]
1460 if AddrIsOffset:
1461 MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress)))
1462 else:
1463 MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress))
1464 #
1465 # Add debug image full path.
1466 #
1467 MapBuffer.write('(IMAGE=%s)\n\n' % (ModuleDebugImage))
1468 #
1469 # Add funtion address
1470 #
1471 for Function in FunctionList:
1472 if AddrIsOffset:
1473 MapBuffer.write(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))
1474 else:
1475 MapBuffer.write(' 0x%010X %s\n' % (BaseAddress + Function[1], Function[0]))
1476 ImageMap.close()
1477
1478 #
1479 # for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1480 #
1481 if ModeIsSmm:
1482 BaseAddress = BaseAddress + ModuleInfo.Image.Size
1483
1484 ## Collect MAP information of all FVs
1485 #
1486 def _CollectFvMapBuffer (self, MapBuffer, Wa, ModuleList):
1487 if self.Fdf:
1488 # First get the XIP base address for FV map file.
1489 GuidPattern = re.compile("[-a-fA-F0-9]+")
1490 GuidName = re.compile("\(GUID=[-a-fA-F0-9]+")
1491 for FvName in Wa.FdfProfile.FvDict:
1492 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')
1493 if not os.path.exists(FvMapBuffer):
1494 continue
1495 FvMap = open(FvMapBuffer, 'r')
1496 #skip FV size information
1497 FvMap.readline()
1498 FvMap.readline()
1499 FvMap.readline()
1500 FvMap.readline()
1501 for Line in FvMap:
1502 MatchGuid = GuidPattern.match(Line)
1503 if MatchGuid is not None:
1504 #
1505 # Replace GUID with module name
1506 #
1507 GuidString = MatchGuid.group()
1508 if GuidString.upper() in ModuleList:
1509 Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)
1510 MapBuffer.write(Line)
1511 #
1512 # Add the debug image full path.
1513 #
1514 MatchGuid = GuidName.match(Line)
1515 if MatchGuid is not None:
1516 GuidString = MatchGuid.group().split("=")[1]
1517 if GuidString.upper() in ModuleList:
1518 MapBuffer.write('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi')))
1519
1520 FvMap.close()
1521
1522 ## Collect MAP information of all modules
1523 #
1524 def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):
1525 sys.stdout.write ("Generate Load Module At Fix Address Map")
1526 sys.stdout.flush()
1527 PatchEfiImageList = []
1528 PeiModuleList = {}
1529 BtModuleList = {}
1530 RtModuleList = {}
1531 SmmModuleList = {}
1532 PeiSize = 0
1533 BtSize = 0
1534 RtSize = 0
1535 # reserve 4K size in SMRAM to make SMM module address not from 0.
1536 SmmSize = 0x1000
1537 for ModuleGuid in ModuleList:
1538 Module = ModuleList[ModuleGuid]
1539 GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)
1540
1541 OutputImageFile = ''
1542 for ResultFile in Module.CodaTargetList:
1543 if str(ResultFile.Target).endswith('.efi'):
1544 #
1545 # module list for PEI, DXE, RUNTIME and SMM
1546 #
1547 OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')
1548 ImageClass = PeImageClass (OutputImageFile)
1549 if not ImageClass.IsValid:
1550 EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)
1551 ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, Module.DebugDir, ImageClass)
1552 if Module.ModuleType in [SUP_MODULE_PEI_CORE, SUP_MODULE_PEIM, EDK_COMPONENT_TYPE_COMBINED_PEIM_DRIVER, EDK_COMPONENT_TYPE_PIC_PEIM, EDK_COMPONENT_TYPE_RELOCATABLE_PEIM, SUP_MODULE_DXE_CORE]:
1553 PeiModuleList[Module.MetaFile] = ImageInfo
1554 PeiSize += ImageInfo.Image.Size
1555 elif Module.ModuleType in [EDK_COMPONENT_TYPE_BS_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_UEFI_DRIVER]:
1556 BtModuleList[Module.MetaFile] = ImageInfo
1557 BtSize += ImageInfo.Image.Size
1558 elif Module.ModuleType in [SUP_MODULE_DXE_RUNTIME_DRIVER, EDK_COMPONENT_TYPE_RT_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, EDK_COMPONENT_TYPE_SAL_RT_DRIVER]:
1559 RtModuleList[Module.MetaFile] = ImageInfo
1560 RtSize += ImageInfo.Image.Size
1561 elif Module.ModuleType in [SUP_MODULE_SMM_CORE, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE, SUP_MODULE_MM_CORE_STANDALONE]:
1562 SmmModuleList[Module.MetaFile] = ImageInfo
1563 SmmSize += ImageInfo.Image.Size
1564 if Module.ModuleType == SUP_MODULE_DXE_SMM_DRIVER:
1565 PiSpecVersion = Module.Module.Specification.get('PI_SPECIFICATION_VERSION', '0x00000000')
1566 # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.
1567 if int(PiSpecVersion, 16) < 0x0001000A:
1568 BtModuleList[Module.MetaFile] = ImageInfo
1569 BtSize += ImageInfo.Image.Size
1570 break
1571 #
1572 # EFI image is final target.
1573 # Check EFI image contains patchable FixAddress related PCDs.
1574 #
1575 if OutputImageFile != '':
1576 ModuleIsPatch = False
1577 for Pcd in Module.ModulePcdList:
1578 if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:
1579 ModuleIsPatch = True
1580 break
1581 if not ModuleIsPatch:
1582 for Pcd in Module.LibraryPcdList:
1583 if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:
1584 ModuleIsPatch = True
1585 break
1586
1587 if not ModuleIsPatch:
1588 continue
1589 #
1590 # Module includes the patchable load fix address PCDs.
1591 # It will be fixed up later.
1592 #
1593 PatchEfiImageList.append (OutputImageFile)
1594
1595 #
1596 # Get Top Memory address
1597 #
1598 ReservedRuntimeMemorySize = 0
1599 TopMemoryAddress = 0
1600 if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:
1601 TopMemoryAddress = 0
1602 else:
1603 TopMemoryAddress = self.LoadFixAddress
1604 if TopMemoryAddress < RtSize + BtSize + PeiSize:
1605 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")
1606
1607 #
1608 # Patch FixAddress related PCDs into EFI image
1609 #
1610 for EfiImage in PatchEfiImageList:
1611 EfiImageMap = EfiImage.replace('.efi', '.map')
1612 if not os.path.exists(EfiImageMap):
1613 continue
1614 #
1615 # Get PCD offset in EFI image by GenPatchPcdTable function
1616 #
1617 PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage)
1618 #
1619 # Patch real PCD value by PatchPcdValue tool
1620 #
1621 for PcdInfo in PcdTable:
1622 ReturnValue = 0
1623 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:
1624 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize / 0x1000))
1625 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:
1626 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize / 0x1000))
1627 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:
1628 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize / 0x1000))
1629 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:
1630 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize / 0x1000))
1631 if ReturnValue != 0:
1632 EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)
1633
1634 MapBuffer.write('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize / 0x1000))
1635 MapBuffer.write('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize / 0x1000))
1636 MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize / 0x1000))
1637 if len (SmmModuleList) > 0:
1638 MapBuffer.write('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize / 0x1000))
1639
1640 PeiBaseAddr = TopMemoryAddress - RtSize - BtSize
1641 BtBaseAddr = TopMemoryAddress - RtSize
1642 RtBaseAddr = TopMemoryAddress - ReservedRuntimeMemorySize
1643
1644 self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)
1645 self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)
1646 self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)
1647 self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset=False, ModeIsSmm=True)
1648 MapBuffer.write('\n\n')
1649 sys.stdout.write ("\n")
1650 sys.stdout.flush()
1651
1652 ## Save platform Map file
1653 #
1654 def _SaveMapFile (self, MapBuffer, Wa):
1655 #
1656 # Map file path is got.
1657 #
1658 MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')
1659 #
1660 # Save address map into MAP file.
1661 #
1662 SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)
1663 MapBuffer.close()
1664 if self.LoadFixAddress != 0:
1665 sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath))
1666 sys.stdout.flush()
1667
1668 ## Build active platform for different build targets and different tool chains
1669 #
1670 def _BuildPlatform(self):
1671 SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)
1672 for BuildTarget in self.BuildTargetList:
1673 GlobalData.gGlobalDefines['TARGET'] = BuildTarget
1674 index = 0
1675 for ToolChain in self.ToolChainList:
1676 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
1677 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
1678 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
1679 index += 1
1680 Wa = WorkspaceAutoGen(
1681 self.WorkspaceDir,
1682 self.PlatformFile,
1683 BuildTarget,
1684 ToolChain,
1685 self.ArchList,
1686 self.BuildDatabase,
1687 self.TargetTxt,
1688 self.ToolDef,
1689 self.Fdf,
1690 self.FdList,
1691 self.FvList,
1692 self.CapList,
1693 self.SkuId,
1694 self.UniFlag,
1695 self.Progress
1696 )
1697 self.Fdf = Wa.FdfFile
1698 self.LoadFixAddress = Wa.Platform.LoadFixAddress
1699 self.BuildReport.AddPlatformReport(Wa)
1700 self.Progress.Stop("done!")
1701
1702 # Add ffs build to makefile
1703 CmdListDict = {}
1704 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
1705 CmdListDict = self._GenFfsCmd()
1706
1707 for Arch in Wa.ArchList:
1708 GlobalData.gGlobalDefines['ARCH'] = Arch
1709 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
1710 for Module in Pa.Platform.Modules:
1711 # Get ModuleAutoGen object to generate C code file and makefile
1712 Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)
1713 if Ma is None:
1714 continue
1715 self.BuildModules.append(Ma)
1716 self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict)
1717
1718 # Create MAP file when Load Fix Address is enabled.
1719 if self.Target in ["", "all", "fds"]:
1720 for Arch in Wa.ArchList:
1721 GlobalData.gGlobalDefines['ARCH'] = Arch
1722 #
1723 # Check whether the set fix address is above 4G for 32bit image.
1724 #
1725 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
1726 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platform with IA32 or ARM arch modules")
1727 #
1728 # Get Module List
1729 #
1730 ModuleList = {}
1731 for Pa in Wa.AutoGenObjectList:
1732 for Ma in Pa.ModuleAutoGenList:
1733 if Ma is None:
1734 continue
1735 if not Ma.IsLibrary:
1736 ModuleList[Ma.Guid.upper()] = Ma
1737
1738 MapBuffer = BytesIO('')
1739 if self.LoadFixAddress != 0:
1740 #
1741 # Rebase module to the preferred memory address before GenFds
1742 #
1743 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
1744 if self.Fdf:
1745 #
1746 # create FDS again for the updated EFI image
1747 #
1748 self._Build("fds", Wa)
1749 #
1750 # Create MAP file for all platform FVs after GenFds.
1751 #
1752 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
1753 #
1754 # Save MAP buffer into MAP file.
1755 #
1756 self._SaveMapFile (MapBuffer, Wa)
1757
1758 ## Build active module for different build targets, different tool chains and different archs
1759 #
1760 def _BuildModule(self):
1761 for BuildTarget in self.BuildTargetList:
1762 GlobalData.gGlobalDefines['TARGET'] = BuildTarget
1763 index = 0
1764 for ToolChain in self.ToolChainList:
1765 WorkspaceAutoGenTime = time.time()
1766 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
1767 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
1768 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
1769 index += 1
1770 #
1771 # module build needs platform build information, so get platform
1772 # AutoGen first
1773 #
1774 Wa = WorkspaceAutoGen(
1775 self.WorkspaceDir,
1776 self.PlatformFile,
1777 BuildTarget,
1778 ToolChain,
1779 self.ArchList,
1780 self.BuildDatabase,
1781 self.TargetTxt,
1782 self.ToolDef,
1783 self.Fdf,
1784 self.FdList,
1785 self.FvList,
1786 self.CapList,
1787 self.SkuId,
1788 self.UniFlag,
1789 self.Progress,
1790 self.ModuleFile
1791 )
1792 self.Fdf = Wa.FdfFile
1793 self.LoadFixAddress = Wa.Platform.LoadFixAddress
1794 Wa.CreateMakeFile(False)
1795 # Add ffs build to makefile
1796 CmdListDict = None
1797 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
1798 CmdListDict = self._GenFfsCmd()
1799 self.Progress.Stop("done!")
1800 MaList = []
1801 ExitFlag = threading.Event()
1802 ExitFlag.clear()
1803 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))
1804 for Arch in Wa.ArchList:
1805 AutoGenStart = time.time()
1806 GlobalData.gGlobalDefines['ARCH'] = Arch
1807 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
1808 for Module in Pa.Platform.Modules:
1809 if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name:
1810 Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)
1811 if Ma is None: continue
1812 MaList.append(Ma)
1813 if Ma.CanSkipbyHash():
1814 self.HashSkipModules.append(Ma)
1815 continue
1816 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
1817 if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1818 # for target which must generate AutoGen code and makefile
1819 if not self.SkipAutoGen or self.Target == 'genc':
1820 self.Progress.Start("Generating code")
1821 Ma.CreateCodeFile(True)
1822 self.Progress.Stop("done!")
1823 if self.Target == "genc":
1824 return True
1825 if not self.SkipAutoGen or self.Target == 'genmake':
1826 self.Progress.Start("Generating makefile")
1827 if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:
1828 Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])
1829 del CmdListDict[Module.File, Arch]
1830 else:
1831 Ma.CreateMakeFile(True)
1832 self.Progress.Stop("done!")
1833 if self.Target == "genmake":
1834 return True
1835 self.BuildModules.append(Ma)
1836 self.AutoGenTime += int(round((time.time() - AutoGenStart)))
1837 MakeStart = time.time()
1838 for Ma in self.BuildModules:
1839 if not Ma.IsBinaryModule:
1840 Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))
1841 # Break build if any build thread has error
1842 if BuildTask.HasError():
1843 # we need a full version of makefile for platform
1844 ExitFlag.set()
1845 BuildTask.WaitForComplete()
1846 Pa.CreateMakeFile(False)
1847 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1848 # Start task scheduler
1849 if not BuildTask.IsOnGoing():
1850 BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)
1851
1852 # in case there's an interruption. we need a full version of makefile for platform
1853 Pa.CreateMakeFile(False)
1854 if BuildTask.HasError():
1855 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1856 self.MakeTime += int(round((time.time() - MakeStart)))
1857
1858 MakeContiue = time.time()
1859 ExitFlag.set()
1860 BuildTask.WaitForComplete()
1861 self.CreateAsBuiltInf()
1862 self.MakeTime += int(round((time.time() - MakeContiue)))
1863 if BuildTask.HasError():
1864 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1865
1866 self.BuildReport.AddPlatformReport(Wa, MaList)
1867 if MaList == []:
1868 EdkLogger.error(
1869 'build',
1870 BUILD_ERROR,
1871 "Module for [%s] is not a component of active platform."\
1872 " Please make sure that the ARCH and inf file path are"\
1873 " given in the same as in [%s]" % \
1874 (', '.join(Wa.ArchList), self.PlatformFile),
1875 ExtraData=self.ModuleFile
1876 )
1877 # Create MAP file when Load Fix Address is enabled.
1878 if self.Target == "fds" and self.Fdf:
1879 for Arch in Wa.ArchList:
1880 #
1881 # Check whether the set fix address is above 4G for 32bit image.
1882 #
1883 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
1884 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")
1885 #
1886 # Get Module List
1887 #
1888 ModuleList = {}
1889 for Pa in Wa.AutoGenObjectList:
1890 for Ma in Pa.ModuleAutoGenList:
1891 if Ma is None:
1892 continue
1893 if not Ma.IsLibrary:
1894 ModuleList[Ma.Guid.upper()] = Ma
1895
1896 MapBuffer = BytesIO('')
1897 if self.LoadFixAddress != 0:
1898 #
1899 # Rebase module to the preferred memory address before GenFds
1900 #
1901 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
1902 #
1903 # create FDS again for the updated EFI image
1904 #
1905 GenFdsStart = time.time()
1906 self._Build("fds", Wa)
1907 self.GenFdsTime += int(round((time.time() - GenFdsStart)))
1908 #
1909 # Create MAP file for all platform FVs after GenFds.
1910 #
1911 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
1912 #
1913 # Save MAP buffer into MAP file.
1914 #
1915 self._SaveMapFile (MapBuffer, Wa)
1916
1917 def _GenFfsCmd(self):
1918 # convert dictionary of Cmd:(Inf,Arch)
1919 # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd...
1920 CmdSetDict = defaultdict(set)
1921 GenFfsDict = GenFds.GenFfsMakefile('', GlobalData.gFdfParser, self, self.ArchList, GlobalData)
1922 for Cmd in GenFfsDict:
1923 tmpInf, tmpArch = GenFfsDict[Cmd]
1924 CmdSetDict[tmpInf, tmpArch].add(Cmd)
1925 return CmdSetDict
1926
1927 ## Build a platform in multi-thread mode
1928 #
1929 def _MultiThreadBuildPlatform(self):
1930 SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)
1931 for BuildTarget in self.BuildTargetList:
1932 GlobalData.gGlobalDefines['TARGET'] = BuildTarget
1933 index = 0
1934 for ToolChain in self.ToolChainList:
1935 WorkspaceAutoGenTime = time.time()
1936 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
1937 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
1938 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
1939 index += 1
1940 Wa = WorkspaceAutoGen(
1941 self.WorkspaceDir,
1942 self.PlatformFile,
1943 BuildTarget,
1944 ToolChain,
1945 self.ArchList,
1946 self.BuildDatabase,
1947 self.TargetTxt,
1948 self.ToolDef,
1949 self.Fdf,
1950 self.FdList,
1951 self.FvList,
1952 self.CapList,
1953 self.SkuId,
1954 self.UniFlag,
1955 self.Progress
1956 )
1957 self.Fdf = Wa.FdfFile
1958 self.LoadFixAddress = Wa.Platform.LoadFixAddress
1959 self.BuildReport.AddPlatformReport(Wa)
1960 Wa.CreateMakeFile(False)
1961
1962 # Add ffs build to makefile
1963 CmdListDict = None
1964 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
1965 CmdListDict = self._GenFfsCmd()
1966
1967 # multi-thread exit flag
1968 ExitFlag = threading.Event()
1969 ExitFlag.clear()
1970 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))
1971 for Arch in Wa.ArchList:
1972 AutoGenStart = time.time()
1973 GlobalData.gGlobalDefines['ARCH'] = Arch
1974 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
1975 if Pa is None:
1976 continue
1977 ModuleList = []
1978 for Inf in Pa.Platform.Modules:
1979 ModuleList.append(Inf)
1980 # Add the INF only list in FDF
1981 if GlobalData.gFdfParser is not None:
1982 for InfName in GlobalData.gFdfParser.Profile.InfList:
1983 Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch)
1984 if Inf in Pa.Platform.Modules:
1985 continue
1986 ModuleList.append(Inf)
1987 for Module in ModuleList:
1988 # Get ModuleAutoGen object to generate C code file and makefile
1989 Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)
1990
1991 if Ma is None:
1992 continue
1993 if Ma.CanSkipbyHash():
1994 self.HashSkipModules.append(Ma)
1995 continue
1996
1997 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
1998 if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1999 # for target which must generate AutoGen code and makefile
2000 if not self.SkipAutoGen or self.Target == 'genc':
2001 Ma.CreateCodeFile(True)
2002 if self.Target == "genc":
2003 continue
2004
2005 if not self.SkipAutoGen or self.Target == 'genmake':
2006 if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:
2007 Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])
2008 del CmdListDict[Module.File, Arch]
2009 else:
2010 Ma.CreateMakeFile(True)
2011 if self.Target == "genmake":
2012 continue
2013 self.BuildModules.append(Ma)
2014 self.Progress.Stop("done!")
2015 self.AutoGenTime += int(round((time.time() - AutoGenStart)))
2016 MakeStart = time.time()
2017 for Ma in self.BuildModules:
2018 # Generate build task for the module
2019 if not Ma.IsBinaryModule:
2020 Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))
2021 # Break build if any build thread has error
2022 if BuildTask.HasError():
2023 # we need a full version of makefile for platform
2024 ExitFlag.set()
2025 BuildTask.WaitForComplete()
2026 Pa.CreateMakeFile(False)
2027 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
2028 # Start task scheduler
2029 if not BuildTask.IsOnGoing():
2030 BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)
2031
2032 # in case there's an interruption. we need a full version of makefile for platform
2033 Pa.CreateMakeFile(False)
2034 if BuildTask.HasError():
2035 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
2036 self.MakeTime += int(round((time.time() - MakeStart)))
2037
2038 MakeContiue = time.time()
2039
2040 #
2041 #
2042 # All modules have been put in build tasks queue. Tell task scheduler
2043 # to exit if all tasks are completed
2044 #
2045 ExitFlag.set()
2046 BuildTask.WaitForComplete()
2047 self.CreateAsBuiltInf()
2048 self.MakeTime += int(round((time.time() - MakeContiue)))
2049 #
2050 # Check for build error, and raise exception if one
2051 # has been signaled.
2052 #
2053 if BuildTask.HasError():
2054 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
2055
2056 # Create MAP file when Load Fix Address is enabled.
2057 if self.Target in ["", "all", "fds"]:
2058 for Arch in Wa.ArchList:
2059 #
2060 # Check whether the set fix address is above 4G for 32bit image.
2061 #
2062 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
2063 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")
2064 #
2065 # Get Module List
2066 #
2067 ModuleList = {}
2068 for Pa in Wa.AutoGenObjectList:
2069 for Ma in Pa.ModuleAutoGenList:
2070 if Ma is None:
2071 continue
2072 if not Ma.IsLibrary:
2073 ModuleList[Ma.Guid.upper()] = Ma
2074 #
2075 # Rebase module to the preferred memory address before GenFds
2076 #
2077 MapBuffer = BytesIO('')
2078 if self.LoadFixAddress != 0:
2079 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
2080
2081 if self.Fdf:
2082 #
2083 # Generate FD image if there's a FDF file found
2084 #
2085 GenFdsStart = time.time()
2086 if GenFdsApi(Wa.GenFdsCommandDict, self.Db):
2087 EdkLogger.error("build", COMMAND_FAILURE)
2088
2089 #
2090 # Create MAP file for all platform FVs after GenFds.
2091 #
2092 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
2093 self.GenFdsTime += int(round((time.time() - GenFdsStart)))
2094 #
2095 # Save MAP buffer into MAP file.
2096 #
2097 self._SaveMapFile(MapBuffer, Wa)
2098
2099 ## Generate GuidedSectionTools.txt in the FV directories.
2100 #
2101 def CreateGuidedSectionToolsFile(self):
2102 for BuildTarget in self.BuildTargetList:
2103 for ToolChain in self.ToolChainList:
2104 Wa = WorkspaceAutoGen(
2105 self.WorkspaceDir,
2106 self.PlatformFile,
2107 BuildTarget,
2108 ToolChain,
2109 self.ArchList,
2110 self.BuildDatabase,
2111 self.TargetTxt,
2112 self.ToolDef,
2113 self.Fdf,
2114 self.FdList,
2115 self.FvList,
2116 self.CapList,
2117 self.SkuId,
2118 self.UniFlag
2119 )
2120 FvDir = Wa.FvDir
2121 if not os.path.exists(FvDir):
2122 continue
2123
2124 for Arch in self.ArchList:
2125 # Build up the list of supported architectures for this build
2126 prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)
2127
2128 # Look through the tool definitions for GUIDed tools
2129 guidAttribs = []
2130 for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():
2131 if attrib.upper().endswith('_GUID'):
2132 split = attrib.split('_')
2133 thisPrefix = '_'.join(split[0:3]) + '_'
2134 if thisPrefix == prefix:
2135 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]
2136 guid = guid.lower()
2137 toolName = split[3]
2138 path = '_'.join(split[0:4]) + '_PATH'
2139 path = self.ToolDef.ToolsDefTxtDictionary[path]
2140 path = self.GetFullPathOfTool(path)
2141 guidAttribs.append((guid, toolName, path))
2142
2143 # Write out GuidedSecTools.txt
2144 toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')
2145 toolsFile = open(toolsFile, 'wt')
2146 for guidedSectionTool in guidAttribs:
2147 print(' '.join(guidedSectionTool), file=toolsFile)
2148 toolsFile.close()
2149
2150 ## Returns the full path of the tool.
2151 #
2152 def GetFullPathOfTool (self, tool):
2153 if os.path.exists(tool):
2154 return os.path.realpath(tool)
2155 else:
2156 # We need to search for the tool using the
2157 # PATH environment variable.
2158 for dirInPath in os.environ['PATH'].split(os.pathsep):
2159 foundPath = os.path.join(dirInPath, tool)
2160 if os.path.exists(foundPath):
2161 return os.path.realpath(foundPath)
2162
2163 # If the tool was not found in the path then we just return
2164 # the input tool.
2165 return tool
2166
2167 ## Launch the module or platform build
2168 #
2169 def Launch(self):
2170 if not self.ModuleFile:
2171 if not self.SpawnMode or self.Target not in ["", "all"]:
2172 self.SpawnMode = False
2173 self._BuildPlatform()
2174 else:
2175 self._MultiThreadBuildPlatform()
2176 self.CreateGuidedSectionToolsFile()
2177 else:
2178 self.SpawnMode = False
2179 self._BuildModule()
2180
2181 if self.Target == 'cleanall':
2182 RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True)
2183
2184 def CreateAsBuiltInf(self):
2185 for Module in self.BuildModules:
2186 Module.CreateAsBuiltInf()
2187 for Module in self.HashSkipModules:
2188 Module.CreateAsBuiltInf(True)
2189 self.BuildModules = []
2190 self.HashSkipModules = []
2191 ## Do some clean-up works when error occurred
2192 def Relinquish(self):
2193 OldLogLevel = EdkLogger.GetLevel()
2194 EdkLogger.SetLevel(EdkLogger.ERROR)
2195 #self.DumpBuildData()
2196 Utils.Progressor.Abort()
2197 if self.SpawnMode == True:
2198 BuildTask.Abort()
2199 EdkLogger.SetLevel(OldLogLevel)
2200
2201 def DumpBuildData(self):
2202 CacheDirectory = os.path.dirname(GlobalData.gDatabasePath)
2203 Utils.CreateDirectory(CacheDirectory)
2204 Utils._DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))
2205 Utils._DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))
2206
2207 def RestoreBuildData(self):
2208 FilePath = os.path.join(os.path.dirname(GlobalData.gDatabasePath), "gFileTimeStampCache")
2209 if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):
2210 Utils.gFileTimeStampCache = Utils._DataRestore(FilePath)
2211 if Utils.gFileTimeStampCache is None:
2212 Utils.gFileTimeStampCache = {}
2213
2214 FilePath = os.path.join(os.path.dirname(GlobalData.gDatabasePath), "gDependencyDatabase")
2215 if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):
2216 Utils.gDependencyDatabase = Utils._DataRestore(FilePath)
2217 if Utils.gDependencyDatabase is None:
2218 Utils.gDependencyDatabase = {}
2219
2220 def ParseDefines(DefineList=[]):
2221 DefineDict = {}
2222 if DefineList is not None:
2223 for Define in DefineList:
2224 DefineTokenList = Define.split("=", 1)
2225 if not GlobalData.gMacroNamePattern.match(DefineTokenList[0]):
2226 EdkLogger.error('build', FORMAT_INVALID,
2227 "The macro name must be in the pattern [A-Z][A-Z0-9_]*",
2228 ExtraData=DefineTokenList[0])
2229
2230 if len(DefineTokenList) == 1:
2231 DefineDict[DefineTokenList[0]] = "TRUE"
2232 else:
2233 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()
2234 return DefineDict
2235
2236 gParamCheck = []
2237 def SingleCheckCallback(option, opt_str, value, parser):
2238 if option not in gParamCheck:
2239 setattr(parser.values, option.dest, value)
2240 gParamCheck.append(option)
2241 else:
2242 parser.error("Option %s only allows one instance in command line!" % option)
2243
2244 def LogBuildTime(Time):
2245 if Time:
2246 TimeDurStr = ''
2247 TimeDur = time.gmtime(Time)
2248 if TimeDur.tm_yday > 1:
2249 TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + ", %d day(s)" % (TimeDur.tm_yday - 1)
2250 else:
2251 TimeDurStr = time.strftime("%H:%M:%S", TimeDur)
2252 return TimeDurStr
2253 else:
2254 return None
2255
2256 ## Parse command line options
2257 #
2258 # Using standard Python module optparse to parse command line option of this tool.
2259 #
2260 # @retval Opt A optparse.Values object containing the parsed options
2261 # @retval Args Target of build command
2262 #
2263 def MyOptionParser():
2264 Parser = OptionParser(description=__copyright__, version=__version__, prog="build.exe", usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")
2265 Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32', 'X64', 'EBC', 'ARM', 'AARCH64'], dest="TargetArch",
2266 help="ARCHS is one of list: IA32, X64, ARM, AARCH64 or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")
2267 Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,
2268 help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")
2269 Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,
2270 help="Build the module specified by the INF file name argument.")
2271 Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.",
2272 action="append")
2273 Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",
2274 help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")
2275 Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,
2276 help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")
2277
2278 Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,
2279 help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. When value is set to 0, tool automatically detect number of "\
2280 "processor threads, set value to 1 means disable multi-thread build, and set value to more than 1 means user specify the threads number to build.")
2281
2282 Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,
2283 help="The name of the FDF file to use, which overrides the setting in the DSC file.")
2284 Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],
2285 help="The name of FD to be generated. The name must be from [FD] section in FDF file.")
2286 Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],
2287 help="The name of FV to be generated. The name must be from [FV] section in FDF file.")
2288 Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[],
2289 help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.")
2290 Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")
2291 Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")
2292
2293 Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", default=False, help="Don't check case of file name.")
2294
2295 Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")
2296 Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")
2297
2298 Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",
2299 help="Make use of silent mode of (n)make.")
2300 Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
2301 Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
2302 "including library instances selected, final dependency expression, "\
2303 "and warning messages, etc.")
2304 Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
2305 Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")
2306
2307 Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")
2308 Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD', 'LIBRARY', 'FLASH', 'DEPEX', 'BUILD_FLAGS', 'FIXED_ADDRESS', 'HASH', 'EXECUTION_ORDER'], dest="ReportType", default=[],
2309 help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, HASH, EXECUTION_ORDER]. "\
2310 "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, HASH, BUILD_FLAGS, FIXED_ADDRESS]")
2311 Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",
2312 help="Specify the specific option to parse EDK UNI file. Must be one of: [-c, -s]. -c is for EDK framework UNI file, and -s is for EDK UEFI UNI file. "\
2313 "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\
2314 "will override the setting in [BuildOptions] section of platform DSC.")
2315 Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism")
2316 Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")
2317 Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.")
2318 Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")
2319 Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")
2320 Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.")
2321 Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.")
2322 Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.")
2323 Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.")
2324 Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.")
2325 (Opt, Args) = Parser.parse_args()
2326 return (Opt, Args)
2327
2328 ## Tool entrance method
2329 #
2330 # This method mainly dispatch specific methods per the command line options.
2331 # If no error found, return zero value so the caller of this tool can know
2332 # if it's executed successfully or not.
2333 #
2334 # @retval 0 Tool was successful
2335 # @retval 1 Tool failed
2336 #
2337 def Main():
2338 StartTime = time.time()
2339
2340 # Initialize log system
2341 EdkLogger.Initialize()
2342 GlobalData.gCommand = sys.argv[1:]
2343 #
2344 # Parse the options and args
2345 #
2346 (Option, Target) = MyOptionParser()
2347 GlobalData.gOptions = Option
2348 GlobalData.gCaseInsensitive = Option.CaseInsensitive
2349
2350 # Set log level
2351 if Option.verbose is not None:
2352 EdkLogger.SetLevel(EdkLogger.VERBOSE)
2353 elif Option.quiet is not None:
2354 EdkLogger.SetLevel(EdkLogger.QUIET)
2355 elif Option.debug is not None:
2356 EdkLogger.SetLevel(Option.debug + 1)
2357 else:
2358 EdkLogger.SetLevel(EdkLogger.INFO)
2359
2360 if Option.LogFile is not None:
2361 EdkLogger.SetLogFile(Option.LogFile)
2362
2363 if Option.WarningAsError == True:
2364 EdkLogger.SetWarningAsError()
2365
2366 if platform.platform().find("Windows") >= 0:
2367 GlobalData.gIsWindows = True
2368 else:
2369 GlobalData.gIsWindows = False
2370
2371 EdkLogger.quiet("Build environment: %s" % platform.platform())
2372 EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime()));
2373 ReturnCode = 0
2374 MyBuild = None
2375 BuildError = True
2376 try:
2377 if len(Target) == 0:
2378 Target = "all"
2379 elif len(Target) >= 2:
2380 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",
2381 ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))
2382 else:
2383 Target = Target[0].lower()
2384
2385 if Target not in gSupportedTarget:
2386 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,
2387 ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))
2388
2389 #
2390 # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
2391 #
2392 CheckEnvVariable()
2393 GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros))
2394
2395 Workspace = os.getenv("WORKSPACE")
2396 #
2397 # Get files real name in workspace dir
2398 #
2399 GlobalData.gAllFiles = Utils.DirCache(Workspace)
2400
2401 WorkingDirectory = os.getcwd()
2402 if not Option.ModuleFile:
2403 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))
2404 FileNum = len(FileList)
2405 if FileNum >= 2:
2406 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),
2407 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")
2408 elif FileNum == 1:
2409 Option.ModuleFile = NormFile(FileList[0], Workspace)
2410
2411 if Option.ModuleFile:
2412 if os.path.isabs (Option.ModuleFile):
2413 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:
2414 Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)
2415 Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)
2416 ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)
2417 if ErrorCode != 0:
2418 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
2419
2420 if Option.PlatformFile is not None:
2421 if os.path.isabs (Option.PlatformFile):
2422 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:
2423 Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)
2424 Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)
2425
2426 if Option.FdfFile is not None:
2427 if os.path.isabs (Option.FdfFile):
2428 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:
2429 Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)
2430 Option.FdfFile = PathClass(Option.FdfFile, Workspace)
2431 ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)
2432 if ErrorCode != 0:
2433 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
2434
2435 if Option.Flag is not None and Option.Flag not in ['-c', '-s']:
2436 EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")
2437
2438 MyBuild = Build(Target, Workspace, Option)
2439 GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)
2440 if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):
2441 MyBuild.Launch()
2442
2443 #MyBuild.DumpBuildData()
2444 #
2445 # All job done, no error found and no exception raised
2446 #
2447 BuildError = False
2448 except FatalError as X:
2449 if MyBuild is not None:
2450 # for multi-thread build exits safely
2451 MyBuild.Relinquish()
2452 if Option is not None and Option.debug is not None:
2453 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
2454 ReturnCode = X.args[0]
2455 except Warning as X:
2456 # error from Fdf parser
2457 if MyBuild is not None:
2458 # for multi-thread build exits safely
2459 MyBuild.Relinquish()
2460 if Option is not None and Option.debug is not None:
2461 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
2462 else:
2463 EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)
2464 ReturnCode = FORMAT_INVALID
2465 except KeyboardInterrupt:
2466 ReturnCode = ABORT_ERROR
2467 if Option is not None and Option.debug is not None:
2468 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
2469 except:
2470 if MyBuild is not None:
2471 # for multi-thread build exits safely
2472 MyBuild.Relinquish()
2473
2474 # try to get the meta-file from the object causing exception
2475 Tb = sys.exc_info()[-1]
2476 MetaFile = GlobalData.gProcessingFile
2477 while Tb is not None:
2478 if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):
2479 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile
2480 Tb = Tb.tb_next
2481 EdkLogger.error(
2482 "\nbuild",
2483 CODE_ERROR,
2484 "Unknown fatal error when processing [%s]" % MetaFile,
2485 ExtraData="\n(Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!)\n",
2486 RaiseError=False
2487 )
2488 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
2489 ReturnCode = CODE_ERROR
2490 finally:
2491 Utils.Progressor.Abort()
2492 Utils.ClearDuplicatedInf()
2493
2494 if ReturnCode == 0:
2495 try:
2496 MyBuild.LaunchPostbuild()
2497 Conclusion = "Done"
2498 except:
2499 Conclusion = "Failed"
2500 elif ReturnCode == ABORT_ERROR:
2501 Conclusion = "Aborted"
2502 else:
2503 Conclusion = "Failed"
2504 FinishTime = time.time()
2505 BuildDuration = time.gmtime(int(round(FinishTime - StartTime)))
2506 BuildDurationStr = ""
2507 if BuildDuration.tm_yday > 1:
2508 BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1)
2509 else:
2510 BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration)
2511 if MyBuild is not None:
2512 if not BuildError:
2513 MyBuild.BuildReport.GenerateReport(BuildDurationStr, LogBuildTime(MyBuild.AutoGenTime), LogBuildTime(MyBuild.MakeTime), LogBuildTime(MyBuild.GenFdsTime))
2514
2515 EdkLogger.SetLevel(EdkLogger.QUIET)
2516 EdkLogger.quiet("\n- %s -" % Conclusion)
2517 EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))
2518 EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)
2519 return ReturnCode
2520
2521 if __name__ == '__main__':
2522 r = Main()
2523 ## 0-127 is a safe return range, and 1 is a standard default error
2524 if r < 0 or r > 127: r = 1
2525 sys.exit(r)
2526