1## @file
2# Install distribution package.
3#
4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
5#
6# SPDX-License-Identifier: BSD-2-Clause-Patent
7#
8
9'''
10MkPkg
11'''
12
13##
14# Import Modules
15#
16from os import remove
17from os import getcwd
18from os import chdir
19import os.path
20from sys import stdin
21from sys import platform
22from traceback import format_exc
23from platform import python_version
24from hashlib import md5
25from time import strftime
26from time import localtime
27from uuid import uuid4
28
29from Logger import StringTable as ST
30from Logger.ToolError import OPTION_UNKNOWN_ERROR
31from Logger.ToolError import OPTION_VALUE_INVALID
32from Logger.ToolError import ABORT_ERROR
33from Logger.ToolError import UPT_REPKG_ERROR
34from Logger.ToolError import CODE_ERROR
35from Logger.ToolError import FatalError
36from Logger.ToolError import FILE_NOT_FOUND
37import Logger.Log as Logger
38
39from Xml.XmlParser import DistributionPackageXml
40from Xml.IniToXml import IniToXml
41
42from Library import GlobalData
43from Library.ParserValidate import IsValidPath
44
45from Core.DistributionPackageClass import DistributionPackageClass
46from Core.PackageFile import PackageFile
47from Common.MultipleWorkspace import MultipleWorkspace as mws
48
49## CheckForExistingDp
50#
51# Check if there is a same name DP file existing
52# @param Path: The path to be checked
53#
54def CheckForExistingDp(Path):
55    if os.path.exists(Path):
56        Logger.Info(ST.MSG_DISTRIBUTION_PACKAGE_FILE_EXISTS % Path)
57        Input = stdin.readline()
58        Input = Input.replace('\r', '').replace('\n', '')
59        if Input.upper() != "Y":
60            Logger.Error("\nMkPkg", ABORT_ERROR, ST.ERR_USER_ABORT, RaiseError=True)
61
62## Tool entrance method
63#
64# This method mainly dispatch specific methods per the command line options.
65# If no error found, return zero value so the caller of this tool can know
66# if it's executed successfully or not.
67#
68#
69def Main(Options = None):
70    if Options is None:
71        Logger.Error("\nMkPkg", OPTION_UNKNOWN_ERROR, ST.ERR_OPTION_NOT_FOUND)
72    try:
73        DataBase = GlobalData.gDB
74        ContentFileClosed = True
75        WorkspaceDir = GlobalData.gWORKSPACE
76
77        #
78        # Init PackFileToCreate
79        #
80        if not Options.PackFileToCreate:
81            Logger.Error("\nMkPkg", OPTION_UNKNOWN_ERROR, ST.ERR_OPTION_NOT_FOUND)
82
83        #
84        # Handle if the distribution package file already exists
85        #
86        CheckForExistingDp(Options.PackFileToCreate)
87
88        #
89        # Check package file existing and valid
90        #
91        CheckFileList('.DEC', Options.PackageFileList, ST.ERR_INVALID_PACKAGE_NAME, ST.ERR_INVALID_PACKAGE_PATH)
92        #
93        # Check module file existing and valid
94        #
95        CheckFileList('.INF', Options.ModuleFileList, ST.ERR_INVALID_MODULE_NAME, ST.ERR_INVALID_MODULE_PATH)
96
97        #
98        # Get list of files that installed with RePackage attribute available
99        #
100        RePkgDict = DataBase.GetRePkgDict()
101
102        ContentFile = PackageFile(GlobalData.gCONTENT_FILE, "w")
103        ContentFileClosed = False
104
105        #
106        # Add temp distribution header
107        #
108        if Options.PackageInformationDataFile:
109            XmlFile = IniToXml(Options.PackageInformationDataFile)
110            DistPkg = DistributionPackageXml().FromXml(XmlFile)
111            remove(XmlFile)
112
113            #
114            # add distribution level tool/misc files
115            # before pack, current dir should be workspace dir, else the full
116            # path will be in the pack file
117            #
118            Cwd = getcwd()
119            chdir(WorkspaceDir)
120            ToolObject = DistPkg.Tools
121            MiscObject = DistPkg.MiscellaneousFiles
122            FileList = []
123            if ToolObject:
124                FileList += ToolObject.GetFileList()
125            if MiscObject:
126                FileList += MiscObject.GetFileList()
127            for FileObject in FileList:
128                #
129                # If you have unicode file names, please convert them to byte
130                # strings in your desired encoding before passing them to
131                # write().
132                #
133                FromFile = os.path.normpath(FileObject.GetURI()).encode('utf_8')
134                FileFullPath = mws.join(WorkspaceDir, FromFile)
135                if FileFullPath in RePkgDict:
136                    (DpGuid, DpVersion, DpName, Repackage) = RePkgDict[FileFullPath]
137                    if not Repackage:
138                        Logger.Error("\nMkPkg",
139                                     UPT_REPKG_ERROR,
140                                     ST.ERR_UPT_REPKG_ERROR,
141                                     ExtraData=ST.MSG_REPKG_CONFLICT %\
142                                     (FileFullPath, DpGuid, DpVersion, DpName)
143                                     )
144                    else:
145                        DistPkg.Header.RePackage = True
146                ContentFile.PackFile(FromFile)
147            chdir(Cwd)
148
149        #
150        # Add init dp information
151        #
152        else:
153            DistPkg = DistributionPackageClass()
154            DistPkg.Header.Name = 'Distribution Package'
155            DistPkg.Header.Guid = str(uuid4())
156            DistPkg.Header.Version = '1.0'
157
158        DistPkg.GetDistributionPackage(WorkspaceDir, Options.PackageFileList, \
159                                       Options.ModuleFileList)
160        FileList, MetaDataFileList = DistPkg.GetDistributionFileList()
161        for File in FileList + MetaDataFileList:
162            FileFullPath = os.path.normpath(os.path.join(WorkspaceDir, File))
163            #
164            # check whether file was included in a distribution that can not
165            # be repackaged
166            #
167            if FileFullPath in RePkgDict:
168                (DpGuid, DpVersion, DpName, Repackage) = RePkgDict[FileFullPath]
169                if not Repackage:
170                    Logger.Error("\nMkPkg",
171                                 UPT_REPKG_ERROR,
172                                 ST.ERR_UPT_REPKG_ERROR,
173                                 ExtraData = \
174                                 ST.MSG_REPKG_CONFLICT %(FileFullPath, DpName, \
175                                                         DpGuid, DpVersion)
176                                 )
177                else:
178                    DistPkg.Header.RePackage = True
179
180        Cwd = getcwd()
181        chdir(WorkspaceDir)
182        ContentFile.PackFiles(FileList)
183        chdir(Cwd)
184
185        Logger.Verbose(ST.MSG_COMPRESS_DISTRIBUTION_PKG)
186
187        ContentFile.Close()
188        ContentFileClosed = True
189
190        #
191        # Add Md5Signature
192        #
193        DistPkg.Header.Signature = md5(open(str(ContentFile), 'rb').read()).hexdigest()
194        #
195        # Add current Date
196        #
197        DistPkg.Header.Date = str(strftime("%Y-%m-%dT%H:%M:%S", localtime()))
198
199        #
200        # Finish final dp file
201        #
202        DistPkgFile = PackageFile(Options.PackFileToCreate, "w")
203        DistPkgFile.PackFile(str(ContentFile))
204        DistPkgXml = DistributionPackageXml()
205        DistPkgFile.PackData(DistPkgXml.ToXml(DistPkg), GlobalData.gDESC_FILE)
206        DistPkgFile.Close()
207        Logger.Quiet(ST.MSG_FINISH)
208        ReturnCode = 0
209
210    except FatalError as XExcept:
211        ReturnCode = XExcept.args[0]
212        if Logger.GetLevel() <= Logger.DEBUG_9:
213            Logger.Quiet(ST.MSG_PYTHON_ON % \
214                         (python_version(), platform) + format_exc())
215    except KeyboardInterrupt:
216        ReturnCode = ABORT_ERROR
217        if Logger.GetLevel() <= Logger.DEBUG_9:
218            Logger.Quiet(ST.MSG_PYTHON_ON % \
219                         (python_version(), platform) + format_exc())
220    except OSError:
221        pass
222    except:
223        Logger.Error(
224                    "\nMkPkg",
225                    CODE_ERROR,
226                    ST.ERR_UNKNOWN_FATAL_CREATING_ERR % \
227                    Options.PackFileToCreate,
228                    ExtraData=ST.MSG_SEARCH_FOR_HELP % ST.MSG_EDKII_MAIL_ADDR,
229                    RaiseError=False
230                    )
231        Logger.Quiet(ST.MSG_PYTHON_ON % \
232                     (python_version(), platform) + format_exc())
233        ReturnCode = CODE_ERROR
234    finally:
235        if os.path.exists(GlobalData.gCONTENT_FILE):
236            if not ContentFileClosed:
237                ContentFile.Close()
238            os.remove(GlobalData.gCONTENT_FILE)
239
240    return ReturnCode
241
242
243## CheckFileList
244#
245# @param QualifiedExt:             QualifiedExt
246# @param FileList:                 FileList
247# @param ErrorStringExt:           ErrorStringExt
248# @param ErrorStringFullPath:      ErrorStringFullPath
249#
250def CheckFileList(QualifiedExt, FileList, ErrorStringExt, ErrorStringFullPath):
251    if not FileList:
252        return
253    WorkspaceDir = GlobalData.gWORKSPACE
254    WorkspaceDir = os.path.normpath(WorkspaceDir)
255    for Item in FileList:
256        Ext = os.path.splitext(Item)[1]
257        if Ext.upper() != QualifiedExt.upper():
258            Logger.Error("\nMkPkg", OPTION_VALUE_INVALID, \
259                         ErrorStringExt % Item)
260
261        Item = os.path.normpath(Item)
262        Path = mws.join(WorkspaceDir, Item)
263        if not os.path.exists(Path):
264            Logger.Error("\nMkPkg", FILE_NOT_FOUND, ST.ERR_NOT_FOUND % Item)
265        elif Item == Path:
266            Logger.Error("\nMkPkg", OPTION_VALUE_INVALID,
267                         ErrorStringFullPath % Item)
268        elif not IsValidPath(Item, WorkspaceDir):
269            Logger.Error("\nMkPkg", OPTION_VALUE_INVALID, \
270                         ErrorStringExt % Item)
271
272        if not os.path.split(Item)[0]:
273            Logger.Error("\nMkPkg", OPTION_VALUE_INVALID, \
274                         ST.ERR_INVALID_METAFILE_PATH % Item)
275