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

4.11
Last change on this file since c88c92d was c88c92d, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 17, 2019 at 10:32:45 AM

sb: Change default prefix

Use OS prefix + "rtems" + $rtems_version as the default prefix to
automatically separate different RTEMS versions.

Close #3679.

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