4af638f0358cefffc3e935beee35045c7bff7a59
[people/mcb30/basetools.git] / Source / Python / build / build.py
1 ## @file\r
2 # build a platform or a module\r
3 #\r
4 #  Copyright (c) 2007, 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
18 import os\r
19 import re\r
20 import sys\r
21 import glob\r
22 import time\r
23 import traceback\r
24 \r
25 from threading import *\r
26 from optparse import OptionParser\r
27 from subprocess import *\r
28 from Common import Misc as Utils\r
29 \r
30 from Common.TargetTxtClassObject import *\r
31 from Common.ToolDefClassObject import *\r
32 from Common.DataType import *\r
33 from AutoGen.AutoGen import *\r
34 from GenFds.FdfParser import *\r
35 from Common.BuildToolError import *\r
36 from Workspace.WorkspaceBuild import *\r
37 \r
38 import Common.EdkLogger\r
39 import Common.GlobalData as GlobalData\r
40 \r
41 # Version and Copyright\r
42 VersionNumber = "0.1"\r
43 __version__ = "%prog Version " + VersionNumber\r
44 __copyright__ = "Copyright (c) 2007, Intel Corporation  All rights reserved."\r
45 \r
46 ## standard targets of build command\r
47 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
48 \r
49 ## build configuration file\r
50 gBuildConfiguration = "Conf/target.txt"\r
51 gBuildCacheDir = "Conf/.cache"\r
52 \r
53 ## Check environment PATH variable to make sure the specified tool is found\r
54 #\r
55 #   If the tool is found in the PATH, then True is returned\r
56 #   Otherwise, False is returned\r
57 #\r
58 def IsToolInPath(tool):\r
59     if os.environ.has_key('PATHEXT'):\r
60         extns = os.environ['PATHEXT'].split(os.path.pathsep)\r
61     else:\r
62         extns = ('',)\r
63     for pathDir in os.environ['PATH'].split(os.path.pathsep):\r
64         for ext in extns:\r
65             if os.path.exists(os.path.join(pathDir, tool + ext)):\r
66                 return True\r
67     return False\r
68 \r
69 ## Check environment variables\r
70 #\r
71 #  Check environment variables that must be set for build. Currently they are\r
72 #\r
73 #   WORKSPACE           The directory all packages/platforms start from\r
74 #   EDK_TOOLS_PATH      The directory contains all tools needed by the build\r
75 #   PATH                $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
76 #\r
77 #   If any of above environment variable is not set or has error, the build\r
78 #   will be broken.\r
79 #\r
80 def CheckEnvVariable():\r
81     # check WORKSPACE\r
82     if "WORKSPACE" not in os.environ:\r
83         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", \r
84                         ExtraData="WORKSPACE")\r
85 \r
86     WorkspaceDir = os.path.normpath(os.environ["WORKSPACE"])\r
87     if not os.path.exists(WorkspaceDir):\r
88         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="WORKSPACE = %s" % WorkspaceDir)\r
89     os.environ["WORKSPACE"] = WorkspaceDir\r
90 \r
91     #\r
92     # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP\r
93     # \r
94     os.environ["EDK_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)\r
95     if "EFI_SOURCE" not in os.environ:\r
96         os.environ["EFI_SOURCE"] = os.environ["EDK_SOURCE"]\r
97 \r
98     EfiSourceDir = os.path.normpath(os.environ["EFI_SOURCE"])\r
99     EdkSourceDir = os.path.normpath(os.environ["EDK_SOURCE"])\r
100     if not os.path.exists(EdkSourceDir):\r
101         EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir)\r
102     if not os.path.exists(EfiSourceDir):\r
103         EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir)\r
104 \r
105     # change absolute path to relative path to WORKSPACE\r
106     if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
107         EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE", \r
108                         ExtraData="WORKSPACE = %s\n    EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir))\r
109     if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
110         EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE", \r
111                         ExtraData="WORKSPACE = %s\n    EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir))\r
112     EfiSourceDir = EfiSourceDir[len(WorkspaceDir)+1:]\r
113     EdkSourceDir = EdkSourceDir[len(WorkspaceDir)+1:]\r
114 \r
115     # check EDK_TOOLS_PATH\r
116     if "EDK_TOOLS_PATH" not in os.environ:\r
117         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", \r
118                         ExtraData="EDK_TOOLS_PATH")\r
119 \r
120     # check PATH\r
121     if "PATH" not in os.environ:\r
122         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", \r
123                         ExtraData="PATH")\r
124 \r
125     PathString = os.environ["PATH"]\r
126     ToolPath = os.path.normpath(os.path.join(os.environ["EDK_TOOLS_PATH"], 'Bin', sys.platform.title()))\r
127 \r
128     if not IsToolInPath('build'):\r
129         os.environ['PATH'] = os.path.pathsep.join((os.environ['PATH'], ToolPath))\r
130 \r
131         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Please execute %s to set %s in environment variable: PATH!\n"\r
132                             % (os.path.normpath(os.path.join(PathString, 'edksetup.bat')), ToolPath))\r
133 \r
134     # for macro replacement in R9 DSC/DEC/INF file\r
135     GlobalData.gGlobalDefines["WORKSPACE"] = ""\r
136 \r
137     # for macro replacement in R8 INF file\r
138     GlobalData.gGlobalDefines["EFI_SOURCE"] = EfiSourceDir\r
139     GlobalData.gGlobalDefines["EDK_SOURCE"] = EdkSourceDir\r
140 \r
141 ## Get normalized file path\r
142 #\r
143 # Convert the path to be local format, and remove the WORKSPACE path at the\r
144 # beginning if the file path is given in full path.\r
145 #\r
146 # @param  FilePath      File path to be normalized\r
147 # @param  Workspace     Workspace path which the FilePath will be checked against\r
148 #\r
149 # @retval string        The normalized file path\r
150 #\r
151 def NormFile(FilePath, Workspace):\r
152     # check if the path is absolute or relative\r
153     if os.path.isabs(FilePath):\r
154         FileFullPath = os.path.normpath(FilePath)\r
155     else:\r
156         FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))\r
157 \r
158     # check if the file path exists or not\r
159     if not os.path.isfile(FileFullPath):\r
160         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)"  % FileFullPath)\r
161 \r
162     # remove workspace directory from the beginning part of the file path\r
163     if Workspace[-1] in ["\\", "/"]:\r
164         return FileFullPath[len(Workspace):]\r
165     else:\r
166         return FileFullPath[(len(Workspace) + 1):]\r
167 \r
168 ## Get the output of an external program\r
169 #\r
170 # This is the entrance method of thread reading output of an external program and\r
171 # putting them in STDOUT/STDERR of current program.\r
172 #\r
173 # @param  From      The stream message read from\r
174 # @param  To        The stream message put on\r
175 # @param  ExitFlag  The flag used to indicate stopping reading\r
176 #\r
177 def ReadMessage(From, To, ExitFlag):\r
178     while True:\r
179         # read one line a time\r
180         Line = From.readline()\r
181         # empty string means "end"\r
182         if Line != None and Line != "":\r
183             To(Line.rstrip())\r
184         else:\r
185             break\r
186         if ExitFlag.isSet():\r
187             break\r
188 \r
189 ## Launch an external program\r
190 #\r
191 # This method will call subprocess.Popen to execute an external program with\r
192 # given options in specified directory. Because of the dead-lock issue during\r
193 # redirecting output of the external program, threads are used to to do the\r
194 # redirection work.\r
195 #\r
196 # @param  Command               A list or string containing the call of the program\r
197 # @param  WorkingDir            The directory in which the program will be running\r
198 #\r
199 def LaunchCommand(Command, WorkingDir):\r
200     # if working directory doesn't exist, Popen() will raise an exception\r
201     if not os.path.isdir(WorkingDir):\r
202         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
203 \r
204     Proc = None\r
205     EndOfProcedure = None\r
206     try:\r
207         # launch the command\r
208         Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir)\r
209 \r
210         # launch two threads to read the STDOUT and STDERR\r
211         EndOfProcedure = Event()\r
212         EndOfProcedure.clear()\r
213         if Proc.stdout:\r
214             StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
215             StdOutThread.setName("STDOUT-Redirector")\r
216             StdOutThread.setDaemon(False)\r
217             StdOutThread.start()\r
218 \r
219         if Proc.stderr:\r
220             StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
221             StdErrThread.setName("STDERR-Redirector")\r
222             StdErrThread.setDaemon(False)\r
223             StdErrThread.start()\r
224 \r
225         # waiting for program exit\r
226         Proc.wait()\r
227     except: # in case of aborting\r
228         # terminate the threads redirecting the program output\r
229         if EndOfProcedure != None:\r
230             EndOfProcedure.set()\r
231         if Proc == None:\r
232             if type(Command) != type(""):\r
233                 Command = " ".join(Command)\r
234             EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
235 \r
236     if Proc.stdout:\r
237         StdOutThread.join()\r
238     if Proc.stderr:\r
239         StdErrThread.join()\r
240 \r
241     # check the return code of the program\r
242     if Proc.returncode != 0:\r
243         if type(Command) != type(""):\r
244             Command = " ".join(Command)\r
245         EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
246 \r
247 ## The smallest unit that can be built in multi-thread build mode\r
248 #\r
249 # This is the base class of build unit. The "Obj" parameter must provide\r
250 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
251 # missing build.\r
252 #\r
253 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
254 #\r
255 class BuildUnit:\r
256     ## The constructor\r
257     #\r
258     #   @param  self        The object pointer\r
259     #   @param  Obj         The object the build is working on\r
260     #   @param  Target      The build target name, one of gSupportedTarget\r
261     #   @param  Dependency  The BuildUnit(s) which must be completed in advance\r
262     #   @param  WorkingDir  The directory build command starts in\r
263     #\r
264     def __init__(self, Obj, BuildComamnd, Target, Dependency, WorkingDir="."):\r
265         self.BuildObject = Obj\r
266         self.Dependency = Dependency\r
267         self.WorkingDir = WorkingDir\r
268         self.Target = Target\r
269         self.BuildCommand = BuildComamnd\r
270 \r
271     ## str() method\r
272     #\r
273     #   It just returns the string representaion of self.BuildObject\r
274     #\r
275     #   @param  self        The object pointer\r
276     #\r
277     def __str__(self):\r
278         return str(self.BuildObject)\r
279 \r
280     ## "==" operator method\r
281     #\r
282     #   It just compares self.BuildObject with "Other". So self.BuildObject must\r
283     #   provide its own __eq__() method.\r
284     #\r
285     #   @param  self        The object pointer\r
286     #   @param  Other       The other BuildUnit object compared to\r
287     #\r
288     def __eq__(self, Other):\r
289         return Other != None and self.BuildObject == Other.BuildObject\r
290 \r
291     ## hash() method\r
292     #\r
293     #   It just returns the hash value of self.BuildObject which must be hashable.\r
294     #\r
295     #   @param  self        The object pointer\r
296     #\r
297     def __hash__(self):\r
298         return hash(self.BuildObject)\r
299 \r
300 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
301 #\r
302 # This class is for module build by nmake/make build system. The "Obj" parameter\r
303 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
304 # be make units missing build.\r
305 #\r
306 # Currently the "Obj" should be only ModuleAutoGen object.\r
307 #\r
308 class ModuleMakeUnit(BuildUnit):\r
309     ## The constructor\r
310     #\r
311     #   @param  self        The object pointer\r
312     #   @param  Obj         The ModuleAutoGen object the build is working on\r
313     #   @param  Target      The build target name, one of gSupportedTarget\r
314     #\r
315     def __init__(self, Obj, Target):\r
316         Dependency = [ModuleMakeUnit(La, Target) for La in Obj.BuildInfo.LibraryAutoGenList]\r
317         BuildUnit.__init__(self, Obj, Obj.GetBuildCommand(), Target, Dependency, Obj.GetMakeFileDir())\r
318         if Target in [None, "", "all"]:\r
319             self.Target = "pbuild"\r
320 \r
321 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
322 #\r
323 # This class is for platform build by nmake/make build system. The "Obj" parameter\r
324 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
325 # be make units missing build.\r
326 #\r
327 # Currently the "Obj" should be only PlatformAutoGen object.\r
328 #\r
329 class PlatformMakeUnit(BuildUnit):\r
330     ## The constructor\r
331     #\r
332     #   @param  self        The object pointer\r
333     #   @param  Obj         The PlatformAutoGen object the build is working on\r
334     #   @param  Target      The build target name, one of gSupportedTarget\r
335     #\r
336     def __init__(self, Obj, Target):\r
337         Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.BuildInfo.LibraryAutoGenList]\r
338         Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.BuildInfo.ModuleAutoGenList])\r
339         BuildUnit.__init__(self, Obj, Obj.GetBuildCommand(), Target, Dependency, Obj.GetMakeFileDir())\r
340 \r
341 ## The class representing the task of a module build or platform build\r
342 #\r
343 # This class manages the build tasks in multi-thread build mode. Its jobs include\r
344 # scheduling thread running, catching thread error, monitor the thread status, etc.\r
345 #\r
346 class BuildTask:\r
347     # queue for tasks waiting for schedule\r
348     _PendingQueue = sdict()\r
349     _PendingQueueLock = threading.Lock()\r
350 \r
351     # queue for tasks ready for running\r
352     _ReadyQueue = sdict()\r
353     _ReadyQueueLock = threading.Lock()\r
354 \r
355     # queue for run tasks\r
356     _RunningQueue = sdict()\r
357     _RunningQueueLock = threading.Lock()\r
358 \r
359     # queue containing all build tasks, in case duplicate build\r
360     _TaskQueue = sdict()\r
361 \r
362     # flag indicating error occurs in a running thread\r
363     _ErrorFlag = threading.Event()\r
364     _ErrorFlag.clear()\r
365     _ErrorMessage = ""\r
366 \r
367     # BoundedSemaphore object used to control the number of running threads\r
368     _Thread = None\r
369 \r
370     # flag indicating if the scheduler is started or not\r
371     _SchedulerStopped = threading.Event()\r
372     _SchedulerStopped.set()\r
373 \r
374     ## Start the task scheduler thread\r
375     #\r
376     #   @param  MaxThreadNumber     The maximum thread number\r
377     #   @param  ExitFlag            Flag used to end the scheduler\r
378     #\r
379     @staticmethod\r
380     def StartScheduler(MaxThreadNumber, ExitFlag):\r
381         SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
382         SchedulerThread.setName("Build-Task-Scheduler")\r
383         SchedulerThread.setDaemon(False)\r
384         SchedulerThread.start()\r
385 \r
386     ## Scheduler method\r
387     #\r
388     #   @param  MaxThreadNumber     The maximum thread number\r
389     #   @param  ExitFlag            Flag used to end the scheduler\r
390     #\r
391     @staticmethod\r
392     def Scheduler(MaxThreadNumber, ExitFlag):\r
393         BuildTask._SchedulerStopped.clear()\r
394         try:\r
395             # use BoundedSemaphore to control the maximum running threads\r
396             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
397             #\r
398             # scheduling loop, which will exits when no pending/ready task and\r
399             # indicated to do so, or there's error in running thread\r
400             #\r
401             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
402                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
403                 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
404                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
405                 # get all pending tasks\r
406                 BuildTask._PendingQueueLock.acquire()\r
407                 BuildObjectList = BuildTask._PendingQueue.keys()\r
408                 BuildTask._PendingQueueLock.release()\r
409 \r
410                 #\r
411                 # check if their dependency is resolved, and if true, move them\r
412                 # into ready queue\r
413                 #\r
414                 for BuildObject in BuildObjectList:\r
415                     Bt = BuildTask._PendingQueue[BuildObject]\r
416                     if Bt.IsReady():\r
417                         BuildTask._PendingQueueLock.acquire()\r
418                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
419                         BuildTask._PendingQueueLock.release()\r
420 \r
421                 # launch build thread until the maximum number of threads is reached\r
422                 while not BuildTask._ErrorFlag.isSet():\r
423                     # empty ready queue, do nothing further\r
424                     if len(BuildTask._ReadyQueue) == 0:\r
425                         break\r
426 \r
427                     # wait for active thread(s) exit\r
428                     BuildTask._Thread.acquire(True)\r
429 \r
430                     # start a new build thread\r
431                     Bo = BuildTask._ReadyQueue.keys()[0]\r
432                     Bt = BuildTask._ReadyQueue.pop(Bo)\r
433 \r
434                     # move into running queue\r
435                     BuildTask._RunningQueueLock.acquire()\r
436                     BuildTask._RunningQueue[Bo] = Bt\r
437                     BuildTask._RunningQueueLock.release()\r
438 \r
439                     Bt.Start()\r
440 \r
441                 # avoid tense loop\r
442                 time.sleep(0.01)\r
443 \r
444             # wait for all running threads exit\r
445             if BuildTask._ErrorFlag.isSet():\r
446                 EdkLogger.quiet("\nWaiting for all Command-Threads exit...")\r
447             # while not BuildTask._ErrorFlag.isSet() and \\r
448             while len(BuildTask._RunningQueue) > 0:\r
449                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
450                 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
451                 # avoid tense loop\r
452                 time.sleep(0.1)\r
453         except BaseException, X:\r
454             #\r
455             # TRICK: hide the output of threads left runing, so that the user can\r
456             #        catch the error message easily\r
457             #\r
458             EdkLogger.SetLevel(EdkLogger.QUIET)\r
459             BuildTask._ErrorFlag.set()\r
460             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
461 \r
462         BuildTask._PendingQueue.clear()\r
463         BuildTask._ReadyQueue.clear()\r
464         BuildTask._RunningQueue.clear()\r
465         BuildTask._TaskQueue.clear()\r
466         BuildTask._SchedulerStopped.set()\r
467 \r
468     ## Wait for all running method exit\r
469     #\r
470     @staticmethod\r
471     def WaitForComplete():\r
472         BuildTask._SchedulerStopped.wait()\r
473 \r
474     ## Check if the scheduler is running or not\r
475     #\r
476     @staticmethod\r
477     def IsOnGoing():\r
478         return not BuildTask._SchedulerStopped.isSet()\r
479 \r
480     ## Abort the build\r
481     @staticmethod\r
482     def Abort():\r
483         if BuildTask.IsOnGoing():\r
484             BuildTask._ErrorFlag.set()\r
485             BuildTask.WaitForComplete()\r
486 \r
487     ## Check if there's error in running thread\r
488     #\r
489     #   Since the main thread cannot catch exceptions in other thread, we have to\r
490     #   use threading.Event to communicate this formation to main thread.\r
491     #\r
492     @staticmethod\r
493     def HasError():\r
494         return BuildTask._ErrorFlag.isSet()\r
495 \r
496     ## Get error message in running thread\r
497     #\r
498     #   Since the main thread cannot catch exceptions in other thread, we have to\r
499     #   use a static variable to communicate this message to main thread.\r
500     #\r
501     @staticmethod\r
502     def GetErrorMessage():\r
503         return BuildTask._ErrorMessage\r
504 \r
505     ## Factory method to create a BuildTask object\r
506     #\r
507     #   This method will check if a module is building or has been built. And if\r
508     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
509     #   not, create and return a new BuildTask object. The new BuildTask object\r
510     #   will be appended to the _PendingQueue for scheduling later.\r
511     #\r
512     #   @param  BuildItem       A BuildUnit object representing a build object\r
513     #   @param  Dependency      The dependent build object of BuildItem\r
514     #\r
515     @staticmethod\r
516     def New(BuildItem, Dependency=None):\r
517         if BuildItem in BuildTask._TaskQueue:\r
518             Bt = BuildTask._TaskQueue[BuildItem]\r
519             return Bt\r
520 \r
521         Bt = BuildTask()\r
522         Bt._Init(BuildItem, Dependency)\r
523         BuildTask._TaskQueue[BuildItem] = Bt\r
524 \r
525         BuildTask._PendingQueueLock.acquire()\r
526         BuildTask._PendingQueue[BuildItem] = Bt\r
527         BuildTask._PendingQueueLock.release()\r
528 \r
529         return Bt\r
530 \r
531     ## The real constructor of BuildTask\r
532     #\r
533     #   @param  BuildItem       A BuildUnit object representing a build object\r
534     #   @param  Dependency      The dependent build object of BuildItem\r
535     #\r
536     def _Init(self, BuildItem, Dependency=None):\r
537         self.BuildItem = BuildItem\r
538 \r
539         self.DependencyList = []\r
540         if Dependency == None:\r
541             Dependency = BuildItem.Dependency\r
542         else:\r
543             Dependency.extend(BuildItem.Dependency)\r
544         self.AddDependency(Dependency)\r
545         # flag indicating build completes, used to avoid unnecessary re-build\r
546         self.CompleteFlag = False\r
547 \r
548     ## Check if all dependent build tasks are completed or not\r
549     #\r
550     def IsReady(self):\r
551         ReadyFlag = True\r
552         for Dep in self.DependencyList:\r
553             if Dep.CompleteFlag == True:\r
554                 continue\r
555             ReadyFlag = False\r
556             break\r
557 \r
558         return ReadyFlag\r
559 \r
560     ## Add dependent build task\r
561     #\r
562     #   @param  Dependency      The list of dependent build objects\r
563     #\r
564     def AddDependency(self, Dependency):\r
565         for Dep in Dependency:\r
566             self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
567 \r
568     ## The thread wrapper of LaunchCommand function\r
569     #\r
570     # @param  Command               A list or string contains the call of the command\r
571     # @param  WorkingDir            The directory in which the program will be running\r
572     #\r
573     def _CommandThread(self, Command, WorkingDir):\r
574         try:\r
575             LaunchCommand(Command, WorkingDir)\r
576             self.CompleteFlag = True\r
577         except:\r
578             #\r
579             # TRICK: hide the output of threads left runing, so that the user can\r
580             #        catch the error message easily\r
581             #\r
582             EdkLogger.SetLevel(EdkLogger.QUIET)\r
583             BuildTask._ErrorFlag.set()\r
584             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
585                                       (threading.currentThread().getName(), Command, WorkingDir)\r
586         # indicate there's a thread is available for another build task\r
587         BuildTask._RunningQueueLock.acquire()\r
588         BuildTask._RunningQueue.pop(self.BuildItem)\r
589         BuildTask._RunningQueueLock.release()\r
590         BuildTask._Thread.release()\r
591 \r
592     ## Start build task thread\r
593     #\r
594     def Start(self):\r
595         Command = self.BuildItem.BuildCommand + (self.BuildItem.Target,)\r
596         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
597         self.BuildTread.setName("Command-Thread")\r
598         self.BuildTread.setDaemon(False)\r
599         self.BuildTread.start()\r
600 \r
601 ## The class implementing the EDK2 build process\r
602 #\r
603 #   The build process includes:\r
604 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
605 #       2. Parse DSC file of active platform\r
606 #       3. Parse FDF file if any\r
607 #       4. Establish build database, including parse all other files (module, package)\r
608 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
609 #       6. Call build command\r
610 #\r
611 class Build():\r
612     ## Constructor\r
613     #\r
614     # Constructor will load all necessary configurations, parse platform, modules\r
615     # and packages and the establish a database for AutoGen.\r
616     #\r
617     #   @param  Target              The build command target, one of gSupportedTarget\r
618     #   @param  WorkspaceDir        The directory of workspace\r
619     #   @param  Platform            The DSC file of active platform\r
620     #   @param  Module              The INF file of active module, if any\r
621     #   @param  Arch                The Arch list of platform or module\r
622     #   @param  ToolChain           The name list of toolchain\r
623     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
624     #   @param  FlashDefinition     The FDF file of active platform\r
625     #   @param  FdList=[]           The FD names to be individually built\r
626     #   @param  FvList=[]           The FV names to be individually built\r
627     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
628     #   @param  SpawnMode           Indicate multi-thread build mode\r
629     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
630     #\r
631     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain, \r
632                  BuildTarget, FlashDefinition, FdList=[], FvList=[], \r
633                  MakefileType="nmake", SpawnMode=False, ThreadNumber=2,\r
634                  SkipAutoGen=False, Reparse=False):\r
635 \r
636         self.WorkspaceDir = WorkspaceDir\r
637 \r
638         self.Target         = Target\r
639         self.PlatformFile   = Platform\r
640         self.ModuleFile     = Module\r
641         self.ArchList       = Arch\r
642         self.ToolChainList  = ToolChain\r
643         self.BuildTargetList= BuildTarget\r
644         self.Fdf            = FlashDefinition\r
645         self.FdList         = FdList\r
646         self.FvList         = FvList\r
647         self.MakefileType   = MakefileType\r
648         self.SpawnMode      = SpawnMode\r
649         self.ThreadNumber   = ThreadNumber\r
650         self.SkipAutoGen    = SkipAutoGen\r
651         self.Reparse        = Reparse\r
652 \r
653         self.TargetTxt    = TargetTxtClassObject()\r
654         self.ToolDef      = ToolDefClassObject()\r
655 \r
656         # print dot charater during doing some time-consuming work\r
657         self.Progress = Utils.Progressor()\r
658 \r
659         # parse target.txt, tools_def.txt, and platform file\r
660         #self.Progress.Start("Loading build configuration")\r
661         self.RestoreBuildData()\r
662         self.LoadConfiguration()\r
663         #self.Progress.Stop("done!")\r
664 \r
665         #self.Progress.Start("Parsing platform/modules/packages")\r
666         self.InitBuild()\r
667         #self.Progress.Stop("done!")\r
668 \r
669         # print current build environment and configuration\r
670         #EdkLogger.quiet('')\r
671         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
672         EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
673         EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
674         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
675         EdkLogger.info('')\r
676         EdkLogger.info('%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
677         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
678         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
679 \r
680         EdkLogger.info('')\r
681         if self.PlatformFile != None and self.PlatformFile != "":\r
682             EdkLogger.info('%-24s = %s' % ("Active Platform", self.PlatformFile))\r
683 \r
684         if self.Fdf != None and self.Fdf != "":\r
685             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
686 \r
687         if self.ModuleFile != None and self.ModuleFile != "":\r
688             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
689 \r
690         if self.SpawnMode:\r
691             EdkLogger.verbose('%-24s = %s' % ("Max Thread Number", self.ThreadNumber))\r
692 \r
693         # establish build database, INF/DEC files will be parsed in this stage\r
694         self.Progress.Start("\nParsing meta-data")\r
695         try:\r
696             if self.Fdf != None and self.Fdf != "":\r
697                 FdfFile = os.path.join(self.WorkspaceDir, self.Fdf)\r
698 \r
699                 Fdf = FdfParser(FdfFile)\r
700                 Fdf.ParseFile()\r
701 \r
702                 PcdSet = Fdf.Profile.PcdDict\r
703             else:\r
704                 PcdSet = {}\r
705 \r
706             self.Ewb.GenBuildDatabase(PcdSet)\r
707             self.Platform = self.Ewb.Build[self.ArchList[0]].PlatformDatabase[self.PlatformFile]\r
708 \r
709         except BaseException, X:\r
710             if isinstance(X, Warning):\r
711                 EdkLogger.error(X.ToolName, BUILD_ERROR, X.message, X.FileName, X.LineNumber, RaiseError = False)\r
712             raise\r
713         self.Progress.Stop("done!")\r
714 \r
715     ## Load configuration\r
716     #\r
717     #   This method will parse target.txt and get the build configurations.\r
718     #\r
719     def LoadConfiguration(self):\r
720         #\r
721         # Check target.txt and tools_def.txt and Init them\r
722         #\r
723         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
724         if os.path.isfile(BuildConfigurationFile) == True:\r
725             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
726 \r
727             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
728             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
729             if os.path.isfile(ToolDefinitionFile) == True:\r
730                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
731             else:\r
732                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
733         else:\r
734             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
735 \r
736         # if no ARCH given in command line, get it from target.txt\r
737         if self.ArchList == None or len(self.ArchList) == 0:\r
738             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
739             if len(self.ArchList) == 0:\r
740                 self.ArchList = ARCH_LIST\r
741 \r
742         # if no build target given in command line, get it from target.txt\r
743         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
744             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
745             if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
746                 self.BuildTargetList = ['DEBUG', 'RELEASE']\r
747 \r
748         # if no tool chain given in command line, get it from target.txt\r
749         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
750             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
751             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
752                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
753 \r
754         # check if the tool chains are defined or not\r
755         NewToolChainList = []\r
756         for ToolChain in self.ToolChainList:\r
757             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
758                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
759             else:\r
760                 NewToolChainList.append(ToolChain)\r
761         # if no tool chain available, break the build\r
762         if len(NewToolChainList) == 0:\r
763             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
764                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
765         else:\r
766             self.ToolChainList = NewToolChainList\r
767 \r
768         if self.ThreadNumber == None or self.ThreadNumber == "":\r
769             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
770             if self.ThreadNumber == '':\r
771                 self.ThreadNumber = 0\r
772             else:\r
773                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
774 \r
775         if self.ThreadNumber == 0:\r
776             self.SpawnMode = False\r
777         elif self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MULTIPLE_THREAD].lower() in ["enable", "true"]:\r
778             self.SpawnMode = True\r
779 \r
780         if self.PlatformFile == None:\r
781             self.PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
782             if self.PlatformFile == None or self.PlatformFile == "":\r
783                 WorkingDirectory = os.getcwd()\r
784                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
785                 FileNum = len(FileList)\r
786                 if FileNum >= 2:\r
787                     EdkLogger.error("build", OPTION_MISSING, \r
788                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
789                 elif FileNum == 1:\r
790                     self.PlatformFile = NormFile(FileList[0], self.WorkspaceDir)\r
791             else:\r
792                 self.PlatformFile = NormFile(self.PlatformFile, self.WorkspaceDir)\r
793 \r
794     ## Initialize build configuration\r
795     #\r
796     #   This method will parse DSC file and merge the configurations from\r
797     #   command line and target.txt, then get the final build configurations.\r
798     #\r
799     def InitBuild(self):\r
800         if self.PlatformFile == None or self.PlatformFile == "":\r
801             EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE,\r
802                             ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
803 \r
804         Wb = WorkspaceBuild(self.PlatformFile, self.WorkspaceDir, self.Reparse)\r
805         PcdSet = {}\r
806         if self.Fdf != None:\r
807             self.Fdf = NormFile(self.Fdf, self.WorkspaceDir)\r
808             Wb.Fdf = self.Fdf\r
809         else:\r
810             self.Fdf = Wb.Fdf\r
811 \r
812         # parse FDF file to get PCD information\r
813         if self.Fdf != None and self.Fdf != "":\r
814             if Wb.Fdf == None or Wb.Fdf == "":\r
815                 Wb.Fdf = self.Fdf\r
816 \r
817             Wb.FdTargetList.extend(self.FdList)\r
818             Wb.FvTargetList.extend(self.FvList)\r
819         else:\r
820             PcdSet = {}\r
821             if self.FdList != []:\r
822                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
823             if self.FvList != []:\r
824                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
825 \r
826         Wb.TargetTxt = self.TargetTxt\r
827         Wb.ToolDef = self.ToolDef\r
828         #\r
829         # Merge Arch\r
830         #\r
831         if self.ArchList == None or len(self.ArchList) == 0:\r
832             ArchList = Wb.SupArchList\r
833         else:\r
834             ArchList = list(set(self.ArchList) & set(Wb.SupArchList))\r
835         if len(ArchList) == 0:\r
836             EdkLogger.error("build", PARAMETER_INVALID,\r
837                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
838                                         % (" ".join(Wb.SupArchList), " ".join(self.ArchList)))\r
839         elif len(ArchList) != len(self.ArchList):\r
840             SkippedArchList = set(self.ArchList).symmetric_difference(set(Wb.SupArchList))\r
841             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
842                            % (" ".join(SkippedArchList), " ".join(Wb.SupArchList), " ".join(self.ArchList)))\r
843         self.ArchList = ArchList\r
844 \r
845         # Merge build target\r
846         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
847             BuildTargetList = Wb.BuildTarget\r
848         else:\r
849             BuildTargetList = list(set(self.BuildTargetList) & set(Wb.BuildTarget))\r
850         if BuildTargetList == []:\r
851             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
852                                 % (" ".join(Wb.BuildTarget), " ".join(self.BuildTargetList)))\r
853         self.BuildTargetList = BuildTargetList\r
854 \r
855         self.Ewb = Wb\r
856 \r
857     ## Build a module or platform\r
858     #\r
859     # Create autogen code and makfile for a module or platform, and the launch\r
860     # "make" command to build it\r
861     #\r
862     #   @param  Target                      The target of build command\r
863     #   @param  Platform                    The platform file\r
864     #   @param  Module                      The module file\r
865     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
866     #   @param  ToolChain                   The name of toolchain to build\r
867     #   @param  Arch                        The arch of the module/platform\r
868     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
869     #                                       for dependent modules/Libraries\r
870     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
871     #                                       for dependent modules/Libraries\r
872     #\r
873     def _Build(self, Target, Platform, Module, BuildTarget, ToolChain, Arch, CreateDepModuleCodeFile=True, CreateDepModuleMakeFile=True):\r
874         #\r
875         # no need to generate code/makefile for dependent modules/libraries\r
876         # if the target is 'fds'\r
877         #\r
878         if Target == 'fds':\r
879             CreateDepModuleCodeFile = False\r
880             CreateDepModuleMakeFile = False\r
881             CreateDepModuleAutoGenObject = True\r
882         else:\r
883             CreateDepModuleAutoGenObject = False\r
884 \r
885         if Module != None:\r
886             AutoGenResult = ModuleAutoGen.New(self.Ewb, Platform, Module, BuildTarget,\r
887                                               ToolChain, Arch)\r
888         else:\r
889             AutoGenResult = PlatformAutoGen.New(self.Ewb, Platform, BuildTarget,\r
890                                                 ToolChain, Arch, CreateDepModuleAutoGenObject)\r
891         if AutoGenResult == None:\r
892             return False\r
893 \r
894         # skip file generation for cleanxxx targets and run target\r
895         if Target not in ['clean', 'cleanlib', 'cleanall', 'run']:    \r
896             # for target which must generate AutoGen code and makefile\r
897             if not self.SkipAutoGen or Target == 'genc':\r
898                 self.Progress.Start("Generating code")\r
899                 AutoGenResult.CreateCodeFile(CreateDepModuleCodeFile)\r
900                 self.Progress.Stop("done!")\r
901             if Target == "genc":\r
902                 return True\r
903     \r
904             if not self.SkipAutoGen or Target == 'genmake':\r
905                 self.Progress.Start("Generating makefile")\r
906                 AutoGenResult.CreateMakeFile(CreateDepModuleMakeFile)\r
907                 self.Progress.Stop("done!")\r
908             if Target == "genmake":\r
909                 return True\r
910 \r
911         EdkLogger.info("")\r
912         BuildCommand = AutoGenResult.GetBuildCommand()\r
913         if BuildCommand == None or len(BuildCommand) == 0:\r
914             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
915 \r
916         BuildCommand = BuildCommand + (Target,)\r
917         LaunchCommand(BuildCommand, os.path.join(self.WorkspaceDir, AutoGenResult.GetMakeFileDir()))\r
918         return True\r
919 \r
920     ## Build active platform for different build targets and different tool chains\r
921     #\r
922     def _BuildPlatform(self):\r
923         for BuildTarget in self.BuildTargetList:\r
924             for ToolChain in self.ToolChainList:\r
925                 self._Build(self.Target, self.PlatformFile, None, BuildTarget, ToolChain, self.ArchList)\r
926 \r
927     ## Build active module for different build targets, different tool chains and different archs\r
928     #\r
929     def _BuildModule(self):\r
930         for BuildTarget in self.BuildTargetList:\r
931             for ToolChain in self.ToolChainList:\r
932                 #\r
933                 # module build needs platform build information, so get platform\r
934                 # AutoGen first\r
935                 #\r
936                 if self.Target == 'fds':\r
937                     #\r
938                     # we need to re-generate the command in platform's Makefile\r
939                     # in case the user changed command line option for 'fds' target\r
940                     #\r
941                     PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList, True)\r
942                     self._Build("genmake", self.PlatformFile, None, BuildTarget, ToolChain, self.ArchList, False, False)\r
943                 else:\r
944                     PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList)\r
945 \r
946                 for Arch in self.ArchList:\r
947                     self._Build(self.Target, self.PlatformFile, self.ModuleFile, BuildTarget, ToolChain, Arch)\r
948 \r
949     ## Build a platform in multi-thread mode\r
950     #\r
951     def _MultiThreadBuildPlatform(self):\r
952         for BuildTarget in self.BuildTargetList:\r
953             for ToolChain in self.ToolChainList:\r
954                 Pa = PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList)\r
955                 if Pa == None:\r
956                     continue\r
957                 # multi-thread exit flag\r
958                 ExitFlag = threading.Event()\r
959                 ExitFlag.clear()\r
960                 for Arch in self.ArchList:\r
961                     for Module in Pa.Platform[Arch].Modules:\r
962                         # Get ModuleAutoGen object to generate C code file and makefile\r
963                         Ma = ModuleAutoGen.New(self.Ewb, self.PlatformFile, Module, BuildTarget, ToolChain, Arch)\r
964                         if Ma == None:\r
965                             continue\r
966                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
967                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
968                             # for target which must generate AutoGen code and makefile\r
969                             if not self.SkipAutoGen or self.Target == 'genc':\r
970                                 Ma.CreateCodeFile(True)\r
971                             if self.Target == "genc":\r
972                                 continue\r
973 \r
974                             if not self.SkipAutoGen or self.Target == 'genmake':\r
975                                 Ma.CreateMakeFile(True)\r
976                             if self.Target == "genmake":\r
977                                 continue\r
978                         # Generate build task for the module\r
979                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
980                         # Break build if any build thread has error\r
981                         if BuildTask.HasError():\r
982                             # we need a full version of makefile for platform\r
983                             ExitFlag.set()\r
984                             BuildTask.WaitForComplete()\r
985                             Pa.CreateModuleAutoGen()\r
986                             Pa.CreateMakeFile(False)\r
987                             EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
988                         # Start task scheduler\r
989                         if not BuildTask.IsOnGoing():\r
990                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
991 \r
992                 #\r
993                 # All modules have been put in build tasks queue. Tell task scheduler\r
994                 # to exit if all tasks are completed\r
995                 #\r
996                 ExitFlag.set()\r
997                 BuildTask.WaitForComplete()\r
998                 # in case there's an interruption. we need a full version of makefile for platform\r
999                 Pa.CreateModuleAutoGen()\r
1000                 Pa.CreateMakeFile(False)\r
1001                 if BuildTask.HasError():\r
1002                     EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
1003 \r
1004                 # Generate FD image if there's a FDF file found\r
1005                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1006                     LaunchCommand(Pa.GetBuildCommand() + ("fds",), Pa.GetMakeFileDir())\r
1007 \r
1008     ## Generate GuidedSectionTools.txt in the FV directories.\r
1009     #\r
1010     def CreateGuidedSectionToolsFile(self):\r
1011         for Dsc in self.Ewb.DscDatabase.keys():\r
1012             for Arch in self.ArchList:\r
1013                 for BuildTarget in self.BuildTargetList:\r
1014                     for ToolChain in self.ToolChainList:\r
1015                         FvDir = os.path.join(\r
1016                                     self.WorkspaceDir,\r
1017                                     self.Ewb.Build[Arch].PlatformDatabase[Dsc].OutputDirectory,\r
1018                                     '_'.join((BuildTarget, ToolChain)),\r
1019                                     'FV'\r
1020                                     )\r
1021                         if not os.path.exists(FvDir):\r
1022                             continue \r
1023                         # Build up the list of supported architectures for this build\r
1024                         prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1025 \r
1026                         # Look through the tool definitions for GUIDed tools\r
1027                         guidAttribs = []\r
1028                         for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1029                             if attrib.upper().endswith('_GUID'):\r
1030                                 split = attrib.split('_')\r
1031                                 thisPrefix = '_'.join(split[0:3]) + '_'\r
1032                                 if thisPrefix == prefix:\r
1033                                     guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1034                                     guid = guid.lower()\r
1035                                     toolName = split[3]\r
1036                                     path = '_'.join(split[0:4]) + '_PATH'\r
1037                                     path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1038                                     path = self.GetFullPathOfTool(path)\r
1039                                     guidAttribs.append((guid, toolName, path))\r
1040 \r
1041                         # Write out GuidedSecTools.txt\r
1042                         toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1043                         toolsFile = open(toolsFile, 'wt')\r
1044                         for guidedSectionTool in guidAttribs:\r
1045                             print >> toolsFile, ' '.join(guidedSectionTool)\r
1046                         toolsFile.close()\r
1047 \r
1048     ## Returns the full path of the tool.\r
1049     #\r
1050     def GetFullPathOfTool (self, tool):\r
1051         if os.path.exists(tool):\r
1052             return os.path.realpath(tool)\r
1053         else:\r
1054             # We need to search for the tool using the\r
1055             # PATH environment variable.\r
1056             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1057                 foundPath = os.path.join(dirInPath, tool)\r
1058                 if os.path.exists(foundPath):\r
1059                     return os.path.realpath(foundPath)\r
1060 \r
1061         # If the tool was not found in the path then we just return\r
1062         # the input tool.\r
1063         return tool\r
1064 \r
1065     ## Launch the module or platform build\r
1066     #\r
1067     def Launch(self):\r
1068         if self.ModuleFile == None or self.ModuleFile == "":\r
1069             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1070                 self.SpawnMode = False\r
1071                 self._BuildPlatform()\r
1072             else:\r
1073                 self._MultiThreadBuildPlatform()\r
1074             self.CreateGuidedSectionToolsFile()\r
1075         else:\r
1076             self.SpawnMode = False\r
1077             self._BuildModule()\r
1078 \r
1079     ## Do some clean-up works when error occurred\r
1080     def Relinquish(self):\r
1081         self.DumpBuildData()\r
1082         Utils.Progressor.Abort()\r
1083         if self.SpawnMode == True:\r
1084             BuildTask.Abort()\r
1085 \r
1086     def DumpBuildData(self):\r
1087         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1088         Utils.CreateDirectory(CacheDirectory)\r
1089         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1090         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1091 \r
1092     def RestoreBuildData(self):\r
1093         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1094         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1095             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1096             if Utils.gFileTimeStampCache == None:\r
1097                 Utils.gFileTimeStampCache = {}\r
1098         \r
1099         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1100         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1101             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1102             if Utils.gDependencyDatabase == None:\r
1103                 Utils.gDependencyDatabase = {}\r
1104 \r
1105 def ParseDefines(DefineList=[]):\r
1106     DefineDict = {}\r
1107     if DefineList != None:\r
1108         for Define in DefineList:\r
1109             DefineTokenList = Define.split("=", 1)\r
1110             if len(DefineTokenList) == 1:\r
1111                 DefineDict[DefineTokenList[0]] = ""\r
1112             else:\r
1113                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1114     return DefineDict\r
1115 \r
1116 ## Parse command line options\r
1117 #\r
1118 # Using standard Python module optparse to parse command line option of this tool.\r
1119 #\r
1120 #   @retval Opt   A optparse.Values object containing the parsed options\r
1121 #   @retval Args  Target of build command\r
1122 #\r
1123 def MyOptionParser():\r
1124     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [target]")\r
1125     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC'], dest="TargetArch",\r
1126         help="ARCHS is one of list: IA32, X64, IPF or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")\r
1127     Parser.add_option("-p", "--platform", action="store", type="string", dest="PlatformFile",\r
1128         help="Build the platform specified by the DSC file name argument, overrides target.txt's ACTIVE_PLATFORM definition.")\r
1129     Parser.add_option("-m", "--module", action="store", type="string", dest="ModuleFile",\r
1130         help="Build the module specified by the INF file name argument.")\r
1131     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1132         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1133     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1134         help="Using the Tool Chain Tagname to build the platform, overrides target.txt's TOOL_CHAIN_TAG definition.")\r
1135     Parser.add_option("-s", "--spawn", action="store_true", type=None, dest="SpawnMode",\r
1136         help="If this flag is specified, as soon as a module can be built, the build will start, without waiting for AutoGen to complete remaining modules. While this option provides feedback that looks fast, due to overhead of the AutoGen function, this option is slower than letting AutoGen complete before starting the MAKE phase.")\r
1137     Parser.add_option("-n", action="store", type="int", dest="ThreadNumber",\r
1138         help="Build the platform using multi-threaded compiler, this option must combine with spawn option. The value overrides target.txt's MULTIPLE_THREAD and MAX_CONCURRENT_THREAD_NUMBER, less than 2 will disable multi-thread builds.")\r
1139     Parser.add_option("-f", "--fdf", action="store", type="string", dest="FdfFile",\r
1140         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1141     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1142         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1143     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1144         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1145     Parser.add_option("-k", "--msft", action="store_const", dest="MakefileType", const="nmake", help="Make Option: Generate only NMAKE Makefiles: Makefile")\r
1146     Parser.add_option("-g", "--gcc", action="store_const", dest="MakefileType", const="gmake", help="Make Option: Generate only GMAKE Makefiles: GNUmakefile")\r
1147     Parser.add_option("-l", "--all", action="store_const", dest="MakefileType", const="all", help="Make Option: Generate both NMAKE and GMAKE makefiles.")\r
1148 \r
1149     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1150     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1151 \r
1152     # Parser.add_option("-D", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1153     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1154 \r
1155     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1156     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Putlog in specified file as well as on console.")\r
1157     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1158     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1159                                                                                "including library instances selected, final dependency expression, "\\r
1160                                                                                "and warning messages, etc.")\r
1161     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1162 \r
1163     (Opt, Args)=Parser.parse_args()\r
1164     return (Opt, Args)\r
1165 \r
1166 ## Tool entrance method\r
1167 #\r
1168 # This method mainly dispatch specific methods per the command line options.\r
1169 # If no error found, return zero value so the caller of this tool can know\r
1170 # if it's executed successfully or not.\r
1171 #\r
1172 #   @retval 0     Tool was successful\r
1173 #   @retval 1     Tool failed\r
1174 #\r
1175 def Main():\r
1176     StartTime = time.clock()\r
1177 \r
1178     # Initialize log system\r
1179     EdkLogger.Initialize()\r
1180 \r
1181     #\r
1182     # Parse the options and args\r
1183     #\r
1184     (Option, Target) = MyOptionParser()\r
1185 \r
1186     # Set log level\r
1187     if Option.verbose != None:\r
1188         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1189     elif Option.quiet != None:\r
1190         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1191     elif Option.debug != None:\r
1192         EdkLogger.SetLevel(Option.debug + 1)\r
1193     else:\r
1194         EdkLogger.SetLevel(EdkLogger.INFO)\r
1195 \r
1196     if Option.LogFile != None:\r
1197         EdkLogger.SetLogFile(Option.LogFile)\r
1198 \r
1199     if Option.WarningAsError == True:\r
1200         EdkLogger.SetWarningAsError()\r
1201 \r
1202     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[00:00]" + "\n")\r
1203     ReturnCode = 0\r
1204     MyBuild = None\r
1205     try:\r
1206         if len(Target) == 0:\r
1207             Target = "all"\r
1208         elif len(Target) >= 2:\r
1209             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than on targets is not supported.",\r
1210                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1211         else:\r
1212             Target = Target[0].lower()\r
1213     \r
1214         if Target not in gSupportedTarget:\r
1215             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1216                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1217     \r
1218         # GlobalData.gGlobalDefines = ParseDefines(Option.Defines)\r
1219         #\r
1220         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1221         #\r
1222         CheckEnvVariable()\r
1223         Workspace = os.getenv("WORKSPACE")\r
1224 \r
1225         WorkingDirectory = os.getcwd()\r
1226         if Option.ModuleFile != None:\r
1227             Option.ModuleFile = NormFile(Option.ModuleFile, Workspace)\r
1228         else:\r
1229             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1230             FileNum = len(FileList)\r
1231             if FileNum >= 2:\r
1232                 EdkLogger.error("build", None, "There are %d INF files in %s.\n" % (FileNum, WorkingDirectory))\r
1233             elif FileNum == 1:\r
1234                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1235 \r
1236         if Option.PlatformFile != None:\r
1237             Option.PlatformFile = NormFile(Option.PlatformFile, Workspace)\r
1238 \r
1239         if Option.FdfFile != None:\r
1240             Option.FdfFile = NormFile(Option.FdfFile, Workspace)\r
1241 \r
1242         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile, \r
1243                         Option.TargetArch, Option.ToolChain, Option.BuildTarget, \r
1244                         Option.FdfFile, Option.RomImage, Option.FvImage, \r
1245                         Option.MakefileType, Option.SpawnMode, Option.ThreadNumber, \r
1246                         Option.SkipAutoGen, Option.Reparse)\r
1247         MyBuild.Launch()\r
1248         MyBuild.DumpBuildData()\r
1249     except BaseException, X:\r
1250         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1251         if MyBuild != None:\r
1252             # for multi-thread build exits safely\r
1253             MyBuild.Relinquish()\r
1254         EdkLogger.quiet("")\r
1255         if Option != None and Option.debug != None:\r
1256             EdkLogger.quiet(traceback.format_exc())\r
1257         else:\r
1258             EdkLogger.quiet(str(X))\r
1259         ReturnCode = 1\r
1260     finally:\r
1261         Utils.Progressor.Abort()\r
1262 \r
1263     if MyBuild != None:\r
1264         MyBuild.Ewb.Db.Close()\r
1265 \r
1266     FinishTime = time.clock()\r
1267     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1268     EdkLogger.quiet("\n%s [%s]" % (time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1269 \r
1270     return ReturnCode\r
1271 \r
1272 if __name__ == '__main__':\r
1273     sys.exit(Main())\r