2 # build a platform or a module
4 # Copyright (c) 2007 - 2010, Intel Corporation
6 # All rights reserved. This program and the accompanying materials
7 # are licensed and made available under the terms and conditions of the BSD License
8 # which accompanies this distribution. The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.php
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
28 from threading
import *
29 from optparse
import OptionParser
30 from subprocess
import *
31 from Common
import Misc
as Utils
33 from Common
.TargetTxtClassObject
import *
34 from Common
.ToolDefClassObject
import *
35 from Common
.DataType
import *
36 from AutoGen
.AutoGen
import *
37 from Common
.BuildToolError
import *
38 from Workspace
.WorkspaceDatabase
import *
40 from BuildReport
import BuildReport
41 from GenPatchPcdTable
.GenPatchPcdTable
import *
42 from PatchPcdValue
.PatchPcdValue
import *
44 import Common
.EdkLogger
45 import Common
.GlobalData
as GlobalData
47 # Version and Copyright
49 __version__
= "%prog Version " + VersionNumber
50 __copyright__
= "Copyright (c) 2007 - 2010, Intel Corporation All rights reserved."
52 ## standard targets of build command
53 gSupportedTarget
= ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']
55 ## build configuration file
56 gBuildConfiguration
= "Conf/target.txt"
57 gBuildCacheDir
= "Conf/.cache"
58 gToolsDefinition
= "Conf/tools_def.txt"
60 ## Check environment PATH variable to make sure the specified tool is found
62 # If the tool is found in the PATH, then True is returned
63 # Otherwise, False is returned
65 def IsToolInPath(tool
):
66 if os
.environ
.has_key('PATHEXT'):
67 extns
= os
.environ
['PATHEXT'].split(os
.path
.pathsep
)
70 for pathDir
in os
.environ
['PATH'].split(os
.path
.pathsep
):
72 if os
.path
.exists(os
.path
.join(pathDir
, tool
+ ext
)):
76 ## Check environment variables
78 # Check environment variables that must be set for build. Currently they are
80 # WORKSPACE The directory all packages/platforms start from
81 # EDK_TOOLS_PATH The directory contains all tools needed by the build
82 # PATH $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH
84 # If any of above environment variable is not set or has error, the build
87 def CheckEnvVariable():
89 if "WORKSPACE" not in os
.environ
:
90 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
91 ExtraData
="WORKSPACE")
93 WorkspaceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["WORKSPACE"]))
94 if not os
.path
.exists(WorkspaceDir
):
95 EdkLogger
.error("build", FILE_NOT_FOUND
, "WORKSPACE doesn't exist", ExtraData
="%s" % WorkspaceDir
)
96 elif ' ' in WorkspaceDir
:
97 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in WORKSPACE path",
98 ExtraData
=WorkspaceDir
)
99 os
.environ
["WORKSPACE"] = WorkspaceDir
102 # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP
104 os
.environ
["ECP_SOURCE"] = os
.path
.join(WorkspaceDir
, GlobalData
.gEdkCompatibilityPkg
)
105 if "EFI_SOURCE" not in os
.environ
:
106 os
.environ
["EFI_SOURCE"] = os
.environ
["ECP_SOURCE"]
107 if "EDK_SOURCE" not in os
.environ
:
108 os
.environ
["EDK_SOURCE"] = os
.environ
["ECP_SOURCE"]
111 # Unify case of characters on case-insensitive systems
113 EfiSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["EFI_SOURCE"]))
114 EdkSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["EDK_SOURCE"]))
115 EcpSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["ECP_SOURCE"]))
117 os
.environ
["EFI_SOURCE"] = EfiSourceDir
118 os
.environ
["EDK_SOURCE"] = EdkSourceDir
119 os
.environ
["ECP_SOURCE"] = EcpSourceDir
120 os
.environ
["EDK_TOOLS_PATH"] = os
.path
.normcase(os
.environ
["EDK_TOOLS_PATH"])
122 if not os
.path
.exists(EcpSourceDir
):
123 EdkLogger
.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir
)
124 elif ' ' in EcpSourceDir
:
125 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in ECP_SOURCE path",
126 ExtraData
=EcpSourceDir
)
127 if not os
.path
.exists(EdkSourceDir
):
128 if EdkSourceDir
== EcpSourceDir
:
129 EdkLogger
.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir
)
131 EdkLogger
.error("build", PARAMETER_INVALID
, "EDK_SOURCE does not exist",
132 ExtraData
=EdkSourceDir
)
133 elif ' ' in EdkSourceDir
:
134 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in EDK_SOURCE path",
135 ExtraData
=EdkSourceDir
)
136 if not os
.path
.exists(EfiSourceDir
):
137 if EfiSourceDir
== EcpSourceDir
:
138 EdkLogger
.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir
)
140 EdkLogger
.error("build", PARAMETER_INVALID
, "EFI_SOURCE does not exist",
141 ExtraData
=EfiSourceDir
)
142 elif ' ' in EfiSourceDir
:
143 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in EFI_SOURCE path",
144 ExtraData
=EfiSourceDir
)
146 # change absolute path to relative path to WORKSPACE
147 if EfiSourceDir
.upper().find(WorkspaceDir
.upper()) != 0:
148 EdkLogger
.error("build", PARAMETER_INVALID
, "EFI_SOURCE is not under WORKSPACE",
149 ExtraData
="WORKSPACE = %s\n EFI_SOURCE = %s" % (WorkspaceDir
, EfiSourceDir
))
150 if EdkSourceDir
.upper().find(WorkspaceDir
.upper()) != 0:
151 EdkLogger
.error("build", PARAMETER_INVALID
, "EDK_SOURCE is not under WORKSPACE",
152 ExtraData
="WORKSPACE = %s\n EDK_SOURCE = %s" % (WorkspaceDir
, EdkSourceDir
))
153 if EcpSourceDir
.upper().find(WorkspaceDir
.upper()) != 0:
154 EdkLogger
.error("build", PARAMETER_INVALID
, "ECP_SOURCE is not under WORKSPACE",
155 ExtraData
="WORKSPACE = %s\n ECP_SOURCE = %s" % (WorkspaceDir
, EcpSourceDir
))
157 # check EDK_TOOLS_PATH
158 if "EDK_TOOLS_PATH" not in os
.environ
:
159 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
160 ExtraData
="EDK_TOOLS_PATH")
163 if "PATH" not in os
.environ
:
164 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
167 GlobalData
.gWorkspace
= WorkspaceDir
168 GlobalData
.gEfiSource
= EfiSourceDir
169 GlobalData
.gEdkSource
= EdkSourceDir
170 GlobalData
.gEcpSource
= EcpSourceDir
172 ## Get normalized file path
174 # Convert the path to be local format, and remove the WORKSPACE path at the
175 # beginning if the file path is given in full path.
177 # @param FilePath File path to be normalized
178 # @param Workspace Workspace path which the FilePath will be checked against
180 # @retval string The normalized file path
182 def NormFile(FilePath
, Workspace
):
183 # check if the path is absolute or relative
184 if os
.path
.isabs(FilePath
):
185 FileFullPath
= os
.path
.normpath(FilePath
)
187 FileFullPath
= os
.path
.normpath(os
.path
.join(Workspace
, FilePath
))
189 # check if the file path exists or not
190 if not os
.path
.isfile(FileFullPath
):
191 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath
)
193 # remove workspace directory from the beginning part of the file path
194 if Workspace
[-1] in ["\\", "/"]:
195 return FileFullPath
[len(Workspace
):]
197 return FileFullPath
[(len(Workspace
) + 1):]
199 ## Get the output of an external program
201 # This is the entrance method of thread reading output of an external program and
202 # putting them in STDOUT/STDERR of current program.
204 # @param From The stream message read from
205 # @param To The stream message put on
206 # @param ExitFlag The flag used to indicate stopping reading
208 def ReadMessage(From
, To
, ExitFlag
):
210 # read one line a time
211 Line
= From
.readline()
212 # empty string means "end"
213 if Line
!= None and Line
!= "":
220 ## Launch an external program
222 # This method will call subprocess.Popen to execute an external program with
223 # given options in specified directory. Because of the dead-lock issue during
224 # redirecting output of the external program, threads are used to to do the
227 # @param Command A list or string containing the call of the program
228 # @param WorkingDir The directory in which the program will be running
230 def LaunchCommand(Command
, WorkingDir
):
231 # if working directory doesn't exist, Popen() will raise an exception
232 if not os
.path
.isdir(WorkingDir
):
233 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=WorkingDir
)
236 EndOfProcedure
= None
239 Proc
= Popen(Command
, stdout
=PIPE
, stderr
=PIPE
, env
=os
.environ
, cwd
=WorkingDir
, bufsize
=-1)
241 # launch two threads to read the STDOUT and STDERR
242 EndOfProcedure
= Event()
243 EndOfProcedure
.clear()
245 StdOutThread
= Thread(target
=ReadMessage
, args
=(Proc
.stdout
, EdkLogger
.info
, EndOfProcedure
))
246 StdOutThread
.setName("STDOUT-Redirector")
247 StdOutThread
.setDaemon(False)
251 StdErrThread
= Thread(target
=ReadMessage
, args
=(Proc
.stderr
, EdkLogger
.quiet
, EndOfProcedure
))
252 StdErrThread
.setName("STDERR-Redirector")
253 StdErrThread
.setDaemon(False)
256 # waiting for program exit
258 except: # in case of aborting
259 # terminate the threads redirecting the program output
260 if EndOfProcedure
!= None:
263 if type(Command
) != type(""):
264 Command
= " ".join(Command
)
265 EdkLogger
.error("build", COMMAND_FAILURE
, "Failed to start command", ExtraData
="%s [%s]" % (Command
, WorkingDir
))
272 # check the return code of the program
273 if Proc
.returncode
!= 0:
274 if type(Command
) != type(""):
275 Command
= " ".join(Command
)
276 EdkLogger
.error("build", COMMAND_FAILURE
, ExtraData
="%s [%s]" % (Command
, WorkingDir
))
278 ## The smallest unit that can be built in multi-thread build mode
280 # This is the base class of build unit. The "Obj" parameter must provide
281 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units
284 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.
289 # @param self The object pointer
290 # @param Obj The object the build is working on
291 # @param Target The build target name, one of gSupportedTarget
292 # @param Dependency The BuildUnit(s) which must be completed in advance
293 # @param WorkingDir The directory build command starts in
295 def __init__(self
, Obj
, BuildCommand
, Target
, Dependency
, WorkingDir
="."):
296 self
.BuildObject
= Obj
297 self
.Dependency
= Dependency
298 self
.WorkingDir
= WorkingDir
300 self
.BuildCommand
= BuildCommand
301 if BuildCommand
== None or len(BuildCommand
) == 0:
302 EdkLogger
.error("build", OPTION_MISSING
, "No build command found for",
307 # It just returns the string representaion of self.BuildObject
309 # @param self The object pointer
312 return str(self
.BuildObject
)
314 ## "==" operator method
316 # It just compares self.BuildObject with "Other". So self.BuildObject must
317 # provide its own __eq__() method.
319 # @param self The object pointer
320 # @param Other The other BuildUnit object compared to
322 def __eq__(self
, Other
):
323 return Other
!= None and self
.BuildObject
== Other
.BuildObject \
324 and self
.BuildObject
.Arch
== Other
.BuildObject
.Arch
328 # It just returns the hash value of self.BuildObject which must be hashable.
330 # @param self The object pointer
333 return hash(self
.BuildObject
) + hash(self
.BuildObject
.Arch
)
336 return repr(self
.BuildObject
)
338 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode
340 # This class is for module build by nmake/make build system. The "Obj" parameter
341 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
342 # be make units missing build.
344 # Currently the "Obj" should be only ModuleAutoGen object.
346 class ModuleMakeUnit(BuildUnit
):
349 # @param self The object pointer
350 # @param Obj The ModuleAutoGen object the build is working on
351 # @param Target The build target name, one of gSupportedTarget
353 def __init__(self
, Obj
, Target
):
354 Dependency
= [ModuleMakeUnit(La
, Target
) for La
in Obj
.LibraryAutoGenList
]
355 BuildUnit
.__init
__(self
, Obj
, Obj
.BuildCommand
, Target
, Dependency
, Obj
.MakeFileDir
)
356 if Target
in [None, "", "all"]:
357 self
.Target
= "tbuild"
359 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode
361 # This class is for platform build by nmake/make build system. The "Obj" parameter
362 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
363 # be make units missing build.
365 # Currently the "Obj" should be only PlatformAutoGen object.
367 class PlatformMakeUnit(BuildUnit
):
370 # @param self The object pointer
371 # @param Obj The PlatformAutoGen object the build is working on
372 # @param Target The build target name, one of gSupportedTarget
374 def __init__(self
, Obj
, Target
):
375 Dependency
= [ModuleMakeUnit(Lib
, Target
) for Lib
in self
.BuildObject
.LibraryAutoGenList
]
376 Dependency
.extend([ModuleMakeUnit(Mod
, Target
) for Mod
in self
.BuildObject
.ModuleAutoGenList
])
377 BuildUnit
.__init
__(self
, Obj
, Obj
.BuildCommand
, Target
, Dependency
, Obj
.MakeFileDir
)
379 ## The class representing the task of a module build or platform build
381 # This class manages the build tasks in multi-thread build mode. Its jobs include
382 # scheduling thread running, catching thread error, monitor the thread status, etc.
385 # queue for tasks waiting for schedule
386 _PendingQueue
= sdict()
387 _PendingQueueLock
= threading
.Lock()
389 # queue for tasks ready for running
390 _ReadyQueue
= sdict()
391 _ReadyQueueLock
= threading
.Lock()
393 # queue for run tasks
394 _RunningQueue
= sdict()
395 _RunningQueueLock
= threading
.Lock()
397 # queue containing all build tasks, in case duplicate build
400 # flag indicating error occurs in a running thread
401 _ErrorFlag
= threading
.Event()
405 # BoundedSemaphore object used to control the number of running threads
408 # flag indicating if the scheduler is started or not
409 _SchedulerStopped
= threading
.Event()
410 _SchedulerStopped
.set()
412 ## Start the task scheduler thread
414 # @param MaxThreadNumber The maximum thread number
415 # @param ExitFlag Flag used to end the scheduler
418 def StartScheduler(MaxThreadNumber
, ExitFlag
):
419 SchedulerThread
= Thread(target
=BuildTask
.Scheduler
, args
=(MaxThreadNumber
, ExitFlag
))
420 SchedulerThread
.setName("Build-Task-Scheduler")
421 SchedulerThread
.setDaemon(False)
422 SchedulerThread
.start()
423 # wait for the scheduler to be started, especially useful in Linux
424 while not BuildTask
.IsOnGoing():
429 # @param MaxThreadNumber The maximum thread number
430 # @param ExitFlag Flag used to end the scheduler
433 def Scheduler(MaxThreadNumber
, ExitFlag
):
434 BuildTask
._SchedulerStopped
.clear()
436 # use BoundedSemaphore to control the maximum running threads
437 BuildTask
._Thread
= BoundedSemaphore(MaxThreadNumber
)
439 # scheduling loop, which will exits when no pending/ready task and
440 # indicated to do so, or there's error in running thread
442 while (len(BuildTask
._PendingQueue
) > 0 or len(BuildTask
._ReadyQueue
) > 0 \
443 or not ExitFlag
.isSet()) and not BuildTask
._ErrorFlag
.isSet():
444 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "Pending Queue (%d), Ready Queue (%d)"
445 % (len(BuildTask
._PendingQueue
), len(BuildTask
._ReadyQueue
)))
447 # get all pending tasks
448 BuildTask
._PendingQueueLock
.acquire()
449 BuildObjectList
= BuildTask
._PendingQueue
.keys()
451 # check if their dependency is resolved, and if true, move them
454 for BuildObject
in BuildObjectList
:
455 Bt
= BuildTask
._PendingQueue
[BuildObject
]
457 BuildTask
._ReadyQueue
[BuildObject
] = BuildTask
._PendingQueue
.pop(BuildObject
)
458 BuildTask
._PendingQueueLock
.release()
460 # launch build thread until the maximum number of threads is reached
461 while not BuildTask
._ErrorFlag
.isSet():
462 # empty ready queue, do nothing further
463 if len(BuildTask
._ReadyQueue
) == 0:
466 # wait for active thread(s) exit
467 BuildTask
._Thread
.acquire(True)
469 # start a new build thread
470 Bo
= BuildTask
._ReadyQueue
.keys()[0]
471 Bt
= BuildTask
._ReadyQueue
.pop(Bo
)
473 # move into running queue
474 BuildTask
._RunningQueueLock
.acquire()
475 BuildTask
._RunningQueue
[Bo
] = Bt
476 BuildTask
._RunningQueueLock
.release()
485 # wait for all running threads exit
486 if BuildTask
._ErrorFlag
.isSet():
487 EdkLogger
.quiet("\nWaiting for all build threads exit...")
488 # while not BuildTask._ErrorFlag.isSet() and \
489 while len(BuildTask
._RunningQueue
) > 0:
490 EdkLogger
.verbose("Waiting for thread ending...(%d)" % len(BuildTask
._RunningQueue
))
491 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "Threads [%s]" % ", ".join([Th
.getName() for Th
in threading
.enumerate()]))
494 except BaseException
, X
:
496 # TRICK: hide the output of threads left runing, so that the user can
497 # catch the error message easily
499 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
500 BuildTask
._ErrorFlag
.set()
501 BuildTask
._ErrorMessage
= "build thread scheduler error\n\t%s" % str(X
)
503 BuildTask
._PendingQueue
.clear()
504 BuildTask
._ReadyQueue
.clear()
505 BuildTask
._RunningQueue
.clear()
506 BuildTask
._TaskQueue
.clear()
507 BuildTask
._SchedulerStopped
.set()
509 ## Wait for all running method exit
512 def WaitForComplete():
513 BuildTask
._SchedulerStopped
.wait()
515 ## Check if the scheduler is running or not
519 return not BuildTask
._SchedulerStopped
.isSet()
524 if BuildTask
.IsOnGoing():
525 BuildTask
._ErrorFlag
.set()
526 BuildTask
.WaitForComplete()
528 ## Check if there's error in running thread
530 # Since the main thread cannot catch exceptions in other thread, we have to
531 # use threading.Event to communicate this formation to main thread.
535 return BuildTask
._ErrorFlag
.isSet()
537 ## Get error message in running thread
539 # Since the main thread cannot catch exceptions in other thread, we have to
540 # use a static variable to communicate this message to main thread.
543 def GetErrorMessage():
544 return BuildTask
._ErrorMessage
546 ## Factory method to create a BuildTask object
548 # This method will check if a module is building or has been built. And if
549 # true, just return the associated BuildTask object in the _TaskQueue. If
550 # not, create and return a new BuildTask object. The new BuildTask object
551 # will be appended to the _PendingQueue for scheduling later.
553 # @param BuildItem A BuildUnit object representing a build object
554 # @param Dependency The dependent build object of BuildItem
557 def New(BuildItem
, Dependency
=None):
558 if BuildItem
in BuildTask
._TaskQueue
:
559 Bt
= BuildTask
._TaskQueue
[BuildItem
]
563 Bt
._Init
(BuildItem
, Dependency
)
564 BuildTask
._TaskQueue
[BuildItem
] = Bt
566 BuildTask
._PendingQueueLock
.acquire()
567 BuildTask
._PendingQueue
[BuildItem
] = Bt
568 BuildTask
._PendingQueueLock
.release()
572 ## The real constructor of BuildTask
574 # @param BuildItem A BuildUnit object representing a build object
575 # @param Dependency The dependent build object of BuildItem
577 def _Init(self
, BuildItem
, Dependency
=None):
578 self
.BuildItem
= BuildItem
580 self
.DependencyList
= []
581 if Dependency
== None:
582 Dependency
= BuildItem
.Dependency
584 Dependency
.extend(BuildItem
.Dependency
)
585 self
.AddDependency(Dependency
)
586 # flag indicating build completes, used to avoid unnecessary re-build
587 self
.CompleteFlag
= False
589 ## Check if all dependent build tasks are completed or not
593 for Dep
in self
.DependencyList
:
594 if Dep
.CompleteFlag
== True:
601 ## Add dependent build task
603 # @param Dependency The list of dependent build objects
605 def AddDependency(self
, Dependency
):
606 for Dep
in Dependency
:
607 self
.DependencyList
.append(BuildTask
.New(Dep
)) # BuildTask list
609 ## The thread wrapper of LaunchCommand function
611 # @param Command A list or string contains the call of the command
612 # @param WorkingDir The directory in which the program will be running
614 def _CommandThread(self
, Command
, WorkingDir
):
616 LaunchCommand(Command
, WorkingDir
)
617 self
.CompleteFlag
= True
620 # TRICK: hide the output of threads left runing, so that the user can
621 # catch the error message easily
623 if not BuildTask
._ErrorFlag
.isSet():
624 GlobalData
.gBuildingModule
= "%s [%s, %s, %s]" % (str(self
.BuildItem
.BuildObject
),
625 self
.BuildItem
.BuildObject
.Arch
,
626 self
.BuildItem
.BuildObject
.ToolChain
,
627 self
.BuildItem
.BuildObject
.BuildTarget
629 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
630 BuildTask
._ErrorFlag
.set()
631 BuildTask
._ErrorMessage
= "%s broken\n %s [%s]" % \
632 (threading
.currentThread().getName(), Command
, WorkingDir
)
633 # indicate there's a thread is available for another build task
634 BuildTask
._RunningQueueLock
.acquire()
635 BuildTask
._RunningQueue
.pop(self
.BuildItem
)
636 BuildTask
._RunningQueueLock
.release()
637 BuildTask
._Thread
.release()
639 ## Start build task thread
642 EdkLogger
.quiet("Building ... %s" % repr(self
.BuildItem
))
643 Command
= self
.BuildItem
.BuildCommand
+ [self
.BuildItem
.Target
]
644 self
.BuildTread
= Thread(target
=self
._CommandThread
, args
=(Command
, self
.BuildItem
.WorkingDir
))
645 self
.BuildTread
.setName("build thread")
646 self
.BuildTread
.setDaemon(False)
647 self
.BuildTread
.start()
649 ## The class contains the information related to EFI image
654 # Constructor will load all required image information.
656 # @param BaseName The full file path of image.
657 # @param Guid The GUID for image.
658 # @param Arch Arch of this image.
659 # @param OutpuDir The output directory for image.
660 # @param ImageClass PeImage Information
662 def __init__(self
, BaseName
, Guid
, Arch
, OutpuDir
, ImageClass
):
663 self
.BaseName
= BaseName
666 self
.OutpuDir
= OutpuDir
667 self
.Image
= ImageClass
668 self
.Image
.Size
= (self
.Image
.Size
/ 0x1000 + 1) * 0x1000
670 ## The class implementing the EDK2 build process
672 # The build process includes:
673 # 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf
674 # 2. Parse DSC file of active platform
675 # 3. Parse FDF file if any
676 # 4. Establish build database, including parse all other files (module, package)
677 # 5. Create AutoGen files (C code file, depex file, makefile) if necessary
678 # 6. Call build command
683 # Constructor will load all necessary configurations, parse platform, modules
684 # and packages and the establish a database for AutoGen.
686 # @param Target The build command target, one of gSupportedTarget
687 # @param WorkspaceDir The directory of workspace
688 # @param Platform The DSC file of active platform
689 # @param Module The INF file of active module, if any
690 # @param Arch The Arch list of platform or module
691 # @param ToolChain The name list of toolchain
692 # @param BuildTarget The "DEBUG" or "RELEASE" build
693 # @param FlashDefinition The FDF file of active platform
694 # @param FdList=[] The FD names to be individually built
695 # @param FvList=[] The FV names to be individually built
696 # @param MakefileType The type of makefile (for MSFT make or GNU make)
697 # @param SilentMode Indicate multi-thread build mode
698 # @param ThreadNumber The maximum number of thread if in multi-thread build mode
699 # @param SkipAutoGen Skip AutoGen step
700 # @param Reparse Re-parse all meta files
701 # @param SkuId SKU id from command line
703 def __init__(self
, Target
, WorkspaceDir
, Platform
, Module
, Arch
, ToolChain
,
704 BuildTarget
, FlashDefinition
, FdList
=[], FvList
=[],
705 MakefileType
="nmake", SilentMode
=False, ThreadNumber
=2,
706 SkipAutoGen
=False, Reparse
=False, SkuId
=None,
707 ReportFile
=None, ReportType
=None):
709 self
.WorkspaceDir
= WorkspaceDir
711 self
.PlatformFile
= Platform
712 self
.ModuleFile
= Module
714 self
.ToolChainList
= ToolChain
715 self
.BuildTargetList
= BuildTarget
716 self
.Fdf
= FlashDefinition
719 self
.MakefileType
= MakefileType
720 self
.SilentMode
= SilentMode
721 self
.ThreadNumber
= ThreadNumber
722 self
.SkipAutoGen
= SkipAutoGen
723 self
.Reparse
= Reparse
725 self
.SpawnMode
= True
726 self
.BuildReport
= BuildReport(ReportFile
, ReportType
)
727 self
.TargetTxt
= TargetTxtClassObject()
728 self
.ToolDef
= ToolDefClassObject()
729 self
.Db
= WorkspaceDatabase(None, GlobalData
.gGlobalDefines
, self
.Reparse
)
730 #self.Db = WorkspaceDatabase(None, {}, self.Reparse)
731 self
.BuildDatabase
= self
.Db
.BuildObject
733 self
.LoadFixAddress
= 0
735 # print dot charater during doing some time-consuming work
736 self
.Progress
= Utils
.Progressor()
738 # parse target.txt, tools_def.txt, and platform file
739 #self.RestoreBuildData()
740 self
.LoadConfiguration()
743 # @attention Treat $(TARGET) in meta data files as special macro when it has only one build target.
744 # This is not a complete support for $(TARGET) macro as it can only support one build target in ONE
745 # invocation of build command. However, it should cover the frequent usage model that $(TARGET) macro
746 # is used in DSC files to specify different libraries & PCD setting for debug/release build.
748 if len(self
.BuildTargetList
) == 1:
749 self
.Db
._GlobalMacros
.setdefault("TARGET", self
.BuildTargetList
[0])
753 # print current build environment and configuration
754 EdkLogger
.quiet("%-24s = %s" % ("WORKSPACE", os
.environ
["WORKSPACE"]))
755 EdkLogger
.quiet("%-24s = %s" % ("ECP_SOURCE", os
.environ
["ECP_SOURCE"]))
756 EdkLogger
.quiet("%-24s = %s" % ("EDK_SOURCE", os
.environ
["EDK_SOURCE"]))
757 EdkLogger
.quiet("%-24s = %s" % ("EFI_SOURCE", os
.environ
["EFI_SOURCE"]))
758 EdkLogger
.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os
.environ
["EDK_TOOLS_PATH"]))
760 EdkLogger
.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self
.ArchList
)))
761 EdkLogger
.info('%-24s = %s' % ("TARGET", ' '.join(self
.BuildTargetList
)))
762 EdkLogger
.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self
.ToolChainList
)))
764 EdkLogger
.info('\n%-24s = %s' % ("Active Platform", self
.PlatformFile
))
766 if self
.Fdf
!= None and self
.Fdf
!= "":
767 EdkLogger
.info('%-24s = %s' % ("Flash Image Definition", self
.Fdf
))
769 if self
.ModuleFile
!= None and self
.ModuleFile
!= "":
770 EdkLogger
.info('%-24s = %s' % ("Active Module", self
.ModuleFile
))
772 os
.chdir(self
.WorkspaceDir
)
773 self
.Progress
.Start("\nProcessing meta-data")
775 ## Load configuration
777 # This method will parse target.txt and get the build configurations.
779 def LoadConfiguration(self
):
781 # Check target.txt and tools_def.txt and Init them
783 BuildConfigurationFile
= os
.path
.normpath(os
.path
.join(self
.WorkspaceDir
, gBuildConfiguration
))
784 if os
.path
.isfile(BuildConfigurationFile
) == True:
785 StatusCode
= self
.TargetTxt
.LoadTargetTxtFile(BuildConfigurationFile
)
787 ToolDefinitionFile
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_TOOL_CHAIN_CONF
]
788 if ToolDefinitionFile
== '':
789 ToolDefinitionFile
= gToolsDefinition
790 ToolDefinitionFile
= os
.path
.normpath(os
.path
.join(self
.WorkspaceDir
, ToolDefinitionFile
))
791 if os
.path
.isfile(ToolDefinitionFile
) == True:
792 StatusCode
= self
.ToolDef
.LoadToolDefFile(ToolDefinitionFile
)
794 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=ToolDefinitionFile
)
796 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=BuildConfigurationFile
)
798 # if no ARCH given in command line, get it from target.txt
799 if self
.ArchList
== None or len(self
.ArchList
) == 0:
800 self
.ArchList
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_TARGET_ARCH
]
802 # if no build target given in command line, get it from target.txt
803 if self
.BuildTargetList
== None or len(self
.BuildTargetList
) == 0:
804 self
.BuildTargetList
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_TARGET
]
806 # if no tool chain given in command line, get it from target.txt
807 if self
.ToolChainList
== None or len(self
.ToolChainList
) == 0:
808 self
.ToolChainList
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_TOOL_CHAIN_TAG
]
809 if self
.ToolChainList
== None or len(self
.ToolChainList
) == 0:
810 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
, ExtraData
="No toolchain given. Don't know how to build.\n")
812 # check if the tool chains are defined or not
813 NewToolChainList
= []
814 for ToolChain
in self
.ToolChainList
:
815 if ToolChain
not in self
.ToolDef
.ToolsDefTxtDatabase
[TAB_TOD_DEFINES_TOOL_CHAIN_TAG
]:
816 EdkLogger
.warn("build", "Tool chain [%s] is not defined" % ToolChain
)
818 NewToolChainList
.append(ToolChain
)
819 # if no tool chain available, break the build
820 if len(NewToolChainList
) == 0:
821 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
,
822 ExtraData
="[%s] not defined. No toolchain available for build!\n" % ", ".join(self
.ToolChainList
))
824 self
.ToolChainList
= NewToolChainList
826 if self
.ThreadNumber
== None:
827 self
.ThreadNumber
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER
]
828 if self
.ThreadNumber
== '':
829 self
.ThreadNumber
= 0
831 self
.ThreadNumber
= int(self
.ThreadNumber
, 0)
833 if self
.ThreadNumber
== 0:
834 self
.ThreadNumber
= 1
836 if not self
.PlatformFile
:
837 PlatformFile
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_ACTIVE_PLATFORM
]
839 # Try to find one in current directory
840 WorkingDirectory
= os
.getcwd()
841 FileList
= glob
.glob(os
.path
.normpath(os
.path
.join(WorkingDirectory
, '*.dsc')))
842 FileNum
= len(FileList
)
844 EdkLogger
.error("build", OPTION_MISSING
,
845 ExtraData
="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum
, WorkingDirectory
))
847 PlatformFile
= FileList
[0]
849 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
,
850 ExtraData
="No active platform specified in target.txt or command line! Nothing can be built.\n")
852 self
.PlatformFile
= PathClass(NormFile(PlatformFile
, self
.WorkspaceDir
), self
.WorkspaceDir
)
853 ErrorCode
, ErrorInfo
= self
.PlatformFile
.Validate(".dsc", False)
855 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
857 ## Initialize build configuration
859 # This method will parse DSC file and merge the configurations from
860 # command line and target.txt, then get the final build configurations.
863 ErrorCode
, ErrorInfo
= self
.PlatformFile
.Validate(".dsc")
865 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
867 # create metafile database
868 self
.Db
.InitDatabase()
870 # we need information in platform description file to determine how to build
871 self
.Platform
= self
.BuildDatabase
[self
.PlatformFile
, 'COMMON']
873 self
.Fdf
= self
.Platform
.FlashDefinition
875 LoadFixAddressString
= None
876 if TAB_FIX_LOAD_TOP_MEMORY_ADDRESS
in GlobalData
.gGlobalDefines
:
877 LoadFixAddressString
= GlobalData
.gGlobalDefines
[TAB_FIX_LOAD_TOP_MEMORY_ADDRESS
]
879 LoadFixAddressString
= self
.Platform
.LoadFixAddress
881 if LoadFixAddressString
!= None and LoadFixAddressString
!= '':
883 if LoadFixAddressString
.upper().startswith('0X'):
884 self
.LoadFixAddress
= int (LoadFixAddressString
, 16)
886 self
.LoadFixAddress
= int (LoadFixAddressString
)
888 EdkLogger
.error("build", PARAMETER_INVALID
, "FIX_LOAD_TOP_MEMORY_ADDRESS %s is not valid dec or hex string" % (LoadFixAddressString
))
889 if self
.LoadFixAddress
< 0:
890 EdkLogger
.error("build", PARAMETER_INVALID
, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid negative value %s" % (LoadFixAddressString
))
891 if self
.LoadFixAddress
!= 0xFFFFFFFFFFFFFFFF and self
.LoadFixAddress
% 0x1000 != 0:
892 EdkLogger
.error("build", PARAMETER_INVALID
, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid unaligned 4K value %s" % (LoadFixAddressString
))
894 if self
.SkuId
== None or self
.SkuId
== '':
895 self
.SkuId
= self
.Platform
.SkuName
897 # check FD/FV build target
898 if self
.Fdf
== None or self
.Fdf
== "":
899 if self
.FdList
!= []:
900 EdkLogger
.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self
.FdList
))
902 if self
.FvList
!= []:
903 EdkLogger
.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self
.FvList
))
906 FdfParserObj
= FdfParser(str(self
.Fdf
))
907 FdfParserObj
.ParseFile()
908 for fvname
in self
.FvList
:
909 if fvname
.upper() not in FdfParserObj
.Profile
.FvDict
.keys():
910 EdkLogger
.error("build", OPTION_VALUE_INVALID
,
911 "No such an FV in FDF file: %s" % fvname
)
916 if self
.ArchList
== None or len(self
.ArchList
) == 0:
917 ArchList
= set(self
.Platform
.SupArchList
)
919 ArchList
= set(self
.ArchList
) & set(self
.Platform
.SupArchList
)
920 if len(ArchList
) == 0:
921 EdkLogger
.error("build", PARAMETER_INVALID
,
922 ExtraData
= "Active platform supports [%s] only, but [%s] is given."
923 % (" ".join(self
.Platform
.SupArchList
), " ".join(self
.ArchList
)))
924 elif len(ArchList
) != len(self
.ArchList
):
925 SkippedArchList
= set(self
.ArchList
).symmetric_difference(set(self
.Platform
.SupArchList
))
926 EdkLogger
.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"
927 % (" ".join(SkippedArchList
), " ".join(self
.Platform
.SupArchList
), " ".join(self
.ArchList
)))
928 self
.ArchList
= tuple(ArchList
)
931 if self
.BuildTargetList
== None or len(self
.BuildTargetList
) == 0:
932 BuildTargetList
= self
.Platform
.BuildTargets
934 BuildTargetList
= list(set(self
.BuildTargetList
) & set(self
.Platform
.BuildTargets
))
935 if BuildTargetList
== []:
936 EdkLogger
.error("build", PARAMETER_INVALID
, "Active platform only supports [%s], but [%s] is given"
937 % (" ".join(self
.Platform
.BuildTargets
), " ".join(self
.BuildTargetList
)))
938 self
.BuildTargetList
= BuildTargetList
940 ## Build a module or platform
942 # Create autogen code and makfile for a module or platform, and the launch
943 # "make" command to build it
945 # @param Target The target of build command
946 # @param Platform The platform file
947 # @param Module The module file
948 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
949 # @param ToolChain The name of toolchain to build
950 # @param Arch The arch of the module/platform
951 # @param CreateDepModuleCodeFile Flag used to indicate creating code
952 # for dependent modules/Libraries
953 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
954 # for dependent modules/Libraries
956 def _Build(self
, Target
, AutoGenObject
, CreateDepsCodeFile
=True, CreateDepsMakeFile
=True):
957 if AutoGenObject
== None:
960 # skip file generation for cleanxxx targets, run and fds target
961 if Target
not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
962 # for target which must generate AutoGen code and makefile
963 if not self
.SkipAutoGen
or Target
== 'genc':
964 self
.Progress
.Start("Generating code")
965 AutoGenObject
.CreateCodeFile(CreateDepsCodeFile
)
966 self
.Progress
.Stop("done!")
970 if not self
.SkipAutoGen
or Target
== 'genmake':
971 self
.Progress
.Start("Generating makefile")
972 AutoGenObject
.CreateMakeFile(CreateDepsMakeFile
)
973 self
.Progress
.Stop("done!")
974 if Target
== "genmake":
977 # always recreate top/platform makefile when clean, just in case of inconsistency
978 AutoGenObject
.CreateCodeFile(False)
979 AutoGenObject
.CreateMakeFile(False)
981 if EdkLogger
.GetLevel() == EdkLogger
.QUIET
:
982 EdkLogger
.quiet("Building ... %s" % repr(AutoGenObject
))
984 BuildCommand
= AutoGenObject
.BuildCommand
985 if BuildCommand
== None or len(BuildCommand
) == 0:
986 EdkLogger
.error("build", OPTION_MISSING
, ExtraData
="No MAKE command found for [%s, %s, %s]" % Key
)
988 BuildCommand
= BuildCommand
+ [Target
]
989 LaunchCommand(BuildCommand
, AutoGenObject
.MakeFileDir
)
990 if Target
== 'cleanall':
992 #os.rmdir(AutoGenObject.BuildDir)
993 RemoveDirectory(AutoGenObject
.BuildDir
, True)
994 except WindowsError, X
:
995 EdkLogger
.error("build", FILE_DELETE_FAILURE
, ExtraData
=str(X
))
998 ## Rebase module image and Get function address for the inpug module list.
1000 def _RebaseModule (self
, MapBuffer
, BaseAddress
, ModuleList
, AddrIsOffset
= True, ModeIsSmm
= False):
1002 AddrIsOffset
= False
1003 InfFileNameList
= ModuleList
.keys()
1004 #InfFileNameList.sort()
1005 for InfFile
in InfFileNameList
:
1006 sys
.stdout
.write (".")
1008 ModuleInfo
= ModuleList
[InfFile
]
1009 ModuleName
= ModuleInfo
.BaseName
1010 ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1012 BaseAddress
= BaseAddress
- ModuleInfo
.Image
.Size
1014 # Update Image to new BaseAddress by GenFw tool
1016 LaunchCommand(["GenFw", "--rebase", str(BaseAddress
), "-r", ModuleInfo
.Image
.FileName
], ModuleInfo
.OutpuDir
)
1019 # Set new address to the section header only for SMM driver.
1021 LaunchCommand(["GenFw", "--address", str(BaseAddress
), "-r", ModuleInfo
.Image
.FileName
], ModuleInfo
.OutpuDir
)
1023 # Collect funtion address from Map file
1025 ImageMapTable
= ModuleInfo
.Image
.FileName
.replace('.efi', '.map')
1027 if os
.path
.exists(ImageMapTable
):
1028 OrigImageBaseAddress
= 0
1029 ImageMap
= open (ImageMapTable
, 'r')
1030 for LinStr
in ImageMap
:
1031 if len (LinStr
.strip()) == 0:
1034 # Get the preferred address set on link time.
1036 if LinStr
.find ('Preferred load address is') != -1:
1037 StrList
= LinStr
.split()
1038 OrigImageBaseAddress
= int (StrList
[len(StrList
) - 1], 16)
1040 StrList
= LinStr
.split()
1041 if len (StrList
) > 4:
1042 if StrList
[3] == 'f' or StrList
[3] =='F':
1044 RelativeAddress
= int (StrList
[2], 16) - OrigImageBaseAddress
1045 FunctionList
.append ((Name
, RelativeAddress
))
1046 if ModuleInfo
.Arch
== 'IPF' and Name
.endswith('_ModuleEntryPoint'):
1048 # Get the real entry point address for IPF image.
1050 ModuleInfo
.Image
.EntryPoint
= RelativeAddress
1053 # Add general information.
1056 MapBuffer
.write('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName
, BaseAddress
, BaseAddress
+ ModuleInfo
.Image
.EntryPoint
))
1058 MapBuffer
.write('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName
, 0 - BaseAddress
, 0 - (BaseAddress
+ ModuleInfo
.Image
.EntryPoint
)))
1060 MapBuffer
.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName
, BaseAddress
, BaseAddress
+ ModuleInfo
.Image
.EntryPoint
))
1062 # Add guid and general seciton section.
1064 TextSectionAddress
= 0
1065 DataSectionAddress
= 0
1066 for SectionHeader
in ModuleInfo
.Image
.SectionHeaderList
:
1067 if SectionHeader
[0] == '.text':
1068 TextSectionAddress
= SectionHeader
[1]
1069 elif SectionHeader
[0] in ['.data', '.sdata']:
1070 DataSectionAddress
= SectionHeader
[1]
1072 MapBuffer
.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n\n' % (ModuleInfo
.Guid
, 0 - (BaseAddress
+ TextSectionAddress
), 0 - (BaseAddress
+ DataSectionAddress
)))
1074 MapBuffer
.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n\n' % (ModuleInfo
.Guid
, BaseAddress
+ TextSectionAddress
, BaseAddress
+ DataSectionAddress
))
1076 # Add funtion address
1078 for Function
in FunctionList
:
1080 MapBuffer
.write(' -0x%010X %s\n' % (0 - (BaseAddress
+ Function
[1]), Function
[0]))
1082 MapBuffer
.write(' 0x%010X %s\n' % (BaseAddress
+ Function
[1], Function
[0]))
1086 # for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1089 BaseAddress
= BaseAddress
+ ModuleInfo
.Image
.Size
1091 ## Collect MAP information of all FVs
1093 def _CollectFvMapBuffer (self
, MapBuffer
, Wa
, ModuleList
):
1095 # First get the XIP base address for FV map file.
1096 GuidPattern
= re
.compile("[-a-fA-F0-9]+")
1097 for FvName
in Wa
.FdfProfile
.FvDict
.keys():
1098 FvMapBuffer
= os
.path
.join(Wa
.FvDir
, FvName
+ '.Fv.map')
1099 if not os
.path
.exists(FvMapBuffer
):
1101 FvMap
= open (FvMapBuffer
, 'r')
1102 #skip FV size information
1108 MatchGuid
= GuidPattern
.match(Line
)
1109 if MatchGuid
!= None:
1111 # Replace GUID with module name
1113 GuidString
= MatchGuid
.group()
1114 if GuidString
.upper() in ModuleList
:
1115 Line
= Line
.replace(GuidString
, ModuleList
[GuidString
.upper()].Name
)
1116 MapBuffer
.write('%s' % (Line
))
1119 ## Collect MAP information of all modules
1121 def _CollectModuleMapBuffer (self
, MapBuffer
, ModuleList
):
1122 sys
.stdout
.write ("Generate Load Module At Fix Address Map")
1124 PatchEfiImageList
= []
1132 # reserve 4K size in SMRAM to make SMM module address not from 0.
1134 IsIpfPlatform
= False
1135 if 'IPF' in self
.ArchList
:
1136 IsIpfPlatform
= True
1137 for ModuleGuid
in ModuleList
:
1138 Module
= ModuleList
[ModuleGuid
]
1139 GlobalData
.gProcessingFile
= "%s [%s, %s, %s]" % (Module
.MetaFile
, Module
.Arch
, Module
.ToolChain
, Module
.BuildTarget
)
1141 OutputImageFile
= ''
1142 for ResultFile
in Module
.CodaTargetList
:
1143 if str(ResultFile
.Target
).endswith('.efi'):
1145 # module list for PEI, DXE, RUNTIME and SMM
1147 OutputImageFile
= os
.path
.join(Module
.OutputDir
, Module
.Name
+ '.efi')
1148 ImageClass
= PeImageClass (OutputImageFile
)
1149 if not ImageClass
.IsValid
:
1150 EdkLogger
.error("build", FILE_PARSE_FAILURE
, ExtraData
=ImageClass
.ErrorInfo
)
1151 ImageInfo
= PeImageInfo(Module
.Name
, Module
.Guid
, Module
.Arch
, Module
.OutputDir
, ImageClass
)
1152 if Module
.ModuleType
in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']:
1153 PeiModuleList
[Module
.MetaFile
] = ImageInfo
1154 PeiSize
+= ImageInfo
.Image
.Size
1155 elif Module
.ModuleType
in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']:
1156 BtModuleList
[Module
.MetaFile
] = ImageInfo
1157 BtSize
+= ImageInfo
.Image
.Size
1158 elif Module
.ModuleType
in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']:
1159 RtModuleList
[Module
.MetaFile
] = ImageInfo
1160 #IPF runtime driver needs to be at 2 page alignment.
1161 if IsIpfPlatform
and ImageInfo
.Image
.Size
% 0x2000 != 0:
1162 ImageInfo
.Image
.Size
= (ImageInfo
.Image
.Size
/ 0x2000 + 1) * 0x2000
1163 RtSize
+= ImageInfo
.Image
.Size
1164 elif Module
.ModuleType
in ['SMM_CORE', 'DXE_SMM_DRIVER']:
1165 SmmModuleList
[Module
.MetaFile
] = ImageInfo
1166 SmmSize
+= ImageInfo
.Image
.Size
1167 if Module
.ModuleType
== 'DXE_SMM_DRIVER':
1169 if 'PI_SPECIFICATION_VERSION' in Module
.Module
.Specification
:
1170 PiSpecVersion
= Module
.Module
.Specification
['PI_SPECIFICATION_VERSION']
1171 # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.
1172 if PiSpecVersion
< 0x0001000A:
1173 BtModuleList
[Module
.MetaFile
] = ImageInfo
1174 BtSize
+= ImageInfo
.Image
.Size
1177 # EFI image is final target.
1178 # Check EFI image contains patchable FixAddress related PCDs.
1180 if OutputImageFile
!= '':
1181 ModuleIsPatch
= False
1182 for Pcd
in Module
.ModulePcdList
:
1183 if Pcd
.Type
== TAB_PCDS_PATCHABLE_IN_MODULE
and Pcd
.TokenCName
in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST
:
1184 ModuleIsPatch
= True
1186 if not ModuleIsPatch
:
1187 for Pcd
in Module
.LibraryPcdList
:
1188 if Pcd
.Type
== TAB_PCDS_PATCHABLE_IN_MODULE
and Pcd
.TokenCName
in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST
:
1189 ModuleIsPatch
= True
1192 if not ModuleIsPatch
:
1195 # Module includes the patchable load fix address PCDs.
1196 # It will be fixed up later.
1198 PatchEfiImageList
.append (OutputImageFile
)
1201 # Get Top Memory address
1203 ReservedRuntimeMemorySize
= 0
1204 TopMemoryAddress
= 0
1205 if self
.LoadFixAddress
== 0xFFFFFFFFFFFFFFFF:
1206 TopMemoryAddress
= 0
1208 TopMemoryAddress
= self
.LoadFixAddress
1209 if TopMemoryAddress
< RtSize
+ BtSize
+ PeiSize
:
1210 EdkLogger
.error("build", PARAMETER_INVALID
, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")
1211 # Make IPF runtime driver at 2 page alignment.
1213 ReservedRuntimeMemorySize
= TopMemoryAddress
% 0x2000
1214 RtSize
= RtSize
+ ReservedRuntimeMemorySize
1217 # Patch FixAddress related PCDs into EFI image
1219 for EfiImage
in PatchEfiImageList
:
1220 EfiImageMap
= EfiImage
.replace('.efi', '.map')
1221 if not os
.path
.exists(EfiImageMap
):
1224 # Get PCD offset in EFI image by GenPatchPcdTable function
1226 PcdTable
= parsePcdInfoFromMapFile(EfiImageMap
, EfiImage
)
1228 # Patch real PCD value by PatchPcdValue tool
1230 for PcdInfo
in PcdTable
:
1232 if PcdInfo
[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE
:
1233 ReturnValue
, ErrorInfo
= PatchBinaryFile (EfiImage
, PcdInfo
[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE
, str (PeiSize
/0x1000))
1234 elif PcdInfo
[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE
:
1235 ReturnValue
, ErrorInfo
= PatchBinaryFile (EfiImage
, PcdInfo
[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE
, str (BtSize
/0x1000))
1236 elif PcdInfo
[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE
:
1237 ReturnValue
, ErrorInfo
= PatchBinaryFile (EfiImage
, PcdInfo
[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE
, str (RtSize
/0x1000))
1238 elif PcdInfo
[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE
and len (SmmModuleList
) > 0:
1239 ReturnValue
, ErrorInfo
= PatchBinaryFile (EfiImage
, PcdInfo
[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE
, str (SmmSize
/0x1000))
1240 if ReturnValue
!= 0:
1241 EdkLogger
.error("build", PARAMETER_INVALID
, "Patch PCD value failed", ExtraData
=ErrorInfo
)
1243 MapBuffer
.write('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize
/0x1000))
1244 MapBuffer
.write('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize
/0x1000))
1245 MapBuffer
.write('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize
/0x1000))
1246 if len (SmmModuleList
) > 0:
1247 MapBuffer
.write('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize
/0x1000))
1249 PeiBaseAddr
= TopMemoryAddress
- RtSize
- BtSize
1250 BtBaseAddr
= TopMemoryAddress
- RtSize
1251 RtBaseAddr
= TopMemoryAddress
- ReservedRuntimeMemorySize
1253 self
._RebaseModule
(MapBuffer
, PeiBaseAddr
, PeiModuleList
, TopMemoryAddress
== 0)
1254 self
._RebaseModule
(MapBuffer
, BtBaseAddr
, BtModuleList
, TopMemoryAddress
== 0)
1255 self
._RebaseModule
(MapBuffer
, RtBaseAddr
, RtModuleList
, TopMemoryAddress
== 0)
1256 self
._RebaseModule
(MapBuffer
, 0x1000, SmmModuleList
, AddrIsOffset
= False, ModeIsSmm
= True)
1257 MapBuffer
.write('\n\n')
1258 sys
.stdout
.write ("\n")
1261 ## Save platform Map file
1263 def _SaveMapFile (self
, MapBuffer
, Wa
):
1265 # Map file path is got.
1267 MapFilePath
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ '.map')
1269 # Save address map into MAP file.
1271 SaveFileOnChange(MapFilePath
, MapBuffer
.getvalue(), False)
1273 if self
.LoadFixAddress
!= 0:
1274 sys
.stdout
.write ("\nLoad Module At Fix Address Map file saved to %s\n" %(MapFilePath))
1277 ## Build active platform for different build targets and different tool chains
1279 def _BuildPlatform(self
):
1280 for BuildTarget
in self
.BuildTargetList
:
1281 for ToolChain
in self
.ToolChainList
:
1282 Wa
= WorkspaceAutoGen(
1296 self
.BuildReport
.AddPlatformReport(Wa
)
1297 self
.Progress
.Stop("done!")
1298 self
._Build
(self
.Target
, Wa
)
1300 # Create MAP file when Load Fix Address is enabled.
1301 if self
.Target
in ["", "all", "fds"]:
1302 for Arch
in self
.ArchList
:
1304 # Check whether the set fix address is above 4G for 32bit image.
1306 if (Arch
== 'IA32' or Arch
== 'ARM') and self
.LoadFixAddress
!= 0xFFFFFFFFFFFFFFFF and self
.LoadFixAddress
>= 0x100000000:
1307 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")
1312 for Pa
in Wa
.AutoGenObjectList
:
1313 for Ma
in Pa
.ModuleAutoGenList
:
1316 if not Ma
.IsLibrary
:
1317 ModuleList
[Ma
.Guid
.upper()] = Ma
1319 MapBuffer
= StringIO('')
1320 if self
.LoadFixAddress
!= 0:
1322 # Rebase module to the preferred memory address before GenFds
1324 self
._CollectModuleMapBuffer
(MapBuffer
, ModuleList
)
1327 # create FDS again for the updated EFI image
1329 self
._Build
("fds", Wa
)
1331 # Create MAP file for all platform FVs after GenFds.
1333 self
._CollectFvMapBuffer
(MapBuffer
, Wa
, ModuleList
)
1335 # Save MAP buffer into MAP file.
1337 self
._SaveMapFile
(MapBuffer
, Wa
)
1339 ## Build active module for different build targets, different tool chains and different archs
1341 def _BuildModule(self
):
1342 for BuildTarget
in self
.BuildTargetList
:
1343 for ToolChain
in self
.ToolChainList
:
1345 # module build needs platform build information, so get platform
1348 Wa
= WorkspaceAutoGen(
1362 Wa
.CreateMakeFile(False)
1363 self
.Progress
.Stop("done!")
1365 for Arch
in self
.ArchList
:
1366 Ma
= ModuleAutoGen(Wa
, self
.ModuleFile
, BuildTarget
, ToolChain
, Arch
, self
.PlatformFile
)
1367 if Ma
== None: continue
1369 self
._Build
(self
.Target
, Ma
)
1371 self
.BuildReport
.AddPlatformReport(Wa
, MaList
)
1376 "Module for [%s] is not a component of active platform."\
1377 " Please make sure that the ARCH and inf file path are"\
1378 " given in the same as in [%s]" %\
1379 (', '.join(self
.ArchList
), self
.Platform
),
1380 ExtraData
=self
.ModuleFile
1382 # Create MAP file when Load Fix Address is enabled.
1383 if self
.Target
== "fds" and self
.Fdf
!= '':
1384 for Arch
in self
.ArchList
:
1386 # Check whether the set fix address is above 4G for 32bit image.
1388 if (Arch
== 'IA32' or Arch
== 'ARM') and self
.LoadFixAddress
!= 0xFFFFFFFFFFFFFFFF and self
.LoadFixAddress
>= 0x100000000:
1389 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")
1394 for Pa
in Wa
.AutoGenObjectList
:
1395 for Ma
in Pa
.ModuleAutoGenList
:
1398 if not Ma
.IsLibrary
:
1399 ModuleList
[Ma
.Guid
.upper()] = Ma
1401 MapBuffer
= StringIO('')
1402 if self
.LoadFixAddress
!= 0:
1404 # Rebase module to the preferred memory address before GenFds
1406 self
._CollectModuleMapBuffer
(MapBuffer
, ModuleList
)
1408 # create FDS again for the updated EFI image
1410 self
._Build
("fds", Wa
)
1412 # Create MAP file for all platform FVs after GenFds.
1414 self
._CollectFvMapBuffer
(MapBuffer
, Wa
, ModuleList
)
1416 # Save MAP buffer into MAP file.
1418 self
._SaveMapFile
(MapBuffer
, Wa
)
1420 ## Build a platform in multi-thread mode
1422 def _MultiThreadBuildPlatform(self
):
1423 for BuildTarget
in self
.BuildTargetList
:
1424 for ToolChain
in self
.ToolChainList
:
1425 Wa
= WorkspaceAutoGen(
1439 self
.BuildReport
.AddPlatformReport(Wa
)
1440 Wa
.CreateMakeFile(False)
1442 # multi-thread exit flag
1443 ExitFlag
= threading
.Event()
1445 for Arch
in self
.ArchList
:
1446 Pa
= PlatformAutoGen(Wa
, self
.PlatformFile
, BuildTarget
, ToolChain
, Arch
)
1449 for Module
in Pa
.Platform
.Modules
:
1450 # Get ModuleAutoGen object to generate C code file and makefile
1451 Ma
= ModuleAutoGen(Wa
, Module
, BuildTarget
, ToolChain
, Arch
, self
.PlatformFile
)
1454 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
1455 if self
.Target
not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1456 # for target which must generate AutoGen code and makefile
1457 if not self
.SkipAutoGen
or self
.Target
== 'genc':
1458 Ma
.CreateCodeFile(True)
1459 if self
.Target
== "genc":
1462 if not self
.SkipAutoGen
or self
.Target
== 'genmake':
1463 Ma
.CreateMakeFile(True)
1464 if self
.Target
== "genmake":
1466 self
.Progress
.Stop("done!")
1467 # Generate build task for the module
1468 Bt
= BuildTask
.New(ModuleMakeUnit(Ma
, self
.Target
))
1469 # Break build if any build thread has error
1470 if BuildTask
.HasError():
1471 # we need a full version of makefile for platform
1473 BuildTask
.WaitForComplete()
1474 Pa
.CreateMakeFile(False)
1475 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1476 # Start task scheduler
1477 if not BuildTask
.IsOnGoing():
1478 BuildTask
.StartScheduler(self
.ThreadNumber
, ExitFlag
)
1480 # in case there's an interruption. we need a full version of makefile for platform
1481 Pa
.CreateMakeFile(False)
1482 if BuildTask
.HasError():
1483 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1486 # All modules have been put in build tasks queue. Tell task scheduler
1487 # to exit if all tasks are completed
1490 BuildTask
.WaitForComplete()
1493 # Check for build error, and raise exception if one
1494 # has been signaled.
1496 if BuildTask
.HasError():
1497 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1499 # Create MAP file when Load Fix Address is enabled.
1500 if self
.Target
in ["", "all", "fds"]:
1501 for Arch
in self
.ArchList
:
1503 # Check whether the set fix address is above 4G for 32bit image.
1505 if (Arch
== 'IA32' or Arch
== 'ARM') and self
.LoadFixAddress
!= 0xFFFFFFFFFFFFFFFF and self
.LoadFixAddress
>= 0x100000000:
1506 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")
1511 for Pa
in Wa
.AutoGenObjectList
:
1512 for Ma
in Pa
.ModuleAutoGenList
:
1515 if not Ma
.IsLibrary
:
1516 ModuleList
[Ma
.Guid
.upper()] = Ma
1518 # Rebase module to the preferred memory address before GenFds
1520 MapBuffer
= StringIO('')
1521 if self
.LoadFixAddress
!= 0:
1522 self
._CollectModuleMapBuffer
(MapBuffer
, ModuleList
)
1524 # Generate FD image if there's a FDF file found
1525 if self
.Fdf
!= '' and self
.Target
in ["", "all", "fds"]:
1526 LaunchCommand(Wa
.BuildCommand
+ ["fds"], Wa
.MakeFileDir
)
1528 # Create MAP file for all platform FV after GenFds
1529 if self
.Target
in ["", "all", "fds"]:
1532 # Create MAP file for all platform FVs after GenFds.
1534 self
._CollectFvMapBuffer
(MapBuffer
, Wa
, ModuleList
)
1536 # Save MAP buffer into MAP file.
1538 self
._SaveMapFile
(MapBuffer
, Wa
)
1540 ## Generate GuidedSectionTools.txt in the FV directories.
1542 def CreateGuidedSectionToolsFile(self
):
1543 for Arch
in self
.ArchList
:
1544 for BuildTarget
in self
.BuildTargetList
:
1545 for ToolChain
in self
.ToolChainList
:
1546 FvDir
= os
.path
.join(
1548 self
.Platform
.OutputDirectory
,
1549 '_'.join((BuildTarget
, ToolChain
)),
1552 if not os
.path
.exists(FvDir
):
1554 # Build up the list of supported architectures for this build
1555 prefix
= '%s_%s_%s_' % (BuildTarget
, ToolChain
, Arch
)
1557 # Look through the tool definitions for GUIDed tools
1559 for (attrib
, value
) in self
.ToolDef
.ToolsDefTxtDictionary
.iteritems():
1560 if attrib
.upper().endswith('_GUID'):
1561 split
= attrib
.split('_')
1562 thisPrefix
= '_'.join(split
[0:3]) + '_'
1563 if thisPrefix
== prefix
:
1564 guid
= self
.ToolDef
.ToolsDefTxtDictionary
[attrib
]
1567 path
= '_'.join(split
[0:4]) + '_PATH'
1568 path
= self
.ToolDef
.ToolsDefTxtDictionary
[path
]
1569 path
= self
.GetFullPathOfTool(path
)
1570 guidAttribs
.append((guid
, toolName
, path
))
1572 # Write out GuidedSecTools.txt
1573 toolsFile
= os
.path
.join(FvDir
, 'GuidedSectionTools.txt')
1574 toolsFile
= open(toolsFile
, 'wt')
1575 for guidedSectionTool
in guidAttribs
:
1576 print >> toolsFile
, ' '.join(guidedSectionTool
)
1579 ## Returns the full path of the tool.
1581 def GetFullPathOfTool (self
, tool
):
1582 if os
.path
.exists(tool
):
1583 return os
.path
.realpath(tool
)
1585 # We need to search for the tool using the
1586 # PATH environment variable.
1587 for dirInPath
in os
.environ
['PATH'].split(os
.pathsep
):
1588 foundPath
= os
.path
.join(dirInPath
, tool
)
1589 if os
.path
.exists(foundPath
):
1590 return os
.path
.realpath(foundPath
)
1592 # If the tool was not found in the path then we just return
1596 ## Launch the module or platform build
1599 if self
.ModuleFile
== None or self
.ModuleFile
== "":
1600 if not self
.SpawnMode
or self
.Target
not in ["", "all"]:
1601 self
.SpawnMode
= False
1602 self
._BuildPlatform
()
1604 self
._MultiThreadBuildPlatform
()
1605 self
.CreateGuidedSectionToolsFile()
1607 self
.SpawnMode
= False
1610 ## Do some clean-up works when error occurred
1611 def Relinquish(self
):
1612 OldLogLevel
= EdkLogger
.GetLevel()
1613 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
1614 #self.DumpBuildData()
1615 Utils
.Progressor
.Abort()
1616 if self
.SpawnMode
== True:
1618 EdkLogger
.SetLevel(OldLogLevel
)
1620 def DumpBuildData(self
):
1621 CacheDirectory
= os
.path
.join(self
.WorkspaceDir
, gBuildCacheDir
)
1622 Utils
.CreateDirectory(CacheDirectory
)
1623 Utils
.DataDump(Utils
.gFileTimeStampCache
, os
.path
.join(CacheDirectory
, "gFileTimeStampCache"))
1624 Utils
.DataDump(Utils
.gDependencyDatabase
, os
.path
.join(CacheDirectory
, "gDependencyDatabase"))
1626 def RestoreBuildData(self
):
1627 FilePath
= os
.path
.join(self
.WorkspaceDir
, gBuildCacheDir
, "gFileTimeStampCache")
1628 if Utils
.gFileTimeStampCache
== {} and os
.path
.isfile(FilePath
):
1629 Utils
.gFileTimeStampCache
= Utils
.DataRestore(FilePath
)
1630 if Utils
.gFileTimeStampCache
== None:
1631 Utils
.gFileTimeStampCache
= {}
1633 FilePath
= os
.path
.join(self
.WorkspaceDir
, gBuildCacheDir
, "gDependencyDatabase")
1634 if Utils
.gDependencyDatabase
== {} and os
.path
.isfile(FilePath
):
1635 Utils
.gDependencyDatabase
= Utils
.DataRestore(FilePath
)
1636 if Utils
.gDependencyDatabase
== None:
1637 Utils
.gDependencyDatabase
= {}
1639 def ParseDefines(DefineList
=[]):
1641 if DefineList
!= None:
1642 for Define
in DefineList
:
1643 DefineTokenList
= Define
.split("=", 1)
1644 if len(DefineTokenList
) == 1:
1645 DefineDict
[DefineTokenList
[0]] = ""
1647 DefineDict
[DefineTokenList
[0]] = DefineTokenList
[1].strip()
1651 def SingleCheckCallback(option
, opt_str
, value
, parser
):
1652 if option
not in gParamCheck
:
1653 setattr(parser
.values
, option
.dest
, value
)
1654 gParamCheck
.append(option
)
1656 parser
.error("Option %s only allows one instance in command line!" % option
)
1658 ## Parse command line options
1660 # Using standard Python module optparse to parse command line option of this tool.
1662 # @retval Opt A optparse.Values object containing the parsed options
1663 # @retval Args Target of build command
1665 def MyOptionParser():
1666 Parser
= OptionParser(description
=__copyright__
,version
=__version__
,prog
="build.exe",usage
="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")
1667 Parser
.add_option("-a", "--arch", action
="append", type="choice", choices
=['IA32','X64','IPF','EBC','ARM'], dest
="TargetArch",
1668 help="ARCHS is one of list: IA32, X64, IPF, ARM or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")
1669 Parser
.add_option("-p", "--platform", action
="callback", type="string", dest
="PlatformFile", callback
=SingleCheckCallback
,
1670 help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")
1671 Parser
.add_option("-m", "--module", action
="callback", type="string", dest
="ModuleFile", callback
=SingleCheckCallback
,
1672 help="Build the module specified by the INF file name argument.")
1673 Parser
.add_option("-b", "--buildtarget", action
="append", type="choice", choices
=['DEBUG','RELEASE'], dest
="BuildTarget",
1674 help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")
1675 Parser
.add_option("-t", "--tagname", action
="append", type="string", dest
="ToolChain",
1676 help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")
1677 Parser
.add_option("-x", "--sku-id", action
="callback", type="string", dest
="SkuId", callback
=SingleCheckCallback
,
1678 help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")
1680 Parser
.add_option("-n", action
="callback", type="int", dest
="ThreadNumber", callback
=SingleCheckCallback
,
1681 help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. Less than 2 will disable multi-thread builds.")
1683 Parser
.add_option("-f", "--fdf", action
="callback", type="string", dest
="FdfFile", callback
=SingleCheckCallback
,
1684 help="The name of the FDF file to use, which overrides the setting in the DSC file.")
1685 Parser
.add_option("-r", "--rom-image", action
="append", type="string", dest
="RomImage", default
=[],
1686 help="The name of FD to be generated. The name must be from [FD] section in FDF file.")
1687 Parser
.add_option("-i", "--fv-image", action
="append", type="string", dest
="FvImage", default
=[],
1688 help="The name of FV to be generated. The name must be from [FV] section in FDF file.")
1690 Parser
.add_option("-u", "--skip-autogen", action
="store_true", dest
="SkipAutoGen", help="Skip AutoGen step.")
1691 Parser
.add_option("-e", "--re-parse", action
="store_true", dest
="Reparse", help="Re-parse all meta-data files.")
1693 Parser
.add_option("-c", "--case-insensitive", action
="store_true", dest
="CaseInsensitive", help="Don't check case of file name.")
1695 # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",
1696 # help="Define global macro which can be used in DSC/DEC/INF files.")
1698 Parser
.add_option("-w", "--warning-as-error", action
="store_true", dest
="WarningAsError", help="Treat warning in tools as error.")
1699 Parser
.add_option("-j", "--log", action
="store", dest
="LogFile", help="Put log in specified file as well as on console.")
1701 Parser
.add_option("-s", "--silent", action
="store_true", type=None, dest
="SilentMode",
1702 help="Make use of silent mode of (n)make.")
1703 Parser
.add_option("-q", "--quiet", action
="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
1704 Parser
.add_option("-v", "--verbose", action
="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
1705 "including library instances selected, final dependency expression, "\
1706 "and warning messages, etc.")
1707 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
1708 Parser
.add_option("-D", "--define", action
="append", type="string", dest
="Macros", help="Macro: \"Name [= Value]\".")
1710 Parser
.add_option("-y", "--report-file", action
="store", dest
="ReportFile", help="Create/overwrite the report to the specified filename.")
1711 Parser
.add_option("-Y", "--report-type", action
="append", type="choice", choices
=['PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','FIXED_ADDRESS', 'EXECUTION_ORDER'], dest
="ReportType", default
=[],
1712 help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, EXECUTION_ORDER]. "\
1713 "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS]")
1715 (Opt
, Args
)=Parser
.parse_args()
1718 ## Tool entrance method
1720 # This method mainly dispatch specific methods per the command line options.
1721 # If no error found, return zero value so the caller of this tool can know
1722 # if it's executed successfully or not.
1724 # @retval 0 Tool was successful
1725 # @retval 1 Tool failed
1728 StartTime
= time
.time()
1730 # Initialize log system
1731 EdkLogger
.Initialize()
1734 # Parse the options and args
1736 (Option
, Target
) = MyOptionParser()
1737 GlobalData
.gOptions
= Option
1738 GlobalData
.gCaseInsensitive
= Option
.CaseInsensitive
1741 if Option
.verbose
!= None:
1742 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
1743 elif Option
.quiet
!= None:
1744 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
1745 elif Option
.debug
!= None:
1746 EdkLogger
.SetLevel(Option
.debug
+ 1)
1748 EdkLogger
.SetLevel(EdkLogger
.INFO
)
1750 if Option
.LogFile
!= None:
1751 EdkLogger
.SetLogFile(Option
.LogFile
)
1753 if Option
.WarningAsError
== True:
1754 EdkLogger
.SetWarningAsError()
1756 if platform
.platform().find("Windows") >= 0:
1757 GlobalData
.gIsWindows
= True
1759 GlobalData
.gIsWindows
= False
1761 EdkLogger
.quiet(time
.strftime("%H:%M:%S, %b.%d %Y ", time
.localtime()) + "[%s]\n" % platform
.platform())
1765 if len(Target
) == 0:
1767 elif len(Target
) >= 2:
1768 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "More than one targets are not supported.",
1769 ExtraData
="Please select one of: %s" %(' '.join(gSupportedTarget
)))
1771 Target
= Target
[0].lower()
1773 if Target
not in gSupportedTarget
:
1774 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "Not supported target [%s]." % Target
,
1775 ExtraData
="Please select one of: %s" %(' '.join(gSupportedTarget
)))
1777 GlobalData
.gGlobalDefines
= ParseDefines(Option
.Macros
)
1779 # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
1782 Workspace
= os
.getenv("WORKSPACE")
1784 # Get files real name in workspace dir
1786 GlobalData
.gAllFiles
= Utils
.DirCache(Workspace
)
1788 WorkingDirectory
= os
.getcwd()
1789 if not Option
.ModuleFile
:
1790 FileList
= glob
.glob(os
.path
.normpath(os
.path
.join(WorkingDirectory
, '*.inf')))
1791 FileNum
= len(FileList
)
1793 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "There are %d INF files in %s." % (FileNum
, WorkingDirectory
),
1794 ExtraData
="Please use '-m <INF_FILE_PATH>' switch to choose one.")
1796 Option
.ModuleFile
= NormFile(FileList
[0], Workspace
)
1798 if Option
.ModuleFile
:
1799 if os
.path
.isabs (Option
.ModuleFile
):
1800 if os
.path
.normcase (os
.path
.normpath(Option
.ModuleFile
)).find (Workspace
) == 0:
1801 Option
.ModuleFile
= NormFile(os
.path
.normpath(Option
.ModuleFile
), Workspace
)
1802 Option
.ModuleFile
= PathClass(Option
.ModuleFile
, Workspace
)
1803 ErrorCode
, ErrorInfo
= Option
.ModuleFile
.Validate(".inf", False)
1805 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
1807 if Option
.PlatformFile
!= None:
1808 if os
.path
.isabs (Option
.PlatformFile
):
1809 if os
.path
.normcase (os
.path
.normpath(Option
.PlatformFile
)).find (Workspace
) == 0:
1810 Option
.PlatformFile
= NormFile(os
.path
.normpath(Option
.PlatformFile
), Workspace
)
1811 Option
.PlatformFile
= PathClass(Option
.PlatformFile
, Workspace
)
1812 ErrorCode
, ErrorInfo
= Option
.PlatformFile
.Validate(".dsc", False)
1814 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
1816 if Option
.FdfFile
!= None:
1817 if os
.path
.isabs (Option
.FdfFile
):
1818 if os
.path
.normcase (os
.path
.normpath(Option
.FdfFile
)).find (Workspace
) == 0:
1819 Option
.FdfFile
= NormFile(os
.path
.normpath(Option
.FdfFile
), Workspace
)
1820 Option
.FdfFile
= PathClass(Option
.FdfFile
, Workspace
)
1821 ErrorCode
, ErrorInfo
= Option
.FdfFile
.Validate(".fdf", False)
1823 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
1825 MyBuild
= Build(Target
, Workspace
, Option
.PlatformFile
, Option
.ModuleFile
,
1826 Option
.TargetArch
, Option
.ToolChain
, Option
.BuildTarget
,
1827 Option
.FdfFile
, Option
.RomImage
, Option
.FvImage
,
1828 None, Option
.SilentMode
, Option
.ThreadNumber
,
1829 Option
.SkipAutoGen
, Option
.Reparse
, Option
.SkuId
,
1830 Option
.ReportFile
, Option
.ReportType
)
1832 #MyBuild.DumpBuildData()
1833 except FatalError
, X
:
1835 # for multi-thread build exits safely
1836 MyBuild
.Relinquish()
1837 if Option
!= None and Option
.debug
!= None:
1838 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
1839 ReturnCode
= X
.args
[0]
1841 # error from Fdf parser
1843 # for multi-thread build exits safely
1844 MyBuild
.Relinquish()
1845 if Option
!= None and Option
.debug
!= None:
1846 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
1848 EdkLogger
.error(X
.ToolName
, FORMAT_INVALID
, File
=X
.FileName
, Line
=X
.LineNumber
, ExtraData
=X
.Message
, RaiseError
= False)
1849 ReturnCode
= FORMAT_INVALID
1850 except KeyboardInterrupt:
1851 ReturnCode
= ABORT_ERROR
1852 if Option
!= None and Option
.debug
!= None:
1853 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
1856 # for multi-thread build exits safely
1857 MyBuild
.Relinquish()
1859 # try to get the meta-file from the object causing exception
1860 Tb
= sys
.exc_info()[-1]
1861 MetaFile
= GlobalData
.gProcessingFile
1863 if 'self' in Tb
.tb_frame
.f_locals
and hasattr(Tb
.tb_frame
.f_locals
['self'], 'MetaFile'):
1864 MetaFile
= Tb
.tb_frame
.f_locals
['self'].MetaFile
1869 "Unknown fatal error when processing [%s]" % MetaFile
,
1870 ExtraData
="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n",
1873 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
1874 ReturnCode
= CODE_ERROR
1876 Utils
.Progressor
.Abort()
1880 elif ReturnCode
== ABORT_ERROR
:
1881 Conclusion
= "Aborted"
1883 Conclusion
= "Failed"
1884 FinishTime
= time
.time()
1885 BuildDuration
= time
.strftime("%M:%S", time
.gmtime(int(round(FinishTime
- StartTime
))))
1887 MyBuild
.BuildReport
.GenerateReport(BuildDuration
)
1889 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
1890 EdkLogger
.quiet("\n- %s -\n%s [%s]" % (Conclusion
, time
.strftime("%H:%M:%S, %b.%d %Y", time
.localtime()), BuildDuration
))
1894 if __name__
== '__main__':
1896 ## 0-127 is a safe return range, and 1 is a standard default error
1897 if r
< 0 or r
> 127: r
= 1