From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from rn-mailsvcp-ppex-lapp35.apple.com (rn-mailsvcp-ppex-lapp35.apple.com [17.179.253.44]) by mx.groups.io with SMTP id smtpd.web12.38472.1628870147134068379 for ; Fri, 13 Aug 2021 08:55:47 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@apple.com header.s=20180706 header.b=uxOYuxL9; spf=pass (domain: apple.com, ip: 17.179.253.44, mailfrom: afish@apple.com) Received: from pps.filterd (rn-mailsvcp-ppex-lapp35.rno.apple.com [127.0.0.1]) by rn-mailsvcp-ppex-lapp35.rno.apple.com (8.16.1.2/8.16.1.2) with SMTP id 17DFbhC8001362; Fri, 13 Aug 2021 08:45:18 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=apple.com; h=from : message-id : content-type : mime-version : subject : date : in-reply-to : cc : to : references; s=20180706; bh=svxv3qfkW82oiGLR5OgOFwgMnxbU9b7is3qDG//utKY=; b=uxOYuxL9CbAxiUoZ97yVqD7Aiya80NL5lrDiNzhByjA+T96jJFvRQ+W2necgK1Oc+anC 8ghVPv70xB2y8GuBMrjhfFFiBhNX/ZeAiwklW8Jeoy/2tNqSNPW8DR65SmAKL+wYbjA0 lVtqqUNzoqewKgmkP13tL2ia9vKItsqm5/BC/5Tmvaeic4vDwtJA3+QmCafjOaiwW8TU f/ccxbUzi8+148xDsAiy90XBIHrc8JMcqMu9Vhvbh0rPESr9tiP37eK4RKcdUIjb79Eq wqz3DadldfuP2E7NzMzwo8epzXhdF+/KYIs5vRl/LxjmzzrOodpAuslzIjM4qrQewr9a Mg== Received: from rn-mailsvcp-mta-lapp02.rno.apple.com (rn-mailsvcp-mta-lapp02.rno.apple.com [10.225.203.150]) by rn-mailsvcp-ppex-lapp35.rno.apple.com with ESMTP id 3ader1g85r-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NO); Fri, 13 Aug 2021 08:45:18 -0700 Received: from rn-mailsvcp-mmp-lapp01.rno.apple.com (rn-mailsvcp-mmp-lapp01.rno.apple.com [17.179.253.14]) by rn-mailsvcp-mta-lapp02.rno.apple.com (Oracle Communications Messaging Server 8.1.0.9.20210415 64bit (built Apr 15 2021)) with ESMTPS id <0QXS0052KBRILB50@rn-mailsvcp-mta-lapp02.rno.apple.com>; Fri, 13 Aug 2021 08:45:18 -0700 (PDT) Received: from process_milters-daemon.rn-mailsvcp-mmp-lapp01.rno.apple.com by rn-mailsvcp-mmp-lapp01.rno.apple.com (Oracle Communications Messaging Server 8.1.0.9.20210415 64bit (built Apr 15 2021)) id <0QXS00X00BRDBZ00@rn-mailsvcp-mmp-lapp01.rno.apple.com>; Fri, 13 Aug 2021 08:45:18 -0700 (PDT) X-Va-A: X-Va-T-CD: 393dcb553abf60c683832e42ee486742 X-Va-E-CD: 347c455bb597b22fc6077d60b9ba1be1 X-Va-R-CD: d0174db34bac38def2d8b6045b927355 X-Va-CD: 0 X-Va-ID: 6f5654e8-16b4-42e5-bec2-5238b413a9e5 X-V-A: X-V-T-CD: 393dcb553abf60c683832e42ee486742 X-V-E-CD: 347c455bb597b22fc6077d60b9ba1be1 X-V-R-CD: d0174db34bac38def2d8b6045b927355 X-V-CD: 0 X-V-ID: 66939979-1e5c-4b64-a666-6ceb4a920e7f X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.391,18.0.790 definitions=2021-08-13_05:2021-08-13,2021-08-13 signatures=0 Received: from [17.235.43.20] (unknown [17.235.43.20]) by rn-mailsvcp-mmp-lapp01.rno.apple.com (Oracle Communications Messaging Server 8.1.0.9.20210415 64bit (built Apr 15 2021)) with ESMTPSA id <0QXS010JUBRH4Z00@rn-mailsvcp-mmp-lapp01.rno.apple.com>; Fri, 13 Aug 2021 08:45:18 -0700 (PDT) From: "Andrew Fish" Message-id: <384B069D-236A-4CE9-8E2A-4123A8818554@apple.com> MIME-version: 1.0 (Mac OS X Mail 14.0 \(3654.20.0.2.1\)) Subject: Re: [edk2-devel] [Patch 2/2] BaseTools: a new build option for variable default value generation Date: Fri, 13 Aug 2021 08:45:16 -0700 In-reply-to: <20210813114520.2057-1-bob.c.feng@intel.com> Cc: Liming Gao , Yuwei Chen To: edk2-devel-groups-io , Bob Feng References: <20210813114520.2057-1-bob.c.feng@intel.com> X-Mailer: Apple Mail (2.3654.20.0.2.1) X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.391,18.0.790 definitions=2021-08-13_05:2021-08-13,2021-08-13 signatures=0 Content-type: multipart/alternative; boundary="Apple-Mail=_35D03FF2-A3E3-46ED-9EAC-AE4A45DCFD22" --Apple-Mail=_35D03FF2-A3E3-46ED-9EAC-AE4A45DCFD22 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On Aug 13, 2021, at 4:45 AM, Bob Feng wrote: >=20 > REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D3562 >=20 > Create a new build option to enable vfrcompile to generate Json > format EFI variable information file and read it to generate=20 > the EFI variable default value binary file. >=20 > Signed-off-by: Bob Feng > Cc: Liming Gao > Cc: Yuwei Chen > --- > BaseTools/Source/Python/AutoGen/DataPipe.py | 2 + > .../Source/Python/AutoGen/GenDefaultVar.py | 498 ++++++++++++++++++ > .../Source/Python/AutoGen/ModuleAutoGen.py | 9 + > .../Python/AutoGen/ModuleAutoGenHelper.py | 4 + > BaseTools/Source/Python/Common/GlobalData.py | 5 + > BaseTools/Source/Python/build/build.py | 18 + > BaseTools/Source/Python/build/buildoptions.py | 1 + > 7 files changed, 537 insertions(+) > create mode 100644 BaseTools/Source/Python/AutoGen/GenDefaultVar.py >=20 > diff --git a/BaseTools/Source/Python/AutoGen/DataPipe.py b/BaseTools/Sour= ce/Python/AutoGen/DataPipe.py > index 86ac2b928d9c..fa0c36b98f21 100755 > --- a/BaseTools/Source/Python/AutoGen/DataPipe.py > +++ b/BaseTools/Source/Python/AutoGen/DataPipe.py > @@ -165,5 +165,7 @@ class MemoryDataPipe(DataPipe): > self.DataContainer =3D {"BinCacheSource":GlobalData.gBinCacheSour= ce} >=20 > self.DataContainer =3D {"BinCacheDest":GlobalData.gBinCacheDest} >=20 > self.DataContainer =3D {"EnableGenfdsMultiThread":GlobalData.gEna= bleGenfdsMultiThread} > + > + self.DataContainer =3D {"GenDefaultVarBin": GlobalData.gGenDefau= ltVarBin} > diff --git a/BaseTools/Source/Python/AutoGen/GenDefaultVar.py b/BaseTools= /Source/Python/AutoGen/GenDefaultVar.py > new file mode 100644 > index 000000000000..b82cce18ed26 > --- /dev/null > +++ b/BaseTools/Source/Python/AutoGen/GenDefaultVar.py > @@ -0,0 +1,498 @@ > +import json > +from ctypes import * > +import re > +import copy > +from struct import unpack > +import os > + > +class GUID(Structure): We should use (LittleEndianStructure) vs. (Structure). Structure is the end= ian of the Host, and EFI is little endian. Same comment for all the ctype s= tructs. LittleEndianStructure is like using < with struct.pack()/struct.unp= ack().=20 > + _fields_ =3D [ > + ('Guid1', c_uint32), > + ('Guid2', c_uint16), > + ('Guid3', c_uint16), > + ('Guid4', ARRAY(c_uint8, 8)), > + ] > + I think it would be better to use the same field names as the EFI C structu= res.=20 typedef struct { UINT32 Data1; UINT16 Data2; UINT16 Data3; UINT8 Data4[8]; } GUID Longer term I think the BaseBools should have a GUID class that can be shar= ed that abstracts things GUIDs. It should store GUIDs as Python uuid.UUID()= or at least in the common string form, and have useful operations.=20 I=E2=80=99ve got an example GUID class in my gdb patch set, but it also loa= ds the GUID database and deals with C name, C GUID structure init form, and= strings. But it feels like we could pull out a common lower level GUID cla= ss out of that.=20 > + def from_list(self, listformat): > + self.Guid1 =3D listformat[0] > + self.Guid2 =3D listformat[1] > + self.Guid3 =3D listformat[2] > + for i in range(8): > + self.Guid4[i] =3D listformat[i+3] > + > + def __cmp__(self, otherguid): > + if isinstance(otherguid, GUID): > + return 1 > + rt =3D False > + if self.Guid1 =3D=3D otherguid.Guid1 and self.Guid2 =3D=3D other= guid.Guid2 and self.Guid3 =3D=3D otherguid.Guid3: > + rt =3D True > + for i in range(8): > + rt =3D rt & (self.Guid4[i] =3D=3D otherguid.Guid4[i]) > + return rt > + > + > +class TIME(Structure): > + _fields_ =3D [ > + ('Year', c_uint16), > + ('Month', c_uint8), > + ('Day', c_uint8), > + ('Hour', c_uint8), > + ('Minute', c_uint8), > + ('Second', c_uint8), > + ('Pad1', c_uint8), > + ('Nanosecond', c_uint32), > + ('TimeZone', c_uint16), > + ('Daylight', c_uint8), > + ('Pad2', c_uint8), > + ] > + def __init__(self): > + self.Year =3D 0x0 > + self.Month =3D 0x0 > + self.Day =3D 0x0 > + self.Hour =3D 0x0 > + self.Minute =3D 0x0 > + self.Second =3D 0x0 > + self.Pad1 =3D 0x0 > + self.Nanosecond =3D 0x0 > + self.TimeZone =3D 0x0 > + self.Daylight =3D 0x0 > + self.Pad2 =3D 0x0 > + > + > +EFI_VARIABLE_GUID =3D [0xddcf3616, 0x3275, 0x4164, > + 0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d] > +EFI_AUTHENTICATED_VARIABLE_GUID =3D [ > + 0xaaf32c78, 0x947b, 0x439a, 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77= , 0x92] > + > +AuthVarGuid =3D GUID() > +AuthVarGuid.from_list(EFI_AUTHENTICATED_VARIABLE_GUID) > +VarGuid =3D GUID() > +VarGuid.from_list(EFI_VARIABLE_GUID) > + > +# Variable Store Header Format. > +VARIABLE_STORE_FORMATTED =3D 0x5a > +# Variable Store Header State. > +VARIABLE_STORE_HEALTHY =3D 0xfe > + > + > +class VARIABLE_STORE_HEADER(Structure): > + _fields_ =3D [ > + ('Signature', GUID), > + ('Size', c_uint32), > + ('Format', c_uint8), > + ('State', c_uint8), > + ('Reserved', c_uint16), > + ('Reserved1', c_uint32), > + ] > + > + > +# Variable data start flag. > +VARIABLE_DATA =3D 0x55AA > + > +# Variable State flags. > +VAR_IN_DELETED_TRANSITION =3D 0xfe > +VAR_DELETED =3D 0xfd > +VAR_HEADER_VALID_ONLY =3D 0x7f > +VAR_ADDED =3D 0x3f > + > + > +class VARIABLE_HEADER(Structure): > + _fields_ =3D [ > + ('StartId', c_uint16), > + ('State', c_uint8), > + ('Reserved', c_uint8), > + ('Attributes', c_uint32), > + ('NameSize', c_uint32), > + ('DataSize', c_uint32), > + ('VendorGuid', GUID), > + ] > + > + > +class AUTHENTICATED_VARIABLE_HEADER(Structure): > + _fields_ =3D [ > + ('StartId', c_uint16), > + ('State', c_uint8), > + ('Reserved', c_uint8), > + ('Attributes', c_uint32), > + ('MonotonicCount', c_uint64), > + ('TimeStamp', TIME), > + ('PubKeyIndex', c_uint32), > + ('NameSize', c_uint32), > + ('DataSize', c_uint32), > + ('VendorGuid', GUID), > + ] > + _pack_ =3D 1 > + > + > +# Alignment of Variable Data Header in Variable Store region. > +HEADER_ALIGNMENT =3D 4 > + > + > +class DEFAULT_INFO(Structure): > + _fields_ =3D [ > + ('DefaultId', c_uint16), > + ('BoardId', c_uint16), > + ] > + > + > +class DEFAULT_DATA(Structure): > + _fields_ =3D [ > + ('HeaderSize', c_uint16), > + ('DefaultInfo', DEFAULT_INFO), > + ] > + > +class DELTA_DATA(Structure): > + _fields_ =3D [ > + ('Offset', c_uint16), > + ('Value', c_uint8), > + ] > + _pack_ =3D 1 > + > +array_re =3D re.compile( > + "(?P[a-z_A-Z][a-z_A-Z0-9]*)\[(?P[1-9][0-9]*)\]") > + > + > +class VarField(): > + def __init__(self): > + self.Offset =3D 0 > + self.Value =3D 0 > + self.Size =3D 0 > + > + @property > + def Type(self): > + if self.Size =3D=3D 1: > + return "UINT8" > + if self.Size =3D=3D 2: > + return "UINT16" > + if self.Size =3D=3D 4: > + return "UINT32" > + if self.Size =3D=3D 8: > + return "UINT64" > + > + return "UINT8" > + > + > +BASIC_TYPE =3D { > + "BOOLEAN": 1, > + "UINT8": 1, > + "UINT16": 2, > + "UINT32": 4, > + "UINT64": 8 > +} This might be more of a phase 2=E2=80=A6. I think you made a lot of extra work by inventing your own type system. You= should just convert to ctype as fast as you can.=20 BASIC_TYPE =3D { "BOOLEAN": c_uint8, "UINT8": c_uint8, "UINT16": c_uint16, "UINT32": c_uint32, "UINT64": c_uint64 } Then you can just sizeof() to get the size FYI I wrote this ctype dumper in case it is useful debugging this code. It= picks off EFI_GUID so it can print it as a proper UUID using the GUID clas= s I mentioned above, so basically a nicer print option.=20 def ctype_to_str(ctype, indent=3D'', hide_list=3D[]): ''' Given a ctype object print out as a string by walking the _fields_ in the cstring Class ''' result =3D '' for field in ctype._fields_: attr =3D getattr(ctype, field[0]) tname =3D type(attr).__name__ if field[0] in hide_list: continue result +=3D indent + f'{field[0]} =3D ' if tname =3D=3D 'EFI_GUID': result +=3D GuidNames.to_name(GuidNames.to_uuid(attr)) + '\n' elif issubclass(type(attr), Structure): result +=3D f'{tname}\n' + \ ctype_to_str(attr, indent + ' ', hide_list) elif isinstance(attr, int): result +=3D f'0x{attr:x}\n' else: result +=3D f'{attr}\n' return result Thanks, Andrew Fish > +class CStruct(): > + > + > + def __init__(self, typedefs): > + self.TypeDefs =3D typedefs > + self.TypeStack =3D copy.deepcopy(typedefs) > + self.finalDefs =3D {} > + > + def CalStuctSize(self, sType): > + rt =3D 0 > + if sType in BASIC_TYPE: > + return BASIC_TYPE[sType] > + > + ma =3D array_re.match(sType) > + if ma: > + mType =3D ma.group('mType') > + mSize =3D ma.group('mSize') > + rt +=3D int(mSize) * self.CalStuctSize(mType) > + else: > + for subType in self.TypeDefs[sType]: > + rt +=3D self.CalStuctSize(subType['Type']) > + > + return rt > + > + def expend(self, fielditem): > + fieldname =3D fielditem['Name'] > + fieldType =3D fielditem['Type'] > + fieldOffset =3D fielditem['Offset'] > + > + ma =3D array_re.match(fieldType) > + if ma: > + mType =3D ma.group('mType') > + mSize =3D ma.group('mSize') > + return [{"Name": "%s[%d]" % (fieldname, i), "Type": mType, "= Offset": (fieldOffset + i*self.CalStuctSize(mType))} for i in range(int(mSi= ze))] > + else: > + return [{"Name": "%s.%s" % (fieldname, item['Name']), "Type"= :item['Type'], "Offset": (fieldOffset + item['Offset'])} for item in self.T= ypeDefs[fielditem['Type']]] > + > + def ExpandTypes(self): > + if not self.finalDefs: > + for datatype in self.TypeStack: > + result =3D [] > + mTypeStack =3D self.TypeStack[datatype] > + while len(mTypeStack) > 0: > + item =3D mTypeStack.pop() > + if item['Type'] in self.BASIC_TYPE: > + result.append(item) > + elif item['Type'] =3D=3D '(null)': > + continue > + else: > + for expand_item in self.expend(item): > + mTypeStack.append(expand_item) > + self.finalDefs[datatype] =3D result > + self.finalDefs > + return self.finalDefs > + > +def Get_Occupied_Size(FileLength, alignment): > + if FileLength % alignment =3D=3D 0: > + return FileLength > + return FileLength + (alignment-(FileLength % alignment)) > + > +def Occupied_Size(buffer, alignment): > + FileLength =3D len(buffer) > + if FileLength % alignment !=3D 0: > + buffer +=3D b'\0' * (alignment-(FileLength % alignment)) > + return buffer > + > +def PackStruct(cStruct): > + length =3D sizeof(cStruct) > + p =3D cast(pointer(cStruct), POINTER(c_char * length)) > + return p.contents.raw > + > +def calculate_delta(default, theother): > + > + if len(default) - len(theother) !=3D 0: > + return [] > + > + data_delta =3D [] > + for i in range(len(default)): > + if default[i] !=3D theother[i]: > + data_delta.append([i, theother[i]]) > + return data_delta > + > +class Variable(): > + def __init__(self): > + self.mAlign =3D 1 > + self.mTotalSize =3D 1 > + self.mValue =3D {} # {defaultstore: value} > + self.mBin =3D {} > + self.fields =3D {} # {defaultstore: fileds} > + self.delta =3D {} > + self.attributes =3D 0 > + self.mType =3D '' > + self.guid =3D '' > + self.mName =3D '' > + self.cDefs =3D None > + > + @property > + def GuidArray(self): > + > + guid_array =3D [] > + guid =3D self.guid.strip().strip("{").strip("}") > + for item in guid.split(","): > + field =3D item.strip().strip("{").strip("}") > + guid_array.append(int(field,16)) > + return guid_array > + > + def update_delta_offset(self,base): > + for default_id in self.delta: > + for delta_list in self.delta[default_id]: > + delta_list[0] +=3D base > + > + def pack(self): > + > + for defaultid in self.mValue: > + var_value =3D self.mValue[defaultid] > + auth_var =3D AUTHENTICATED_VARIABLE_HEADER() > + auth_var.StartId =3D VARIABLE_DATA > + auth_var.State =3D VAR_ADDED > + auth_var.Reserved =3D 0x00 > + auth_var.Attributes =3D 0x00000007 > + auth_var.MonotonicCount =3D 0x0 > + auth_var.TimeStamp =3D TIME() > + auth_var.PubKeyIndex =3D 0x0 > + var_name_buffer =3D self.mName.encode('utf-16le') + b'\0\0' > + auth_var.NameSize =3D len(var_name_buffer) > + auth_var.DataSize =3D len(var_value) > + vendor_guid =3D GUID() > + vendor_guid.from_list(self.GuidArray) > + auth_var.VendorGuid =3D vendor_guid > + > + self.mBin[defaultid] =3D PackStruct(auth_var) + Occupied_Siz= e(var_name_buffer + var_value, 4) > + > + def TypeCheck(self,data_type, data_size): > + if BASIC_TYPE[data_type] =3D=3D data_size: > + return True > + return False > + > + def ValueToBytes(self,data_type,data_value,data_size): > + > + rt =3D b'' > + if not self.TypeCheck(data_type, data_size): > + print(data_type,data_value,data_size) > + > + if data_type =3D=3D "BOOLEAN" or data_type =3D=3D 'UINT8': > + p =3D cast(pointer(c_uint8(int(data_value,16))), POINTER(c_c= har * 1)) > + rt =3D p.contents.raw > + elif data_type =3D=3D 'UINT16': > + p =3D cast(pointer(c_uint16(int(data_value,16))), POINTER(c_= char * 2)) > + rt =3D p.contents.raw > + elif data_type =3D=3D 'UINT32': > + p =3D cast(pointer(c_uint32(int(data_value,16))), POINTER(c_= char * 4)) > + rt =3D p.contents.raw > + elif data_type =3D=3D 'UINT64': > + p =3D cast(pointer(c_uint64(int(data_value,16))), POINTER(c_= char * 8)) > + rt =3D p.contents.raw > + > + return rt > + > + def serial(self): > + for defaultstore in self.fields: > + vValue =3D b'' > + vfields =3D {vf.Offset: vf for vf in self.fields[defaultstor= e]} > + i =3D 0 > + while i < self.mTotalSize: > + if i in vfields: > + vfield =3D vfields[i] > + vValue +=3D self.ValueToBytes(vfield.Type, vfield.Va= lue,vfield.Size) > + i +=3D vfield.Size > + else: > + vValue +=3D self.ValueToBytes('UINT8','0x00',1) > + i +=3D 1 > + > + self.mValue[defaultstore] =3D vValue > + standard_default =3D self.mValue[0] > + > + for defaultid in self.mValue: > + if defaultid =3D=3D 0: > + continue > + others_default =3D self.mValue[defaultid] > + > + self.delta.setdefault(defaultid, []).extend(calculate_delta( > + standard_default, others_default)) > + > +class DefaultVariableGenerator(): > + def __init__(self): > + self.NvVarInfo =3D [] > + > + def LoadNvVariableInfo(self, VarInfoFilelist): > + > + VarDataDict =3D {} > + DataStruct =3D {} > + VarDefine =3D {} > + VarAttributes =3D {} > + for VarInfoFile in VarInfoFilelist: > + with open(VarInfoFile.strip(), "r") as fd: > + data =3D json.load(fd) > + > + DataStruct.update(data.get("DataStruct", {})) > + Data =3D data.get("Data") > + VarDefine.update(data.get("VarDefine")) > + VarAttributes.update(data.get("DataStructAttribute")) > + > + for vardata in Data: > + if vardata['VendorGuid'] =3D=3D 'NA': > + continue > + VarDataDict.setdefault( > + (vardata['VendorGuid'], vardata["VarName"]), []).app= end(vardata) > + > + cStructDefs =3D CStruct(DataStruct) > + for guid, varname in VarDataDict: > + v =3D Variable() > + v.guid =3D guid > + vardef =3D VarDefine.get(varname) > + if vardef is None: > + for var in VarDefine: > + if VarDefine[var]['Type'] =3D=3D varname: > + vardef =3D VarDefine[var] > + break > + else: > + continue > + v.attributes =3D vardef['Attributes'] > + v.mType =3D vardef['Type'] > + v.mAlign =3D VarAttributes[v.mType]['Alignment'] > + v.mTotalSize =3D VarAttributes[v.mType]['TotalSize'] > + v.Struct =3D DataStruct[v.mType] > + v.mName =3D varname > + v.cDefs =3D cStructDefs > + for fieldinfo in VarDataDict.get((guid, varname), []): > + vf =3D VarField() > + vf.Offset =3D fieldinfo['Offset'] > + vf.Value =3D fieldinfo['Value'] > + vf.Size =3D fieldinfo['Size'] > + v.fields.setdefault( > + int(fieldinfo['DefaultStore'], 10), []).append(vf) > + v.serial() > + v.pack() > + self.NvVarInfo.append(v) > + > + def PackDeltaData(self): > + > + default_id_set =3D set() > + for v in self.NvVarInfo: > + default_id_set |=3D set(v.mBin.keys()) > + > + if default_id_set: > + default_id_set.remove(0) > + delta_buff_set =3D {} > + for defaultid in default_id_set: > + delta_buff =3D b'' > + for v in self.NvVarInfo: > + delta_list =3D v.delta.get(defaultid,[]) > + for delta in delta_list: > + delta_data =3D DELTA_DATA() > + delta_data.Offset, delta_data.Value =3D delta > + delta_buff +=3D PackStruct(delta_data) > + delta_buff_set[defaultid] =3D delta_buff > + > + return delta_buff_set > + > + def PackDefaultData(self): > + > + default_data_header =3D DEFAULT_DATA() > + default_data_header.HeaderSize =3D sizeof(DEFAULT_DATA) > + default_data_header.DefaultInfo.DefaultId =3D 0x0 > + default_data_header.DefaultInfo.BoardId =3D 0x0 > + default_data_header_buffer =3D PackStruct(default_data_header) > + > + > + variable_store =3D VARIABLE_STORE_HEADER() > + variable_store.Signature =3D AuthVarGuid > + > + variable_store_size =3D Get_Occupied_Size(sizeof(DEFAULT_DATA) += sizeof(VARIABLE_STORE_HEADER), 4) > + for v in self.NvVarInfo: > + variable_store_size +=3D Get_Occupied_Size(len(v.mBin[0]), 4= ) > + > + variable_store.Size =3D variable_store_size > + variable_store.Format =3D VARIABLE_STORE_FORMATTED > + variable_store.State =3D VARIABLE_STORE_HEALTHY > + variable_store.Reserved =3D 0x0 > + variable_store.Reserved2 =3D 0x0 > + > + variable_storage_header_buffer =3D PackStruct(variable_store) > + > + variable_data =3D b'' > + v_offset =3D 0 > + for v in self.NvVarInfo: > + v.update_delta_offset(v_offset) > + variable_data +=3D Occupied_Size(v.mBin[0],4) > + v_offset +=3D Get_Occupied_Size(len(v.mBin[0]),4) > + > + > + final_buff =3D Occupied_Size(default_data_header_buffer + variab= le_storage_header_buffer,4) + variable_data > + > + return final_buff > + > + def generate(self, jsonlistfile,output_folder): > + if not os.path.exists(jsonlistfile): > + return > + if not os.path.exists(output_folder): > + os.makedirs(output_folder) > + try: > + with open(jsonlistfile,"r") as fd: > + filelist =3D fd.readlines() > + genVar =3D DefaultVariableGenerator() > + genVar.LoadNvVariableInfo(filelist) > + with open(os.path.join(output_folder, "default.bin"), "wb") = as fd: > + fd.write(genVar.PackDefaultData()) > + > + delta_set =3D genVar.PackDeltaData() > + for default_id in delta_set: > + with open(os.path.join(output_folder, "defaultdelta_%s.b= in" % default_id), "wb") as fd: > + fd.write(delta_set[default_id]) > + except: > + print("generate varbin file failed") > + > + > + > diff --git a/BaseTools/Source/Python/AutoGen/ModuleAutoGen.py b/BaseTools= /Source/Python/AutoGen/ModuleAutoGen.py > index d70b0d7ae828..0daf3352f91b 100755 > --- a/BaseTools/Source/Python/AutoGen/ModuleAutoGen.py > +++ b/BaseTools/Source/Python/AutoGen/ModuleAutoGen.py > @@ -433,10 +433,19 @@ class ModuleAutoGen(AutoGen): > ## Return the directory to store auto-gened source files of the modul= e > @cached_property > def DebugDir(self): > return _MakeDir((self.BuildDir, "DEBUG")) >=20 > + > + @cached_property > + def DefaultVarJsonFiles(self): > + rt =3D [] > + for SrcFile in self.SourceFileList: > + if SrcFile.Ext.lower() =3D=3D '.vfr': > + rt.append(os.path.join(self.DebugDir,os.path.join(os.pat= h.dirname(SrcFile.File), "{}_var.json".format(SrcFile.BaseName)))) > + return rt > + > ## Return the path of custom file > @cached_property > def CustomMakefile(self): > RetVal =3D {} > for Type in self.Module.CustomMakefile: > diff --git a/BaseTools/Source/Python/AutoGen/ModuleAutoGenHelper.py b/Bas= eTools/Source/Python/AutoGen/ModuleAutoGenHelper.py > index 036fdac3d7df..b46d041f58ab 100644 > --- a/BaseTools/Source/Python/AutoGen/ModuleAutoGenHelper.py > +++ b/BaseTools/Source/Python/AutoGen/ModuleAutoGenHelper.py > @@ -644,10 +644,14 @@ class PlatformInfo(AutoGenInfo): > if Attr !=3D 'PATH': > BuildOptions[ExpandedTool][Attr] +=3D " "= + mws.handleWsMacro(Value) > else: > BuildOptions[ExpandedTool][Attr] =3D mws.= handleWsMacro(Value) >=20 > + if self.DataPipe.Get("GenDefaultVarBin"): > + if BuildOptions.get('VFR',{}).get('FLAGS'): > + BuildOptions['VFR']['FLAGS'] +=3D " " + "--variable" > + > return BuildOptions, BuildRuleOrder >=20 > def ApplyLibraryInstance(self,module): > alldeps =3D self.DataPipe.Get("DEPS") > if alldeps is None: > diff --git a/BaseTools/Source/Python/Common/GlobalData.py b/BaseTools/Sou= rce/Python/Common/GlobalData.py > index 61ab3f7e24cd..c68ca8fbb3f7 100755 > --- a/BaseTools/Source/Python/Common/GlobalData.py > +++ b/BaseTools/Source/Python/Common/GlobalData.py > @@ -88,10 +88,15 @@ gIgnoreSource =3D False > # > gFdfParser =3D None >=20 > BuildOptionPcd =3D [] >=20 > +# > +# Build flag for generate default variable binary file > +# > +gGenDefaultVarBin =3D False > + > # > # Mixed PCD name dict > # > MixedPcd =3D {} >=20 > diff --git a/BaseTools/Source/Python/build/build.py b/BaseTools/Source/Py= thon/build/build.py > index 02b489892422..2f6fc6b20faf 100755 > --- a/BaseTools/Source/Python/build/build.py > +++ b/BaseTools/Source/Python/build/build.py > @@ -750,10 +750,11 @@ class Build(): > GlobalData.gUseHashCache =3D BuildOptions.UseHashCache > GlobalData.gBinCacheDest =3D BuildOptions.BinCacheDest > GlobalData.gBinCacheSource =3D BuildOptions.BinCacheSource > GlobalData.gEnableGenfdsMultiThread =3D not BuildOptions.NoGenfds= MultiThread > GlobalData.gDisableIncludePathCheck =3D BuildOptions.DisableInclu= dePathCheck > + GlobalData.gGenDefaultVarBin =3D BuildOptions.GenDefaultVarBin >=20 > if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache: > EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData=3D"-= -binary-destination must be used together with --hash.") >=20 > if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache: > @@ -1459,10 +1460,14 @@ class Build(): > self.BuildModules =3D [] > return True >=20 > # genfds > if Target =3D=3D 'fds': > + if GlobalData.gGenDefaultVarBin: > + from AutoGen.GenDefaultVar import DefaultVariableGenerat= or > + variable_info_filelist =3D os.path.join(AutoGenObject.Bu= ildDir,"variable_info_filelist.txt") > + DefaultVariableGenerator().generate(variable_info_fileli= st,AutoGenObject.FvDir) > if GenFdsApi(AutoGenObject.GenFdsCommandDict, self.Db): > EdkLogger.error("build", COMMAND_FAILURE) > Threshold =3D self.GetFreeSizeThreshold() > if Threshold: > self.CheckFreeSizeThreshold(Threshold, AutoGenObject.FvDi= r) > @@ -2247,10 +2252,19 @@ class Build(): > AutoGenIdFile =3D os.path.join(GlobalData.gConfDirectory,".AutoGe= nIdFile.txt") > with open(AutoGenIdFile,"w") as fw: > fw.write("Arch=3D%s\n" % "|".join((Wa.ArchList))) > fw.write("BuildDir=3D%s\n" % Wa.BuildDir) > fw.write("PlatformGuid=3D%s\n" % str(Wa.AutoGenObjectList[0].= Guid)) > + variable_info_filelist =3D os.path.join(Wa.BuildDir,"variable_in= fo_filelist.txt") > + vfr_var_json =3D [] > + if GlobalData.gGenDefaultVarBin: > + for ma in self.AllModules: > + vfr_var_json.extend(ma.DefaultVarJsonFiles) > + SaveFileOnChange(variable_info_filelist, "\n".join(vfr_var_j= son), False) > + else: > + if os.path.exists(variable_info_filelist): > + os.remove(variable_info_filelist) >=20 > if GlobalData.gBinCacheSource: > BuildModules.extend(self.MakeCacheMiss) > elif GlobalData.gUseHashCache and not GlobalData.gBinCacheDest: > BuildModules.extend(self.PreMakeCacheMiss) > @@ -2359,10 +2373,14 @@ class Build(): >=20 > if self.Fdf: > # > # Generate FD image if there's a FDF file found > # > + if GlobalData.gGenDefaultVarBin: > + from AutoGen.GenDefaultVar import DefaultVar= iableGenerator > + variable_info_filelist =3D os.path.join(Wa.B= uildDir,"variable_info_filelist.txt") > + DefaultVariableGenerator().generate(variable= _info_filelist,Wa.FvDir) > GenFdsStart =3D time.time() > if GenFdsApi(Wa.GenFdsCommandDict, self.Db): > EdkLogger.error("build", COMMAND_FAILURE) > Threshold =3D self.GetFreeSizeThreshold() > if Threshold: > diff --git a/BaseTools/Source/Python/build/buildoptions.py b/BaseTools/So= urce/Python/build/buildoptions.py > index 39d92cff209d..6886ba7f8eb6 100644 > --- a/BaseTools/Source/Python/build/buildoptions.py > +++ b/BaseTools/Source/Python/build/buildoptions.py > @@ -99,7 +99,8 @@ class MyOptionParser(): > Parser.add_option("--hash", action=3D"store_true", dest=3D"UseHas= hCache", default=3DFalse, help=3D"Enable hash-based caching during build pr= ocess.") > Parser.add_option("--binary-destination", action=3D"store", type= =3D"string", dest=3D"BinCacheDest", help=3D"Generate a cache of binary file= s in the specified directory.") > Parser.add_option("--binary-source", action=3D"store", type=3D"st= ring", dest=3D"BinCacheSource", help=3D"Consume a cache of binary files fro= m the specified directory.") > Parser.add_option("--genfds-multi-thread", action=3D"store_true",= dest=3D"GenfdsMultiThread", default=3DTrue, help=3D"Enable GenFds multi th= read to generate ffs file.") > Parser.add_option("--no-genfds-multi-thread", action=3D"store_tru= e", dest=3D"NoGenfdsMultiThread", default=3DFalse, help=3D"Disable GenFds m= ulti thread to generate ffs file.") > + Parser.add_option("--gen-default-variable-bin", action=3D"store_= true", dest=3D"GenDefaultVarBin", default=3DFalse, help=3D"Generate default= variable binary file.") > Parser.add_option("--disable-include-path-check", action=3D"store= _true", dest=3D"DisableIncludePathCheck", default=3DFalse, help=3D"Disable = the include path check for outside of package.") > self.BuildOption, self.BuildTarget =3D Parser.parse_args() > --=20 > 2.18.0.windows.1 >=20 >=20 >=20 >=20 >=20 >=20 --Apple-Mail=_35D03FF2-A3E3-46ED-9EAC-AE4A45DCFD22 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8

On Aug 13, 20= 21, at 4:45 AM, Bob Feng <bob.c.feng@intel.com> wrote:

REF: https://bugzilla.tianoco= re.org/show_bug.cgi?id=3D3562

Create a new= build option to enable vfrcompile to generate Json
format EF= I variable information file and read it to generate
the EFI = variable default value binary file.

Signed-off= -by: Bob Feng <bob.c.= feng@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Yuwei Chen <yuwei.chen@intel.com>
---
BaseTools/= Source/Python/AutoGen/DataPipe.py   |   2 +
.../Source/Python/AutoGen/GenDefaultVar.py    | 498 ++++= ++++++++++++++
.../Source/Python/AutoGen/ModuleAutoGen.py &n= bsp;  |   9 +
.../Python/AutoGen/ModuleA= utoGenHelper.py     |   4 +
Ba= seTools/Source/Python/Common/GlobalData.py  |   5 +
BaseTools/Source/Python/build/build.py      = ;  |  18 +
BaseTools/Source/Python/build/buil= doptions.py |   1 +
7 files changed, 537 insertion= s(+)
create mode 100644 BaseTools/Source/Python/AutoGen/GenD= efaultVar.py

diff --git a/BaseTools/Source/Pyt= hon/AutoGen/DataPipe.py b/BaseTools/Source/Python/AutoGen/DataPipe.py
index 86ac2b928d9c..fa0c36b98f21 100755
--- a/BaseTo= ols/Source/Python/AutoGen/DataPipe.py
+++ b/BaseTools/Source/= Python/AutoGen/DataPipe.py
@@ -165,5 +165,7 @@ class MemoryDa= taPipe(DataPipe):
       =  self.DataContainer =3D {"BinCacheSource":GlobalData.gBinCacheSource}<= br class=3D"">
       &nb= sp;self.DataContainer =3D {"BinCacheDest":GlobalData.gBinCacheDest}

        self= .DataContainer =3D {"EnableGenfdsMultiThread":GlobalData.gEnableGenfdsMulti= Thread}
+
+       = ; self.DataContainer =3D {"GenDefaultVarBin": GlobalData.gGenDefaultVa= rBin}
diff --git a/BaseTools/Source/Python/AutoGen/GenDefault= Var.py b/BaseTools/Source/Python/AutoGen/GenDefaultVar.py
new= file mode 100644
index 000000000000..b82cce18ed26
--- /dev/null
+++ b/BaseTools/Source/Python/AutoGen/Gen= DefaultVar.py
@@ -0,0 +1,498 @@
+import json+from ctypes import *
+import re
+i= mport copy
+from struct import unpack
+import o= s
+
+class GUID(Structure):

We should use (LittleEnd= ianStructure) vs. (Structure). Structure is the endian of the Host, and EFI= is little endian. Same comment for all the ctype structs. LittleEndianStru= cture is like using < with struct.pack()/struct.unpack(). 
+    _fields_ =3D [
+    =     ('Guid1',        = ;    c_uint32),
+     = ;   ('Guid2',        &nbs= p;   c_uint16),
+     &nbs= p;  ('Guid3',         &nb= sp;  c_uint16),
+      &nb= sp; ('Guid4',          &n= bsp; ARRAY(c_uint8, 8)),
+    ]
+

I think it would be better to use the same field names as the EFI C= structures. 

<= /tr>
= typedef struct {
UINT32 Data1;
UINT16 Data2;
UINT16 Data3;
= UINT8 Data4[8];
} GUID

Longer= term I think the BaseBools should have a GUID class that can be shared tha= t abstracts things GUIDs. It should store GUIDs as Python uuid.UUID() or at= least in the common string form, and have useful operations. 

I=E2=80=99ve got an example GUID class in my g= db patch set, but it also loads the GUID database and deals with C name, C = GUID structure init form, and strings. But it feels like we could pull out = a common lower level GUID class out of that. 

=
= +    def from_list(self, listformat):
+  =       self.Guid1 =3D listformat[0]
+        self.Guid2 =3D listformat= [1]
+        self.Guid3 = =3D listformat[2]
+        = ;for i in range(8):
+       &nb= sp;    self.Guid4[i] =3D listformat[i+3]
= +
+    def __cmp__(self, otherguid):
+        if isinstance(otherguid, = GUID):
+         &nbs= p;  return 1
+       =  rt =3D False
+       &nbs= p;if self.Guid1 =3D=3D otherguid.Guid1 and self.Guid2 =3D=3D otherguid.Guid= 2 and self.Guid3 =3D=3D otherguid.Guid3:
+    =         rt =3D True
+=            for i in= range(8):
+         =        rt =3D rt & (self.Guid4[i] = =3D=3D otherguid.Guid4[i])
+      &n= bsp; return rt
+
+
+class TI= ME(Structure):
+    _fields_ =3D [
+        ('Year',    &= nbsp;        c_uint16),
+        ('Month',    =         c_uint8),
+ &= nbsp;      ('Day',     &n= bsp;        c_uint8),
+        ('Hour',    &nb= sp;        c_uint8),
= +        ('Minute',    &n= bsp;      c_uint8),
+  &nb= sp;     ('Second',      &= nbsp;    c_uint8),
+    &n= bsp;   ('Pad1',        &n= bsp;    c_uint8),
+    &nb= sp;   ('Nanosecond',       c_u= int32),
+        ('TimeZon= e',         c_uint16),
+        ('Daylight',   &nbs= p;     c_uint8),
+    = ;    ('Pad2',        = ;     c_uint8),
+    = ]
+    def __init__(self):
+ &nb= sp;      self.Year =3D 0x0
+ &n= bsp;      self.Month =3D 0x0
+ =        self.Day =3D 0x0
+ =        self.Hour =3D 0x0
+=        self.Minute =3D 0x0
+        self.Second =3D 0x0
+        self.Pad1 =3D 0x0
+        self.Nanosecond =3D 0x0<= br class=3D"">+        self.TimeZone =3D= 0x0
+        self.Dayligh= t =3D 0x0
+        self.Pa= d2 =3D 0x0
+
+
+EFI_VARIABLE_GUID= =3D [0xddcf3616, 0x3275, 0x4164,
+     &= nbsp;           &nbs= p;   0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d]
+EFI_AUTHENTICATED_VARIABLE_GUID =3D [
+   &= nbsp;0xaaf32c78, 0x947b, 0x439a, 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, = 0x92]
+
+AuthVarGuid =3D GUID()
+= AuthVarGuid.from_list(EFI_AUTHENTICATED_VARIABLE_GUID)
+VarGu= id =3D GUID()
+VarGuid.from_list(EFI_VARIABLE_GUID)
+
+# Variable Store Header Format.
+VARIA= BLE_STORE_FORMATTED =3D 0x5a
+# Variable Store Header State.<= br class=3D"">+VARIABLE_STORE_HEALTHY =3D 0xfe
+
+
+class VARIABLE_STORE_HEADER(Structure):
+ =    _fields_ =3D [
+     &n= bsp;  ('Signature',        &nb= sp;       GUID),
+  &= nbsp;     ('Size',      &= nbsp;           &nbs= p;  c_uint32),
+      &nbs= p; ('Format',          &n= bsp;        c_uint8),
+        ('State',    &n= bsp;            = ;   c_uint8),
+      =   ('Reserved',         &n= bsp;       c_uint16),
+ &n= bsp;      ('Reserved1',    &nb= sp;           c_uint= 32),
+    ]
+
++# Variable data start flag.
+VARIABLE_DATA =3D 0= x55AA
+
+# Variable State flags.
= +VAR_IN_DELETED_TRANSITION =3D 0xfe
+VAR_DELETED =3D 0xfd
+VAR_HEADER_VALID_ONLY =3D 0x7f
+VAR_ADDED =3D 0x3= f
+
+
+class VARIABLE_HEADER(Stru= cture):
+    _fields_ =3D [
+ &n= bsp;      ('StartId',     = ;            &n= bsp;c_uint16),
+        ('= State',            &= nbsp;       c_uint8),
+ &n= bsp;      ('Reserved',    &nbs= p;            c= _uint8),
+        ('Attrib= utes',            &n= bsp;  c_uint32),
+      &n= bsp; ('NameSize',         &nbs= p;       c_uint32),
+ &nbs= p;      ('DataSize',     =             c_u= int32),
+        ('VendorG= uid',            &nb= sp;  GUID),
+    ]
++
+class AUTHENTICATED_VARIABLE_HEADER(Structure= ):
+    _fields_ =3D [
+  &= nbsp;     ('StartId',     &nbs= p;            c= _uint16),
+        ('State= ',             =        c_uint8),
+  &= nbsp;     ('Reserved',     &nb= sp;           c_uint= 8),
+        ('Attributes'= ,             &= nbsp; c_uint32),
+       &= nbsp;('MonotonicCount',         &nb= sp; c_uint64),
+       &nb= sp;('TimeStamp',          &nbs= p;     TIME),
+    &n= bsp;   ('PubKeyIndex',       &= nbsp;      c_uint32),
+  &= nbsp;     ('NameSize',     &nb= sp;           c_uint= 32),
+        ('DataSize',=             &n= bsp;   c_uint32),
+     &n= bsp;  ('VendorGuid',        &n= bsp;      GUID),
+   =  ]
+    _pack_ =3D 1
+
+
+# Alignment of Variable Data Header in Variable = Store region.
+HEADER_ALIGNMENT =3D 4
+
+
+class DEFAULT_INFO(Structure):
+ &nbs= p;  _fields_ =3D [
+      =   ('DefaultId',         &= nbsp;      c_uint16),
+  &= nbsp;     ('BoardId',     &nbs= p;            c= _uint16),
+    ]
+
+
+class DEFAULT_DATA(Structure):
+  &nbs= p; _fields_ =3D [
+       =  ('HeaderSize',          =      c_uint16),
+    =     ('DefaultInfo',       = ;       DEFAULT_INFO),
+ &= nbsp;  ]
+
+class DELTA_DATA(Structur= e):
+    _fields_ =3D [
+  =       ('Offset', c_uint16),
+ &= nbsp;      ('Value', c_uint8),
= +    ]
+    _pack_ =3D 1
+
+array_re =3D re.compile(
+  &nb= sp; "(?P<mType>[a-z_A-Z][a-z_A-Z0-9]*)\[(?P<mSize>[1-9][0-= 9]*)\]")
+
+
+class VarField():+    def __init__(self):
+  =       self.Offset =3D 0
+  = ;      self.Value =3D 0
+  = ;      self.Size =3D 0
+
+    @property
+    def= Type(self):
+        if s= elf.Size =3D=3D 1:
+       &nbs= p;    return "UINT8"
+    =     if self.Size =3D=3D 2:
+   =          return "UINT16"
+        if self.Size =3D=3D 4:=
+          &nbs= p; return "UINT32"
+       = ; if self.Size =3D=3D 8:
+      = ;      return "UINT64"
+
+        return "UINT8"
+
+
+BASIC_TYPE =3D {
+ &nb= sp;  "BOOLEAN": 1,
+    "UINT8": 1,<= br class=3D"">+    "UINT16": 2,
+   =  "UINT32": 4,
+    "UINT64": 8
+}

<= div>This might be more of a phase 2=E2=80=A6.

I think you made a lot of extra work by inventing your own type syst= em. You should just convert to ctype as fast as you can. 
BASIC_TYPE =3D {
    "BOOL= EAN": c_uint8,
    "UINT8": c_uint8,
  &= nbsp; "UINT16": c_uint16,
    "UINT32": c_uint32,
=
    "UINT64": c_uint64
}

Then you can just sizeof() to get the size=

FYI I wrote this= ctype dumper in case it is useful debugging this code.  It picks off = EFI_GUID so it can print it as a proper UUID using the GUID class I mention= ed above, so basically a nicer print option. 
def ctype_to_str(ctype, i= ndent=3D'', hide_list=3D[]):
    '''
    Given a ctype object print out as a string by w= alking the _fields_
    in the cstring Class=
     '''
  &= nbsp; result =3D ''
    for field in ctype._= fields_:
        attr =3D getattr(= ctype, field[0])
        tname =3D= type(attr).__name__
        if fi= eld[0] in hide_list:
         = ;   continue

        result +=3D indent + f'{field[0]} =3D '
<= div class=3D"">        if tname =3D=3D 'EFI_GUID':
            result +=3D Guid= Names.to_name(GuidNames.to_uuid(attr)) + '\n'
  &= nbsp;     elif issubclass(type(attr), Structure):
            result +=3D f'{tname}\n' + = \
              &nb= sp; ctype_to_str(attr, indent + '  ', hide_list)
=         elif isinstance(attr, int):
            result +=3D f'0x{attr:x}\n'
        else:
&n= bsp;           result +=3D f'{attr}\n'

    return result<= /div>

Thanks,
Andrew Fish

+class CStruct():
+
+
+    def __init__= (self, typedefs):
+        = ;self.TypeDefs =3D typedefs
+      &= nbsp; self.TypeStack =3D copy.deepcopy(typedefs)
+  = ;      self.finalDefs =3D {}
+<= br class=3D"">+    def CalStuctSize(self, sType):
+        rt =3D 0
+ =        if sType in BASIC_TYPE:
+            r= eturn BASIC_TYPE[sType]
+
+    &= nbsp;   ma =3D array_re.match(sType)
+  &= nbsp;     if ma:
+    = ;        mType =3D ma.group('mType'= )
+          &nb= sp; mSize =3D ma.group('mSize')
+    &nbs= p;       rt +=3D int(mSize) * self.CalSt= uctSize(mType)
+        el= se:
+          &= nbsp; for subType in self.TypeDefs[sType]:
+   = ;            &n= bsp;rt +=3D self.CalStuctSize(subType['Type'])
+
+        return rt
+
+    def expend(self, fielditem):
+=        fieldname =3D fielditem['Name']<= br class=3D"">+        fieldType =3D fie= lditem['Type']
+        fi= eldOffset =3D fielditem['Offset']
+
+  &nb= sp;     ma =3D array_re.match(fieldType)
+        if ma:
+ &n= bsp;          mType =3D m= a.group('mType')
+        =     mSize =3D ma.group('mSize')
+  &= nbsp;         return [{"Name":= "%s[%d]" % (fieldname, i), "Type": mType, "Offset": (fieldOffset + i*self.= CalStuctSize(mType))} for i in range(int(mSize))]
+  &nb= sp;     else:
+    &n= bsp;       return [{"Name": "%s.%s" % (f= ieldname, item['Name']), "Type":item['Type'], "Offset": (fieldOffset + item= ['Offset'])} for item in self.TypeDefs[fielditem['Type']]]
+<= br class=3D"">+    def ExpandTypes(self):
+ &n= bsp;      if not self.finalDefs:
+            for d= atatype in self.TypeStack:
+      &n= bsp;         result =3D []
+           &n= bsp;    mTypeStack =3D self.TypeStack[datatype]
+            =     while len(mTypeStack) > 0:
+  = ;            &n= bsp;     item =3D mTypeStack.pop()
+=             &n= bsp;      if item['Type'] in self.BASIC_TYPE:=
+          &nbs= p;            &= nbsp;result.append(item)
+      &nbs= p;            &= nbsp;elif item['Type'] =3D=3D '(null)':
+    &= nbsp;           &nbs= p;       continue
+  =             &nb= sp;     else:
+    &n= bsp;            = ;       for expand_item in self.expend(i= tem):
+          = ;            &n= bsp;     mTypeStack.append(expand_item)
+            &= nbsp;   self.finalDefs[datatype] =3D result
+ =            self.fina= lDefs
+        return self= .finalDefs
+
+def Get_Occupied_Size(FileLength,= alignment):
+    if FileLength % alignment = =3D=3D 0:
+        return = FileLength
+    return FileLength + (alignment= -(FileLength % alignment))
+
+def Occupied_Size= (buffer, alignment):
+    FileLength =3D len(b= uffer)
+    if FileLength % alignment !=3D 0:<= br class=3D"">+        buffer +=3D b'\0'= * (alignment-(FileLength % alignment))
+    r= eturn buffer
+
+def PackStruct(cStruct):
+    length =3D sizeof(cStruct)
+ &nbs= p;  p =3D cast(pointer(cStruct), POINTER(c_char * length))
+    return p.contents.raw
+
+def calculate_delta(default, theother):
+
+    if len(default) - len(theother) !=3D 0:
+        return []
+
+    data_delta =3D []
+   = ; for i in range(len(default)):
+    &nbs= p;   if default[i] !=3D theother[i]:
+  &= nbsp;         data_delta.appen= d([i, theother[i]])
+    return data_delta
+
+class Variable():
+   &= nbsp;def __init__(self):
+      &nbs= p; self.mAlign =3D 1
+      &nb= sp; self.mTotalSize =3D 1
+     &nbs= p;  self.mValue =3D {}  # {defaultstore: value}
+        self.mBin =3D {}
+        self.fields =3D {}  # {d= efaultstore: fileds}
+       &n= bsp;self.delta =3D {}
+       &= nbsp;self.attributes =3D 0
+      &n= bsp; self.mType =3D ''
+      &= nbsp; self.guid =3D ''
+      &= nbsp; self.mName =3D ''
+      =   self.cDefs =3D None
+
+   = ; @property
+    def GuidArray(self):
+
+        guid= _array =3D []
+        gui= d =3D self.guid.strip().strip("{").strip("}")
+   &= nbsp;    for item in guid.split(","):
+ &= nbsp;          field =3D = item.strip().strip("{").strip("}")
+     =        guid_array.append(int(field,16))<= br class=3D"">+        return guid_array=
+
+    def update_delta_offset(= self,base):
+        for d= efault_id in self.delta:
+      &nbs= p;     for delta_list in self.delta[default_id]:+           =      delta_list[0] +=3D base
+
+    def pack(self):
+
= +        for defaultid in self.mValue:+           =  var_value =3D self.mValue[defaultid]
+   &nbs= p;        auth_var =3D AUTHENTICATE= D_VARIABLE_HEADER()
+       &nb= sp;    auth_var.StartId =3D VARIABLE_DATA
+            auth_v= ar.State =3D VAR_ADDED
+       =      auth_var.Reserved =3D 0x00
+ &n= bsp;          auth_var.At= tributes =3D 0x00000007
+       = ;     auth_var.MonotonicCount =3D 0x0
+            auth_= var.TimeStamp =3D TIME()
+      &nbs= p;     auth_var.PubKeyIndex =3D 0x0
= +            var_nam= e_buffer =3D self.mName.encode('utf-16le') + b'\0\0'
+  =           auth_var.NameSi= ze =3D len(var_name_buffer)
+      &= nbsp;     auth_var.DataSize =3D len(var_value)
+           &n= bsp;vendor_guid =3D GUID()
+      &n= bsp;     vendor_guid.from_list(self.GuidArray)
+           &n= bsp;auth_var.VendorGuid =3D vendor_guid
+
+ &nb= sp;          self.mBin[de= faultid] =3D PackStruct(auth_var) + Occupied_Size(var_name_buffer + var_val= ue, 4)
+
+    def TypeCheck(self= ,data_type, data_size):
+       = ; if BASIC_TYPE[data_type] =3D=3D data_size:
+  &nb= sp;         return True
+        return False
+
+    def ValueToBytes(self,data_type,d= ata_value,data_size):
+
+    &nb= sp;   rt =3D b''
+     &nb= sp;  if not self.TypeCheck(data_type, data_size):
+=            print(da= ta_type,data_value,data_size)
+
+   &= nbsp;    if data_type =3D=3D "BOOLEAN" or data_type =3D= =3D 'UINT8':
+        &nbs= p;   p =3D cast(pointer(c_uint8(int(data_value,16))), POINTE= R(c_char * 1))
+        &n= bsp;   rt =3D p.contents.raw
+   &nb= sp;    elif data_type =3D=3D 'UINT16':
+ =            p =3D cas= t(pointer(c_uint16(int(data_value,16))), POINTER(c_char * 2))
+            rt =3D= p.contents.raw
+        e= lif data_type =3D=3D 'UINT32':
+     &nbs= p;      p =3D cast(pointer(c_uint32(int(data_= value,16))), POINTER(c_char * 4))
+     &= nbsp;      rt =3D p.contents.raw
+        elif data_type =3D=3D 'UINT64= ':
+          &n= bsp; p =3D cast(pointer(c_uint64(int(data_value,16))), POINTER(c_char = * 8))
+          = ;  rt =3D p.contents.raw
+
+  &n= bsp;     return rt
+
+=    def serial(self):
+    &nbs= p;   for defaultstore in self.fields:
+  =           vValue =3D b''<= br class=3D"">+           = ; vfields =3D {vf.Offset: vf for vf in self.fields[defaultstore]}
+           &nb= sp;i =3D 0
+         =    while i < self.mTotalSize:
+   = ;            &n= bsp;if i in vfields:
+       &n= bsp;            = ;vfield =3D vfields[i]
+       =             &nb= sp;vValue +=3D self.ValueToBytes(vfield.Type, vfield.Value,vfield.Size)
+           &n= bsp;        i +=3D vfield.Size
+           &nb= sp;    else:
+     &n= bsp;            = ;  vValue +=3D self.ValueToBytes('UINT8','0x00',1)
= +             &= nbsp;      i +=3D 1
+
+            s= elf.mValue[defaultstore] =3D vValue
+     = ;   standard_default =3D self.mValue[0]
+
+        for defaultid in self= .mValue:
+         &n= bsp;  if defaultid =3D=3D 0:
+    &n= bsp;           conti= nue
+          &= nbsp; others_default =3D self.mValue[defaultid]
+
+            = ;self.delta.setdefault(defaultid, []).extend(calculate_delta(
+             =    standard_default, others_default))
+
+class DefaultVariableGenerator():
+   &nbs= p;def __init__(self):
+       &= nbsp;self.NvVarInfo =3D []
+
+   &nbs= p;def LoadNvVariableInfo(self, VarInfoFilelist):
+
+        VarDataDict =3D {}
+        DataStruct =3D {}
+        VarDefine =3D {}
+        VarAttributes =3D {}+        for VarInfoFile in= VarInfoFilelist:
+        = ;    with open(VarInfoFile.strip(), "r") as fd:
+            =     data =3D json.load(fd)
+
+            DataS= truct.update(data.get("DataStruct", {}))
+    =         Data =3D data.get("Data")+           =  VarDefine.update(data.get("VarDefine"))
+   &= nbsp;        VarAttributes.update(d= ata.get("DataStructAttribute"))
+
+   = ;         for vardata in Data:=
+          &nbs= p;     if vardata['VendorGuid'] =3D=3D 'NA':
+           &nbs= p;        continue
+ =             &nb= sp;  VarDataDict.setdefault(
+    &n= bsp;            = ;   (vardata['VendorGuid'], vardata["VarName"]), []).append(= vardata)
+
+      &nbs= p; cStructDefs =3D CStruct(DataStruct)
+   &nb= sp;    for guid, varname in VarDataDict:
= +            v =3D V= ariable()
+         &= nbsp;  v.guid =3D guid
+     &n= bsp;      vardef =3D VarDefine.get(varname)+           =  if vardef is None:
+      &nbs= p;         for var in VarDefin= e:
+          &n= bsp;         if VarDefine[var]= ['Type'] =3D=3D varname:
+      &nbs= p;            &= nbsp;    vardef =3D VarDefine[var]
+ &nbs= p;            &= nbsp;         break
+            &= nbsp;   else:
+      =             &nb= sp; continue
+        = ;    v.attributes =3D vardef['Attributes']
+            v.mTy= pe =3D vardef['Type']
+       &= nbsp;    v.mAlign =3D VarAttributes[v.mType]['Alignment= ']
+          &n= bsp; v.mTotalSize =3D VarAttributes[v.mType]['TotalSize']
+            v.Str= uct =3D DataStruct[v.mType]
+      &= nbsp;     v.mName =3D varname
+ &nbs= p;          v.cDefs =3D c= StructDefs
+         =    for fieldinfo in VarDataDict.get((guid, varname), []):+           &= nbsp;    vf =3D VarField()
+   =             &nb= sp;vf.Offset =3D fieldinfo['Offset']
+    &nbs= p;           vf.Valu= e =3D fieldinfo['Value']
+      &nbs= p;         vf.Size =3D fieldin= fo['Size']
+         =        v.fields.setdefault(
+             = ;       int(fieldinfo['DefaultStore'], 1= 0), []).append(vf)
+       &nbs= p;    v.serial()
+    &nbs= p;       v.pack()
+  =           self.NvVarInfo.= append(v)
+
+    def PackDeltaDa= ta(self):
+
+      &nb= sp; default_id_set =3D set()
+     &= nbsp;  for v in self.NvVarInfo:
+    = ;        default_id_set |=3D set(v.= mBin.keys())
+
+      =   if default_id_set:
+     &nbs= p;      default_id_set.remove(0)
+        delta_buff_set =3D {}
+        for defaultid in default= _id_set:
+         &n= bsp;  delta_buff =3D b''
+     =        for v in self.NvVarInfo:
+            &= nbsp;   delta_list =3D v.delta.get(defaultid,[])
+            &nbs= p;   for delta in delta_list:
+   &n= bsp;            = ;    delta_data =3D DELTA_DATA()
+  =             &nb= sp;     delta_data.Offset, delta_data.Value =3D de= lta
+          &= nbsp;         delta_buff +=3D = PackStruct(delta_data)
+       =      delta_buff_set[defaultid] =3D delta_buff
+
+        retur= n delta_buff_set
+
+    def Pack= DefaultData(self):
+
+     =    default_data_header =3D DEFAULT_DATA()
+ &n= bsp;      default_data_header.HeaderSize =3D = sizeof(DEFAULT_DATA)
+       &n= bsp;default_data_header.DefaultInfo.DefaultId =3D 0x0
+  = ;      default_data_header.DefaultInfo.BoardI= d =3D 0x0
+        default= _data_header_buffer =3D PackStruct(default_data_header)
+
+
+        vari= able_store =3D VARIABLE_STORE_HEADER()
+    &n= bsp;   variable_store.Signature =3D AuthVarGuid
+
+        variable_stor= e_size =3D Get_Occupied_Size(sizeof(DEFAULT_DATA) + sizeof(VARIABLE_STORE_H= EADER), 4)
+        for v = in self.NvVarInfo:
+       &nbs= p;    variable_store_size +=3D Get_Occupied_Size(len(v.= mBin[0]), 4)
+
+      =   variable_store.Size =3D variable_store_size
+ &nb= sp;      variable_store.Format =3D VARIABLE_S= TORE_FORMATTED
+        va= riable_store.State =3D VARIABLE_STORE_HEALTHY
+   &= nbsp;    variable_store.Reserved =3D 0x0
= +        variable_store.Reserved2 =3D 0x= 0
+
+        = ;variable_storage_header_buffer =3D PackStruct(variable_store)
+
+        variable_data= =3D b''
+        v_offset= =3D 0
+        for v in s= elf.NvVarInfo:
+        &n= bsp;   v.update_delta_offset(v_offset)
+  = ;          variable_data = +=3D Occupied_Size(v.mBin[0],4)
+     &nb= sp;      v_offset +=3D Get_Occupied_Size(len(= v.mBin[0]),4)
+
+
+   &= nbsp;    final_buff =3D Occupied_Size(default_data_head= er_buffer + variable_storage_header_buffer,4) + variable_data
+
+        return final_b= uff
+
+    def generate(self, js= onlistfile,output_folder):
+      &n= bsp; if not os.path.exists(jsonlistfile):
+   =          return
= +        if not os.path.exists(output_fo= lder):
+         &nbs= p;  os.makedirs(output_folder)
+    =     try:
+      =       with open(jsonlistfile,"r") as fd:
+           &nb= sp;    filelist =3D fd.readlines()
+ &nbs= p;          genVar =3D De= faultVariableGenerator()
+      &nbs= p;     genVar.LoadNvVariableInfo(filelist)
+            = with open(os.path.join(output_folder, "default.bin"), "wb") as fd:
+            &= nbsp;   fd.write(genVar.PackDefaultData())
++           =  delta_set =3D genVar.PackDeltaData()
+   &nbs= p;        for default_id in delta_s= et:
+          &= nbsp;     with open(os.path.join(output_folder, "d= efaultdelta_%s.bin" % default_id), "wb") as fd:
+   = ;            &n= bsp;    fd.write(delta_set[default_id])
+=        except:
+  &n= bsp;         print("generate v= arbin file failed")
+
+
+
diff --git a/BaseTools/Source/Python/AutoGen/ModuleAutoGen.py b/Base= Tools/Source/Python/AutoGen/ModuleAutoGen.py
index d70b0d7ae8= 28..0daf3352f91b 100755
--- a/BaseTools/Source/Python/AutoGen= /ModuleAutoGen.py
+++ b/BaseTools/Source/Python/AutoGen/Modul= eAutoGen.py
@@ -433,10 +433,19 @@ class ModuleAutoGen(AutoGen= ):
    ## Return the directory to store = auto-gened source files of the module
   &nbs= p;@cached_property
    def DebugDir(self= ):
        return _M= akeDir((self.BuildDir, "DEBUG"))

+
+    @cached_property
+    d= ef DefaultVarJsonFiles(self):
+      = ;  rt =3D []
+       =  for SrcFile in self.SourceFileList:
+    = ;        if SrcFile.Ext.lower() =3D= =3D '.vfr':
+         = ;       rt.append(os.path.join(self.Debu= gDir,os.path.join(os.path.dirname(SrcFile.File), "{}_var.json".format(SrcFi= le.BaseName))))
+        r= eturn rt
+
    ## Return t= he path of custom file
    @cached_prope= rty
    def CustomMakefile(self):
        RetVal =3D {}
        for Type in self= .Module.CustomMakefile:
diff --git a/BaseTools/Source/Python/= AutoGen/ModuleAutoGenHelper.py b/BaseTools/Source/Python/AutoGen/ModuleAuto= GenHelper.py
index 036fdac3d7df..b46d041f58ab 100644
--- a/BaseTools/Source/Python/AutoGen/ModuleAutoGenHelper.py
+++ b/BaseTools/Source/Python/AutoGen/ModuleAutoGenHelper.py
@@ -644,10 +644,14 @@ class PlatformInfo(AutoGenInfo):
=             &n= bsp;            = ;   if Attr !=3D 'PATH':
   &n= bsp;            = ;            &n= bsp;   BuildOptions[ExpandedTool][Attr] +=3D " " + mws.handl= eWsMacro(Value)
       &n= bsp;            = ;        else:
 = ;            &n= bsp;            = ;      BuildOptions[ExpandedTool][Attr] =3D m= ws.handleWsMacro(Value)

+    &n= bsp;   if self.DataPipe.Get("GenDefaultVarBin"):
+            if B= uildOptions.get('VFR',{}).get('FLAGS'):
+    &= nbsp;           Buil= dOptions['VFR']['FLAGS'] +=3D " " + "--variable"
+
        return BuildOptions,= BuildRuleOrder

    def A= pplyLibraryInstance(self,module):
    &n= bsp;   alldeps =3D self.DataPipe.Get("DEPS")
=         if alldeps is None:
diff --git a/BaseTools/Source/Python/Common/GlobalData.py b/BaseTool= s/Source/Python/Common/GlobalData.py
index 61ab3f7e24cd..c68c= a8fbb3f7 100755
--- a/BaseTools/Source/Python/Common/GlobalDa= ta.py
+++ b/BaseTools/Source/Python/Common/GlobalData.py
@@ -88,10 +88,15 @@ gIgnoreSource =3D False
#
gFdfParser =3D None

BuildOptionPcd= =3D []

+#
+# Build flag for gen= erate default variable binary file
+#
+gGenDefa= ultVarBin =3D False
+
#
# Mixed= PCD name dict
#
MixedPcd =3D {}

diff --git a/BaseTools/Source/Python/build/build.py b/Base= Tools/Source/Python/build/build.py
index 02b489892422..2f6fc6= b20faf 100755
--- a/BaseTools/Source/Python/build/build.py+++ b/BaseTools/Source/Python/build/build.py
@@ -= 750,10 +750,11 @@ class Build():
    &nb= sp;   GlobalData.gUseHashCache =3D BuildOptions.UseHashCache=
        GlobalData.= gBinCacheDest   =3D BuildOptions.BinCacheDest
&nbs= p;       GlobalData.gBinCacheSource =3D = BuildOptions.BinCacheSource
     &n= bsp;  GlobalData.gEnableGenfdsMultiThread =3D not BuildOptions.No= GenfdsMultiThread
       =  GlobalData.gDisableIncludePathCheck =3D BuildOptions.DisableIncludePa= thCheck
+        GlobalDat= a.gGenDefaultVarBin =3D BuildOptions.GenDefaultVarBin

        if GlobalData.gBin= CacheDest and not GlobalData.gUseHashCache:
  &nbs= p;         EdkLogger.error("bu= ild", OPTION_NOT_SUPPORTED, ExtraData=3D"--binary-destination must be used = together with --hash.")

   &nb= sp;    if GlobalData.gBinCacheSource and not GlobalData= .gUseHashCache:
@@ -1459,10 +1460,14 @@ class Build():
           =  self.BuildModules =3D []
     = ;       return True

        # genfds
        if Target =3D=3D 'fd= s':
+          &= nbsp; if GlobalData.gGenDefaultVarBin:
+   &nb= sp;            = from AutoGen.GenDefaultVar import DefaultVariableGenerator
+ =             &nb= sp;  variable_info_filelist =3D os.path.join(AutoGenObject.BuildD= ir,"variable_info_filelist.txt")
+     &n= bsp;          DefaultVari= ableGenerator().generate(variable_info_filelist,AutoGenObject.FvDir)
           =  if GenFdsApi(AutoGenObject.GenFdsCommandDict, self.Db):
            &= nbsp;   EdkLogger.error("build", COMMAND_FAILURE)
           &n= bsp;Threshold =3D self.GetFreeSizeThreshold()
  &n= bsp;         if Threshold:
          &nb= sp;     self.CheckFreeSizeThreshold(Threshold, Aut= oGenObject.FvDir)
@@ -2247,10 +2252,19 @@ class Build():
        AutoGenIdFile = =3D os.path.join(GlobalData.gConfDirectory,".AutoGenIdFile.txt")
        with open(AutoGenIdF= ile,"w") as fw:
       &n= bsp;    fw.write("Arch=3D%s\n" % "|".join((Wa.ArchList)= ))
         &nb= sp;  fw.write("BuildDir=3D%s\n" % Wa.BuildDir)
&nb= sp;           fw.wri= te("PlatformGuid=3D%s\n" % str(Wa.AutoGenObjectList[0].Guid))
+        variable_info_filelist =3D os.= path.join(Wa.BuildDir,"variable_info_filelist.txt")
+  &= nbsp;     vfr_var_json =3D []
+ &nbs= p;      if GlobalData.gGenDefaultVarBin:
+           &nb= sp;for ma in self.AllModules:
+      = ;          vfr_var_json.e= xtend(ma.DefaultVarJsonFiles)
+      = ;      SaveFileOnChange(variable_info_filelis= t, "\n".join(vfr_var_json), False)
+     =    else:
+       = ;     if os.path.exists(variable_info_filelist):+           =      os.remove(variable_info_filelist)

        if Globa= lData.gBinCacheSource:
      &= nbsp;     BuildModules.extend(self.MakeCacheMiss)<= br class=3D"">         elif GlobalD= ata.gUseHashCache and not GlobalData.gBinCacheDest:
 &n= bsp;          BuildModule= s.extend(self.PreMakeCacheMiss)
@@ -2359,10 +2373,14 @@ class= Build():

      = ;            &n= bsp; if self.Fdf:
      &= nbsp;           &nbs= p;     #
    &n= bsp;            = ;       # Generate FD image if there's a= FDF file found
       &n= bsp;            = ;    #
+      &n= bsp;            = ;     if GlobalData.gGenDefaultVarBin:
+            &nbs= p;            &= nbsp;  from AutoGen.GenDefaultVar import DefaultVariableGenerator=
+          &nbs= p;            &= nbsp;    variable_info_filelist =3D os.path.join(Wa.Bui= ldDir,"variable_info_filelist.txt")
+     = ;            &n= bsp;          DefaultVari= ableGenerator().generate(variable_info_filelist,Wa.FvDir)
&n= bsp;            = ;           GenFdsSt= art =3D time.time()
      &nbs= p;            &= nbsp;    if GenFdsApi(Wa.GenFdsCommandDict, self.Db):           &= nbsp;           &nbs= p;     EdkLogger.error("build", COMMAND_FAILURE)           &= nbsp;           &nbs= p; Threshold =3D self.GetFreeSizeThreshold()
 &nbs= p;            &= nbsp;         if Threshold:diff --git a/BaseTools/Source/Python/build/buildoptions.py b/Ba= seTools/Source/Python/build/buildoptions.py
index 39d92cff209= d..6886ba7f8eb6 100644
--- a/BaseTools/Source/Python/build/bu= ildoptions.py
+++ b/BaseTools/Source/Python/build/buildoption= s.py
@@ -99,7 +99,8 @@ class MyOptionParser():
=         Parser.add_option("--hash"= , action=3D"store_true", dest=3D"UseHashCache", default=3DFalse, help=3D"En= able hash-based caching during build process.")
  =       Parser.add_option("--binary-destination= ", action=3D"store", type=3D"string", dest=3D"BinCacheDest", help=3D"Genera= te a cache of binary files in the specified directory.")
&nb= sp;       Parser.add_option("--binary-so= urce", action=3D"store", type=3D"string", dest=3D"BinCacheSource", help=3D"= Consume a cache of binary files from the specified directory.")
        Parser.add_option("--ge= nfds-multi-thread", action=3D"store_true", dest=3D"GenfdsMultiThread", defa= ult=3DTrue, help=3D"Enable GenFds multi thread to generate ffs file.")
        Parser.add_optio= n("--no-genfds-multi-thread", action=3D"store_true", dest=3D"NoGenfdsMultiT= hread", default=3DFalse, help=3D"Disable GenFds multi thread to generate ff= s file.")
+        Parser.= add_option("--gen-default-variable-bin", action=3D"store_true", dest=3D"Gen= DefaultVarBin", default=3DFalse, help=3D"Generate default variable binary f= ile.")
        Parse= r.add_option("--disable-include-path-check", action=3D"store_true", dest=3D= "DisableIncludePathCheck", default=3DFalse, help=3D"Disable the include pat= h check for outside of package.")
    &n= bsp;   self.BuildOption, self.BuildTarget =3D Parser.parse_a= rgs()
--
2.18.0.windows.1







--Apple-Mail=_35D03FF2-A3E3-46ED-9EAC-AE4A45DCFD22--