Sync EDKII BaseTools to BaseTools project r1903.
[efi/edk2/.git] / edk2 / BaseTools / Source / Python / PackagingTool / InstallPkg.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 sys\r
19 import glob\r
20 import shutil\r
21 import traceback\r
22 import platform\r
23 from optparse import OptionParser\r
24 \r
25 import Common.EdkLogger as EdkLogger\r
26 from Common.BuildToolError import *\r
27 from Common.Misc import *\r
28 from Common.XmlParser import *\r
29 from Common.InfClassObjectLight import Inf\r
30 from Common.DecClassObjectLight import Dec\r
31 \r
32 from PackageFile import *\r
33 from IpiDb import *\r
34 from DependencyRules import *\r
35 import md5\r
36 \r
37 # Version and Copyright\r
38 VersionNumber = "0.1"\r
39 __version__ = "%prog Version " + VersionNumber\r
40 __copyright__ = "Copyright (c) 2008, Intel Corporation  All rights reserved."\r
41 \r
42 ## Check environment variables\r
43 #\r
44 #  Check environment variables that must be set for build. Currently they are\r
45 #\r
46 #   WORKSPACE           The directory all packages/platforms start from\r
47 #   EDK_TOOLS_PATH      The directory contains all tools needed by the build\r
48 #   PATH                $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
49 #\r
50 #   If any of above environment variable is not set or has error, the build\r
51 #   will be broken.\r
52 #\r
53 def CheckEnvVariable():\r
54     # check WORKSPACE\r
55     if "WORKSPACE" not in os.environ:\r
56         EdkLogger.error("InstallPkg", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
57                         ExtraData="WORKSPACE")\r
58 \r
59     WorkspaceDir = os.path.normpath(os.environ["WORKSPACE"])\r
60     if not os.path.exists(WorkspaceDir):\r
61         EdkLogger.error("InstallPkg", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir)\r
62     elif ' ' in WorkspaceDir:\r
63         EdkLogger.error("InstallPkg", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path", \r
64                         ExtraData=WorkspaceDir)\r
65     os.environ["WORKSPACE"] = WorkspaceDir\r
66 \r
67 ## Parse command line options\r
68 #\r
69 # Using standard Python module optparse to parse command line option of this tool.\r
70 #\r
71 #   @retval Opt   A optparse.Values object containing the parsed options\r
72 #   @retval Args  Target of build command\r
73 #\r
74 def MyOptionParser():\r
75     UsageString = "%prog -i <distribution_package> [-t] [-f] [-q | -v] [-h]"\r
76 \r
77     Parser = OptionParser(description=__copyright__,version=__version__,prog="InstallPkg",usage=UsageString)\r
78 \r
79     Parser.add_option("-?", action="help", help="show this help message and exit")\r
80 \r
81     Parser.add_option("-i", "--distribution-package", action="store", type="string", dest="PackageFile",\r
82             help="The distribution package to be installed")\r
83 \r
84     Parser.add_option("-t", "--install-tools", action="store_true", type=None, dest="Tools",\r
85             help="Specify it to install tools or ignore the tools of the distribution package.")\r
86     \r
87     Parser.add_option("-f", "--misc-files", action="store_true", type=None, dest="MiscFiles",\r
88             help="Specify it to install misc file or ignore the misc files of the distribution package.")\r
89 \r
90     Parser.add_option("-q", "--quiet", action="store_const", dest="LogLevel", const=EdkLogger.QUIET,\r
91             help="Disable all messages except FATAL ERRORS.")\r
92 \r
93     Parser.add_option("-v", "--verbose", action="store_const", dest="LogLevel", const=EdkLogger.VERBOSE,\r
94             help="Turn on verbose output")\r
95 \r
96     Parser.add_option("-d", "--debug", action="store", type="int", dest="LogLevel",\r
97             help="Enable debug messages at specified level.")\r
98 \r
99     Parser.set_defaults(LogLevel=EdkLogger.INFO)\r
100 \r
101     (Opt, Args)=Parser.parse_args()\r
102 \r
103     return Opt\r
104 \r
105 def InstallNewPackage(WorkspaceDir, Path):\r
106     FullPath = os.path.normpath(os.path.join(WorkspaceDir, Path))\r
107     if os.path.exists(FullPath):\r
108         print "Directory [%s] already exists, please select another location, press [Enter] with no input to quit:" %Path\r
109         Input = sys.stdin.readline()\r
110         Input = Input.replace('\r', '').replace('\n', '')\r
111         if Input == '':\r
112             EdkLogger.error("InstallPkg", UNKNOWN_ERROR, "User interrupt")\r
113         Input = Input.replace('\r', '').replace('\n', '')\r
114         return InstallNewPackage(WorkspaceDir, Input)\r
115     else:\r
116         return Path\r
117 \r
118 def InstallNewFile(WorkspaceDir, File):\r
119     FullPath = os.path.normpath(os.path.join(WorkspaceDir, File))\r
120     if os.path.exists(FullPath):\r
121         print "File [%s] already exists, please select another path, press [Enter] with no input to quit:" %File\r
122         Input = sys.stdin.readline()\r
123         Input = Input.replace('\r', '').replace('\n', '')\r
124         if Input == '':\r
125             EdkLogger.error("InstallPkg", UNKNOWN_ERROR, "User interrupt")\r
126         Input = Input.replace('\r', '').replace('\n', '')\r
127         return InstallNewFile(WorkspaceDir, Input)\r
128     else:\r
129         return File\r
130 \r
131 ## Tool entrance method\r
132 #\r
133 # This method mainly dispatch specific methods per the command line options.\r
134 # If no error found, return zero value so the caller of this tool can know\r
135 # if it's executed successfully or not.\r
136 #\r
137 #   @retval 0     Tool was successful\r
138 #   @retval 1     Tool failed\r
139 #\r
140 def Main():\r
141     EdkLogger.Initialize()\r
142     Options = None\r
143     DistFileName = 'dist.pkg'\r
144     ContentFileName = 'content.zip'\r
145     DistFile, ContentZipFile, UnpackDir = None, None, None\r
146     \r
147     Options = MyOptionParser()\r
148     try:\r
149         if Options.LogLevel < EdkLogger.DEBUG_9:\r
150             EdkLogger.SetLevel(Options.LogLevel + 1)\r
151         else:\r
152             EdkLogger.SetLevel(Options.LogLevel)\r
153 \r
154         CheckEnvVariable()\r
155         WorkspaceDir = os.environ["WORKSPACE"]\r
156         if not Options.PackageFile:\r
157             EdkLogger.error("InstallPkg", OPTION_NOT_SUPPORTED, ExtraData="Must specify one distribution package")\r
158 \r
159         # unzip dist.pkg file\r
160         EdkLogger.quiet("Unzipping and parsing distribution package XML file ... ")\r
161         DistFile = PackageFile(Options.PackageFile)\r
162         UnpackDir = os.path.normpath(os.path.join(WorkspaceDir, ".tmp"))\r
163         DistPkgFile = DistFile.UnpackFile(DistFileName, os.path.normpath(os.path.join(UnpackDir, DistFileName)))\r
164         if not DistPkgFile:\r
165             EdkLogger.error("InstallPkg", FILE_NOT_FOUND, "File [%s] is broken in distribution package" %DistFileName)\r
166         \r
167         # Generate distpkg\r
168         DistPkgObj = DistributionPackageXml()\r
169         DistPkg = DistPkgObj.FromXml(DistPkgFile)\r
170 \r
171         # prepare check dependency\r
172         Db = IpiDatabase(os.path.normpath(os.path.join(WorkspaceDir, "Conf/DistributionPackageDatabase.db")))\r
173         Db.InitDatabase()\r
174         Dep = DependencyRules(Db)\r
175         \r
176         # Check distribution package exist\r
177         if Dep.CheckDpExists(DistPkg.Header.Guid, DistPkg.Header.Version):\r
178             EdkLogger.error("InstallPkg", UNKNOWN_ERROR, "This distribution package has been installed", ExtraData=DistPkg.Header.Name)\r
179         \r
180         # unzip contents.zip file\r
181         ContentFile = DistFile.UnpackFile(ContentFileName, os.path.normpath(os.path.join(UnpackDir, ContentFileName)))\r
182         ContentZipFile = PackageFile(ContentFile)\r
183         if not ContentFile:\r
184             EdkLogger.error("InstallPkg", FILE_NOT_FOUND, "File [%s] is broken in distribution package" %ContentFileName)\r
185         \r
186         # verify MD5 signature\r
187         Md5Sigature = md5.new(open(ContentFile).read())\r
188         if DistPkg.Header.Signature != Md5Sigature.hexdigest():\r
189             EdkLogger.error("InstallPkg", FILE_CHECKSUM_FAILURE, ExtraData=ContentFile)\r
190         \r
191         # Check package exist and install\r
192         for Guid,Version,Path in DistPkg.PackageSurfaceArea:\r
193             PackagePath = os.path.dirname(Path)\r
194             NewPackagePath = PackagePath\r
195             Package = DistPkg.PackageSurfaceArea[Guid,Version,Path]\r
196             EdkLogger.info("Installing package ... %s" % Package.PackageHeader.Name)\r
197             if Dep.CheckPackageExists(Guid, Version):\r
198                 EdkLogger.quiet("Package [%s] has been installed" %Path)\r
199             NewPackagePath = InstallNewPackage(WorkspaceDir, PackagePath)\r
200             Package.FileList = []\r
201             for Item in Package.MiscFiles.Files:\r
202                 FromFile = os.path.join(PackagePath, Item.Filename)\r
203                 ToFile = os.path.normpath(os.path.join(WorkspaceDir, NewPackagePath, Item.Filename))\r
204                 ContentZipFile.UnpackFile(FromFile, ToFile)\r
205                 Package.FileList.append(ToFile)\r
206             \r
207             # Update package\r
208             Package.PackageHeader.CombinePath = Package.PackageHeader.CombinePath.replace(PackagePath, NewPackagePath, 1)\r
209             # Update modules of package\r
210             Module = None\r
211             for ModuleGuid, ModuleVersion, ModulePath in Package.Modules:\r
212                 Module = Package.Modules[ModuleGuid, ModuleVersion, ModulePath]\r
213                 NewModulePath = ModulePath.replace(PackagePath, NewPackagePath, 1)\r
214                 del Package.Modules[ModuleGuid, ModuleVersion, ModulePath]\r
215                 Package.Modules[ModuleGuid, ModuleVersion, NewModulePath] = Module\r
216             del DistPkg.PackageSurfaceArea[Guid,Version,Path]\r
217             DistPkg.PackageSurfaceArea[Guid,Version,Package.PackageHeader.CombinePath] = Package\r
218 \r
219 #            SaveFileOnChange(os.path.join(Options.InstallDir, ModulePath, Module.Header.Name, ".inf"), Inf.ModuleToInf(Module), False)\r
220 #            EdkLogger.info("Installing package ... %s" % Package.Header.Name)\r
221 #            shutil.copytree(os.path.join(ContentFileDir, Path), Options.InstallDir)\r
222 #            SaveFileOnChange(os.path.join(Options.InstallDir, Path, Package.Header.Name, ".dec"), Dec.PackageToDec(Package), False)\r
223 \r
224         # Check module exist and install\r
225         Module = None\r
226         for Guid,Version,Path in DistPkg.ModuleSurfaceArea:\r
227             ModulePath = os.path.dirname(Path)\r
228             NewModulePath = ModulePath\r
229             Module = DistPkg.ModuleSurfaceArea[Guid,Version,Path]\r
230             EdkLogger.info("Installing module ... %s" % Module.ModuleHeader.Name)\r
231             if Dep.CheckModuleExists(Guid, Version):\r
232                 EdkLogger.quiet("Module [%s] has been installed" %Path)\r
233             NewModulePath = InstallNewPackage(WorkspaceDir, ModulePath)\r
234             Module.FileList = []\r
235             for Item in Module.MiscFiles.Files:\r
236                 ModulePath = ModulePath[os.path.normpath(ModulePath).rfind(os.path.normpath('/'))+1:]\r
237                 FromFile = os.path.join(ModulePath, Item.Filename)\r
238                 ToFile = os.path.normpath(os.path.join(WorkspaceDir, NewModulePath, Item.Filename))\r
239                 ContentZipFile.UnpackFile(FromFile, ToFile)\r
240                 Module.FileList.append(ToFile)\r
241             \r
242 #            EdkLogger.info("Installing module ... %s" % Module.Header.Name)\r
243 #            shutil.copytree(os.path.join(ContentFileDir, Path), Options.InstallDir)\r
244 #            SaveFileOnChange(os.path.join(Options.InstallDir, Path, Module.Header.Name, ".inf"), Inf.ModuleToInf(Module), False)\r
245             \r
246             # Update module\r
247             Module.ModuleHeader.CombinePath = Module.ModuleHeader.CombinePath.replace(os.path.dirname(Path), NewModulePath, 1)\r
248             del DistPkg.ModuleSurfaceArea[Guid,Version,Path]\r
249             DistPkg.ModuleSurfaceArea[Guid,Version,Module.ModuleHeader.CombinePath] = Module\r
250 #            \r
251 #        \r
252 #        for Guid,Version,Path in DistPkg.PackageSurfaceArea:\r
253 #            print Guid,Version,Path\r
254 #            for item in DistPkg.PackageSurfaceArea[Guid,Version,Path].FileList:\r
255 #                print item\r
256 #        for Guid,Version,Path in DistPkg.ModuleSurfaceArea:\r
257 #            print Guid,Version,Path\r
258 #            for item in DistPkg.ModuleSurfaceArea[Guid,Version,Path].FileList:\r
259 #                print item\r
260 \r
261         if Options.Tools:\r
262             EdkLogger.info("Installing tools ... ")\r
263             for File in DistPkg.Tools.Files:\r
264                 FromFile = File.Filename\r
265                 ToFile = InstallNewFile(WorkspaceDir, FromFile)\r
266                 ContentZipFile.UnpackFile(FromFile, ToFile)\r
267         if Options.MiscFiles:\r
268             EdkLogger.info("Installing misc files ... ")\r
269             for File in DistPkg.MiscellaneousFiles.Files:\r
270                 FromFile = File.Filename\r
271                 ToFile = InstallNewFile(WorkspaceDir, FromFile)\r
272                 ContentZipFile.UnpackFile(FromFile, ToFile)\r
273 \r
274         # update database\r
275         EdkLogger.quiet("Update Distribution Package Database ...")\r
276         Db.AddDPObject(DistPkg)\r
277 \r
278     except FatalError, X:\r
279         if Options and Options.LogLevel < EdkLogger.DEBUG_9:\r
280             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
281         ReturnCode = X.args[0]\r
282     except KeyboardInterrupt:\r
283         ReturnCode = ABORT_ERROR\r
284         if Options and Options.LogLevel < EdkLogger.DEBUG_9:\r
285             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
286     except:\r
287         EdkLogger.error(\r
288                     "\nInstallPkg",\r
289                     CODE_ERROR,\r
290                     "Unknown fatal error when installing [%s]" % Options.PackageFile,\r
291                     ExtraData="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n",\r
292                     RaiseError=False\r
293                     )\r
294         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
295         ReturnCode = CODE_ERROR\r
296     finally:\r
297         EdkLogger.quiet("Removing temp files ... ")\r
298         if DistFile:\r
299             DistFile.Close()\r
300         if ContentZipFile:\r
301             ContentZipFile.Close()\r
302         if UnpackDir:\r
303             shutil.rmtree(UnpackDir)\r
304         \r
305         EdkLogger.quiet("DONE")\r
306         Progressor.Abort()\r
307 \r
308 if __name__ == '__main__':\r
309     sys.exit(Main())\r