source: rtems-source-builder/source-builder/sb/options.py @ d2d4678

5
Last change on this file since d2d4678 was d2d4678, checked in by Chris Johns <chrisj@…>, on 09/27/18 at 22:16:49

sb: Include optional args in the valid option processing.

Optional arguments were not being included in the valid list of
options being checked so --mail resulted in being unknown.

  • Property mode set to 100644
File size: 26.8 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2016 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Permission to use, copy, modify, and/or distribute this software for any
9# purpose with or without fee is hereby granted, provided that the above
10# copyright notice and this permission notice appear in all copies.
11#
12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#
21# Determine the defaults and load the specific file.
22#
23
24from __future__ import print_function
25
26import datetime
27import glob
28import pprint
29import re
30import os
31import string
32
33import error
34import execute
35import git
36import log
37import macros
38import path
39import sys
40
41import version
42
43basepath = 'sb'
44
45#
46# Save the host and POSIX state.
47#
48host_windows = False
49host_posix = True
50
51class command_line:
52    """Process the command line in a common way for all Tool Builder commands."""
53
54    def __init__(self, argv, optargs, _defaults, command_path):
55        self._long_opts = {
56            # key                 macro                handler            param  defs   init
57            '--prefix'               : ('_prefix',           self._lo_path,     True,  None,  False),
58            '--topdir'               : ('_topdir',           self._lo_path,     True,  None,  False),
59            '--configdir'            : ('_configdir',        self._lo_path,     True,  None,  False),
60            '--builddir'             : ('_builddir',         self._lo_path,     True,  None,  False),
61            '--sourcedir'            : ('_sourcedir',        self._lo_path,     True,  None,  False),
62            '--patchdir'             : ('_patchdir',         self._lo_path,     True,  None,  False),
63            '--tmppath'              : ('_tmppath',          self._lo_path,     True,  None,  False),
64            '--jobs'                 : ('_jobs',             self._lo_jobs,     True,  'max', True),
65            '--log'                  : ('_logfile',          self._lo_string,   True,  None,  False),
66            '--url'                  : ('_url_base',         self._lo_string,   True,  None,  False),
67            '--no-download'          : ('_disable_download', self._lo_bool,     False, '0',   True),
68            '--macros'               : ('_macros',           self._lo_string,   True,  None,  False),
69            '--source-only-download' : ('_source_download',  self._lo_bool,     False, '0',   True),
70            '--targetcflags'         : ('_targetcflags',     self._lo_string,   True,  None,  False),
71            '--targetcxxflags'       : ('_targetcxxflags',   self._lo_string,   True,  None,  False),
72            '--libstdcxxflags'       : ('_libstdcxxflags',   self._lo_string,   True,  None,  False),
73            '--force'                : ('_force',            self._lo_bool,     False, '0',   True),
74            '--quiet'                : ('_quiet',            self._lo_bool,     False, '0',   True),
75            '--trace'                : ('_trace',            self._lo_bool,     False, '0',   True),
76            '--dry-run'              : ('_dry_run',          self._lo_bool,     False, '0',   True),
77            '--warn-all'             : ('_warn_all',         self._lo_bool,     False, '0',   True),
78            '--no-clean'             : ('_no_clean',         self._lo_bool,     False, '0',   True),
79            '--keep-going'           : ('_keep_going',       self._lo_bool,     False, '0',   True),
80            '--always-clean'         : ('_always_clean',     self._lo_bool,     False, '0',   True),
81            '--no-install'           : ('_no_install',       self._lo_bool,     False, '0',   True),
82            '--regression'           : ('_regression',       self._lo_bool,     False, '0',   True),
83            '--host'                 : ('_host',             self._lo_triplets, True,  None,  False),
84            '--build'                : ('_build',            self._lo_triplets, True,  None,  False),
85            '--target'               : ('_target',           self._lo_triplets, True,  None,  False),
86            '--help'                 : (None,                self._lo_help,     False, None,  False)
87            }
88
89        self.command_path = command_path
90        self.command_name = path.basename(argv[0])
91        self.argv = argv
92        self.args = argv[1:]
93        self.optargs = optargs
94        self.defaults = _defaults
95        self.opts = { 'params' : [] }
96        for lo in self._long_opts:
97            self.opts[lo[2:]] = self._long_opts[lo][3]
98            if self._long_opts[lo][4]:
99                self.defaults[self._long_opts[lo][0]] = ('none', 'none', self._long_opts[lo][3])
100
101    def __str__(self):
102        def _dict(dd):
103            s = ''
104            ddl = list(dd.keys())
105            ddl.sort()
106            for d in ddl:
107                s += '  ' + d + ': ' + str(dd[d]) + '\n'
108            return s
109
110        s = 'command: ' + self.command() + \
111            '\nargs: ' + str(self.args) + \
112            '\nopts:\n' + _dict(self.opts)
113
114        return s
115
116    def _lo_string(self, opt, macro, value):
117        if value is None:
118            raise error.general('option requires a value: %s' % (opt))
119        self.opts[opt[2:]] = value
120        self.defaults[macro] = value
121
122    def _lo_path(self, opt, macro, value):
123        if value is None:
124            raise error.general('option requires a path: %s' % (opt))
125        value = path.abspath(value)
126        self.opts[opt[2:]] = value
127        self.defaults[macro] = value
128
129    def _lo_jobs(self, opt, macro, value):
130        if value is None:
131            raise error.general('option requires a value: %s' % (opt))
132        ok = False
133        if value in ['max', 'none', 'half']:
134            ok = True
135        else:
136            try:
137                i = int(value)
138                ok = True
139            except:
140                pass
141            if not ok:
142                try:
143                    f = float(value)
144                    ok = True
145                except:
146                    pass
147        if not ok:
148            raise error.general('invalid jobs option: %s' % (value))
149        self.defaults[macro] = value
150        self.opts[opt[2:]] = value
151
152    def _lo_bool(self, opt, macro, value):
153        if value is not None:
154            raise error.general('option does not take a value: %s' % (opt))
155        self.opts[opt[2:]] = '1'
156        self.defaults[macro] = '1'
157
158    def _lo_triplets(self, opt, macro, value):
159        #
160        # This is a target triplet. Run it past config.sub to make make sure it
161        # is ok.  The target triplet is 'cpu-vendor-os'.
162        #
163        e = execute.capture_execution()
164        config_sub = path.join(self.command_path,
165                               basepath, 'config.sub')
166        exit_code, proc, output = e.shell(config_sub + ' ' + value)
167        if exit_code == 0:
168            value = output
169        self.defaults[macro] = ('triplet', 'none', value)
170        self.opts[opt[2:]] = value
171        _cpu = macro + '_cpu'
172        _arch = macro + '_arch'
173        _vendor = macro + '_vendor'
174        _os = macro + '_os'
175        _arch_value = ''
176        _vendor_value = ''
177        _os_value = ''
178        dash = value.find('-')
179        if dash >= 0:
180            _arch_value = value[:dash]
181            value = value[dash + 1:]
182        dash = value.find('-')
183        if dash >= 0:
184            _vendor_value = value[:dash]
185            value = value[dash + 1:]
186        if len(value):
187            _os_value = value
188        self.defaults[_cpu]    = _arch_value
189        self.defaults[_arch]   = _arch_value
190        self.defaults[_vendor] = _vendor_value
191        self.defaults[_os]     = _os_value
192
193    def _lo_help(self, opt, macro, value):
194        self.help()
195
196    def help(self):
197        print('%s: [options] [args]' % (self.command_name))
198        print('RTEMS Source Builder, an RTEMS Tools Project (c) 2012-2015 Chris Johns')
199        print('Options and arguments:')
200        print('--force                : Force the build to proceed')
201        print('--quiet                : Quiet output (not used)')
202        print('--trace                : Trace the execution')
203        print('--dry-run              : Do everything but actually run the build')
204        print('--warn-all             : Generate warnings')
205        print('--no-clean             : Do not clean up the build tree')
206        print('--always-clean         : Always clean the build tree, even with an error')
207        print('--keep-going           : Do not stop on an error.')
208        print('--regression           : Set --no-install, --keep-going and --always-clean')
209        print('--jobs                 : Run with specified number of jobs, default: num CPUs.')
210        print('--host                 : Set the host triplet')
211        print('--build                : Set the build triplet')
212        print('--target               : Set the target triplet')
213        print('--prefix path          : Tools build prefix, ie where they are installed')
214        print('--topdir path          : Top of the build tree, default is $PWD')
215        print('--configdir path       : Path to the configuration directory, default: ./config')
216        print('--builddir path        : Path to the build directory, default: ./build')
217        print('--sourcedir path       : Path to the source directory, default: ./source')
218        print('--patchdir path        : Path to the patches directory, default: ./patches')
219        print('--tmppath path         : Path to the temp directory, default: ./tmp')
220        print('--macros file[,[file]  : Macro format files to load after the defaults')
221        print('--log file             : Log file where all build out is written too')
222        print('--url url[,url]        : URL to look for source')
223        print('--no-download          : Disable the source downloader')
224        print('--no-install           : Do not install the packages to the prefix')
225        print('--targetcflags flags   : List of C flags for the target code')
226        print('--targetcxxflags flags : List of C++ flags for the target code')
227        print('--libstdcxxflags flags : List of C++ flags to build the target libstdc++ code')
228        print('--source-only-download : Only download the source')
229        print('--with-<label>         : Add the --with-<label> to the build')
230        print('--without-<label>      : Add the --without-<label> to the build')
231        print('--rtems-tools path     : Path to an install RTEMS tool set')
232        print('--rtems-bsp arc/bsp    : Standard RTEMS architecure and BSP specifier')
233        print('--rtems-version ver    : The RTEMS major/minor version string')
234        if self.optargs:
235            for a in self.optargs:
236                print('%-22s : %s' % (a, self.optargs[a]))
237        raise error.exit()
238
239    def process(self):
240        for a in self.args:
241            if a == '-?' or a == '--help':
242                self.help()
243        arg = 0
244        while arg < len(self.args):
245            a = self.args[arg]
246            if a.startswith('--'):
247                los = a.split('=')
248                lo = los[0]
249                if lo in self._long_opts:
250                    long_opt = self._long_opts[lo]
251                    if len(los) == 1:
252                        if long_opt[2]:
253                            if arg == len(self.args) - 1:
254                                raise error.general('option requires a parameter: %s' % (lo))
255                            arg += 1
256                            value = self.args[arg]
257                        else:
258                            value = None
259                    else:
260                        value = '='.join(los[1:])
261                    long_opt[1](lo, long_opt[0], value)
262                else:
263                    if a.startswith('--with'):
264                        if len(los) != 1:
265                            value = los[1]
266                        else:
267                            value = '1'
268                        self.defaults[los[0][2:].replace('-', '_').lower()] = ('none', 'none', value)
269                    else:
270                        if lo not in self.optargs:
271                            raise error.general('unknown option: %s' % (lo))
272            else:
273                if a.startswith('-'):
274                    raise error.general('not short options; only "-?"')
275                self.opts['params'].append(a)
276            arg += 1
277
278    def pre_process(self):
279        arg = 0
280        while arg < len(self.args):
281            a = self.args[arg]
282            if a == '--source-only-download':
283                self.args += ['--dry-run',
284                              '--with-download',
285                              '--quiet',
286                              '--without-log',
287                              '--without-error-report',
288                              '--without-release-url']
289            arg += 1
290
291    def post_process(self, logfile = True):
292        # Handle the log first.
293        logctrl = self.parse_args('--without-log')
294        if logctrl is None:
295            if logfile:
296                logfiles = self.logfiles()
297            else:
298                logfiles = None
299            log.default = log.log(streams = logfiles)
300        if self.trace():
301            log.tracing = True
302        if self.quiet():
303            log.quiet = True
304        # Must have a host
305        if self.defaults['_host'] == self.defaults['nil']:
306            raise error.general('--host not set')
307        # Must have a host
308        if self.defaults['_build'] == self.defaults['nil']:
309            raise error.general('--build not set')
310        # Manage the regression option
311        if self.opts['regression'] != '0':
312            self.opts['no-install'] = '1'
313            self.defaults['_no_install'] = '1'
314            self.opts['keep-going'] = '1'
315            self.defaults['_keep_going'] = '1'
316            self.opts['always-clean'] = '1'
317            self.defaults['_always_clean'] = '1'
318        # Handle the jobs for make
319        if '_ncpus' not in self.defaults:
320            raise error.general('host number of CPUs not set')
321        ncpus = self.jobs(self.defaults['_ncpus'])
322        if ncpus > 1:
323            self.defaults['_smp_mflags'] = '-j %d' % (ncpus)
324        else:
325            self.defaults['_smp_mflags'] = self.defaults['nil']
326        # Load user macro files
327        um = self.user_macros()
328        if um:
329            checked = path.exists(um)
330            if False in checked:
331                raise error.general('macro file not found: %s' % (um[checked.index(False)]))
332            for m in um:
333                self.defaults.load(m)
334        # Check if the user has a private set of macros to load
335        if 'RSB_MACROS' in os.environ:
336            if path.exists(os.environ['RSB_MACROS']):
337                self.defaults.load(os.environ['RSB_MACROS'])
338        if 'HOME' in os.environ:
339            rsb_macros = path.join(os.environ['HOME'], '.rsb_macros')
340            if path.exists(rsb_macros):
341                self.defaults.load(rsb_macros)
342
343    def sb_released(self):
344        if version.released():
345            self.defaults['rsb_released'] = '1'
346        self.defaults['rsb_version'] = version.str()
347
348    def sb_git(self):
349        repo = git.repo(self.defaults.expand('%{_sbdir}'), self)
350        if repo.valid():
351            repo_valid = '1'
352            repo_head = repo.head()
353            repo_clean = not repo.dirty()
354            repo_remotes = '%{nil}'
355            remotes = repo.remotes()
356            if 'origin' in remotes:
357                repo_remotes = '%s/origin' % (remotes['origin']['url'])
358            repo_id = repo_head
359            if not repo_clean:
360                repo_id += '-modified'
361            repo_mail = repo.email()
362        else:
363            repo_valid = '0'
364            repo_head = '%{nil}'
365            repo_clean = '%{nil}'
366            repo_remotes = '%{nil}'
367            repo_id = 'no-repo'
368            repo_mail = None
369        self.defaults['_sbgit_valid'] = repo_valid
370        self.defaults['_sbgit_head']  = repo_head
371        self.defaults['_sbgit_clean'] = str(repo_clean)
372        self.defaults['_sbgit_remotes'] = str(repo_remotes)
373        self.defaults['_sbgit_id']    = repo_id
374        if repo_mail is not None:
375            self.defaults['_sbgit_mail'] = repo_mail
376
377    def command(self):
378        return path.join(self.command_path, self.command_name)
379
380    def force(self):
381        return self.opts['force'] != '0'
382
383    def dry_run(self):
384        return self.opts['dry-run'] != '0'
385
386    def set_dry_run(self):
387        self.opts['dry-run'] = '1'
388
389    def quiet(self):
390        return self.opts['quiet'] != '0'
391
392    def trace(self):
393        return self.opts['trace'] != '0'
394
395    def warn_all(self):
396        return self.opts['warn-all'] != '0'
397
398    def keep_going(self):
399        return self.opts['keep-going'] != '0'
400
401    def no_clean(self):
402        return self.opts['no-clean'] != '0'
403
404    def always_clean(self):
405        return self.opts['always-clean'] != '0'
406
407    def no_install(self):
408        return self.opts['no-install'] != '0'
409
410    def canadian_cross(self):
411        _host = self.defaults.expand('%{_host}')
412        _build = self.defaults.expand('%{_build}')
413        _target = self.defaults.expand('%{_target}')
414        if len(_target):
415            return len(_host) and len(_build) and (_target) and \
416                _host != _build and _host != _target
417        return len(_host) and len(_build) and _host != _build
418
419    def user_macros(self):
420        #
421        # Return something even if it does not exist.
422        #
423        if self.opts['macros'] is None:
424            return None
425        um = []
426        configs = self.defaults.expand('%{_configdir}').split(':')
427        for m in self.opts['macros'].split(','):
428            if path.exists(m):
429                um += [m]
430            else:
431                # Get the expanded config macros then check them.
432                cm = path.expand(m, configs)
433                ccm = path.exists(cm)
434                if True in ccm:
435                    # Pick the first found
436                    um += [cm[ccm.index(True)]]
437                else:
438                    um += [m]
439        return um if len(um) else None
440
441    def jobs(self, cpus):
442        cpus = int(cpus)
443        if self.opts['jobs'] == 'none':
444            cpus = 0
445        elif self.opts['jobs'] == 'max':
446            pass
447        elif self.opts['jobs'] == 'half':
448            cpus = cpus / 2
449        else:
450            ok = False
451            try:
452                i = int(self.opts['jobs'])
453                cpus = i
454                ok = True
455            except:
456                pass
457            if not ok:
458                try:
459                    f = float(self.opts['jobs'])
460                    cpus = f * cpus
461                    ok = True
462                except:
463                    pass
464                if not ok:
465                    raise error.internal('bad jobs option: %s' % (self.opts['jobs']))
466        if cpus <= 0:
467            cpu = 1
468        return cpus
469
470    def params(self):
471        return self.opts['params']
472
473    def parse_args(self, arg, error = True, extra = True):
474        for a in range(0, len(self.args)):
475            if self.args[a].startswith(arg):
476                lhs = None
477                rhs = None
478                if '=' in self.args[a]:
479                    eqs = self.args[a].split('=')
480                    lhs = eqs[0]
481                    if len(eqs) > 2:
482                        rhs = '='.join(eqs[1:])
483                    else:
484                        rhs = eqs[1]
485                elif extra:
486                    lhs = self.args[a]
487                    a += 1
488                    if a < len(self.args):
489                        rhs = self.args[a]
490                return [lhs, rhs]
491            a += 1
492        return None
493
494    def get_arg(self, arg):
495        if self.optargs is None or arg not in self.optargs:
496            return None
497        return self.parse_args(arg)
498
499    def with_arg(self, label, default = 'not-found'):
500        # the default if there is no option for without.
501        result = default
502        for pre in ['with', 'without']:
503            arg_str = '--%s-%s' % (pre, label)
504            arg_label = '%s_%s' % (pre, label)
505            arg = self.parse_args(arg_str, error = False, extra = False)
506            if arg is not None:
507                if arg[1] is  None:
508                    result = 'yes'
509                else:
510                    result = arg[1]
511                break
512        return [arg_label, result]
513
514    def get_config_files(self, config):
515        #
516        # Convert to shell paths and return shell paths.
517        #
518        # @fixme should this use a passed in set of defaults and not
519        #        not the initial set of values ?
520        #
521        config = path.shell(config)
522        if '*' in config or '?' in config:
523            print(config)
524            configdir = path.dirname(config)
525            configbase = path.basename(config)
526            if len(configbase) == 0:
527                configbase = '*'
528            if not configbase.endswith('.cfg'):
529                configbase = configbase + '.cfg'
530            if len(configdir) == 0:
531                configdir = self.macros.expand(self.defaults['_configdir'])
532            configs = []
533            for cp in configdir.split(':'):
534                hostconfigdir = path.host(cp)
535                for f in glob.glob(os.path.join(hostconfigdir, configbase)):
536                    configs += path.shell(f)
537        else:
538            configs = [config]
539        return configs
540
541    def config_files(self):
542        configs = []
543        for config in self.opts['params']:
544            configs.extend(self.get_config_files(config))
545        return configs
546
547    def logfiles(self):
548        if 'log' in self.opts and self.opts['log'] is not None:
549            return self.opts['log'].split(',')
550        return ['rsb-log-%s.txt' % (datetime.datetime.now().strftime('%Y%m%d-%H%M%S'))]
551
552    def urls(self):
553        if self.opts['url'] is not None:
554            return self.opts['url'].split(',')
555        return None
556
557    def download_disabled(self):
558        return self.opts['no-download'] != '0'
559
560    def disable_install(self):
561        self.opts['no-install'] = '1'
562
563    def info(self):
564        s = ' Command Line: %s%s' % (' '.join(self.argv), os.linesep)
565        s += ' Python: %s' % (sys.version.replace('\n', ''))
566        return s
567
568    def log_info(self):
569        log.output(self.info())
570
571    def rtems_options(self):
572        # Check for RTEMS specific helper options.
573        rtems_tools = self.parse_args('--rtems-tools')
574        if rtems_tools is not None:
575            if self.get_arg('--with-tools') is not None:
576                raise error.general('--rtems-tools and --with-tools cannot be used together')
577            self.args.append('--with-tools=%s' % (rtems_tools[1]))
578        rtems_arch_bsp = self.parse_args('--rtems-bsp')
579        if rtems_arch_bsp is not None:
580            if self.get_arg('--target') is not None:
581                raise error.general('--rtems-bsp and --target cannot be used together')
582            ab = rtems_arch_bsp[1].split('/')
583            if len(ab) != 2:
584                raise error.general('invalid --rtems-bsp option')
585            rtems_version = self.parse_args('--rtems-version')
586            if rtems_version is None:
587                rtems_version = version.version()
588            else:
589                rtems_version = rtems_version[1]
590            self.args.append('--target=%s-rtems%s' % (ab[0], rtems_version))
591            self.args.append('--with-rtems-bsp=%s' % (ab[1]))
592
593def load(args, optargs = None, defaults = '%{_sbdir}/defaults.mc', logfile = True):
594    """
595    Copy the defaults, get the host specific values and merge them overriding
596    any matching defaults, then create an options object to handle the command
597    line merging in any command line overrides. Finally post process the
598    command line.
599    """
600
601    global host_windows
602    global host_posix
603
604    #
605    # The path to this command.
606    #
607    command_path = path.dirname(args[0])
608    if len(command_path) == 0:
609        command_path = '.'
610
611    #
612    # The command line contains the base defaults object all build objects copy
613    # and modify by loading a configuration.
614    #
615    o = command_line(args,
616                     optargs,
617                     macros.macros(name = defaults,
618                                   sbdir = command_path),
619                     command_path)
620
621    overrides = None
622    if os.name == 'nt':
623        try:
624            import windows
625            overrides = windows.load()
626            host_windows = True
627            host_posix = False
628        except:
629            raise error.general('failed to load Windows host support')
630    elif os.name == 'posix':
631        uname = os.uname()
632        try:
633            if uname[0].startswith('MINGW64_NT'):
634                import windows
635                overrides = windows.load()
636                host_windows = True
637            elif uname[0].startswith('CYGWIN_NT'):
638                import windows
639                overrides = windows.load()
640            elif uname[0] == 'Darwin':
641                import darwin
642                overrides = darwin.load()
643            elif uname[0] == 'FreeBSD':
644                import freebsd
645                overrides = freebsd.load()
646            elif uname[0] == 'NetBSD':
647                import netbsd
648                overrides = netbsd.load()
649            elif uname[0] == 'Linux':
650                import linux
651                overrides = linux.load()
652            elif uname[0] == 'SunOS':
653                import solaris
654                overrides = solaris.load()
655        except error.general as ge:
656            raise error.general('failed to load %s host support: %s' % (uname[0], ge))
657        except:
658            raise error.general('failed to load %s host support' % (uname[0]))
659    else:
660        raise error.general('unsupported host type; please add')
661    if overrides is None:
662        raise error.general('no hosts defaults found; please add')
663    for k in overrides:
664        o.defaults[k] = overrides[k]
665
666    o.sb_released()
667    o.sb_git()
668    o.rtems_options()
669    o.pre_process()
670    o.process()
671    o.post_process(logfile)
672
673    #
674    # Load the release settings
675    #
676    version.load_release_settings(o.defaults)
677
678    return o
679
680def run(args):
681    try:
682        _opts = load(args = args, defaults = 'defaults.mc')
683        log.notice('RTEMS Source Builder - Defaults, %s' % (version.str()))
684        _opts.log_info()
685        log.notice('Options:')
686        log.notice(str(_opts))
687        log.notice('Defaults:')
688        log.notice(str(_opts.defaults))
689        log.notice('with-opt1: %r' % (_opts.with_arg('opt1')))
690        log.notice('without-opt2: %r' % (_opts.with_arg('opt2')))
691    except error.general as gerr:
692        print(gerr)
693        sys.exit(1)
694    except error.internal as ierr:
695        print(ierr)
696        sys.exit(1)
697    except error.exit as eerr:
698        pass
699    except KeyboardInterrupt:
700        _notice(opts, 'abort: user terminated')
701        sys.exit(1)
702    sys.exit(0)
703
704if __name__ == '__main__':
705    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.