]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/build/build.py
Sync EDKII BaseTools to BaseTools project r1903.
[mirror_edk2.git] / BaseTools / Source / Python / build / build.py
CommitLineData
52302d4d
LG
1## @file\r
2# build a platform or a module\r
3#\r
4# Copyright (c) 2007 - 2010, Intel Corporation\r
5#\r
6# All rights reserved. This program and the accompanying materials\r
7# are licensed and made available under the terms and conditions of the BSD License\r
8# which accompanies this distribution. The full text of the license may be found at\r
9# http://opensource.org/licenses/bsd-license.php\r
10#\r
11# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13#\r
14\r
15##\r
16# Import Modules\r
17#\r
18import os\r
19import re\r
20import StringIO\r
21import sys\r
22import glob\r
23import time\r
24import platform\r
25import traceback\r
26\r
27from struct import *\r
28from threading import *\r
29from optparse import OptionParser\r
30from subprocess import *\r
31from Common import Misc as Utils\r
32\r
33from Common.TargetTxtClassObject import *\r
34from Common.ToolDefClassObject import *\r
35from Common.DataType import *\r
36from AutoGen.AutoGen import *\r
37from Common.BuildToolError import *\r
38from Workspace.WorkspaceDatabase import *\r
39\r
40from BuildReport import BuildReport\r
41from GenPatchPcdTable.GenPatchPcdTable import *\r
42from PatchPcdValue.PatchPcdValue import *\r
43\r
44import Common.EdkLogger\r
45import Common.GlobalData as GlobalData\r
46\r
47# Version and Copyright\r
48VersionNumber = "0.5"\r
49__version__ = "%prog Version " + VersionNumber\r
50__copyright__ = "Copyright (c) 2007 - 2010, Intel Corporation All rights reserved."\r
51\r
52## standard targets of build command\r
53gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
54\r
55## build configuration file\r
56gBuildConfiguration = "Conf/target.txt"\r
57gBuildCacheDir = "Conf/.cache"\r
58gToolsDefinition = "Conf/tools_def.txt"\r
59\r
60## Check environment PATH variable to make sure the specified tool is found\r
61#\r
62# If the tool is found in the PATH, then True is returned\r
63# Otherwise, False is returned\r
64#\r
65def IsToolInPath(tool):\r
66 if os.environ.has_key('PATHEXT'):\r
67 extns = os.environ['PATHEXT'].split(os.path.pathsep)\r
68 else:\r
69 extns = ('',)\r
70 for pathDir in os.environ['PATH'].split(os.path.pathsep):\r
71 for ext in extns:\r
72 if os.path.exists(os.path.join(pathDir, tool + ext)):\r
73 return True\r
74 return False\r
75\r
76## Check environment variables\r
77#\r
78# Check environment variables that must be set for build. Currently they are\r
79#\r
80# WORKSPACE The directory all packages/platforms start from\r
81# EDK_TOOLS_PATH The directory contains all tools needed by the build\r
82# PATH $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
83#\r
84# If any of above environment variable is not set or has error, the build\r
85# will be broken.\r
86#\r
87def CheckEnvVariable():\r
88 # check WORKSPACE\r
89 if "WORKSPACE" not in os.environ:\r
90 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
91 ExtraData="WORKSPACE")\r
92\r
93 WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))\r
94 if not os.path.exists(WorkspaceDir):\r
95 EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir)\r
96 elif ' ' in WorkspaceDir:\r
97 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",\r
98 ExtraData=WorkspaceDir)\r
99 os.environ["WORKSPACE"] = WorkspaceDir\r
100\r
101 #\r
102 # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP\r
103 #\r
104 os.environ["ECP_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)\r
105 if "EFI_SOURCE" not in os.environ:\r
106 os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"]\r
107 if "EDK_SOURCE" not in os.environ:\r
108 os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"]\r
109\r
110 #\r
111 # Unify case of characters on case-insensitive systems\r
112 #\r
113 EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"]))\r
114 EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"]))\r
115 EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"]))\r
116 \r
117 os.environ["EFI_SOURCE"] = EfiSourceDir\r
118 os.environ["EDK_SOURCE"] = EdkSourceDir\r
119 os.environ["ECP_SOURCE"] = EcpSourceDir\r
120 os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])\r
121 \r
122 if not os.path.exists(EcpSourceDir):\r
123 EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir)\r
124 elif ' ' in EcpSourceDir:\r
125 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path",\r
126 ExtraData=EcpSourceDir)\r
127 if not os.path.exists(EdkSourceDir):\r
128 if EdkSourceDir == EcpSourceDir:\r
129 EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir)\r
130 else:\r
131 EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist",\r
132 ExtraData=EdkSourceDir)\r
133 elif ' ' in EdkSourceDir:\r
134 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path",\r
135 ExtraData=EdkSourceDir)\r
136 if not os.path.exists(EfiSourceDir):\r
137 if EfiSourceDir == EcpSourceDir:\r
138 EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir)\r
139 else:\r
140 EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist",\r
141 ExtraData=EfiSourceDir)\r
142 elif ' ' in EfiSourceDir:\r
143 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path",\r
144 ExtraData=EfiSourceDir)\r
145\r
146 # change absolute path to relative path to WORKSPACE\r
147 if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
148 EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE",\r
149 ExtraData="WORKSPACE = %s\n EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir))\r
150 if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
151 EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE",\r
152 ExtraData="WORKSPACE = %s\n EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir))\r
153 if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
154 EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE",\r
155 ExtraData="WORKSPACE = %s\n ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir))\r
156\r
157 # check EDK_TOOLS_PATH\r
158 if "EDK_TOOLS_PATH" not in os.environ:\r
159 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
160 ExtraData="EDK_TOOLS_PATH")\r
161\r
162 # check PATH\r
163 if "PATH" not in os.environ:\r
164 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
165 ExtraData="PATH")\r
166\r
167 GlobalData.gWorkspace = WorkspaceDir\r
168 GlobalData.gEfiSource = EfiSourceDir\r
169 GlobalData.gEdkSource = EdkSourceDir\r
170 GlobalData.gEcpSource = EcpSourceDir\r
171\r
172## Get normalized file path\r
173#\r
174# Convert the path to be local format, and remove the WORKSPACE path at the\r
175# beginning if the file path is given in full path.\r
176#\r
177# @param FilePath File path to be normalized\r
178# @param Workspace Workspace path which the FilePath will be checked against\r
179#\r
180# @retval string The normalized file path\r
181#\r
182def NormFile(FilePath, Workspace):\r
183 # check if the path is absolute or relative\r
184 if os.path.isabs(FilePath):\r
185 FileFullPath = os.path.normpath(FilePath)\r
186 else:\r
187 FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))\r
188\r
189 # check if the file path exists or not\r
190 if not os.path.isfile(FileFullPath):\r
191 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath)\r
192\r
193 # remove workspace directory from the beginning part of the file path\r
194 if Workspace[-1] in ["\\", "/"]:\r
195 return FileFullPath[len(Workspace):]\r
196 else:\r
197 return FileFullPath[(len(Workspace) + 1):]\r
198\r
199## Get the output of an external program\r
200#\r
201# This is the entrance method of thread reading output of an external program and\r
202# putting them in STDOUT/STDERR of current program.\r
203#\r
204# @param From The stream message read from\r
205# @param To The stream message put on\r
206# @param ExitFlag The flag used to indicate stopping reading\r
207#\r
208def ReadMessage(From, To, ExitFlag):\r
209 while True:\r
210 # read one line a time\r
211 Line = From.readline()\r
212 # empty string means "end"\r
213 if Line != None and Line != "":\r
214 To(Line.rstrip())\r
215 else:\r
216 break\r
217 if ExitFlag.isSet():\r
218 break\r
219\r
220## Launch an external program\r
221#\r
222# This method will call subprocess.Popen to execute an external program with\r
223# given options in specified directory. Because of the dead-lock issue during\r
224# redirecting output of the external program, threads are used to to do the\r
225# redirection work.\r
226#\r
227# @param Command A list or string containing the call of the program\r
228# @param WorkingDir The directory in which the program will be running\r
229#\r
230def LaunchCommand(Command, WorkingDir):\r
231 # if working directory doesn't exist, Popen() will raise an exception\r
232 if not os.path.isdir(WorkingDir):\r
233 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
234\r
235 Proc = None\r
236 EndOfProcedure = None\r
237 try:\r
238 # launch the command\r
239 Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)\r
240\r
241 # launch two threads to read the STDOUT and STDERR\r
242 EndOfProcedure = Event()\r
243 EndOfProcedure.clear()\r
244 if Proc.stdout:\r
245 StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
246 StdOutThread.setName("STDOUT-Redirector")\r
247 StdOutThread.setDaemon(False)\r
248 StdOutThread.start()\r
249\r
250 if Proc.stderr:\r
251 StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
252 StdErrThread.setName("STDERR-Redirector")\r
253 StdErrThread.setDaemon(False)\r
254 StdErrThread.start()\r
255\r
256 # waiting for program exit\r
257 Proc.wait()\r
258 except: # in case of aborting\r
259 # terminate the threads redirecting the program output\r
260 if EndOfProcedure != None:\r
261 EndOfProcedure.set()\r
262 if Proc == None:\r
263 if type(Command) != type(""):\r
264 Command = " ".join(Command)\r
265 EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
266\r
267 if Proc.stdout:\r
268 StdOutThread.join()\r
269 if Proc.stderr:\r
270 StdErrThread.join()\r
271\r
272 # check the return code of the program\r
273 if Proc.returncode != 0:\r
274 if type(Command) != type(""):\r
275 Command = " ".join(Command)\r
276 EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
277\r
278## The smallest unit that can be built in multi-thread build mode\r
279#\r
280# This is the base class of build unit. The "Obj" parameter must provide\r
281# __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
282# missing build.\r
283#\r
284# Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
285#\r
286class BuildUnit:\r
287 ## The constructor\r
288 #\r
289 # @param self The object pointer\r
290 # @param Obj The object the build is working on\r
291 # @param Target The build target name, one of gSupportedTarget\r
292 # @param Dependency The BuildUnit(s) which must be completed in advance\r
293 # @param WorkingDir The directory build command starts in\r
294 #\r
295 def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):\r
296 self.BuildObject = Obj\r
297 self.Dependency = Dependency\r
298 self.WorkingDir = WorkingDir\r
299 self.Target = Target\r
300 self.BuildCommand = BuildCommand\r
301 if BuildCommand == None or len(BuildCommand) == 0:\r
302 EdkLogger.error("build", OPTION_MISSING, "No build command found for",\r
303 ExtraData=str(Obj))\r
304\r
305 ## str() method\r
306 #\r
307 # It just returns the string representaion of self.BuildObject\r
308 #\r
309 # @param self The object pointer\r
310 #\r
311 def __str__(self):\r
312 return str(self.BuildObject)\r
313\r
314 ## "==" operator method\r
315 #\r
316 # It just compares self.BuildObject with "Other". So self.BuildObject must\r
317 # provide its own __eq__() method.\r
318 #\r
319 # @param self The object pointer\r
320 # @param Other The other BuildUnit object compared to\r
321 #\r
322 def __eq__(self, Other):\r
323 return Other != None and self.BuildObject == Other.BuildObject \\r
324 and self.BuildObject.Arch == Other.BuildObject.Arch\r
325\r
326 ## hash() method\r
327 #\r
328 # It just returns the hash value of self.BuildObject which must be hashable.\r
329 #\r
330 # @param self The object pointer\r
331 #\r
332 def __hash__(self):\r
333 return hash(self.BuildObject) + hash(self.BuildObject.Arch)\r
334\r
335 def __repr__(self):\r
336 return repr(self.BuildObject)\r
337\r
338## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
339#\r
340# This class is for module build by nmake/make build system. The "Obj" parameter\r
341# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
342# be make units missing build.\r
343#\r
344# Currently the "Obj" should be only ModuleAutoGen object.\r
345#\r
346class ModuleMakeUnit(BuildUnit):\r
347 ## The constructor\r
348 #\r
349 # @param self The object pointer\r
350 # @param Obj The ModuleAutoGen object the build is working on\r
351 # @param Target The build target name, one of gSupportedTarget\r
352 #\r
353 def __init__(self, Obj, Target):\r
354 Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]\r
355 BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
356 if Target in [None, "", "all"]:\r
357 self.Target = "tbuild"\r
358\r
359## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
360#\r
361# This class is for platform build by nmake/make build system. The "Obj" parameter\r
362# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
363# be make units missing build.\r
364#\r
365# Currently the "Obj" should be only PlatformAutoGen object.\r
366#\r
367class PlatformMakeUnit(BuildUnit):\r
368 ## The constructor\r
369 #\r
370 # @param self The object pointer\r
371 # @param Obj The PlatformAutoGen object the build is working on\r
372 # @param Target The build target name, one of gSupportedTarget\r
373 #\r
374 def __init__(self, Obj, Target):\r
375 Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
376 Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
377 BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
378\r
379## The class representing the task of a module build or platform build\r
380#\r
381# This class manages the build tasks in multi-thread build mode. Its jobs include\r
382# scheduling thread running, catching thread error, monitor the thread status, etc.\r
383#\r
384class BuildTask:\r
385 # queue for tasks waiting for schedule\r
386 _PendingQueue = sdict()\r
387 _PendingQueueLock = threading.Lock()\r
388\r
389 # queue for tasks ready for running\r
390 _ReadyQueue = sdict()\r
391 _ReadyQueueLock = threading.Lock()\r
392\r
393 # queue for run tasks\r
394 _RunningQueue = sdict()\r
395 _RunningQueueLock = threading.Lock()\r
396\r
397 # queue containing all build tasks, in case duplicate build\r
398 _TaskQueue = sdict()\r
399\r
400 # flag indicating error occurs in a running thread\r
401 _ErrorFlag = threading.Event()\r
402 _ErrorFlag.clear()\r
403 _ErrorMessage = ""\r
404\r
405 # BoundedSemaphore object used to control the number of running threads\r
406 _Thread = None\r
407\r
408 # flag indicating if the scheduler is started or not\r
409 _SchedulerStopped = threading.Event()\r
410 _SchedulerStopped.set()\r
411\r
412 ## Start the task scheduler thread\r
413 #\r
414 # @param MaxThreadNumber The maximum thread number\r
415 # @param ExitFlag Flag used to end the scheduler\r
416 #\r
417 @staticmethod\r
418 def StartScheduler(MaxThreadNumber, ExitFlag):\r
419 SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
420 SchedulerThread.setName("Build-Task-Scheduler")\r
421 SchedulerThread.setDaemon(False)\r
422 SchedulerThread.start()\r
423 # wait for the scheduler to be started, especially useful in Linux\r
424 while not BuildTask.IsOnGoing():\r
425 time.sleep(0.01)\r
426\r
427 ## Scheduler method\r
428 #\r
429 # @param MaxThreadNumber The maximum thread number\r
430 # @param ExitFlag Flag used to end the scheduler\r
431 #\r
432 @staticmethod\r
433 def Scheduler(MaxThreadNumber, ExitFlag):\r
434 BuildTask._SchedulerStopped.clear()\r
435 try:\r
436 # use BoundedSemaphore to control the maximum running threads\r
437 BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
438 #\r
439 # scheduling loop, which will exits when no pending/ready task and\r
440 # indicated to do so, or there's error in running thread\r
441 #\r
442 while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
443 or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
444 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
445 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
446\r
447 # get all pending tasks\r
448 BuildTask._PendingQueueLock.acquire()\r
449 BuildObjectList = BuildTask._PendingQueue.keys()\r
450 #\r
451 # check if their dependency is resolved, and if true, move them\r
452 # into ready queue\r
453 #\r
454 for BuildObject in BuildObjectList:\r
455 Bt = BuildTask._PendingQueue[BuildObject]\r
456 if Bt.IsReady():\r
457 BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
458 BuildTask._PendingQueueLock.release()\r
459\r
460 # launch build thread until the maximum number of threads is reached\r
461 while not BuildTask._ErrorFlag.isSet():\r
462 # empty ready queue, do nothing further\r
463 if len(BuildTask._ReadyQueue) == 0:\r
464 break\r
465\r
466 # wait for active thread(s) exit\r
467 BuildTask._Thread.acquire(True)\r
468\r
469 # start a new build thread\r
470 Bo = BuildTask._ReadyQueue.keys()[0]\r
471 Bt = BuildTask._ReadyQueue.pop(Bo)\r
472\r
473 # move into running queue\r
474 BuildTask._RunningQueueLock.acquire()\r
475 BuildTask._RunningQueue[Bo] = Bt\r
476 BuildTask._RunningQueueLock.release()\r
477\r
478 Bt.Start()\r
479 # avoid tense loop\r
480 time.sleep(0.01)\r
481\r
482 # avoid tense loop\r
483 time.sleep(0.01)\r
484\r
485 # wait for all running threads exit\r
486 if BuildTask._ErrorFlag.isSet():\r
487 EdkLogger.quiet("\nWaiting for all build threads exit...")\r
488 # while not BuildTask._ErrorFlag.isSet() and \\r
489 while len(BuildTask._RunningQueue) > 0:\r
490 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
491 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
492 # avoid tense loop\r
493 time.sleep(0.1)\r
494 except BaseException, X:\r
495 #\r
496 # TRICK: hide the output of threads left runing, so that the user can\r
497 # catch the error message easily\r
498 #\r
499 EdkLogger.SetLevel(EdkLogger.ERROR)\r
500 BuildTask._ErrorFlag.set()\r
501 BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
502\r
503 BuildTask._PendingQueue.clear()\r
504 BuildTask._ReadyQueue.clear()\r
505 BuildTask._RunningQueue.clear()\r
506 BuildTask._TaskQueue.clear()\r
507 BuildTask._SchedulerStopped.set()\r
508\r
509 ## Wait for all running method exit\r
510 #\r
511 @staticmethod\r
512 def WaitForComplete():\r
513 BuildTask._SchedulerStopped.wait()\r
514\r
515 ## Check if the scheduler is running or not\r
516 #\r
517 @staticmethod\r
518 def IsOnGoing():\r
519 return not BuildTask._SchedulerStopped.isSet()\r
520\r
521 ## Abort the build\r
522 @staticmethod\r
523 def Abort():\r
524 if BuildTask.IsOnGoing():\r
525 BuildTask._ErrorFlag.set()\r
526 BuildTask.WaitForComplete()\r
527\r
528 ## Check if there's error in running thread\r
529 #\r
530 # Since the main thread cannot catch exceptions in other thread, we have to\r
531 # use threading.Event to communicate this formation to main thread.\r
532 #\r
533 @staticmethod\r
534 def HasError():\r
535 return BuildTask._ErrorFlag.isSet()\r
536\r
537 ## Get error message in running thread\r
538 #\r
539 # Since the main thread cannot catch exceptions in other thread, we have to\r
540 # use a static variable to communicate this message to main thread.\r
541 #\r
542 @staticmethod\r
543 def GetErrorMessage():\r
544 return BuildTask._ErrorMessage\r
545\r
546 ## Factory method to create a BuildTask object\r
547 #\r
548 # This method will check if a module is building or has been built. And if\r
549 # true, just return the associated BuildTask object in the _TaskQueue. If\r
550 # not, create and return a new BuildTask object. The new BuildTask object\r
551 # will be appended to the _PendingQueue for scheduling later.\r
552 #\r
553 # @param BuildItem A BuildUnit object representing a build object\r
554 # @param Dependency The dependent build object of BuildItem\r
555 #\r
556 @staticmethod\r
557 def New(BuildItem, Dependency=None):\r
558 if BuildItem in BuildTask._TaskQueue:\r
559 Bt = BuildTask._TaskQueue[BuildItem]\r
560 return Bt\r
561\r
562 Bt = BuildTask()\r
563 Bt._Init(BuildItem, Dependency)\r
564 BuildTask._TaskQueue[BuildItem] = Bt\r
565\r
566 BuildTask._PendingQueueLock.acquire()\r
567 BuildTask._PendingQueue[BuildItem] = Bt\r
568 BuildTask._PendingQueueLock.release()\r
569\r
570 return Bt\r
571\r
572 ## The real constructor of BuildTask\r
573 #\r
574 # @param BuildItem A BuildUnit object representing a build object\r
575 # @param Dependency The dependent build object of BuildItem\r
576 #\r
577 def _Init(self, BuildItem, Dependency=None):\r
578 self.BuildItem = BuildItem\r
579\r
580 self.DependencyList = []\r
581 if Dependency == None:\r
582 Dependency = BuildItem.Dependency\r
583 else:\r
584 Dependency.extend(BuildItem.Dependency)\r
585 self.AddDependency(Dependency)\r
586 # flag indicating build completes, used to avoid unnecessary re-build\r
587 self.CompleteFlag = False\r
588\r
589 ## Check if all dependent build tasks are completed or not\r
590 #\r
591 def IsReady(self):\r
592 ReadyFlag = True\r
593 for Dep in self.DependencyList:\r
594 if Dep.CompleteFlag == True:\r
595 continue\r
596 ReadyFlag = False\r
597 break\r
598\r
599 return ReadyFlag\r
600\r
601 ## Add dependent build task\r
602 #\r
603 # @param Dependency The list of dependent build objects\r
604 #\r
605 def AddDependency(self, Dependency):\r
606 for Dep in Dependency:\r
607 self.DependencyList.append(BuildTask.New(Dep)) # BuildTask list\r
608\r
609 ## The thread wrapper of LaunchCommand function\r
610 #\r
611 # @param Command A list or string contains the call of the command\r
612 # @param WorkingDir The directory in which the program will be running\r
613 #\r
614 def _CommandThread(self, Command, WorkingDir):\r
615 try:\r
616 LaunchCommand(Command, WorkingDir)\r
617 self.CompleteFlag = True\r
618 except:\r
619 #\r
620 # TRICK: hide the output of threads left runing, so that the user can\r
621 # catch the error message easily\r
622 #\r
623 if not BuildTask._ErrorFlag.isSet():\r
624 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),\r
625 self.BuildItem.BuildObject.Arch,\r
626 self.BuildItem.BuildObject.ToolChain,\r
627 self.BuildItem.BuildObject.BuildTarget\r
628 )\r
629 EdkLogger.SetLevel(EdkLogger.ERROR)\r
630 BuildTask._ErrorFlag.set()\r
631 BuildTask._ErrorMessage = "%s broken\n %s [%s]" % \\r
632 (threading.currentThread().getName(), Command, WorkingDir)\r
633 # indicate there's a thread is available for another build task\r
634 BuildTask._RunningQueueLock.acquire()\r
635 BuildTask._RunningQueue.pop(self.BuildItem)\r
636 BuildTask._RunningQueueLock.release()\r
637 BuildTask._Thread.release()\r
638\r
639 ## Start build task thread\r
640 #\r
641 def Start(self):\r
642 EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))\r
643 Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]\r
644 self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
645 self.BuildTread.setName("build thread")\r
646 self.BuildTread.setDaemon(False)\r
647 self.BuildTread.start()\r
648\r
649## The class contains the information related to EFI image\r
650#\r
651class PeImageInfo():\r
652 ## Constructor\r
653 #\r
654 # Constructor will load all required image information.\r
655 #\r
656 # @param BaseName The full file path of image. \r
657 # @param Guid The GUID for image.\r
658 # @param Arch Arch of this image.\r
659 # @param OutpuDir The output directory for image.\r
660 # @param ImageClass PeImage Information\r
661 #\r
662 def __init__(self, BaseName, Guid, Arch, OutpuDir, ImageClass):\r
663 self.BaseName = BaseName\r
664 self.Guid = Guid\r
665 self.Arch = Arch\r
666 self.OutpuDir = OutpuDir\r
667 self.Image = ImageClass\r
668 self.Image.Size = (self.Image.Size / 0x1000 + 1) * 0x1000\r
669\r
670## The class implementing the EDK2 build process\r
671#\r
672# The build process includes:\r
673# 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
674# 2. Parse DSC file of active platform\r
675# 3. Parse FDF file if any\r
676# 4. Establish build database, including parse all other files (module, package)\r
677# 5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
678# 6. Call build command\r
679#\r
680class Build():\r
681 ## Constructor\r
682 #\r
683 # Constructor will load all necessary configurations, parse platform, modules\r
684 # and packages and the establish a database for AutoGen.\r
685 #\r
686 # @param Target The build command target, one of gSupportedTarget\r
687 # @param WorkspaceDir The directory of workspace\r
688 # @param Platform The DSC file of active platform\r
689 # @param Module The INF file of active module, if any\r
690 # @param Arch The Arch list of platform or module\r
691 # @param ToolChain The name list of toolchain\r
692 # @param BuildTarget The "DEBUG" or "RELEASE" build\r
693 # @param FlashDefinition The FDF file of active platform\r
694 # @param FdList=[] The FD names to be individually built\r
695 # @param FvList=[] The FV names to be individually built\r
696 # @param MakefileType The type of makefile (for MSFT make or GNU make)\r
697 # @param SilentMode Indicate multi-thread build mode\r
698 # @param ThreadNumber The maximum number of thread if in multi-thread build mode\r
699 # @param SkipAutoGen Skip AutoGen step\r
700 # @param Reparse Re-parse all meta files\r
701 # @param SkuId SKU id from command line\r
702 #\r
703 def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain,\r
704 BuildTarget, FlashDefinition, FdList=[], FvList=[],\r
705 MakefileType="nmake", SilentMode=False, ThreadNumber=2,\r
706 SkipAutoGen=False, Reparse=False, SkuId=None, \r
707 ReportFile=None, ReportType=None):\r
708\r
709 self.WorkspaceDir = WorkspaceDir\r
710 self.Target = Target\r
711 self.PlatformFile = Platform\r
712 self.ModuleFile = Module\r
713 self.ArchList = Arch\r
714 self.ToolChainList = ToolChain\r
715 self.BuildTargetList= BuildTarget\r
716 self.Fdf = FlashDefinition\r
717 self.FdList = FdList\r
718 self.FvList = FvList\r
719 self.MakefileType = MakefileType\r
720 self.SilentMode = SilentMode\r
721 self.ThreadNumber = ThreadNumber\r
722 self.SkipAutoGen = SkipAutoGen\r
723 self.Reparse = Reparse\r
724 self.SkuId = SkuId\r
725 self.SpawnMode = True\r
726 self.BuildReport = BuildReport(ReportFile, ReportType)\r
727 self.TargetTxt = TargetTxtClassObject()\r
728 self.ToolDef = ToolDefClassObject()\r
729 self.Db = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)\r
730 #self.Db = WorkspaceDatabase(None, {}, self.Reparse)\r
731 self.BuildDatabase = self.Db.BuildObject\r
732 self.Platform = None\r
733 self.LoadFixAddress = 0\r
734\r
735 # print dot charater during doing some time-consuming work\r
736 self.Progress = Utils.Progressor()\r
737\r
738 # parse target.txt, tools_def.txt, and platform file\r
739 #self.RestoreBuildData()\r
740 self.LoadConfiguration()\r
741 self.InitBuild()\r
742\r
743 # print current build environment and configuration\r
744 EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
745 EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))\r
746 EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
747 EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
748 EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
749\r
750 EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
751 EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
752 EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
753\r
754 EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))\r
755\r
756 if self.Fdf != None and self.Fdf != "":\r
757 EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
758\r
759 if self.ModuleFile != None and self.ModuleFile != "":\r
760 EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
761\r
762 os.chdir(self.WorkspaceDir)\r
763 self.Progress.Start("\nProcessing meta-data")\r
764\r
765 ## Load configuration\r
766 #\r
767 # This method will parse target.txt and get the build configurations.\r
768 #\r
769 def LoadConfiguration(self):\r
770 #\r
771 # Check target.txt and tools_def.txt and Init them\r
772 #\r
773 BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
774 if os.path.isfile(BuildConfigurationFile) == True:\r
775 StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
776\r
777 ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
778 if ToolDefinitionFile == '':\r
779 ToolDefinitionFile = gToolsDefinition\r
780 ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
781 if os.path.isfile(ToolDefinitionFile) == True:\r
782 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
783 else:\r
784 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
785 else:\r
786 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
787\r
788 # if no ARCH given in command line, get it from target.txt\r
789 if self.ArchList == None or len(self.ArchList) == 0:\r
790 self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
791\r
792 # if no build target given in command line, get it from target.txt\r
793 if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
794 self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
795\r
796 # if no tool chain given in command line, get it from target.txt\r
797 if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
798 self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
799 if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
800 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
801\r
802 # check if the tool chains are defined or not\r
803 NewToolChainList = []\r
804 for ToolChain in self.ToolChainList:\r
805 if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
806 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
807 else:\r
808 NewToolChainList.append(ToolChain)\r
809 # if no tool chain available, break the build\r
810 if len(NewToolChainList) == 0:\r
811 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
812 ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
813 else:\r
814 self.ToolChainList = NewToolChainList\r
815\r
816 if self.ThreadNumber == None:\r
817 self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
818 if self.ThreadNumber == '':\r
819 self.ThreadNumber = 0\r
820 else:\r
821 self.ThreadNumber = int(self.ThreadNumber, 0)\r
822\r
823 if self.ThreadNumber == 0:\r
824 self.ThreadNumber = 1\r
825\r
826 if not self.PlatformFile:\r
827 PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
828 if not PlatformFile:\r
829 # Try to find one in current directory\r
830 WorkingDirectory = os.getcwd()\r
831 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
832 FileNum = len(FileList)\r
833 if FileNum >= 2:\r
834 EdkLogger.error("build", OPTION_MISSING,\r
835 ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
836 elif FileNum == 1:\r
837 PlatformFile = FileList[0]\r
838 else:\r
839 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
840 ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
841\r
842 self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
843 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
844 if ErrorCode != 0:\r
845 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
846\r
847 ## Initialize build configuration\r
848 #\r
849 # This method will parse DSC file and merge the configurations from\r
850 # command line and target.txt, then get the final build configurations.\r
851 #\r
852 def InitBuild(self):\r
853 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")\r
854 if ErrorCode != 0:\r
855 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
856\r
857 # create metafile database\r
858 self.Db.InitDatabase()\r
859\r
860 # we need information in platform description file to determine how to build\r
861 self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']\r
862 if not self.Fdf:\r
863 self.Fdf = self.Platform.FlashDefinition\r
864 \r
865 LoadFixAddressString = None\r
866 if TAB_FIX_LOAD_TOP_MEMORY_ADDRESS in GlobalData.gGlobalDefines:\r
867 LoadFixAddressString = GlobalData.gGlobalDefines[TAB_FIX_LOAD_TOP_MEMORY_ADDRESS]\r
868 else:\r
869 LoadFixAddressString = self.Platform.LoadFixAddress\r
870\r
871 if LoadFixAddressString != None and LoadFixAddressString != '':\r
872 try:\r
873 if LoadFixAddressString.upper().startswith('0X'):\r
874 self.LoadFixAddress = int (LoadFixAddressString, 16)\r
875 else:\r
876 self.LoadFixAddress = int (LoadFixAddressString)\r
877 except:
878 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS %s is not valid dec or hex string" % (LoadFixAddressString))\r
879 if self.LoadFixAddress < 0:\r
880 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid negative value %s" % (LoadFixAddressString))\r
881 if self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress % 0x1000 != 0:\r
882 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid unaligned 4K value %s" % (LoadFixAddressString))\r
883\r
884 if self.SkuId == None or self.SkuId == '':\r
885 self.SkuId = self.Platform.SkuName\r
886\r
887 # check FD/FV build target\r
888 if self.Fdf == None or self.Fdf == "":\r
889 if self.FdList != []:\r
890 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
891 self.FdList = []\r
892 if self.FvList != []:\r
893 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
894 self.FvList = []\r
895 else:\r
896 FdfParserObj = FdfParser(str(self.Fdf))\r
897 FdfParserObj.ParseFile()\r
898 for fvname in self.FvList:\r
899 if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():\r
900 EdkLogger.error("build", OPTION_VALUE_INVALID,\r
901 "No such an FV in FDF file: %s" % fvname)\r
902\r
903 #\r
904 # Merge Arch\r
905 #\r
906 if self.ArchList == None or len(self.ArchList) == 0:\r
907 ArchList = set(self.Platform.SupArchList)\r
908 else:\r
909 ArchList = set(self.ArchList) & set(self.Platform.SupArchList)\r
910 if len(ArchList) == 0:\r
911 EdkLogger.error("build", PARAMETER_INVALID,\r
912 ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
913 % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
914 elif len(ArchList) != len(self.ArchList):\r
915 SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))\r
916 EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
917 % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
918 self.ArchList = tuple(ArchList)\r
919\r
920 # Merge build target\r
921 if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
922 BuildTargetList = self.Platform.BuildTargets\r
923 else:\r
924 BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))\r
925 if BuildTargetList == []:\r
926 EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
927 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))\r
928 self.BuildTargetList = BuildTargetList\r
929\r
930 ## Build a module or platform\r
931 #\r
932 # Create autogen code and makfile for a module or platform, and the launch\r
933 # "make" command to build it\r
934 #\r
935 # @param Target The target of build command\r
936 # @param Platform The platform file\r
937 # @param Module The module file\r
938 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"\r
939 # @param ToolChain The name of toolchain to build\r
940 # @param Arch The arch of the module/platform\r
941 # @param CreateDepModuleCodeFile Flag used to indicate creating code\r
942 # for dependent modules/Libraries\r
943 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile\r
944 # for dependent modules/Libraries\r
945 #\r
946 def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):\r
947 if AutoGenObject == None:\r
948 return False\r
949\r
950 # skip file generation for cleanxxx targets, run and fds target\r
951 if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
952 # for target which must generate AutoGen code and makefile\r
953 if not self.SkipAutoGen or Target == 'genc':\r
954 self.Progress.Start("Generating code")\r
955 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
956 self.Progress.Stop("done!")\r
957 if Target == "genc":\r
958 return True\r
959\r
960 if not self.SkipAutoGen or Target == 'genmake':\r
961 self.Progress.Start("Generating makefile")\r
962 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
963 self.Progress.Stop("done!")\r
964 if Target == "genmake":\r
965 return True\r
966 else:\r
967 # always recreate top/platform makefile when clean, just in case of inconsistency\r
968 AutoGenObject.CreateCodeFile(False)\r
969 AutoGenObject.CreateMakeFile(False)\r
970\r
971 if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
972 EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
973\r
974 BuildCommand = AutoGenObject.BuildCommand\r
975 if BuildCommand == None or len(BuildCommand) == 0:\r
976 EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
977\r
978 BuildCommand = BuildCommand + [Target]\r
979 LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
980 if Target == 'cleanall':\r
981 try:\r
982 #os.rmdir(AutoGenObject.BuildDir)\r
983 RemoveDirectory(AutoGenObject.BuildDir, True)\r
984 except WindowsError, X:\r
985 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
986 return True\r
987\r
988 ## Rebase module image and Get function address for the inpug module list.\r
989 #\r
990 def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
991 if ModeIsSmm:\r
992 AddrIsOffset = False\r
993 InfFileNameList = ModuleList.keys()\r
994 #InfFileNameList.sort()\r
995 for InfFile in InfFileNameList:\r
996 sys.stdout.write (".")
997 sys.stdout.flush()
998 ModuleInfo = ModuleList[InfFile]\r
999 ModuleName = ModuleInfo.BaseName\r
1000 ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1001 if not ModeIsSmm:\r
1002 BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
1003 #\r
1004 # Update Image to new BaseAddress by GenFw tool\r
1005 #\r
1006 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1007 else:\r
1008 #\r
1009 # Set new address to the section header only for SMM driver.\r
1010 #\r
1011 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1012 #\r
1013 # Collect funtion address from Map file\r
1014 #\r
1015 ImageMapTable = ModuleInfo.Image.FileName.replace('.efi', '.map')\r
1016 FunctionList = []\r
1017 if os.path.exists(ImageMapTable):\r
1018 OrigImageBaseAddress = 0\r
1019 ImageMap = open (ImageMapTable, 'r')\r
1020 for LinStr in ImageMap:\r
1021 if len (LinStr.strip()) == 0:\r
1022 continue\r
1023 #\r
1024 # Get the preferred address set on link time.\r
1025 #\r
1026 if LinStr.find ('Preferred load address is') != -1:\r
1027 StrList = LinStr.split()\r
1028 OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)\r
1029\r
1030 StrList = LinStr.split()\r
1031 if len (StrList) > 4:\r
1032 if StrList[3] == 'f' or StrList[3] =='F':\r
1033 Name = StrList[1]\r
1034 RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress\r
1035 FunctionList.append ((Name, RelativeAddress))\r
1036 if ModuleInfo.Arch == 'IPF' and Name.endswith('_ModuleEntryPoint'):\r
1037 #\r
1038 # Get the real entry point address for IPF image.\r
1039 #\r
1040 ModuleInfo.Image.EntryPoint = RelativeAddress\r
1041 ImageMap.close()\r
1042 #\r
1043 # Add general information.\r
1044 #\r
1045 if ModeIsSmm:\r
1046 MapBuffer.write('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1047 elif AddrIsOffset:\r
1048 MapBuffer.write('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
1049 else:\r
1050 MapBuffer.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1051 #\r
1052 # Add guid and general seciton section.\r
1053 #\r
1054 TextSectionAddress = 0\r
1055 DataSectionAddress = 0\r
1056 for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
1057 if SectionHeader[0] == '.text':\r
1058 TextSectionAddress = SectionHeader[1]\r
1059 elif SectionHeader[0] in ['.data', '.sdata']:\r
1060 DataSectionAddress = SectionHeader[1]\r
1061 if AddrIsOffset:\r
1062 MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress))) \r
1063 else:\r
1064 MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress)) \r
1065 #\r
1066 # Add funtion address\r
1067 #\r
1068 for Function in FunctionList:\r
1069 if AddrIsOffset:\r
1070 MapBuffer.write(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))\r
1071 else:\r
1072 MapBuffer.write(' 0x%010X %s\n' % (BaseAddress + Function[1], Function[0]))\r
1073 ImageMap.close()\r
1074\r
1075 #\r
1076 # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1077 #\r
1078 if ModeIsSmm:\r
1079 BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
1080\r
1081 ## Collect MAP information of all FVs\r
1082 #\r
1083 def _CollectFvMapBuffer (self, MapBuffer, Wa):\r
1084 if self.Fdf != '':\r
1085 # First get the XIP base address for FV map file.\r
1086 for FvName in Wa.FdfProfile.FvDict.keys():\r
1087 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1088 if not os.path.exists(FvMapBuffer):\r
1089 continue\r
1090 FvMap = open (FvMapBuffer, 'r')\r
1091 #skip FV size information\r
1092 FvMap.readline()\r
1093 FvMap.readline()\r
1094 FvMap.readline()\r
1095 FvMap.readline()\r
1096 MapBuffer.write(FvMap.read())\r
1097 FvMap.close()\r
1098\r
1099 ## Collect MAP information of all modules\r
1100 #\r
1101 def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1102 sys.stdout.write ("Generate Load Module At Fix Address Map")
1103 sys.stdout.flush()
1104 PatchEfiImageList = []\r
1105 PeiModuleList = {}\r
1106 BtModuleList = {}\r
1107 RtModuleList = {}\r
1108 SmmModuleList = {}\r
1109 PeiSize = 0\r
1110 BtSize = 0\r
1111 RtSize = 0\r
1112 # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1113 SmmSize = 0x1000\r
1114 IsIpfPlatform = False\r
1115 if 'IPF' in self.ArchList:\r
1116 IsIpfPlatform = True\r
1117 for Module in ModuleList:\r
1118 GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1119 \r
1120 OutputImageFile = ''\r
1121 for ResultFile in Module.CodaTargetList:\r
1122 if str(ResultFile.Target).endswith('.efi'):\r
1123 #\r
1124 # module list for PEI, DXE, RUNTIME and SMM\r
1125 #\r
1126 OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1127 ImageClass = PeImageClass (OutputImageFile)\r
1128 if not ImageClass.IsValid:\r
1129 EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1130 ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, ImageClass)\r
1131 if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']:\r
1132 PeiModuleList[Module.MetaFile] = ImageInfo\r
1133 PeiSize += ImageInfo.Image.Size\r
1134 elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']:\r
1135 BtModuleList[Module.MetaFile] = ImageInfo\r
1136 BtSize += ImageInfo.Image.Size\r
1137 elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']:\r
1138 RtModuleList[Module.MetaFile] = ImageInfo\r
1139 #IPF runtime driver needs to be at 2 page alignment.\r
1140 if IsIpfPlatform and ImageInfo.Image.Size % 0x2000 != 0:\r
1141 ImageInfo.Image.Size = (ImageInfo.Image.Size / 0x2000 + 1) * 0x2000\r
1142 RtSize += ImageInfo.Image.Size\r
1143 elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']:\r
1144 SmmModuleList[Module.MetaFile] = ImageInfo\r
1145 SmmSize += ImageInfo.Image.Size\r
1146 if Module.ModuleType == 'DXE_SMM_DRIVER':\r
1147 PiSpecVersion = 0
1148 if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification:
1149 PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION']
1150 # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1151 if PiSpecVersion < 0x0001000A:\r
1152 BtModuleList[Module.MetaFile] = ImageInfo\r
1153 BtSize += ImageInfo.Image.Size\r
1154 break\r
1155 #\r
1156 # EFI image is final target.\r
1157 # Check EFI image contains patchable FixAddress related PCDs.\r
1158 #\r
1159 if OutputImageFile != '':\r
1160 ModuleIsPatch = False\r
1161 for Pcd in Module.ModulePcdList:\r
1162 if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1163 ModuleIsPatch = True\r
1164 break\r
1165 if not ModuleIsPatch:\r
1166 for Pcd in Module.LibraryPcdList:\r
1167 if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1168 ModuleIsPatch = True\r
1169 break\r
1170 \r
1171 if not ModuleIsPatch:\r
1172 continue\r
1173 #\r
1174 # Module includes the patchable load fix address PCDs.\r
1175 # It will be fixed up later. \r
1176 #\r
1177 PatchEfiImageList.append (OutputImageFile)\r
1178 \r
1179 #\r
1180 # Get Top Memory address\r
1181 #\r
1182 ReservedRuntimeMemorySize = 0\r
1183 TopMemoryAddress = 0\r
1184 if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1185 TopMemoryAddress = 0\r
1186 else:\r
1187 TopMemoryAddress = self.LoadFixAddress\r
1188 if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1189 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")\r
1190 # Make IPF runtime driver at 2 page alignment.\r
1191 if IsIpfPlatform:\r
1192 ReservedRuntimeMemorySize = TopMemoryAddress % 0x2000\r
1193 RtSize = RtSize + ReservedRuntimeMemorySize\r
1194\r
1195 #\r
1196 # Patch FixAddress related PCDs into EFI image\r
1197 #\r
1198 for EfiImage in PatchEfiImageList: \r
1199 EfiImageMap = EfiImage.replace('.efi', '.map')\r
1200 if not os.path.exists(EfiImageMap):\r
1201 continue\r
1202 #\r
1203 # Get PCD offset in EFI image by GenPatchPcdTable function\r
1204 #\r
1205 PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage)
1206 #\r
1207 # Patch real PCD value by PatchPcdValue tool\r
1208 #\r
1209 for PcdInfo in PcdTable:\r
1210 ReturnValue = 0\r
1211 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1212 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize/0x1000))\r
1213 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1214 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize/0x1000))\r
1215 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1216 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize/0x1000))\r
1217 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:\r
1218 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize/0x1000))\r
1219 if ReturnValue != 0:\r
1220 EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1221 \r
1222 MapBuffer.write('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize/0x1000))\r
1223 MapBuffer.write('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize/0x1000))\r
1224 MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize/0x1000))\r
1225 if len (SmmModuleList) > 0:\r
1226 MapBuffer.write('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize/0x1000))\r
1227 \r
1228 PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1229 BtBaseAddr = TopMemoryAddress - RtSize\r
1230 RtBaseAddr = TopMemoryAddress - ReservedRuntimeMemorySize\r
1231\r
1232 self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1233 self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1234 self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1235 self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset = False, ModeIsSmm = True)\r
1236 MapBuffer.write('\n\n')\r
1237 sys.stdout.write ("\n")
1238 sys.stdout.flush()
1239 \r
1240 ## Save platform Map file\r
1241 #\r
1242 def _SaveMapFile (self, MapBuffer, Wa):\r
1243 #\r
1244 # Map file path is got.\r
1245 #\r
1246 MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1247 #\r
1248 # Save address map into MAP file.\r
1249 #\r
1250 SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)\r
1251 MapBuffer.close()\r
1252 sys.stdout.write ("\nLoad Module At Fix Address Map file saved to %s\n" %(MapFilePath))
1253 sys.stdout.flush()
1254\r
1255 ## Build active platform for different build targets and different tool chains\r
1256 #\r
1257 def _BuildPlatform(self):\r
1258 for BuildTarget in self.BuildTargetList:\r
1259 for ToolChain in self.ToolChainList:\r
1260 Wa = WorkspaceAutoGen(\r
1261 self.WorkspaceDir,\r
1262 self.Platform,\r
1263 BuildTarget,\r
1264 ToolChain,\r
1265 self.ArchList,\r
1266 self.BuildDatabase,\r
1267 self.TargetTxt,\r
1268 self.ToolDef,\r
1269 self.Fdf,\r
1270 self.FdList,\r
1271 self.FvList,\r
1272 self.SkuId\r
1273 )\r
1274 self.BuildReport.AddPlatformReport(Wa)\r
1275 self.Progress.Stop("done!")\r
1276 self._Build(self.Target, Wa)\r
1277 \r
1278 # Create MAP file when Load Fix Address is enabled.\r
1279 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1280 for Arch in self.ArchList:\r
1281 #\r
1282 # Check whether the set fix address is above 4G for 32bit image.\r
1283 #\r
1284 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1285 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")\r
1286 #\r
1287 # Get Module List\r
1288 #\r
1289 ModuleList = []\r
1290 for Pa in Wa.AutoGenObjectList:\r
1291 for Ma in Pa.ModuleAutoGenList:\r
1292 if Ma == None:\r
1293 continue\r
1294 if not Ma.IsLibrary:\r
1295 ModuleList.append (Ma)\r
1296\r
1297 MapBuffer = StringIO('')\r
1298 #\r
1299 # Rebase module to the preferred memory address before GenFds\r
1300 #\r
1301 self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1302 if self.Fdf != '':\r
1303 #\r
1304 # create FDS again for the updated EFI image\r
1305 #\r
1306 self._Build("fds", Wa)\r
1307 #\r
1308 # Create MAP file for all platform FVs after GenFds.\r
1309 #\r
1310 self._CollectFvMapBuffer(MapBuffer, Wa)\r
1311 #\r
1312 # Save MAP buffer into MAP file.\r
1313 #\r
1314 self._SaveMapFile (MapBuffer, Wa)\r
1315\r
1316 ## Build active module for different build targets, different tool chains and different archs\r
1317 #\r
1318 def _BuildModule(self):\r
1319 for BuildTarget in self.BuildTargetList:\r
1320 for ToolChain in self.ToolChainList:\r
1321 #\r
1322 # module build needs platform build information, so get platform\r
1323 # AutoGen first\r
1324 #\r
1325 Wa = WorkspaceAutoGen(\r
1326 self.WorkspaceDir,\r
1327 self.Platform,\r
1328 BuildTarget,\r
1329 ToolChain,\r
1330 self.ArchList,\r
1331 self.BuildDatabase,\r
1332 self.TargetTxt,\r
1333 self.ToolDef,\r
1334 self.Fdf,\r
1335 self.FdList,\r
1336 self.FvList,\r
1337 self.SkuId\r
1338 )\r
1339 self.BuildReport.AddPlatformReport(Wa)\r
1340 Wa.CreateMakeFile(False)\r
1341 self.Progress.Stop("done!")\r
1342 MaList = []\r
1343 for Arch in self.ArchList:\r
1344 Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1345 if Ma == None: continue\r
1346 MaList.append(Ma)\r
1347 self._Build(self.Target, Ma)\r
1348 if MaList == []:\r
1349 EdkLogger.error(\r
1350 'build',\r
1351 BUILD_ERROR,\r
1352 "Module for [%s] is not a component of active platform."\\r
1353 " Please make sure that the ARCH and inf file path are"\\r
1354 " given in the same as in [%s]" %\\r
1355 (', '.join(self.ArchList), self.Platform),\r
1356 ExtraData=self.ModuleFile\r
1357 )\r
1358 # Create MAP file when Load Fix Address is enabled.\r
1359 if self.LoadFixAddress != 0 and self.Target == "fds" and self.Fdf != '':\r
1360 for Arch in self.ArchList:\r
1361 #\r
1362 # Check whether the set fix address is above 4G for 32bit image.\r
1363 #\r
1364 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1365 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")\r
1366 #\r
1367 # Get Module List\r
1368 #\r
1369 ModuleList = []\r
1370 for Pa in Wa.AutoGenObjectList:\r
1371 for Ma in Pa.ModuleAutoGenList:\r
1372 if Ma == None:\r
1373 continue\r
1374 if not Ma.IsLibrary:\r
1375 ModuleList.append (Ma)\r
1376\r
1377 MapBuffer = StringIO('')\r
1378 #\r
1379 # Rebase module to the preferred memory address before GenFds\r
1380 #\r
1381 self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1382 #\r
1383 # create FDS again for the updated EFI image\r
1384 #\r
1385 self._Build("fds", Wa)\r
1386 #\r
1387 # Create MAP file for all platform FVs after GenFds.\r
1388 #\r
1389 self._CollectFvMapBuffer(MapBuffer, Wa)\r
1390 #\r
1391 # Save MAP buffer into MAP file.\r
1392 #\r
1393 self._SaveMapFile (MapBuffer, Wa)\r
1394\r
1395 ## Build a platform in multi-thread mode\r
1396 #\r
1397 def _MultiThreadBuildPlatform(self):\r
1398 for BuildTarget in self.BuildTargetList:\r
1399 for ToolChain in self.ToolChainList:\r
1400 Wa = WorkspaceAutoGen(\r
1401 self.WorkspaceDir,\r
1402 self.Platform,\r
1403 BuildTarget,\r
1404 ToolChain,\r
1405 self.ArchList,\r
1406 self.BuildDatabase,\r
1407 self.TargetTxt,\r
1408 self.ToolDef,\r
1409 self.Fdf,\r
1410 self.FdList,\r
1411 self.FvList,\r
1412 self.SkuId\r
1413 )\r
1414 self.BuildReport.AddPlatformReport(Wa)\r
1415 Wa.CreateMakeFile(False)\r
1416\r
1417 # multi-thread exit flag\r
1418 ExitFlag = threading.Event()\r
1419 ExitFlag.clear()\r
1420 for Arch in self.ArchList:\r
1421 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1422 if Pa == None:\r
1423 continue\r
1424 for Module in Pa.Platform.Modules:\r
1425 # Get ModuleAutoGen object to generate C code file and makefile\r
1426 Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1427 if Ma == None:\r
1428 continue\r
1429 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1430 if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1431 # for target which must generate AutoGen code and makefile\r
1432 if not self.SkipAutoGen or self.Target == 'genc':\r
1433 Ma.CreateCodeFile(True)\r
1434 if self.Target == "genc":\r
1435 continue\r
1436\r
1437 if not self.SkipAutoGen or self.Target == 'genmake':\r
1438 Ma.CreateMakeFile(True)\r
1439 if self.Target == "genmake":\r
1440 continue\r
1441 self.Progress.Stop("done!")\r
1442 # Generate build task for the module\r
1443 Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1444 # Break build if any build thread has error\r
1445 if BuildTask.HasError():\r
1446 # we need a full version of makefile for platform\r
1447 ExitFlag.set()\r
1448 BuildTask.WaitForComplete()\r
1449 Pa.CreateMakeFile(False)\r
1450 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1451 # Start task scheduler\r
1452 if not BuildTask.IsOnGoing():\r
1453 BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1454\r
1455 # in case there's an interruption. we need a full version of makefile for platform\r
1456 Pa.CreateMakeFile(False)\r
1457 if BuildTask.HasError():\r
1458 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1459\r
1460 #\r
1461 # All modules have been put in build tasks queue. Tell task scheduler\r
1462 # to exit if all tasks are completed\r
1463 #\r
1464 ExitFlag.set()\r
1465 BuildTask.WaitForComplete()\r
1466\r
1467 #\r
1468 # Check for build error, and raise exception if one\r
1469 # has been signaled.\r
1470 #\r
1471 if BuildTask.HasError():\r
1472 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1473\r
1474 # Create MAP file when Load Fix Address is enabled.\r
1475 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1476 for Arch in self.ArchList:\r
1477 #\r
1478 # Check whether the set fix address is above 4G for 32bit image.\r
1479 #\r
1480 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1481 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")\r
1482 #\r
1483 # Get Module List\r
1484 #\r
1485 ModuleList = []\r
1486 for Pa in Wa.AutoGenObjectList:\r
1487 for Ma in Pa.ModuleAutoGenList:\r
1488 if Ma == None:\r
1489 continue\r
1490 if not Ma.IsLibrary:\r
1491 ModuleList.append (Ma)\r
1492 #\r
1493 # Rebase module to the preferred memory address before GenFds\r
1494 #\r
1495 MapBuffer = StringIO('')\r
1496 self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1497\r
1498 # Generate FD image if there's a FDF file found\r
1499 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1500 LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)\r
1501\r
1502 # Create MAP file for all platform FV after GenFds\r
1503 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1504 if self.Fdf != '':\r
1505 #\r
1506 # Create MAP file for all platform FVs after GenFds.\r
1507 #\r
1508 self._CollectFvMapBuffer(MapBuffer, Wa)\r
1509 #\r
1510 # Save MAP buffer into MAP file.\r
1511 #\r
1512 self._SaveMapFile(MapBuffer, Wa)\r
1513\r
1514 ## Generate GuidedSectionTools.txt in the FV directories.\r
1515 #\r
1516 def CreateGuidedSectionToolsFile(self):\r
1517 for Arch in self.ArchList:\r
1518 for BuildTarget in self.BuildTargetList:\r
1519 for ToolChain in self.ToolChainList:\r
1520 FvDir = os.path.join(\r
1521 self.WorkspaceDir,\r
1522 self.Platform.OutputDirectory,\r
1523 '_'.join((BuildTarget, ToolChain)),\r
1524 'FV'\r
1525 )\r
1526 if not os.path.exists(FvDir):\r
1527 continue\r
1528 # Build up the list of supported architectures for this build\r
1529 prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1530\r
1531 # Look through the tool definitions for GUIDed tools\r
1532 guidAttribs = []\r
1533 for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1534 if attrib.upper().endswith('_GUID'):\r
1535 split = attrib.split('_')\r
1536 thisPrefix = '_'.join(split[0:3]) + '_'\r
1537 if thisPrefix == prefix:\r
1538 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1539 guid = guid.lower()\r
1540 toolName = split[3]\r
1541 path = '_'.join(split[0:4]) + '_PATH'\r
1542 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1543 path = self.GetFullPathOfTool(path)\r
1544 guidAttribs.append((guid, toolName, path))\r
1545\r
1546 # Write out GuidedSecTools.txt\r
1547 toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1548 toolsFile = open(toolsFile, 'wt')\r
1549 for guidedSectionTool in guidAttribs:\r
1550 print >> toolsFile, ' '.join(guidedSectionTool)\r
1551 toolsFile.close()\r
1552\r
1553 ## Returns the full path of the tool.\r
1554 #\r
1555 def GetFullPathOfTool (self, tool):\r
1556 if os.path.exists(tool):\r
1557 return os.path.realpath(tool)\r
1558 else:\r
1559 # We need to search for the tool using the\r
1560 # PATH environment variable.\r
1561 for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1562 foundPath = os.path.join(dirInPath, tool)\r
1563 if os.path.exists(foundPath):\r
1564 return os.path.realpath(foundPath)\r
1565\r
1566 # If the tool was not found in the path then we just return\r
1567 # the input tool.\r
1568 return tool\r
1569\r
1570 ## Launch the module or platform build\r
1571 #\r
1572 def Launch(self):\r
1573 if self.ModuleFile == None or self.ModuleFile == "":\r
1574 if not self.SpawnMode or self.Target not in ["", "all"]:\r
1575 self.SpawnMode = False\r
1576 self._BuildPlatform()\r
1577 else:\r
1578 self._MultiThreadBuildPlatform()\r
1579 self.CreateGuidedSectionToolsFile()\r
1580 else:\r
1581 self.SpawnMode = False\r
1582 self._BuildModule()\r
1583\r
1584 ## Do some clean-up works when error occurred\r
1585 def Relinquish(self):\r
1586 OldLogLevel = EdkLogger.GetLevel()\r
1587 EdkLogger.SetLevel(EdkLogger.ERROR)\r
1588 #self.DumpBuildData()\r
1589 Utils.Progressor.Abort()\r
1590 if self.SpawnMode == True:\r
1591 BuildTask.Abort()\r
1592 EdkLogger.SetLevel(OldLogLevel)\r
1593\r
1594 def DumpBuildData(self):\r
1595 CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1596 Utils.CreateDirectory(CacheDirectory)\r
1597 Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1598 Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1599\r
1600 def RestoreBuildData(self):\r
1601 FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1602 if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1603 Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1604 if Utils.gFileTimeStampCache == None:\r
1605 Utils.gFileTimeStampCache = {}\r
1606\r
1607 FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1608 if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1609 Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1610 if Utils.gDependencyDatabase == None:\r
1611 Utils.gDependencyDatabase = {}\r
1612\r
1613def ParseDefines(DefineList=[]):\r
1614 DefineDict = {}\r
1615 if DefineList != None:\r
1616 for Define in DefineList:\r
1617 DefineTokenList = Define.split("=", 1)\r
1618 if len(DefineTokenList) == 1:\r
1619 DefineDict[DefineTokenList[0]] = ""\r
1620 else:\r
1621 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1622 return DefineDict\r
1623\r
1624gParamCheck = []\r
1625def SingleCheckCallback(option, opt_str, value, parser):\r
1626 if option not in gParamCheck:\r
1627 setattr(parser.values, option.dest, value)\r
1628 gParamCheck.append(option)\r
1629 else:\r
1630 parser.error("Option %s only allows one instance in command line!" % option)\r
1631\r
1632## Parse command line options\r
1633#\r
1634# Using standard Python module optparse to parse command line option of this tool.\r
1635#\r
1636# @retval Opt A optparse.Values object containing the parsed options\r
1637# @retval Args Target of build command\r
1638#\r
1639def MyOptionParser():\r
1640 Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
1641 Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",\r
1642 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.")\r
1643 Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
1644 help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1645 Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
1646 help="Build the module specified by the INF file name argument.")\r
1647 Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1648 help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1649 Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1650 help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1651 Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
1652 help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1653\r
1654 Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
1655 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.")\r
1656\r
1657 Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
1658 help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1659 Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1660 help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1661 Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1662 help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1663\r
1664 Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1665 Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1666\r
1667 Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")\r
1668\r
1669 # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1670 # help="Define global macro which can be used in DSC/DEC/INF files.")\r
1671\r
1672 Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1673 Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
1674\r
1675 Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
1676 help="Make use of silent mode of (n)make.")\r
1677 Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1678 Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1679 "including library instances selected, final dependency expression, "\\r
1680 "and warning messages, etc.")\r
1681 Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1682 Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
1683\r
1684 Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
1685 Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','FIXED_ADDRESS', 'EXECUTION_ORDER'], dest="ReportType", default=[],\r
1686 help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, EXECUTION_ORDER]. "\\r
1687 "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS]")\r
1688\r
1689 (Opt, Args)=Parser.parse_args()\r
1690 return (Opt, Args)\r
1691\r
1692## Tool entrance method\r
1693#\r
1694# This method mainly dispatch specific methods per the command line options.\r
1695# If no error found, return zero value so the caller of this tool can know\r
1696# if it's executed successfully or not.\r
1697#\r
1698# @retval 0 Tool was successful\r
1699# @retval 1 Tool failed\r
1700#\r
1701def Main():\r
1702 StartTime = time.time()\r
1703\r
1704 # Initialize log system\r
1705 EdkLogger.Initialize()\r
1706\r
1707 #\r
1708 # Parse the options and args\r
1709 #\r
1710 (Option, Target) = MyOptionParser()\r
1711 GlobalData.gOptions = Option\r
1712 GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
1713\r
1714 # Set log level\r
1715 if Option.verbose != None:\r
1716 EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1717 elif Option.quiet != None:\r
1718 EdkLogger.SetLevel(EdkLogger.QUIET)\r
1719 elif Option.debug != None:\r
1720 EdkLogger.SetLevel(Option.debug + 1)\r
1721 else:\r
1722 EdkLogger.SetLevel(EdkLogger.INFO)\r
1723\r
1724 if Option.LogFile != None:\r
1725 EdkLogger.SetLogFile(Option.LogFile)\r
1726\r
1727 if Option.WarningAsError == True:\r
1728 EdkLogger.SetWarningAsError()\r
1729\r
1730 if platform.platform().find("Windows") >= 0:\r
1731 GlobalData.gIsWindows = True\r
1732 else:\r
1733 GlobalData.gIsWindows = False\r
1734\r
1735 EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())\r
1736 ReturnCode = 0\r
1737 MyBuild = None\r
1738 try:\r
1739 if len(Target) == 0:\r
1740 Target = "all"\r
1741 elif len(Target) >= 2:\r
1742 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
1743 ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1744 else:\r
1745 Target = Target[0].lower()\r
1746\r
1747 if Target not in gSupportedTarget:\r
1748 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1749 ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1750\r
1751 GlobalData.gGlobalDefines = ParseDefines(Option.Macros)\r
1752 #\r
1753 # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1754 #\r
1755 CheckEnvVariable()\r
1756 Workspace = os.getenv("WORKSPACE")\r
1757 #\r
1758 # Get files real name in workspace dir\r
1759 #\r
1760 GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
1761\r
1762 WorkingDirectory = os.getcwd()\r
1763 if not Option.ModuleFile:\r
1764 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1765 FileNum = len(FileList)\r
1766 if FileNum >= 2:\r
1767 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
1768 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
1769 elif FileNum == 1:\r
1770 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1771\r
1772 if Option.ModuleFile:\r
1773 if os.path.isabs (Option.ModuleFile):\r
1774 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
1775 Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
1776 Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
1777 ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
1778 if ErrorCode != 0:\r
1779 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1780\r
1781 if Option.PlatformFile != None:\r
1782 if os.path.isabs (Option.PlatformFile):\r
1783 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
1784 Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
1785 Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
1786 ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)\r
1787 if ErrorCode != 0:\r
1788 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1789\r
1790 if Option.FdfFile != None:\r
1791 if os.path.isabs (Option.FdfFile):\r
1792 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
1793 Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
1794 Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
1795 ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
1796 if ErrorCode != 0:\r
1797 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1798\r
1799 MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,\r
1800 Option.TargetArch, Option.ToolChain, Option.BuildTarget,\r
1801 Option.FdfFile, Option.RomImage, Option.FvImage,\r
1802 None, Option.SilentMode, Option.ThreadNumber,\r
1803 Option.SkipAutoGen, Option.Reparse, Option.SkuId, \r
1804 Option.ReportFile, Option.ReportType)\r
1805 MyBuild.Launch()\r
1806 #MyBuild.DumpBuildData()\r
1807 except FatalError, X:\r
1808 if MyBuild != None:\r
1809 # for multi-thread build exits safely\r
1810 MyBuild.Relinquish()\r
1811 if Option != None and Option.debug != None:\r
1812 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1813 ReturnCode = X.args[0]\r
1814 except Warning, X:\r
1815 # error from Fdf parser\r
1816 if MyBuild != None:\r
1817 # for multi-thread build exits safely\r
1818 MyBuild.Relinquish()\r
1819 if Option != None and Option.debug != None:\r
1820 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1821 else:\r
1822 EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)\r
1823 ReturnCode = FORMAT_INVALID\r
1824 except KeyboardInterrupt:\r
1825 ReturnCode = ABORT_ERROR\r
1826 if Option != None and Option.debug != None:\r
1827 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1828 except:\r
1829 if MyBuild != None:\r
1830 # for multi-thread build exits safely\r
1831 MyBuild.Relinquish()\r
1832\r
1833 # try to get the meta-file from the object causing exception\r
1834 Tb = sys.exc_info()[-1]\r
1835 MetaFile = GlobalData.gProcessingFile\r
1836 while Tb != None:\r
1837 if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
1838 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
1839 Tb = Tb.tb_next\r
1840 EdkLogger.error(\r
1841 "\nbuild",\r
1842 CODE_ERROR,\r
1843 "Unknown fatal error when processing [%s]" % MetaFile,\r
1844 ExtraData="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n",\r
1845 RaiseError=False\r
1846 )\r
1847 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1848 ReturnCode = CODE_ERROR\r
1849 finally:\r
1850 Utils.Progressor.Abort()\r
1851\r
1852 if ReturnCode == 0:\r
1853 Conclusion = "Done"\r
1854 elif ReturnCode == ABORT_ERROR:\r
1855 Conclusion = "Aborted"\r
1856 else:\r
1857 Conclusion = "Failed"\r
1858 FinishTime = time.time()\r
1859 BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1860 if MyBuild != None:\r
1861 MyBuild.BuildReport.GenerateReport(BuildDuration)\r
1862 MyBuild.Db.Close()\r
1863 EdkLogger.SetLevel(EdkLogger.QUIET)\r
1864 EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1865\r
1866 return ReturnCode\r
1867\r
1868if __name__ == '__main__':\r
1869 r = Main()\r
1870 ## 0-127 is a safe return range, and 1 is a standard default error\r
1871 if r < 0 or r > 127: r = 1\r
1872 sys.exit(r)\r
1873\r