2 # build a platform or a module
4 # Copyright (c) 2007, 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.
26 from threading
import *
27 from optparse
import OptionParser
28 from subprocess
import *
29 from Common
import Misc
as Utils
31 from Common
.TargetTxtClassObject
import *
32 from Common
.ToolDefClassObject
import *
33 from Common
.DataType
import *
34 from AutoGen
.AutoGen
import *
35 from Common
.BuildToolError
import *
36 from Workspace
.WorkspaceDatabase
import *
38 import Common
.EdkLogger
39 import Common
.GlobalData
as GlobalData
41 # Version and Copyright
43 __version__
= "%prog Version " + VersionNumber
44 __copyright__
= "Copyright (c) 2007, Intel Corporation All rights reserved."
46 ## standard targets of build command
47 gSupportedTarget
= ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']
49 ## build configuration file
50 gBuildConfiguration
= "Conf/target.txt"
51 gBuildCacheDir
= "Conf/.cache"
52 gToolsDefinition
= "Conf/tools_def.txt"
54 ## Check environment PATH variable to make sure the specified tool is found
56 # If the tool is found in the PATH, then True is returned
57 # Otherwise, False is returned
59 def IsToolInPath(tool
):
60 if os
.environ
.has_key('PATHEXT'):
61 extns
= os
.environ
['PATHEXT'].split(os
.path
.pathsep
)
64 for pathDir
in os
.environ
['PATH'].split(os
.path
.pathsep
):
66 if os
.path
.exists(os
.path
.join(pathDir
, tool
+ ext
)):
70 ## Check environment variables
72 # Check environment variables that must be set for build. Currently they are
74 # WORKSPACE The directory all packages/platforms start from
75 # EDK_TOOLS_PATH The directory contains all tools needed by the build
76 # PATH $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH
78 # If any of above environment variable is not set or has error, the build
81 def CheckEnvVariable():
83 if "WORKSPACE" not in os
.environ
:
84 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
85 ExtraData
="WORKSPACE")
87 WorkspaceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["WORKSPACE"]))
88 if not os
.path
.exists(WorkspaceDir
):
89 EdkLogger
.error("build", FILE_NOT_FOUND
, "WORKSPACE doesn't exist", ExtraData
="%s" % WorkspaceDir
)
90 elif ' ' in WorkspaceDir
:
91 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in WORKSPACE path",
92 ExtraData
=WorkspaceDir
)
93 os
.environ
["WORKSPACE"] = WorkspaceDir
96 # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP
98 os
.environ
["ECP_SOURCE"] = os
.path
.join(WorkspaceDir
, GlobalData
.gEdkCompatibilityPkg
)
99 if "EFI_SOURCE" not in os
.environ
:
100 os
.environ
["EFI_SOURCE"] = os
.environ
["ECP_SOURCE"]
101 if "EDK_SOURCE" not in os
.environ
:
102 os
.environ
["EDK_SOURCE"] = os
.environ
["ECP_SOURCE"]
105 # Unify case of characters on case-insensitive systems
107 EfiSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["EFI_SOURCE"]))
108 EdkSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["EDK_SOURCE"]))
109 EcpSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["ECP_SOURCE"]))
111 os
.environ
["EFI_SOURCE"] = EfiSourceDir
112 os
.environ
["EDK_SOURCE"] = EdkSourceDir
113 os
.environ
["ECP_SOURCE"] = EcpSourceDir
114 os
.environ
["EDK_TOOLS_PATH"] = os
.path
.normcase(os
.environ
["EDK_TOOLS_PATH"])
116 if not os
.path
.exists(EcpSourceDir
):
117 EdkLogger
.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir
)
118 elif ' ' in EcpSourceDir
:
119 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in ECP_SOURCE path",
120 ExtraData
=EcpSourceDir
)
121 if not os
.path
.exists(EdkSourceDir
):
122 if EdkSourceDir
== EcpSourceDir
:
123 EdkLogger
.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir
)
125 EdkLogger
.error("build", PARAMETER_INVALID
, "EDK_SOURCE does not exist",
126 ExtraData
=EdkSourceDir
)
127 elif ' ' in EdkSourceDir
:
128 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in EDK_SOURCE path",
129 ExtraData
=EdkSourceDir
)
130 if not os
.path
.exists(EfiSourceDir
):
131 if EfiSourceDir
== EcpSourceDir
:
132 EdkLogger
.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir
)
134 EdkLogger
.error("build", PARAMETER_INVALID
, "EFI_SOURCE does not exist",
135 ExtraData
=EfiSourceDir
)
136 elif ' ' in EfiSourceDir
:
137 EdkLogger
.error("build", FORMAT_NOT_SUPPORTED
, "No space is allowed in EFI_SOURCE path",
138 ExtraData
=EfiSourceDir
)
140 # change absolute path to relative path to WORKSPACE
141 if EfiSourceDir
.upper().find(WorkspaceDir
.upper()) != 0:
142 EdkLogger
.error("build", PARAMETER_INVALID
, "EFI_SOURCE is not under WORKSPACE",
143 ExtraData
="WORKSPACE = %s\n EFI_SOURCE = %s" % (WorkspaceDir
, EfiSourceDir
))
144 if EdkSourceDir
.upper().find(WorkspaceDir
.upper()) != 0:
145 EdkLogger
.error("build", PARAMETER_INVALID
, "EDK_SOURCE is not under WORKSPACE",
146 ExtraData
="WORKSPACE = %s\n EDK_SOURCE = %s" % (WorkspaceDir
, EdkSourceDir
))
147 if EcpSourceDir
.upper().find(WorkspaceDir
.upper()) != 0:
148 EdkLogger
.error("build", PARAMETER_INVALID
, "ECP_SOURCE is not under WORKSPACE",
149 ExtraData
="WORKSPACE = %s\n ECP_SOURCE = %s" % (WorkspaceDir
, EcpSourceDir
))
151 # check EDK_TOOLS_PATH
152 if "EDK_TOOLS_PATH" not in os
.environ
:
153 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
154 ExtraData
="EDK_TOOLS_PATH")
157 if "PATH" not in os
.environ
:
158 EdkLogger
.error("build", ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
161 GlobalData
.gWorkspace
= WorkspaceDir
162 GlobalData
.gEfiSource
= EfiSourceDir
163 GlobalData
.gEdkSource
= EdkSourceDir
164 GlobalData
.gEcpSource
= EcpSourceDir
166 ## Get normalized file path
168 # Convert the path to be local format, and remove the WORKSPACE path at the
169 # beginning if the file path is given in full path.
171 # @param FilePath File path to be normalized
172 # @param Workspace Workspace path which the FilePath will be checked against
174 # @retval string The normalized file path
176 def NormFile(FilePath
, Workspace
):
177 # check if the path is absolute or relative
178 if os
.path
.isabs(FilePath
):
179 FileFullPath
= os
.path
.normpath(FilePath
)
181 FileFullPath
= os
.path
.normpath(os
.path
.join(Workspace
, FilePath
))
183 # check if the file path exists or not
184 if not os
.path
.isfile(FileFullPath
):
185 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath
)
187 # remove workspace directory from the beginning part of the file path
188 if Workspace
[-1] in ["\\", "/"]:
189 return FileFullPath
[len(Workspace
):]
191 return FileFullPath
[(len(Workspace
) + 1):]
193 ## Get the output of an external program
195 # This is the entrance method of thread reading output of an external program and
196 # putting them in STDOUT/STDERR of current program.
198 # @param From The stream message read from
199 # @param To The stream message put on
200 # @param ExitFlag The flag used to indicate stopping reading
202 def ReadMessage(From
, To
, ExitFlag
):
204 # read one line a time
205 Line
= From
.readline()
206 # empty string means "end"
207 if Line
!= None and Line
!= "":
214 ## Launch an external program
216 # This method will call subprocess.Popen to execute an external program with
217 # given options in specified directory. Because of the dead-lock issue during
218 # redirecting output of the external program, threads are used to to do the
221 # @param Command A list or string containing the call of the program
222 # @param WorkingDir The directory in which the program will be running
224 def LaunchCommand(Command
, WorkingDir
):
225 # if working directory doesn't exist, Popen() will raise an exception
226 if not os
.path
.isdir(WorkingDir
):
227 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=WorkingDir
)
230 EndOfProcedure
= None
233 Proc
= Popen(Command
, stdout
=PIPE
, stderr
=PIPE
, env
=os
.environ
, cwd
=WorkingDir
, bufsize
=-1)
235 # launch two threads to read the STDOUT and STDERR
236 EndOfProcedure
= Event()
237 EndOfProcedure
.clear()
239 StdOutThread
= Thread(target
=ReadMessage
, args
=(Proc
.stdout
, EdkLogger
.info
, EndOfProcedure
))
240 StdOutThread
.setName("STDOUT-Redirector")
241 StdOutThread
.setDaemon(False)
245 StdErrThread
= Thread(target
=ReadMessage
, args
=(Proc
.stderr
, EdkLogger
.quiet
, EndOfProcedure
))
246 StdErrThread
.setName("STDERR-Redirector")
247 StdErrThread
.setDaemon(False)
250 # waiting for program exit
252 except: # in case of aborting
253 # terminate the threads redirecting the program output
254 if EndOfProcedure
!= None:
257 if type(Command
) != type(""):
258 Command
= " ".join(Command
)
259 EdkLogger
.error("build", COMMAND_FAILURE
, "Failed to start command", ExtraData
="%s [%s]" % (Command
, WorkingDir
))
266 # check the return code of the program
267 if Proc
.returncode
!= 0:
268 if type(Command
) != type(""):
269 Command
= " ".join(Command
)
270 EdkLogger
.error("build", COMMAND_FAILURE
, ExtraData
="%s [%s]" % (Command
, WorkingDir
))
272 ## The smallest unit that can be built in multi-thread build mode
274 # This is the base class of build unit. The "Obj" parameter must provide
275 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units
278 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.
283 # @param self The object pointer
284 # @param Obj The object the build is working on
285 # @param Target The build target name, one of gSupportedTarget
286 # @param Dependency The BuildUnit(s) which must be completed in advance
287 # @param WorkingDir The directory build command starts in
289 def __init__(self
, Obj
, BuildCommand
, Target
, Dependency
, WorkingDir
="."):
290 self
.BuildObject
= Obj
291 self
.Dependency
= Dependency
292 self
.WorkingDir
= WorkingDir
294 self
.BuildCommand
= BuildCommand
295 if BuildCommand
== None or len(BuildCommand
) == 0:
296 EdkLogger
.error("build", OPTION_MISSING
, "No build command found for",
301 # It just returns the string representaion of self.BuildObject
303 # @param self The object pointer
306 return str(self
.BuildObject
)
308 ## "==" operator method
310 # It just compares self.BuildObject with "Other". So self.BuildObject must
311 # provide its own __eq__() method.
313 # @param self The object pointer
314 # @param Other The other BuildUnit object compared to
316 def __eq__(self
, Other
):
317 return Other
!= None and self
.BuildObject
== Other
.BuildObject \
318 and self
.BuildObject
.Arch
== Other
.BuildObject
.Arch
322 # It just returns the hash value of self.BuildObject which must be hashable.
324 # @param self The object pointer
327 return hash(self
.BuildObject
) + hash(self
.BuildObject
.Arch
)
330 return repr(self
.BuildObject
)
332 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode
334 # This class is for module build by nmake/make build system. The "Obj" parameter
335 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
336 # be make units missing build.
338 # Currently the "Obj" should be only ModuleAutoGen object.
340 class ModuleMakeUnit(BuildUnit
):
343 # @param self The object pointer
344 # @param Obj The ModuleAutoGen object the build is working on
345 # @param Target The build target name, one of gSupportedTarget
347 def __init__(self
, Obj
, Target
):
348 Dependency
= [ModuleMakeUnit(La
, Target
) for La
in Obj
.LibraryAutoGenList
]
349 BuildUnit
.__init
__(self
, Obj
, Obj
.BuildCommand
, Target
, Dependency
, Obj
.MakeFileDir
)
350 if Target
in [None, "", "all"]:
351 self
.Target
= "tbuild"
353 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode
355 # This class is for platform build by nmake/make build system. The "Obj" parameter
356 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
357 # be make units missing build.
359 # Currently the "Obj" should be only PlatformAutoGen object.
361 class PlatformMakeUnit(BuildUnit
):
364 # @param self The object pointer
365 # @param Obj The PlatformAutoGen object the build is working on
366 # @param Target The build target name, one of gSupportedTarget
368 def __init__(self
, Obj
, Target
):
369 Dependency
= [ModuleMakeUnit(Lib
, Target
) for Lib
in self
.BuildObject
.LibraryAutoGenList
]
370 Dependency
.extend([ModuleMakeUnit(Mod
, Target
) for Mod
in self
.BuildObject
.ModuleAutoGenList
])
371 BuildUnit
.__init
__(self
, Obj
, Obj
.BuildCommand
, Target
, Dependency
, Obj
.MakeFileDir
)
373 ## The class representing the task of a module build or platform build
375 # This class manages the build tasks in multi-thread build mode. Its jobs include
376 # scheduling thread running, catching thread error, monitor the thread status, etc.
379 # queue for tasks waiting for schedule
380 _PendingQueue
= sdict()
381 _PendingQueueLock
= threading
.Lock()
383 # queue for tasks ready for running
384 _ReadyQueue
= sdict()
385 _ReadyQueueLock
= threading
.Lock()
387 # queue for run tasks
388 _RunningQueue
= sdict()
389 _RunningQueueLock
= threading
.Lock()
391 # queue containing all build tasks, in case duplicate build
394 # flag indicating error occurs in a running thread
395 _ErrorFlag
= threading
.Event()
399 # BoundedSemaphore object used to control the number of running threads
402 # flag indicating if the scheduler is started or not
403 _SchedulerStopped
= threading
.Event()
404 _SchedulerStopped
.set()
406 ## Start the task scheduler thread
408 # @param MaxThreadNumber The maximum thread number
409 # @param ExitFlag Flag used to end the scheduler
412 def StartScheduler(MaxThreadNumber
, ExitFlag
):
413 SchedulerThread
= Thread(target
=BuildTask
.Scheduler
, args
=(MaxThreadNumber
, ExitFlag
))
414 SchedulerThread
.setName("Build-Task-Scheduler")
415 SchedulerThread
.setDaemon(False)
416 SchedulerThread
.start()
417 # wait for the scheduler to be started, especially useful in Linux
418 while not BuildTask
.IsOnGoing():
423 # @param MaxThreadNumber The maximum thread number
424 # @param ExitFlag Flag used to end the scheduler
427 def Scheduler(MaxThreadNumber
, ExitFlag
):
428 BuildTask
._SchedulerStopped
.clear()
430 # use BoundedSemaphore to control the maximum running threads
431 BuildTask
._Thread
= BoundedSemaphore(MaxThreadNumber
)
433 # scheduling loop, which will exits when no pending/ready task and
434 # indicated to do so, or there's error in running thread
436 while (len(BuildTask
._PendingQueue
) > 0 or len(BuildTask
._ReadyQueue
) > 0 \
437 or not ExitFlag
.isSet()) and not BuildTask
._ErrorFlag
.isSet():
438 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "Pending Queue (%d), Ready Queue (%d)"
439 % (len(BuildTask
._PendingQueue
), len(BuildTask
._ReadyQueue
)))
441 # get all pending tasks
442 BuildTask
._PendingQueueLock
.acquire()
443 BuildObjectList
= BuildTask
._PendingQueue
.keys()
445 # check if their dependency is resolved, and if true, move them
448 for BuildObject
in BuildObjectList
:
449 Bt
= BuildTask
._PendingQueue
[BuildObject
]
451 BuildTask
._ReadyQueue
[BuildObject
] = BuildTask
._PendingQueue
.pop(BuildObject
)
452 BuildTask
._PendingQueueLock
.release()
454 # launch build thread until the maximum number of threads is reached
455 while not BuildTask
._ErrorFlag
.isSet():
456 # empty ready queue, do nothing further
457 if len(BuildTask
._ReadyQueue
) == 0:
460 # wait for active thread(s) exit
461 BuildTask
._Thread
.acquire(True)
463 # start a new build thread
464 Bo
= BuildTask
._ReadyQueue
.keys()[0]
465 Bt
= BuildTask
._ReadyQueue
.pop(Bo
)
467 # move into running queue
468 BuildTask
._RunningQueueLock
.acquire()
469 BuildTask
._RunningQueue
[Bo
] = Bt
470 BuildTask
._RunningQueueLock
.release()
479 # wait for all running threads exit
480 if BuildTask
._ErrorFlag
.isSet():
481 EdkLogger
.quiet("\nWaiting for all build threads exit...")
482 # while not BuildTask._ErrorFlag.isSet() and \
483 while len(BuildTask
._RunningQueue
) > 0:
484 EdkLogger
.verbose("Waiting for thread ending...(%d)" % len(BuildTask
._RunningQueue
))
485 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "Threads [%s]" % ", ".join([Th
.getName() for Th
in threading
.enumerate()]))
488 except BaseException
, X
:
490 # TRICK: hide the output of threads left runing, so that the user can
491 # catch the error message easily
493 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
494 BuildTask
._ErrorFlag
.set()
495 BuildTask
._ErrorMessage
= "build thread scheduler error\n\t%s" % str(X
)
497 BuildTask
._PendingQueue
.clear()
498 BuildTask
._ReadyQueue
.clear()
499 BuildTask
._RunningQueue
.clear()
500 BuildTask
._TaskQueue
.clear()
501 BuildTask
._SchedulerStopped
.set()
503 ## Wait for all running method exit
506 def WaitForComplete():
507 BuildTask
._SchedulerStopped
.wait()
509 ## Check if the scheduler is running or not
513 return not BuildTask
._SchedulerStopped
.isSet()
518 if BuildTask
.IsOnGoing():
519 BuildTask
._ErrorFlag
.set()
520 BuildTask
.WaitForComplete()
522 ## Check if there's error in running thread
524 # Since the main thread cannot catch exceptions in other thread, we have to
525 # use threading.Event to communicate this formation to main thread.
529 return BuildTask
._ErrorFlag
.isSet()
531 ## Get error message in running thread
533 # Since the main thread cannot catch exceptions in other thread, we have to
534 # use a static variable to communicate this message to main thread.
537 def GetErrorMessage():
538 return BuildTask
._ErrorMessage
540 ## Factory method to create a BuildTask object
542 # This method will check if a module is building or has been built. And if
543 # true, just return the associated BuildTask object in the _TaskQueue. If
544 # not, create and return a new BuildTask object. The new BuildTask object
545 # will be appended to the _PendingQueue for scheduling later.
547 # @param BuildItem A BuildUnit object representing a build object
548 # @param Dependency The dependent build object of BuildItem
551 def New(BuildItem
, Dependency
=None):
552 if BuildItem
in BuildTask
._TaskQueue
:
553 Bt
= BuildTask
._TaskQueue
[BuildItem
]
557 Bt
._Init
(BuildItem
, Dependency
)
558 BuildTask
._TaskQueue
[BuildItem
] = Bt
560 BuildTask
._PendingQueueLock
.acquire()
561 BuildTask
._PendingQueue
[BuildItem
] = Bt
562 BuildTask
._PendingQueueLock
.release()
566 ## The real constructor of BuildTask
568 # @param BuildItem A BuildUnit object representing a build object
569 # @param Dependency The dependent build object of BuildItem
571 def _Init(self
, BuildItem
, Dependency
=None):
572 self
.BuildItem
= BuildItem
574 self
.DependencyList
= []
575 if Dependency
== None:
576 Dependency
= BuildItem
.Dependency
578 Dependency
.extend(BuildItem
.Dependency
)
579 self
.AddDependency(Dependency
)
580 # flag indicating build completes, used to avoid unnecessary re-build
581 self
.CompleteFlag
= False
583 ## Check if all dependent build tasks are completed or not
587 for Dep
in self
.DependencyList
:
588 if Dep
.CompleteFlag
== True:
595 ## Add dependent build task
597 # @param Dependency The list of dependent build objects
599 def AddDependency(self
, Dependency
):
600 for Dep
in Dependency
:
601 self
.DependencyList
.append(BuildTask
.New(Dep
)) # BuildTask list
603 ## The thread wrapper of LaunchCommand function
605 # @param Command A list or string contains the call of the command
606 # @param WorkingDir The directory in which the program will be running
608 def _CommandThread(self
, Command
, WorkingDir
):
610 LaunchCommand(Command
, WorkingDir
)
611 self
.CompleteFlag
= True
614 # TRICK: hide the output of threads left runing, so that the user can
615 # catch the error message easily
617 if not BuildTask
._ErrorFlag
.isSet():
618 GlobalData
.gBuildingModule
= "%s [%s, %s, %s]" % (str(self
.BuildItem
.BuildObject
),
619 self
.BuildItem
.BuildObject
.Arch
,
620 self
.BuildItem
.BuildObject
.ToolChain
,
621 self
.BuildItem
.BuildObject
.BuildTarget
623 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
624 BuildTask
._ErrorFlag
.set()
625 BuildTask
._ErrorMessage
= "%s broken\n %s [%s]" % \
626 (threading
.currentThread().getName(), Command
, WorkingDir
)
627 # indicate there's a thread is available for another build task
628 BuildTask
._RunningQueueLock
.acquire()
629 BuildTask
._RunningQueue
.pop(self
.BuildItem
)
630 BuildTask
._RunningQueueLock
.release()
631 BuildTask
._Thread
.release()
633 ## Start build task thread
636 EdkLogger
.quiet("Building ... %s" % repr(self
.BuildItem
))
637 Command
= self
.BuildItem
.BuildCommand
+ [self
.BuildItem
.Target
]
638 self
.BuildTread
= Thread(target
=self
._CommandThread
, args
=(Command
, self
.BuildItem
.WorkingDir
))
639 self
.BuildTread
.setName("build thread")
640 self
.BuildTread
.setDaemon(False)
641 self
.BuildTread
.start()
643 ## The class implementing the EDK2 build process
645 # The build process includes:
646 # 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf
647 # 2. Parse DSC file of active platform
648 # 3. Parse FDF file if any
649 # 4. Establish build database, including parse all other files (module, package)
650 # 5. Create AutoGen files (C code file, depex file, makefile) if necessary
651 # 6. Call build command
656 # Constructor will load all necessary configurations, parse platform, modules
657 # and packages and the establish a database for AutoGen.
659 # @param Target The build command target, one of gSupportedTarget
660 # @param WorkspaceDir The directory of workspace
661 # @param Platform The DSC file of active platform
662 # @param Module The INF file of active module, if any
663 # @param Arch The Arch list of platform or module
664 # @param ToolChain The name list of toolchain
665 # @param BuildTarget The "DEBUG" or "RELEASE" build
666 # @param FlashDefinition The FDF file of active platform
667 # @param FdList=[] The FD names to be individually built
668 # @param FvList=[] The FV names to be individually built
669 # @param MakefileType The type of makefile (for MSFT make or GNU make)
670 # @param SilentMode Indicate multi-thread build mode
671 # @param ThreadNumber The maximum number of thread if in multi-thread build mode
672 # @param SkipAutoGen Skip AutoGen step
673 # @param Reparse Re-parse all meta files
674 # @param SkuId SKU id from command line
676 def __init__(self
, Target
, WorkspaceDir
, Platform
, Module
, Arch
, ToolChain
,
677 BuildTarget
, FlashDefinition
, FdList
=[], FvList
=[],
678 MakefileType
="nmake", SilentMode
=False, ThreadNumber
=2,
679 SkipAutoGen
=False, Reparse
=False, SkuId
=None):
681 self
.WorkspaceDir
= WorkspaceDir
683 self
.PlatformFile
= Platform
684 self
.ModuleFile
= Module
686 self
.ToolChainList
= ToolChain
687 self
.BuildTargetList
= BuildTarget
688 self
.Fdf
= FlashDefinition
691 self
.MakefileType
= MakefileType
692 self
.SilentMode
= SilentMode
693 self
.ThreadNumber
= ThreadNumber
694 self
.SkipAutoGen
= SkipAutoGen
695 self
.Reparse
= Reparse
697 self
.SpawnMode
= True
699 self
.TargetTxt
= TargetTxtClassObject()
700 self
.ToolDef
= ToolDefClassObject()
701 self
.Db
= WorkspaceDatabase(None, GlobalData
.gGlobalDefines
, self
.Reparse
)
702 #self.Db = WorkspaceDatabase(None, {}, self.Reparse)
703 self
.BuildDatabase
= self
.Db
.BuildObject
706 # print dot charater during doing some time-consuming work
707 self
.Progress
= Utils
.Progressor()
709 # parse target.txt, tools_def.txt, and platform file
710 #self.RestoreBuildData()
711 self
.LoadConfiguration()
714 # print current build environment and configuration
715 EdkLogger
.quiet("%-24s = %s" % ("WORKSPACE", os
.environ
["WORKSPACE"]))
716 EdkLogger
.quiet("%-24s = %s" % ("ECP_SOURCE", os
.environ
["ECP_SOURCE"]))
717 EdkLogger
.quiet("%-24s = %s" % ("EDK_SOURCE", os
.environ
["EDK_SOURCE"]))
718 EdkLogger
.quiet("%-24s = %s" % ("EFI_SOURCE", os
.environ
["EFI_SOURCE"]))
719 EdkLogger
.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os
.environ
["EDK_TOOLS_PATH"]))
721 EdkLogger
.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self
.ArchList
)))
722 EdkLogger
.info('%-24s = %s' % ("TARGET", ' '.join(self
.BuildTargetList
)))
723 EdkLogger
.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self
.ToolChainList
)))
725 EdkLogger
.info('\n%-24s = %s' % ("Active Platform", self
.PlatformFile
))
727 if self
.Fdf
!= None and self
.Fdf
!= "":
728 EdkLogger
.info('%-24s = %s' % ("Flash Image Definition", self
.Fdf
))
730 if self
.ModuleFile
!= None and self
.ModuleFile
!= "":
731 EdkLogger
.info('%-24s = %s' % ("Active Module", self
.ModuleFile
))
733 os
.chdir(self
.WorkspaceDir
)
734 self
.Progress
.Start("\nProcessing meta-data")
736 ## Load configuration
738 # This method will parse target.txt and get the build configurations.
740 def LoadConfiguration(self
):
742 # Check target.txt and tools_def.txt and Init them
744 BuildConfigurationFile
= os
.path
.normpath(os
.path
.join(self
.WorkspaceDir
, gBuildConfiguration
))
745 if os
.path
.isfile(BuildConfigurationFile
) == True:
746 StatusCode
= self
.TargetTxt
.LoadTargetTxtFile(BuildConfigurationFile
)
748 ToolDefinitionFile
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_TOOL_CHAIN_CONF
]
749 if ToolDefinitionFile
== '':
750 ToolDefinitionFile
= gToolsDefinition
751 ToolDefinitionFile
= os
.path
.normpath(os
.path
.join(self
.WorkspaceDir
, ToolDefinitionFile
))
752 if os
.path
.isfile(ToolDefinitionFile
) == True:
753 StatusCode
= self
.ToolDef
.LoadToolDefFile(ToolDefinitionFile
)
755 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=ToolDefinitionFile
)
757 EdkLogger
.error("build", FILE_NOT_FOUND
, ExtraData
=BuildConfigurationFile
)
759 # if no ARCH given in command line, get it from target.txt
760 if self
.ArchList
== None or len(self
.ArchList
) == 0:
761 self
.ArchList
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_TARGET_ARCH
]
763 # if no build target given in command line, get it from target.txt
764 if self
.BuildTargetList
== None or len(self
.BuildTargetList
) == 0:
765 self
.BuildTargetList
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_TARGET
]
767 # if no tool chain given in command line, get it from target.txt
768 if self
.ToolChainList
== None or len(self
.ToolChainList
) == 0:
769 self
.ToolChainList
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_TOOL_CHAIN_TAG
]
770 if self
.ToolChainList
== None or len(self
.ToolChainList
) == 0:
771 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
, ExtraData
="No toolchain given. Don't know how to build.\n")
773 # check if the tool chains are defined or not
774 NewToolChainList
= []
775 for ToolChain
in self
.ToolChainList
:
776 if ToolChain
not in self
.ToolDef
.ToolsDefTxtDatabase
[TAB_TOD_DEFINES_TOOL_CHAIN_TAG
]:
777 EdkLogger
.warn("build", "Tool chain [%s] is not defined" % ToolChain
)
779 NewToolChainList
.append(ToolChain
)
780 # if no tool chain available, break the build
781 if len(NewToolChainList
) == 0:
782 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
,
783 ExtraData
="[%s] not defined. No toolchain available for build!\n" % ", ".join(self
.ToolChainList
))
785 self
.ToolChainList
= NewToolChainList
787 if self
.ThreadNumber
== None:
788 self
.ThreadNumber
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER
]
789 if self
.ThreadNumber
== '':
790 self
.ThreadNumber
= 0
792 self
.ThreadNumber
= int(self
.ThreadNumber
, 0)
794 if self
.ThreadNumber
== 0:
795 self
.ThreadNumber
= 1
797 if not self
.PlatformFile
:
798 PlatformFile
= self
.TargetTxt
.TargetTxtDictionary
[DataType
.TAB_TAT_DEFINES_ACTIVE_PLATFORM
]
800 # Try to find one in current directory
801 WorkingDirectory
= os
.getcwd()
802 FileList
= glob
.glob(os
.path
.normpath(os
.path
.join(WorkingDirectory
, '*.dsc')))
803 FileNum
= len(FileList
)
805 EdkLogger
.error("build", OPTION_MISSING
,
806 ExtraData
="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum
, WorkingDirectory
))
808 PlatformFile
= FileList
[0]
810 EdkLogger
.error("build", RESOURCE_NOT_AVAILABLE
,
811 ExtraData
="No active platform specified in target.txt or command line! Nothing can be built.\n")
813 self
.PlatformFile
= PathClass(NormFile(PlatformFile
, self
.WorkspaceDir
), self
.WorkspaceDir
)
814 ErrorCode
, ErrorInfo
= self
.PlatformFile
.Validate(".dsc", False)
816 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
818 ## Initialize build configuration
820 # This method will parse DSC file and merge the configurations from
821 # command line and target.txt, then get the final build configurations.
824 ErrorCode
, ErrorInfo
= self
.PlatformFile
.Validate(".dsc")
826 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
828 # create metafile database
829 self
.Db
.InitDatabase()
831 # we need information in platform description file to determine how to build
832 self
.Platform
= self
.BuildDatabase
[self
.PlatformFile
, 'COMMON']
834 self
.Fdf
= self
.Platform
.FlashDefinition
836 if self
.SkuId
== None or self
.SkuId
== '':
837 self
.SkuId
= self
.Platform
.SkuName
839 # check FD/FV build target
840 if self
.Fdf
== None or self
.Fdf
== "":
841 if self
.FdList
!= []:
842 EdkLogger
.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self
.FdList
))
844 if self
.FvList
!= []:
845 EdkLogger
.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self
.FvList
))
848 FdfParserObj
= FdfParser(str(self
.Fdf
))
849 FdfParserObj
.ParseFile()
850 for fvname
in self
.FvList
:
851 if fvname
.upper() not in FdfParserObj
.Profile
.FvDict
.keys():
852 EdkLogger
.error("build", OPTION_VALUE_INVALID
,
853 "No such an FV in FDF file: %s" % fvname
)
858 if self
.ArchList
== None or len(self
.ArchList
) == 0:
859 ArchList
= set(self
.Platform
.SupArchList
)
861 ArchList
= set(self
.ArchList
) & set(self
.Platform
.SupArchList
)
862 if len(ArchList
) == 0:
863 EdkLogger
.error("build", PARAMETER_INVALID
,
864 ExtraData
= "Active platform supports [%s] only, but [%s] is given."
865 % (" ".join(self
.Platform
.SupArchList
), " ".join(self
.ArchList
)))
866 elif len(ArchList
) != len(self
.ArchList
):
867 SkippedArchList
= set(self
.ArchList
).symmetric_difference(set(self
.Platform
.SupArchList
))
868 EdkLogger
.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"
869 % (" ".join(SkippedArchList
), " ".join(self
.Platform
.SupArchList
), " ".join(self
.ArchList
)))
870 self
.ArchList
= tuple(ArchList
)
873 if self
.BuildTargetList
== None or len(self
.BuildTargetList
) == 0:
874 BuildTargetList
= self
.Platform
.BuildTargets
876 BuildTargetList
= list(set(self
.BuildTargetList
) & set(self
.Platform
.BuildTargets
))
877 if BuildTargetList
== []:
878 EdkLogger
.error("build", PARAMETER_INVALID
, "Active platform only supports [%s], but [%s] is given"
879 % (" ".join(self
.Platform
.BuildTargets
), " ".join(self
.BuildTargetList
)))
880 self
.BuildTargetList
= BuildTargetList
882 ## Build a module or platform
884 # Create autogen code and makfile for a module or platform, and the launch
885 # "make" command to build it
887 # @param Target The target of build command
888 # @param Platform The platform file
889 # @param Module The module file
890 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
891 # @param ToolChain The name of toolchain to build
892 # @param Arch The arch of the module/platform
893 # @param CreateDepModuleCodeFile Flag used to indicate creating code
894 # for dependent modules/Libraries
895 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
896 # for dependent modules/Libraries
898 def _Build(self
, Target
, AutoGenObject
, CreateDepsCodeFile
=True, CreateDepsMakeFile
=True):
899 if AutoGenObject
== None:
902 # skip file generation for cleanxxx targets, run and fds target
903 if Target
not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
904 # for target which must generate AutoGen code and makefile
905 if not self
.SkipAutoGen
or Target
== 'genc':
906 self
.Progress
.Start("Generating code")
907 AutoGenObject
.CreateCodeFile(CreateDepsCodeFile
)
908 self
.Progress
.Stop("done!")
912 if not self
.SkipAutoGen
or Target
== 'genmake':
913 self
.Progress
.Start("Generating makefile")
914 AutoGenObject
.CreateMakeFile(CreateDepsMakeFile
)
915 self
.Progress
.Stop("done!")
916 if Target
== "genmake":
919 # always recreate top/platform makefile when clean, just in case of inconsistency
920 AutoGenObject
.CreateCodeFile(False)
921 AutoGenObject
.CreateMakeFile(False)
923 if EdkLogger
.GetLevel() == EdkLogger
.QUIET
:
924 EdkLogger
.quiet("Building ... %s" % repr(AutoGenObject
))
926 BuildCommand
= AutoGenObject
.BuildCommand
927 if BuildCommand
== None or len(BuildCommand
) == 0:
928 EdkLogger
.error("build", OPTION_MISSING
, ExtraData
="No MAKE command found for [%s, %s, %s]" % Key
)
930 BuildCommand
= BuildCommand
+ [Target
]
931 LaunchCommand(BuildCommand
, AutoGenObject
.MakeFileDir
)
932 if Target
== 'cleanall':
934 #os.rmdir(AutoGenObject.BuildDir)
935 RemoveDirectory(AutoGenObject
.BuildDir
, True)
936 except WindowsError, X
:
937 EdkLogger
.error("build", FILE_DELETE_FAILURE
, ExtraData
=str(X
))
940 ## Build active platform for different build targets and different tool chains
942 def _BuildPlatform(self
):
943 for BuildTarget
in self
.BuildTargetList
:
944 for ToolChain
in self
.ToolChainList
:
945 Wa
= WorkspaceAutoGen(
959 self
.Progress
.Stop("done!")
960 self
._Build
(self
.Target
, Wa
)
962 ## Build active module for different build targets, different tool chains and different archs
964 def _BuildModule(self
):
965 for BuildTarget
in self
.BuildTargetList
:
966 for ToolChain
in self
.ToolChainList
:
968 # module build needs platform build information, so get platform
971 Wa
= WorkspaceAutoGen(
985 Wa
.CreateMakeFile(False)
986 self
.Progress
.Stop("done!")
988 for Arch
in self
.ArchList
:
989 Ma
= ModuleAutoGen(Wa
, self
.ModuleFile
, BuildTarget
, ToolChain
, Arch
, self
.PlatformFile
)
990 if Ma
== None: continue
992 self
._Build
(self
.Target
, Ma
)
997 "Module for [%s] is not a component of active platform."\
998 " Please make sure that the ARCH and inf file path are"\
999 " given in the same as in [%s]" %\
1000 (', '.join(self
.ArchList
), self
.Platform
),
1001 ExtraData
=self
.ModuleFile
1004 ## Build a platform in multi-thread mode
1006 def _MultiThreadBuildPlatform(self
):
1007 for BuildTarget
in self
.BuildTargetList
:
1008 for ToolChain
in self
.ToolChainList
:
1009 Wa
= WorkspaceAutoGen(
1023 Wa
.CreateMakeFile(False)
1025 # multi-thread exit flag
1026 ExitFlag
= threading
.Event()
1028 for Arch
in self
.ArchList
:
1029 Pa
= PlatformAutoGen(Wa
, self
.PlatformFile
, BuildTarget
, ToolChain
, Arch
)
1032 for Module
in Pa
.Platform
.Modules
:
1033 # Get ModuleAutoGen object to generate C code file and makefile
1034 Ma
= ModuleAutoGen(Wa
, Module
, BuildTarget
, ToolChain
, Arch
, self
.PlatformFile
)
1037 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
1038 if self
.Target
not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1039 # for target which must generate AutoGen code and makefile
1040 if not self
.SkipAutoGen
or self
.Target
== 'genc':
1041 Ma
.CreateCodeFile(True)
1042 if self
.Target
== "genc":
1045 if not self
.SkipAutoGen
or self
.Target
== 'genmake':
1046 Ma
.CreateMakeFile(True)
1047 if self
.Target
== "genmake":
1049 self
.Progress
.Stop("done!")
1050 # Generate build task for the module
1051 Bt
= BuildTask
.New(ModuleMakeUnit(Ma
, self
.Target
))
1052 # Break build if any build thread has error
1053 if BuildTask
.HasError():
1054 # we need a full version of makefile for platform
1056 BuildTask
.WaitForComplete()
1057 Pa
.CreateMakeFile(False)
1058 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1059 # Start task scheduler
1060 if not BuildTask
.IsOnGoing():
1061 BuildTask
.StartScheduler(self
.ThreadNumber
, ExitFlag
)
1063 # in case there's an interruption. we need a full version of makefile for platform
1064 Pa
.CreateMakeFile(False)
1065 if BuildTask
.HasError():
1066 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1069 # All modules have been put in build tasks queue. Tell task scheduler
1070 # to exit if all tasks are completed
1073 BuildTask
.WaitForComplete()
1076 # Check for build error, and raise exception if one
1077 # has been signaled.
1079 if BuildTask
.HasError():
1080 EdkLogger
.error("build", BUILD_ERROR
, "Failed to build module", ExtraData
=GlobalData
.gBuildingModule
)
1082 # Generate FD image if there's a FDF file found
1083 if self
.Fdf
!= '' and self
.Target
in ["", "all", "fds"]:
1084 LaunchCommand(Wa
.BuildCommand
+ ["fds"], Wa
.MakeFileDir
)
1086 ## Generate GuidedSectionTools.txt in the FV directories.
1088 def CreateGuidedSectionToolsFile(self
):
1089 for Arch
in self
.ArchList
:
1090 for BuildTarget
in self
.BuildTargetList
:
1091 for ToolChain
in self
.ToolChainList
:
1092 FvDir
= os
.path
.join(
1094 self
.Platform
.OutputDirectory
,
1095 '_'.join((BuildTarget
, ToolChain
)),
1098 if not os
.path
.exists(FvDir
):
1100 # Build up the list of supported architectures for this build
1101 prefix
= '%s_%s_%s_' % (BuildTarget
, ToolChain
, Arch
)
1103 # Look through the tool definitions for GUIDed tools
1105 for (attrib
, value
) in self
.ToolDef
.ToolsDefTxtDictionary
.iteritems():
1106 if attrib
.upper().endswith('_GUID'):
1107 split
= attrib
.split('_')
1108 thisPrefix
= '_'.join(split
[0:3]) + '_'
1109 if thisPrefix
== prefix
:
1110 guid
= self
.ToolDef
.ToolsDefTxtDictionary
[attrib
]
1113 path
= '_'.join(split
[0:4]) + '_PATH'
1114 path
= self
.ToolDef
.ToolsDefTxtDictionary
[path
]
1115 path
= self
.GetFullPathOfTool(path
)
1116 guidAttribs
.append((guid
, toolName
, path
))
1118 # Write out GuidedSecTools.txt
1119 toolsFile
= os
.path
.join(FvDir
, 'GuidedSectionTools.txt')
1120 toolsFile
= open(toolsFile
, 'wt')
1121 for guidedSectionTool
in guidAttribs
:
1122 print >> toolsFile
, ' '.join(guidedSectionTool
)
1125 ## Returns the full path of the tool.
1127 def GetFullPathOfTool (self
, tool
):
1128 if os
.path
.exists(tool
):
1129 return os
.path
.realpath(tool
)
1131 # We need to search for the tool using the
1132 # PATH environment variable.
1133 for dirInPath
in os
.environ
['PATH'].split(os
.pathsep
):
1134 foundPath
= os
.path
.join(dirInPath
, tool
)
1135 if os
.path
.exists(foundPath
):
1136 return os
.path
.realpath(foundPath
)
1138 # If the tool was not found in the path then we just return
1142 ## Launch the module or platform build
1145 if self
.ModuleFile
== None or self
.ModuleFile
== "":
1146 if not self
.SpawnMode
or self
.Target
not in ["", "all"]:
1147 self
.SpawnMode
= False
1148 self
._BuildPlatform
()
1150 self
._MultiThreadBuildPlatform
()
1151 self
.CreateGuidedSectionToolsFile()
1153 self
.SpawnMode
= False
1156 ## Do some clean-up works when error occurred
1157 def Relinquish(self
):
1158 OldLogLevel
= EdkLogger
.GetLevel()
1159 EdkLogger
.SetLevel(EdkLogger
.ERROR
)
1160 #self.DumpBuildData()
1161 Utils
.Progressor
.Abort()
1162 if self
.SpawnMode
== True:
1164 EdkLogger
.SetLevel(OldLogLevel
)
1166 def DumpBuildData(self
):
1167 CacheDirectory
= os
.path
.join(self
.WorkspaceDir
, gBuildCacheDir
)
1168 Utils
.CreateDirectory(CacheDirectory
)
1169 Utils
.DataDump(Utils
.gFileTimeStampCache
, os
.path
.join(CacheDirectory
, "gFileTimeStampCache"))
1170 Utils
.DataDump(Utils
.gDependencyDatabase
, os
.path
.join(CacheDirectory
, "gDependencyDatabase"))
1172 def RestoreBuildData(self
):
1173 FilePath
= os
.path
.join(self
.WorkspaceDir
, gBuildCacheDir
, "gFileTimeStampCache")
1174 if Utils
.gFileTimeStampCache
== {} and os
.path
.isfile(FilePath
):
1175 Utils
.gFileTimeStampCache
= Utils
.DataRestore(FilePath
)
1176 if Utils
.gFileTimeStampCache
== None:
1177 Utils
.gFileTimeStampCache
= {}
1179 FilePath
= os
.path
.join(self
.WorkspaceDir
, gBuildCacheDir
, "gDependencyDatabase")
1180 if Utils
.gDependencyDatabase
== {} and os
.path
.isfile(FilePath
):
1181 Utils
.gDependencyDatabase
= Utils
.DataRestore(FilePath
)
1182 if Utils
.gDependencyDatabase
== None:
1183 Utils
.gDependencyDatabase
= {}
1185 def ParseDefines(DefineList
=[]):
1187 if DefineList
!= None:
1188 for Define
in DefineList
:
1189 DefineTokenList
= Define
.split("=", 1)
1190 if len(DefineTokenList
) == 1:
1191 DefineDict
[DefineTokenList
[0]] = ""
1193 DefineDict
[DefineTokenList
[0]] = DefineTokenList
[1].strip()
1197 def SingleCheckCallback(option
, opt_str
, value
, parser
):
1198 if option
not in gParamCheck
:
1199 setattr(parser
.values
, option
.dest
, value
)
1200 gParamCheck
.append(option
)
1202 parser
.error("Option %s only allows one instance in command line!" % option
)
1204 ## Parse command line options
1206 # Using standard Python module optparse to parse command line option of this tool.
1208 # @retval Opt A optparse.Values object containing the parsed options
1209 # @retval Args Target of build command
1211 def MyOptionParser():
1212 Parser
= OptionParser(description
=__copyright__
,version
=__version__
,prog
="build.exe",usage
="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")
1213 Parser
.add_option("-a", "--arch", action
="append", type="choice", choices
=['IA32','X64','IPF','EBC','ARM'], dest
="TargetArch",
1214 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.")
1215 Parser
.add_option("-p", "--platform", action
="callback", type="string", dest
="PlatformFile", callback
=SingleCheckCallback
,
1216 help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")
1217 Parser
.add_option("-m", "--module", action
="callback", type="string", dest
="ModuleFile", callback
=SingleCheckCallback
,
1218 help="Build the module specified by the INF file name argument.")
1219 Parser
.add_option("-b", "--buildtarget", action
="append", type="choice", choices
=['DEBUG','RELEASE'], dest
="BuildTarget",
1220 help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")
1221 Parser
.add_option("-t", "--tagname", action
="append", type="string", dest
="ToolChain",
1222 help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")
1223 Parser
.add_option("-x", "--sku-id", action
="callback", type="string", dest
="SkuId", callback
=SingleCheckCallback
,
1224 help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")
1226 Parser
.add_option("-n", action
="callback", type="int", dest
="ThreadNumber", callback
=SingleCheckCallback
,
1227 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.")
1229 Parser
.add_option("-f", "--fdf", action
="callback", type="string", dest
="FdfFile", callback
=SingleCheckCallback
,
1230 help="The name of the FDF file to use, which overrides the setting in the DSC file.")
1231 Parser
.add_option("-r", "--rom-image", action
="append", type="string", dest
="RomImage", default
=[],
1232 help="The name of FD to be generated. The name must be from [FD] section in FDF file.")
1233 Parser
.add_option("-i", "--fv-image", action
="append", type="string", dest
="FvImage", default
=[],
1234 help="The name of FV to be generated. The name must be from [FV] section in FDF file.")
1236 Parser
.add_option("-u", "--skip-autogen", action
="store_true", dest
="SkipAutoGen", help="Skip AutoGen step.")
1237 Parser
.add_option("-e", "--re-parse", action
="store_true", dest
="Reparse", help="Re-parse all meta-data files.")
1239 Parser
.add_option("-c", "--case-insensitive", action
="store_true", dest
="CaseInsensitive", help="Don't check case of file name.")
1241 # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",
1242 # help="Define global macro which can be used in DSC/DEC/INF files.")
1244 Parser
.add_option("-w", "--warning-as-error", action
="store_true", dest
="WarningAsError", help="Treat warning in tools as error.")
1245 Parser
.add_option("-j", "--log", action
="store", dest
="LogFile", help="Put log in specified file as well as on console.")
1247 Parser
.add_option("-s", "--silent", action
="store_true", type=None, dest
="SilentMode",
1248 help="Make use of silent mode of (n)make.")
1249 Parser
.add_option("-q", "--quiet", action
="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
1250 Parser
.add_option("-v", "--verbose", action
="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
1251 "including library instances selected, final dependency expression, "\
1252 "and warning messages, etc.")
1253 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
1254 Parser
.add_option("-D", "--define", action
="append", type="string", dest
="Macros", help="Macro: \"Name [= Value]\".")
1256 (Opt
, Args
)=Parser
.parse_args()
1259 ## Tool entrance method
1261 # This method mainly dispatch specific methods per the command line options.
1262 # If no error found, return zero value so the caller of this tool can know
1263 # if it's executed successfully or not.
1265 # @retval 0 Tool was successful
1266 # @retval 1 Tool failed
1269 StartTime
= time
.time()
1271 # Initialize log system
1272 EdkLogger
.Initialize()
1275 # Parse the options and args
1277 (Option
, Target
) = MyOptionParser()
1278 GlobalData
.gOptions
= Option
1279 GlobalData
.gCaseInsensitive
= Option
.CaseInsensitive
1282 if Option
.verbose
!= None:
1283 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
1284 elif Option
.quiet
!= None:
1285 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
1286 elif Option
.debug
!= None:
1287 EdkLogger
.SetLevel(Option
.debug
+ 1)
1289 EdkLogger
.SetLevel(EdkLogger
.INFO
)
1291 if Option
.LogFile
!= None:
1292 EdkLogger
.SetLogFile(Option
.LogFile
)
1294 if Option
.WarningAsError
== True:
1295 EdkLogger
.SetWarningAsError()
1297 if platform
.platform().find("Windows") >= 0:
1298 GlobalData
.gIsWindows
= True
1300 GlobalData
.gIsWindows
= False
1302 EdkLogger
.quiet(time
.strftime("%H:%M:%S, %b.%d %Y ", time
.localtime()) + "[%s]\n" % platform
.platform())
1306 if len(Target
) == 0:
1308 elif len(Target
) >= 2:
1309 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "More than one targets are not supported.",
1310 ExtraData
="Please select one of: %s" %(' '.join(gSupportedTarget
)))
1312 Target
= Target
[0].lower()
1314 if Target
not in gSupportedTarget
:
1315 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "Not supported target [%s]." % Target
,
1316 ExtraData
="Please select one of: %s" %(' '.join(gSupportedTarget
)))
1318 GlobalData
.gGlobalDefines
= ParseDefines(Option
.Macros
)
1320 # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
1323 Workspace
= os
.getenv("WORKSPACE")
1325 # Get files real name in workspace dir
1327 GlobalData
.gAllFiles
= Utils
.DirCache(Workspace
)
1329 WorkingDirectory
= os
.getcwd()
1330 if not Option
.ModuleFile
:
1331 FileList
= glob
.glob(os
.path
.normpath(os
.path
.join(WorkingDirectory
, '*.inf')))
1332 FileNum
= len(FileList
)
1334 EdkLogger
.error("build", OPTION_NOT_SUPPORTED
, "There are %d INF files in %s." % (FileNum
, WorkingDirectory
),
1335 ExtraData
="Please use '-m <INF_FILE_PATH>' switch to choose one.")
1337 Option
.ModuleFile
= NormFile(FileList
[0], Workspace
)
1339 if Option
.ModuleFile
:
1340 Option
.ModuleFile
= PathClass(Option
.ModuleFile
, Workspace
)
1341 ErrorCode
, ErrorInfo
= Option
.ModuleFile
.Validate(".inf", False)
1343 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
1345 if Option
.PlatformFile
!= None:
1346 Option
.PlatformFile
= PathClass(Option
.PlatformFile
, Workspace
)
1347 ErrorCode
, ErrorInfo
= Option
.PlatformFile
.Validate(".dsc", False)
1349 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
1351 if Option
.FdfFile
!= None:
1352 Option
.FdfFile
= PathClass(Option
.FdfFile
, Workspace
)
1353 ErrorCode
, ErrorInfo
= Option
.FdfFile
.Validate(".fdf", False)
1355 EdkLogger
.error("build", ErrorCode
, ExtraData
=ErrorInfo
)
1357 MyBuild
= Build(Target
, Workspace
, Option
.PlatformFile
, Option
.ModuleFile
,
1358 Option
.TargetArch
, Option
.ToolChain
, Option
.BuildTarget
,
1359 Option
.FdfFile
, Option
.RomImage
, Option
.FvImage
,
1360 None, Option
.SilentMode
, Option
.ThreadNumber
,
1361 Option
.SkipAutoGen
, Option
.Reparse
, Option
.SkuId
)
1363 #MyBuild.DumpBuildData()
1364 except FatalError
, X
:
1366 # for multi-thread build exits safely
1367 MyBuild
.Relinquish()
1368 if Option
!= None and Option
.debug
!= None:
1369 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
1370 ReturnCode
= X
.args
[0]
1372 # error from Fdf parser
1374 # for multi-thread build exits safely
1375 MyBuild
.Relinquish()
1376 if Option
!= None and Option
.debug
!= None:
1377 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
1379 EdkLogger
.error(X
.ToolName
, FORMAT_INVALID
, File
=X
.FileName
, Line
=X
.LineNumber
, ExtraData
=X
.Message
, RaiseError
= False)
1380 ReturnCode
= FORMAT_INVALID
1381 except KeyboardInterrupt:
1382 ReturnCode
= ABORT_ERROR
1383 if Option
!= None and Option
.debug
!= None:
1384 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
1387 # for multi-thread build exits safely
1388 MyBuild
.Relinquish()
1390 # try to get the meta-file from the object causing exception
1391 Tb
= sys
.exc_info()[-1]
1392 MetaFile
= GlobalData
.gProcessingFile
1394 if 'self' in Tb
.tb_frame
.f_locals
and hasattr(Tb
.tb_frame
.f_locals
['self'], 'MetaFile'):
1395 MetaFile
= Tb
.tb_frame
.f_locals
['self'].MetaFile
1400 "Unknown fatal error when processing [%s]" % MetaFile
,
1401 ExtraData
="\n(Please send email to dev@buildtools.tianocore.org for help, attaching following call stack trace!)\n",
1404 EdkLogger
.quiet("(Python %s on %s) " % (platform
.python_version(), sys
.platform
) + traceback
.format_exc())
1405 ReturnCode
= CODE_ERROR
1407 Utils
.Progressor
.Abort()
1414 elif ReturnCode
== ABORT_ERROR
:
1415 Conclusion
= "Aborted"
1417 Conclusion
= "Failed"
1418 FinishTime
= time
.time()
1419 BuildDuration
= time
.strftime("%M:%S", time
.gmtime(int(round(FinishTime
- StartTime
))))
1420 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
1421 EdkLogger
.quiet("\n- %s -\n%s [%s]" % (Conclusion
, time
.strftime("%H:%M:%S, %b.%d %Y", time
.localtime()), BuildDuration
))
1425 if __name__
== '__main__':
1427 ## 0-127 is a safe return range, and 1 is a standard default error
1428 if r
< 0 or r
> 127: r
= 1