From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mx.groups.io; dkim=missing; spf=fail (domain: intel.com, ip: , mailfrom: bob.c.feng@intel.com) Received: from mga11.intel.com (mga11.intel.com []) by groups.io with SMTP; Tue, 30 Jul 2019 22:53:05 -0700 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga004.jf.intel.com ([10.7.209.38]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 30 Jul 2019 22:53:04 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.64,328,1559545200"; d="scan'208";a="323616829" Received: from shwdepsi1121.ccr.corp.intel.com ([10.239.158.47]) by orsmga004.jf.intel.com with ESMTP; 30 Jul 2019 22:53:03 -0700 From: "Bob Feng" To: devel@edk2.groups.io Cc: Bob Feng , Liming Gao Subject: [Patch 08/10 V5] BaseTools: Move BuildOption parser out of build.py Date: Wed, 31 Jul 2019 13:52:42 +0800 Message-Id: <20190731055244.19872-9-bob.c.feng@intel.com> X-Mailer: git-send-email 2.20.1.windows.1 In-Reply-To: <20190731055244.19872-1-bob.c.feng@intel.com> References: <20190731055244.19872-1-bob.c.feng@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=1875 Build tool supports user to specify the conf folder. To make the build options be evaluated at the beginning of launching build, extract the buildoption function from build.py to a new .py file. Signed-off-by: Bob Feng Cc: Liming Gao --- .../Python/Common/TargetTxtClassObject.py | 28 ++++- BaseTools/Source/Python/build/build.py | 108 +----------------- BaseTools/Source/Python/build/buildoptions.py | 92 +++++++++++++++ 3 files changed, 121 insertions(+), 107 deletions(-) create mode 100644 BaseTools/Source/Python/build/buildoptions.py diff --git a/BaseTools/Source/Python/Common/TargetTxtClassObject.py b/BaseTools/Source/Python/Common/TargetTxtClassObject.py index 79a5acc01074..16cc75ccb7c8 100644 --- a/BaseTools/Source/Python/Common/TargetTxtClassObject.py +++ b/BaseTools/Source/Python/Common/TargetTxtClassObject.py @@ -8,16 +8,19 @@ ## # Import Modules # from __future__ import print_function from __future__ import absolute_import +from buildoptions import BuildOption,BuildTarget +import Common.GlobalData as GlobalData import Common.LongFilePathOs as os from . import EdkLogger from . import DataType from .BuildToolError import * -from . import GlobalData + from Common.LongFilePathSupport import OpenLongFilePath as open +from Common.MultipleWorkspace import MultipleWorkspace as mws gDefaultTargetTxtFile = "target.txt" ## TargetTxtClassObject # @@ -139,16 +142,33 @@ class TargetTxtClassObject(object): # # @param ConfDir: Conf dir # # @retval Target An instance of TargetTxtClassObject() with loaded target.txt # -def TargetTxtDict(ConfDir): +def TargetTxtDict(): Target = TargetTxtClassObject() - Target.LoadTargetTxtFile(os.path.normpath(os.path.join(ConfDir, gDefaultTargetTxtFile))) + if BuildOption.ConfDirectory: + # Get alternate Conf location, if it is absolute, then just use the absolute directory name + ConfDirectoryPath = os.path.normpath(BuildOption.ConfDirectory) + + if not os.path.isabs(ConfDirectoryPath): + # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE + # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf + ConfDirectoryPath = mws.join(os.environ["WORKSPACE"], ConfDirectoryPath) + else: + if "CONF_PATH" in os.environ: + ConfDirectoryPath = os.path.normcase(os.path.normpath(os.environ["CONF_PATH"])) + else: + # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf + ConfDirectoryPath = mws.join(os.environ["WORKSPACE"], 'Conf') + GlobalData.gConfDirectory = ConfDirectoryPath + targettxt = os.path.normpath(os.path.join(ConfDirectoryPath, gDefaultTargetTxtFile)) + if os.path.exists(targettxt): + Target.LoadTargetTxtFile(targettxt) return Target -TargetTxt = TargetTxtDict(os.path.join(os.getenv("WORKSPACE"),"Conf")) +TargetTxt = TargetTxtDict() ## # # This acts like the main() function for the script, unless it is 'import'ed into another # script. diff --git a/BaseTools/Source/Python/build/build.py b/BaseTools/Source/Python/build/build.py index 323fc8f64e31..1c03f063668b 100644 --- a/BaseTools/Source/Python/build/build.py +++ b/BaseTools/Source/Python/build/build.py @@ -24,11 +24,11 @@ import traceback import multiprocessing from threading import Thread,Event,BoundedSemaphore import threading from subprocess import Popen,PIPE from collections import OrderedDict, defaultdict -from optparse import OptionParser +from buildoptions import BuildOption,BuildTarget from AutoGen.PlatformAutoGen import PlatformAutoGen from AutoGen.ModuleAutoGen import ModuleAutoGen from AutoGen.WorkspaceAutoGen import WorkspaceAutoGen from AutoGen.AutoGenWorker import AutoGenWorkerInProcess,AutoGenManager,\ LogAgent @@ -41,11 +41,11 @@ from Common.Misc import PathClass,SaveFileOnChange,RemoveDirectory from Common.StringUtils import NormPath from Common.MultipleWorkspace import MultipleWorkspace as mws from Common.BuildToolError import * from Common.DataType import * import Common.EdkLogger as EdkLogger -from Common.BuildVersion import gBUILD_VERSION + from Workspace.WorkspaceDatabase import BuildDB from BuildReport import BuildReport from GenPatchPcdTable.GenPatchPcdTable import PeImageClass,parsePcdInfoFromMapFile from PatchPcdValue.PatchPcdValue import PatchBinaryFile @@ -53,14 +53,10 @@ from PatchPcdValue.PatchPcdValue import PatchBinaryFile import Common.GlobalData as GlobalData from GenFds.GenFds import GenFds, GenFdsApi import multiprocessing as mp from multiprocessing import Manager -# Version and Copyright -VersionNumber = "0.60" + ' ' + gBUILD_VERSION -__version__ = "%prog Version " + VersionNumber -__copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation All rights reserved." ## standard targets of build command gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run'] ## build configuration file @@ -761,26 +757,11 @@ class Build(): GlobalData.gBinCacheDest = BinCacheDest else: if GlobalData.gBinCacheDest is not None: EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.") - if self.ConfDirectory: - # Get alternate Conf location, if it is absolute, then just use the absolute directory name - ConfDirectoryPath = os.path.normpath(self.ConfDirectory) - - if not os.path.isabs(ConfDirectoryPath): - # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE - # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf - ConfDirectoryPath = mws.join(self.WorkspaceDir, ConfDirectoryPath) - else: - if "CONF_PATH" in os.environ: - ConfDirectoryPath = os.path.normcase(os.path.normpath(os.environ["CONF_PATH"])) - else: - # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf - ConfDirectoryPath = mws.join(self.WorkspaceDir, 'Conf') - GlobalData.gConfDirectory = ConfDirectoryPath - GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath)) + GlobalData.gDatabasePath = os.path.normpath(os.path.join(GlobalData.gConfDirectory, GlobalData.gDatabasePath)) if not os.path.exists(os.path.join(GlobalData.gConfDirectory, '.cache')): os.makedirs(os.path.join(GlobalData.gConfDirectory, '.cache')) self.Db = BuildDB self.BuildDatabase = self.Db.BuildObject self.Platform = None @@ -2298,17 +2279,11 @@ def ParseDefines(DefineList=[]): DefineDict[DefineTokenList[0]] = "TRUE" else: DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip() return DefineDict -gParamCheck = [] -def SingleCheckCallback(option, opt_str, value, parser): - if option not in gParamCheck: - setattr(parser.values, option.dest, value) - gParamCheck.append(option) - else: - parser.error("Option %s only allows one instance in command line!" % option) + def LogBuildTime(Time): if Time: TimeDurStr = '' TimeDur = time.gmtime(Time) @@ -2318,83 +2293,10 @@ def LogBuildTime(Time): TimeDurStr = time.strftime("%H:%M:%S", TimeDur) return TimeDurStr else: return None -## Parse command line options -# -# Using standard Python module optparse to parse command line option of this tool. -# -# @retval Opt A optparse.Values object containing the parsed options -# @retval Args Target of build command -# -def MyOptionParser(): - Parser = OptionParser(description=__copyright__, version=__version__, prog="build.exe", usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]") - Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32', 'X64', 'EBC', 'ARM', 'AARCH64'], dest="TargetArch", - help="ARCHS is one of list: IA32, X64, ARM, AARCH64 or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.") - Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback, - help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.") - Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback, - help="Build the module specified by the INF file name argument.") - Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.", - action="append") - Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain", - help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.") - Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback, - help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.") - - Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback, - help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. When value is set to 0, tool automatically detect number of "\ - "processor threads, set value to 1 means disable multi-thread build, and set value to more than 1 means user specify the threads number to build.") - - Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback, - help="The name of the FDF file to use, which overrides the setting in the DSC file.") - Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[], - help="The name of FD to be generated. The name must be from [FD] section in FDF file.") - Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[], - help="The name of FV to be generated. The name must be from [FV] section in FDF file.") - Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[], - help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.") - Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.") - Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.") - - Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", default=False, help="Don't check case of file name.") - - Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.") - Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.") - - Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode", - help="Make use of silent mode of (n)make.") - Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.") - Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\ - "including library instances selected, final dependency expression, "\ - "and warning messages, etc.") - Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.") - Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".") - - Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.") - Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD', 'LIBRARY', 'FLASH', 'DEPEX', 'BUILD_FLAGS', 'FIXED_ADDRESS', 'HASH', 'EXECUTION_ORDER'], dest="ReportType", default=[], - help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, HASH, EXECUTION_ORDER]. "\ - "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, HASH, BUILD_FLAGS, FIXED_ADDRESS]") - Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag", - help="Specify the specific option to parse EDK UNI file. Must be one of: [-c, -s]. -c is for EDK framework UNI file, and -s is for EDK UEFI UNI file. "\ - "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\ - "will override the setting in [BuildOptions] section of platform DSC.") - Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism") - Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.") - Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.") - Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files") - Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ") - Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.") - Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.") - Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.") - Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.") - Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.") - Parser.add_option("--disable-include-path-check", action="store_true", dest="DisableIncludePathCheck", default=False, help="Disable the include path check for outside of package.") - (Opt, Args) = Parser.parse_args() - return (Opt, Args) - ## Tool entrance method # # This method mainly dispatch specific methods per the command line options. # If no error found, return zero value so the caller of this tool can know # if it's executed successfully or not. @@ -2413,11 +2315,11 @@ def Main(): EdkLogger.LogClientInitialize(LogQ) GlobalData.gCommand = sys.argv[1:] # # Parse the options and args # - (Option, Target) = MyOptionParser() + Option, Target = BuildOption, BuildTarget GlobalData.gOptions = Option GlobalData.gCaseInsensitive = Option.CaseInsensitive # Set log level LogLevel = EdkLogger.INFO diff --git a/BaseTools/Source/Python/build/buildoptions.py b/BaseTools/Source/Python/build/buildoptions.py new file mode 100644 index 000000000000..7161aa66f23e --- /dev/null +++ b/BaseTools/Source/Python/build/buildoptions.py @@ -0,0 +1,92 @@ +## @file +# build a platform or a module +# +# Copyright (c) 2014, Hewlett-Packard Development Company, L.P.
+# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2018, Hewlett Packard Enterprise Development, L.P.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +# Version and Copyright +from Common.BuildVersion import gBUILD_VERSION +from optparse import OptionParser +VersionNumber = "0.60" + ' ' + gBUILD_VERSION +__version__ = "%prog Version " + VersionNumber +__copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation All rights reserved." + +gParamCheck = [] +def SingleCheckCallback(option, opt_str, value, parser): + if option not in gParamCheck: + setattr(parser.values, option.dest, value) + gParamCheck.append(option) + else: + parser.error("Option %s only allows one instance in command line!" % option) + +def MyOptionParser(): + Parser = OptionParser(description=__copyright__, version=__version__, prog="build.exe", usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]") + Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32', 'X64', 'EBC', 'ARM', 'AARCH64'], dest="TargetArch", + help="ARCHS is one of list: IA32, X64, ARM, AARCH64 or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.") + Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback, + help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.") + Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback, + help="Build the module specified by the INF file name argument.") + Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.", + action="append") + Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain", + help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.") + Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback, + help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.") + + Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback, + help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. When value is set to 0, tool automatically detect number of "\ + "processor threads, set value to 1 means disable multi-thread build, and set value to more than 1 means user specify the threads number to build.") + + Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback, + help="The name of the FDF file to use, which overrides the setting in the DSC file.") + Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[], + help="The name of FD to be generated. The name must be from [FD] section in FDF file.") + Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[], + help="The name of FV to be generated. The name must be from [FV] section in FDF file.") + Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[], + help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.") + Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.") + Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.") + + Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", default=False, help="Don't check case of file name.") + + Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.") + Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.") + + Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode", + help="Make use of silent mode of (n)make.") + Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.") + Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\ + "including library instances selected, final dependency expression, "\ + "and warning messages, etc.") + Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.") + Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".") + + Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.") + Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD', 'LIBRARY', 'FLASH', 'DEPEX', 'BUILD_FLAGS', 'FIXED_ADDRESS', 'HASH', 'EXECUTION_ORDER'], dest="ReportType", default=[], + help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, HASH, EXECUTION_ORDER]. "\ + "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, HASH, BUILD_FLAGS, FIXED_ADDRESS]") + Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag", + help="Specify the specific option to parse EDK UNI file. Must be one of: [-c, -s]. -c is for EDK framework UNI file, and -s is for EDK UEFI UNI file. "\ + "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\ + "will override the setting in [BuildOptions] section of platform DSC.") + Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism") + Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.") + Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.") + Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files") + Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ") + Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.") + Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.") + Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.") + Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.") + Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.") + Parser.add_option("--disable-include-path-check", action="store_true", dest="DisableIncludePathCheck", default=False, help="Disable the include path check for outside of package.") + (Opt, Args) = Parser.parse_args() + return (Opt, Args) + +BuildOption, BuildTarget = MyOptionParser() -- 2.20.1.windows.1