summaryrefslogtreecommitdiffstats
path: root/BaseTools/Source/Python/UPT/Library/Misc.py
blob: 77ba3584e00054b56aa94535e59f676db44a0fee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
## @file
# Common routines used by all tools
#
# Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#

'''
Misc
'''

##
# Import Modules
#
import os.path
from os import access
from os import F_OK
from os import makedirs
from os import getcwd
from os import chdir
from os import listdir
from os import remove
from os import rmdir
from os import linesep
from os import walk
from os import environ
import re
from collections import OrderedDict as Sdict

import Logger.Log as Logger
from Logger import StringTable as ST
from Logger import ToolError
from Library import GlobalData
from Library.DataType import SUP_MODULE_LIST
from Library.DataType import END_OF_LINE
from Library.DataType import TAB_SPLIT
from Library.DataType import TAB_LANGUAGE_EN_US
from Library.DataType import TAB_LANGUAGE_EN
from Library.DataType import TAB_LANGUAGE_EN_X
from Library.DataType import TAB_UNI_FILE_SUFFIXS
from Library.StringUtils import GetSplitValueList
from Library.ParserValidate import IsValidHexVersion
from Library.ParserValidate import IsValidPath
from Object.POM.CommonObject import TextObject
from Core.FileHook import __FileHookOpen__
from Common.MultipleWorkspace import MultipleWorkspace as mws

## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C
# structure style
#
# @param      Guid:    The GUID string
#
def GuidStringToGuidStructureString(Guid):
    GuidList = Guid.split('-')
    Result = '{'
    for Index in range(0, 3, 1):
        Result = Result + '0x' + GuidList[Index] + ', '
    Result = Result + '{0x' + GuidList[3][0:2] + ', 0x' + GuidList[3][2:4]
    for Index in range(0, 12, 2):
        Result = Result + ', 0x' + GuidList[4][Index:Index + 2]
    Result += '}}'
    return Result

## Check whether GUID string is of format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
#
# @param      GuidValue:   The GUID value
#
def CheckGuidRegFormat(GuidValue):
    ## Regular expression used to find out register format of GUID
    #
    RegFormatGuidPattern = re.compile("^\s*([0-9a-fA-F]){8}-"
                                       "([0-9a-fA-F]){4}-"
                                       "([0-9a-fA-F]){4}-"
                                       "([0-9a-fA-F]){4}-"
                                       "([0-9a-fA-F]){12}\s*$")

    if RegFormatGuidPattern.match(GuidValue):
        return True
    else:
        return False


## Convert GUID string in C structure style to
# xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
#
# @param      GuidValue:   The GUID value in C structure format
#
def GuidStructureStringToGuidString(GuidValue):
    GuidValueString = GuidValue.lower().replace("{", "").replace("}", "").\
    replace(" ", "").replace(";", "")
    GuidValueList = GuidValueString.split(",")
    if len(GuidValueList) != 11:
        return ''
    try:
        return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
                int(GuidValueList[0], 16),
                int(GuidValueList[1], 16),
                int(GuidValueList[2], 16),
                int(GuidValueList[3], 16),
                int(GuidValueList[4], 16),
                int(GuidValueList[5], 16),
                int(GuidValueList[6], 16),
                int(GuidValueList[7], 16),
                int(GuidValueList[8], 16),
                int(GuidValueList[9], 16),
                int(GuidValueList[10], 16)
                )
    except BaseException:
        return ''

## Create directories
#
# @param      Directory:   The directory name
#
def CreateDirectory(Directory):
    if Directory is None or Directory.strip() == "":
        return True
    try:
        if not access(Directory, F_OK):
            makedirs(Directory)
    except BaseException:
        return False
    return True

## Remove directories, including files and sub-directories in it
#
# @param      Directory:   The directory name
#
def RemoveDirectory(Directory, Recursively=False):
    if Directory is None or Directory.strip() == "" or not \
    os.path.exists(Directory):
        return
    if Recursively:
        CurrentDirectory = getcwd()
        chdir(Directory)
        for File in listdir("."):
            if os.path.isdir(File):
                RemoveDirectory(File, Recursively)
            else:
                remove(File)
        chdir(CurrentDirectory)
    rmdir(Directory)

## Store content in file
#
# This method is used to save file only when its content is changed. This is
# quite useful for "make" system to decide what will be re-built and what
# won't.
#
# @param      File:            The path of file
# @param      Content:         The new content of the file
# @param      IsBinaryFile:    The flag indicating if the file is binary file
#                              or not
#
def SaveFileOnChange(File, Content, IsBinaryFile=True):
    if os.path.exists(File):
        if IsBinaryFile:
            try:
                if Content == __FileHookOpen__(File, "rb").read():
                    return False
            except BaseException:
                Logger.Error(None, ToolError.FILE_OPEN_FAILURE, ExtraData=File)
        else:
            try:
                if Content == __FileHookOpen__(File, "r").read():
                    return False
            except BaseException:
                Logger.Error(None, ToolError.FILE_OPEN_FAILURE, ExtraData=File)

    CreateDirectory(os.path.dirname(File))
    if IsBinaryFile:
        try:
            FileFd = __FileHookOpen__(File, "wb")
            FileFd.write(Content)
            FileFd.close()
        except BaseException:
            Logger.Error(None, ToolError.FILE_CREATE_FAILURE, ExtraData=File)
    else:
        try:
            FileFd = __FileHookOpen__(File, "w")
            FileFd.write(Content)
            FileFd.close()
        except BaseException:
            Logger.Error(None, ToolError.FILE_CREATE_FAILURE, ExtraData=File)

    return True

## Get all files of a directory
#
# @param Root:       Root dir
# @param SkipList :  The files need be skipped
#
def GetFiles(Root, SkipList=None, FullPath=True):
    OriPath = os.path.normpath(Root)
    FileList = []
    for Root, Dirs, Files in walk(Root):
        if SkipList:
            for Item in SkipList:
                if Item in Dirs:
                    Dirs.remove(Item)
                if Item in Files:
                    Files.remove(Item)
        for Dir in Dirs:
            if Dir.startswith('.'):
                Dirs.remove(Dir)

        for File in Files:
            if File.startswith('.'):
                continue
            File = os.path.normpath(os.path.join(Root, File))
            if not FullPath:
                File = File[len(OriPath) + 1:]
            FileList.append(File)

    return FileList

## Get all non-metadata files of a directory
#
# @param Root:       Root Dir
# @param SkipList :  List of path need be skipped
# @param FullPath:  True if the returned file should be full path
# @param PrefixPath: the path that need to be added to the files found
# @return: the list of files found
#
def GetNonMetaDataFiles(Root, SkipList, FullPath, PrefixPath):
    FileList = GetFiles(Root, SkipList, FullPath)
    NewFileList = []
    for File in FileList:
        ExtName = os.path.splitext(File)[1]
        #
        # skip '.dec', '.inf', '.dsc', '.fdf' files
        #
        if ExtName.lower() not in ['.dec', '.inf', '.dsc', '.fdf']:
            NewFileList.append(os.path.normpath(os.path.join(PrefixPath, File)))

    return NewFileList

## Check if given file exists or not
#
# @param      File:    File name or path to be checked
# @param      Dir:     The directory the file is relative to
#
def ValidFile(File, Ext=None):
    File = File.replace('\\', '/')
    if Ext is not None:
        FileExt = os.path.splitext(File)[1]
        if FileExt.lower() != Ext.lower():
            return False
    if not os.path.exists(File):
        return False
    return True

## RealPath
#
# @param      File:    File name or path to be checked
# @param      Dir:     The directory the file is relative to
# @param      OverrideDir:     The override directory
#
def RealPath(File, Dir='', OverrideDir=''):
    NewFile = os.path.normpath(os.path.join(Dir, File))
    NewFile = GlobalData.gALL_FILES[NewFile]
    if not NewFile and OverrideDir:
        NewFile = os.path.normpath(os.path.join(OverrideDir, File))
        NewFile = GlobalData.gALL_FILES[NewFile]
    return NewFile

## RealPath2
#
# @param      File:    File name or path to be checked
# @param      Dir:     The directory the file is relative to
# @param      OverrideDir:     The override directory
#
def RealPath2(File, Dir='', OverrideDir=''):
    if OverrideDir:
        NewFile = GlobalData.gALL_FILES[os.path.normpath(os.path.join\
                                                        (OverrideDir, File))]
        if NewFile:
            if OverrideDir[-1] == os.path.sep:
                return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]
            else:
                return NewFile[len(OverrideDir) + 1:], \
            NewFile[0:len(OverrideDir)]

    NewFile = GlobalData.gALL_FILES[os.path.normpath(os.path.join(Dir, File))]
    if NewFile:
        if Dir:
            if Dir[-1] == os.path.sep:
                return NewFile[len(Dir):], NewFile[0:len(Dir)]
            else:
                return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)]
        else:
            return NewFile, ''

    return None, None

## CommonPath
#
# @param PathList: PathList
#
def CommonPath(PathList):
    Path1 = min(PathList).split(os.path.sep)
    Path2 = max(PathList).split(os.path.sep)
    for Index in range(min(len(Path1), len(Path2))):
        if Path1[Index] != Path2[Index]:
            return os.path.sep.join(Path1[:Index])
    return os.path.sep.join(Path1)

## PathClass
#
class PathClass(object):
    def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,
                 Arch='COMMON', ToolChainFamily='', Target='', TagName='', \
                 ToolCode=''):
        self.Arch = Arch
        self.File = str(File)
        if os.path.isabs(self.File):
            self.Root = ''
            self.AlterRoot = ''
        else:
            self.Root = str(Root)
            self.AlterRoot = str(AlterRoot)

        #
        # Remove any '.' and '..' in path
        #
        if self.Root:
            self.Path = os.path.normpath(os.path.join(self.Root, self.File))
            self.Root = os.path.normpath(CommonPath([self.Root, self.Path]))
            #
            # eliminate the side-effect of 'C:'
            #
            if self.Root[-1] == ':':
                self.Root += os.path.sep
            #
            # file path should not start with path separator
            #
            if self.Root[-1] == os.path.sep:
                self.File = self.Path[len(self.Root):]
            else:
                self.File = self.Path[len(self.Root) + 1:]
        else:
            self.Path = os.path.normpath(self.File)

        self.SubDir, self.Name = os.path.split(self.File)
        self.BaseName, self.Ext = os.path.splitext(self.Name)

        if self.Root:
            if self.SubDir:
                self.Dir = os.path.join(self.Root, self.SubDir)
            else:
                self.Dir = self.Root
        else:
            self.Dir = self.SubDir

        if IsBinary:
            self.Type = Type
        else:
            self.Type = self.Ext.lower()

        self.IsBinary = IsBinary
        self.Target = Target
        self.TagName = TagName
        self.ToolCode = ToolCode
        self.ToolChainFamily = ToolChainFamily

        self._Key = None

    ## Convert the object of this class to a string
    #
    #  Convert member Path of the class to a string
    #
    def __str__(self):
        return self.Path

    ## Override __eq__ function
    #
    # Check whether PathClass are the same
    #
    def __eq__(self, Other):
        if isinstance(Other, type(self)):
            return self.Path == Other.Path
        else:
            return self.Path == str(Other)

    ## Override __hash__ function
    #
    # Use Path as key in hash table
    #
    def __hash__(self):
        return hash(self.Path)

    ## _GetFileKey
    #
    def _GetFileKey(self):
        if self._Key is None:
            self._Key = self.Path.upper()
        return self._Key
    ## Validate
    #
    def Validate(self, Type='', CaseSensitive=True):
        if GlobalData.gCASE_INSENSITIVE:
            CaseSensitive = False
        if Type and Type.lower() != self.Type:
            return ToolError.FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % \
        (self.File, Type, self.Type)

        RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)
        if not RealRoot and not RealFile:
            RealFile = self.File
            if self.AlterRoot:
                RealFile = os.path.join(self.AlterRoot, self.File)
            elif self.Root:
                RealFile = os.path.join(self.Root, self.File)
            return ToolError.FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile)

        ErrorCode = 0
        ErrorInfo = ''
        if RealRoot != self.Root or RealFile != self.File:
            if CaseSensitive and (RealFile != self.File or \
                                  (RealRoot != self.Root and RealRoot != \
                                   self.AlterRoot)):
                ErrorCode = ToolError.FILE_CASE_MISMATCH
                ErrorInfo = self.File + '\n\t' + RealFile + \
                 " [in file system]"

            self.SubDir, self.Name = os.path.split(RealFile)
            self.BaseName, self.Ext = os.path.splitext(self.Name)
            if self.SubDir:
                self.Dir = os.path.join(RealRoot, self.SubDir)
            else:
                self.Dir = RealRoot
            self.File = RealFile
            self.Root = RealRoot
            self.Path = os.path.join(RealRoot, RealFile)
        return ErrorCode, ErrorInfo

    Key = property(_GetFileKey)

## Get current workspace
#
#  get WORKSPACE from environment variable if present,if not use current working directory as WORKSPACE
#
def GetWorkspace():
    #
    # check WORKSPACE
    #
    if "WORKSPACE" in environ:
        WorkspaceDir = os.path.normpath(environ["WORKSPACE"])
        if not os.path.exists(WorkspaceDir):
            Logger.Error("UPT",
                         ToolError.UPT_ENVIRON_MISSING_ERROR,
                         ST.ERR_WORKSPACE_NOTEXIST,
                         ExtraData="%s" % WorkspaceDir)
    else:
        WorkspaceDir = os.getcwd()

    if WorkspaceDir[-1] == ':':
        WorkspaceDir += os.sep

    PackagesPath = os.environ.get("PACKAGES_PATH")
    mws.setWs(WorkspaceDir, PackagesPath)

    return WorkspaceDir, mws.PACKAGES_PATH

## Get relative path
#
#  use full path and workspace to get relative path
#  the destination of this function is mainly to resolve the root path issue(like c: or c:\)
#
#  @param Fullpath: a string of fullpath
#  @param Workspace: a string of workspace
#
def GetRelativePath(Fullpath, Workspace):

    RelativePath = ''
    if Workspace.endswith(os.sep):
        RelativePath = Fullpath[Fullpath.upper().find(Workspace.upper())+len(Workspace):]
    else:
        RelativePath = Fullpath[Fullpath.upper().find(Workspace.upper())+len(Workspace)+1:]

    return RelativePath

## Check whether all module types are in list
#
# check whether all module types (SUP_MODULE_LIST) are in list
#
# @param ModuleList:  a list of ModuleType
#
def IsAllModuleList(ModuleList):
    NewModuleList = [Module.upper() for Module in ModuleList]
    for Module in SUP_MODULE_LIST:
        if Module not in NewModuleList:
            return False
    else:
        return True

## Dictionary that use comment(GenericComment, TailComment) as value,
# if a new comment which key already in the dic is inserted, then the
# comment will be merged.
# Key is (Statement, SupArch), when TailComment is added, it will ident
# according to Statement
#
class MergeCommentDict(dict):
    ## []= operator
    #
    def __setitem__(self, Key, CommentVal):
        GenericComment, TailComment = CommentVal
        if Key in self:
            OrigVal1, OrigVal2 = dict.__getitem__(self, Key)
            Statement = Key[0]
            dict.__setitem__(self, Key, (OrigVal1 + GenericComment, OrigVal2 \
                                         + len(Statement) * ' ' + TailComment))
        else:
            dict.__setitem__(self, Key, (GenericComment, TailComment))

    ## =[] operator
    #
    def __getitem__(self, Key):
        return dict.__getitem__(self, Key)


## GenDummyHelpTextObj
#
# @retval HelpTxt:   Generated dummy help text object
#
def GenDummyHelpTextObj():
    HelpTxt = TextObject()
    HelpTxt.SetLang(TAB_LANGUAGE_EN_US)
    HelpTxt.SetString(' ')
    return HelpTxt

## ConvertVersionToDecimal, the minor version should be within 0 - 99
# <HexVersion>          ::=  "0x" <Major> <Minor>
# <Major>               ::=  (a-fA-F0-9){4}
# <Minor>               ::=  (a-fA-F0-9){4}
# <DecVersion>          ::=  (0-65535) ["." (0-99)]
#
# @param StringIn:  The string contains version defined in INF file.
#                   It can be Decimal or Hex
#
def ConvertVersionToDecimal(StringIn):
    if IsValidHexVersion(StringIn):
        Value = int(StringIn, 16)
        Major = Value >> 16
        Minor = Value & 0xFFFF
        MinorStr = str(Minor)
        if len(MinorStr) == 1:
            MinorStr = '0' + MinorStr
        return str(Major) + '.' + MinorStr
    else:
        if StringIn.find(TAB_SPLIT) != -1:
            return StringIn
        elif StringIn:
            return StringIn + '.0'
        else:
            #
            # when StringIn is '', return it directly
            #
            return StringIn

## GetHelpStringByRemoveHashKey
#
# Remove hash key at the header of string and return the remain.
#
# @param String:  The string need to be processed.
#
def GetHelpStringByRemoveHashKey(String):
    ReturnString = ''
    PattenRemoveHashKey = re.compile(r"^[#+\s]+", re.DOTALL)
    String = String.strip()
    if String == '':
        return String

    LineList = GetSplitValueList(String, END_OF_LINE)
    for Line in LineList:
        ValueList = PattenRemoveHashKey.split(Line)
        if len(ValueList) == 1:
            ReturnString += ValueList[0] + END_OF_LINE
        else:
            ReturnString += ValueList[1] + END_OF_LINE

    if ReturnString.endswith('\n') and not ReturnString.endswith('\n\n') and ReturnString != '\n':
        ReturnString = ReturnString[:-1]

    return ReturnString

## ConvPathFromAbsToRel
#
# Get relative file path from absolute path.
#
# @param Path:  The string contain file absolute path.
# @param Root:  The string contain the parent path of Path in.
#
#
def ConvPathFromAbsToRel(Path, Root):
    Path = os.path.normpath(Path)
    Root = os.path.normpath(Root)
    FullPath = os.path.normpath(os.path.join(Root, Path))

    #
    # If Path is absolute path.
    # It should be in Root.
    #
    if os.path.isabs(Path):
        return FullPath[FullPath.find(Root) + len(Root) + 1:]

    else:
        return Path

## ConvertPath
#
# Convert special characters to '_', '\' to '/'
# return converted path: Test!1.inf -> Test_1.inf
#
# @param Path: Path to be converted
#
def ConvertPath(Path):
    RetPath = ''
    for Char in Path.strip():
        if Char.isalnum() or Char in '.-_/':
            RetPath = RetPath + Char
        elif Char == '\\':
            RetPath = RetPath + '/'
        else:
            RetPath = RetPath + '_'
    return RetPath

## ConvertSpec
#
# during install, convert the Spec string extract from UPD into INF allowable definition,
# the difference is period is allowed in the former (not the first letter) but not in the latter.
# return converted Spec string
#
# @param SpecStr: SpecStr to be converted
#
def ConvertSpec(SpecStr):
    RetStr = ''
    for Char in SpecStr:
        if Char.isalnum() or Char == '_':
            RetStr = RetStr + Char
        else:
            RetStr = RetStr + '_'

    return RetStr


## IsEqualList
#
# Judge two lists are identical(contain same item).
# The rule is elements in List A are in List B and elements in List B are in List A.
#
# @param ListA, ListB  Lists need to be judged.
#
# @return True  ListA and ListB are identical
# @return False ListA and ListB are different with each other
#
def IsEqualList(ListA, ListB):
    if ListA == ListB:
        return True

    for ItemA in ListA:
        if not ItemA in ListB:
            return False

    for ItemB in ListB:
        if not ItemB in ListA:
            return False

    return True

## ConvertArchList
#
# Convert item in ArchList if the start character is lower case.
# In UDP spec, Arch is only allowed as: [A-Z]([a-zA-Z0-9])*
#
# @param ArchList The ArchList need to be converted.
#
# @return NewList  The ArchList been converted.
#
def ConvertArchList(ArchList):
    NewArchList = []
    if not ArchList:
        return NewArchList

    if isinstance(ArchList, list):
        for Arch in ArchList:
            Arch = Arch.upper()
            NewArchList.append(Arch)
    elif isinstance(ArchList, str):
        ArchList = ArchList.upper()
        NewArchList.append(ArchList)

    return NewArchList

## ProcessLineExtender
#
# Process the LineExtender of Line in LineList.
# If one line ends with a line extender, then it will be combined together with next line.
#
# @param LineList The LineList need to be processed.
#
# @return NewList  The ArchList been processed.
#
def ProcessLineExtender(LineList):
    NewList = []
    Count = 0
    while Count < len(LineList):
        if LineList[Count].strip().endswith("\\") and Count + 1 < len(LineList):
            NewList.append(LineList[Count].strip()[:-2] + LineList[Count + 1])
            Count = Count + 1
        else:
            NewList.append(LineList[Count])

        Count = Count + 1

    return NewList

## ProcessEdkComment
#
# Process EDK style comment in LineList: c style /* */ comment or cpp style // comment
#
#
# @param LineList The LineList need to be processed.
#
# @return LineList  The LineList been processed.
# @return FirstPos  Where Edk comment is first found, -1 if not found
#
def ProcessEdkComment(LineList):
    FindEdkBlockComment = False
    Count = 0
    StartPos = -1
    EndPos = -1
    FirstPos = -1

    while(Count < len(LineList)):
        Line = LineList[Count].strip()
        if Line.startswith("/*"):
            #
            # handling c style comment
            #
            StartPos = Count
            while Count < len(LineList):
                Line = LineList[Count].strip()
                if Line.endswith("*/"):
                    if (Count == StartPos) and Line.strip() == '/*/':
                        Count = Count + 1
                        continue
                    EndPos = Count
                    FindEdkBlockComment = True
                    break
                Count = Count + 1

            if FindEdkBlockComment:
                if FirstPos == -1:
                    FirstPos = StartPos
                for Index in range(StartPos, EndPos+1):
                    LineList[Index] = ''
                FindEdkBlockComment = False
        elif Line.find("//") != -1 and not Line.startswith("#"):
            #
            # handling cpp style comment
            #
            LineList[Count] = Line.replace("//", '#')
            if FirstPos == -1:
                FirstPos = Count

        Count = Count + 1

    return LineList, FirstPos

## GetLibInstanceInfo
#
# Get the information from Library Instance INF file.
#
# @param string.  A string start with # and followed by INF file path
# @param WorkSpace. The WorkSpace directory used to combined with INF file path.
#
# @return GUID, Version
def GetLibInstanceInfo(String, WorkSpace, LineNo):

    FileGuidString = ""
    VerString = ""

    OriginalString = String
    String = String.strip()
    if not String:
        return None, None
    #
    # Remove "#" characters at the beginning
    #
    String = GetHelpStringByRemoveHashKey(String)
    String = String.strip()

    #
    # Validate file name exist.
    #
    FullFileName = os.path.normpath(os.path.realpath(os.path.join(WorkSpace, String)))
    if not (ValidFile(FullFileName)):
        Logger.Error("InfParser",
                     ToolError.FORMAT_INVALID,
                     ST.ERR_FILELIST_EXIST % (String),
                     File=GlobalData.gINF_MODULE_NAME,
                     Line=LineNo,
                     ExtraData=OriginalString)

    #
    # Validate file exist/format.
    #
    if IsValidPath(String, WorkSpace):
        IsValidFileFlag = True
    else:
        Logger.Error("InfParser",
                     ToolError.FORMAT_INVALID,
                     ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID % (String),
                     File=GlobalData.gINF_MODULE_NAME,
                     Line=LineNo,
                     ExtraData=OriginalString)
        return False
    if IsValidFileFlag:
        FileLinesList = []

        try:
            FInputfile = open(FullFileName, "r")
            try:
                FileLinesList = FInputfile.readlines()
            except BaseException:
                Logger.Error("InfParser",
                             ToolError.FILE_READ_FAILURE,
                             ST.ERR_FILE_OPEN_FAILURE,
                             File=FullFileName)
            finally:
                FInputfile.close()
        except BaseException:
            Logger.Error("InfParser",
                         ToolError.FILE_READ_FAILURE,
                         ST.ERR_FILE_OPEN_FAILURE,
                         File=FullFileName)

        ReFileGuidPattern = re.compile("^\s*FILE_GUID\s*=.*$")
        ReVerStringPattern = re.compile("^\s*VERSION_STRING\s*=.*$")

        FileLinesList = ProcessLineExtender(FileLinesList)

        for Line in FileLinesList:
            if ReFileGuidPattern.match(Line):
                FileGuidString = Line
            if ReVerStringPattern.match(Line):
                VerString = Line

        if FileGuidString:
            FileGuidString = GetSplitValueList(FileGuidString, '=', 1)[1]
        if VerString:
            VerString = GetSplitValueList(VerString, '=', 1)[1]

        return FileGuidString, VerString

## GetLocalValue
#
# Generate the local value for INF and DEC file. If Lang attribute not present, then use this value.
# If present, and there is no element without the Lang attribute, and one of the elements has the rfc1766 code is
# "en-x-tianocore", or "en-US" if "en-x-tianocore" was not found, or "en" if "en-US" was not found, or startswith 'en'
# if 'en' was not found, then use this value.
# If multiple entries of a tag exist which have the same language code, use the last entry.
#
# @param ValueList  A list need to be processed.
# @param UseFirstValue: True to use the first value, False to use the last value
#
# @return LocalValue
def GetLocalValue(ValueList, UseFirstValue=False):
    Value1 = ''
    Value2 = ''
    Value3 = ''
    Value4 = ''
    Value5 = ''
    for (Key, Value) in ValueList:
        if Key == TAB_LANGUAGE_EN_X:
            if UseFirstValue:
                if not Value1:
                    Value1 = Value
            else:
                Value1 = Value
        if Key == TAB_LANGUAGE_EN_US:
            if UseFirstValue:
                if not Value2:
                    Value2 = Value
            else:
                Value2 = Value
        if Key == TAB_LANGUAGE_EN:
            if UseFirstValue:
                if not Value3:
                    Value3 = Value
            else:
                Value3 = Value
        if Key.startswith(TAB_LANGUAGE_EN):
            if UseFirstValue:
                if not Value4:
                    Value4 = Value
            else:
                Value4 = Value
        if Key == '':
            if UseFirstValue:
                if not Value5:
                    Value5 = Value
            else:
                Value5 = Value

    if Value1:
        return Value1
    if Value2:
        return Value2
    if Value3:
        return Value3
    if Value4:
        return Value4
    if Value5:
        return Value5

    return ''


## GetCharIndexOutStr
#
# Get comment character index outside a string
#
# @param Line:              The string to be checked
# @param CommentCharacter:  Comment char, used to ignore comment content
#
# @retval Index
#
def GetCharIndexOutStr(CommentCharacter, Line):
    #
    # remove whitespace
    #
    Line = Line.strip()

    #
    # Check whether comment character is in a string
    #
    InString = False
    for Index in range(0, len(Line)):
        if Line[Index] == '"':
            InString = not InString
        elif Line[Index] == CommentCharacter and InString :
            pass
        elif Line[Index] == CommentCharacter and (Index +1) < len(Line) and Line[Index+1] == CommentCharacter \
            and not InString :
            return Index
    return -1

## ValidateUNIFilePath
#
# Check the UNI file path
#
# @param FilePath: The UNI file path
#
def ValidateUNIFilePath(Path):
    Suffix = Path[Path.rfind(TAB_SPLIT):]

    #
    # Check if the suffix is one of the '.uni', '.UNI', '.Uni'
    #
    if Suffix not in TAB_UNI_FILE_SUFFIXS:
        Logger.Error("Unicode File Parser",
                        ToolError.FORMAT_INVALID,
                        Message=ST.ERR_UNI_FILE_SUFFIX_WRONG,
                        ExtraData=Path)

    #
    # Check if '..' in the file name(without suffix)
    #
    if (TAB_SPLIT + TAB_SPLIT) in Path:
        Logger.Error("Unicode File Parser",
                        ToolError.FORMAT_INVALID,
                        Message=ST.ERR_UNI_FILE_NAME_INVALID,
                        ExtraData=Path)

    #
    # Check if the file name is valid according to the DEC and INF specification
    #
    Pattern = '[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*'
    FileName = Path.replace(Suffix, '')
    InvalidCh = re.sub(Pattern, '', FileName)
    if InvalidCh:
        Logger.Error("Unicode File Parser",
                        ToolError.FORMAT_INVALID,
                        Message=ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID,
                        ExtraData=Path)