Sync EDKII BaseTools to BaseTools project r1903.
[efi/edk2/.git] / edk2 / BaseTools / Source / Python / PackagingTool / MkPkg.py
1 ## @file\r
2 # Install distribution package.\r
3 #\r
4 # Copyright (c) 2007, Intel Corporation\r
5 # All rights reserved. This program and the accompanying materials\r
6 # are licensed and made available under the terms and conditions of the BSD License\r
7 # which accompanies this distribution.  The full text of the license may be found at\r
8 # http://opensource.org/licenses/bsd-license.php\r
9 #\r
10 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 #\r
13 \r
14 ##\r
15 # Import Modules\r
16 #\r
17 import os\r
18 import os.path\r
19 import sys\r
20 import glob\r
21 import shutil\r
22 import traceback\r
23 import platform\r
24 from optparse import OptionParser\r
25 import md5\r
26 import time\r
27 import uuid\r
28 \r
29 from PackageFile import *\r
30 import Common.EdkLogger as EdkLogger\r
31 from Common.BuildToolError import *\r
32 from Common.Misc import *\r
33 from Common.XmlParser import *\r
34 from CommonDataClass.DistributionPackageClass import *\r
35 from Common.DecClassObjectLight import Dec\r
36 from Common.InfClassObjectLight import Inf\r
37 \r
38 from PackageFile import *\r
39 \r
40 # Version and Copyright\r
41 VersionNumber = "0.1"\r
42 __version__ = "%prog Version " + VersionNumber\r
43 __copyright__ = "Copyright (c) 2008, Intel Corporation  All rights reserved."\r
44 \r
45 ## Check environment variables\r
46 #\r
47 #  Check environment variables that must be set for build. Currently they are\r
48 #\r
49 #   WORKSPACE           The directory all packages/platforms start from\r
50 #   EDK_TOOLS_PATH      The directory contains all tools needed by the build\r
51 #   PATH                $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
52 #\r
53 #   If any of above environment variable is not set or has error, the build\r
54 #   will be broken.\r
55 #\r
56 def CheckEnvVariable():\r
57     # check WORKSPACE\r
58     if "WORKSPACE" not in os.environ:\r
59         EdkLogger.error("MkPkg", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
60                         ExtraData="WORKSPACE")\r
61 \r
62     WorkspaceDir = os.path.normpath(os.environ["WORKSPACE"])\r
63     if not os.path.exists(WorkspaceDir):\r
64         EdkLogger.error("MkPkg", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir)\r
65     elif ' ' in WorkspaceDir:\r
66         EdkLogger.error("MkPkg", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path", \r
67                         ExtraData=WorkspaceDir)\r
68     os.environ["WORKSPACE"] = WorkspaceDir\r
69 \r
70 ## Parse command line options\r
71 #\r
72 # Using standard Python module optparse to parse command line option of this tool.\r
73 #\r
74 #   @retval Opt   A optparse.Values object containing the parsed options\r
75 #   @retval Args  Target of build command\r
76 #\r
77 def MyOptionParser():\r
78     UsageString = "%prog -m <module_file> -p <package_file> [-o distribution_file] " + \\r
79                    "[-x xml-file-header] [-t tools-directory] [-f misc-files] [-q | -v] [-h]"\r
80 \r
81     Parser = OptionParser(description=__copyright__,version=__version__,prog="MkPkg",usage=UsageString)\r
82 \r
83     Parser.add_option("-?", action="help", help="show this help message and exit")\r
84 \r
85     Parser.add_option("-o", "--output-file", action="store", type="string", dest="DistributionFile",\r
86             help="Specify the distribution file to be created.")\r
87 \r
88     Parser.add_option("-f", "--misc-files", action="append", type="string", dest="MiscFiles",\r
89             help="Specify any misc files.")\r
90 \r
91     Parser.add_option("-x", "--xml-file-header", action="store", type=None, dest="TemplateFile",\r
92             help="Specify the xml file which includes header information for creating the distribution file.")\r
93 \r
94     Parser.add_option("-t", "--tools-directory", action="store", type=None, dest="ToolsDir",\r
95             help="Specify the directory name of tools.")\r
96 \r
97     Parser.add_option("-m", "--module", action="append", type="string", dest="ModuleFileList",\r
98             help="The inf file of module to be distributed standalone.")\r
99 \r
100     Parser.add_option("-p", "--package", action="append", type="string", dest="PackageFileList",\r
101             help="The dec file of package to be distributed.")\r
102 \r
103     Parser.add_option("-q", "--quiet", action="store_const", dest="LogLevel", const=EdkLogger.QUIET,\r
104             help="Disable all messages except FATAL ERRORS.")\r
105 \r
106     Parser.add_option("-v", "--verbose", action="store_const", dest="LogLevel", const=EdkLogger.VERBOSE,\r
107             help="Turn on verbose output")\r
108 \r
109     Parser.add_option("-d", "--debug", action="store", type="int", dest="LogLevel",\r
110             help="Enable debug messages at specified level.")\r
111 \r
112     Parser.set_defaults(LogLevel=EdkLogger.INFO)\r
113 \r
114     (Opt, Args)=Parser.parse_args()\r
115     # error check\r
116     if not Opt.ModuleFileList and not Opt.PackageFileList:\r
117         EdkLogger.error("MkPkg", OPTION_NOT_SUPPORTED, ExtraData="At least one package file or module file must be specified")\r
118     if Opt.TemplateFile:\r
119         if not os.path.exists(Opt.TemplateFile):\r
120             EdkLogger.error(\r
121                             "\nMkPkg",\r
122                             FILE_NOT_FOUND,\r
123                             "Template file [%s] not found" % Opt.TemplateFile\r
124                             )\r
125     return Opt\r
126 \r
127 ## Tool entrance method\r
128 #\r
129 # This method mainly dispatch specific methods per the command line options.\r
130 # If no error found, return zero value so the caller of this tool can know\r
131 # if it's executed successfully or not.\r
132 #\r
133 #   @retval 0     Tool was successful\r
134 #   @retval 1     Tool failed\r
135 #\r
136 def Main():\r
137     EdkLogger.Initialize()\r
138     Options = MyOptionParser()\r
139     try:\r
140         if Options.LogLevel < EdkLogger.DEBUG_9:\r
141             EdkLogger.SetLevel(Options.LogLevel + 1)\r
142         else:\r
143             EdkLogger.SetLevel(Options.LogLevel)\r
144 \r
145         CheckEnvVariable()\r
146         WorkspaceDir = os.environ["WORKSPACE"]\r
147         \r
148         # Init DistributionFile\r
149         if not Options.DistributionFile:\r
150             Options.DistributionFile = "DistributionPackage.zip"\r
151         \r
152         # Check Tools Dir\r
153         if Options.ToolsDir:\r
154             if not os.path.isdir(os.path.normpath(os.path.join(WorkspaceDir, Options.ToolsDir))):\r
155                 EdkLogger.error(\r
156                                 "\nMkPkg",\r
157                                 FILE_NOT_FOUND,\r
158                                 "Tools directory [%s] not found" % Options.ToolsDir\r
159                                 )\r
160         \r
161         # Check misc files\r
162         if Options.MiscFiles:\r
163             for Item in Options.MiscFiles:\r
164                 FullPath = os.path.normpath(os.path.join(WorkspaceDir, Item))\r
165                 if not os.path.isfile(FullPath):\r
166                     EdkLogger.error(\r
167                                     "\nMkPkg",\r
168                                     FILE_NOT_FOUND,\r
169                                     "Misc file [%s] not found" % Item\r
170                                     )\r
171         \r
172         #Check package file existing and valid\r
173         if Options.PackageFileList:\r
174             for Item in Options.PackageFileList:\r
175                 (Name, Ext) = os.path.splitext(Item)\r
176                 if Ext.upper() != '.DEC':\r
177                     EdkLogger.error(\r
178                     "\nMkPkg",\r
179                     OPTION_VALUE_INVALID,\r
180                     "[%s] is not a valid package name" % Item\r
181                     )\r
182                 Path = os.path.normpath(os.path.join(WorkspaceDir, Item))\r
183                 if not os.path.exists(Path):\r
184                     EdkLogger.error(\r
185                         "\nMkPkg",\r
186                         FILE_NOT_FOUND,\r
187                         "[%s] not found" % Item\r
188                         )\r
189         #Check module file existing and valid\r
190         if Options.ModuleFileList:\r
191             for Item in Options.ModuleFileList:\r
192                 (Name, Ext) = os.path.splitext(Item)\r
193                 if Ext.upper() != '.INF':\r
194                     EdkLogger.error(\r
195                     "\nMkPkg",\r
196                     OPTION_VALUE_INVALID,\r
197                     "[%s] is not a valid module name" % Item\r
198                     )\r
199                 Path = os.path.normpath(os.path.join(WorkspaceDir, Item))\r
200                 if not os.path.exists(Path):\r
201                     EdkLogger.error(\r
202                         "\nMkPkg",\r
203                         FILE_NOT_FOUND,\r
204                         "[%s] not found" % Item\r
205                         )\r
206 \r
207         ContentFile = PackageFile("content.zip", "w")\r
208         DistPkg = DistributionPackageClass()\r
209         DistPkg.GetDistributionPackage(WorkspaceDir, Options.PackageFileList, Options.ModuleFileList)\r
210         DistPkgXml = DistributionPackageXml()\r
211         for Item in DistPkg.PackageSurfaceArea:\r
212             ContentFile.Pack(os.path.dirname(os.path.normpath(os.path.join(WorkspaceDir,Item[2]))))\r
213         for Item in DistPkg.ModuleSurfaceArea:\r
214             ContentFile.Pack(os.path.dirname(os.path.normpath(os.path.join(WorkspaceDir,Item[2]))))\r
215 \r
216         # Add tools files and information\r
217         if Options.ToolsDir:\r
218             ToolsFiles = MiscFileClass()\r
219             ToolsRoot = os.path.normpath(os.path.join(WorkspaceDir, Options.ToolsDir))\r
220             ContentFile.Pack(ToolsRoot)\r
221             ToolsFileList = GetFiles(ToolsRoot, ['CVS', '.svn'])\r
222             for Item in ToolsFileList:\r
223                 OriPath = Item[len(WorkspaceDir)+1:]\r
224                 FileObj = FileClass()\r
225                 FileObj.Filename = OriPath\r
226                 (Name, Ext) = os.path.splitext(OriPath)\r
227                 if Ext.upper() in ['EXE', 'COM', 'EFI']:\r
228                     FileObj.Executable = 'True'\r
229                 ToolsFiles.Files.append(FileObj)\r
230             DistPkg.Tools = ToolsFiles\r
231         \r
232         # Add misc files and information\r
233         if Options.MiscFiles:\r
234             MiscFiles = MiscFileClass()\r
235             for Item in Options.MiscFiles:\r
236                 ContentFile.PackFile(Item)\r
237                 FileObj = FileClass()\r
238                 FileObj.Filename = Item\r
239                 (Name, Ext) = os.path.splitext(Item)\r
240                 if Ext.upper() in ['EXE', 'COM', 'EFI']:\r
241                     FileObj.Executable = 'True'\r
242                 MiscFiles.Files.append(FileObj)\r
243             DistPkg.MiscellaneousFiles = MiscFiles\r
244                 \r
245         print "Compressing Distribution Package File ..."\r
246         ContentFile.Close()\r
247         \r
248         # Add temp distribution header\r
249         if Options.TemplateFile:\r
250             TempXML = DistributionPackageXml()\r
251             DistPkg.Header = TempXML.FromXml(Options.TemplateFile).Header\r
252         # Add init dp information\r
253         else:\r
254             DistPkg.Header.Name = 'Distribution Package'\r
255             DistPkg.Header.Guid = str(uuid.uuid4())\r
256             DistPkg.Header.Version = '1.0'\r
257         \r
258         # Add Md5Sigature\r
259         Md5Sigature = md5.new(open(str(ContentFile)).read())\r
260         DistPkg.Header.Signature = Md5Sigature.hexdigest()\r
261         # Add current Date\r
262         DistPkg.Header.Date = str(time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime()))\r
263 \r
264         # Finish final dp file\r
265         DistPkgFile = PackageFile(Options.DistributionFile, "w")\r
266         DistPkgFile.PackFile(str(ContentFile))\r
267         DistPkgFile.PackData(DistPkgXml.ToXml(DistPkg), "dist.pkg")\r
268         DistPkgFile.Close()\r
269         print "DONE"\r
270 \r
271     except FatalError, X:\r
272         if Options and Options.LogLevel < EdkLogger.DEBUG_9:\r
273             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
274         ReturnCode = X.args[0]\r
275     except KeyboardInterrupt:\r
276         ReturnCode = ABORT_ERROR\r
277         if Options and Options.LogLevel < EdkLogger.DEBUG_9:\r
278             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
279     except:\r
280         EdkLogger.error(\r
281                     "\nMkPkg",\r
282                     CODE_ERROR,\r
283                     "Unknown fatal error when creating [%s]" % Options.DistributionFile,\r
284                     ExtraData="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n",\r
285                     RaiseError=False\r
286                     )\r
287         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
288         ReturnCode = CODE_ERROR\r
289     finally:\r
290         Progressor.Abort()\r
291 \r
292 if __name__ == '__main__':\r
293     sys.exit(Main())\r
294 \r