]>
Commit | Line | Data |
---|---|---|
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 | # | |
18 | import os | |
19 | import re | |
20 | import sys | |
21 | import glob | |
22 | import time | |
23 | import platform | |
24 | import traceback | |
25 | ||
26 | from threading import * | |
27 | from optparse import OptionParser | |
28 | from subprocess import * | |
29 | from Common import Misc as Utils | |
30 | ||
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 * | |
37 | ||
38 | import Common.EdkLogger | |
39 | import Common.GlobalData as GlobalData | |
40 | ||
41 | # Version and Copyright | |
42 | VersionNumber = "0.5" | |
43 | __version__ = "%prog Version " + VersionNumber | |
44 | __copyright__ = "Copyright (c) 2007, Intel Corporation All rights reserved." | |
45 | ||
46 | ## standard targets of build command | |
47 | gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run'] | |
48 | ||
49 | ## build configuration file | |
50 | gBuildConfiguration = "Conf/target.txt" | |
51 | gBuildCacheDir = "Conf/.cache" | |
52 | gToolsDefinition = "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 | # | |
59 | def 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 | # | |
81 | def 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 | # | |
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) | |
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 | # | |
202 | def 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 | # | |
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) | |
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 | # | |
280 | class 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 | # | |
340 | class 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 | # | |
361 | class 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 | # | |
378 | class 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 | # | |
653 | class 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 | ||
1185 | def 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 | ||
1196 | gParamCheck = [] | |
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) | |
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 | # | |
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.") | |
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 | # | |
1268 | def 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 | ||
1425 | if __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 |