]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/GenFds/GenFds.py
BaseTools: Fix the bug that FixedPcdGetPtr failure for CArray Pcd
[mirror_edk2.git] / BaseTools / Source / Python / GenFds / GenFds.py
CommitLineData
f51461c8
LG
1## @file\r
2# generate flash image\r
3#\r
95816356 4# Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.<BR>\r
f51461c8
LG
5#\r
6# This program and the accompanying materials\r
7# are licensed and made available under the terms and conditions of the BSD License\r
8# which accompanies this distribution. The full text of the license may be found at\r
9# http://opensource.org/licenses/bsd-license.php\r
10#\r
11# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13#\r
14\r
15##\r
16# Import Modules\r
17#\r
18from optparse import OptionParser\r
19import sys\r
1be2ed90 20import Common.LongFilePathOs as os\r
f51461c8
LG
21import linecache\r
22import FdfParser\r
23import Common.BuildToolError as BuildToolError\r
24from GenFdsGlobalVariable import GenFdsGlobalVariable\r
25from Workspace.WorkspaceDatabase import WorkspaceDatabase\r
26from Workspace.BuildClassObject import PcdClassObject\r
27from Workspace.BuildClassObject import ModuleBuildClassObject\r
28import RuleComplexFile\r
29from EfiSection import EfiSection\r
30import StringIO\r
31import Common.TargetTxtClassObject as TargetTxtClassObject\r
32import Common.ToolDefClassObject as ToolDefClassObject\r
95816356 33from Common.DataType import *\r
f51461c8
LG
34import Common.GlobalData as GlobalData\r
35from Common import EdkLogger\r
36from Common.String import *\r
47fea6af 37from Common.Misc import DirCache, PathClass\r
f51461c8 38from Common.Misc import SaveFileOnChange\r
97fa0ee9 39from Common.Misc import ClearDuplicatedInf\r
e4ac870f 40from Common.Misc import GuidStructureStringToGuidString\r
6b17c11b 41from Common.Misc import CheckPcdDatum\r
f51461c8 42from Common.BuildVersion import gBUILD_VERSION\r
05cc51ad 43from Common.MultipleWorkspace import MultipleWorkspace as mws\r
f51461c8
LG
44\r
45## Version and Copyright\r
46versionNumber = "1.0" + ' ' + gBUILD_VERSION\r
47__version__ = "%prog Version " + versionNumber\r
95816356 48__copyright__ = "Copyright (c) 2007 - 2017, Intel Corporation All rights reserved."\r
f51461c8
LG
49\r
50## Tool entrance method\r
51#\r
52# This method mainly dispatch specific methods per the command line options.\r
53# If no error found, return zero value so the caller of this tool can know\r
54# if it's executed successfully or not.\r
55#\r
56# @retval 0 Tool was successful\r
57# @retval 1 Tool failed\r
58#\r
59def main():\r
60 global Options\r
61 Options = myOptionParser()\r
62\r
63 global Workspace\r
64 Workspace = ""\r
65 ArchList = None\r
66 ReturnCode = 0\r
67\r
68 EdkLogger.Initialize()\r
69 try:\r
70 if Options.verbose != None:\r
71 EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
72 GenFdsGlobalVariable.VerboseMode = True\r
73 \r
74 if Options.FixedAddress != None:\r
75 GenFdsGlobalVariable.FixedLoadAddress = True\r
76 \r
77 if Options.quiet != None:\r
78 EdkLogger.SetLevel(EdkLogger.QUIET)\r
79 if Options.debug != None:\r
80 EdkLogger.SetLevel(Options.debug + 1)\r
81 GenFdsGlobalVariable.DebugLevel = Options.debug\r
82 else:\r
83 EdkLogger.SetLevel(EdkLogger.INFO)\r
84\r
85 if (Options.Workspace == None):\r
86 EdkLogger.error("GenFds", OPTION_MISSING, "WORKSPACE not defined",\r
87 ExtraData="Please use '-w' switch to pass it or set the WORKSPACE environment variable.")\r
88 elif not os.path.exists(Options.Workspace):\r
89 EdkLogger.error("GenFds", PARAMETER_INVALID, "WORKSPACE is invalid",\r
90 ExtraData="Please use '-w' switch to pass it or set the WORKSPACE environment variable.")\r
91 else:\r
92 Workspace = os.path.normcase(Options.Workspace)\r
93 GenFdsGlobalVariable.WorkSpaceDir = Workspace\r
94 if 'EDK_SOURCE' in os.environ.keys():\r
95 GenFdsGlobalVariable.EdkSourceDir = os.path.normcase(os.environ['EDK_SOURCE'])\r
96 if (Options.debug):\r
47fea6af 97 GenFdsGlobalVariable.VerboseLogger("Using Workspace:" + Workspace)\r
f51461c8 98 os.chdir(GenFdsGlobalVariable.WorkSpaceDir)\r
05cc51ad
LY
99 \r
100 # set multiple workspace\r
101 PackagesPath = os.getenv("PACKAGES_PATH")\r
102 mws.setWs(GenFdsGlobalVariable.WorkSpaceDir, PackagesPath)\r
f51461c8
LG
103\r
104 if (Options.filename):\r
105 FdfFilename = Options.filename\r
106 FdfFilename = GenFdsGlobalVariable.ReplaceWorkspaceMacro(FdfFilename)\r
107\r
108 if FdfFilename[0:2] == '..':\r
109 FdfFilename = os.path.realpath(FdfFilename)\r
47fea6af 110 if not os.path.isabs(FdfFilename):\r
05cc51ad 111 FdfFilename = mws.join(GenFdsGlobalVariable.WorkSpaceDir, FdfFilename)\r
f51461c8
LG
112 if not os.path.exists(FdfFilename):\r
113 EdkLogger.error("GenFds", FILE_NOT_FOUND, ExtraData=FdfFilename)\r
f51461c8
LG
114\r
115 GenFdsGlobalVariable.FdfFile = FdfFilename\r
116 GenFdsGlobalVariable.FdfFileTimeStamp = os.path.getmtime(FdfFilename)\r
117 else:\r
118 EdkLogger.error("GenFds", OPTION_MISSING, "Missing FDF filename")\r
119\r
120 if (Options.BuildTarget):\r
121 GenFdsGlobalVariable.TargetName = Options.BuildTarget\r
f51461c8
LG
122\r
123 if (Options.ToolChain):\r
124 GenFdsGlobalVariable.ToolChainTag = Options.ToolChain\r
f51461c8
LG
125\r
126 if (Options.activePlatform):\r
127 ActivePlatform = Options.activePlatform\r
128 ActivePlatform = GenFdsGlobalVariable.ReplaceWorkspaceMacro(ActivePlatform)\r
129\r
130 if ActivePlatform[0:2] == '..':\r
131 ActivePlatform = os.path.realpath(ActivePlatform)\r
132\r
133 if not os.path.isabs (ActivePlatform):\r
05cc51ad 134 ActivePlatform = mws.join(GenFdsGlobalVariable.WorkSpaceDir, ActivePlatform)\r
f51461c8
LG
135\r
136 if not os.path.exists(ActivePlatform) :\r
137 EdkLogger.error("GenFds", FILE_NOT_FOUND, "ActivePlatform doesn't exist!")\r
f51461c8
LG
138 else:\r
139 EdkLogger.error("GenFds", OPTION_MISSING, "Missing active platform")\r
140\r
e642ceb8 141 GenFdsGlobalVariable.ActivePlatform = PathClass(NormPath(ActivePlatform))\r
f51461c8 142\r
97fa0ee9
YL
143 if (Options.ConfDirectory):\r
144 # Get alternate Conf location, if it is absolute, then just use the absolute directory name\r
145 ConfDirectoryPath = os.path.normpath(Options.ConfDirectory)\r
146 if ConfDirectoryPath.startswith('"'):\r
147 ConfDirectoryPath = ConfDirectoryPath[1:]\r
148 if ConfDirectoryPath.endswith('"'):\r
149 ConfDirectoryPath = ConfDirectoryPath[:-1]\r
150 if not os.path.isabs(ConfDirectoryPath):\r
151 # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE\r
152 # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf\r
153 ConfDirectoryPath = os.path.join(GenFdsGlobalVariable.WorkSpaceDir, ConfDirectoryPath)\r
154 else:\r
00bcb5c2
YZ
155 if "CONF_PATH" in os.environ.keys():\r
156 ConfDirectoryPath = os.path.normcase(os.environ["CONF_PATH"])\r
157 else:\r
158 # Get standard WORKSPACE/Conf, use the absolute path to the WORKSPACE/Conf\r
159 ConfDirectoryPath = mws.join(GenFdsGlobalVariable.WorkSpaceDir, 'Conf')\r
97fa0ee9
YL
160 GenFdsGlobalVariable.ConfDir = ConfDirectoryPath\r
161 BuildConfigurationFile = os.path.normpath(os.path.join(ConfDirectoryPath, "target.txt"))\r
f51461c8 162 if os.path.isfile(BuildConfigurationFile) == True:\r
e4979bee
YZ
163 TargetTxt = TargetTxtClassObject.TargetTxtClassObject()\r
164 TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
165 # if no build target given in command line, get it from target.txt\r
166 if not GenFdsGlobalVariable.TargetName:\r
167 BuildTargetList = TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
168 if len(BuildTargetList) != 1:\r
169 EdkLogger.error("GenFds", OPTION_VALUE_INVALID, ExtraData="Only allows one instance for Target.")\r
170 GenFdsGlobalVariable.TargetName = BuildTargetList[0]\r
171\r
172 # if no tool chain given in command line, get it from target.txt\r
173 if not GenFdsGlobalVariable.ToolChainTag:\r
174 ToolChainList = TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
175 if ToolChainList == None or len(ToolChainList) == 0:\r
176 EdkLogger.error("GenFds", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.")\r
177 if len(ToolChainList) != 1:\r
178 EdkLogger.error("GenFds", OPTION_VALUE_INVALID, ExtraData="Only allows one instance for ToolChain.")\r
179 GenFdsGlobalVariable.ToolChainTag = ToolChainList[0]\r
f51461c8
LG
180 else:\r
181 EdkLogger.error("GenFds", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
182\r
97fa0ee9
YL
183 #Set global flag for build mode\r
184 GlobalData.gIgnoreSource = Options.IgnoreSources\r
185\r
f51461c8
LG
186 if Options.Macros:\r
187 for Pair in Options.Macros:\r
97fa0ee9
YL
188 if Pair.startswith('"'):\r
189 Pair = Pair[1:]\r
190 if Pair.endswith('"'):\r
191 Pair = Pair[:-1]\r
f51461c8
LG
192 List = Pair.split('=')\r
193 if len(List) == 2:\r
e4979bee
YZ
194 if not List[1].strip():\r
195 EdkLogger.error("GenFds", OPTION_VALUE_INVALID, ExtraData="No Value given for Macro %s" %List[0])\r
f51461c8
LG
196 if List[0].strip() == "EFI_SOURCE":\r
197 GlobalData.gEfiSource = List[1].strip()\r
198 GlobalData.gGlobalDefines["EFI_SOURCE"] = GlobalData.gEfiSource\r
199 continue\r
200 elif List[0].strip() == "EDK_SOURCE":\r
201 GlobalData.gEdkSource = List[1].strip()\r
202 GlobalData.gGlobalDefines["EDK_SOURCE"] = GlobalData.gEdkSource\r
203 continue\r
204 elif List[0].strip() in ["WORKSPACE", "TARGET", "TOOLCHAIN"]:\r
205 GlobalData.gGlobalDefines[List[0].strip()] = List[1].strip()\r
206 else:\r
207 GlobalData.gCommandLineDefines[List[0].strip()] = List[1].strip()\r
208 else:\r
209 GlobalData.gCommandLineDefines[List[0].strip()] = "TRUE"\r
210 os.environ["WORKSPACE"] = Workspace\r
211\r
e4979bee
YZ
212 # Use the -t and -b option as gGlobalDefines's TOOLCHAIN and TARGET if they are not defined\r
213 if "TARGET" not in GlobalData.gGlobalDefines.keys():\r
214 GlobalData.gGlobalDefines["TARGET"] = GenFdsGlobalVariable.TargetName\r
215 if "TOOLCHAIN" not in GlobalData.gGlobalDefines.keys():\r
216 GlobalData.gGlobalDefines["TOOLCHAIN"] = GenFdsGlobalVariable.ToolChainTag\r
217 if "TOOL_CHAIN_TAG" not in GlobalData.gGlobalDefines.keys():\r
218 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = GenFdsGlobalVariable.ToolChainTag\r
219\r
f51461c8 220 """call Workspace build create database"""\r
97fa0ee9
YL
221 GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath))\r
222 BuildWorkSpace = WorkspaceDatabase(GlobalData.gDatabasePath)\r
f51461c8
LG
223 BuildWorkSpace.InitDatabase()\r
224 \r
225 #\r
226 # Get files real name in workspace dir\r
227 #\r
228 GlobalData.gAllFiles = DirCache(Workspace)\r
229 GlobalData.gWorkspace = Workspace\r
230\r
231 if (Options.archList) :\r
232 ArchList = Options.archList.split(',')\r
233 else:\r
234# EdkLogger.error("GenFds", OPTION_MISSING, "Missing build ARCH")\r
235 ArchList = BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, 'COMMON', Options.BuildTarget, Options.ToolChain].SupArchList\r
236\r
237 TargetArchList = set(BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, 'COMMON', Options.BuildTarget, Options.ToolChain].SupArchList) & set(ArchList)\r
238 if len(TargetArchList) == 0:\r
239 EdkLogger.error("GenFds", GENFDS_ERROR, "Target ARCH %s not in platform supported ARCH %s" % (str(ArchList), str(BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, 'COMMON'].SupArchList)))\r
240 \r
241 for Arch in ArchList:\r
242 GenFdsGlobalVariable.OutputDirFromDscDict[Arch] = NormPath(BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, Options.BuildTarget, Options.ToolChain].OutputDirectory)\r
243 GenFdsGlobalVariable.PlatformName = BuildWorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, Options.BuildTarget, Options.ToolChain].PlatformName\r
244\r
245 if (Options.outputDir):\r
246 OutputDirFromCommandLine = GenFdsGlobalVariable.ReplaceWorkspaceMacro(Options.outputDir)\r
247 if not os.path.isabs (OutputDirFromCommandLine):\r
248 OutputDirFromCommandLine = os.path.join(GenFdsGlobalVariable.WorkSpaceDir, OutputDirFromCommandLine)\r
249 for Arch in ArchList:\r
250 GenFdsGlobalVariable.OutputDirDict[Arch] = OutputDirFromCommandLine\r
251 else:\r
252 for Arch in ArchList:\r
253 GenFdsGlobalVariable.OutputDirDict[Arch] = os.path.join(GenFdsGlobalVariable.OutputDirFromDscDict[Arch], GenFdsGlobalVariable.TargetName + '_' + GenFdsGlobalVariable.ToolChainTag)\r
254\r
255 for Key in GenFdsGlobalVariable.OutputDirDict:\r
256 OutputDir = GenFdsGlobalVariable.OutputDirDict[Key]\r
257 if OutputDir[0:2] == '..':\r
258 OutputDir = os.path.realpath(OutputDir)\r
259\r
260 if OutputDir[1] != ':':\r
261 OutputDir = os.path.join (GenFdsGlobalVariable.WorkSpaceDir, OutputDir)\r
262\r
263 if not os.path.exists(OutputDir):\r
264 EdkLogger.error("GenFds", FILE_NOT_FOUND, ExtraData=OutputDir)\r
265 GenFdsGlobalVariable.OutputDirDict[Key] = OutputDir\r
266\r
267 """ Parse Fdf file, has to place after build Workspace as FDF may contain macros from DSC file """\r
268 FdfParserObj = FdfParser.FdfParser(FdfFilename)\r
269 FdfParserObj.ParseFile()\r
270\r
271 if FdfParserObj.CycleReferenceCheck():\r
272 EdkLogger.error("GenFds", FORMAT_NOT_SUPPORTED, "Cycle Reference Detected in FDF file")\r
273\r
274 if (Options.uiFdName) :\r
275 if Options.uiFdName.upper() in FdfParserObj.Profile.FdDict.keys():\r
276 GenFds.OnlyGenerateThisFd = Options.uiFdName\r
277 else:\r
278 EdkLogger.error("GenFds", OPTION_VALUE_INVALID,\r
279 "No such an FD in FDF file: %s" % Options.uiFdName)\r
280\r
281 if (Options.uiFvName) :\r
282 if Options.uiFvName.upper() in FdfParserObj.Profile.FvDict.keys():\r
283 GenFds.OnlyGenerateThisFv = Options.uiFvName\r
284 else:\r
285 EdkLogger.error("GenFds", OPTION_VALUE_INVALID,\r
286 "No such an FV in FDF file: %s" % Options.uiFvName)\r
287\r
288 if (Options.uiCapName) :\r
289 if Options.uiCapName.upper() in FdfParserObj.Profile.CapsuleDict.keys():\r
290 GenFds.OnlyGenerateThisCap = Options.uiCapName\r
291 else:\r
292 EdkLogger.error("GenFds", OPTION_VALUE_INVALID,\r
293 "No such a Capsule in FDF file: %s" % Options.uiCapName)\r
294\r
6b17c11b
YZ
295 GenFdsGlobalVariable.WorkSpace = BuildWorkSpace\r
296 if ArchList != None:\r
297 GenFdsGlobalVariable.ArchList = ArchList\r
298\r
299 if Options.OptionPcd:\r
300 GlobalData.BuildOptionPcd = Options.OptionPcd\r
301 CheckBuildOptionPcd()\r
302\r
f51461c8
LG
303 """Modify images from build output if the feature of loading driver at fixed address is on."""\r
304 if GenFdsGlobalVariable.FixedLoadAddress:\r
305 GenFds.PreprocessImage(BuildWorkSpace, GenFdsGlobalVariable.ActivePlatform)\r
135ae8c8
YZ
306\r
307 # Record the FV Region info that may specific in the FD\r
308 if FdfParserObj.Profile.FvDict and FdfParserObj.Profile.FdDict:\r
309 for Fv in FdfParserObj.Profile.FvDict:\r
310 FvObj = FdfParserObj.Profile.FvDict[Fv]\r
311 for Fd in FdfParserObj.Profile.FdDict:\r
312 FdObj = FdfParserObj.Profile.FdDict[Fd]\r
313 for RegionObj in FdObj.RegionList:\r
314 if RegionObj.RegionType != 'FV':\r
315 continue\r
316 for RegionData in RegionObj.RegionDataList:\r
317 if FvObj.UiFvName.upper() == RegionData.upper():\r
318 if FvObj.FvRegionInFD:\r
319 if FvObj.FvRegionInFD != RegionObj.Size:\r
320 EdkLogger.error("GenFds", FORMAT_INVALID, "The FV %s's region is specified in multiple FD with different value." %FvObj.UiFvName)\r
321 else:\r
322 FvObj.FvRegionInFD = RegionObj.Size\r
323 RegionObj.BlockInfoOfRegion(FdObj.BlockSizeList, FvObj)\r
324\r
f51461c8
LG
325 """Call GenFds"""\r
326 GenFds.GenFd('', FdfParserObj, BuildWorkSpace, ArchList)\r
327\r
328 """Generate GUID cross reference file"""\r
329 GenFds.GenerateGuidXRefFile(BuildWorkSpace, ArchList)\r
330\r
331 """Display FV space info."""\r
332 GenFds.DisplayFvSpaceInfo(FdfParserObj)\r
333\r
334 except FdfParser.Warning, X:\r
47fea6af 335 EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)\r
f51461c8
LG
336 ReturnCode = FORMAT_INVALID\r
337 except FatalError, X:\r
338 if Options.debug != None:\r
339 import traceback\r
340 EdkLogger.quiet(traceback.format_exc())\r
341 ReturnCode = X.args[0]\r
342 except:\r
343 import traceback\r
344 EdkLogger.error(\r
345 "\nPython",\r
346 CODE_ERROR,\r
347 "Tools code failure",\r
3a0f8bde 348 ExtraData="Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!\n",\r
f51461c8
LG
349 RaiseError=False\r
350 )\r
351 EdkLogger.quiet(traceback.format_exc())\r
352 ReturnCode = CODE_ERROR\r
97fa0ee9
YL
353 finally:\r
354 ClearDuplicatedInf()\r
f51461c8
LG
355 return ReturnCode\r
356\r
357gParamCheck = []\r
358def SingleCheckCallback(option, opt_str, value, parser):\r
359 if option not in gParamCheck:\r
360 setattr(parser.values, option.dest, value)\r
361 gParamCheck.append(option)\r
362 else:\r
363 parser.error("Option %s only allows one instance in command line!" % option)\r
6b17c11b
YZ
364\r
365def CheckBuildOptionPcd():\r
366 for Arch in GenFdsGlobalVariable.ArchList:\r
367 PkgList = GenFdsGlobalVariable.WorkSpace.GetPackageList(GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag)\r
368 for i, pcd in enumerate(GlobalData.BuildOptionPcd):\r
369 if type(pcd) is tuple:\r
370 continue\r
371 (pcdname, pcdvalue) = pcd.split('=')\r
372 if not pcdvalue:\r
373 EdkLogger.error('GenFds', OPTION_MISSING, "No Value specified for the PCD %s." % (pcdname))\r
374 if '.' in pcdname:\r
375 (TokenSpaceGuidCName, TokenCName) = pcdname.split('.')\r
376 HasTokenSpace = True\r
377 else:\r
378 TokenCName = pcdname\r
379 TokenSpaceGuidCName = ''\r
380 HasTokenSpace = False\r
381 TokenSpaceGuidCNameList = []\r
382 FoundFlag = False\r
383 PcdDatumType = ''\r
384 NewValue = ''\r
385 for package in PkgList:\r
386 for key in package.Pcds:\r
387 PcdItem = package.Pcds[key]\r
388 if HasTokenSpace:\r
389 if (PcdItem.TokenCName, PcdItem.TokenSpaceGuidCName) == (TokenCName, TokenSpaceGuidCName):\r
390 PcdDatumType = PcdItem.DatumType\r
391 NewValue = BuildOptionPcdValueFormat(TokenSpaceGuidCName, TokenCName, PcdDatumType, pcdvalue)\r
392 FoundFlag = True\r
393 else:\r
394 if PcdItem.TokenCName == TokenCName:\r
395 if not PcdItem.TokenSpaceGuidCName in TokenSpaceGuidCNameList:\r
396 if len (TokenSpaceGuidCNameList) < 1:\r
397 TokenSpaceGuidCNameList.append(PcdItem.TokenSpaceGuidCName)\r
398 PcdDatumType = PcdItem.DatumType\r
399 TokenSpaceGuidCName = PcdItem.TokenSpaceGuidCName\r
400 NewValue = BuildOptionPcdValueFormat(TokenSpaceGuidCName, TokenCName, PcdDatumType, pcdvalue)\r
401 FoundFlag = True\r
402 else:\r
403 EdkLogger.error(\r
404 'GenFds',\r
405 PCD_VALIDATION_INFO_ERROR,\r
406 "The Pcd %s is found under multiple different TokenSpaceGuid: %s and %s." % (TokenCName, PcdItem.TokenSpaceGuidCName, TokenSpaceGuidCNameList[0])\r
407 )\r
408\r
409 GlobalData.BuildOptionPcd[i] = (TokenSpaceGuidCName, TokenCName, NewValue)\r
410\r
411def BuildOptionPcdValueFormat(TokenSpaceGuidCName, TokenCName, PcdDatumType, Value):\r
412 if PcdDatumType == 'VOID*':\r
413 if Value.startswith('L'):\r
414 if not Value[1]:\r
415 EdkLogger.error('GenFds', OPTION_VALUE_INVALID, 'For Void* type PCD, when specify the Value in the command line, please use the following format: "string", L"string", B"{...}"')\r
416 Value = Value[0] + '"' + Value[1:] + '"'\r
417 elif Value.startswith('B'):\r
418 if not Value[1]:\r
419 EdkLogger.error('GenFds', OPTION_VALUE_INVALID, 'For Void* type PCD, when specify the Value in the command line, please use the following format: "string", L"string", B"{...}"')\r
420 Value = Value[1:]\r
421 else:\r
422 if not Value[0]:\r
423 EdkLogger.error('GenFds', OPTION_VALUE_INVALID, 'For Void* type PCD, when specify the Value in the command line, please use the following format: "string", L"string", B"{...}"')\r
424 Value = '"' + Value + '"'\r
425\r
426 IsValid, Cause = CheckPcdDatum(PcdDatumType, Value)\r
427 if not IsValid:\r
428 EdkLogger.error('build', FORMAT_INVALID, Cause, ExtraData="%s.%s" % (TokenSpaceGuidCName, TokenCName))\r
429 if PcdDatumType == 'BOOLEAN':\r
430 Value = Value.upper()\r
431 if Value == 'TRUE' or Value == '1':\r
432 Value = '1'\r
433 elif Value == 'FALSE' or Value == '0':\r
434 Value = '0'\r
435 return Value\r
436\r
91ae2988
YZ
437## FindExtendTool()\r
438#\r
439# Find location of tools to process data\r
440#\r
441# @param KeyStringList Filter for inputs of section generation\r
442# @param CurrentArchList Arch list\r
443# @param NameGuid The Guid name\r
444#\r
445def FindExtendTool(KeyStringList, CurrentArchList, NameGuid):\r
95816356 446 ToolDb = ToolDefClassObject.ToolDefDict(GenFdsGlobalVariable.ConfDir).ToolsDefTxtDatabase\r
91ae2988
YZ
447 # if user not specify filter, try to deduce it from global data.\r
448 if KeyStringList == None or KeyStringList == []:\r
449 Target = GenFdsGlobalVariable.TargetName\r
450 ToolChain = GenFdsGlobalVariable.ToolChainTag\r
91ae2988
YZ
451 if ToolChain not in ToolDb['TOOL_CHAIN_TAG']:\r
452 EdkLogger.error("GenFds", GENFDS_ERROR, "Can not find external tool because tool tag %s is not defined in tools_def.txt!" % ToolChain)\r
453 KeyStringList = [Target + '_' + ToolChain + '_' + CurrentArchList[0]]\r
454 for Arch in CurrentArchList:\r
455 if Target + '_' + ToolChain + '_' + Arch not in KeyStringList:\r
456 KeyStringList.append(Target + '_' + ToolChain + '_' + Arch)\r
457\r
458 if GenFdsGlobalVariable.GuidToolDefinition:\r
459 if NameGuid in GenFdsGlobalVariable.GuidToolDefinition.keys():\r
460 return GenFdsGlobalVariable.GuidToolDefinition[NameGuid]\r
461\r
462 ToolDefinition = ToolDefClassObject.ToolDefDict(GenFdsGlobalVariable.ConfDir).ToolsDefTxtDictionary\r
463 ToolPathTmp = None\r
464 ToolOption = None\r
95816356
YZ
465 ToolPathKey = None\r
466 ToolOptionKey = None\r
467 KeyList = None\r
91ae2988
YZ
468 for ToolDef in ToolDefinition.items():\r
469 if NameGuid == ToolDef[1]:\r
470 KeyList = ToolDef[0].split('_')\r
471 Key = KeyList[0] + \\r
472 '_' + \\r
473 KeyList[1] + \\r
474 '_' + \\r
475 KeyList[2]\r
476 if Key in KeyStringList and KeyList[4] == 'GUID':\r
95816356
YZ
477 ToolPathKey = Key + '_' + KeyList[3] + '_PATH'\r
478 ToolOptionKey = Key + '_' + KeyList[3] + '_FLAGS'\r
479 ToolPath = ToolDefinition.get(ToolPathKey)\r
480 ToolOption = ToolDefinition.get(ToolOptionKey)\r
91ae2988
YZ
481 if ToolPathTmp == None:\r
482 ToolPathTmp = ToolPath\r
483 else:\r
484 if ToolPathTmp != ToolPath:\r
485 EdkLogger.error("GenFds", GENFDS_ERROR, "Don't know which tool to use, %s or %s ?" % (ToolPathTmp, ToolPath))\r
486\r
95816356
YZ
487 BuildOption = {}\r
488 for Arch in CurrentArchList:\r
489 Platform = GenFdsGlobalVariable.WorkSpace.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]\r
490 # key is (ToolChainFamily, ToolChain, CodeBase)\r
491 for item in Platform.BuildOptions:\r
492 if '_PATH' in item[1] or '_FLAGS' in item[1] or '_GUID' in item[1]:\r
493 if not item[0] or (item[0] and GenFdsGlobalVariable.ToolChainFamily== item[0]):\r
494 if item[1] not in BuildOption:\r
495 BuildOption[item[1]] = Platform.BuildOptions[item]\r
496 if BuildOption:\r
497 ToolList = [TAB_TOD_DEFINES_TARGET, TAB_TOD_DEFINES_TOOL_CHAIN_TAG, TAB_TOD_DEFINES_TARGET_ARCH]\r
498 for Index in range(2, -1, -1):\r
499 for Key in dict(BuildOption):\r
500 List = Key.split('_')\r
501 if List[Index] == '*':\r
502 for String in ToolDb[ToolList[Index]]:\r
503 if String in [Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]:\r
504 List[Index] = String\r
505 NewKey = '%s_%s_%s_%s_%s' % tuple(List)\r
506 if NewKey not in BuildOption:\r
507 BuildOption[NewKey] = BuildOption[Key]\r
508 continue\r
509 del BuildOption[Key]\r
510 elif List[Index] not in ToolDb[ToolList[Index]]:\r
511 del BuildOption[Key]\r
512 if BuildOption:\r
513 if not KeyList:\r
514 for Op in BuildOption:\r
515 if NameGuid == BuildOption[Op]:\r
516 KeyList = Op.split('_')\r
517 Key = KeyList[0] + '_' + KeyList[1] +'_' + KeyList[2]\r
518 if Key in KeyStringList and KeyList[4] == 'GUID':\r
519 ToolPathKey = Key + '_' + KeyList[3] + '_PATH'\r
520 ToolOptionKey = Key + '_' + KeyList[3] + '_FLAGS'\r
521 if ToolPathKey in BuildOption.keys():\r
522 ToolPathTmp = BuildOption.get(ToolPathKey)\r
523 if ToolOptionKey in BuildOption.keys():\r
524 ToolOption = BuildOption.get(ToolOptionKey)\r
525\r
91ae2988
YZ
526 GenFdsGlobalVariable.GuidToolDefinition[NameGuid] = (ToolPathTmp, ToolOption)\r
527 return ToolPathTmp, ToolOption\r
528\r
f51461c8
LG
529## Parse command line options\r
530#\r
531# Using standard Python module optparse to parse command line option of this tool.\r
532#\r
533# @retval Opt A optparse.Values object containing the parsed options\r
534# @retval Args Target of build command\r
535#\r
536def myOptionParser():\r
537 usage = "%prog [options] -f input_file -a arch_list -b build_target -p active_platform -t tool_chain_tag -D \"MacroName [= MacroValue]\""\r
47fea6af 538 Parser = OptionParser(usage=usage, description=__copyright__, version="%prog " + str(versionNumber))\r
f51461c8
LG
539 Parser.add_option("-f", "--file", dest="filename", type="string", help="Name of FDF file to convert", action="callback", callback=SingleCheckCallback)\r
540 Parser.add_option("-a", "--arch", dest="archList", help="comma separated list containing one or more of: IA32, X64, IPF, ARM, AARCH64 or EBC which should be built, overrides target.txt?s TARGET_ARCH")\r
541 Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
542 Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed.")\r
543 Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
544 Parser.add_option("-p", "--platform", type="string", dest="activePlatform", help="Set the ACTIVE_PLATFORM, overrides target.txt ACTIVE_PLATFORM setting.",\r
545 action="callback", callback=SingleCheckCallback)\r
546 Parser.add_option("-w", "--workspace", type="string", dest="Workspace", default=os.environ.get('WORKSPACE'), help="Set the WORKSPACE",\r
547 action="callback", callback=SingleCheckCallback)\r
548 Parser.add_option("-o", "--outputDir", type="string", dest="outputDir", help="Name of Build Output directory",\r
549 action="callback", callback=SingleCheckCallback)\r
550 Parser.add_option("-r", "--rom_image", dest="uiFdName", help="Build the image using the [FD] section named by FdUiName.")\r
551 Parser.add_option("-i", "--FvImage", dest="uiFvName", help="Build the FV image using the [FV] section named by UiFvName")\r
552 Parser.add_option("-C", "--CapsuleImage", dest="uiCapName", help="Build the Capsule image using the [Capsule] section named by UiCapName")\r
553 Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Set the build TARGET, overrides target.txt TARGET setting.",\r
554 action="callback", callback=SingleCheckCallback)\r
555 Parser.add_option("-t", "--tagname", type="string", dest="ToolChain", help="Using the tools: TOOL_CHAIN_TAG name to build the platform.",\r
556 action="callback", callback=SingleCheckCallback)\r
557 Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
558 Parser.add_option("-s", "--specifyaddress", dest="FixedAddress", action="store_true", type=None, help="Specify driver load address.")\r
97fa0ee9
YL
559 Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")\r
560 Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")\r
6b17c11b 561 Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")\r
97fa0ee9 562\r
f51461c8
LG
563 (Options, args) = Parser.parse_args()\r
564 return Options\r
565\r
566## The class implementing the EDK2 flash image generation process\r
567#\r
568# This process includes:\r
569# 1. Collect workspace information, includes platform and module information\r
570# 2. Call methods of Fd class to generate FD\r
571# 3. Call methods of Fv class to generate FV that not belong to FD\r
572#\r
573class GenFds :\r
574 FdfParsef = None\r
575 # FvName, FdName, CapName in FDF, Image file name\r
576 ImageBinDict = {}\r
577 OnlyGenerateThisFd = None\r
578 OnlyGenerateThisFv = None\r
579 OnlyGenerateThisCap = None\r
580\r
581 ## GenFd()\r
582 #\r
583 # @param OutputDir Output directory\r
584 # @param FdfParser FDF contents parser\r
585 # @param Workspace The directory of workspace\r
586 # @param ArchList The Arch list of platform\r
587 #\r
588 def GenFd (OutputDir, FdfParser, WorkSpace, ArchList):\r
589 GenFdsGlobalVariable.SetDir ('', FdfParser, WorkSpace, ArchList)\r
590\r
591 GenFdsGlobalVariable.VerboseLogger(" Generate all Fd images and their required FV and Capsule images!")\r
592 if GenFds.OnlyGenerateThisCap != None and GenFds.OnlyGenerateThisCap.upper() in GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict.keys():\r
593 CapsuleObj = GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict.get(GenFds.OnlyGenerateThisCap.upper())\r
594 if CapsuleObj != None:\r
595 CapsuleObj.GenCapsule()\r
596 return\r
597\r
598 if GenFds.OnlyGenerateThisFd != None and GenFds.OnlyGenerateThisFd.upper() in GenFdsGlobalVariable.FdfParser.Profile.FdDict.keys():\r
599 FdObj = GenFdsGlobalVariable.FdfParser.Profile.FdDict.get(GenFds.OnlyGenerateThisFd.upper())\r
600 if FdObj != None:\r
601 FdObj.GenFd()\r
602 return\r
603 elif GenFds.OnlyGenerateThisFd == None and GenFds.OnlyGenerateThisFv == None:\r
604 for FdName in GenFdsGlobalVariable.FdfParser.Profile.FdDict.keys():\r
605 FdObj = GenFdsGlobalVariable.FdfParser.Profile.FdDict[FdName]\r
606 FdObj.GenFd()\r
607\r
608 GenFdsGlobalVariable.VerboseLogger("\n Generate other FV images! ")\r
609 if GenFds.OnlyGenerateThisFv != None and GenFds.OnlyGenerateThisFv.upper() in GenFdsGlobalVariable.FdfParser.Profile.FvDict.keys():\r
610 FvObj = GenFdsGlobalVariable.FdfParser.Profile.FvDict.get(GenFds.OnlyGenerateThisFv.upper())\r
611 if FvObj != None:\r
612 Buffer = StringIO.StringIO()\r
613 FvObj.AddToBuffer(Buffer)\r
614 Buffer.close()\r
615 return\r
616 elif GenFds.OnlyGenerateThisFv == None:\r
617 for FvName in GenFdsGlobalVariable.FdfParser.Profile.FvDict.keys():\r
618 Buffer = StringIO.StringIO('')\r
619 FvObj = GenFdsGlobalVariable.FdfParser.Profile.FvDict[FvName]\r
620 FvObj.AddToBuffer(Buffer)\r
621 Buffer.close()\r
622 \r
623 if GenFds.OnlyGenerateThisFv == None and GenFds.OnlyGenerateThisFd == None and GenFds.OnlyGenerateThisCap == None:\r
624 if GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict != {}:\r
625 GenFdsGlobalVariable.VerboseLogger("\n Generate other Capsule images!")\r
626 for CapsuleName in GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict.keys():\r
627 CapsuleObj = GenFdsGlobalVariable.FdfParser.Profile.CapsuleDict[CapsuleName]\r
628 CapsuleObj.GenCapsule()\r
629\r
630 if GenFdsGlobalVariable.FdfParser.Profile.OptRomDict != {}:\r
631 GenFdsGlobalVariable.VerboseLogger("\n Generate all Option ROM!")\r
632 for DriverName in GenFdsGlobalVariable.FdfParser.Profile.OptRomDict.keys():\r
633 OptRomObj = GenFdsGlobalVariable.FdfParser.Profile.OptRomDict[DriverName]\r
634 OptRomObj.AddToBuffer(None)\r
635\r
636 ## GetFvBlockSize()\r
637 #\r
638 # @param FvObj Whose block size to get\r
639 # @retval int Block size value\r
640 #\r
641 def GetFvBlockSize(FvObj):\r
642 DefaultBlockSize = 0x1\r
643 FdObj = None\r
644 if GenFds.OnlyGenerateThisFd != None and GenFds.OnlyGenerateThisFd.upper() in GenFdsGlobalVariable.FdfParser.Profile.FdDict.keys():\r
645 FdObj = GenFdsGlobalVariable.FdfParser.Profile.FdDict[GenFds.OnlyGenerateThisFd.upper()]\r
646 if FdObj == None:\r
647 for ElementFd in GenFdsGlobalVariable.FdfParser.Profile.FdDict.values():\r
648 for ElementRegion in ElementFd.RegionList:\r
649 if ElementRegion.RegionType == 'FV':\r
650 for ElementRegionData in ElementRegion.RegionDataList:\r
651 if ElementRegionData != None and ElementRegionData.upper() == FvObj.UiFvName:\r
652 if FvObj.BlockSizeList != []:\r
653 return FvObj.BlockSizeList[0][0]\r
654 else:\r
655 return ElementRegion.BlockSizeOfRegion(ElementFd.BlockSizeList)\r
656 if FvObj.BlockSizeList != []:\r
657 return FvObj.BlockSizeList[0][0]\r
658 return DefaultBlockSize\r
659 else:\r
660 for ElementRegion in FdObj.RegionList:\r
661 if ElementRegion.RegionType == 'FV':\r
662 for ElementRegionData in ElementRegion.RegionDataList:\r
663 if ElementRegionData != None and ElementRegionData.upper() == FvObj.UiFvName:\r
664 if FvObj.BlockSizeList != []:\r
665 return FvObj.BlockSizeList[0][0]\r
666 else:\r
667 return ElementRegion.BlockSizeOfRegion(ElementFd.BlockSizeList)\r
668 return DefaultBlockSize\r
669\r
670 ## DisplayFvSpaceInfo()\r
671 #\r
672 # @param FvObj Whose block size to get\r
673 # @retval None\r
674 #\r
675 def DisplayFvSpaceInfo(FdfParser):\r
676 \r
677 FvSpaceInfoList = []\r
678 MaxFvNameLength = 0\r
679 for FvName in FdfParser.Profile.FvDict:\r
680 if len(FvName) > MaxFvNameLength:\r
681 MaxFvNameLength = len(FvName)\r
682 FvSpaceInfoFileName = os.path.join(GenFdsGlobalVariable.FvDir, FvName.upper() + '.Fv.map')\r
683 if os.path.exists(FvSpaceInfoFileName):\r
684 FileLinesList = linecache.getlines(FvSpaceInfoFileName)\r
685 TotalFound = False\r
686 Total = ''\r
687 UsedFound = False\r
688 Used = ''\r
689 FreeFound = False\r
690 Free = ''\r
691 for Line in FileLinesList:\r
692 NameValue = Line.split('=')\r
693 if len(NameValue) == 2:\r
694 if NameValue[0].strip() == 'EFI_FV_TOTAL_SIZE':\r
695 TotalFound = True\r
696 Total = NameValue[1].strip()\r
697 if NameValue[0].strip() == 'EFI_FV_TAKEN_SIZE':\r
698 UsedFound = True\r
699 Used = NameValue[1].strip()\r
700 if NameValue[0].strip() == 'EFI_FV_SPACE_SIZE':\r
701 FreeFound = True\r
702 Free = NameValue[1].strip()\r
703 \r
704 if TotalFound and UsedFound and FreeFound:\r
705 FvSpaceInfoList.append((FvName, Total, Used, Free))\r
706 \r
707 GenFdsGlobalVariable.InfLogger('\nFV Space Information')\r
708 for FvSpaceInfo in FvSpaceInfoList:\r
709 Name = FvSpaceInfo[0]\r
710 TotalSizeValue = long(FvSpaceInfo[1], 0)\r
711 UsedSizeValue = long(FvSpaceInfo[2], 0)\r
712 FreeSizeValue = long(FvSpaceInfo[3], 0)\r
713 if UsedSizeValue == TotalSizeValue:\r
714 Percentage = '100'\r
715 else:\r
47fea6af
YZ
716 Percentage = str((UsedSizeValue + 0.0) / TotalSizeValue)[0:4].lstrip('0.')\r
717\r
f51461c8
LG
718 GenFdsGlobalVariable.InfLogger(Name + ' ' + '[' + Percentage + '%Full] ' + str(TotalSizeValue) + ' total, ' + str(UsedSizeValue) + ' used, ' + str(FreeSizeValue) + ' free')\r
719\r
720 ## PreprocessImage()\r
721 #\r
722 # @param BuildDb Database from build meta data files\r
723 # @param DscFile modules from dsc file will be preprocessed\r
724 # @retval None\r
725 #\r
726 def PreprocessImage(BuildDb, DscFile):\r
727 PcdDict = BuildDb.BuildObject[DscFile, 'COMMON', GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].Pcds\r
728 PcdValue = ''\r
729 for Key in PcdDict:\r
730 PcdObj = PcdDict[Key]\r
731 if PcdObj.TokenCName == 'PcdBsBaseAddress':\r
732 PcdValue = PcdObj.DefaultValue\r
733 break\r
734 \r
735 if PcdValue == '':\r
736 return\r
737 \r
738 Int64PcdValue = long(PcdValue, 0)\r
739 if Int64PcdValue == 0 or Int64PcdValue < -1: \r
740 return\r
741 \r
742 TopAddress = 0\r
743 if Int64PcdValue > 0:\r
744 TopAddress = Int64PcdValue\r
745 \r
746 ModuleDict = BuildDb.BuildObject[DscFile, 'COMMON', GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag].Modules\r
747 for Key in ModuleDict:\r
748 ModuleObj = BuildDb.BuildObject[Key, 'COMMON', GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]\r
749 print ModuleObj.BaseName + ' ' + ModuleObj.ModuleType\r
750\r
751 def GenerateGuidXRefFile(BuildDb, ArchList):\r
752 GuidXRefFileName = os.path.join(GenFdsGlobalVariable.FvDir, "Guid.xref")\r
753 GuidXRefFile = StringIO.StringIO('')\r
e4ac870f 754 GuidDict = {}\r
f51461c8
LG
755 for Arch in ArchList:\r
756 PlatformDataBase = BuildDb.BuildObject[GenFdsGlobalVariable.ActivePlatform, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]\r
757 for ModuleFile in PlatformDataBase.Modules:\r
758 Module = BuildDb.BuildObject[ModuleFile, Arch, GenFdsGlobalVariable.TargetName, GenFdsGlobalVariable.ToolChainTag]\r
759 GuidXRefFile.write("%s %s\n" % (Module.Guid, Module.BaseName))\r
e4ac870f
LG
760 for key, item in Module.Protocols.items():\r
761 GuidDict[key] = item\r
762 for key, item in Module.Guids.items():\r
763 GuidDict[key] = item\r
764 for key, item in Module.Ppis.items():\r
765 GuidDict[key] = item\r
766 # Append GUIDs, Protocols, and PPIs to the Xref file\r
767 GuidXRefFile.write("\n")\r
768 for key, item in GuidDict.items():\r
769 GuidXRefFile.write("%s %s\n" % (GuidStructureStringToGuidString(item).upper(), key))\r
770\r
f51461c8
LG
771 if GuidXRefFile.getvalue():\r
772 SaveFileOnChange(GuidXRefFileName, GuidXRefFile.getvalue(), False)\r
773 GenFdsGlobalVariable.InfLogger("\nGUID cross reference file can be found at %s" % GuidXRefFileName)\r
774 elif os.path.exists(GuidXRefFileName):\r
775 os.remove(GuidXRefFileName)\r
776 GuidXRefFile.close()\r
777\r
778 ##Define GenFd as static function\r
779 GenFd = staticmethod(GenFd)\r
780 GetFvBlockSize = staticmethod(GetFvBlockSize)\r
781 DisplayFvSpaceInfo = staticmethod(DisplayFvSpaceInfo)\r
782 PreprocessImage = staticmethod(PreprocessImage)\r
783 GenerateGuidXRefFile = staticmethod(GenerateGuidXRefFile)\r
784\r
785if __name__ == '__main__':\r
786 r = main()\r
787 ## 0-127 is a safe return range, and 1 is a standard default error\r
788 if r < 0 or r > 127: r = 1\r
789 sys.exit(r)\r
790\r