From 6780eef1f9ed0af24795708c3be7adafd7113691 Mon Sep 17 00:00:00 2001 From: lgao4 Date: Mon, 15 Nov 2010 02:51:34 +0000 Subject: Sync EDKII BaseTools to BaseTools project r2093. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11057 6f19259b-4bc3-4df7-8a09-765794883524 --- BaseTools/Source/Python/AutoGen/AutoGen.py | 233 +++++++++++++++++++++++---- BaseTools/Source/Python/AutoGen/GenC.py | 11 +- BaseTools/Source/Python/AutoGen/StrGather.py | 93 ++++++++++- 3 files changed, 300 insertions(+), 37 deletions(-) (limited to 'BaseTools/Source/Python/AutoGen') diff --git a/BaseTools/Source/Python/AutoGen/AutoGen.py b/BaseTools/Source/Python/AutoGen/AutoGen.py index aaba768b4a..4ecf2eafe7 100644 --- a/BaseTools/Source/Python/AutoGen/AutoGen.py +++ b/BaseTools/Source/Python/AutoGen/AutoGen.py @@ -189,7 +189,12 @@ class WorkspaceAutoGen(AutoGen): # Pa.CollectPlatformDynamicPcds() self.AutoGenObjectList.append(Pa) - + + # + # Check PCDs token value conflict in each DEC file. + # + self._CheckAllPcdsTokenValueConflict() + self._BuildDir = None self._FvDir = None self._MakeFileDir = None @@ -248,6 +253,75 @@ class WorkspaceAutoGen(AutoGen): # BuildCommand should be all the same. So just get one from platform AutoGen self._BuildCommand = self.AutoGenObjectList[0].BuildCommand return self._BuildCommand + + ## Check the PCDs token value conflict in each DEC file. + # + # Will cause build break and raise error message while two PCDs conflict. + # + # @return None + # + def _CheckAllPcdsTokenValueConflict(self): + if len(self.BuildDatabase.WorkspaceDb.PackageList) >= 1: + for Package in self.BuildDatabase.WorkspaceDb.PackageList: + PcdList = Package.Pcds.values() + PcdList.sort(lambda x, y: cmp(x.TokenValue, y.TokenValue)) + Count = 0 + while (Count < len(PcdList) - 1) : + Item = PcdList[Count] + ItemNext = PcdList[Count + 1] + # + # Make sure in the same token space the TokenValue should be unique + # + if (Item.TokenValue == ItemNext.TokenValue): + SameTokenValuePcdList = [] + SameTokenValuePcdList.append(Item) + SameTokenValuePcdList.append(ItemNext) + RemainPcdListLength = len(PcdList) - Count - 2 + for ValueSameCount in range(RemainPcdListLength): + if PcdList[len(PcdList) - RemainPcdListLength + ValueSameCount].TokenValue == Item.TokenValue: + SameTokenValuePcdList.append(PcdList[len(PcdList) - RemainPcdListLength + ValueSameCount]) + else: + break; + # + # Sort same token value PCD list with TokenGuid and TokenCName + # + SameTokenValuePcdList.sort(lambda x, y: cmp("%s.%s"%(x.TokenSpaceGuidCName, x.TokenCName), "%s.%s"%(y.TokenSpaceGuidCName, y.TokenCName))) + SameTokenValuePcdListCount = 0 + while (SameTokenValuePcdListCount < len(SameTokenValuePcdList) - 1): + TemListItem = SameTokenValuePcdList[SameTokenValuePcdListCount] + TemListItemNext = SameTokenValuePcdList[SameTokenValuePcdListCount + 1] + + if (TemListItem.TokenSpaceGuidCName == TemListItemNext.TokenSpaceGuidCName) and (TemListItem.TokenCName != TemListItemNext.TokenCName): + EdkLogger.error( + 'build', + FORMAT_INVALID, + "The TokenValue [%s] of PCD [%s.%s] is conflict with: [%s.%s] in %s"\ + % (TemListItem.TokenValue, TemListItem.TokenSpaceGuidCName, TemListItem.TokenCName, TemListItemNext.TokenSpaceGuidCName, TemListItemNext.TokenCName, Package), + ExtraData=None + ) + SameTokenValuePcdListCount += 1 + Count += SameTokenValuePcdListCount + Count += 1 + + PcdList = Package.Pcds.values() + PcdList.sort(lambda x, y: cmp("%s.%s"%(x.TokenSpaceGuidCName, x.TokenCName), "%s.%s"%(y.TokenSpaceGuidCName, y.TokenCName))) + Count = 0 + while (Count < len(PcdList) - 1) : + Item = PcdList[Count] + ItemNext = PcdList[Count + 1] + # + # Check PCDs with same TokenSpaceGuidCName.TokenCName have same token value as well. + # + if (Item.TokenSpaceGuidCName == ItemNext.TokenSpaceGuidCName) and (Item.TokenCName == ItemNext.TokenCName) and (Item.TokenValue != ItemNext.TokenValue): + EdkLogger.error( + 'build', + FORMAT_INVALID, + "The TokenValue [%s] of PCD [%s.%s] in %s defined in two places should be same as well."\ + % (Item.TokenValue, Item.TokenSpaceGuidCName, Item.TokenCName, Package), + ExtraData=None + ) + Count += 1 + ## Create makefile for the platform and modules in it # @@ -306,7 +380,27 @@ class PlatformAutoGen(AutoGen): # _DynaPcdList_ = [] _NonDynaPcdList_ = [] - + + # + # The priority list while override build option + # + PrioList = {"0x11111" : 16, # TARGET_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE (Highest) + "0x01111" : 15, # ******_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE + "0x10111" : 14, # TARGET_*********_ARCH_COMMANDTYPE_ATTRIBUTE + "0x00111" : 13, # ******_*********_ARCH_COMMANDTYPE_ATTRIBUTE + "0x11011" : 12, # TARGET_TOOLCHAIN_****_COMMANDTYPE_ATTRIBUTE + "0x01011" : 11, # ******_TOOLCHAIN_****_COMMANDTYPE_ATTRIBUTE + "0x10011" : 10, # TARGET_*********_****_COMMANDTYPE_ATTRIBUTE + "0x00011" : 9, # ******_*********_****_COMMANDTYPE_ATTRIBUTE + "0x11101" : 8, # TARGET_TOOLCHAIN_ARCH_***********_ATTRIBUTE + "0x01101" : 7, # ******_TOOLCHAIN_ARCH_***********_ATTRIBUTE + "0x10101" : 6, # TARGET_*********_ARCH_***********_ATTRIBUTE + "0x00101" : 5, # ******_*********_ARCH_***********_ATTRIBUTE + "0x11001" : 4, # TARGET_TOOLCHAIN_****_***********_ATTRIBUTE + "0x01001" : 3, # ******_TOOLCHAIN_****_***********_ATTRIBUTE + "0x10001" : 2, # TARGET_*********_****_***********_ATTRIBUTE + "0x00001" : 1} # ******_*********_****_***********_ATTRIBUTE (Lowest) + ## The real constructor of PlatformAutoGen # # This method is not supposed to be called by users of PlatformAutoGen. It's @@ -481,12 +575,12 @@ class PlatformAutoGen(AutoGen): UnicodePcdArray = [] HiiPcdArray = [] OtherPcdArray = [] + VpdPcdDict = {} VpdFile = VpdInfoFile.VpdInfoFile() NeedProcessVpdMapFile = False if (self.Workspace.ArchList[-1] == self.Arch): for Pcd in self._DynamicPcdList: - # just pick the a value to determine whether is unicode string type Sku = Pcd.SkuInfoList[Pcd.SkuInfoList.keys()[0]] Sku.VpdOffset = Sku.VpdOffset.strip() @@ -500,32 +594,47 @@ class PlatformAutoGen(AutoGen): HiiPcdArray.append(Pcd) else: OtherPcdArray.append(Pcd) - if Pcd.Type in [TAB_PCDS_DYNAMIC_VPD, TAB_PCDS_DYNAMIC_EX_VPD]: - if not (self.Platform.VpdToolGuid == None or self.Platform.VpdToolGuid == ''): - # - # Fix the optional data of VPD PCD. - # - if (Pcd.DatumType.strip() != "VOID*"): - if Sku.DefaultValue == '': - Pcd.SkuInfoList[Pcd.SkuInfoList.keys()[0]].DefaultValue = Pcd.MaxDatumSize - Pcd.MaxDatumSize = None - else: - EdkLogger.error("build", AUTOGEN_ERROR, "PCD setting error", - File=self.MetaFile, - ExtraData="\n\tPCD: %s.%s format incorrect in DSC: %s\n\t\t\n" - % (Pcd.TokenSpaceGuidCName, Pcd.TokenCName, self.Platform.MetaFile.Path)) - - VpdFile.Add(Pcd, Sku.VpdOffset) - # if the offset of a VPD is *, then it need to be fixed up by third party tool. - if not NeedProcessVpdMapFile and Sku.VpdOffset == "*": - NeedProcessVpdMapFile = True + VpdPcdDict[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName)] = Pcd + + PlatformPcds = self.Platform.Pcds.keys() + PlatformPcds.sort() + # + # Add VPD type PCD into VpdFile and determine whether the VPD PCD need to be fixed up. + # + for PcdKey in PlatformPcds: + Pcd = self.Platform.Pcds[PcdKey] + if Pcd.Type in [TAB_PCDS_DYNAMIC_VPD, TAB_PCDS_DYNAMIC_EX_VPD]: + Pcd = VpdPcdDict[PcdKey] + Sku = Pcd.SkuInfoList[Pcd.SkuInfoList.keys()[0]] + Sku.VpdOffset = Sku.VpdOffset.strip() + # + # Fix the optional data of VPD PCD. + # + if (Pcd.DatumType.strip() != "VOID*"): + if Sku.DefaultValue == '': + Pcd.SkuInfoList[Pcd.SkuInfoList.keys()[0]].DefaultValue = Pcd.MaxDatumSize + Pcd.MaxDatumSize = None + else: + EdkLogger.error("build", AUTOGEN_ERROR, "PCD setting error", + File=self.MetaFile, + ExtraData="\n\tPCD: %s.%s format incorrect in DSC: %s\n\t\t\n" + % (Pcd.TokenSpaceGuidCName, Pcd.TokenCName, self.Platform.MetaFile.Path)) + + VpdFile.Add(Pcd, Sku.VpdOffset) + # if the offset of a VPD is *, then it need to be fixed up by third party tool. + if not NeedProcessVpdMapFile and Sku.VpdOffset == "*": + NeedProcessVpdMapFile = True + if self.Platform.VpdToolGuid == None or self.Platform.VpdToolGuid == '': + EdkLogger.error("Build", FILE_NOT_FOUND, \ + "Fail to find third-party BPDG tool to process VPD PCDs. BPDG Guid tool need to be defined in tools_def.txt and VPD_TOOL_GUID need to be provided in DSC file.") + # # Fix the PCDs define in VPD PCD section that never referenced by module. # An example is PCD for signature usage. - # - for DscPcd in self.Platform.Pcds: + # + for DscPcd in PlatformPcds: DscPcdEntry = self.Platform.Pcds[DscPcd] if DscPcdEntry.Type in [TAB_PCDS_DYNAMIC_VPD, TAB_PCDS_DYNAMIC_EX_VPD]: if not (self.Platform.VpdToolGuid == None or self.Platform.VpdToolGuid == ''): @@ -614,7 +723,7 @@ class PlatformAutoGen(AutoGen): # just pick the a value to determine whether is unicode string type Sku = Pcd.SkuInfoList[Pcd.SkuInfoList.keys()[0]] if Sku.VpdOffset == "*": - Sku.VpdOffset = VpdFile.GetOffset(Pcd)[0] + Sku.VpdOffset = VpdFile.GetOffset(Pcd)[0] else: EdkLogger.error("build", FILE_READ_FAILURE, "Can not find VPD map file %s to fix up VPD offset." % VpdMapFilePath) @@ -693,7 +802,7 @@ class PlatformAutoGen(AutoGen): if "FLAGS" in self.ToolDefinition["MAKE"]: NewOption = self.ToolDefinition["MAKE"]["FLAGS"].strip() if NewOption != '': - self._BuildCommand += SplitOption(NewOption) + self._BuildCommand += SplitOption(NewOption) return self._BuildCommand ## Get tool chain definition @@ -1220,16 +1329,86 @@ class PlatformAutoGen(AutoGen): EdkLogger.verbose("\t" + LibraryName + " : " + str(Library) + ' ' + str(type(Library))) return LibraryList + ## Calculate the priority value of the build option + # + # @param Key Build option definition contain: TARGET_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE + # + # @retval Value Priority value based on the priority list. + # + def CalculatePriorityValue(self, Key): + Target, ToolChain, Arch, CommandType, Attr = Key.split('_') + PriorityValue = 0x11111 + if Target == "*": + PriorityValue &= 0x01111 + if ToolChain == "*": + PriorityValue &= 0x10111 + if Arch == "*": + PriorityValue &= 0x11011 + if CommandType == "*": + PriorityValue &= 0x11101 + if Attr == "*": + PriorityValue &= 0x11110 + + return self.PrioList["0x%0.5x"%PriorityValue] + + ## Expand * in build option key # # @param Options Options to be expanded # # @retval options Options expanded - # + # def _ExpandBuildOption(self, Options, ModuleStyle=None): BuildOptions = {} FamilyMatch = False FamilyIsNull = True + + OverrideList = {} + # + # Construct a list contain the build options which need override. + # + for Key in Options: + # + # Key[0] -- tool family + # Key[1] -- TARGET_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE + # + if Key[0] == self.BuildRuleFamily : + Target, ToolChain, Arch, CommandType, Attr = Key[1].split('_') + if Target == self.BuildTarget or Target == "*": + if ToolChain == self.ToolChain or ToolChain == "*": + if Arch == self.Arch or Arch == "*": + if Options[Key].startswith("="): + if OverrideList.get(Key[1]) != None: + OverrideList.pop(Key[1]) + OverrideList[Key[1]] = Options[Key] + + # + # Use the highest priority value. + # + if (len(OverrideList) >= 2): + KeyList = OverrideList.keys() + for Index in range(len(KeyList)): + NowKey = KeyList[Index] + Target1, ToolChain1, Arch1, CommandType1, Attr1 = NowKey.split("_") + for Index1 in range(len(KeyList) - Index - 1): + NextKey = KeyList[Index1 + Index + 1] + # + # Compare two Key, if one is included by another, choose the higher priority one + # + Target2, ToolChain2, Arch2, CommandType2, Attr2 = NextKey.split("_") + if Target1 == Target2 or Target1 == "*" or Target2 == "*": + if ToolChain1 == ToolChain2 or ToolChain1 == "*" or ToolChain2 == "*": + if Arch1 == Arch2 or Arch1 == "*" or Arch2 == "*": + if CommandType1 == CommandType2 or CommandType1 == "*" or CommandType2 == "*": + if Attr1 == Attr2 or Attr1 == "*" or Attr2 == "*": + if self.CalculatePriorityValue(NowKey) > self.CalculatePriorityValue(NextKey): + if Options.get((self.BuildRuleFamily, NextKey)) != None: + Options.pop((self.BuildRuleFamily, NextKey)) + else: + if Options.get((self.BuildRuleFamily, NowKey)) != None: + Options.pop((self.BuildRuleFamily, NowKey)) + + for Key in Options: if ModuleStyle != None and len (Key) > 2: # Check Module style is EDK or EDKII. diff --git a/BaseTools/Source/Python/AutoGen/GenC.py b/BaseTools/Source/Python/AutoGen/GenC.py index 2a133d3812..3c256c8b74 100644 --- a/BaseTools/Source/Python/AutoGen/GenC.py +++ b/BaseTools/Source/Python/AutoGen/GenC.py @@ -1860,8 +1860,10 @@ def CreateUnicodeStringCode(Info, AutoGenC, AutoGenH, UniGenCFlag, UniGenBinBuff IncList = [Info.MetaFile.Dir] # Get all files under [Sources] section in inf file for EDK-II module + EDK2Module = True SrcList = [F for F in Info.SourceFileList] if Info.AutoGenVersion < 0x00010005: + EDK2Module = False # Get all files under the module directory for EDK-I module Cwd = os.getcwd() os.chdir(Info.MetaFile.Dir) @@ -1883,7 +1885,7 @@ def CreateUnicodeStringCode(Info, AutoGenC, AutoGenH, UniGenCFlag, UniGenBinBuff CompatibleMode = False # - # -s is a temporary option dedicated for building .UNI files with ISO 639-2 lanauge codes of EDK Shell in EDK2 + # -s is a temporary option dedicated for building .UNI files with ISO 639-2 language codes of EDK Shell in EDK2 # if 'BUILD' in Info.BuildOption and Info.BuildOption['BUILD']['FLAGS'].find('-s') > -1: if CompatibleMode: @@ -1894,7 +1896,12 @@ def CreateUnicodeStringCode(Info, AutoGenC, AutoGenH, UniGenCFlag, UniGenBinBuff else: ShellMode = False - Header, Code = GetStringFiles(Info.UnicodeFileList, SrcList, IncList, Info.IncludePathList, ['.uni', '.inf'], Info.Name, CompatibleMode, ShellMode, UniGenCFlag, UniGenBinBuffer) + #RFC4646 is only for EDKII modules and ISO639-2 for EDK modules + if EDK2Module: + FilterInfo = [EDK2Module] + [Info.PlatformInfo.Platform.RFCLanguages] + else: + FilterInfo = [EDK2Module] + [Info.PlatformInfo.Platform.ISOLanguages] + Header, Code = GetStringFiles(Info.UnicodeFileList, SrcList, IncList, Info.IncludePathList, ['.uni', '.inf'], Info.Name, CompatibleMode, ShellMode, UniGenCFlag, UniGenBinBuffer, FilterInfo) if CompatibleMode or UniGenCFlag: AutoGenC.Append("\n//\n//Unicode String Pack Definition\n//\n") AutoGenC.Append(Code) diff --git a/BaseTools/Source/Python/AutoGen/StrGather.py b/BaseTools/Source/Python/AutoGen/StrGather.py index 52ab439e9b..3df493834c 100644 --- a/BaseTools/Source/Python/AutoGen/StrGather.py +++ b/BaseTools/Source/Python/AutoGen/StrGather.py @@ -284,6 +284,65 @@ def CreateCFileStringValue(Value): return Str +## GetFilteredLanguage +# +# apply get best language rules to the UNI language code list +# +# @param UniLanguageList: language code definition list in *.UNI file +# @param LanguageFilterList: language code filter list of RFC4646 format in DSC file +# +# @retval UniLanguageListFiltered: the filtered language code +# +def GetFilteredLanguage(UniLanguageList, LanguageFilterList): + UniLanguageListFiltered = [] + # if filter list is empty, then consider there is no filter + if LanguageFilterList == []: + UniLanguageListFiltered = UniLanguageList + return UniLanguageListFiltered + for Language in LanguageFilterList: + # first check for exact match + if Language in UniLanguageList: + if Language not in UniLanguageListFiltered: + UniLanguageListFiltered += [Language] + # find the first one with the same/equivalent primary tag + else: + if Language.find('-') != -1: + PrimaryTag = Language[0:Language.find('-')].lower() + else: + PrimaryTag = Language + + if len(PrimaryTag) == 3: + PrimaryTag = LangConvTable.get(PrimaryTag) + + for UniLanguage in UniLanguageList: + if UniLanguage.find('-') != -1: + UniLanguagePrimaryTag = UniLanguage[0:UniLanguage.find('-')].lower() + else: + UniLanguagePrimaryTag = UniLanguage + + if len(UniLanguagePrimaryTag) == 3: + UniLanguagePrimaryTag = LangConvTable.get(UniLanguagePrimaryTag) + + if PrimaryTag == UniLanguagePrimaryTag: + if UniLanguage not in UniLanguageListFiltered: + UniLanguageListFiltered += [UniLanguage] + break + else: + # Here is rule 3 for "get best language" + # If tag is not listed in the Unicode file, the default ("en") tag should be used for that language + # for better processing, find the one that best suit for it. + DefaultTag = 'en' + if DefaultTag not in UniLanguageListFiltered: + # check whether language code with primary code equivalent with DefaultTag already in the list, if so, use that + for UniLanguage in UniLanguageList: + if UniLanguage.startswith('en-') or UniLanguage.startswith('eng-'): + if UniLanguage not in UniLanguageListFiltered: + UniLanguageListFiltered += [UniLanguage] + break + else: + UniLanguageListFiltered += [DefaultTag] + return UniLanguageListFiltered + ## Create content of .c file # @@ -293,10 +352,11 @@ def CreateCFileStringValue(Value): # @param UniObjectClass A UniObjectClass instance # @param IsCompatibleMode Compatible mode # @param UniBinBuffer UniBinBuffer to contain UniBinary data. +# @param FilterInfo Platform language filter information # # @retval Str: A string of .c file content # -def CreateCFileContent(BaseName, UniObjectClass, IsCompatibleMode, UniBinBuffer=None): +def CreateCFileContent(BaseName, UniObjectClass, IsCompatibleMode, UniBinBuffer, FilterInfo): # # Init array length # @@ -304,13 +364,29 @@ def CreateCFileContent(BaseName, UniObjectClass, IsCompatibleMode, UniBinBuffer= Str = '' Offset = 0 + EDK2Module = FilterInfo[0] + if EDK2Module: + LanguageFilterList = FilterInfo[1] + else: + # EDK module is using ISO639-2 format filter, convert to the RFC4646 format + LanguageFilterList = [LangConvTable.get(F.lower()) for F in FilterInfo[1]] + + UniLanguageList = [] + for IndexI in range(len(UniObjectClass.LanguageDef)): + UniLanguageList += [UniObjectClass.LanguageDef[IndexI][0]] + + UniLanguageListFiltered = GetFilteredLanguage(UniLanguageList, LanguageFilterList) + + # # Create lines for each language's strings # for IndexI in range(len(UniObjectClass.LanguageDef)): Language = UniObjectClass.LanguageDef[IndexI][0] LangPrintName = UniObjectClass.LanguageDef[IndexI][1] - + if Language not in UniLanguageListFiltered: + continue + StringBuffer = StringIO() StrStringValue = '' ArrayLength = 0 @@ -428,13 +504,14 @@ def CreateCFileEnd(): # @param BaseName: The basename of strings # @param UniObjectClass A UniObjectClass instance # @param IsCompatibleMode Compatible Mode +# @param FilterInfo Platform language filter information # -# @retval CFile: A string of complete .c file +# @retval CFile: A string of complete .c file # -def CreateCFile(BaseName, UniObjectClass, IsCompatibleMode): +def CreateCFile(BaseName, UniObjectClass, IsCompatibleMode, FilterInfo): CFile = '' #CFile = WriteLine(CFile, CreateCFileHeader()) - CFile = WriteLine(CFile, CreateCFileContent(BaseName, UniObjectClass, IsCompatibleMode)) + CFile = WriteLine(CFile, CreateCFileContent(BaseName, UniObjectClass, IsCompatibleMode, None, FilterInfo)) CFile = WriteLine(CFile, CreateCFileEnd()) return CFile @@ -518,7 +595,7 @@ def SearchString(UniObjectClass, FileList, IsCompatibleMode): # This function is used for UEFI2.1 spec # # -def GetStringFiles(UniFilList, SourceFileList, IncludeList, IncludePathList, SkipList, BaseName, IsCompatibleMode = False, ShellMode = False, UniGenCFlag = True, UniGenBinBuffer = None): +def GetStringFiles(UniFilList, SourceFileList, IncludeList, IncludePathList, SkipList, BaseName, IsCompatibleMode = False, ShellMode = False, UniGenCFlag = True, UniGenBinBuffer = None, FilterInfo = [True, []]): Status = True ErrorMessage = '' @@ -540,9 +617,9 @@ def GetStringFiles(UniFilList, SourceFileList, IncludeList, IncludePathList, Ski HFile = CreateHFile(BaseName, Uni, IsCompatibleMode, UniGenCFlag) CFile = None if IsCompatibleMode or UniGenCFlag: - CFile = CreateCFile(BaseName, Uni, IsCompatibleMode) + CFile = CreateCFile(BaseName, Uni, IsCompatibleMode, FilterInfo) if UniGenBinBuffer: - CreateCFileContent(BaseName, Uni, IsCompatibleMode, UniGenBinBuffer) + CreateCFileContent(BaseName, Uni, IsCompatibleMode, UniGenBinBuffer, FilterInfo) return HFile, CFile -- cgit v1.2.3