]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/build/build.py
Change the GUID value of FVB to be that of FVB2 in PI 1.2 spec. this will force FVB...
[mirror_edk2.git] / BaseTools / Source / Python / build / build.py
CommitLineData
30fdf114
LG
1## @file
2# build a platform or a module
3#
4# Copyright (c) 2007, Intel Corporation
5#
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
10#
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.
13#
14
15##
16# Import Modules
17#
18import os
19import re
20import sys
21import glob
22import time
23import platform
24import traceback
25
26from threading import *
27from optparse import OptionParser
28from subprocess import *
29from Common import Misc as Utils
30
31from Common.TargetTxtClassObject import *
32from Common.ToolDefClassObject import *
33from Common.DataType import *
34from AutoGen.AutoGen import *
35from Common.BuildToolError import *
36from Workspace.WorkspaceDatabase import *
37
38import Common.EdkLogger
39import Common.GlobalData as GlobalData
40
41# Version and Copyright
42VersionNumber = "0.5"
43__version__ = "%prog Version " + VersionNumber
44__copyright__ = "Copyright (c) 2007, Intel Corporation All rights reserved."
45
46## standard targets of build command
47gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']
48
49## build configuration file
50gBuildConfiguration = "Conf/target.txt"
51gBuildCacheDir = "Conf/.cache"
52gToolsDefinition = "Conf/tools_def.txt"
53
54## Check environment PATH variable to make sure the specified tool is found
55#
56# If the tool is found in the PATH, then True is returned
57# Otherwise, False is returned
58#
59def IsToolInPath(tool):
60 if os.environ.has_key('PATHEXT'):
61 extns = os.environ['PATHEXT'].split(os.path.pathsep)
62 else:
63 extns = ('',)
64 for pathDir in os.environ['PATH'].split(os.path.pathsep):
65 for ext in extns:
66 if os.path.exists(os.path.join(pathDir, tool + ext)):
67 return True
68 return False
69
70## Check environment variables
71#
72# Check environment variables that must be set for build. Currently they are
73#
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
77#
78# If any of above environment variable is not set or has error, the build
79# will be broken.
80#
81def CheckEnvVariable():
82 # check WORKSPACE
83 if "WORKSPACE" not in os.environ:
84 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
85 ExtraData="WORKSPACE")
86
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
94
95 #
96 # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP
97 #
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"]
103
104 #
105 # Unify case of characters on case-insensitive systems
106 #
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"]))
110
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"])
115
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)
124 else:
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)
133 else:
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)
139
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))
150
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")
155
156 # check PATH
157 if "PATH" not in os.environ:
158 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
159 ExtraData="PATH")
160
30fdf114
LG
161 GlobalData.gWorkspace = WorkspaceDir
162 GlobalData.gEfiSource = EfiSourceDir
163 GlobalData.gEdkSource = EdkSourceDir
164 GlobalData.gEcpSource = EcpSourceDir
165
166## Get normalized file path
167#
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.
170#
171# @param FilePath File path to be normalized
172# @param Workspace Workspace path which the FilePath will be checked against
173#
174# @retval string The normalized file path
175#
176def NormFile(FilePath, Workspace):
177 # check if the path is absolute or relative
178 if os.path.isabs(FilePath):
179 FileFullPath = os.path.normpath(FilePath)
180 else:
181 FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))
182
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)
186
187 # remove workspace directory from the beginning part of the file path
188 if Workspace[-1] in ["\\", "/"]:
189 return FileFullPath[len(Workspace):]
190 else:
191 return FileFullPath[(len(Workspace) + 1):]
192
193## Get the output of an external program
194#
195# This is the entrance method of thread reading output of an external program and
196# putting them in STDOUT/STDERR of current program.
197#
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
201#
202def ReadMessage(From, To, ExitFlag):
203 while True:
204 # read one line a time
205 Line = From.readline()
206 # empty string means "end"
207 if Line != None and Line != "":
208 To(Line.rstrip())
209 else:
210 break
211 if ExitFlag.isSet():
212 break
213
214## Launch an external program
215#
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
219# redirection work.
220#
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
223#
224def 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)
228
229 Proc = None
230 EndOfProcedure = None
231 try:
232 # launch the command
233 Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)
234
235 # launch two threads to read the STDOUT and STDERR
236 EndOfProcedure = Event()
237 EndOfProcedure.clear()
238 if Proc.stdout:
239 StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))
240 StdOutThread.setName("STDOUT-Redirector")
241 StdOutThread.setDaemon(False)
242 StdOutThread.start()
243
244 if Proc.stderr:
245 StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))
246 StdErrThread.setName("STDERR-Redirector")
247 StdErrThread.setDaemon(False)
248 StdErrThread.start()
249
250 # waiting for program exit
251 Proc.wait()
252 except: # in case of aborting
253 # terminate the threads redirecting the program output
254 if EndOfProcedure != None:
255 EndOfProcedure.set()
256 if Proc == 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))
260
261 if Proc.stdout:
262 StdOutThread.join()
263 if Proc.stderr:
264 StdErrThread.join()
265
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))
271
272## The smallest unit that can be built in multi-thread build mode
273#
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
276# missing build.
277#
278# Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.
279#
280class BuildUnit:
281 ## The constructor
282 #
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
288 #
289 def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):
290 self.BuildObject = Obj
291 self.Dependency = Dependency
292 self.WorkingDir = WorkingDir
293 self.Target = Target
294 self.BuildCommand = BuildCommand
295 if BuildCommand == None or len(BuildCommand) == 0:
296 EdkLogger.error("build", OPTION_MISSING, "No build command found for",
297 ExtraData=str(Obj))
298
299 ## str() method
300 #
301 # It just returns the string representaion of self.BuildObject
302 #
303 # @param self The object pointer
304 #
305 def __str__(self):
306 return str(self.BuildObject)
307
308 ## "==" operator method
309 #
310 # It just compares self.BuildObject with "Other". So self.BuildObject must
311 # provide its own __eq__() method.
312 #
313 # @param self The object pointer
314 # @param Other The other BuildUnit object compared to
315 #
316 def __eq__(self, Other):
317 return Other != None and self.BuildObject == Other.BuildObject \
318 and self.BuildObject.Arch == Other.BuildObject.Arch
319
320 ## hash() method
321 #
322 # It just returns the hash value of self.BuildObject which must be hashable.
323 #
324 # @param self The object pointer
325 #
326 def __hash__(self):
327 return hash(self.BuildObject) + hash(self.BuildObject.Arch)
328
329 def __repr__(self):
330 return repr(self.BuildObject)
331
332## The smallest module unit that can be built by nmake/make command in multi-thread build mode
333#
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.
337#
338# Currently the "Obj" should be only ModuleAutoGen object.
339#
340class ModuleMakeUnit(BuildUnit):
341 ## The constructor
342 #
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
346 #
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"
352
353## The smallest platform unit that can be built by nmake/make command in multi-thread build mode
354#
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.
358#
359# Currently the "Obj" should be only PlatformAutoGen object.
360#
361class PlatformMakeUnit(BuildUnit):
362 ## The constructor
363 #
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
367 #
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)
372
373## The class representing the task of a module build or platform build
374#
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.
377#
378class BuildTask:
379 # queue for tasks waiting for schedule
380 _PendingQueue = sdict()
381 _PendingQueueLock = threading.Lock()
382
383 # queue for tasks ready for running
384 _ReadyQueue = sdict()
385 _ReadyQueueLock = threading.Lock()
386
387 # queue for run tasks
388 _RunningQueue = sdict()
389 _RunningQueueLock = threading.Lock()
390
391 # queue containing all build tasks, in case duplicate build
392 _TaskQueue = sdict()
393
394 # flag indicating error occurs in a running thread
395 _ErrorFlag = threading.Event()
396 _ErrorFlag.clear()
397 _ErrorMessage = ""
398
399 # BoundedSemaphore object used to control the number of running threads
400 _Thread = None
401
402 # flag indicating if the scheduler is started or not
403 _SchedulerStopped = threading.Event()
404 _SchedulerStopped.set()
405
406 ## Start the task scheduler thread
407 #
408 # @param MaxThreadNumber The maximum thread number
409 # @param ExitFlag Flag used to end the scheduler
410 #
411 @staticmethod
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():
419 time.sleep(0.01)
420
421 ## Scheduler method
422 #
423 # @param MaxThreadNumber The maximum thread number
424 # @param ExitFlag Flag used to end the scheduler
425 #
426 @staticmethod
427 def Scheduler(MaxThreadNumber, ExitFlag):
428 BuildTask._SchedulerStopped.clear()
429 try:
430 # use BoundedSemaphore to control the maximum running threads
431 BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)
432 #
433 # scheduling loop, which will exits when no pending/ready task and
434 # indicated to do so, or there's error in running thread
435 #
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)))
440
441 # get all pending tasks
442 BuildTask._PendingQueueLock.acquire()
443 BuildObjectList = BuildTask._PendingQueue.keys()
444 #
445 # check if their dependency is resolved, and if true, move them
446 # into ready queue
447 #
448 for BuildObject in BuildObjectList:
449 Bt = BuildTask._PendingQueue[BuildObject]
450 if Bt.IsReady():
451 BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)
452 BuildTask._PendingQueueLock.release()
453
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:
458 break
459
460 # wait for active thread(s) exit
461 BuildTask._Thread.acquire(True)
462
463 # start a new build thread
464 Bo = BuildTask._ReadyQueue.keys()[0]
465 Bt = BuildTask._ReadyQueue.pop(Bo)
466
467 # move into running queue
468 BuildTask._RunningQueueLock.acquire()
469 BuildTask._RunningQueue[Bo] = Bt
470 BuildTask._RunningQueueLock.release()
471
472 Bt.Start()
473 # avoid tense loop
474 time.sleep(0.01)
475
476 # avoid tense loop
477 time.sleep(0.01)
478
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()]))
486 # avoid tense loop
487 time.sleep(0.1)
488 except BaseException, X:
489 #
490 # TRICK: hide the output of threads left runing, so that the user can
491 # catch the error message easily
492 #
493 EdkLogger.SetLevel(EdkLogger.ERROR)
494 BuildTask._ErrorFlag.set()
495 BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)
496
497 BuildTask._PendingQueue.clear()
498 BuildTask._ReadyQueue.clear()
499 BuildTask._RunningQueue.clear()
500 BuildTask._TaskQueue.clear()
501 BuildTask._SchedulerStopped.set()
502
503 ## Wait for all running method exit
504 #
505 @staticmethod
506 def WaitForComplete():
507 BuildTask._SchedulerStopped.wait()
508
509 ## Check if the scheduler is running or not
510 #
511 @staticmethod
512 def IsOnGoing():
513 return not BuildTask._SchedulerStopped.isSet()
514
515 ## Abort the build
516 @staticmethod
517 def Abort():
518 if BuildTask.IsOnGoing():
519 BuildTask._ErrorFlag.set()
520 BuildTask.WaitForComplete()
521
522 ## Check if there's error in running thread
523 #
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.
526 #
527 @staticmethod
528 def HasError():
529 return BuildTask._ErrorFlag.isSet()
530
531 ## Get error message in running thread
532 #
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.
535 #
536 @staticmethod
537 def GetErrorMessage():
538 return BuildTask._ErrorMessage
539
540 ## Factory method to create a BuildTask object
541 #
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.
546 #
547 # @param BuildItem A BuildUnit object representing a build object
548 # @param Dependency The dependent build object of BuildItem
549 #
550 @staticmethod
551 def New(BuildItem, Dependency=None):
552 if BuildItem in BuildTask._TaskQueue:
553 Bt = BuildTask._TaskQueue[BuildItem]
554 return Bt
555
556 Bt = BuildTask()
557 Bt._Init(BuildItem, Dependency)
558 BuildTask._TaskQueue[BuildItem] = Bt
559
560 BuildTask._PendingQueueLock.acquire()
561 BuildTask._PendingQueue[BuildItem] = Bt
562 BuildTask._PendingQueueLock.release()
563
564 return Bt
565
566 ## The real constructor of BuildTask
567 #
568 # @param BuildItem A BuildUnit object representing a build object
569 # @param Dependency The dependent build object of BuildItem
570 #
571 def _Init(self, BuildItem, Dependency=None):
572 self.BuildItem = BuildItem
573
574 self.DependencyList = []
575 if Dependency == None:
576 Dependency = BuildItem.Dependency
577 else:
578 Dependency.extend(BuildItem.Dependency)
579 self.AddDependency(Dependency)
580 # flag indicating build completes, used to avoid unnecessary re-build
581 self.CompleteFlag = False
582
583 ## Check if all dependent build tasks are completed or not
584 #
585 def IsReady(self):
586 ReadyFlag = True
587 for Dep in self.DependencyList:
588 if Dep.CompleteFlag == True:
589 continue
590 ReadyFlag = False
591 break
592
593 return ReadyFlag
594
595 ## Add dependent build task
596 #
597 # @param Dependency The list of dependent build objects
598 #
599 def AddDependency(self, Dependency):
600 for Dep in Dependency:
601 self.DependencyList.append(BuildTask.New(Dep)) # BuildTask list
602
603 ## The thread wrapper of LaunchCommand function
604 #
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
607 #
608 def _CommandThread(self, Command, WorkingDir):
609 try:
610 LaunchCommand(Command, WorkingDir)
611 self.CompleteFlag = True
612 except:
613 #
614 # TRICK: hide the output of threads left runing, so that the user can
615 # catch the error message easily
616 #
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
622 )
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()
632
633 ## Start build task thread
634 #
635 def Start(self):
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()
642
643## The class implementing the EDK2 build process
644#
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
652#
653class Build():
654 ## Constructor
655 #
656 # Constructor will load all necessary configurations, parse platform, modules
657 # and packages and the establish a database for AutoGen.
658 #
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
675 #
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):
680
681 self.WorkspaceDir = WorkspaceDir
682 self.Target = Target
683 self.PlatformFile = Platform
684 self.ModuleFile = Module
685 self.ArchList = Arch
686 self.ToolChainList = ToolChain
687 self.BuildTargetList= BuildTarget
688 self.Fdf = FlashDefinition
689 self.FdList = FdList
690 self.FvList = FvList
691 self.MakefileType = MakefileType
692 self.SilentMode = SilentMode
693 self.ThreadNumber = ThreadNumber
694 self.SkipAutoGen = SkipAutoGen
695 self.Reparse = Reparse
696 self.SkuId = SkuId
697 self.SpawnMode = True
698
699 self.TargetTxt = TargetTxtClassObject()
700 self.ToolDef = ToolDefClassObject()
fd171542 701 self.Db = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)
702 #self.Db = WorkspaceDatabase(None, {}, self.Reparse)
30fdf114
LG
703 self.BuildDatabase = self.Db.BuildObject
704 self.Platform = None
705
706 # print dot charater during doing some time-consuming work
707 self.Progress = Utils.Progressor()
708
709 # parse target.txt, tools_def.txt, and platform file
710 #self.RestoreBuildData()
711 self.LoadConfiguration()
712 self.InitBuild()
713
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"]))
720
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)))
724
725 EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))
726
727 if self.Fdf != None and self.Fdf != "":
728 EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))
729
730 if self.ModuleFile != None and self.ModuleFile != "":
731 EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))
732
733 os.chdir(self.WorkspaceDir)
734 self.Progress.Start("\nProcessing meta-data")
735
736 ## Load configuration
737 #
738 # This method will parse target.txt and get the build configurations.
739 #
740 def LoadConfiguration(self):
741 #
742 # Check target.txt and tools_def.txt and Init them
743 #
744 BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))
745 if os.path.isfile(BuildConfigurationFile) == True:
746 StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)
747
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)
754 else:
755 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)
756 else:
757 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)
758
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]
762
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]
766
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")
772
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)
778 else:
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))
784 else:
785 self.ToolChainList = NewToolChainList
786
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
791 else:
792 self.ThreadNumber = int(self.ThreadNumber, 0)
793
794 if self.ThreadNumber == 0:
795 self.ThreadNumber = 1
796
797 if not self.PlatformFile:
798 PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]
799 if not PlatformFile:
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)
804 if FileNum >= 2:
805 EdkLogger.error("build", OPTION_MISSING,
806 ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))
807 elif FileNum == 1:
808 PlatformFile = FileList[0]
809 else:
810 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
811 ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")
812
813 self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)
814 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
815 if ErrorCode != 0:
816 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
817
818 ## Initialize build configuration
819 #
820 # This method will parse DSC file and merge the configurations from
821 # command line and target.txt, then get the final build configurations.
822 #
823 def InitBuild(self):
824 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")
825 if ErrorCode != 0:
826 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
827
828 # create metafile database
829 self.Db.InitDatabase()
830
831 # we need information in platform description file to determine how to build
832 self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']
833 if not self.Fdf:
834 self.Fdf = self.Platform.FlashDefinition
835
836 if self.SkuId == None or self.SkuId == '':
837 self.SkuId = self.Platform.SkuName
838
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))
843 self.FdList = []
844 if self.FvList != []:
845 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))
846 self.FvList = []
847 else:
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)
854
855 #
856 # Merge Arch
857 #
858 if self.ArchList == None or len(self.ArchList) == 0:
859 ArchList = set(self.Platform.SupArchList)
860 else:
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)
871
872 # Merge build target
873 if self.BuildTargetList == None or len(self.BuildTargetList) == 0:
874 BuildTargetList = self.Platform.BuildTargets
875 else:
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
881
882 ## Build a module or platform
883 #
884 # Create autogen code and makfile for a module or platform, and the launch
885 # "make" command to build it
886 #
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
897 #
898 def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):
899 if AutoGenObject == None:
900 return False
901
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!")
909 if Target == "genc":
910 return True
911
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":
917 return True
918 else:
919 # always recreate top/platform makefile when clean, just in case of inconsistency
920 AutoGenObject.CreateCodeFile(False)
921 AutoGenObject.CreateMakeFile(False)
922
923 if EdkLogger.GetLevel() == EdkLogger.QUIET:
924 EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
925
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)
929
930 BuildCommand = BuildCommand + [Target]
931 LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
932 if Target == 'cleanall':
933 try:
934 #os.rmdir(AutoGenObject.BuildDir)
935 RemoveDirectory(AutoGenObject.BuildDir, True)
936 except WindowsError, X:
937 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
938 return True
939
940 ## Build active platform for different build targets and different tool chains
941 #
942 def _BuildPlatform(self):
943 for BuildTarget in self.BuildTargetList:
944 for ToolChain in self.ToolChainList:
945 Wa = WorkspaceAutoGen(
946 self.WorkspaceDir,
947 self.Platform,
948 BuildTarget,
949 ToolChain,
950 self.ArchList,
951 self.BuildDatabase,
952 self.TargetTxt,
953 self.ToolDef,
954 self.Fdf,
955 self.FdList,
956 self.FvList,
957 self.SkuId
958 )
959 self.Progress.Stop("done!")
960 self._Build(self.Target, Wa)
961
962 ## Build active module for different build targets, different tool chains and different archs
963 #
964 def _BuildModule(self):
965 for BuildTarget in self.BuildTargetList:
966 for ToolChain in self.ToolChainList:
967 #
968 # module build needs platform build information, so get platform
969 # AutoGen first
970 #
971 Wa = WorkspaceAutoGen(
972 self.WorkspaceDir,
973 self.Platform,
974 BuildTarget,
975 ToolChain,
976 self.ArchList,
977 self.BuildDatabase,
978 self.TargetTxt,
979 self.ToolDef,
980 self.Fdf,
981 self.FdList,
982 self.FvList,
983 self.SkuId
984 )
985 Wa.CreateMakeFile(False)
986 self.Progress.Stop("done!")
987 MaList = []
988 for Arch in self.ArchList:
989 Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)
990 if Ma == None: continue
991 MaList.append(Ma)
992 self._Build(self.Target, Ma)
993 if MaList == []:
994 EdkLogger.error(
995 'build',
996 BUILD_ERROR,
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
1002 )
1003
1004 ## Build a platform in multi-thread mode
1005 #
1006 def _MultiThreadBuildPlatform(self):
1007 for BuildTarget in self.BuildTargetList:
1008 for ToolChain in self.ToolChainList:
1009 Wa = WorkspaceAutoGen(
1010 self.WorkspaceDir,
1011 self.Platform,
1012 BuildTarget,
1013 ToolChain,
1014 self.ArchList,
1015 self.BuildDatabase,
1016 self.TargetTxt,
1017 self.ToolDef,
1018 self.Fdf,
1019 self.FdList,
1020 self.FvList,
1021 self.SkuId
1022 )
1023 Wa.CreateMakeFile(False)
1024
1025 # multi-thread exit flag
1026 ExitFlag = threading.Event()
1027 ExitFlag.clear()
1028 for Arch in self.ArchList:
1029 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
1030 if Pa == None:
1031 continue
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)
1035 if Ma == None:
1036 continue
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":
1043 continue
1044
1045 if not self.SkipAutoGen or self.Target == 'genmake':
1046 Ma.CreateMakeFile(True)
1047 if self.Target == "genmake":
1048 continue
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
1055 ExitFlag.set()
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)
1062
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)
1067
1068 #
1069 # All modules have been put in build tasks queue. Tell task scheduler
1070 # to exit if all tasks are completed
1071 #
1072 ExitFlag.set()
1073 BuildTask.WaitForComplete()
1074
1075 #
1076 # Check for build error, and raise exception if one
1077 # has been signaled.
1078 #
1079 if BuildTask.HasError():
1080 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1081
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)
1085
1086 ## Generate GuidedSectionTools.txt in the FV directories.
1087 #
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(
1093 self.WorkspaceDir,
1094 self.Platform.OutputDirectory,
1095 '_'.join((BuildTarget, ToolChain)),
1096 'FV'
1097 )
1098 if not os.path.exists(FvDir):
1099 continue
1100 # Build up the list of supported architectures for this build
1101 prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)
1102
1103 # Look through the tool definitions for GUIDed tools
1104 guidAttribs = []
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]
1111 guid = guid.lower()
1112 toolName = split[3]
1113 path = '_'.join(split[0:4]) + '_PATH'
1114 path = self.ToolDef.ToolsDefTxtDictionary[path]
1115 path = self.GetFullPathOfTool(path)
1116 guidAttribs.append((guid, toolName, path))
1117
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)
1123 toolsFile.close()
1124
1125 ## Returns the full path of the tool.
1126 #
1127 def GetFullPathOfTool (self, tool):
1128 if os.path.exists(tool):
1129 return os.path.realpath(tool)
1130 else:
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)
1137
1138 # If the tool was not found in the path then we just return
1139 # the input tool.
1140 return tool
1141
1142 ## Launch the module or platform build
1143 #
1144 def Launch(self):
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()
1149 else:
1150 self._MultiThreadBuildPlatform()
1151 self.CreateGuidedSectionToolsFile()
1152 else:
1153 self.SpawnMode = False
1154 self._BuildModule()
1155
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:
1163 BuildTask.Abort()
1164 EdkLogger.SetLevel(OldLogLevel)
1165
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"))
1171
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 = {}
1178
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 = {}
1184
1185def ParseDefines(DefineList=[]):
1186 DefineDict = {}
1187 if DefineList != None:
1188 for Define in DefineList:
1189 DefineTokenList = Define.split("=", 1)
1190 if len(DefineTokenList) == 1:
1191 DefineDict[DefineTokenList[0]] = ""
1192 else:
1193 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()
1194 return DefineDict
1195
1196gParamCheck = []
1197def SingleCheckCallback(option, opt_str, value, parser):
1198 if option not in gParamCheck:
1199 setattr(parser.values, option.dest, value)
1200 gParamCheck.append(option)
1201 else:
1202 parser.error("Option %s only allows one instance in command line!" % option)
1203
1204## Parse command line options
1205#
1206# Using standard Python module optparse to parse command line option of this tool.
1207#
1208# @retval Opt A optparse.Values object containing the parsed options
1209# @retval Args Target of build command
1210#
1211def 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.")
1225
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.")
1228
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.")
1235
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.")
1238
1239 Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")
1240
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.")
1243
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.")
1246
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.")
fd171542 1254 Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")
30fdf114
LG
1255
1256 (Opt, Args)=Parser.parse_args()
1257 return (Opt, Args)
1258
1259## Tool entrance method
1260#
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.
1264#
1265# @retval 0 Tool was successful
1266# @retval 1 Tool failed
1267#
1268def Main():
1269 StartTime = time.time()
1270
1271 # Initialize log system
1272 EdkLogger.Initialize()
1273
1274 #
1275 # Parse the options and args
1276 #
1277 (Option, Target) = MyOptionParser()
1278 GlobalData.gOptions = Option
1279 GlobalData.gCaseInsensitive = Option.CaseInsensitive
1280
1281 # Set log level
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)
1288 else:
1289 EdkLogger.SetLevel(EdkLogger.INFO)
1290
1291 if Option.LogFile != None:
1292 EdkLogger.SetLogFile(Option.LogFile)
1293
1294 if Option.WarningAsError == True:
1295 EdkLogger.SetWarningAsError()
1296
1297 if platform.platform().find("Windows") >= 0:
1298 GlobalData.gIsWindows = True
1299 else:
1300 GlobalData.gIsWindows = False
1301
1302 EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())
1303 ReturnCode = 0
1304 MyBuild = None
1305 try:
1306 if len(Target) == 0:
1307 Target = "all"
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)))
1311 else:
1312 Target = Target[0].lower()
1313
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)))
1317
fd171542 1318 GlobalData.gGlobalDefines = ParseDefines(Option.Macros)
30fdf114
LG
1319 #
1320 # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
1321 #
1322 CheckEnvVariable()
1323 Workspace = os.getenv("WORKSPACE")
1324 #
1325 # Get files real name in workspace dir
1326 #
1327 GlobalData.gAllFiles = Utils.DirCache(Workspace)
1328
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)
1333 if FileNum >= 2:
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.")
1336 elif FileNum == 1:
1337 Option.ModuleFile = NormFile(FileList[0], Workspace)
1338
1339 if Option.ModuleFile:
1340 Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)
1341 ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)
1342 if ErrorCode != 0:
1343 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
1344
1345 if Option.PlatformFile != None:
1346 Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)
1347 ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)
1348 if ErrorCode != 0:
1349 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
1350
1351 if Option.FdfFile != None:
1352 Option.FdfFile = PathClass(Option.FdfFile, Workspace)
1353 ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)
1354 if ErrorCode != 0:
1355 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
1356
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)
1362 MyBuild.Launch()
1363 #MyBuild.DumpBuildData()
1364 except FatalError, X:
1365 if MyBuild != None:
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]
1371 except Warning, X:
1372 # error from Fdf parser
1373 if MyBuild != None:
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())
1378 else:
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())
1385 except:
1386 if MyBuild != None:
1387 # for multi-thread build exits safely
1388 MyBuild.Relinquish()
1389
1390 # try to get the meta-file from the object causing exception
1391 Tb = sys.exc_info()[-1]
1392 MetaFile = GlobalData.gProcessingFile
1393 while Tb != None:
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
1396 Tb = Tb.tb_next
1397 EdkLogger.error(
1398 "\nbuild",
1399 CODE_ERROR,
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",
1402 RaiseError=False
1403 )
1404 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
1405 ReturnCode = CODE_ERROR
1406 finally:
1407 Utils.Progressor.Abort()
1408
1409 if MyBuild != None:
1410 MyBuild.Db.Close()
1411
1412 if ReturnCode == 0:
1413 Conclusion = "Done"
1414 elif ReturnCode == ABORT_ERROR:
1415 Conclusion = "Aborted"
1416 else:
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))
1422
1423 return ReturnCode
1424
1425if __name__ == '__main__':
1426 r = 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
1429 sys.exit(r)
1430