1## @file
2#
3# PackageFile class represents the zip file of a distribution package.
4#
5# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6#
7# SPDX-License-Identifier: BSD-2-Clause-Patent
8#
9
10'''
11PackageFile
12'''
13
14##
15# Import Modules
16#
17import os.path
18import zipfile
19import tempfile
20import platform
21
22from Logger.ToolError import FILE_OPEN_FAILURE
23from Logger.ToolError import FILE_CHECKSUM_FAILURE
24from Logger.ToolError import FILE_NOT_FOUND
25from Logger.ToolError import FILE_DECOMPRESS_FAILURE
26from Logger.ToolError import FILE_UNKNOWN_ERROR
27from Logger.ToolError import FILE_WRITE_FAILURE
28from Logger.ToolError import FILE_COMPRESS_FAILURE
29import Logger.Log as Logger
30from Logger import StringTable as ST
31from Library.Misc import CreateDirectory
32from Library.Misc import RemoveDirectory
33from Core.FileHook import __FileHookOpen__
34from Common.MultipleWorkspace import MultipleWorkspace as mws
35
36
37class PackageFile:
38    def __init__(self, FileName, Mode="r"):
39        self._FileName = FileName
40        if Mode not in ["r", "w", "a"]:
41            Mode = "r"
42        try:
43            self._ZipFile = zipfile.ZipFile(FileName, Mode, \
44                                            zipfile.ZIP_DEFLATED)
45            self._Files = {}
46            for Filename in self._ZipFile.namelist():
47                self._Files[os.path.normpath(Filename)] = Filename
48        except BaseException as Xstr:
49            Logger.Error("PackagingTool", FILE_OPEN_FAILURE,
50                            ExtraData="%s (%s)" % (FileName, str(Xstr)))
51
52        BadFile = self._ZipFile.testzip()
53        if BadFile is not None:
54            Logger.Error("PackagingTool", FILE_CHECKSUM_FAILURE,
55                            ExtraData="[%s] in %s" % (BadFile, FileName))
56
57    def GetZipFile(self):
58        return self._ZipFile
59
60    ## Get file name
61    #
62    def __str__(self):
63        return self._FileName
64
65    ## Extract the file
66    #
67    # @param To:  the destination file
68    #
69    def Unpack(self, ToDest):
70        for FileN in self._ZipFile.namelist():
71            ToFile = os.path.normpath(os.path.join(ToDest, FileN))
72            Msg = "%s -> %s" % (FileN, ToFile)
73            Logger.Info(Msg)
74            self.Extract(FileN, ToFile)
75
76    ## Extract the file
77    #
78    # @param File:  the extracted file
79    # @param ToFile:  the destination file
80    #
81    def UnpackFile(self, File, ToFile):
82        File = File.replace('\\', '/')
83        if File in self._ZipFile.namelist():
84            Msg = "%s -> %s" % (File, ToFile)
85            Logger.Info(Msg)
86            self.Extract(File, ToFile)
87            return ToFile
88
89        return ''
90
91    ## Extract the file
92    #
93    # @param Which:  the source path
94    # @param ToDest:  the destination path
95    #
96    def Extract(self, Which, ToDest):
97        Which = os.path.normpath(Which)
98        if Which not in self._Files:
99            Logger.Error("PackagingTool", FILE_NOT_FOUND,
100                            ExtraData="[%s] in %s" % (Which, self._FileName))
101        try:
102            FileContent = self._ZipFile.read(self._Files[Which])
103        except BaseException as Xstr:
104            Logger.Error("PackagingTool", FILE_DECOMPRESS_FAILURE,
105                            ExtraData="[%s] in %s (%s)" % (Which, \
106                                                           self._FileName, \
107                                                           str(Xstr)))
108        try:
109            CreateDirectory(os.path.dirname(ToDest))
110            if os.path.exists(ToDest) and not os.access(ToDest, os.W_OK):
111                Logger.Warn("PackagingTool", \
112                            ST.WRN_FILE_NOT_OVERWRITTEN % ToDest)
113                return
114            else:
115                ToFile = __FileHookOpen__(ToDest, 'wb')
116        except BaseException as Xstr:
117            Logger.Error("PackagingTool", FILE_OPEN_FAILURE,
118                            ExtraData="%s (%s)" % (ToDest, str(Xstr)))
119
120        try:
121            ToFile.write(FileContent)
122            ToFile.close()
123        except BaseException as Xstr:
124            Logger.Error("PackagingTool", FILE_WRITE_FAILURE,
125                            ExtraData="%s (%s)" % (ToDest, str(Xstr)))
126
127    ## Remove the file
128    #
129    # @param Files:  the removed files
130    #
131    def Remove(self, Files):
132        TmpDir = os.path.join(tempfile.gettempdir(), ".packaging")
133        if os.path.exists(TmpDir):
134            RemoveDirectory(TmpDir, True)
135
136        os.mkdir(TmpDir)
137        self.Unpack(TmpDir)
138        for SinF in Files:
139            SinF = os.path.normpath(SinF)
140            if SinF not in self._Files:
141                Logger.Error("PackagingTool", FILE_NOT_FOUND,
142                                ExtraData="%s is not in %s!" % \
143                                (SinF, self._FileName))
144            self._Files.pop(SinF)
145        self._ZipFile.close()
146
147        self._ZipFile = zipfile.ZipFile(self._FileName, "w", \
148                                        zipfile.ZIP_DEFLATED)
149        Cwd = os.getcwd()
150        os.chdir(TmpDir)
151        self.PackFiles(self._Files)
152        os.chdir(Cwd)
153        RemoveDirectory(TmpDir, True)
154
155    ## Pack the files under Top directory, the directory shown in the zipFile start from BaseDir,
156    # BaseDir should be the parent directory of the Top directory, for example,
157    # Pack(Workspace\Dir1, Workspace) will pack files under Dir1, and the path in the zipfile will
158    # start from Workspace
159    #
160    # @param Top:  the top directory
161    # @param BaseDir:  the base directory
162    #
163    def Pack(self, Top, BaseDir):
164        if not os.path.isdir(Top):
165            Logger.Error("PackagingTool", FILE_UNKNOWN_ERROR, \
166                         "%s is not a directory!" %Top)
167
168        FilesToPack = []
169        Cwd = os.getcwd()
170        os.chdir(BaseDir)
171        RelaDir = Top[Top.upper().find(BaseDir.upper()).\
172                      join(len(BaseDir).join(1)):]
173
174        for Root, Dirs, Files in os.walk(RelaDir):
175            if 'CVS' in Dirs:
176                Dirs.remove('CVS')
177            if '.svn' in Dirs:
178                Dirs.remove('.svn')
179
180            for Dir in Dirs:
181                if Dir.startswith('.'):
182                    Dirs.remove(Dir)
183            for File1 in Files:
184                if File1.startswith('.'):
185                    continue
186                ExtName = os.path.splitext(File1)[1]
187                #
188                # skip '.dec', '.inf', '.dsc', '.fdf' files
189                #
190                if ExtName.lower() in ['.dec', '.inf', '.dsc', '.fdf']:
191                    continue
192                FilesToPack.append(os.path.join(Root, File1))
193        self.PackFiles(FilesToPack)
194        os.chdir(Cwd)
195
196    ## Pack the file
197    #
198    # @param Files:  the files to pack
199    #
200    def PackFiles(self, Files):
201        for File in Files:
202            Cwd = os.getcwd()
203            os.chdir(mws.getWs(mws.WORKSPACE, File))
204            self.PackFile(File)
205            os.chdir(Cwd)
206
207    ## Pack the file
208    #
209    # @param File:  the files to pack
210    # @param ArcName:  the Arc Name
211    #
212    def PackFile(self, File, ArcName=None):
213        try:
214            #
215            # avoid packing same file multiple times
216            #
217            if platform.system() != 'Windows':
218                File = File.replace('\\', '/')
219            ZipedFilesNameList = self._ZipFile.namelist()
220            for ZipedFile in ZipedFilesNameList:
221                if File == os.path.normpath(ZipedFile):
222                    return
223            Logger.Info("packing ..." + File)
224            self._ZipFile.write(File, ArcName)
225        except BaseException as Xstr:
226            Logger.Error("PackagingTool", FILE_COMPRESS_FAILURE,
227                            ExtraData="%s (%s)" % (File, str(Xstr)))
228
229    ## Write data to the packed file
230    #
231    # @param Data:  data to write
232    # @param ArcName:  the Arc Name
233    #
234    def PackData(self, Data, ArcName):
235        try:
236            if os.path.splitext(ArcName)[1].lower() == '.pkg':
237                Data = Data.encode('utf_8')
238            self._ZipFile.writestr(ArcName, Data)
239        except BaseException as Xstr:
240            Logger.Error("PackagingTool", FILE_COMPRESS_FAILURE,
241                            ExtraData="%s (%s)" % (ArcName, str(Xstr)))
242
243    ## Close file
244    #
245    #
246    def Close(self):
247        self._ZipFile.close()
248
249
250
251