1## @file
2#
3# This file is the main entry for UPT
4#
5# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6#
7# SPDX-License-Identifier: BSD-2-Clause-Patent
8#
9
10'''
11UPT
12'''
13
14## import modules
15#
16import locale
17import sys
18from imp import reload
19encoding = locale.getdefaultlocale()[1]
20if encoding:
21    reload(sys)
22    sys.setdefaultencoding(encoding)
23from Core import FileHook
24import os.path
25from sys import platform
26import platform as pf
27from optparse import OptionParser
28from traceback import format_exc
29from platform import python_version
30
31from Logger import StringTable as ST
32import Logger.Log as Logger
33from Logger.StringTable import MSG_VERSION
34from Logger.StringTable import MSG_DESCRIPTION
35from Logger.StringTable import MSG_USAGE
36from Logger.ToolError import FILE_NOT_FOUND
37from Logger.ToolError import OPTION_MISSING
38from Logger.ToolError import FILE_TYPE_MISMATCH
39from Logger.ToolError import OPTION_CONFLICT
40from Logger.ToolError import FatalError
41from Logger.ToolError import UPT_ALREADY_INSTALLED_ERROR
42from Common.MultipleWorkspace import MultipleWorkspace as mws
43
44import MkPkg
45import InstallPkg
46import RmPkg
47import InventoryWs
48import ReplacePkg
49import TestInstall
50from Library.Misc import GetWorkspace
51from Library import GlobalData
52from Core.IpiDb import IpiDatabase
53from BuildVersion import gBUILD_VERSION
54
55## CheckConflictOption
56#
57# CheckConflictOption
58#
59def CheckConflictOption(Opt):
60    if (Opt.PackFileToCreate or Opt.PackFileToInstall or Opt.PackFileToRemove or Opt.PackFileToReplace) \
61    and Opt.InventoryWs:
62        Logger.Error("UPT", OPTION_CONFLICT, ExtraData=ST.ERR_L_OA_EXCLUSIVE)
63    elif Opt.PackFileToReplace and (Opt.PackFileToCreate or Opt.PackFileToInstall or Opt.PackFileToRemove):
64        Logger.Error("UPT", OPTION_CONFLICT, ExtraData=ST.ERR_U_ICR_EXCLUSIVE)
65    elif (Opt.PackFileToCreate and Opt.PackFileToInstall and Opt.PackFileToRemove):
66        Logger.Error("UPT", OPTION_CONFLICT, ExtraData=ST.ERR_REQUIRE_I_C_R_OPTION)
67    elif Opt.PackFileToCreate and Opt.PackFileToInstall:
68        Logger.Error("UPT", OPTION_CONFLICT, ExtraData=ST.ERR_I_C_EXCLUSIVE)
69    elif Opt.PackFileToInstall and Opt.PackFileToRemove:
70        Logger.Error("UPT", OPTION_CONFLICT, ExtraData=ST.ERR_I_R_EXCLUSIVE)
71    elif Opt.PackFileToCreate and  Opt.PackFileToRemove:
72        Logger.Error("UPT", OPTION_CONFLICT, ExtraData=ST.ERR_C_R_EXCLUSIVE)
73    elif Opt.TestDistFiles and (Opt.PackFileToCreate or Opt.PackFileToInstall \
74                                or Opt.PackFileToRemove or Opt.PackFileToReplace):
75        Logger.Error("UPT", OPTION_CONFLICT, ExtraData=ST.ERR_C_R_EXCLUSIVE)
76
77    if Opt.CustomPath and Opt.UseGuidedPkgPath:
78        Logger.Warn("UPT", ST.WARN_CUSTOMPATH_OVERRIDE_USEGUIDEDPATH)
79        Opt.UseGuidedPkgPath = False
80
81## SetLogLevel
82#
83def SetLogLevel(Opt):
84    if Opt.opt_verbose:
85        Logger.SetLevel(Logger.VERBOSE)
86    elif Opt.opt_quiet:
87        Logger.SetLevel(Logger.QUIET + 1)
88    elif Opt.debug_level is not None:
89        if Opt.debug_level < 0 or Opt.debug_level > 9:
90            Logger.Warn("UPT", ST.ERR_DEBUG_LEVEL)
91            Logger.SetLevel(Logger.INFO)
92        else:
93            Logger.SetLevel(Opt.debug_level + 1)
94    elif Opt.opt_slient:
95        Logger.SetLevel(Logger.SILENT)
96    else:
97        Logger.SetLevel(Logger.INFO)
98
99## Main
100#
101# Main
102#
103def Main():
104    Logger.Initialize()
105
106    Parser = OptionParser(version=(MSG_VERSION + ' Build ' + gBUILD_VERSION), description=MSG_DESCRIPTION,
107                          prog="UPT.exe", usage=MSG_USAGE)
108
109    Parser.add_option("-d", "--debug", action="store", type="int", dest="debug_level", help=ST.HLP_PRINT_DEBUG_INFO)
110
111    Parser.add_option("-v", "--verbose", action="store_true", dest="opt_verbose",
112                      help=ST.HLP_PRINT_INFORMATIONAL_STATEMENT)
113
114    Parser.add_option("-s", "--silent", action="store_true", dest="opt_slient", help=ST.HLP_RETURN_NO_DISPLAY)
115
116    Parser.add_option("-q", "--quiet", action="store_true", dest="opt_quiet", help=ST.HLP_RETURN_AND_DISPLAY)
117
118    Parser.add_option("-i", "--install", action="append", type="string", dest="Install_Distribution_Package_File",
119                      help=ST.HLP_SPECIFY_PACKAGE_NAME_INSTALL)
120
121    Parser.add_option("-c", "--create", action="store", type="string", dest="Create_Distribution_Package_File",
122                      help=ST.HLP_SPECIFY_PACKAGE_NAME_CREATE)
123
124    Parser.add_option("-r", "--remove", action="store", type="string", dest="Remove_Distribution_Package_File",
125                      help=ST.HLP_SPECIFY_PACKAGE_NAME_REMOVE)
126
127    Parser.add_option("-t", "--template", action="store", type="string", dest="Package_Information_Data_File",
128                      help=ST.HLP_SPECIFY_TEMPLATE_NAME_CREATE)
129
130    Parser.add_option("-p", "--dec-filename", action="append", type="string", dest="EDK2_DEC_Filename",
131                      help=ST.HLP_SPECIFY_DEC_NAME_CREATE)
132
133    Parser.add_option("-m", "--inf-filename", action="append", type="string", dest="EDK2_INF_Filename",
134                      help=ST.HLP_SPECIFY_INF_NAME_CREATE)
135
136    Parser.add_option("-l", "--list", action="store_true", dest="List_Dist_Installed",
137                      help=ST.HLP_LIST_DIST_INSTALLED)
138
139    Parser.add_option("-f", "--force", action="store_true", dest="Yes", help=ST.HLP_DISABLE_PROMPT)
140
141    Parser.add_option("-n", "--custom-path", action="store_true", dest="CustomPath", help=ST.HLP_CUSTOM_PATH_PROMPT)
142
143    Parser.add_option("-x", "--free-lock", action="store_true", dest="SkipLock", help=ST.HLP_SKIP_LOCK_CHECK)
144
145    Parser.add_option("-u", "--replace", action="store", type="string", dest="Replace_Distribution_Package_File",
146                      help=ST.HLP_SPECIFY_PACKAGE_NAME_REPLACE)
147
148    Parser.add_option("-o", "--original", action="store", type="string", dest="Original_Distribution_Package_File",
149                      help=ST.HLP_SPECIFY_PACKAGE_NAME_TO_BE_REPLACED)
150
151    Parser.add_option("--use-guided-paths", action="store_true", dest="Use_Guided_Paths", help=ST.HLP_USE_GUIDED_PATHS)
152
153    Parser.add_option("-j", "--test-install", action="append", type="string",
154                      dest="Test_Install_Distribution_Package_Files", help=ST.HLP_TEST_INSTALL)
155
156    Opt = Parser.parse_args()[0]
157
158    Var2Var = [
159        ("PackageInformationDataFile", Opt.Package_Information_Data_File),
160        ("PackFileToInstall", Opt.Install_Distribution_Package_File),
161        ("PackFileToCreate", Opt.Create_Distribution_Package_File),
162        ("PackFileToRemove", Opt.Remove_Distribution_Package_File),
163        ("PackageFileList", Opt.EDK2_DEC_Filename),
164        ("ModuleFileList", Opt.EDK2_INF_Filename),
165        ("InventoryWs", Opt.List_Dist_Installed),
166        ("PackFileToReplace", Opt.Replace_Distribution_Package_File),
167        ("PackFileToBeReplaced", Opt.Original_Distribution_Package_File),
168        ("UseGuidedPkgPath", Opt.Use_Guided_Paths),
169        ("TestDistFiles", Opt.Test_Install_Distribution_Package_Files)
170    ]
171
172    for Var in Var2Var:
173        setattr(Opt, Var[0], Var[1])
174
175    try:
176        GlobalData.gWORKSPACE, GlobalData.gPACKAGE_PATH = GetWorkspace()
177    except FatalError as XExcept:
178        if Logger.GetLevel() <= Logger.DEBUG_9:
179            Logger.Quiet(ST.MSG_PYTHON_ON % (python_version(), platform) + format_exc())
180        return XExcept.args[0]
181
182    # Support WORKSPACE is a long path
183    # Only works for windows system
184    if pf.system() == 'Windows':
185        Vol = 'B:'
186        for Index in range(90, 65, -1):
187            Vol = chr(Index) + ':'
188            if not os.path.isdir(Vol):
189                os.system('subst %s "%s"' % (Vol, GlobalData.gWORKSPACE))
190                break
191        GlobalData.gWORKSPACE = '%s\\' % Vol
192
193    WorkspaceDir = GlobalData.gWORKSPACE
194
195    SetLogLevel(Opt)
196
197    Mgr = FileHook.RecoverMgr(WorkspaceDir)
198    FileHook.SetRecoverMgr(Mgr)
199
200    GlobalData.gDB = IpiDatabase(os.path.normpath(os.path.join(WorkspaceDir, \
201                                                               "Conf/DistributionPackageDatabase.db")), WorkspaceDir)
202    GlobalData.gDB.InitDatabase(Opt.SkipLock)
203
204    #
205    # Make sure the Db will get closed correctly
206    #
207    try:
208        ReturnCode = 0
209        CheckConflictOption(Opt)
210
211        RunModule = None
212        if Opt.PackFileToCreate:
213            if Opt.PackageInformationDataFile:
214                if not os.path.exists(Opt.PackageInformationDataFile):
215                    if not os.path.exists(os.path.join(WorkspaceDir, Opt.PackageInformationDataFile)):
216                        Logger.Error("\nUPT", FILE_NOT_FOUND, ST.ERR_NO_TEMPLATE_FILE % Opt.PackageInformationDataFile)
217                    else:
218                        Opt.PackageInformationDataFile = os.path.join(WorkspaceDir, Opt.PackageInformationDataFile)
219            else:
220                Logger.Error("UPT", OPTION_MISSING, ExtraData=ST.ERR_REQUIRE_T_OPTION)
221            if not Opt.PackFileToCreate.endswith('.dist'):
222                Logger.Error("CreatePkg", FILE_TYPE_MISMATCH, ExtraData=ST.ERR_DIST_EXT_ERROR % Opt.PackFileToCreate)
223            RunModule = MkPkg.Main
224
225        elif Opt.PackFileToInstall:
226            AbsPath = []
227            for Item in Opt.PackFileToInstall:
228                if not Item.endswith('.dist'):
229                    Logger.Error("InstallPkg", FILE_TYPE_MISMATCH, ExtraData=ST.ERR_DIST_EXT_ERROR % Item)
230
231                AbsPath.append(GetFullPathDist(Item, WorkspaceDir))
232                if not AbsPath:
233                    Logger.Error("InstallPkg", FILE_NOT_FOUND, ST.ERR_INSTALL_DIST_NOT_FOUND % Item)
234
235            Opt.PackFileToInstall = AbsPath
236            setattr(Opt, 'PackageFile', Opt.PackFileToInstall)
237            RunModule = InstallPkg.Main
238
239        elif Opt.PackFileToRemove:
240            if not Opt.PackFileToRemove.endswith('.dist'):
241                Logger.Error("RemovePkg", FILE_TYPE_MISMATCH, ExtraData=ST.ERR_DIST_EXT_ERROR % Opt.PackFileToRemove)
242            head, tail = os.path.split(Opt.PackFileToRemove)
243            if head or not tail:
244                Logger.Error("RemovePkg",
245                             FILE_TYPE_MISMATCH,
246                             ExtraData=ST.ERR_DIST_FILENAME_ONLY_FOR_REMOVE % Opt.PackFileToRemove)
247
248            setattr(Opt, 'DistributionFile', Opt.PackFileToRemove)
249            RunModule = RmPkg.Main
250        elif Opt.InventoryWs:
251            RunModule = InventoryWs.Main
252
253        elif Opt.PackFileToBeReplaced and not Opt.PackFileToReplace:
254            Logger.Error("ReplacePkg", OPTION_MISSING, ExtraData=ST.ERR_REQUIRE_U_OPTION)
255
256        elif Opt.PackFileToReplace:
257            if not Opt.PackFileToReplace.endswith('.dist'):
258                Logger.Error("ReplacePkg", FILE_TYPE_MISMATCH, ExtraData=ST.ERR_DIST_EXT_ERROR % Opt.PackFileToReplace)
259            if not Opt.PackFileToBeReplaced:
260                Logger.Error("ReplacePkg", OPTION_MISSING, ExtraData=ST.ERR_REQUIRE_O_OPTION)
261            if not Opt.PackFileToBeReplaced.endswith('.dist'):
262                Logger.Error("ReplacePkg",
263                             FILE_TYPE_MISMATCH,
264                             ExtraData=ST.ERR_DIST_EXT_ERROR % Opt.PackFileToBeReplaced)
265
266            head, tail = os.path.split(Opt.PackFileToBeReplaced)
267            if head or not tail:
268                Logger.Error("ReplacePkg",
269                             FILE_TYPE_MISMATCH,
270                             ExtraData=ST.ERR_DIST_FILENAME_ONLY_FOR_REPLACE_ORIG % Opt.PackFileToBeReplaced)
271
272            AbsPath = GetFullPathDist(Opt.PackFileToReplace, WorkspaceDir)
273            if not AbsPath:
274                Logger.Error("ReplacePkg", FILE_NOT_FOUND, ST.ERR_REPLACE_DIST_NOT_FOUND % Opt.PackFileToReplace)
275
276            Opt.PackFileToReplace = AbsPath
277            RunModule = ReplacePkg.Main
278
279        elif Opt.Test_Install_Distribution_Package_Files:
280            for Dist in Opt.Test_Install_Distribution_Package_Files:
281                if not Dist.endswith('.dist'):
282                    Logger.Error("TestInstall", FILE_TYPE_MISMATCH, ExtraData=ST.ERR_DIST_EXT_ERROR % Dist)
283
284            setattr(Opt, 'DistFiles', Opt.Test_Install_Distribution_Package_Files)
285            RunModule = TestInstall.Main
286
287        else:
288            Parser.print_usage()
289            return OPTION_MISSING
290
291        ReturnCode = RunModule(Opt)
292    except FatalError as XExcept:
293        ReturnCode = XExcept.args[0]
294        if Logger.GetLevel() <= Logger.DEBUG_9:
295            Logger.Quiet(ST.MSG_PYTHON_ON % (python_version(), platform) + \
296                         format_exc())
297    finally:
298        try:
299            if ReturnCode != 0 and ReturnCode != UPT_ALREADY_INSTALLED_ERROR:
300                Logger.Quiet(ST.MSG_RECOVER_START)
301                GlobalData.gDB.RollBack()
302                Mgr.rollback()
303                Logger.Quiet(ST.MSG_RECOVER_DONE)
304            else:
305                GlobalData.gDB.Commit()
306                Mgr.commit()
307        except Exception:
308            Logger.Quiet(ST.MSG_RECOVER_FAIL)
309        GlobalData.gDB.CloseDb()
310
311        if pf.system() == 'Windows':
312            os.system('subst %s /D' % GlobalData.gWORKSPACE.replace('\\', ''))
313
314    return ReturnCode
315
316## GetFullPathDist
317#
318#  This function will check DistFile existence, if not absolute path, then try current working directory,
319#  then $(WORKSPACE),and return the AbsPath. If file doesn't find, then return None
320#
321# @param DistFile:       The distribution file in either relative path or absolute path
322# @param WorkspaceDir:   Workspace Directory
323# @return AbsPath:       The Absolute path of the distribution file if existed, None else
324#
325def GetFullPathDist(DistFile, WorkspaceDir):
326    if os.path.isabs(DistFile):
327        if not (os.path.exists(DistFile) and os.path.isfile(DistFile)):
328            return None
329        else:
330            return DistFile
331    else:
332        AbsPath = os.path.normpath(os.path.join(os.getcwd(), DistFile))
333        if not (os.path.exists(AbsPath) and os.path.isfile(AbsPath)):
334            AbsPath = os.path.normpath(os.path.join(WorkspaceDir, DistFile))
335            if not (os.path.exists(AbsPath) and os.path.isfile(AbsPath)):
336                return None
337
338        return AbsPath
339
340if __name__ == '__main__':
341    RETVAL = Main()
342    #
343    # 0-127 is a safe return range, and 1 is a standard default error
344    #
345    if RETVAL < 0 or RETVAL > 127:
346        RETVAL = 1
347    sys.exit(RETVAL)
348