2 # build a platform or a module
4 # Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>
5 # Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
6 # Copyright (c) 2018, Hewlett Packard Enterprise Development, L.P.<BR>
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
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.
20 from __future__
import print_function
21 import Common
.LongFilePathOs
as os
28 import encodings
.ascii
29 import multiprocessing
32 from threading
import *
34 from optparse
import OptionParser
35 from subprocess
import *
36 from Common
import Misc
as Utils
38 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
39 from Common
.TargetTxtClassObject
import TargetTxtClassObject
40 from Common
.ToolDefClassObject
import ToolDefClassObject
41 from Common
.DataType
import *
42 from Common
.BuildVersion
import gBUILD_VERSION
43 from AutoGen
.AutoGen
import *
44 from Common
.BuildToolError
import *
45 from Workspace
.WorkspaceDatabase
import WorkspaceDatabase
46 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
48 from BuildReport
import BuildReport
49 from GenPatchPcdTable
.GenPatchPcdTable
import *
50 from PatchPcdValue
.PatchPcdValue
import *
52 import Common
.EdkLogger
53 import Common
.GlobalData
as GlobalData
54 from GenFds
.GenFds
import GenFds
, GenFdsApi
56 from collections
import OrderedDict
, defaultdict
58 # Version and Copyright
59 VersionNumber
= "0.60" + ' ' + gBUILD_VERSION
60 __version__
= "%prog Version " + VersionNumber
61 __copyright__
= "Copyright (c) 2007 - 2018, Intel Corporation All rights reserved."
63 ## standard targets of build command
64 gSupportedTarget
= ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']
66 ## build configuration file
67 gBuildConfiguration
= "target.txt"
68 gToolsDefinition
= "tools_def.txt"
70 TemporaryTablePattern
= re
.compile(r
'^_\d+_\d+_[a-fA-F0-9]+$')
73 ## Check environment PATH variable to make sure the specified tool is found
75 # If the tool is found in the PATH, then True is returned
76 # Otherwise, False is returned
78 def IsToolInPath(tool
):
79 if 'PATHEXT' in os
.environ
:
80 extns
= os
.environ
['PATHEXT'].split(os
.path
.pathsep
)
83 for pathDir
in os
.environ
['PATH'].split(os
.path
.pathsep
):
85 if os
.path
.exists(os
.path
.join(pathDir
, tool
+ ext
)):
89 ## Check environment variables
91 # Check environment variables that must be set for build. Currently they are
93 # WORKSPACE The directory all packages/platforms start from
94 # EDK_TOOLS_PATH The directory contains all tools needed by the build
95 # PATH $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH
97 # If any of above environment variable is not set or has error, the build
100 def CheckEnvVariable():
102 if "WORKSPACE" not in os
.environ
:
103 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
104 ExtraData
="WORKSPACE")
106 WorkspaceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["WORKSPACE"]))
107 if not os
.path
.exists(WorkspaceDir
):
108 EdkLogger
.error("build", FILE_NOT_FOUND
, "WORKSPACE doesn't exist", ExtraData
=WorkspaceDir
)
109 elif ' ' in WorkspaceDir
:
110 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in WORKSPACE path",
111 ExtraData
=WorkspaceDir
)
112 os
.environ
["WORKSPACE"] = WorkspaceDir
114 # set multiple workspace
115 PackagesPath
= os
.getenv("PACKAGES_PATH")
116 mws
.setWs(WorkspaceDir
, PackagesPath
)
117 if mws
.PACKAGES_PATH
:
118 for Path
in mws
.PACKAGES_PATH
:
119 if not os
.path
.exists(Path
):
120 EdkLogger
.error("build", FILE_NOT_FOUND
, "One Path in PACKAGES_PATH doesn't exist", ExtraData
=Path
)
122 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in PACKAGES_PATH", ExtraData
=Path
)
125 os
.environ
["EDK_TOOLS_PATH"] = os
.path
.normcase(os
.environ
["EDK_TOOLS_PATH"])
127 # check EDK_TOOLS_PATH
128 if "EDK_TOOLS_PATH" not in os
.environ
:
129 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
130 ExtraData
="EDK_TOOLS_PATH")
133 if "PATH" not in os
.environ
:
134 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
137 GlobalData
.gWorkspace
= WorkspaceDir
139 GlobalData
.gGlobalDefines
["WORKSPACE"] = WorkspaceDir
140 GlobalData
.gGlobalDefines
["EDK_TOOLS_PATH"] = os
.environ
["EDK_TOOLS_PATH"]
142 ## Get normalized file path
144 # Convert the path to be local format, and remove the WORKSPACE path at the
145 # beginning if the file path is given in full path.
147 # @param FilePath File path to be normalized
148 # @param Workspace Workspace path which the FilePath will be checked against
150 # @retval string The normalized file path
152 def NormFile(FilePath
, Workspace
):
153 # check if the path is absolute or relative
154 if os
.path
.isabs(FilePath
):
155 FileFullPath
= os
.path
.normpath(FilePath
)
157 FileFullPath
= os
.path
.normpath(mws
.join(Workspace
, FilePath
))
158 Workspace
= mws
.getWs(Workspace
, FilePath
)
160 # check if the file path exists or not
161 if not os
.path
.isfile(FileFullPath
):
162 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath
)
164 # remove workspace directory from the beginning part of the file path
165 if Workspace
[-1] in ["\\", "/"]:
166 return FileFullPath
[len(Workspace
):]
168 return FileFullPath
[(len(Workspace
) + 1):]
170 ## Get the output of an external program
172 # This is the entrance method of thread reading output of an external program and
173 # putting them in STDOUT/STDERR of current program.
175 # @param From The stream message read from
176 # @param To The stream message put on
177 # @param ExitFlag The flag used to indicate stopping reading
179 def ReadMessage(From
, To
, ExitFlag
):
181 # read one line a time
182 Line
= From
.readline()
183 # empty string means "end"
184 if Line
is not None and Line
!= b
"":
185 To(Line
.rstrip().decode(encoding
='utf-8', errors
='ignore'))
191 ## Launch an external program
193 # This method will call subprocess.Popen to execute an external program with
194 # given options in specified directory. Because of the dead-lock issue during
195 # redirecting output of the external program, threads are used to to do the
198 # @param Command A list or string containing the call of the program
199 # @param WorkingDir The directory in which the program will be running
201 def LaunchCommand(Command
, WorkingDir
):
202 BeginTime
= time
.time()
203 # if working directory doesn't exist, Popen() will raise an exception
204 if not os
.path
.isdir(WorkingDir
):
205 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=WorkingDir
)
207 # Command is used as the first Argument in following Popen().
208 # It could be a string or sequence. We find that if command is a string in following Popen(),
209 # ubuntu may fail with an error message that the command is not found.
210 # So here we may need convert command from string to list instance.
211 if platform
.system() != 'Windows':
212 if not isinstance(Command
, list):
213 Command
= Command
.split()
214 Command
= ' '.join(Command
)
217 EndOfProcedure
= None
220 Proc
= Popen(Command
, stdout
=PIPE
, stderr
=PIPE
, env
=os
.environ
, cwd
=WorkingDir
, bufsize
=-1, shell
=True)
222 # launch two threads to read the STDOUT and STDERR
223 EndOfProcedure
= Event()
224 EndOfProcedure
.clear()
226 StdOutThread
= Thread(target
=ReadMessage
, args
=(Proc
.stdout
, EdkLogger
.info
, EndOfProcedure
))
227 StdOutThread
.setName("STDOUT-Redirector")
228 StdOutThread
.setDaemon(False)
232 StdErrThread
= Thread(target
=ReadMessage
, args
=(Proc
.stderr
, EdkLogger
.quiet
, EndOfProcedure
))
233 StdErrThread
.setName("STDERR-Redirector")
234 StdErrThread
.setDaemon(False)
237 # waiting for program exit
239 except: # in case of aborting
240 # terminate the threads redirecting the program output
241 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
242 if EndOfProcedure
is not None:
245 if not isinstance(Command
, type("")):
246 Command
= " ".join(Command
)
247 EdkLogger
.error("build", COMMAND_FAILURE
, "Failed to start command", ExtraData
="%s [%s]" % (Command
, WorkingDir
))
254 # check the return code of the program
255 if Proc
.returncode
!= 0:
256 if not isinstance(Command
, type("")):
257 Command
= " ".join(Command
)
258 # print out the Response file and its content when make failure
259 RespFile
= os
.path
.join(WorkingDir
, 'OUTPUT', 'respfilelist.txt')
260 if os
.path
.isfile(RespFile
):
262 RespContent
= f
.read()
264 EdkLogger
.info(RespContent
)
266 EdkLogger
.error("build", COMMAND_FAILURE
, ExtraData
="%s [%s]" % (Command
, WorkingDir
))
267 return "%dms" % (int(round((time
.time() - BeginTime
) * 1000)))
269 ## The smallest unit that can be built in multi-thread build mode
271 # This is the base class of build unit. The "Obj" parameter must provide
272 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units
275 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.
280 # @param self The object pointer
281 # @param Obj The object the build is working on
282 # @param Target The build target name, one of gSupportedTarget
283 # @param Dependency The BuildUnit(s) which must be completed in advance
284 # @param WorkingDir The directory build command starts in
286 def __init__(self
, Obj
, BuildCommand
, Target
, Dependency
, WorkingDir
="."):
287 self
.BuildObject
= Obj
288 self
.Dependency
= Dependency
289 self
.WorkingDir
= WorkingDir
291 self
.BuildCommand
= BuildCommand
293 EdkLogger
.error("build", OPTION_MISSING
,
294 "No build command found for this module. "
295 "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
296 (Obj
.BuildTarget
, Obj
.ToolChain
, Obj
.Arch
),
302 # It just returns the string representation of self.BuildObject
304 # @param self The object pointer
307 return str(self
.BuildObject
)
309 ## "==" operator method
311 # It just compares self.BuildObject with "Other". So self.BuildObject must
312 # provide its own __eq__() method.
314 # @param self The object pointer
315 # @param Other The other BuildUnit object compared to
317 def __eq__(self
, Other
):
318 return Other
and self
.BuildObject
== Other
.BuildObject \
319 and Other
.BuildObject \
320 and self
.BuildObject
.Arch
== Other
.BuildObject
.Arch
324 # It just returns the hash value of self.BuildObject which must be hashable.
326 # @param self The object pointer
329 return hash(self
.BuildObject
) + hash(self
.BuildObject
.Arch
)
332 return repr(self
.BuildObject
)
334 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode
336 # This class is for module build by nmake/make build system. The "Obj" parameter
337 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
338 # be make units missing build.
340 # Currently the "Obj" should be only ModuleAutoGen object.
342 class ModuleMakeUnit(BuildUnit
):
345 # @param self The object pointer
346 # @param Obj The ModuleAutoGen object the build is working on
347 # @param Target The build target name, one of gSupportedTarget
349 def __init__(self
, Obj
, Target
):
350 Dependency
= [ModuleMakeUnit(La
, Target
) for La
in Obj
.LibraryAutoGenList
]
351 BuildUnit
.__init
__(self
, Obj
, Obj
.BuildCommand
, Target
, Dependency
, Obj
.MakeFileDir
)
352 if Target
in [None, "", "all"]:
353 self
.Target
= "tbuild"
355 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode
357 # This class is for platform build by nmake/make build system. The "Obj" parameter
358 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
359 # be make units missing build.
361 # Currently the "Obj" should be only PlatformAutoGen object.
363 class PlatformMakeUnit(BuildUnit
):
366 # @param self The object pointer
367 # @param Obj The PlatformAutoGen object the build is working on
368 # @param Target The build target name, one of gSupportedTarget
370 def __init__(self
, Obj
, Target
):
371 Dependency
= [ModuleMakeUnit(Lib
, Target
) for Lib
in self
.BuildObject
.LibraryAutoGenList
]
372 Dependency
.extend([ModuleMakeUnit(Mod
, Target
) for Mod
in self
.BuildObject
.ModuleAutoGenList
])
373 BuildUnit
.__init
__(self
, Obj
, Obj
.BuildCommand
, Target
, Dependency
, Obj
.MakeFileDir
)
375 ## The class representing the task of a module build or platform build
377 # This class manages the build tasks in multi-thread build mode. Its jobs include
378 # scheduling thread running, catching thread error, monitor the thread status, etc.
381 # queue for tasks waiting for schedule
382 _PendingQueue
= OrderedDict()
383 _PendingQueueLock
= threading
.Lock()
385 # queue for tasks ready for running
386 _ReadyQueue
= OrderedDict()
387 _ReadyQueueLock
= threading
.Lock()
389 # queue for run tasks
390 _RunningQueue
= OrderedDict()
391 _RunningQueueLock
= threading
.Lock()
393 # queue containing all build tasks, in case duplicate build
394 _TaskQueue
= OrderedDict()
396 # flag indicating error occurs in a running thread
397 _ErrorFlag
= threading
.Event()
401 # BoundedSemaphore object used to control the number of running threads
404 # flag indicating if the scheduler is started or not
405 _SchedulerStopped
= threading
.Event()
406 _SchedulerStopped
.set()
408 ## Start the task scheduler thread
410 # @param MaxThreadNumber The maximum thread number
411 # @param ExitFlag Flag used to end the scheduler
414 def StartScheduler(MaxThreadNumber
, ExitFlag
):
415 SchedulerThread
= Thread(target
=BuildTask
.Scheduler
, args
=(MaxThreadNumber
, ExitFlag
))
416 SchedulerThread
.setName("Build-Task-Scheduler")
417 SchedulerThread
.setDaemon(False)
418 SchedulerThread
.start()
419 # wait for the scheduler to be started, especially useful in Linux
420 while not BuildTask
.IsOnGoing():
425 # @param MaxThreadNumber The maximum thread number
426 # @param ExitFlag Flag used to end the scheduler
429 def Scheduler(MaxThreadNumber
, ExitFlag
):
430 BuildTask
._SchedulerStopped
.clear()
432 # use BoundedSemaphore to control the maximum running threads
433 BuildTask
._Thread
= BoundedSemaphore(MaxThreadNumber
)
435 # scheduling loop, which will exits when no pending/ready task and
436 # indicated to do so, or there's error in running thread
438 while (len(BuildTask
._PendingQueue
) > 0 or len(BuildTask
._ReadyQueue
) > 0 \
439 or not ExitFlag
.isSet()) and not BuildTask
._ErrorFlag
.isSet():
440 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "Pending Queue (%d), Ready Queue (%d)"
441 % (len(BuildTask
._PendingQueue
), len(BuildTask
._ReadyQueue
)))
443 # get all pending tasks
444 BuildTask
._PendingQueueLock
.acquire()
445 BuildObjectList
= list(BuildTask
._PendingQueue
.keys())
447 # check if their dependency is resolved, and if true, move them
450 for BuildObject
in BuildObjectList
:
451 Bt
= BuildTask
._PendingQueue
[BuildObject
]
453 BuildTask
._ReadyQueue
[BuildObject
] = BuildTask
._PendingQueue
.pop(BuildObject
)
454 BuildTask
._PendingQueueLock
.release()
456 # launch build thread until the maximum number of threads is reached
457 while not BuildTask
._ErrorFlag
.isSet():
458 # empty ready queue, do nothing further
459 if len(BuildTask
._ReadyQueue
) == 0:
462 # wait for active thread(s) exit
463 BuildTask
._Thread
.acquire(True)
465 # start a new build thread
466 Bo
, Bt
= BuildTask
._ReadyQueue
.popitem()
468 # move into running queue
469 BuildTask
._RunningQueueLock
.acquire()
470 BuildTask
._RunningQueue
[Bo
] = Bt
471 BuildTask
._RunningQueueLock
.release()
480 # wait for all running threads exit
481 if BuildTask
._ErrorFlag
.isSet():
482 EdkLogger
.quiet("\nWaiting for all build threads exit...")
483 # while not BuildTask._ErrorFlag.isSet() and \
484 while len(BuildTask
._RunningQueue
) > 0:
485 EdkLogger
.verbose("Waiting for thread ending...(%d)" % len(BuildTask
._RunningQueue
))
486 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "Threads [%s]" % ", ".join(Th
.getName() for Th
in threading
.enumerate()))
489 except BaseException
as X
:
491 # TRICK: hide the output of threads left running, so that the user can
492 # catch the error message easily
494 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
495 BuildTask
._ErrorFlag
.set()
496 BuildTask
._ErrorMessage
= "build thread scheduler error\n\t%s" % str(X
)
498 BuildTask
._PendingQueue
.clear()
499 BuildTask
._ReadyQueue
.clear()
500 BuildTask
._RunningQueue
.clear()
501 BuildTask
._TaskQueue
.clear()
502 BuildTask
._SchedulerStopped
.set()
504 ## Wait for all running method exit
507 def WaitForComplete():
508 BuildTask
._SchedulerStopped
.wait()
510 ## Check if the scheduler is running or not
514 return not BuildTask
._SchedulerStopped
.isSet()
519 if BuildTask
.IsOnGoing():
520 BuildTask
._ErrorFlag
.set()
521 BuildTask
.WaitForComplete()
523 ## Check if there's error in running thread
525 # Since the main thread cannot catch exceptions in other thread, we have to
526 # use threading.Event to communicate this formation to main thread.
530 return BuildTask
._ErrorFlag
.isSet()
532 ## Get error message in running thread
534 # Since the main thread cannot catch exceptions in other thread, we have to
535 # use a static variable to communicate this message to main thread.
538 def GetErrorMessage():
539 return BuildTask
._ErrorMessage
541 ## Factory method to create a BuildTask object
543 # This method will check if a module is building or has been built. And if
544 # true, just return the associated BuildTask object in the _TaskQueue. If
545 # not, create and return a new BuildTask object. The new BuildTask object
546 # will be appended to the _PendingQueue for scheduling later.
548 # @param BuildItem A BuildUnit object representing a build object
549 # @param Dependency The dependent build object of BuildItem
552 def New(BuildItem
, Dependency
=None):
553 if BuildItem
in BuildTask
._TaskQueue
:
554 Bt
= BuildTask
._TaskQueue
[BuildItem
]
558 Bt
._Init
(BuildItem
, Dependency
)
559 BuildTask
._TaskQueue
[BuildItem
] = Bt
561 BuildTask
._PendingQueueLock
.acquire()
562 BuildTask
._PendingQueue
[BuildItem
] = Bt
563 BuildTask
._PendingQueueLock
.release()
567 ## The real constructor of BuildTask
569 # @param BuildItem A BuildUnit object representing a build object
570 # @param Dependency The dependent build object of BuildItem
572 def _Init(self
, BuildItem
, Dependency
=None):
573 self
.BuildItem
= BuildItem
575 self
.DependencyList
= []
576 if Dependency
is None:
577 Dependency
= BuildItem
.Dependency
579 Dependency
.extend(BuildItem
.Dependency
)
580 self
.AddDependency(Dependency
)
581 # flag indicating build completes, used to avoid unnecessary re-build
582 self
.CompleteFlag
= False
584 ## Check if all dependent build tasks are completed or not
588 for Dep
in self
.DependencyList
:
589 if Dep
.CompleteFlag
== True:
596 ## Add dependent build task
598 # @param Dependency The list of dependent build objects
600 def AddDependency(self
, Dependency
):
601 for Dep
in Dependency
:
602 if not Dep
.BuildObject
.IsBinaryModule
:
603 self
.DependencyList
.append(BuildTask
.New(Dep
)) # BuildTask list
605 ## The thread wrapper of LaunchCommand function
607 # @param Command A list or string contains the call of the command
608 # @param WorkingDir The directory in which the program will be running
610 def _CommandThread(self
, Command
, WorkingDir
):
612 self
.BuildItem
.BuildObject
.BuildTime
= LaunchCommand(Command
, WorkingDir
)
613 self
.CompleteFlag
= True
616 # TRICK: hide the output of threads left running, so that the user can
617 # catch the error message easily
619 if not BuildTask
._ErrorFlag
.isSet():
620 GlobalData
.gBuildingModule
= "%s [%s, %s, %s]" % (str(self
.BuildItem
.BuildObject
),
621 self
.BuildItem
.BuildObject
.Arch
,
622 self
.BuildItem
.BuildObject
.ToolChain
,
623 self
.BuildItem
.BuildObject
.BuildTarget
625 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
626 BuildTask
._ErrorFlag
.set()
627 BuildTask
._ErrorMessage
= "%s broken\n %s [%s]" % \
628 (threading
.currentThread().getName(), Command
, WorkingDir
)
629 # indicate there's a thread is available for another build task
630 BuildTask
._RunningQueueLock
.acquire()
631 BuildTask
._RunningQueue
.pop(self
.BuildItem
)
632 BuildTask
._RunningQueueLock
.release()
633 BuildTask
._Thread
.release()
635 ## Start build task thread
638 EdkLogger
.quiet("Building ... %s" % repr(self
.BuildItem
))
639 Command
= self
.BuildItem
.BuildCommand
+ [self
.BuildItem
.Target
]
640 self
.BuildTread
= Thread(target
=self
._CommandThread
, args
=(Command
, self
.BuildItem
.WorkingDir
))
641 self
.BuildTread
.setName("build thread")
642 self
.BuildTread
.setDaemon(False)
643 self
.BuildTread
.start()
645 ## The class contains the information related to EFI image
650 # Constructor will load all required image information.
652 # @param BaseName The full file path of image.
653 # @param Guid The GUID for image.
654 # @param Arch Arch of this image.
655 # @param OutputDir The output directory for image.
656 # @param DebugDir The debug directory for image.
657 # @param ImageClass PeImage Information
659 def __init__(self
, BaseName
, Guid
, Arch
, OutputDir
, DebugDir
, ImageClass
):
660 self
.BaseName
= BaseName
663 self
.OutputDir
= OutputDir
664 self
.DebugDir
= DebugDir
665 self
.Image
= ImageClass
666 self
.Image
.Size
= (self
.Image
.Size
// 0x1000 + 1) * 0x1000
668 ## The class implementing the EDK2 build process
670 # The build process includes:
671 # 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf
672 # 2. Parse DSC file of active platform
673 # 3. Parse FDF file if any
674 # 4. Establish build database, including parse all other files (module, package)
675 # 5. Create AutoGen files (C code file, depex file, makefile) if necessary
676 # 6. Call build command
681 # Constructor will load all necessary configurations, parse platform, modules
682 # and packages and the establish a database for AutoGen.
684 # @param Target The build command target, one of gSupportedTarget
685 # @param WorkspaceDir The directory of workspace
686 # @param BuildOptions Build options passed from command line
688 def __init__(self
, Target
, WorkspaceDir
, BuildOptions
):
689 self
.WorkspaceDir
= WorkspaceDir
691 self
.PlatformFile
= BuildOptions
.PlatformFile
692 self
.ModuleFile
= BuildOptions
.ModuleFile
693 self
.ArchList
= BuildOptions
.TargetArch
694 self
.ToolChainList
= BuildOptions
.ToolChain
695 self
.BuildTargetList
= BuildOptions
.BuildTarget
696 self
.Fdf
= BuildOptions
.FdfFile
697 self
.FdList
= BuildOptions
.RomImage
698 self
.FvList
= BuildOptions
.FvImage
699 self
.CapList
= BuildOptions
.CapName
700 self
.SilentMode
= BuildOptions
.SilentMode
701 self
.ThreadNumber
= BuildOptions
.ThreadNumber
702 self
.SkipAutoGen
= BuildOptions
.SkipAutoGen
703 self
.Reparse
= BuildOptions
.Reparse
704 self
.SkuId
= BuildOptions
.SkuId
706 GlobalData
.gSKUID_CMD
= self
.SkuId
707 self
.ConfDirectory
= BuildOptions
.ConfDirectory
708 self
.SpawnMode
= True
709 self
.BuildReport
= BuildReport(BuildOptions
.ReportFile
, BuildOptions
.ReportType
)
710 self
.TargetTxt
= TargetTxtClassObject()
711 self
.ToolDef
= ToolDefClassObject()
715 GlobalData
.BuildOptionPcd
= BuildOptions
.OptionPcd
if BuildOptions
.OptionPcd
else []
716 #Set global flag for build mode
717 GlobalData
.gIgnoreSource
= BuildOptions
.IgnoreSources
718 GlobalData
.gUseHashCache
= BuildOptions
.UseHashCache
719 GlobalData
.gBinCacheDest
= BuildOptions
.BinCacheDest
720 GlobalData
.gBinCacheSource
= BuildOptions
.BinCacheSource
721 GlobalData
.gEnableGenfdsMultiThread
= BuildOptions
.GenfdsMultiThread
723 if GlobalData
.gBinCacheDest
and not GlobalData
.gUseHashCache
:
724 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, ExtraData
="--binary-destination must be used together with --hash.")
726 if GlobalData
.gBinCacheSource
and not GlobalData
.gUseHashCache
:
727 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, ExtraData
="--binary-source must be used together with --hash.")
729 if GlobalData
.gBinCacheDest
and GlobalData
.gBinCacheSource
:
730 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, ExtraData
="--binary-destination can not be used together with --binary-source.")
732 if GlobalData
.gBinCacheSource
:
733 BinCacheSource
= os
.path
.normpath(GlobalData
.gBinCacheSource
)
734 if not os
.path
.isabs(BinCacheSource
):
735 BinCacheSource
= mws
.join(self
.WorkspaceDir
, BinCacheSource
)
736 GlobalData
.gBinCacheSource
= BinCacheSource
738 if GlobalData
.gBinCacheSource
is not None:
739 EdkLogger
.error("build", OPTION_VALUE_INVALID
, ExtraData
="Invalid value of option --binary-source.")
741 if GlobalData
.gBinCacheDest
:
742 BinCacheDest
= os
.path
.normpath(GlobalData
.gBinCacheDest
)
743 if not os
.path
.isabs(BinCacheDest
):
744 BinCacheDest
= mws
.join(self
.WorkspaceDir
, BinCacheDest
)
745 GlobalData
.gBinCacheDest
= BinCacheDest
747 if GlobalData
.gBinCacheDest
is not None:
748 EdkLogger
.error("build", OPTION_VALUE_INVALID
, ExtraData
="Invalid value of option --binary-destination.")
750 if self
.ConfDirectory
:
751 # Get alternate Conf location, if it is absolute, then just use the absolute directory name
752 ConfDirectoryPath
= os
.path
.normpath(self
.ConfDirectory
)
754 if not os
.path
.isabs(ConfDirectoryPath
):
755 # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE
756 # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf
757 ConfDirectoryPath
= mws
.join(self
.WorkspaceDir
, ConfDirectoryPath
)
759 if "CONF_PATH" in os
.environ
:
760 ConfDirectoryPath
= os
.path
.normcase(os
.path
.normpath(os
.environ
["CONF_PATH"]))
762 # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf
763 ConfDirectoryPath
= mws
.join(self
.WorkspaceDir
, 'Conf')
764 GlobalData
.gConfDirectory
= ConfDirectoryPath
765 GlobalData
.gDatabasePath
= os
.path
.normpath(os
.path
.join(ConfDirectoryPath
, GlobalData
.gDatabasePath
))
767 self
.Db
= WorkspaceDatabase()
768 self
.BuildDatabase
= self
.Db
.BuildObject
770 self
.ToolChainFamily
= None
771 self
.LoadFixAddress
= 0
772 self
.UniFlag
= BuildOptions
.Flag
773 self
.BuildModules
= []
774 self
.HashSkipModules
= []
776 self
.LaunchPrebuildFlag
= False
777 self
.PlatformBuildPath
= os
.path
.join(GlobalData
.gConfDirectory
, '.cache', '.PlatformBuild')
778 if BuildOptions
.CommandLength
:
779 GlobalData
.gCommandMaxLength
= BuildOptions
.CommandLength
781 # print dot character during doing some time-consuming work
782 self
.Progress
= Utils
.Progressor()
783 # print current build environment and configuration
784 EdkLogger
.quiet("%-16s = %s" % ("WORKSPACE", os
.environ
["WORKSPACE"]))
785 if "PACKAGES_PATH" in os
.environ
:
786 # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env.
787 EdkLogger
.quiet("%-16s = %s" % ("PACKAGES_PATH", os
.path
.normcase(os
.path
.normpath(os
.environ
["PACKAGES_PATH"]))))
788 EdkLogger
.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os
.environ
["EDK_TOOLS_PATH"]))
789 if "EDK_TOOLS_BIN" in os
.environ
:
790 # Print the same path style with WORKSPACE env.
791 EdkLogger
.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os
.path
.normcase(os
.path
.normpath(os
.environ
["EDK_TOOLS_BIN"]))))
792 EdkLogger
.quiet("%-16s = %s" % ("CONF_PATH", GlobalData
.gConfDirectory
))
793 if "PYTHON3_ENABLE" in os
.environ
:
794 PYTHON3_ENABLE
= os
.environ
["PYTHON3_ENABLE"]
795 if PYTHON3_ENABLE
!= "TRUE":
796 PYTHON3_ENABLE
= "FALSE"
797 EdkLogger
.quiet("%-16s = %s" % ("PYTHON3_ENABLE", PYTHON3_ENABLE
))
798 if "PYTHON_COMMAND" in os
.environ
:
799 EdkLogger
.quiet("%-16s = %s" % ("PYTHON_COMMAND", os
.environ
["PYTHON_COMMAND"]))
803 EdkLogger
.quiet("%-16s = %s" % ("PREBUILD", self
.Prebuild
))
805 EdkLogger
.quiet("%-16s = %s" % ("POSTBUILD", self
.Postbuild
))
807 self
.LaunchPrebuild()
808 self
.TargetTxt
= TargetTxtClassObject()
809 self
.ToolDef
= ToolDefClassObject()
810 if not (self
.LaunchPrebuildFlag
and os
.path
.exists(self
.PlatformBuildPath
)):
814 os
.chdir(self
.WorkspaceDir
)
816 ## Load configuration
818 # This method will parse target.txt and get the build configurations.
820 def LoadConfiguration(self
):
822 # Check target.txt and tools_def.txt and Init them
824 BuildConfigurationFile
= os
.path
.normpath(os
.path
.join(GlobalData
.gConfDirectory
, gBuildConfiguration
))
825 if os
.path
.isfile(BuildConfigurationFile
) == True:
826 StatusCode
= self
.TargetTxt
.LoadTargetTxtFile(BuildConfigurationFile
)
828 ToolDefinitionFile
= self
.TargetTxt
.TargetTxtDictionary
[TAB_TAT_DEFINES_TOOL_CHAIN_CONF
]
829 if ToolDefinitionFile
== '':
830 ToolDefinitionFile
= gToolsDefinition
831 ToolDefinitionFile
= os
.path
.normpath(mws
.join(self
.WorkspaceDir
, 'Conf', ToolDefinitionFile
))
832 if os
.path
.isfile(ToolDefinitionFile
) == True:
833 StatusCode
= self
.ToolDef
.LoadToolDefFile(ToolDefinitionFile
)
835 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=ToolDefinitionFile
)
837 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=BuildConfigurationFile
)
839 # if no ARCH given in command line, get it from target.txt
840 if not self
.ArchList
:
841 self
.ArchList
= self
.TargetTxt
.TargetTxtDictionary
[TAB_TAT_DEFINES_TARGET_ARCH
]
842 self
.ArchList
= tuple(self
.ArchList
)
844 # if no build target given in command line, get it from target.txt
845 if not self
.BuildTargetList
:
846 self
.BuildTargetList
= self
.TargetTxt
.TargetTxtDictionary
[TAB_TAT_DEFINES_TARGET
]
848 # if no tool chain given in command line, get it from target.txt
849 if not self
.ToolChainList
:
850 self
.ToolChainList
= self
.TargetTxt
.TargetTxtDictionary
[TAB_TAT_DEFINES_TOOL_CHAIN_TAG
]
851 if self
.ToolChainList
is None or len(self
.ToolChainList
) == 0:
852 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
, ExtraData
="No toolchain given. Don't know how to build.\n")
854 # check if the tool chains are defined or not
855 NewToolChainList
= []
856 for ToolChain
in self
.ToolChainList
:
857 if ToolChain
not in self
.ToolDef
.ToolsDefTxtDatabase
[TAB_TOD_DEFINES_TOOL_CHAIN_TAG
]:
858 EdkLogger
.warn("build", "Tool chain [%s] is not defined" % ToolChain
)
860 NewToolChainList
.append(ToolChain
)
861 # if no tool chain available, break the build
862 if len(NewToolChainList
) == 0:
863 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
,
864 ExtraData
="[%s] not defined. No toolchain available for build!\n" % ", ".join(self
.ToolChainList
))
866 self
.ToolChainList
= NewToolChainList
869 ToolDefinition
= self
.ToolDef
.ToolsDefTxtDatabase
870 for Tool
in self
.ToolChainList
:
871 if TAB_TOD_DEFINES_FAMILY
not in ToolDefinition
or Tool
not in ToolDefinition
[TAB_TOD_DEFINES_FAMILY
] \
872 or not ToolDefinition
[TAB_TOD_DEFINES_FAMILY
][Tool
]:
873 EdkLogger
.warn("build", "No tool chain family found in configuration for %s. Default to MSFT." % Tool
)
874 ToolChainFamily
.append(TAB_COMPILER_MSFT
)
876 ToolChainFamily
.append(ToolDefinition
[TAB_TOD_DEFINES_FAMILY
][Tool
])
877 self
.ToolChainFamily
= ToolChainFamily
879 if self
.ThreadNumber
is None:
880 self
.ThreadNumber
= self
.TargetTxt
.TargetTxtDictionary
[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER
]
881 if self
.ThreadNumber
== '':
882 self
.ThreadNumber
= 0
884 self
.ThreadNumber
= int(self
.ThreadNumber
, 0)
886 if self
.ThreadNumber
== 0:
888 self
.ThreadNumber
= multiprocessing
.cpu_count()
889 except (ImportError, NotImplementedError):
890 self
.ThreadNumber
= 1
892 if not self
.PlatformFile
:
893 PlatformFile
= self
.TargetTxt
.TargetTxtDictionary
[TAB_TAT_DEFINES_ACTIVE_PLATFORM
]
895 # Try to find one in current directory
896 WorkingDirectory
= os
.getcwd()
897 FileList
= glob
.glob(os
.path
.normpath(os
.path
.join(WorkingDirectory
, '*.dsc')))
898 FileNum
= len(FileList
)
900 EdkLogger
.error("build", OPTION_MISSING
,
901 ExtraData
="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum
, WorkingDirectory
))
903 PlatformFile
= FileList
[0]
905 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
,
906 ExtraData
="No active platform specified in target.txt or command line! Nothing can be built.\n")
908 self
.PlatformFile
= PathClass(NormFile(PlatformFile
, self
.WorkspaceDir
), self
.WorkspaceDir
)
910 ## Initialize build configuration
912 # This method will parse DSC file and merge the configurations from
913 # command line and target.txt, then get the final build configurations.
916 # parse target.txt, tools_def.txt, and platform file
917 self
.LoadConfiguration()
919 # Allow case-insensitive for those from command line or configuration file
920 ErrorCode
, ErrorInfo
= self
.PlatformFile
.Validate(".dsc", False)
922 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
925 def InitPreBuild(self
):
926 self
.LoadConfiguration()
927 ErrorCode
, ErrorInfo
= self
.PlatformFile
.Validate(".dsc", False)
929 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
930 if self
.BuildTargetList
:
931 GlobalData
.gGlobalDefines
['TARGET'] = self
.BuildTargetList
[0]
933 GlobalData
.gGlobalDefines
['ARCH'] = self
.ArchList
[0]
934 if self
.ToolChainList
:
935 GlobalData
.gGlobalDefines
['TOOLCHAIN'] = self
.ToolChainList
[0]
936 GlobalData
.gGlobalDefines
['TOOL_CHAIN_TAG'] = self
.ToolChainList
[0]
937 if self
.ToolChainFamily
:
938 GlobalData
.gGlobalDefines
['FAMILY'] = self
.ToolChainFamily
[0]
939 if 'PREBUILD' in GlobalData
.gCommandLineDefines
:
940 self
.Prebuild
= GlobalData
.gCommandLineDefines
.get('PREBUILD')
943 Platform
= self
.Db
.MapPlatform(str(self
.PlatformFile
))
944 self
.Prebuild
= str(Platform
.Prebuild
)
948 # Evaluate all arguments and convert arguments that are WORKSPACE
949 # relative paths to absolute paths. Filter arguments that look like
950 # flags or do not follow the file/dir naming rules to avoid false
951 # positives on this conversion.
953 for Arg
in self
.Prebuild
.split():
955 # Do not modify Arg if it looks like a flag or an absolute file path
957 if Arg
.startswith('-') or os
.path
.isabs(Arg
):
958 PrebuildList
.append(Arg
)
961 # Do not modify Arg if it does not look like a Workspace relative
962 # path that starts with a valid package directory name
964 if not Arg
[0].isalpha() or os
.path
.dirname(Arg
) == '':
965 PrebuildList
.append(Arg
)
968 # If Arg looks like a WORKSPACE relative path, then convert to an
969 # absolute path and check to see if the file exists.
971 Temp
= mws
.join(self
.WorkspaceDir
, Arg
)
972 if os
.path
.isfile(Temp
):
974 PrebuildList
.append(Arg
)
975 self
.Prebuild
= ' '.join(PrebuildList
)
976 self
.Prebuild
+= self
.PassCommandOption(self
.BuildTargetList
, self
.ArchList
, self
.ToolChainList
, self
.PlatformFile
, self
.Target
)
978 def InitPostBuild(self
):
979 if 'POSTBUILD' in GlobalData
.gCommandLineDefines
:
980 self
.Postbuild
= GlobalData
.gCommandLineDefines
.get('POSTBUILD')
982 Platform
= self
.Db
.MapPlatform(str(self
.PlatformFile
))
983 self
.Postbuild
= str(Platform
.Postbuild
)
987 # Evaluate all arguments and convert arguments that are WORKSPACE
988 # relative paths to absolute paths. Filter arguments that look like
989 # flags or do not follow the file/dir naming rules to avoid false
990 # positives on this conversion.
992 for Arg
in self
.Postbuild
.split():
994 # Do not modify Arg if it looks like a flag or an absolute file path
996 if Arg
.startswith('-') or os
.path
.isabs(Arg
):
997 PostbuildList
.append(Arg
)
1000 # Do not modify Arg if it does not look like a Workspace relative
1001 # path that starts with a valid package directory name
1003 if not Arg
[0].isalpha() or os
.path
.dirname(Arg
) == '':
1004 PostbuildList
.append(Arg
)
1007 # If Arg looks like a WORKSPACE relative path, then convert to an
1008 # absolute path and check to see if the file exists.
1010 Temp
= mws
.join(self
.WorkspaceDir
, Arg
)
1011 if os
.path
.isfile(Temp
):
1013 PostbuildList
.append(Arg
)
1014 self
.Postbuild
= ' '.join(PostbuildList
)
1015 self
.Postbuild
+= self
.PassCommandOption(self
.BuildTargetList
, self
.ArchList
, self
.ToolChainList
, self
.PlatformFile
, self
.Target
)
1017 def PassCommandOption(self
, BuildTarget
, TargetArch
, ToolChain
, PlatformFile
, Target
):
1019 if GlobalData
.gCommand
and isinstance(GlobalData
.gCommand
, list):
1020 BuildStr
+= ' ' + ' '.join(GlobalData
.gCommand
)
1023 ToolChainFlag
= False
1024 PlatformFileFlag
= False
1026 if GlobalData
.gOptions
and not GlobalData
.gOptions
.BuildTarget
:
1028 if GlobalData
.gOptions
and not GlobalData
.gOptions
.TargetArch
:
1030 if GlobalData
.gOptions
and not GlobalData
.gOptions
.ToolChain
:
1031 ToolChainFlag
= True
1032 if GlobalData
.gOptions
and not GlobalData
.gOptions
.PlatformFile
:
1033 PlatformFileFlag
= True
1035 if TargetFlag
and BuildTarget
:
1036 if isinstance(BuildTarget
, list) or isinstance(BuildTarget
, tuple):
1037 BuildStr
+= ' -b ' + ' -b '.join(BuildTarget
)
1038 elif isinstance(BuildTarget
, str):
1039 BuildStr
+= ' -b ' + BuildTarget
1040 if ArchFlag
and TargetArch
:
1041 if isinstance(TargetArch
, list) or isinstance(TargetArch
, tuple):
1042 BuildStr
+= ' -a ' + ' -a '.join(TargetArch
)
1043 elif isinstance(TargetArch
, str):
1044 BuildStr
+= ' -a ' + TargetArch
1045 if ToolChainFlag
and ToolChain
:
1046 if isinstance(ToolChain
, list) or isinstance(ToolChain
, tuple):
1047 BuildStr
+= ' -t ' + ' -t '.join(ToolChain
)
1048 elif isinstance(ToolChain
, str):
1049 BuildStr
+= ' -t ' + ToolChain
1050 if PlatformFileFlag
and PlatformFile
:
1051 if isinstance(PlatformFile
, list) or isinstance(PlatformFile
, tuple):
1052 BuildStr
+= ' -p ' + ' -p '.join(PlatformFile
)
1053 elif isinstance(PlatformFile
, str):
1054 BuildStr
+= ' -p' + PlatformFile
1055 BuildStr
+= ' --conf=' + GlobalData
.gConfDirectory
1057 BuildStr
+= ' ' + Target
1061 def LaunchPrebuild(self
):
1063 EdkLogger
.info("\n- Prebuild Start -\n")
1064 self
.LaunchPrebuildFlag
= True
1066 # The purpose of .PrebuildEnv file is capture environment variable settings set by the prebuild script
1067 # and preserve them for the rest of the main build step, because the child process environment will
1068 # evaporate as soon as it exits, we cannot get it in build step.
1070 PrebuildEnvFile
= os
.path
.join(GlobalData
.gConfDirectory
, '.cache', '.PrebuildEnv')
1071 if os
.path
.isfile(PrebuildEnvFile
):
1072 os
.remove(PrebuildEnvFile
)
1073 if os
.path
.isfile(self
.PlatformBuildPath
):
1074 os
.remove(self
.PlatformBuildPath
)
1075 if sys
.platform
== "win32":
1076 args
= ' && '.join((self
.Prebuild
, 'set > ' + PrebuildEnvFile
))
1077 Process
= Popen(args
, stdout
=PIPE
, stderr
=PIPE
, shell
=True)
1079 args
= ' && '.join((self
.Prebuild
, 'env > ' + PrebuildEnvFile
))
1080 Process
= Popen(args
, stdout
=PIPE
, stderr
=PIPE
, shell
=True)
1082 # launch two threads to read the STDOUT and STDERR
1083 EndOfProcedure
= Event()
1084 EndOfProcedure
.clear()
1086 StdOutThread
= Thread(target
=ReadMessage
, args
=(Process
.stdout
, EdkLogger
.info
, EndOfProcedure
))
1087 StdOutThread
.setName("STDOUT-Redirector")
1088 StdOutThread
.setDaemon(False)
1089 StdOutThread
.start()
1092 StdErrThread
= Thread(target
=ReadMessage
, args
=(Process
.stderr
, EdkLogger
.quiet
, EndOfProcedure
))
1093 StdErrThread
.setName("STDERR-Redirector")
1094 StdErrThread
.setDaemon(False)
1095 StdErrThread
.start()
1096 # waiting for program exit
1103 if Process
.returncode
!= 0 :
1104 EdkLogger
.error("Prebuild", PREBUILD_ERROR
, 'Prebuild process is not success!')
1106 if os
.path
.exists(PrebuildEnvFile
):
1107 f
= open(PrebuildEnvFile
)
1108 envs
= f
.readlines()
1110 envs
= [l
.split("=", 1) for l
in envs
]
1111 envs
= [[I
.strip() for I
in item
] for item
in envs
if len(item
) == 2]
1112 os
.environ
.update(dict(envs
))
1113 EdkLogger
.info("\n- Prebuild Done -\n")
1115 def LaunchPostbuild(self
):
1117 EdkLogger
.info("\n- Postbuild Start -\n")
1118 if sys
.platform
== "win32":
1119 Process
= Popen(self
.Postbuild
, stdout
=PIPE
, stderr
=PIPE
, shell
=True)
1121 Process
= Popen(self
.Postbuild
, stdout
=PIPE
, stderr
=PIPE
, shell
=True)
1122 # launch two threads to read the STDOUT and STDERR
1123 EndOfProcedure
= Event()
1124 EndOfProcedure
.clear()
1126 StdOutThread
= Thread(target
=ReadMessage
, args
=(Process
.stdout
, EdkLogger
.info
, EndOfProcedure
))
1127 StdOutThread
.setName("STDOUT-Redirector")
1128 StdOutThread
.setDaemon(False)
1129 StdOutThread
.start()
1132 StdErrThread
= Thread(target
=ReadMessage
, args
=(Process
.stderr
, EdkLogger
.quiet
, EndOfProcedure
))
1133 StdErrThread
.setName("STDERR-Redirector")
1134 StdErrThread
.setDaemon(False)
1135 StdErrThread
.start()
1136 # waiting for program exit
1143 if Process
.returncode
!= 0 :
1144 EdkLogger
.error("Postbuild", POSTBUILD_ERROR
, 'Postbuild process is not success!')
1145 EdkLogger
.info("\n- Postbuild Done -\n")
1146 ## Build a module or platform
1148 # Create autogen code and makefile for a module or platform, and the launch
1149 # "make" command to build it
1151 # @param Target The target of build command
1152 # @param Platform The platform file
1153 # @param Module The module file
1154 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
1155 # @param ToolChain The name of toolchain to build
1156 # @param Arch The arch of the module/platform
1157 # @param CreateDepModuleCodeFile Flag used to indicate creating code
1158 # for dependent modules/Libraries
1159 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
1160 # for dependent modules/Libraries
1162 def _BuildPa(self
, Target
, AutoGenObject
, CreateDepsCodeFile
=True, CreateDepsMakeFile
=True, BuildModule
=False, FfsCommand
={}):
1163 if AutoGenObject
is None:
1166 # skip file generation for cleanxxx targets, run and fds target
1167 if Target
not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1168 # for target which must generate AutoGen code and makefile
1169 if not self
.SkipAutoGen
or Target
== 'genc':
1170 self
.Progress
.Start("Generating code")
1171 AutoGenObject
.CreateCodeFile(CreateDepsCodeFile
)
1172 self
.Progress
.Stop("done!")
1173 if Target
== "genc":
1176 if not self
.SkipAutoGen
or Target
== 'genmake':
1177 self
.Progress
.Start("Generating makefile")
1178 AutoGenObject
.CreateMakeFile(CreateDepsMakeFile
, FfsCommand
)
1179 self
.Progress
.Stop("done!")
1180 if Target
== "genmake":
1183 # always recreate top/platform makefile when clean, just in case of inconsistency
1184 AutoGenObject
.CreateCodeFile(False)
1185 AutoGenObject
.CreateMakeFile(False)
1187 if EdkLogger
.GetLevel() == EdkLogger
.QUIET
:
1188 EdkLogger
.quiet("Building ... %s" % repr(AutoGenObject
))
1190 BuildCommand
= AutoGenObject
.BuildCommand
1191 if BuildCommand
is None or len(BuildCommand
) == 0:
1192 EdkLogger
.error("build", OPTION_MISSING
,
1193 "No build command found for this module. "
1194 "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
1195 (AutoGenObject
.BuildTarget
, AutoGenObject
.ToolChain
, AutoGenObject
.Arch
),
1196 ExtraData
=str(AutoGenObject
))
1198 makefile
= GenMake
.BuildFile(AutoGenObject
)._FILE
_NAME
_[GenMake
.gMakeType
]
1202 RunDir
= os
.path
.normpath(os
.path
.join(AutoGenObject
.BuildDir
, GlobalData
.gGlobalDefines
['ARCH']))
1203 Command
= '.\SecMain'
1205 LaunchCommand(Command
, RunDir
)
1210 BuildCommand
= BuildCommand
+ [Target
]
1211 LaunchCommand(BuildCommand
, AutoGenObject
.MakeFileDir
)
1212 self
.CreateAsBuiltInf()
1216 if Target
== 'libraries':
1217 for Lib
in AutoGenObject
.LibraryBuildDirectoryList
:
1218 NewBuildCommand
= BuildCommand
+ ['-f', os
.path
.normpath(os
.path
.join(Lib
, makefile
)), 'pbuild']
1219 LaunchCommand(NewBuildCommand
, AutoGenObject
.MakeFileDir
)
1223 if Target
== 'modules':
1224 for Lib
in AutoGenObject
.LibraryBuildDirectoryList
:
1225 NewBuildCommand
= BuildCommand
+ ['-f', os
.path
.normpath(os
.path
.join(Lib
, makefile
)), 'pbuild']
1226 LaunchCommand(NewBuildCommand
, AutoGenObject
.MakeFileDir
)
1227 for Mod
in AutoGenObject
.ModuleBuildDirectoryList
:
1228 NewBuildCommand
= BuildCommand
+ ['-f', os
.path
.normpath(os
.path
.join(Mod
, makefile
)), 'pbuild']
1229 LaunchCommand(NewBuildCommand
, AutoGenObject
.MakeFileDir
)
1230 self
.CreateAsBuiltInf()
1234 if Target
== 'cleanlib':
1235 for Lib
in AutoGenObject
.LibraryBuildDirectoryList
:
1236 LibMakefile
= os
.path
.normpath(os
.path
.join(Lib
, makefile
))
1237 if os
.path
.exists(LibMakefile
):
1238 NewBuildCommand
= BuildCommand
+ ['-f', LibMakefile
, 'cleanall']
1239 LaunchCommand(NewBuildCommand
, AutoGenObject
.MakeFileDir
)
1243 if Target
== 'clean':
1244 for Mod
in AutoGenObject
.ModuleBuildDirectoryList
:
1245 ModMakefile
= os
.path
.normpath(os
.path
.join(Mod
, makefile
))
1246 if os
.path
.exists(ModMakefile
):
1247 NewBuildCommand
= BuildCommand
+ ['-f', ModMakefile
, 'cleanall']
1248 LaunchCommand(NewBuildCommand
, AutoGenObject
.MakeFileDir
)
1249 for Lib
in AutoGenObject
.LibraryBuildDirectoryList
:
1250 LibMakefile
= os
.path
.normpath(os
.path
.join(Lib
, makefile
))
1251 if os
.path
.exists(LibMakefile
):
1252 NewBuildCommand
= BuildCommand
+ ['-f', LibMakefile
, 'cleanall']
1253 LaunchCommand(NewBuildCommand
, AutoGenObject
.MakeFileDir
)
1257 if Target
== 'cleanall':
1259 #os.rmdir(AutoGenObject.BuildDir)
1260 RemoveDirectory(AutoGenObject
.BuildDir
, True)
1261 except WindowsError as X
:
1262 EdkLogger
.error("build", FILE_DELETE_FAILURE
, ExtraData
=str(X
))
1265 ## Build a module or platform
1267 # Create autogen code and makefile for a module or platform, and the launch
1268 # "make" command to build it
1270 # @param Target The target of build command
1271 # @param Platform The platform file
1272 # @param Module The module file
1273 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
1274 # @param ToolChain The name of toolchain to build
1275 # @param Arch The arch of the module/platform
1276 # @param CreateDepModuleCodeFile Flag used to indicate creating code
1277 # for dependent modules/Libraries
1278 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
1279 # for dependent modules/Libraries
1281 def _Build(self
, Target
, AutoGenObject
, CreateDepsCodeFile
=True, CreateDepsMakeFile
=True, BuildModule
=False):
1282 if AutoGenObject
is None:
1285 # skip file generation for cleanxxx targets, run and fds target
1286 if Target
not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1287 # for target which must generate AutoGen code and makefile
1288 if not self
.SkipAutoGen
or Target
== 'genc':
1289 self
.Progress
.Start("Generating code")
1290 AutoGenObject
.CreateCodeFile(CreateDepsCodeFile
)
1291 self
.Progress
.Stop("done!")
1292 if Target
== "genc":
1295 if not self
.SkipAutoGen
or Target
== 'genmake':
1296 self
.Progress
.Start("Generating makefile")
1297 AutoGenObject
.CreateMakeFile(CreateDepsMakeFile
)
1298 #AutoGenObject.CreateAsBuiltInf()
1299 self
.Progress
.Stop("done!")
1300 if Target
== "genmake":
1303 # always recreate top/platform makefile when clean, just in case of inconsistency
1304 AutoGenObject
.CreateCodeFile(False)
1305 AutoGenObject
.CreateMakeFile(False)
1307 if EdkLogger
.GetLevel() == EdkLogger
.QUIET
:
1308 EdkLogger
.quiet("Building ... %s" % repr(AutoGenObject
))
1310 BuildCommand
= AutoGenObject
.BuildCommand
1311 if BuildCommand
is None or len(BuildCommand
) == 0:
1312 EdkLogger
.error("build", OPTION_MISSING
,
1313 "No build command found for this module. "
1314 "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
1315 (AutoGenObject
.BuildTarget
, AutoGenObject
.ToolChain
, AutoGenObject
.Arch
),
1316 ExtraData
=str(AutoGenObject
))
1321 BuildCommand
= BuildCommand
+ [Target
]
1322 AutoGenObject
.BuildTime
= LaunchCommand(BuildCommand
, AutoGenObject
.MakeFileDir
)
1323 self
.CreateAsBuiltInf()
1328 if GenFdsApi(AutoGenObject
.GenFdsCommandDict
, self
.Db
):
1329 EdkLogger
.error("build", COMMAND_FAILURE
)
1334 RunDir
= os
.path
.normpath(os
.path
.join(AutoGenObject
.BuildDir
, GlobalData
.gGlobalDefines
['ARCH']))
1335 Command
= '.\SecMain'
1337 LaunchCommand(Command
, RunDir
)
1341 if Target
== 'libraries':
1348 if Target
== 'cleanall':
1350 #os.rmdir(AutoGenObject.BuildDir)
1351 RemoveDirectory(AutoGenObject
.BuildDir
, True)
1352 except WindowsError as X
:
1353 EdkLogger
.error("build", FILE_DELETE_FAILURE
, ExtraData
=str(X
))
1356 ## Rebase module image and Get function address for the input module list.
1358 def _RebaseModule (self
, MapBuffer
, BaseAddress
, ModuleList
, AddrIsOffset
= True, ModeIsSmm
= False):
1360 AddrIsOffset
= False
1361 for InfFile
in ModuleList
:
1362 sys
.stdout
.write (".")
1364 ModuleInfo
= ModuleList
[InfFile
]
1365 ModuleName
= ModuleInfo
.BaseName
1366 ModuleOutputImage
= ModuleInfo
.Image
.FileName
1367 ModuleDebugImage
= os
.path
.join(ModuleInfo
.DebugDir
, ModuleInfo
.BaseName
+ '.efi')
1368 ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1370 BaseAddress
= BaseAddress
- ModuleInfo
.Image
.Size
1372 # Update Image to new BaseAddress by GenFw tool
1374 LaunchCommand(["GenFw", "--rebase", str(BaseAddress
), "-r", ModuleOutputImage
], ModuleInfo
.OutputDir
)
1375 LaunchCommand(["GenFw", "--rebase", str(BaseAddress
), "-r", ModuleDebugImage
], ModuleInfo
.DebugDir
)
1378 # Set new address to the section header only for SMM driver.
1380 LaunchCommand(["GenFw", "--address", str(BaseAddress
), "-r", ModuleOutputImage
], ModuleInfo
.OutputDir
)
1381 LaunchCommand(["GenFw", "--address", str(BaseAddress
), "-r", ModuleDebugImage
], ModuleInfo
.DebugDir
)
1383 # Collect function address from Map file
1385 ImageMapTable
= ModuleOutputImage
.replace('.efi', '.map')
1387 if os
.path
.exists(ImageMapTable
):
1388 OrigImageBaseAddress
= 0
1389 ImageMap
= open(ImageMapTable
, 'r')
1390 for LinStr
in ImageMap
:
1391 if len (LinStr
.strip()) == 0:
1394 # Get the preferred address set on link time.
1396 if LinStr
.find ('Preferred load address is') != -1:
1397 StrList
= LinStr
.split()
1398 OrigImageBaseAddress
= int (StrList
[len(StrList
) - 1], 16)
1400 StrList
= LinStr
.split()
1401 if len (StrList
) > 4:
1402 if StrList
[3] == 'f' or StrList
[3] == 'F':
1404 RelativeAddress
= int (StrList
[2], 16) - OrigImageBaseAddress
1405 FunctionList
.append ((Name
, RelativeAddress
))
1409 # Add general information.
1412 MapBuffer
.append('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName
, BaseAddress
, BaseAddress
+ ModuleInfo
.Image
.EntryPoint
))
1414 MapBuffer
.append('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName
, 0 - BaseAddress
, 0 - (BaseAddress
+ ModuleInfo
.Image
.EntryPoint
)))
1416 MapBuffer
.append('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName
, BaseAddress
, BaseAddress
+ ModuleInfo
.Image
.EntryPoint
))
1418 # Add guid and general seciton section.
1420 TextSectionAddress
= 0
1421 DataSectionAddress
= 0
1422 for SectionHeader
in ModuleInfo
.Image
.SectionHeaderList
:
1423 if SectionHeader
[0] == '.text':
1424 TextSectionAddress
= SectionHeader
[1]
1425 elif SectionHeader
[0] in ['.data', '.sdata']:
1426 DataSectionAddress
= SectionHeader
[1]
1428 MapBuffer
.append('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo
.Guid
, 0 - (BaseAddress
+ TextSectionAddress
), 0 - (BaseAddress
+ DataSectionAddress
)))
1430 MapBuffer
.append('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo
.Guid
, BaseAddress
+ TextSectionAddress
, BaseAddress
+ DataSectionAddress
))
1432 # Add debug image full path.
1434 MapBuffer
.append('(IMAGE=%s)\n\n' % (ModuleDebugImage
))
1436 # Add function address
1438 for Function
in FunctionList
:
1440 MapBuffer
.append(' -0x%010X %s\n' % (0 - (BaseAddress
+ Function
[1]), Function
[0]))
1442 MapBuffer
.append(' 0x%010X %s\n' % (BaseAddress
+ Function
[1], Function
[0]))
1446 # for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1449 BaseAddress
= BaseAddress
+ ModuleInfo
.Image
.Size
1451 ## Collect MAP information of all FVs
1453 def _CollectFvMapBuffer (self
, MapBuffer
, Wa
, ModuleList
):
1455 # First get the XIP base address for FV map file.
1456 GuidPattern
= re
.compile("[-a-fA-F0-9]+")
1457 GuidName
= re
.compile("\(GUID=[-a-fA-F0-9]+")
1458 for FvName
in Wa
.FdfProfile
.FvDict
:
1459 FvMapBuffer
= os
.path
.join(Wa
.FvDir
, FvName
+ '.Fv.map')
1460 if not os
.path
.exists(FvMapBuffer
):
1462 FvMap
= open(FvMapBuffer
, 'r')
1463 #skip FV size information
1469 MatchGuid
= GuidPattern
.match(Line
)
1470 if MatchGuid
is not None:
1472 # Replace GUID with module name
1474 GuidString
= MatchGuid
.group()
1475 if GuidString
.upper() in ModuleList
:
1476 Line
= Line
.replace(GuidString
, ModuleList
[GuidString
.upper()].Name
)
1477 MapBuffer
.append(Line
)
1479 # Add the debug image full path.
1481 MatchGuid
= GuidName
.match(Line
)
1482 if MatchGuid
is not None:
1483 GuidString
= MatchGuid
.group().split("=")[1]
1484 if GuidString
.upper() in ModuleList
:
1485 MapBuffer
.append('(IMAGE=%s)\n' % (os
.path
.join(ModuleList
[GuidString
.upper()].DebugDir
, ModuleList
[GuidString
.upper()].Name
+ '.efi')))
1489 ## Collect MAP information of all modules
1491 def _CollectModuleMapBuffer (self
, MapBuffer
, ModuleList
):
1492 sys
.stdout
.write ("Generate Load Module At Fix Address Map")
1494 PatchEfiImageList
= []
1502 # reserve 4K size in SMRAM to make SMM module address not from 0.
1504 for ModuleGuid
in ModuleList
:
1505 Module
= ModuleList
[ModuleGuid
]
1506 GlobalData
.gProcessingFile
= "%s [%s, %s, %s]" % (Module
.MetaFile
, Module
.Arch
, Module
.ToolChain
, Module
.BuildTarget
)
1508 OutputImageFile
= ''
1509 for ResultFile
in Module
.CodaTargetList
:
1510 if str(ResultFile
.Target
).endswith('.efi'):
1512 # module list for PEI, DXE, RUNTIME and SMM
1514 OutputImageFile
= os
.path
.join(Module
.OutputDir
, Module
.Name
+ '.efi')
1515 ImageClass
= PeImageClass (OutputImageFile
)
1516 if not ImageClass
.IsValid
:
1517 EdkLogger
.error("build", FILE_PARSE_FAILURE
, ExtraData
=ImageClass
.ErrorInfo
)
1518 ImageInfo
= PeImageInfo(Module
.Name
, Module
.Guid
, Module
.Arch
, Module
.OutputDir
, Module
.DebugDir
, ImageClass
)
1519 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
]:
1520 PeiModuleList
[Module
.MetaFile
] = ImageInfo
1521 PeiSize
+= ImageInfo
.Image
.Size
1522 elif Module
.ModuleType
in [EDK_COMPONENT_TYPE_BS_DRIVER
, SUP_MODULE_DXE_DRIVER
, SUP_MODULE_UEFI_DRIVER
]:
1523 BtModuleList
[Module
.MetaFile
] = ImageInfo
1524 BtSize
+= ImageInfo
.Image
.Size
1525 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
]:
1526 RtModuleList
[Module
.MetaFile
] = ImageInfo
1527 RtSize
+= ImageInfo
.Image
.Size
1528 elif Module
.ModuleType
in [SUP_MODULE_SMM_CORE
, SUP_MODULE_DXE_SMM_DRIVER
, SUP_MODULE_MM_STANDALONE
, SUP_MODULE_MM_CORE_STANDALONE
]:
1529 SmmModuleList
[Module
.MetaFile
] = ImageInfo
1530 SmmSize
+= ImageInfo
.Image
.Size
1531 if Module
.ModuleType
== SUP_MODULE_DXE_SMM_DRIVER
:
1532 PiSpecVersion
= Module
.Module
.Specification
.get('PI_SPECIFICATION_VERSION', '0x00000000')
1533 # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.
1534 if int(PiSpecVersion
, 16) < 0x0001000A:
1535 BtModuleList
[Module
.MetaFile
] = ImageInfo
1536 BtSize
+= ImageInfo
.Image
.Size
1539 # EFI image is final target.
1540 # Check EFI image contains patchable FixAddress related PCDs.
1542 if OutputImageFile
!= '':
1543 ModuleIsPatch
= False
1544 for Pcd
in Module
.ModulePcdList
:
1545 if Pcd
.Type
== TAB_PCDS_PATCHABLE_IN_MODULE
and Pcd
.TokenCName
in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET
:
1546 ModuleIsPatch
= True
1548 if not ModuleIsPatch
:
1549 for Pcd
in Module
.LibraryPcdList
:
1550 if Pcd
.Type
== TAB_PCDS_PATCHABLE_IN_MODULE
and Pcd
.TokenCName
in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET
:
1551 ModuleIsPatch
= True
1554 if not ModuleIsPatch
:
1557 # Module includes the patchable load fix address PCDs.
1558 # It will be fixed up later.
1560 PatchEfiImageList
.append (OutputImageFile
)
1563 # Get Top Memory address
1565 ReservedRuntimeMemorySize
= 0
1566 TopMemoryAddress
= 0
1567 if self
.LoadFixAddress
== 0xFFFFFFFFFFFFFFFF:
1568 TopMemoryAddress
= 0
1570 TopMemoryAddress
= self
.LoadFixAddress
1571 if TopMemoryAddress
< RtSize
+ BtSize
+ PeiSize
:
1572 EdkLogger
.error("build", PARAMETER_INVALID
, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")
1575 # Patch FixAddress related PCDs into EFI image
1577 for EfiImage
in PatchEfiImageList
:
1578 EfiImageMap
= EfiImage
.replace('.efi', '.map')
1579 if not os
.path
.exists(EfiImageMap
):
1582 # Get PCD offset in EFI image by GenPatchPcdTable function
1584 PcdTable
= parsePcdInfoFromMapFile(EfiImageMap
, EfiImage
)
1586 # Patch real PCD value by PatchPcdValue tool
1588 for PcdInfo
in PcdTable
:
1590 if PcdInfo
[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE
:
1591 ReturnValue
, ErrorInfo
= PatchBinaryFile (EfiImage
, PcdInfo
[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE
, str (PeiSize
// 0x1000))
1592 elif PcdInfo
[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE
:
1593 ReturnValue
, ErrorInfo
= PatchBinaryFile (EfiImage
, PcdInfo
[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE
, str (BtSize
// 0x1000))
1594 elif PcdInfo
[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE
:
1595 ReturnValue
, ErrorInfo
= PatchBinaryFile (EfiImage
, PcdInfo
[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE
, str (RtSize
// 0x1000))
1596 elif PcdInfo
[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE
and len (SmmModuleList
) > 0:
1597 ReturnValue
, ErrorInfo
= PatchBinaryFile (EfiImage
, PcdInfo
[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE
, str (SmmSize
// 0x1000))
1598 if ReturnValue
!= 0:
1599 EdkLogger
.error("build", PARAMETER_INVALID
, "Patch PCD value failed", ExtraData
=ErrorInfo
)
1601 MapBuffer
.append('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize
// 0x1000))
1602 MapBuffer
.append('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize
// 0x1000))
1603 MapBuffer
.append('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize
// 0x1000))
1604 if len (SmmModuleList
) > 0:
1605 MapBuffer
.append('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize
// 0x1000))
1607 PeiBaseAddr
= TopMemoryAddress
- RtSize
- BtSize
1608 BtBaseAddr
= TopMemoryAddress
- RtSize
1609 RtBaseAddr
= TopMemoryAddress
- ReservedRuntimeMemorySize
1611 self
._RebaseModule
(MapBuffer
, PeiBaseAddr
, PeiModuleList
, TopMemoryAddress
== 0)
1612 self
._RebaseModule
(MapBuffer
, BtBaseAddr
, BtModuleList
, TopMemoryAddress
== 0)
1613 self
._RebaseModule
(MapBuffer
, RtBaseAddr
, RtModuleList
, TopMemoryAddress
== 0)
1614 self
._RebaseModule
(MapBuffer
, 0x1000, SmmModuleList
, AddrIsOffset
=False, ModeIsSmm
=True)
1615 MapBuffer
.append('\n\n')
1616 sys
.stdout
.write ("\n")
1619 ## Save platform Map file
1621 def _SaveMapFile (self
, MapBuffer
, Wa
):
1623 # Map file path is got.
1625 MapFilePath
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ '.map')
1627 # Save address map into MAP file.
1629 SaveFileOnChange(MapFilePath
, ''.join(MapBuffer
), False)
1630 if self
.LoadFixAddress
!= 0:
1631 sys
.stdout
.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath
))
1634 ## Build active platform for different build targets and different tool chains
1636 def _BuildPlatform(self
):
1637 SaveFileOnChange(self
.PlatformBuildPath
, '# DO NOT EDIT \n# FILE auto-generated\n', False)
1638 for BuildTarget
in self
.BuildTargetList
:
1639 GlobalData
.gGlobalDefines
['TARGET'] = BuildTarget
1641 for ToolChain
in self
.ToolChainList
:
1642 GlobalData
.gGlobalDefines
['TOOLCHAIN'] = ToolChain
1643 GlobalData
.gGlobalDefines
['TOOL_CHAIN_TAG'] = ToolChain
1644 GlobalData
.gGlobalDefines
['FAMILY'] = self
.ToolChainFamily
[index
]
1646 Wa
= WorkspaceAutoGen(
1663 self
.Fdf
= Wa
.FdfFile
1664 self
.LoadFixAddress
= Wa
.Platform
.LoadFixAddress
1665 self
.BuildReport
.AddPlatformReport(Wa
)
1666 self
.Progress
.Stop("done!")
1668 # Add ffs build to makefile
1670 if GlobalData
.gEnableGenfdsMultiThread
and self
.Fdf
:
1671 CmdListDict
= self
._GenFfsCmd
()
1673 for Arch
in Wa
.ArchList
:
1674 GlobalData
.gGlobalDefines
['ARCH'] = Arch
1675 Pa
= PlatformAutoGen(Wa
, self
.PlatformFile
, BuildTarget
, ToolChain
, Arch
)
1676 for Module
in Pa
.Platform
.Modules
:
1677 # Get ModuleAutoGen object to generate C code file and makefile
1678 Ma
= ModuleAutoGen(Wa
, Module
, BuildTarget
, ToolChain
, Arch
, self
.PlatformFile
)
1681 self
.BuildModules
.append(Ma
)
1682 self
._BuildPa
(self
.Target
, Pa
, FfsCommand
=CmdListDict
)
1684 # Create MAP file when Load Fix Address is enabled.
1685 if self
.Target
in ["", "all", "fds"]:
1686 for Arch
in Wa
.ArchList
:
1687 GlobalData
.gGlobalDefines
['ARCH'] = Arch
1689 # Check whether the set fix address is above 4G for 32bit image.
1691 if (Arch
== 'IA32' or Arch
== 'ARM') and self
.LoadFixAddress
!= 0xFFFFFFFFFFFFFFFF and self
.LoadFixAddress
>= 0x100000000:
1692 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")
1697 for Pa
in Wa
.AutoGenObjectList
:
1698 for Ma
in Pa
.ModuleAutoGenList
:
1701 if not Ma
.IsLibrary
:
1702 ModuleList
[Ma
.Guid
.upper()] = Ma
1705 if self
.LoadFixAddress
!= 0:
1707 # Rebase module to the preferred memory address before GenFds
1709 self
._CollectModuleMapBuffer
(MapBuffer
, ModuleList
)
1712 # create FDS again for the updated EFI image
1714 self
._Build
("fds", Wa
)
1716 # Create MAP file for all platform FVs after GenFds.
1718 self
._CollectFvMapBuffer
(MapBuffer
, Wa
, ModuleList
)
1720 # Save MAP buffer into MAP file.
1722 self
._SaveMapFile
(MapBuffer
, Wa
)
1724 ## Build active module for different build targets, different tool chains and different archs
1726 def _BuildModule(self
):
1727 for BuildTarget
in self
.BuildTargetList
:
1728 GlobalData
.gGlobalDefines
['TARGET'] = BuildTarget
1730 for ToolChain
in self
.ToolChainList
:
1731 WorkspaceAutoGenTime
= time
.time()
1732 GlobalData
.gGlobalDefines
['TOOLCHAIN'] = ToolChain
1733 GlobalData
.gGlobalDefines
['TOOL_CHAIN_TAG'] = ToolChain
1734 GlobalData
.gGlobalDefines
['FAMILY'] = self
.ToolChainFamily
[index
]
1737 # module build needs platform build information, so get platform
1740 Wa
= WorkspaceAutoGen(
1758 self
.Fdf
= Wa
.FdfFile
1759 self
.LoadFixAddress
= Wa
.Platform
.LoadFixAddress
1760 Wa
.CreateMakeFile(False)
1761 # Add ffs build to makefile
1763 if GlobalData
.gEnableGenfdsMultiThread
and self
.Fdf
:
1764 CmdListDict
= self
._GenFfsCmd
()
1765 self
.Progress
.Stop("done!")
1767 ExitFlag
= threading
.Event()
1769 self
.AutoGenTime
+= int(round((time
.time() - WorkspaceAutoGenTime
)))
1770 for Arch
in Wa
.ArchList
:
1771 AutoGenStart
= time
.time()
1772 GlobalData
.gGlobalDefines
['ARCH'] = Arch
1773 Pa
= PlatformAutoGen(Wa
, self
.PlatformFile
, BuildTarget
, ToolChain
, Arch
)
1774 for Module
in Pa
.Platform
.Modules
:
1775 if self
.ModuleFile
.Dir
== Module
.Dir
and self
.ModuleFile
.Name
== Module
.Name
:
1776 Ma
= ModuleAutoGen(Wa
, Module
, BuildTarget
, ToolChain
, Arch
, self
.PlatformFile
)
1777 if Ma
is None: continue
1779 if Ma
.CanSkipbyHash():
1780 self
.HashSkipModules
.append(Ma
)
1782 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
1783 if self
.Target
not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1784 # for target which must generate AutoGen code and makefile
1785 if not self
.SkipAutoGen
or self
.Target
== 'genc':
1786 self
.Progress
.Start("Generating code")
1787 Ma
.CreateCodeFile(True)
1788 self
.Progress
.Stop("done!")
1789 if self
.Target
== "genc":
1791 if not self
.SkipAutoGen
or self
.Target
== 'genmake':
1792 self
.Progress
.Start("Generating makefile")
1793 if CmdListDict
and self
.Fdf
and (Module
.File
, Arch
) in CmdListDict
:
1794 Ma
.CreateMakeFile(True, CmdListDict
[Module
.File
, Arch
])
1795 del CmdListDict
[Module
.File
, Arch
]
1797 Ma
.CreateMakeFile(True)
1798 self
.Progress
.Stop("done!")
1799 if self
.Target
== "genmake":
1801 self
.BuildModules
.append(Ma
)
1802 self
.AutoGenTime
+= int(round((time
.time() - AutoGenStart
)))
1803 MakeStart
= time
.time()
1804 for Ma
in self
.BuildModules
:
1805 if not Ma
.IsBinaryModule
:
1806 Bt
= BuildTask
.New(ModuleMakeUnit(Ma
, self
.Target
))
1807 # Break build if any build thread has error
1808 if BuildTask
.HasError():
1809 # we need a full version of makefile for platform
1811 BuildTask
.WaitForComplete()
1812 Pa
.CreateMakeFile(False)
1813 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1814 # Start task scheduler
1815 if not BuildTask
.IsOnGoing():
1816 BuildTask
.StartScheduler(self
.ThreadNumber
, ExitFlag
)
1818 # in case there's an interruption. we need a full version of makefile for platform
1819 Pa
.CreateMakeFile(False)
1820 if BuildTask
.HasError():
1821 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1822 self
.MakeTime
+= int(round((time
.time() - MakeStart
)))
1824 MakeContiue
= time
.time()
1826 BuildTask
.WaitForComplete()
1827 self
.CreateAsBuiltInf()
1828 self
.MakeTime
+= int(round((time
.time() - MakeContiue
)))
1829 if BuildTask
.HasError():
1830 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1832 self
.BuildReport
.AddPlatformReport(Wa
, MaList
)
1837 "Module for [%s] is not a component of active platform."\
1838 " Please make sure that the ARCH and inf file path are"\
1839 " given in the same as in [%s]" % \
1840 (', '.join(Wa
.ArchList
), self
.PlatformFile
),
1841 ExtraData
=self
.ModuleFile
1843 # Create MAP file when Load Fix Address is enabled.
1844 if self
.Target
== "fds" and self
.Fdf
:
1845 for Arch
in Wa
.ArchList
:
1847 # Check whether the set fix address is above 4G for 32bit image.
1849 if (Arch
== 'IA32' or Arch
== 'ARM') and self
.LoadFixAddress
!= 0xFFFFFFFFFFFFFFFF and self
.LoadFixAddress
>= 0x100000000:
1850 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")
1855 for Pa
in Wa
.AutoGenObjectList
:
1856 for Ma
in Pa
.ModuleAutoGenList
:
1859 if not Ma
.IsLibrary
:
1860 ModuleList
[Ma
.Guid
.upper()] = Ma
1863 if self
.LoadFixAddress
!= 0:
1865 # Rebase module to the preferred memory address before GenFds
1867 self
._CollectModuleMapBuffer
(MapBuffer
, ModuleList
)
1869 # create FDS again for the updated EFI image
1871 GenFdsStart
= time
.time()
1872 self
._Build
("fds", Wa
)
1873 self
.GenFdsTime
+= int(round((time
.time() - GenFdsStart
)))
1875 # Create MAP file for all platform FVs after GenFds.
1877 self
._CollectFvMapBuffer
(MapBuffer
, Wa
, ModuleList
)
1879 # Save MAP buffer into MAP file.
1881 self
._SaveMapFile
(MapBuffer
, Wa
)
1883 def _GenFfsCmd(self
):
1884 # convert dictionary of Cmd:(Inf,Arch)
1885 # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd...
1886 CmdSetDict
= defaultdict(set)
1887 GenFfsDict
= GenFds
.GenFfsMakefile('', GlobalData
.gFdfParser
, self
, self
.ArchList
, GlobalData
)
1888 for Cmd
in GenFfsDict
:
1889 tmpInf
, tmpArch
= GenFfsDict
[Cmd
]
1890 CmdSetDict
[tmpInf
, tmpArch
].add(Cmd
)
1893 ## Build a platform in multi-thread mode
1895 def _MultiThreadBuildPlatform(self
):
1896 SaveFileOnChange(self
.PlatformBuildPath
, '# DO NOT EDIT \n# FILE auto-generated\n', False)
1897 for BuildTarget
in self
.BuildTargetList
:
1898 GlobalData
.gGlobalDefines
['TARGET'] = BuildTarget
1900 for ToolChain
in self
.ToolChainList
:
1901 WorkspaceAutoGenTime
= time
.time()
1902 GlobalData
.gGlobalDefines
['TOOLCHAIN'] = ToolChain
1903 GlobalData
.gGlobalDefines
['TOOL_CHAIN_TAG'] = ToolChain
1904 GlobalData
.gGlobalDefines
['FAMILY'] = self
.ToolChainFamily
[index
]
1906 Wa
= WorkspaceAutoGen(
1923 self
.Fdf
= Wa
.FdfFile
1924 self
.LoadFixAddress
= Wa
.Platform
.LoadFixAddress
1925 self
.BuildReport
.AddPlatformReport(Wa
)
1926 Wa
.CreateMakeFile(False)
1928 # Add ffs build to makefile
1930 if GlobalData
.gEnableGenfdsMultiThread
and self
.Fdf
:
1931 CmdListDict
= self
._GenFfsCmd
()
1933 # multi-thread exit flag
1934 ExitFlag
= threading
.Event()
1936 self
.AutoGenTime
+= int(round((time
.time() - WorkspaceAutoGenTime
)))
1937 for Arch
in Wa
.ArchList
:
1938 AutoGenStart
= time
.time()
1939 GlobalData
.gGlobalDefines
['ARCH'] = Arch
1940 Pa
= PlatformAutoGen(Wa
, self
.PlatformFile
, BuildTarget
, ToolChain
, Arch
)
1944 for Inf
in Pa
.Platform
.Modules
:
1945 ModuleList
.append(Inf
)
1946 # Add the INF only list in FDF
1947 if GlobalData
.gFdfParser
is not None:
1948 for InfName
in GlobalData
.gFdfParser
.Profile
.InfList
:
1949 Inf
= PathClass(NormPath(InfName
), self
.WorkspaceDir
, Arch
)
1950 if Inf
in Pa
.Platform
.Modules
:
1952 ModuleList
.append(Inf
)
1953 for Module
in ModuleList
:
1954 # Get ModuleAutoGen object to generate C code file and makefile
1955 Ma
= ModuleAutoGen(Wa
, Module
, BuildTarget
, ToolChain
, Arch
, self
.PlatformFile
)
1959 if Ma
.CanSkipbyHash():
1960 self
.HashSkipModules
.append(Ma
)
1963 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
1964 if self
.Target
not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1965 # for target which must generate AutoGen code and makefile
1966 if not self
.SkipAutoGen
or self
.Target
== 'genc':
1967 Ma
.CreateCodeFile(True)
1968 if self
.Target
== "genc":
1971 if not self
.SkipAutoGen
or self
.Target
== 'genmake':
1972 if CmdListDict
and self
.Fdf
and (Module
.File
, Arch
) in CmdListDict
:
1973 Ma
.CreateMakeFile(True, CmdListDict
[Module
.File
, Arch
])
1974 del CmdListDict
[Module
.File
, Arch
]
1976 Ma
.CreateMakeFile(True)
1977 if self
.Target
== "genmake":
1979 self
.BuildModules
.append(Ma
)
1980 self
.Progress
.Stop("done!")
1981 self
.AutoGenTime
+= int(round((time
.time() - AutoGenStart
)))
1982 MakeStart
= time
.time()
1983 for Ma
in self
.BuildModules
:
1984 # Generate build task for the module
1985 if not Ma
.IsBinaryModule
:
1986 Bt
= BuildTask
.New(ModuleMakeUnit(Ma
, self
.Target
))
1987 # Break build if any build thread has error
1988 if BuildTask
.HasError():
1989 # we need a full version of makefile for platform
1991 BuildTask
.WaitForComplete()
1992 Pa
.CreateMakeFile(False)
1993 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1994 # Start task scheduler
1995 if not BuildTask
.IsOnGoing():
1996 BuildTask
.StartScheduler(self
.ThreadNumber
, ExitFlag
)
1998 # in case there's an interruption. we need a full version of makefile for platform
1999 Pa
.CreateMakeFile(False)
2000 if BuildTask
.HasError():
2001 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
2002 self
.MakeTime
+= int(round((time
.time() - MakeStart
)))
2004 MakeContiue
= time
.time()
2008 # All modules have been put in build tasks queue. Tell task scheduler
2009 # to exit if all tasks are completed
2012 BuildTask
.WaitForComplete()
2013 self
.CreateAsBuiltInf()
2014 self
.MakeTime
+= int(round((time
.time() - MakeContiue
)))
2016 # Check for build error, and raise exception if one
2017 # has been signaled.
2019 if BuildTask
.HasError():
2020 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
2022 # Create MAP file when Load Fix Address is enabled.
2023 if self
.Target
in ["", "all", "fds"]:
2024 for Arch
in Wa
.ArchList
:
2026 # Check whether the set fix address is above 4G for 32bit image.
2028 if (Arch
== 'IA32' or Arch
== 'ARM') and self
.LoadFixAddress
!= 0xFFFFFFFFFFFFFFFF and self
.LoadFixAddress
>= 0x100000000:
2029 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")
2034 for Pa
in Wa
.AutoGenObjectList
:
2035 for Ma
in Pa
.ModuleAutoGenList
:
2038 if not Ma
.IsLibrary
:
2039 ModuleList
[Ma
.Guid
.upper()] = Ma
2041 # Rebase module to the preferred memory address before GenFds
2044 if self
.LoadFixAddress
!= 0:
2045 self
._CollectModuleMapBuffer
(MapBuffer
, ModuleList
)
2049 # Generate FD image if there's a FDF file found
2051 GenFdsStart
= time
.time()
2052 if GenFdsApi(Wa
.GenFdsCommandDict
, self
.Db
):
2053 EdkLogger
.error("build", COMMAND_FAILURE
)
2056 # Create MAP file for all platform FVs after GenFds.
2058 self
._CollectFvMapBuffer
(MapBuffer
, Wa
, ModuleList
)
2059 self
.GenFdsTime
+= int(round((time
.time() - GenFdsStart
)))
2061 # Save MAP buffer into MAP file.
2063 self
._SaveMapFile
(MapBuffer
, Wa
)
2065 ## Generate GuidedSectionTools.txt in the FV directories.
2067 def CreateGuidedSectionToolsFile(self
):
2068 for BuildTarget
in self
.BuildTargetList
:
2069 for ToolChain
in self
.ToolChainList
:
2070 Wa
= WorkspaceAutoGen(
2087 if not os
.path
.exists(FvDir
):
2090 for Arch
in self
.ArchList
:
2091 # Build up the list of supported architectures for this build
2092 prefix
= '%s_%s_%s_' % (BuildTarget
, ToolChain
, Arch
)
2094 # Look through the tool definitions for GUIDed tools
2096 for (attrib
, value
) in self
.ToolDef
.ToolsDefTxtDictionary
.items():
2097 if attrib
.upper().endswith('_GUID'):
2098 split
= attrib
.split('_')
2099 thisPrefix
= '_'.join(split
[0:3]) + '_'
2100 if thisPrefix
== prefix
:
2101 guid
= self
.ToolDef
.ToolsDefTxtDictionary
[attrib
]
2104 path
= '_'.join(split
[0:4]) + '_PATH'
2105 path
= self
.ToolDef
.ToolsDefTxtDictionary
[path
]
2106 path
= self
.GetFullPathOfTool(path
)
2107 guidAttribs
.append((guid
, toolName
, path
))
2109 # Write out GuidedSecTools.txt
2110 toolsFile
= os
.path
.join(FvDir
, 'GuidedSectionTools.txt')
2111 toolsFile
= open(toolsFile
, 'wt')
2112 for guidedSectionTool
in guidAttribs
:
2113 print(' '.join(guidedSectionTool
), file=toolsFile
)
2116 ## Returns the full path of the tool.
2118 def GetFullPathOfTool (self
, tool
):
2119 if os
.path
.exists(tool
):
2120 return os
.path
.realpath(tool
)
2122 # We need to search for the tool using the
2123 # PATH environment variable.
2124 for dirInPath
in os
.environ
['PATH'].split(os
.pathsep
):
2125 foundPath
= os
.path
.join(dirInPath
, tool
)
2126 if os
.path
.exists(foundPath
):
2127 return os
.path
.realpath(foundPath
)
2129 # If the tool was not found in the path then we just return
2133 ## Launch the module or platform build
2136 if not self
.ModuleFile
:
2137 if not self
.SpawnMode
or self
.Target
not in ["", "all"]:
2138 self
.SpawnMode
= False
2139 self
._BuildPlatform
()
2141 self
._MultiThreadBuildPlatform
()
2142 self
.CreateGuidedSectionToolsFile()
2144 self
.SpawnMode
= False
2147 if self
.Target
== 'cleanall':
2148 RemoveDirectory(os
.path
.dirname(GlobalData
.gDatabasePath
), True)
2150 def CreateAsBuiltInf(self
):
2151 for Module
in self
.BuildModules
:
2152 Module
.CreateAsBuiltInf()
2153 for Module
in self
.HashSkipModules
:
2154 Module
.CreateAsBuiltInf(True)
2155 self
.BuildModules
= []
2156 self
.HashSkipModules
= []
2157 ## Do some clean-up works when error occurred
2158 def Relinquish(self
):
2159 OldLogLevel
= EdkLogger
.GetLevel()
2160 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
2161 Utils
.Progressor
.Abort()
2162 if self
.SpawnMode
== True:
2164 EdkLogger
.SetLevel(OldLogLevel
)
2166 def ParseDefines(DefineList
=[]):
2168 if DefineList
is not None:
2169 for Define
in DefineList
:
2170 DefineTokenList
= Define
.split("=", 1)
2171 if not GlobalData
.gMacroNamePattern
.match(DefineTokenList
[0]):
2172 EdkLogger
.error('build', FORMAT_INVALID
,
2173 "The macro name must be in the pattern [A-Z][A-Z0-9_]*",
2174 ExtraData
=DefineTokenList
[0])
2176 if len(DefineTokenList
) == 1:
2177 DefineDict
[DefineTokenList
[0]] = "TRUE"
2179 DefineDict
[DefineTokenList
[0]] = DefineTokenList
[1].strip()
2183 def SingleCheckCallback(option
, opt_str
, value
, parser
):
2184 if option
not in gParamCheck
:
2185 setattr(parser
.values
, option
.dest
, value
)
2186 gParamCheck
.append(option
)
2188 parser
.error("Option %s only allows one instance in command line!" % option
)
2190 def LogBuildTime(Time
):
2193 TimeDur
= time
.gmtime(Time
)
2194 if TimeDur
.tm_yday
> 1:
2195 TimeDurStr
= time
.strftime("%H:%M:%S", TimeDur
) + ", %d day(s)" % (TimeDur
.tm_yday
- 1)
2197 TimeDurStr
= time
.strftime("%H:%M:%S", TimeDur
)
2202 ## Parse command line options
2204 # Using standard Python module optparse to parse command line option of this tool.
2206 # @retval Opt A optparse.Values object containing the parsed options
2207 # @retval Args Target of build command
2209 def MyOptionParser():
2210 Parser
= OptionParser(description
=__copyright__
, version
=__version__
, prog
="build.exe", usage
="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")
2211 Parser
.add_option("-a", "--arch", action
="append", type="choice", choices
=['IA32', 'X64', 'EBC', 'ARM', 'AARCH64'], dest
="TargetArch",
2212 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.")
2213 Parser
.add_option("-p", "--platform", action
="callback", type="string", dest
="PlatformFile", callback
=SingleCheckCallback
,
2214 help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")
2215 Parser
.add_option("-m", "--module", action
="callback", type="string", dest
="ModuleFile", callback
=SingleCheckCallback
,
2216 help="Build the module specified by the INF file name argument.")
2217 Parser
.add_option("-b", "--buildtarget", type="string", dest
="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.",
2219 Parser
.add_option("-t", "--tagname", action
="append", type="string", dest
="ToolChain",
2220 help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")
2221 Parser
.add_option("-x", "--sku-id", action
="callback", type="string", dest
="SkuId", callback
=SingleCheckCallback
,
2222 help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")
2224 Parser
.add_option("-n", action
="callback", type="int", dest
="ThreadNumber", callback
=SingleCheckCallback
,
2225 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 "\
2226 "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.")
2228 Parser
.add_option("-f", "--fdf", action
="callback", type="string", dest
="FdfFile", callback
=SingleCheckCallback
,
2229 help="The name of the FDF file to use, which overrides the setting in the DSC file.")
2230 Parser
.add_option("-r", "--rom-image", action
="append", type="string", dest
="RomImage", default
=[],
2231 help="The name of FD to be generated. The name must be from [FD] section in FDF file.")
2232 Parser
.add_option("-i", "--fv-image", action
="append", type="string", dest
="FvImage", default
=[],
2233 help="The name of FV to be generated. The name must be from [FV] section in FDF file.")
2234 Parser
.add_option("-C", "--capsule-image", action
="append", type="string", dest
="CapName", default
=[],
2235 help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.")
2236 Parser
.add_option("-u", "--skip-autogen", action
="store_true", dest
="SkipAutoGen", help="Skip AutoGen step.")
2237 Parser
.add_option("-e", "--re-parse", action
="store_true", dest
="Reparse", help="Re-parse all meta-data files.")
2239 Parser
.add_option("-c", "--case-insensitive", action
="store_true", dest
="CaseInsensitive", default
=False, help="Don't check case of file name.")
2241 Parser
.add_option("-w", "--warning-as-error", action
="store_true", dest
="WarningAsError", help="Treat warning in tools as error.")
2242 Parser
.add_option("-j", "--log", action
="store", dest
="LogFile", help="Put log in specified file as well as on console.")
2244 Parser
.add_option("-s", "--silent", action
="store_true", type=None, dest
="SilentMode",
2245 help="Make use of silent mode of (n)make.")
2246 Parser
.add_option("-q", "--quiet", action
="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
2247 Parser
.add_option("-v", "--verbose", action
="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
2248 "including library instances selected, final dependency expression, "\
2249 "and warning messages, etc.")
2250 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
2251 Parser
.add_option("-D", "--define", action
="append", type="string", dest
="Macros", help="Macro: \"Name [= Value]\".")
2253 Parser
.add_option("-y", "--report-file", action
="store", dest
="ReportFile", help="Create/overwrite the report to the specified filename.")
2254 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
=[],
2255 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]. "\
2256 "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]")
2257 Parser
.add_option("-F", "--flag", action
="store", type="string", dest
="Flag",
2258 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. "\
2259 "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\
2260 "will override the setting in [BuildOptions] section of platform DSC.")
2261 Parser
.add_option("-N", "--no-cache", action
="store_true", dest
="DisableCache", default
=False, help="Disable build cache mechanism")
2262 Parser
.add_option("--conf", action
="store", type="string", dest
="ConfDirectory", help="Specify the customized Conf directory.")
2263 Parser
.add_option("--check-usage", action
="store_true", dest
="CheckUsage", default
=False, help="Check usage content of entries listed in INF file.")
2264 Parser
.add_option("--ignore-sources", action
="store_true", dest
="IgnoreSources", default
=False, help="Focus to a binary build and ignore all source files")
2265 Parser
.add_option("--pcd", action
="append", dest
="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")
2266 Parser
.add_option("-l", "--cmd-len", action
="store", type="int", dest
="CommandLength", help="Specify the maximum line length of build command. Default is 4096.")
2267 Parser
.add_option("--hash", action
="store_true", dest
="UseHashCache", default
=False, help="Enable hash-based caching during build process.")
2268 Parser
.add_option("--binary-destination", action
="store", type="string", dest
="BinCacheDest", help="Generate a cache of binary files in the specified directory.")
2269 Parser
.add_option("--binary-source", action
="store", type="string", dest
="BinCacheSource", help="Consume a cache of binary files from the specified directory.")
2270 Parser
.add_option("--genfds-multi-thread", action
="store_true", dest
="GenfdsMultiThread", default
=False, help="Enable GenFds multi thread to generate ffs file.")
2271 (Opt
, Args
) = Parser
.parse_args()
2274 ## Tool entrance method
2276 # This method mainly dispatch specific methods per the command line options.
2277 # If no error found, return zero value so the caller of this tool can know
2278 # if it's executed successfully or not.
2280 # @retval 0 Tool was successful
2281 # @retval 1 Tool failed
2284 StartTime
= time
.time()
2286 # Initialize log system
2287 EdkLogger
.Initialize()
2288 GlobalData
.gCommand
= sys
.argv
[1:]
2290 # Parse the options and args
2292 (Option
, Target
) = MyOptionParser()
2293 GlobalData
.gOptions
= Option
2294 GlobalData
.gCaseInsensitive
= Option
.CaseInsensitive
2297 if Option
.verbose
is not None:
2298 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
2299 elif Option
.quiet
is not None:
2300 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
2301 elif Option
.debug
is not None:
2302 EdkLogger
.SetLevel(Option
.debug
+ 1)
2304 EdkLogger
.SetLevel(EdkLogger
.INFO
)
2306 if Option
.LogFile
is not None:
2307 EdkLogger
.SetLogFile(Option
.LogFile
)
2309 if Option
.WarningAsError
== True:
2310 EdkLogger
.SetWarningAsError()
2312 if platform
.platform().find("Windows") >= 0:
2313 GlobalData
.gIsWindows
= True
2315 GlobalData
.gIsWindows
= False
2317 EdkLogger
.quiet("Build environment: %s" % platform
.platform())
2318 EdkLogger
.quiet(time
.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time
.localtime()));
2323 if len(Target
) == 0:
2325 elif len(Target
) >= 2:
2326 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "More than one targets are not supported.",
2327 ExtraData
="Please select one of: %s" % (' '.join(gSupportedTarget
)))
2329 Target
= Target
[0].lower()
2331 if Target
not in gSupportedTarget
:
2332 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "Not supported target [%s]." % Target
,
2333 ExtraData
="Please select one of: %s" % (' '.join(gSupportedTarget
)))
2336 # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
2339 GlobalData
.gCommandLineDefines
.update(ParseDefines(Option
.Macros
))
2341 Workspace
= os
.getenv("WORKSPACE")
2343 # Get files real name in workspace dir
2345 GlobalData
.gAllFiles
= Utils
.DirCache(Workspace
)
2347 WorkingDirectory
= os
.getcwd()
2348 if not Option
.ModuleFile
:
2349 FileList
= glob
.glob(os
.path
.normpath(os
.path
.join(WorkingDirectory
, '*.inf')))
2350 FileNum
= len(FileList
)
2352 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "There are %d INF files in %s." % (FileNum
, WorkingDirectory
),
2353 ExtraData
="Please use '-m <INF_FILE_PATH>' switch to choose one.")
2355 Option
.ModuleFile
= NormFile(FileList
[0], Workspace
)
2357 if Option
.ModuleFile
:
2358 if os
.path
.isabs (Option
.ModuleFile
):
2359 if os
.path
.normcase (os
.path
.normpath(Option
.ModuleFile
)).find (Workspace
) == 0:
2360 Option
.ModuleFile
= NormFile(os
.path
.normpath(Option
.ModuleFile
), Workspace
)
2361 Option
.ModuleFile
= PathClass(Option
.ModuleFile
, Workspace
)
2362 ErrorCode
, ErrorInfo
= Option
.ModuleFile
.Validate(".inf", False)
2364 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
2366 if Option
.PlatformFile
is not None:
2367 if os
.path
.isabs (Option
.PlatformFile
):
2368 if os
.path
.normcase (os
.path
.normpath(Option
.PlatformFile
)).find (Workspace
) == 0:
2369 Option
.PlatformFile
= NormFile(os
.path
.normpath(Option
.PlatformFile
), Workspace
)
2370 Option
.PlatformFile
= PathClass(Option
.PlatformFile
, Workspace
)
2372 if Option
.FdfFile
is not None:
2373 if os
.path
.isabs (Option
.FdfFile
):
2374 if os
.path
.normcase (os
.path
.normpath(Option
.FdfFile
)).find (Workspace
) == 0:
2375 Option
.FdfFile
= NormFile(os
.path
.normpath(Option
.FdfFile
), Workspace
)
2376 Option
.FdfFile
= PathClass(Option
.FdfFile
, Workspace
)
2377 ErrorCode
, ErrorInfo
= Option
.FdfFile
.Validate(".fdf", False)
2379 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
2381 if Option
.Flag
is not None and Option
.Flag
not in ['-c', '-s']:
2382 EdkLogger
.error("build", OPTION_VALUE_INVALID
, "UNI flag must be one of -c or -s")
2384 MyBuild
= Build(Target
, Workspace
, Option
)
2385 GlobalData
.gCommandLineDefines
['ARCH'] = ' '.join(MyBuild
.ArchList
)
2386 if not (MyBuild
.LaunchPrebuildFlag
and os
.path
.exists(MyBuild
.PlatformBuildPath
)):
2390 # All job done, no error found and no exception raised
2393 except FatalError
as X
:
2394 if MyBuild
is not None:
2395 # for multi-thread build exits safely
2396 MyBuild
.Relinquish()
2397 if Option
is not None and Option
.debug
is not None:
2398 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
2399 ReturnCode
= X
.args
[0]
2400 except Warning as X
:
2401 # error from Fdf parser
2402 if MyBuild
is not None:
2403 # for multi-thread build exits safely
2404 MyBuild
.Relinquish()
2405 if Option
is not None and Option
.debug
is not None:
2406 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
2408 EdkLogger
.error(X
.ToolName
, FORMAT_INVALID
, File
=X
.FileName
, Line
=X
.LineNumber
, ExtraData
=X
.Message
, RaiseError
=False)
2409 ReturnCode
= FORMAT_INVALID
2410 except KeyboardInterrupt:
2411 ReturnCode
= ABORT_ERROR
2412 if Option
is not None and Option
.debug
is not None:
2413 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
2415 if MyBuild
is not None:
2416 # for multi-thread build exits safely
2417 MyBuild
.Relinquish()
2419 # try to get the meta-file from the object causing exception
2420 Tb
= sys
.exc_info()[-1]
2421 MetaFile
= GlobalData
.gProcessingFile
2422 while Tb
is not None:
2423 if 'self' in Tb
.tb_frame
.f_locals
and hasattr(Tb
.tb_frame
.f_locals
['self'], 'MetaFile'):
2424 MetaFile
= Tb
.tb_frame
.f_locals
['self'].MetaFile
2429 "Unknown fatal error when processing [%s]" % MetaFile
,
2430 ExtraData
="\n(Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!)\n",
2433 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
2434 ReturnCode
= CODE_ERROR
2436 Utils
.Progressor
.Abort()
2437 Utils
.ClearDuplicatedInf()
2441 MyBuild
.LaunchPostbuild()
2444 Conclusion
= "Failed"
2445 elif ReturnCode
== ABORT_ERROR
:
2446 Conclusion
= "Aborted"
2448 Conclusion
= "Failed"
2449 FinishTime
= time
.time()
2450 BuildDuration
= time
.gmtime(int(round(FinishTime
- StartTime
)))
2451 BuildDurationStr
= ""
2452 if BuildDuration
.tm_yday
> 1:
2453 BuildDurationStr
= time
.strftime("%H:%M:%S", BuildDuration
) + ", %d day(s)" % (BuildDuration
.tm_yday
- 1)
2455 BuildDurationStr
= time
.strftime("%H:%M:%S", BuildDuration
)
2456 if MyBuild
is not None:
2458 MyBuild
.BuildReport
.GenerateReport(BuildDurationStr
, LogBuildTime(MyBuild
.AutoGenTime
), LogBuildTime(MyBuild
.MakeTime
), LogBuildTime(MyBuild
.GenFdsTime
))
2460 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
2461 EdkLogger
.quiet("\n- %s -" % Conclusion
)
2462 EdkLogger
.quiet(time
.strftime("Build end time: %H:%M:%S, %b.%d %Y", time
.localtime()))
2463 EdkLogger
.quiet("Build total time: %s\n" % BuildDurationStr
)
2466 if __name__
== '__main__':
2468 ## 0-127 is a safe return range, and 1 is a standard default error
2469 if r
< 0 or r
> 127: r
= 1