-#!/usr/bin/python -tt\r
-# vim: ai ts=4 sts=4 et sw=4\r
-#\r
-# Copyright (c) 2012 Intel, Inc.\r
-#\r
-# This program is free software; you can redistribute it and/or modify it\r
-# under the terms of the GNU General Public License as published by the Free\r
-# Software Foundation; version 2 of the License\r
-#\r
-# This program is distributed in the hope that it will be useful, but\r
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\r
-# for more details.\r
-#\r
-# You should have received a copy of the GNU General Public License along\r
-# with this program; if not, write to the Free Software Foundation, Inc., 59\r
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
-\r
-"""Implementation of subcmd: chroot\r
-"""\r
-\r
-import os\r
-import os, sys, re\r
-import pwd\r
-import argparse\r
-\r
-from mic import msger\r
-from mic.utils import misc, errors\r
-from mic.conf import configmgr\r
-from mic.plugin import pluginmgr\r
-\r
-def _root_confirm():\r
- """Make sure command is called by root\r
- There are a lot of commands needed to be run during creating images,\r
- some of them must be run with root privilege like mount, kpartx"""\r
- if os.geteuid() != 0:\r
- msger.error('Root permission is required to continue, abort')\r
- \r
-def main(parser, args, argv):\r
- """mic choot entry point."""\r
-\r
- #args is argparser namespace, argv is the input cmd line\r
- if args is None:\r
- raise errors.Usage("Invalid arguments")\r
-\r
- targetimage = args.imagefile\r
- if not os.path.exists(targetimage):\r
- raise errors.CreatorError("Cannot find the image: %s"\r
- % targetimage)\r
-\r
- _root_confirm()\r
-\r
- configmgr.chroot['saveto'] = args.saveto\r
-\r
- imagetype = misc.get_image_type(targetimage)\r
- if imagetype in ("ext3fsimg", "ext4fsimg", "btrfsimg", "f2fsimg"):\r
- imagetype = "loop"\r
-\r
- chrootclass = None\r
- for pname, pcls in pluginmgr.get_plugins('imager').iteritems():\r
- if pname == imagetype and hasattr(pcls, "do_chroot"):\r
- chrootclass = pcls\r
- break\r
-\r
- if not chrootclass:\r
- raise errors.CreatorError("Cannot support image type: %s" \\r
- % imagetype)\r
-\r
- chrootclass.do_chroot(targetimage, args.cmd)\r
- \r
- \r
+#!/usr/bin/python3 -tt
+# vim: ai ts=4 sts=4 et sw=4
+#
+# Copyright (c) 2012 Intel, Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation; version 2 of the License
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+"""Implementation of subcmd: chroot
+"""
+
+import os
+import os, sys, re
+import pwd
+import argparse
+
+from mic import msger
+from mic.utils import misc, errors
+from mic.conf import configmgr
+from mic.plugin import pluginmgr
+
+def _root_confirm():
+ """Make sure command is called by root
+ There are a lot of commands needed to be run during creating images,
+ some of them must be run with root privilege like mount, kpartx"""
+ if os.geteuid() != 0:
+ msger.error('Root permission is required to continue, abort')
+
+def main(parser, args, argv):
+ """mic choot entry point."""
+
+ #args is argparser namespace, argv is the input cmd line
+ if args is None:
+ raise errors.Usage("Invalid arguments")
+
+ targetimage = args.imagefile
+ if not os.path.exists(targetimage):
+ raise errors.CreatorError("Cannot find the image: %s"
+ % targetimage)
+
+ _root_confirm()
+
+ configmgr.chroot['saveto'] = args.saveto
+
+ imagetype = misc.get_image_type(targetimage)
+ if imagetype in ("ext3fsimg", "ext4fsimg", "btrfsimg", "f2fsimg"):
+ imagetype = "loop"
+
+ chrootclass = None
+ for pname, pcls in list(pluginmgr.get_plugins('imager').items()):
+ if pname == imagetype and hasattr(pcls, "do_chroot"):
+ chrootclass = pcls
+ break
+
+ if not chrootclass:
+ raise errors.CreatorError("Cannot support image type: %s" \
+ % imagetype)
+
+ chrootclass.do_chroot(targetimage, args.cmd)
-
-
-#!/usr/bin/python -tt\r
-# vim: ai ts=4 sts=4 et sw=4\r
-#\r
-# Copyright (c) 2012 Intel, Inc.\r
-#\r
-# This program is free software; you can redistribute it and/or modify it\r
-# under the terms of the GNU General Public License as published by the Free\r
-# Software Foundation; version 2 of the License\r
-#\r
-# This program is distributed in the hope that it will be useful, but\r
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\r
-# for more details.\r
-#\r
-# You should have received a copy of the GNU General Public License along\r
-# with this program; if not, write to the Free Software Foundation, Inc., 59\r
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
-\r
-"""Implementation of subcmd: create\r
-"""\r
-\r
-import os\r
-import os, sys, re\r
-import pwd\r
-import argparse\r
-\r
-from mic import msger\r
-from mic.utils import errors, rpmmisc\r
-from mic.conf import configmgr\r
-from mic.plugin import pluginmgr\r
-\r
-def main(parser, args, argv):\r
- """mic create entry point."""\r
- #args is argparser namespace, argv is the input cmd line\r
- if args is None:\r
- raise errors.Usage("Invalid arguments")\r
-\r
- if not os.path.exists(args.ksfile):\r
- raise errors.CreatorError("Can't find the file: %s" % args.ksfile)\r
-\r
- if os.geteuid() != 0:\r
- msger.error("Root permission is required, abort")\r
- \r
- try:\r
- w = pwd.getpwuid(os.geteuid())\r
- except KeyError:\r
- msger.warning("Might fail in compressing stage for undetermined user")\r
- \r
- abspath = lambda pth: os.path.abspath(os.path.expanduser(pth))\r
- if args.logfile:\r
- logfile_abs_path = abspath(args.logfile)\r
- if os.path.isdir(logfile_abs_path):\r
- raise errors.Usage("logfile's path %s should be file"\r
- % args.logfile)\r
- configmgr.create['logfile'] = logfile_abs_path\r
- configmgr.set_logfile()\r
-\r
- if args.subcommand == "auto":\r
- do_auto(parser, args.ksfile, argv)\r
- return\r
-\r
- if args.interactive:\r
- msger.enable_interactive()\r
- else:\r
- msger.disable_interactive()\r
-\r
- if args.verbose:\r
- msger.set_loglevel('VERBOSE')\r
-\r
- if args.debug:\r
- try:\r
- import rpm\r
- rpm.setVerbosity(rpm.RPMLOG_NOTICE)\r
- except ImportError:\r
- pass\r
-\r
- msger.set_loglevel('DEBUG')\r
-\r
- if args.rpm_debug:\r
- try:\r
- import rpm\r
- rpm.setVerbosity(rpm.RPMLOG_DEBUG)\r
- except ImportError:\r
- pass\r
- #check the imager type\r
- createrClass = None\r
- for subcmd, klass in pluginmgr.get_plugins('imager').iteritems():\r
- if subcmd == args.subcommand and hasattr(klass, 'do_create'):\r
- createrClass = klass\r
-\r
- if createrClass is None:\r
- raise errors.CreatorError("Can't support subcommand %s" % args.subcommand)\r
-\r
- if args.config:\r
- configmgr.reset()\r
- configmgr._siteconf = args.config\r
-\r
- if args.outdir is not None:\r
- configmgr.create['outdir'] = abspath(args.outdir)\r
- if args.cachedir is not None:\r
- configmgr.create['cachedir'] = abspath(args.cachedir)\r
- os.environ['ZYPP_LOCKFILE_ROOT'] = configmgr.create['cachedir']\r
-\r
- for cdir in ('outdir', 'cachedir'):\r
- if os.path.exists(configmgr.create[cdir]) \\r
- and not os.path.isdir(configmgr.create[cdir]):\r
- raise errors.Usage('Invalid directory specified: %s' \\r
- % configmgr.create[cdir])\r
- if not os.path.exists(configmgr.create[cdir]):\r
- os.makedirs(configmgr.create[cdir])\r
- if os.getenv('SUDO_UID', '') and os.getenv('SUDO_GID', ''):\r
- os.chown(configmgr.create[cdir],\r
- int(os.getenv('SUDO_UID')),\r
- int(os.getenv('SUDO_GID')))\r
-\r
- if args.local_pkgs_path is not None:\r
- if not os.path.exists(args.local_pkgs_path):\r
- raise errors.Usage('Local pkgs directory: \'%s\' not exist' \\r
- % args.local_pkgs_path)\r
- configmgr.create['local_pkgs_path'] = args.local_pkgs_path\r
-\r
- if args.release:\r
- configmgr.create['release'] = args.release.rstrip('/')\r
-\r
- if args.record_pkgs:\r
- configmgr.create['record_pkgs'] = []\r
- for infotype in args.record_pkgs.split(','):\r
- if infotype not in ('name', 'content', 'license', 'vcs'):\r
- raise errors.Usage('Invalid pkg recording: %s, valid ones:'\r
- ' "name", "content", "license", "vcs"' \\r
- % infotype)\r
-\r
- configmgr.create['record_pkgs'].append(infotype)\r
-\r
- if args.strict_mode:\r
- configmgr.create['strict_mode'] = args.strict_mode\r
- if args.arch is not None:\r
- supported_arch = sorted(rpmmisc.archPolicies.keys(), reverse=True)\r
- if args.arch in supported_arch:\r
- configmgr.create['arch'] = args.arch\r
- else:\r
- raise errors.Usage('Invalid architecture: "%s".\n'\r
- ' Supported architectures are: \n'\r
- ' %s' % (args.arch,\r
- ', '.join(supported_arch)))\r
-\r
- if args.pkgmgr is not None:\r
- configmgr.create['pkgmgr'] = args.pkgmgr\r
-\r
- if args.skip_set_hosts:\r
- configmgr.create['skip_set_hosts']=args.skip_set_hosts\r
- if args.postscripts_maxruntime:\r
- configmgr.create['postscripts_maxruntime']=int(args.postscripts_maxruntime)\r
- if args.block_recommends:\r
- configmgr.create['block_recommends']=args.block_recommends\r
- if args.runtime:\r
- configmgr.set_runtime(args.runtime)\r
-\r
- if args.use_mic_in_bootstrap:\r
- configmgr.create['use_mic_in_bootstrap'] = args.use_mic_in_bootstrap\r
-\r
- if args.pack_to is not None:\r
- configmgr.create['pack_to'] = args.pack_to\r
-\r
- if args.copy_kernel:\r
- configmgr.create['copy_kernel'] = args.copy_kernel\r
-\r
- if args.install_pkgs:\r
- configmgr.create['install_pkgs'] = []\r
- for pkgtype in args.install_pkgs.split(','):\r
- if pkgtype not in ('source', 'debuginfo', 'debugsource'):\r
- raise errors.Usage('Invalid parameter specified: "%s", '\r
- 'valid values: source, debuginfo, '\r
- 'debusource' % pkgtype)\r
-\r
- configmgr.create['install_pkgs'].append(pkgtype)\r
-\r
- if args.check_pkgs:\r
- for pkg in args.check_pkgs.split(','):\r
- configmgr.create['check_pkgs'].append(pkg)\r
-\r
- if args.enabletmpfs:\r
- configmgr.create['enabletmpfs'] = args.enabletmpfs\r
-\r
- if args.repourl:\r
- for item in args.repourl:\r
- try:\r
- key, val = item.split('=')\r
- except:\r
- continue\r
- configmgr.create['repourl'][key] = val\r
-\r
- if args.repo:\r
- for optvalue in args.repo:\r
- repo = {}\r
- for item in optvalue.split(';'):\r
- try:\r
- key, val = item.split('=')\r
- except:\r
- continue\r
- repo[key.strip()] = val.strip()\r
- if 'name' in repo:\r
- configmgr.create['extrarepos'][repo['name']] = repo\r
-\r
- if args.ignore_ksrepo:\r
- configmgr.create['ignore_ksrepo'] = args.ignore_ksrepo\r
- if args.run_script:\r
- configmgr.create['run_script'] = args.run_script\r
- if args.tpk_install:\r
- configmgr.create['tpk_install'] = args.tpk_install\r
-\r
- creater = createrClass()\r
- creater.do_create(args)\r
-\r
-def do_auto(parser, ksfile, argv):\r
- """${cmd_name}: auto detect image type from magic header\r
-\r
- Usage:\r
- ${name} ${cmd_name} <ksfile>\r
-\r
- ${cmd_option_list}\r
- """\r
- def parse_magic_line(re_str, pstr, ptype='mic'):\r
- ptn = re.compile(re_str)\r
- m = ptn.match(pstr)\r
- if not m or not m.groups():\r
- return None\r
-\r
- inline_argv = m.group(1).strip()\r
- if ptype == 'mic':\r
- m2 = re.search('(?P<format>\w+)', inline_argv)\r
- elif ptype == 'mic2':\r
- m2 = re.search('(-f|--format(=)?)\s*(?P<format>\w+)',\r
- inline_argv)\r
- else:\r
- return None\r
-\r
- if m2:\r
- cmdname = m2.group('format')\r
- inline_argv = inline_argv.replace(m2.group(0), '')\r
- return (cmdname, inline_argv)\r
-\r
- return None\r
-\r
- if not os.path.exists(ksfile):\r
- raise errors.CreatorError("Can't find the file: %s" % ksfile)\r
-\r
- with open(ksfile, 'r') as rf:\r
- first_line = rf.readline()\r
-\r
- mic_re = '^#\s*-\*-mic-options-\*-\s+(.*)\s+-\*-mic-options-\*-'\r
- mic2_re = '^#\s*-\*-mic2-options-\*-\s+(.*)\s+-\*-mic2-options-\*-'\r
-\r
- result = parse_magic_line(mic_re, first_line, 'mic') \\r
- or parse_magic_line(mic2_re, first_line, 'mic2')\r
- if not result:\r
- raise errors.KsError("Invalid magic line in file: %s" % ksfile)\r
- \r
- ksargv = ' '.join(result).split()\r
-\r
- argv.remove("auto")\r
- index = argv.index("create")\r
- #insert the subcommand\r
- argv.insert(index+1, ksargv[0])\r
- options = argv + ksargv[1:]\r
-\r
- args = parser.parse_args(options)\r
-\r
- main(parser, args, options)\r
-\r
+#!/usr/bin/python3 -tt
+# vim: ai ts=4 sts=4 et sw=4
+#
+# Copyright (c) 2012 Intel, Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation; version 2 of the License
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+"""Implementation of subcmd: create
+"""
+
+import os
+import os, sys, re
+import pwd
+import argparse
+
+from mic import msger
+from mic.utils import errors, rpmmisc
+from mic.conf import configmgr
+from mic.plugin import pluginmgr
+
+def main(parser, args, argv):
+ """mic create entry point."""
+ #args is argparser namespace, argv is the input cmd line
+ if args is None:
+ raise errors.Usage("Invalid arguments")
+
+ if not os.path.exists(args.ksfile):
+ raise errors.CreatorError("Can't find the file: %s" % args.ksfile)
+
+ if os.geteuid() != 0:
+ msger.error("Root permission is required, abort")
+
+ try:
+ w = pwd.getpwuid(os.geteuid())
+ except KeyError:
+ msger.warning("Might fail in compressing stage for undetermined user")
+
+ abspath = lambda pth: os.path.abspath(os.path.expanduser(pth))
+ if args.logfile:
+ logfile_abs_path = abspath(args.logfile)
+ if os.path.isdir(logfile_abs_path):
+ raise errors.Usage("logfile's path %s should be file"
+ % args.logfile)
+ configmgr.create['logfile'] = logfile_abs_path
+ configmgr.set_logfile()
+
+ if args.subcommand == "auto":
+ do_auto(parser, args.ksfile, argv)
+ return
+
+ if args.interactive:
+ msger.enable_interactive()
+ else:
+ msger.disable_interactive()
+
+ if args.verbose:
+ msger.set_loglevel('VERBOSE')
+
+ if args.debug:
+ try:
+ import rpm
+ rpm.setVerbosity(rpm.RPMLOG_NOTICE)
+ except ImportError:
+ pass
+
+ msger.set_loglevel('DEBUG')
+
+ if args.rpm_debug:
+ try:
+ import rpm
+ rpm.setVerbosity(rpm.RPMLOG_DEBUG)
+ except ImportError:
+ pass
+
+ #check the imager type
+ createrClass = None
+ for subcmd, klass in list(pluginmgr.get_plugins('imager').items()):
+ if subcmd == args.subcommand and hasattr(klass, 'do_create'):
+ createrClass = klass
+
+ if createrClass is None:
+ raise errors.CreatorError("Can't support subcommand %s" % args.subcommand)
+
+ if args.config:
+ configmgr.reset()
+ configmgr._siteconf = args.config
+
+ if args.outdir is not None:
+ configmgr.create['outdir'] = abspath(args.outdir)
+ if args.cachedir is not None:
+ configmgr.create['cachedir'] = abspath(args.cachedir)
+ os.environ['ZYPP_LOCKFILE_ROOT'] = configmgr.create['cachedir']
+
+ for cdir in ('outdir', 'cachedir'):
+ if os.path.exists(configmgr.create[cdir]) \
+ and not os.path.isdir(configmgr.create[cdir]):
+ raise errors.Usage('Invalid directory specified: %s' \
+ % configmgr.create[cdir])
+ if not os.path.exists(configmgr.create[cdir]):
+ os.makedirs(configmgr.create[cdir])
+ if os.getenv('SUDO_UID', '') and os.getenv('SUDO_GID', ''):
+ os.chown(configmgr.create[cdir],
+ int(os.getenv('SUDO_UID')),
+ int(os.getenv('SUDO_GID')))
+
+ if args.local_pkgs_path is not None:
+ if not os.path.exists(args.local_pkgs_path):
+ raise errors.Usage('Local pkgs directory: \'%s\' not exist' \
+ % args.local_pkgs_path)
+ configmgr.create['local_pkgs_path'] = args.local_pkgs_path
+
+ if args.release:
+ configmgr.create['release'] = args.release.rstrip('/')
+
+ if args.record_pkgs:
+ configmgr.create['record_pkgs'] = []
+ for infotype in args.record_pkgs.split(','):
+ if infotype not in ('name', 'content', 'license', 'vcs'):
+ raise errors.Usage('Invalid pkg recording: %s, valid ones:'
+ ' "name", "content", "license", "vcs"' \
+ % infotype)
+
+ configmgr.create['record_pkgs'].append(infotype)
+
+ if args.strict_mode:
+ configmgr.create['strict_mode'] = args.strict_mode
+ if args.arch is not None:
+ supported_arch = sorted(list(rpmmisc.archPolicies.keys()), reverse=True)
+ if args.arch in supported_arch:
+ configmgr.create['arch'] = args.arch
+ else:
+ raise errors.Usage('Invalid architecture: "%s".\n'
+ ' Supported architectures are: \n'
+ ' %s' % (args.arch,
+ ', '.join(supported_arch)))
+
+ if args.pkgmgr is not None:
+ configmgr.create['pkgmgr'] = args.pkgmgr
+
+ if args.skip_set_hosts:
+ configmgr.create['skip_set_hosts']=args.skip_set_hosts
+
+ if args.postscripts_maxruntime:
+ configmgr.create['postscripts_maxruntime']=int(args.postscripts_maxruntime)
+ if args.block_recommends:
+ configmgr.create['block_recommends']=args.block_recommends
+
+ if args.runtime:
+ configmgr.set_runtime(args.runtime)
+
+ if args.use_mic_in_bootstrap:
+ configmgr.create['use_mic_in_bootstrap'] = args.use_mic_in_bootstrap
+
+ if args.pack_to is not None:
+ configmgr.create['pack_to'] = args.pack_to
+
+ if args.copy_kernel:
+ configmgr.create['copy_kernel'] = args.copy_kernel
+
+ if args.install_pkgs:
+ configmgr.create['install_pkgs'] = []
+ for pkgtype in args.install_pkgs.split(','):
+ if pkgtype not in ('source', 'debuginfo', 'debugsource'):
+ raise errors.Usage('Invalid parameter specified: "%s", '
+ 'valid values: source, debuginfo, '
+ 'debusource' % pkgtype)
+
+ configmgr.create['install_pkgs'].append(pkgtype)
+
+ if args.check_pkgs:
+ for pkg in args.check_pkgs.split(','):
+ configmgr.create['check_pkgs'].append(pkg)
+
+ if args.enabletmpfs:
+ configmgr.create['enabletmpfs'] = args.enabletmpfs
+
+ if args.repourl:
+ for item in args.repourl:
+ try:
+ key, val = item.split('=')
+ except:
+ continue
+ configmgr.create['repourl'][key] = val
+
+ if args.repo:
+ for optvalue in args.repo:
+ repo = {}
+ for item in optvalue.split(';'):
+ try:
+ key, val = item.split('=')
+ except:
+ continue
+ repo[key.strip()] = val.strip()
+ if 'name' in repo:
+ configmgr.create['extrarepos'][repo['name']] = repo
+
+ if args.ignore_ksrepo:
+ configmgr.create['ignore_ksrepo'] = args.ignore_ksrepo
+ if args.run_script:
+ configmgr.create['run_script'] = args.run_script
+ if args.tpk_install:
+ configmgr.create['tpk_install'] = args.tpk_install
+
+ creater = createrClass()
+ creater.do_create(args)
+
+def do_auto(parser, ksfile, argv):
+ """${cmd_name}: auto detect image type from magic header
+
+ Usage:
+ ${name} ${cmd_name} <ksfile>
+
+ ${cmd_option_list}
+ """
+ def parse_magic_line(re_str, pstr, ptype='mic'):
+ ptn = re.compile(re_str)
+ m = ptn.match(pstr)
+ if not m or not m.groups():
+ return None
+
+ inline_argv = m.group(1).strip()
+ if ptype == 'mic':
+ m2 = re.search('(?P<format>\w+)', inline_argv)
+ elif ptype == 'mic2':
+ m2 = re.search('(-f|--format(=)?)\s*(?P<format>\w+)',
+ inline_argv)
+ else:
+ return None
+
+ if m2:
+ cmdname = m2.group('format')
+ inline_argv = inline_argv.replace(m2.group(0), '')
+ return (cmdname, inline_argv)
+
+ return None
+
+ if not os.path.exists(ksfile):
+ raise errors.CreatorError("Can't find the file: %s" % ksfile)
+
+ with open(ksfile, 'r') as rf:
+ first_line = rf.readline()
+
+ mic_re = '^#\s*-\*-mic-options-\*-\s+(.*)\s+-\*-mic-options-\*-'
+ mic2_re = '^#\s*-\*-mic2-options-\*-\s+(.*)\s+-\*-mic2-options-\*-'
+
+ result = parse_magic_line(mic_re, first_line, 'mic') \
+ or parse_magic_line(mic2_re, first_line, 'mic2')
+ if not result:
+ raise errors.KsError("Invalid magic line in file: %s" % ksfile)
+
+ ksargv = ' '.join(result).split()
+
+ argv.remove("auto")
+ index = argv.index("create")
+ #insert the subcommand
+ argv.insert(index+1, ksargv[0])
+ options = argv + ksargv[1:]
+
+ args = parser.parse_args(options)
+
+ main(parser, args, options)
-