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"""
9Install a distribution package
10"""
11##
12# Import Modules
13#
14from Core.FileHook import __FileHookOpen__
15import os.path
16from os import chmod
17from os import SEEK_SET
18from os import SEEK_END
19import stat
20from hashlib import md5
21import copy
22from sys import stdin
23from sys import platform
24from shutil import rmtree
25from shutil import copyfile
26from traceback import format_exc
27from platform import python_version
28
29from Logger import StringTable as ST
30from Logger.ToolError import UNKNOWN_ERROR
31from Logger.ToolError import FILE_UNKNOWN_ERROR
32from Logger.ToolError import OPTION_MISSING
33from Logger.ToolError import UPT_ALREADY_INSTALLED_ERROR
34from Logger.ToolError import FatalError
35from Logger.ToolError import ABORT_ERROR
36from Logger.ToolError import CODE_ERROR
37from Logger.ToolError import FORMAT_INVALID
38from Logger.ToolError import FILE_TYPE_MISMATCH
39import Logger.Log as Logger
40
41from Library.Misc import Sdict
42from Library.Misc import ConvertPath
43from Library.ParserValidate import IsValidInstallPath
44from Xml.XmlParser import DistributionPackageXml
45from GenMetaFile.GenDecFile import PackageToDec
46from GenMetaFile.GenInfFile import ModuleToInf
47from Core.PackageFile import PackageFile
48from Core.PackageFile import FILE_NOT_FOUND
49from Core.PackageFile import FILE_CHECKSUM_FAILURE
50from Core.PackageFile import CreateDirectory
51from Core.DependencyRules import DependencyRules
52from Library import GlobalData
53
54## InstallNewPackage
55#
56# @param WorkspaceDir:   Workspace Directory
57# @param Path:           Package Path
58# @param CustomPath:     whether need to customize path at first
59#
60def InstallNewPackage(WorkspaceDir, Path, CustomPath = False):
61    if os.path.isabs(Path):
62        Logger.Info(ST.MSG_RELATIVE_PATH_ONLY%Path)
63    elif CustomPath:
64        Logger.Info(ST.MSG_NEW_PKG_PATH)
65    else:
66        Path = ConvertPath(Path)
67        Path = os.path.normpath(Path)
68        FullPath = os.path.normpath(os.path.join(WorkspaceDir, Path))
69        if os.path.exists(FullPath):
70            Logger.Info(ST.ERR_DIR_ALREADY_EXIST%FullPath)
71        else:
72            return Path
73
74    Input = stdin.readline()
75    Input = Input.replace('\r', '').replace('\n', '')
76    if Input == '':
77        Logger.Error("InstallPkg", UNKNOWN_ERROR, ST.ERR_USER_INTERRUPT)
78    Input = Input.replace('\r', '').replace('\n', '')
79    return InstallNewPackage(WorkspaceDir, Input, False)
80
81## InstallNewModule
82#
83# @param WorkspaceDir:   Workspace Directory
84# @param Path:           Standalone Module Path
85# @param PathList:       The already installed standalone module Path list
86#
87def InstallNewModule(WorkspaceDir, Path, PathList = None):
88    if PathList is None:
89        PathList = []
90    Path = ConvertPath(Path)
91    Path = os.path.normpath(Path)
92    FullPath = os.path.normpath(os.path.join(WorkspaceDir, Path))
93    if os.path.exists(FullPath) and FullPath not in PathList:
94        Logger.Info(ST.ERR_DIR_ALREADY_EXIST%Path)
95    elif Path == FullPath:
96        Logger.Info(ST.MSG_RELATIVE_PATH_ONLY%FullPath)
97    else:
98        return Path
99
100    Input = stdin.readline()
101    Input = Input.replace('\r', '').replace('\n', '')
102    if Input == '':
103        Logger.Error("InstallPkg", UNKNOWN_ERROR, ST.ERR_USER_INTERRUPT)
104    Input = Input.replace('\r', '').replace('\n', '')
105    return InstallNewModule(WorkspaceDir, Input, PathList)
106
107
108## InstallNewFile
109#
110# @param WorkspaceDir:   Workspace Direction
111# @param File:      File
112#
113def InstallNewFile(WorkspaceDir, File):
114    FullPath = os.path.normpath(os.path.join(WorkspaceDir, File))
115    if os.path.exists(FullPath):
116        Logger.Info(ST.ERR_FILE_ALREADY_EXIST %File)
117        Input = stdin.readline()
118        Input = Input.replace('\r', '').replace('\n', '')
119        if Input == '':
120            Logger.Error("InstallPkg", UNKNOWN_ERROR, ST.ERR_USER_INTERRUPT)
121        Input = Input.replace('\r', '').replace('\n', '')
122        return InstallNewFile(WorkspaceDir, Input)
123    else:
124        return File
125
126## UnZipDp
127#
128# UnZipDp
129#
130def UnZipDp(WorkspaceDir, DpPkgFileName, Index=1):
131    ContentZipFile = None
132    Logger.Quiet(ST.MSG_UZIP_PARSE_XML)
133    DistFile = PackageFile(DpPkgFileName)
134
135    DpDescFileName, ContentFileName = GetDPFile(DistFile.GetZipFile())
136
137    TempDir = os.path.normpath(os.path.join(WorkspaceDir, "Conf/.tmp%s" % str(Index)))
138    GlobalData.gUNPACK_DIR.append(TempDir)
139    DistPkgFile = DistFile.UnpackFile(DpDescFileName, os.path.normpath(os.path.join(TempDir, DpDescFileName)))
140    if not DistPkgFile:
141        Logger.Error("InstallPkg", FILE_NOT_FOUND, ST.ERR_FILE_BROKEN %DpDescFileName)
142
143    #
144    # Generate distpkg
145    #
146    DistPkgObj = DistributionPackageXml()
147    DistPkg = DistPkgObj.FromXml(DistPkgFile)
148    if DistPkg.Header.RePackage == '':
149        DistPkg.Header.RePackage = False
150    if DistPkg.Header.ReadOnly == '':
151        DistPkg.Header.ReadOnly = False
152
153    #
154    # unzip contents.zip file
155    #
156    ContentFile = DistFile.UnpackFile(ContentFileName, os.path.normpath(os.path.join(TempDir, ContentFileName)))
157    if not ContentFile:
158        Logger.Error("InstallPkg", FILE_NOT_FOUND,
159            ST.ERR_FILE_BROKEN % ContentFileName)
160
161    #
162    # Get file size
163    #
164    FileSize = os.path.getsize(ContentFile)
165
166    if FileSize != 0:
167        ContentZipFile = PackageFile(ContentFile)
168
169    #
170    # verify MD5 signature when existed
171    #
172    if DistPkg.Header.Signature != '':
173        Md5Signature = md5(__FileHookOpen__(ContentFile, 'rb').read())
174        if DistPkg.Header.Signature != Md5Signature.hexdigest():
175            ContentZipFile.Close()
176            Logger.Error("InstallPkg", FILE_CHECKSUM_FAILURE,
177                ExtraData=ContentFile)
178
179    return DistPkg, ContentZipFile, DpPkgFileName, DistFile
180
181## GetPackageList
182#
183# GetPackageList
184#
185def GetPackageList(DistPkg, Dep, WorkspaceDir, Options, ContentZipFile, ModuleList, PackageList):
186    NewDict = Sdict()
187    for Guid, Version, Path in DistPkg.PackageSurfaceArea:
188        PackagePath = Path
189        Package = DistPkg.PackageSurfaceArea[Guid, Version, Path]
190        Logger.Info(ST.MSG_INSTALL_PACKAGE % Package.GetName())
191#         if Dep.CheckPackageExists(Guid, Version):
192#             Logger.Info(ST.WRN_PACKAGE_EXISTED %(Guid, Version))
193        if Options.UseGuidedPkgPath:
194            GuidedPkgPath = "%s_%s_%s" % (Package.GetName(), Guid, Version)
195            NewPackagePath = InstallNewPackage(WorkspaceDir, GuidedPkgPath, Options.CustomPath)
196        else:
197            NewPackagePath = InstallNewPackage(WorkspaceDir, PackagePath, Options.CustomPath)
198        InstallPackageContent(PackagePath, NewPackagePath, Package, ContentZipFile, Dep, WorkspaceDir, ModuleList,
199                              DistPkg.Header.ReadOnly)
200        PackageList.append(Package)
201
202        NewDict[Guid, Version, Package.GetPackagePath()] = Package
203
204    #
205    # Now generate meta-data files, first generate all dec for package
206    # dec should be generated before inf, and inf should be generated after
207    # all packages installed, else hard to resolve modules' package
208    # dependency (Hard to get the location of the newly installed package)
209    #
210    for Package in PackageList:
211        FilePath = PackageToDec(Package, DistPkg.Header)
212        Md5Signature = md5(__FileHookOpen__(str(FilePath), 'rb').read())
213        Md5Sum = Md5Signature.hexdigest()
214        if (FilePath, Md5Sum) not in Package.FileList:
215            Package.FileList.append((FilePath, Md5Sum))
216
217    return NewDict
218
219## GetModuleList
220#
221# GetModuleList
222#
223def GetModuleList(DistPkg, Dep, WorkspaceDir, ContentZipFile, ModuleList):
224    #
225    # ModulePathList will keep track of the standalone module path that
226    # we just installed. If a new module's path in that list
227    # (only multiple INF in one directory will be so), we will
228    # install them directly. If not, we will try to create a new directory
229    # for it.
230    #
231    ModulePathList = []
232
233    #
234    # Check module exist and install
235    #
236    Module = None
237    NewDict = Sdict()
238    for Guid, Version, Name, Path in DistPkg.ModuleSurfaceArea:
239        ModulePath = Path
240        Module = DistPkg.ModuleSurfaceArea[Guid, Version, Name, Path]
241        Logger.Info(ST.MSG_INSTALL_MODULE % Module.GetName())
242        if Dep.CheckModuleExists(Guid, Version, Name, Path):
243            Logger.Quiet(ST.WRN_MODULE_EXISTED %Path)
244        #
245        # here check for the multiple inf share the same module path cases:
246        # they should be installed into the same directory
247        #
248        ModuleFullPath = \
249        os.path.normpath(os.path.join(WorkspaceDir, ModulePath))
250        if ModuleFullPath not in ModulePathList:
251            NewModulePath = InstallNewModule(WorkspaceDir, ModulePath, ModulePathList)
252            NewModuleFullPath = os.path.normpath(os.path.join(WorkspaceDir, NewModulePath))
253            ModulePathList.append(NewModuleFullPath)
254        else:
255            NewModulePath = ModulePath
256
257        InstallModuleContent(ModulePath, NewModulePath, '', Module, ContentZipFile, WorkspaceDir, ModuleList, None,
258                             DistPkg.Header.ReadOnly)
259        #
260        # Update module
261        #
262        Module.SetModulePath(Module.GetModulePath().replace(Path, NewModulePath, 1))
263
264        NewDict[Guid, Version, Name, Module.GetModulePath()] = Module
265
266    #
267    # generate all inf for modules
268    #
269    for (Module, Package) in ModuleList:
270        CheckCNameInModuleRedefined(Module, DistPkg)
271        FilePath = ModuleToInf(Module, Package, DistPkg.Header)
272        Md5Signature = md5(__FileHookOpen__(str(FilePath), 'rb').read())
273        Md5Sum = Md5Signature.hexdigest()
274        if Package:
275            if (FilePath, Md5Sum) not in Package.FileList:
276                Package.FileList.append((FilePath, Md5Sum))
277        else:
278            if (FilePath, Md5Sum) not in Module.FileList:
279                Module.FileList.append((FilePath, Md5Sum))
280        #
281        # append the module unicode files to Package FileList
282        #
283        for (FilePath, Md5Sum) in Module.FileList:
284            if str(FilePath).endswith('.uni') and Package and (FilePath, Md5Sum) not in Package.FileList:
285                Package.FileList.append((FilePath, Md5Sum))
286
287    return NewDict
288
289##
290# Get all protocol/ppi/guid CNames and pcd name from all dependent DEC file
291#
292def GetDepProtocolPpiGuidPcdNames(DePackageObjList):
293    #
294    # [[Dec1Protocol1, Dec1Protocol2...], [Dec2Protocols...],...]
295    #
296    DependentProtocolCNames = []
297    DependentPpiCNames = []
298    DependentGuidCNames = []
299    DependentPcdNames = []
300
301    for PackageObj in DePackageObjList:
302        #
303        # Get protocol CName list from all dependent DEC file
304        #
305        ProtocolCNames = []
306        for Protocol in PackageObj.GetProtocolList():
307            if Protocol.GetCName() not in ProtocolCNames:
308                ProtocolCNames.append(Protocol.GetCName())
309
310        DependentProtocolCNames.append(ProtocolCNames)
311
312        #
313        # Get Ppi CName list from all dependent DEC file
314        #
315        PpiCNames = []
316        for Ppi in PackageObj.GetPpiList():
317            if Ppi.GetCName() not in PpiCNames:
318                PpiCNames.append(Ppi.GetCName())
319
320        DependentPpiCNames.append(PpiCNames)
321
322        #
323        # Get Guid CName list from all dependent DEC file
324        #
325        GuidCNames = []
326        for Guid in PackageObj.GetGuidList():
327            if Guid.GetCName() not in GuidCNames:
328                GuidCNames.append(Guid.GetCName())
329
330        DependentGuidCNames.append(GuidCNames)
331
332        #
333        # Get PcdName list from all dependent DEC file
334        #
335        PcdNames = []
336        for Pcd in PackageObj.GetPcdList():
337            PcdName = '.'.join([Pcd.GetTokenSpaceGuidCName(), Pcd.GetCName()])
338            if PcdName not in PcdNames:
339                PcdNames.append(PcdName)
340
341        DependentPcdNames.append(PcdNames)
342
343
344    return DependentProtocolCNames, DependentPpiCNames, DependentGuidCNames, DependentPcdNames
345
346##
347# Check if protocol CName is redefined
348#
349def CheckProtoclCNameRedefined(Module, DependentProtocolCNames):
350    for ProtocolInModule in Module.GetProtocolList():
351        IsCNameDefined = False
352        for PackageProtocolCNames in DependentProtocolCNames:
353            if ProtocolInModule.GetCName() in PackageProtocolCNames:
354                if IsCNameDefined:
355                    Logger.Error("\nUPT", FORMAT_INVALID,
356                                 File = Module.GetFullPath(),
357                                 ExtraData = \
358                                 ST.ERR_INF_PARSER_ITEM_DUPLICATE_IN_DEC % ProtocolInModule.GetCName())
359                else:
360                    IsCNameDefined = True
361
362##
363# Check if Ppi CName is redefined
364#
365def CheckPpiCNameRedefined(Module, DependentPpiCNames):
366    for PpiInModule in Module.GetPpiList():
367        IsCNameDefined = False
368        for PackagePpiCNames in DependentPpiCNames:
369            if PpiInModule.GetCName() in PackagePpiCNames:
370                if IsCNameDefined:
371                    Logger.Error("\nUPT", FORMAT_INVALID,
372                                 File = Module.GetFullPath(),
373                                 ExtraData = ST.ERR_INF_PARSER_ITEM_DUPLICATE_IN_DEC % PpiInModule.GetCName())
374                else:
375                    IsCNameDefined = True
376
377##
378# Check if Guid CName is redefined
379#
380def CheckGuidCNameRedefined(Module, DependentGuidCNames):
381    for GuidInModule in Module.GetGuidList():
382        IsCNameDefined = False
383        for PackageGuidCNames in DependentGuidCNames:
384            if GuidInModule.GetCName() in PackageGuidCNames:
385                if IsCNameDefined:
386                    Logger.Error("\nUPT", FORMAT_INVALID,
387                                 File = Module.GetFullPath(),
388                                 ExtraData = \
389                                 ST.ERR_INF_PARSER_ITEM_DUPLICATE_IN_DEC % GuidInModule.GetCName())
390                else:
391                    IsCNameDefined = True
392
393##
394# Check if PcdName is redefined
395#
396def CheckPcdNameRedefined(Module, DependentPcdNames):
397    PcdObjs = []
398    if not Module.GetBinaryFileList():
399        PcdObjs += Module.GetPcdList()
400    else:
401        Binary = Module.GetBinaryFileList()[0]
402        for AsBuild in Binary.GetAsBuiltList():
403            PcdObjs += AsBuild.GetPatchPcdList() + AsBuild.GetPcdExList()
404
405    for PcdObj in PcdObjs:
406        PcdName = '.'.join([PcdObj.GetTokenSpaceGuidCName(), PcdObj.GetCName()])
407        IsPcdNameDefined = False
408        for PcdNames in DependentPcdNames:
409            if PcdName in PcdNames:
410                if IsPcdNameDefined:
411                    Logger.Error("\nUPT", FORMAT_INVALID,
412                                 File = Module.GetFullPath(),
413                                 ExtraData = ST.ERR_INF_PARSER_ITEM_DUPLICATE_IN_DEC % PcdName)
414                else:
415                    IsPcdNameDefined = True
416
417##
418# Check if any Protocol/Ppi/Guid and Pcd name is redefined in its dependent DEC files
419#
420def CheckCNameInModuleRedefined(Module, DistPkg):
421    DePackageObjList = []
422    #
423    # Get all dependent package objects
424    #
425    for Obj in Module.GetPackageDependencyList():
426        Guid = Obj.GetGuid()
427        Version = Obj.GetVersion()
428        for Key in DistPkg.PackageSurfaceArea:
429            if Key[0] == Guid and Key[1] == Version:
430                if DistPkg.PackageSurfaceArea[Key] not in DePackageObjList:
431                    DePackageObjList.append(DistPkg.PackageSurfaceArea[Key])
432
433    DependentProtocolCNames, DependentPpiCNames, DependentGuidCNames, DependentPcdNames = \
434    GetDepProtocolPpiGuidPcdNames(DePackageObjList)
435
436    CheckProtoclCNameRedefined(Module, DependentProtocolCNames)
437    CheckPpiCNameRedefined(Module, DependentPpiCNames)
438    CheckGuidCNameRedefined(Module, DependentGuidCNames)
439    CheckPcdNameRedefined(Module, DependentPcdNames)
440
441## GenToolMisc
442#
443# GenToolMisc
444#
445#
446def GenToolMisc(DistPkg, WorkspaceDir, ContentZipFile):
447    ToolObject = DistPkg.Tools
448    MiscObject = DistPkg.MiscellaneousFiles
449    DistPkg.FileList = []
450    FileList = []
451    ToolFileNum = 0
452    FileNum = 0
453    RootDir = WorkspaceDir
454
455    #
456    # FileList stores both tools files and misc files
457    # Misc file list must be appended to FileList *AFTER* Tools file list
458    #
459    if ToolObject:
460        FileList += ToolObject.GetFileList()
461        ToolFileNum = len(ToolObject.GetFileList())
462        if 'EDK_TOOLS_PATH' in os.environ:
463            RootDir = os.environ['EDK_TOOLS_PATH']
464    if MiscObject:
465        FileList += MiscObject.GetFileList()
466    for FileObject in FileList:
467        FileNum += 1
468        if FileNum > ToolFileNum:
469            #
470            # Misc files, root should be changed to WORKSPACE
471            #
472            RootDir = WorkspaceDir
473        File = ConvertPath(FileObject.GetURI())
474        ToFile = os.path.normpath(os.path.join(RootDir, File))
475        if os.path.exists(ToFile):
476            Logger.Info( ST.WRN_FILE_EXISTED % ToFile )
477            #
478            # ask for user input the new file name
479            #
480            Logger.Info( ST.MSG_NEW_FILE_NAME)
481            Input = stdin.readline()
482            Input = Input.replace('\r', '').replace('\n', '')
483            OrigPath = os.path.split(ToFile)[0]
484            ToFile = os.path.normpath(os.path.join(OrigPath, Input))
485        FromFile = os.path.join(FileObject.GetURI())
486        Md5Sum = InstallFile(ContentZipFile, FromFile, ToFile, DistPkg.Header.ReadOnly, FileObject.GetExecutable())
487        DistPkg.FileList.append((ToFile, Md5Sum))
488
489## Tool entrance method
490#
491# This method mainly dispatch specific methods per the command line options.
492# If no error found, return zero value so the caller of this tool can know
493# if it's executed successfully or not.
494#
495# @param  Options: command Options
496#
497def Main(Options = None):
498    try:
499        DataBase = GlobalData.gDB
500        WorkspaceDir = GlobalData.gWORKSPACE
501        if not Options.PackageFile:
502            Logger.Error("InstallPkg", OPTION_MISSING, ExtraData=ST.ERR_SPECIFY_PACKAGE)
503
504        # Get all Dist Info
505        DistInfoList = []
506        DistPkgList = []
507        Index = 1
508        for ToBeInstalledDist in Options.PackageFile:
509            #
510            # unzip dist.pkg file
511            #
512            DistInfoList.append(UnZipDp(WorkspaceDir, ToBeInstalledDist, Index))
513            DistPkgList.append(DistInfoList[-1][0])
514            Index += 1
515
516            #
517            # Add dist
518            #
519            GlobalData.gTO_BE_INSTALLED_DIST_LIST.append(DistInfoList[-1][0])
520
521        # Check for dependency
522        Dep = DependencyRules(DataBase, DistPkgList)
523
524        for ToBeInstalledDist in DistInfoList:
525            CheckInstallDpx(Dep, ToBeInstalledDist[0], ToBeInstalledDist[2])
526
527            #
528            # Install distribution
529            #
530            InstallDp(ToBeInstalledDist[0], ToBeInstalledDist[2], ToBeInstalledDist[1],
531                      Options, Dep, WorkspaceDir, DataBase)
532        ReturnCode = 0
533
534    except FatalError as XExcept:
535        ReturnCode = XExcept.args[0]
536        if Logger.GetLevel() <= Logger.DEBUG_9:
537            Logger.Quiet(ST.MSG_PYTHON_ON % (python_version(), platform) + format_exc())
538
539    except KeyboardInterrupt:
540        ReturnCode = ABORT_ERROR
541        if Logger.GetLevel() <= Logger.DEBUG_9:
542            Logger.Quiet(ST.MSG_PYTHON_ON % (python_version(), platform) + format_exc())
543
544    except:
545        ReturnCode = CODE_ERROR
546        Logger.Error(
547                    "\nInstallPkg",
548                    CODE_ERROR,
549                    ST.ERR_UNKNOWN_FATAL_INSTALL_ERR % Options.PackageFile,
550                    ExtraData=ST.MSG_SEARCH_FOR_HELP % ST.MSG_EDKII_MAIL_ADDR,
551                    RaiseError=False
552                    )
553        Logger.Quiet(ST.MSG_PYTHON_ON % (python_version(),
554            platform) + format_exc())
555    finally:
556        Logger.Quiet(ST.MSG_REMOVE_TEMP_FILE_STARTED)
557        for ToBeInstalledDist in DistInfoList:
558            if ToBeInstalledDist[3]:
559                ToBeInstalledDist[3].Close()
560            if ToBeInstalledDist[1]:
561                ToBeInstalledDist[1].Close()
562        for TempDir in GlobalData.gUNPACK_DIR:
563            rmtree(TempDir)
564        GlobalData.gUNPACK_DIR = []
565        Logger.Quiet(ST.MSG_REMOVE_TEMP_FILE_DONE)
566    if ReturnCode == 0:
567        Logger.Quiet(ST.MSG_FINISH)
568    return ReturnCode
569
570# BackupDist method
571#
572# This method will backup the Distribution file into the $(WORKSPACE)/conf/upt, and rename it
573# if there is already a same-named distribution existed.
574#
575# @param DpPkgFileName: The distribution path
576# @param Guid:          The distribution Guid
577# @param Version:       The distribution Version
578# @param WorkspaceDir:  The workspace directory
579# @retval NewDpPkgFileName: The exact backup file name
580#
581def BackupDist(DpPkgFileName, Guid, Version, WorkspaceDir):
582    DistFileName = os.path.split(DpPkgFileName)[1]
583    DestDir = os.path.normpath(os.path.join(WorkspaceDir, GlobalData.gUPT_DIR))
584    CreateDirectory(DestDir)
585    DestFile = os.path.normpath(os.path.join(DestDir, DistFileName))
586    if os.path.exists(DestFile):
587        FileName, Ext = os.path.splitext(DistFileName)
588        NewFileName = FileName + '_' + Guid + '_' + Version + Ext
589        DestFile = os.path.normpath(os.path.join(DestDir, NewFileName))
590        if os.path.exists(DestFile):
591            #
592            # ask for user input the new file name
593            #
594            Logger.Info( ST.MSG_NEW_FILE_NAME_FOR_DIST)
595            Input = stdin.readline()
596            Input = Input.replace('\r', '').replace('\n', '')
597            DestFile = os.path.normpath(os.path.join(DestDir, Input))
598    copyfile(DpPkgFileName, DestFile)
599    NewDpPkgFileName = DestFile[DestFile.find(DestDir) + len(DestDir) + 1:]
600    return NewDpPkgFileName
601
602## CheckInstallDpx method
603#
604#  check whether distribution could be installed
605#
606#   @param  Dep: the DependencyRules instance that used to check dependency
607#   @param  DistPkg: the distribution object
608#
609def CheckInstallDpx(Dep, DistPkg, DistPkgFileName):
610    #
611    # Check distribution package installed or not
612    #
613    if Dep.CheckDpExists(DistPkg.Header.GetGuid(),
614        DistPkg.Header.GetVersion()):
615        Logger.Error("InstallPkg",
616                     UPT_ALREADY_INSTALLED_ERROR,
617                     ST.WRN_DIST_PKG_INSTALLED % os.path.basename(DistPkgFileName))
618    #
619    # Check distribution dependency (all module dependency should be
620    # satisfied)
621    #
622    if not Dep.CheckInstallDpDepexSatisfied(DistPkg):
623        Logger.Error("InstallPkg", UNKNOWN_ERROR,
624            ST.ERR_PACKAGE_NOT_MATCH_DEPENDENCY,
625            ExtraData=DistPkg.Header.Name)
626
627## InstallModuleContent method
628#
629# If this is standalone module, then Package should be none,
630# ModulePath should be ''
631#   @param  FromPath: FromPath
632#   @param  NewPath: NewPath
633#   @param  ModulePath: ModulePath
634#   @param  Module: Module
635#   @param  ContentZipFile: ContentZipFile
636#   @param  WorkspaceDir: WorkspaceDir
637#   @param  ModuleList: ModuleList
638#   @param  Package: Package
639#
640def InstallModuleContent(FromPath, NewPath, ModulePath, Module, ContentZipFile,
641    WorkspaceDir, ModuleList, Package = None, ReadOnly = False):
642
643    if NewPath.startswith("\\") or NewPath.startswith("/"):
644        NewPath = NewPath[1:]
645
646    if not IsValidInstallPath(NewPath):
647        Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%NewPath)
648
649    NewModuleFullPath = os.path.normpath(os.path.join(WorkspaceDir, NewPath,
650        ConvertPath(ModulePath)))
651    Module.SetFullPath(os.path.normpath(os.path.join(NewModuleFullPath,
652        ConvertPath(Module.GetName()) + '.inf')))
653    Module.FileList = []
654
655    for MiscFile in Module.GetMiscFileList():
656        if not MiscFile:
657            continue
658        for Item in MiscFile.GetFileList():
659            File = Item.GetURI()
660            if File.startswith("\\") or File.startswith("/"):
661                File = File[1:]
662
663            if not IsValidInstallPath(File):
664                Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%File)
665
666            FromFile = os.path.join(FromPath, ModulePath, File)
667            Executable = Item.GetExecutable()
668            ToFile = os.path.normpath(os.path.join(NewModuleFullPath, ConvertPath(File)))
669            Md5Sum = InstallFile(ContentZipFile, FromFile, ToFile, ReadOnly, Executable)
670            if Package and ((ToFile, Md5Sum) not in Package.FileList):
671                Package.FileList.append((ToFile, Md5Sum))
672            elif Package:
673                continue
674            elif (ToFile, Md5Sum) not in Module.FileList:
675                Module.FileList.append((ToFile, Md5Sum))
676    for Item in Module.GetSourceFileList():
677        File = Item.GetSourceFile()
678        if File.startswith("\\") or File.startswith("/"):
679            File = File[1:]
680
681        if not IsValidInstallPath(File):
682            Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%File)
683
684        FromFile = os.path.join(FromPath, ModulePath, File)
685        ToFile = os.path.normpath(os.path.join(NewModuleFullPath, ConvertPath(File)))
686        Md5Sum = InstallFile(ContentZipFile, FromFile, ToFile, ReadOnly)
687        if Package and ((ToFile, Md5Sum) not in Package.FileList):
688            Package.FileList.append((ToFile, Md5Sum))
689        elif Package:
690            continue
691        elif (ToFile, Md5Sum) not in Module.FileList:
692            Module.FileList.append((ToFile, Md5Sum))
693    for Item in Module.GetBinaryFileList():
694        FileNameList = Item.GetFileNameList()
695        for FileName in FileNameList:
696            File = FileName.GetFilename()
697            if File.startswith("\\") or File.startswith("/"):
698                File = File[1:]
699
700            if not IsValidInstallPath(File):
701                Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%File)
702
703            FromFile = os.path.join(FromPath, ModulePath, File)
704            ToFile = os.path.normpath(os.path.join(NewModuleFullPath, ConvertPath(File)))
705            Md5Sum = InstallFile(ContentZipFile, FromFile, ToFile, ReadOnly)
706            if Package and ((ToFile, Md5Sum) not in Package.FileList):
707                Package.FileList.append((ToFile, Md5Sum))
708            elif Package:
709                continue
710            elif (ToFile, Md5Sum) not in Module.FileList:
711                Module.FileList.append((ToFile, Md5Sum))
712
713    InstallModuleContentZipFile(ContentZipFile, FromPath, ModulePath, WorkspaceDir, NewPath, Module, Package, ReadOnly,
714                                ModuleList)
715
716## InstallModuleContentZipFile
717#
718# InstallModuleContentZipFile
719#
720def InstallModuleContentZipFile(ContentZipFile, FromPath, ModulePath, WorkspaceDir, NewPath, Module, Package, ReadOnly,
721                                ModuleList):
722    #
723    # Extract other files under current module path in content Zip file but not listed in the description
724    #
725    if ContentZipFile:
726        for FileName in ContentZipFile.GetZipFile().namelist():
727            FileName = os.path.normpath(FileName)
728            CheckPath = os.path.normpath(os.path.join(FromPath, ModulePath))
729            if FileUnderPath(FileName, CheckPath):
730                if FileName.startswith("\\") or FileName.startswith("/"):
731                    FileName = FileName[1:]
732
733                if not IsValidInstallPath(FileName):
734                    Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%FileName)
735
736                FromFile = FileName
737                ToFile = os.path.normpath(os.path.join(WorkspaceDir,
738                        ConvertPath(FileName.replace(FromPath, NewPath, 1))))
739                CheckList = copy.copy(Module.FileList)
740                if Package:
741                    CheckList += Package.FileList
742                for Item in CheckList:
743                    if Item[0] == ToFile:
744                        break
745                else:
746                    Md5Sum = InstallFile(ContentZipFile, FromFile, ToFile, ReadOnly)
747                    if Package and ((ToFile, Md5Sum) not in Package.FileList):
748                        Package.FileList.append((ToFile, Md5Sum))
749                    elif Package:
750                        continue
751                    elif (ToFile, Md5Sum) not in Module.FileList:
752                        Module.FileList.append((ToFile, Md5Sum))
753
754    ModuleList.append((Module, Package))
755
756## FileUnderPath
757#  Check whether FileName started with directory specified by CheckPath
758#
759# @param FileName: the FileName need to be checked
760# @param CheckPath: the path need to be checked against
761# @return:  True or False
762#
763def FileUnderPath(FileName, CheckPath):
764    FileName = FileName.replace('\\', '/')
765    FileName = os.path.normpath(FileName)
766    CheckPath = CheckPath.replace('\\', '/')
767    CheckPath = os.path.normpath(CheckPath)
768    if FileName.startswith(CheckPath):
769        RemainingPath = os.path.normpath(FileName.replace(CheckPath, '', 1))
770        while RemainingPath.startswith('\\') or RemainingPath.startswith('/'):
771            RemainingPath = RemainingPath[1:]
772        if FileName == os.path.normpath(os.path.join(CheckPath, RemainingPath)):
773            return True
774
775    return False
776
777## InstallFile
778#  Extract File from Zipfile, set file attribute, and return the Md5Sum
779#
780# @return:  True or False
781#
782def InstallFile(ContentZipFile, FromFile, ToFile, ReadOnly, Executable=False):
783    if os.path.exists(os.path.normpath(ToFile)):
784        pass
785    else:
786        if not ContentZipFile or not ContentZipFile.UnpackFile(FromFile, ToFile):
787            Logger.Error("UPT", FILE_NOT_FOUND, ST.ERR_INSTALL_FILE_FROM_EMPTY_CONTENT % FromFile)
788
789        if ReadOnly:
790            if not Executable:
791                chmod(ToFile, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
792            else:
793                chmod(ToFile, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
794        elif Executable:
795            chmod(ToFile, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR | stat.S_IWGRP |
796                  stat.S_IWOTH | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
797        else:
798            chmod(ToFile, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
799
800    Md5Signature = md5(__FileHookOpen__(str(ToFile), 'rb').read())
801    Md5Sum = Md5Signature.hexdigest()
802
803    return Md5Sum
804
805## InstallPackageContent method
806#
807#   @param  FromPath: FromPath
808#   @param  ToPath: ToPath
809#   @param  Package: Package
810#   @param  ContentZipFile: ContentZipFile
811#   @param  Dep: Dep
812#   @param  WorkspaceDir: WorkspaceDir
813#   @param  ModuleList: ModuleList
814#
815def InstallPackageContent(FromPath, ToPath, Package, ContentZipFile, Dep,
816    WorkspaceDir, ModuleList, ReadOnly = False):
817    if Dep:
818        pass
819    Package.FileList = []
820
821    if ToPath.startswith("\\") or ToPath.startswith("/"):
822        ToPath = ToPath[1:]
823
824    if not IsValidInstallPath(ToPath):
825        Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%ToPath)
826
827    if FromPath.startswith("\\") or FromPath.startswith("/"):
828        FromPath = FromPath[1:]
829
830    if not IsValidInstallPath(FromPath):
831        Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%FromPath)
832
833    PackageFullPath = os.path.normpath(os.path.join(WorkspaceDir, ToPath))
834    for MiscFile in Package.GetMiscFileList():
835        for Item in MiscFile.GetFileList():
836            FileName = Item.GetURI()
837            if FileName.startswith("\\") or FileName.startswith("/"):
838                FileName = FileName[1:]
839
840            if not IsValidInstallPath(FileName):
841                Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%FileName)
842
843            FromFile = os.path.join(FromPath, FileName)
844            Executable = Item.GetExecutable()
845            ToFile =  (os.path.join(PackageFullPath, ConvertPath(FileName)))
846            Md5Sum = InstallFile(ContentZipFile, FromFile, ToFile, ReadOnly, Executable)
847            if (ToFile, Md5Sum) not in Package.FileList:
848                Package.FileList.append((ToFile, Md5Sum))
849    PackageIncludeArchList = []
850    for Item in Package.GetPackageIncludeFileList():
851        FileName = Item.GetFilePath()
852        if FileName.startswith("\\") or FileName.startswith("/"):
853            FileName = FileName[1:]
854
855        if not IsValidInstallPath(FileName):
856            Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%FileName)
857
858        FromFile = os.path.join(FromPath, FileName)
859        ToFile = os.path.normpath(os.path.join(PackageFullPath, ConvertPath(FileName)))
860        RetFile = ContentZipFile.UnpackFile(FromFile, ToFile)
861        if RetFile == '':
862            #
863            # a non-exist path in Zipfile will return '', which means an include directory in our case
864            # save the information for later DEC creation usage and also create the directory
865            #
866            PackageIncludeArchList.append([Item.GetFilePath(), Item.GetSupArchList()])
867            CreateDirectory(ToFile)
868            continue
869        if ReadOnly:
870            chmod(ToFile, stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH)
871        else:
872            chmod(ToFile, stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH|stat.S_IWUSR|stat.S_IWGRP|stat.S_IWOTH)
873        Md5Signature = md5(__FileHookOpen__(str(ToFile), 'rb').read())
874        Md5Sum = Md5Signature.hexdigest()
875        if (ToFile, Md5Sum) not in Package.FileList:
876            Package.FileList.append((ToFile, Md5Sum))
877    Package.SetIncludeArchList(PackageIncludeArchList)
878
879    for Item in Package.GetStandardIncludeFileList():
880        FileName = Item.GetFilePath()
881        if FileName.startswith("\\") or FileName.startswith("/"):
882            FileName = FileName[1:]
883
884        if not IsValidInstallPath(FileName):
885            Logger.Error("UPT", FORMAT_INVALID, ST.ERR_FILE_NAME_INVALIDE%FileName)
886
887        FromFile = os.path.join(FromPath, FileName)
888        ToFile = os.path.normpath(os.path.join(PackageFullPath, ConvertPath(FileName)))
889        Md5Sum = InstallFile(ContentZipFile, FromFile, ToFile, ReadOnly)
890        if (ToFile, Md5Sum) not in Package.FileList:
891            Package.FileList.append((ToFile, Md5Sum))
892
893    #
894    # Update package
895    #
896    Package.SetPackagePath(Package.GetPackagePath().replace(FromPath,
897        ToPath, 1))
898    Package.SetFullPath(os.path.normpath(os.path.join(PackageFullPath,
899        ConvertPath(Package.GetName()) + '.dec')))
900
901    #
902    # Install files in module
903    #
904    Module = None
905    ModuleDict = Package.GetModuleDict()
906    for ModuleGuid, ModuleVersion, ModuleName, ModulePath in ModuleDict:
907        Module = ModuleDict[ModuleGuid, ModuleVersion, ModuleName, ModulePath]
908        InstallModuleContent(FromPath, ToPath, ModulePath, Module,
909            ContentZipFile, WorkspaceDir, ModuleList, Package, ReadOnly)
910
911## GetDPFile method
912#
913#   @param  ZipFile: A ZipFile
914#
915def GetDPFile(ZipFile):
916    ContentFile = ''
917    DescFile = ''
918    for FileName in ZipFile.namelist():
919        if FileName.endswith('.content'):
920            if not ContentFile:
921                ContentFile = FileName
922                continue
923        elif FileName.endswith('.pkg'):
924            if not DescFile:
925                DescFile = FileName
926                continue
927        else:
928            continue
929
930        Logger.Error("PackagingTool", FILE_TYPE_MISMATCH,
931            ExtraData=ST.ERR_DIST_FILE_TOOMANY)
932    if not DescFile or not ContentFile:
933        Logger.Error("PackagingTool", FILE_UNKNOWN_ERROR,
934            ExtraData=ST.ERR_DIST_FILE_TOOFEW)
935    return DescFile, ContentFile
936
937## InstallDp method
938#
939#   Install the distribution to current workspace
940#
941def InstallDp(DistPkg, DpPkgFileName, ContentZipFile, Options, Dep, WorkspaceDir, DataBase):
942    #
943    # PackageList, ModuleList record the information for the meta-data
944    # files that need to be generated later
945    #
946    PackageList = []
947    ModuleList = []
948    DistPkg.PackageSurfaceArea = GetPackageList(DistPkg, Dep, WorkspaceDir, Options,
949                                                ContentZipFile, ModuleList, PackageList)
950
951    DistPkg.ModuleSurfaceArea = GetModuleList(DistPkg, Dep, WorkspaceDir, ContentZipFile, ModuleList)
952
953    GenToolMisc(DistPkg, WorkspaceDir, ContentZipFile)
954
955    #
956    # copy "Distribution File" to directory $(WORKSPACE)/conf/upt
957    #
958    DistFileName = os.path.split(DpPkgFileName)[1]
959    NewDpPkgFileName = BackupDist(DpPkgFileName, DistPkg.Header.GetGuid(), DistPkg.Header.GetVersion(), WorkspaceDir)
960
961    #
962    # update database
963    #
964    Logger.Quiet(ST.MSG_UPDATE_PACKAGE_DATABASE)
965    DataBase.AddDPObject(DistPkg, NewDpPkgFileName, DistFileName,
966                   DistPkg.Header.RePackage)
967
968