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