source: rtems-source-builder/source-builder/sb/options.py @ 5eb832f

4.104.114.95
Last change on this file since 5eb832f was 5eb832f, checked in by Karel Gardas <karel.gardas@…>, on 10/06/14 at 18:58:45

add support for i386-solaris2 platform

  • Property mode set to 100644
File size: 21.1 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2013 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
24import glob
25import pprint
26import re
27import os
28import string
29
30import error
31import execute
32import git
33import log
34import macros
35import path
36import sys
37
38import version
39
40basepath = 'sb'
41
42#
43# Save the host state.
44#
45host_windows = False
46
47class command_line:
48    """Process the command line in a common way for all Tool Builder commands."""
49
50    def __init__(self, argv, optargs, _defaults, command_path):
51        self._long_opts = {
52            # key                 macro                handler            param  defs   init
53            '--prefix'         : ('_prefix',           self._lo_path,     True,  None,  False),
54            '--topdir'         : ('_topdir',           self._lo_path,     True,  None,  False),
55            '--configdir'      : ('_configdir',        self._lo_path,     True,  None,  False),
56            '--builddir'       : ('_builddir',         self._lo_path,     True,  None,  False),
57            '--sourcedir'      : ('_sourcedir',        self._lo_path,     True,  None,  False),
58            '--tmppath'        : ('_tmppath',          self._lo_path,     True,  None,  False),
59            '--jobs'           : ('_jobs',             self._lo_jobs,     True,  'max', True),
60            '--log'            : ('_logfile',          self._lo_string,   True,  None,  False),
61            '--url'            : ('_url_base',         self._lo_string,   True,  None,  False),
62            '--no-download'    : ('_disable_download', self._lo_bool,     False, '0',   True),
63            '--macros'         : ('_macros',           self._lo_string,   True,  None,  False),
64            '--targetcflags'   : ('_targetcflags',     self._lo_string,   True,  None,  False),
65            '--targetcxxflags' : ('_targetcxxflags',   self._lo_string,   True,  None,  False),
66            '--libstdcxxflags' : ('_libstdcxxflags',   self._lo_string,   True,  None,  False),
67            '--force'          : ('_force',            self._lo_bool,     False, '0',   True),
68            '--quiet'          : ('_quiet',            self._lo_bool,     False, '0',   True),
69            '--trace'          : ('_trace',            self._lo_bool,     False, '0',   True),
70            '--dry-run'        : ('_dry_run',          self._lo_bool,     False, '0',   True),
71            '--warn-all'       : ('_warn_all',         self._lo_bool,     False, '0',   True),
72            '--no-clean'       : ('_no_clean',         self._lo_bool,     False, '0',   True),
73            '--keep-going'     : ('_keep_going',       self._lo_bool,     False, '0',   True),
74            '--always-clean'   : ('_always_clean',     self._lo_bool,     False, '0',   True),
75            '--no-install'     : ('_no_install',       self._lo_bool,     False, '0',   True),
76            '--regression'     : ('_regression',       self._lo_bool,     False, '0',   True),
77            '--host'           : ('_host',             self._lo_triplets, True,  None,  False),
78            '--build'          : ('_build',            self._lo_triplets, True,  None,  False),
79            '--target'         : ('_target',           self._lo_triplets, True,  None,  False),
80            '--help'           : (None,                self._lo_help,     False, None,  False)
81            }
82
83        self.command_path = command_path
84        self.command_name = path.basename(argv[0])
85        self.argv = argv
86        self.args = argv[1:]
87        self.optargs = optargs
88        self.defaults = _defaults
89        self.opts = { 'params' : [] }
90        for lo in self._long_opts:
91            self.opts[lo[2:]] = self._long_opts[lo][3]
92            if self._long_opts[lo][4]:
93                self.defaults[self._long_opts[lo][0]] = ('none', 'none', self._long_opts[lo][3])
94
95    def __str__(self):
96        def _dict(dd):
97            s = ''
98            ddl = dd.keys()
99            ddl.sort()
100            for d in ddl:
101                s += '  ' + d + ': ' + str(dd[d]) + '\n'
102            return s
103
104        s = 'command: ' + self.command() + \
105            '\nargs: ' + str(self.args) + \
106            '\nopts:\n' + _dict(self.opts)
107
108        return s
109
110    def _lo_string(self, opt, macro, value):
111        if value is None:
112            raise error.general('option requires a value: %s' % (opt))
113        self.opts[opt[2:]] = value
114        self.defaults[macro] = value
115
116    def _lo_path(self, opt, macro, value):
117        if value is None:
118            raise error.general('option requires a path: %s' % (opt))
119        value = path.abspath(value)
120        self.opts[opt[2:]] = value
121        self.defaults[macro] = value
122
123    def _lo_jobs(self, opt, macro, value):
124        if value is None:
125            raise error.general('option requires a value: %s' % (opt))
126        ok = False
127        if value in ['max', 'none', 'half']:
128            ok = True
129        else:
130            try:
131                i = int(value)
132                ok = True
133            except:
134                pass
135            if not ok:
136                try:
137                    f = float(value)
138                    ok = True
139                except:
140                    pass
141        if not ok:
142            raise error.general('invalid jobs option: %s' % (value))
143        self.defaults[macro] = value
144        self.opts[opt[2:]] = value
145
146    def _lo_bool(self, opt, macro, value):
147        if value is not None:
148            raise error.general('option does not take a value: %s' % (opt))
149        self.opts[opt[2:]] = '1'
150        self.defaults[macro] = '1'
151
152    def _lo_triplets(self, opt, macro, value):
153        #
154        # This is a target triplet. Run it past config.sub to make make sure it
155        # is ok.  The target triplet is 'cpu-vendor-os'.
156        #
157        e = execute.capture_execution()
158        config_sub = path.join(self.command_path,
159                               basepath, 'config.sub')
160        exit_code, proc, output = e.shell(config_sub + ' ' + value)
161        if exit_code == 0:
162            value = output
163        self.defaults[macro] = ('triplet', 'none', value)
164        self.opts[opt[2:]] = value
165        _cpu = macro + '_cpu'
166        _arch = macro + '_arch'
167        _vendor = macro + '_vendor'
168        _os = macro + '_os'
169        _arch_value = ''
170        _vendor_value = ''
171        _os_value = ''
172        dash = value.find('-')
173        if dash >= 0:
174            _arch_value = value[:dash]
175            value = value[dash + 1:]
176        dash = value.find('-')
177        if dash >= 0:
178            _vendor_value = value[:dash]
179            value = value[dash + 1:]
180        if len(value):
181            _os_value = value
182        self.defaults[_cpu]    = _arch_value
183        self.defaults[_arch]   = _arch_value
184        self.defaults[_vendor] = _vendor_value
185        self.defaults[_os]     = _os_value
186
187    def _lo_help(self, opt, macro, value):
188        self.help()
189
190    def help(self):
191        print '%s: [options] [args]' % (self.command_name)
192        print 'RTEMS Source Builder, an RTEMS Tools Project (c) 2012-2013 Chris Johns'
193        print 'Options and arguments:'
194        print '--force                : Force the build to proceed'
195        print '--quiet                : Quiet output (not used)'
196        print '--trace                : Trace the execution'
197        print '--dry-run              : Do everything but actually run the build'
198        print '--warn-all             : Generate warnings'
199        print '--no-clean             : Do not clean up the build tree'
200        print '--always-clean         : Always clean the build tree, even with an error'
201        print '--keep-going           : Do not stop on an error.'
202        print '--regression           : Set --no-install, --keep-going and --always-clean'
203        print '--jobs                 : Run with specified number of jobs, default: num CPUs.'
204        print '--host                 : Set the host triplet'
205        print '--build                : Set the build triplet'
206        print '--target               : Set the target triplet'
207        print '--prefix path          : Tools build prefix, ie where they are installed'
208        print '--topdir path          : Top of the build tree, default is $PWD'
209        print '--configdir path       : Path to the configuration directory, default: ./config'
210        print '--builddir path        : Path to the build directory, default: ./build'
211        print '--sourcedir path       : Path to the source directory, default: ./source'
212        print '--tmppath path         : Path to the temp directory, default: ./tmp'
213        print '--macros file[,[file]  : Macro format files to load after the defaults'
214        print '--log file             : Log file where all build out is written too'
215        print '--url url[,url]        : URL to look for source'
216        print '--no-download          : Disable the source downloader'
217        print '--no-install           : Do not install the packages to the prefix'
218        print '--targetcflags flags   : List of C flags for the target code'
219        print '--targetcxxflags flags : List of C++ flags for the target code'
220        print '--libstdcxxflags flags : List of C++ flags to build the target libstdc++ code'
221        print '--with-<label>         : Add the --with-<label> to the build'
222        print '--without-<label>      : Add the --without-<label> to the build'
223        if self.optargs:
224            for a in self.optargs:
225                print '%-22s : %s' % (a, self.optargs[a])
226        raise error.exit()
227
228    def process(self):
229        arg = 0
230        while arg < len(self.args):
231            a = self.args[arg]
232            if a == '-?':
233                self.help()
234            elif a.startswith('--'):
235                los = a.split('=')
236                lo = los[0]
237                if lo in self._long_opts:
238                    long_opt = self._long_opts[lo]
239                    if len(los) == 1:
240                        if long_opt[2]:
241                            if arg == len(self.args) - 1:
242                                raise error.general('option requires a parameter: %s' % (lo))
243                            arg += 1
244                            value = self.args[arg]
245                        else:
246                            value = None
247                    else:
248                        value = '='.join(los[1:])
249                    long_opt[1](lo, long_opt[0], value)
250            else:
251                self.opts['params'].append(a)
252            arg += 1
253
254    def post_process(self):
255        # Handle the log first.
256        log.default = log.log(self.logfiles())
257        if self.trace():
258            log.tracing = True
259        if self.quiet():
260            log.quiet = True
261        # Must have a host
262        if self.defaults['_host'] == self.defaults['nil']:
263            raise error.general('--host not set')
264        # Must have a host
265        if self.defaults['_build'] == self.defaults['nil']:
266            raise error.general('--build not set')
267        # Manage the regression option
268        if self.opts['regression'] != '0':
269            self.opts['no-install'] = '1'
270            self.defaults['_no_install'] = '1'
271            self.opts['keep-going'] = '1'
272            self.defaults['_keep_going'] = '1'
273            self.opts['always-clean'] = '1'
274            self.defaults['_always_clean'] = '1'
275        # Handle the jobs for make
276        if '_ncpus' not in self.defaults:
277            raise error.general('host number of CPUs not set')
278        ncpus = self.jobs(self.defaults['_ncpus'])
279        if ncpus > 1:
280            self.defaults['_smp_mflags'] = '-j %d' % (ncpus)
281        else:
282            self.defaults['_smp_mflags'] = self.defaults['nil']
283        # Load user macro files
284        um = self.user_macros()
285        if um:
286            checked = path.exists(um)
287            if False in checked:
288                raise error.general('macro file not found: %s' % (um[checked.index(False)]))
289            for m in um:
290                self.defaults.load(m)
291        # Check if the user has a private set of macros to load
292        if 'RSB_MACROS' in os.environ:
293            if path.exists(os.environ['RSB_MACROS']):
294                self.defaults.load(os.environ['RSB_MACROS'])
295        if 'HOME' in os.environ:
296            rsb_macros = path.join(os.environ['HOME'], '.rsb_macros')
297            if path.exists(rsb_macros):
298                self.defaults.load(rsb_macros)
299
300    def sb_git(self):
301        repo = git.repo(self.defaults.expand('%{_sbdir}'), self)
302        if repo.valid():
303            repo_valid = '1'
304            repo_head = repo.head()
305            repo_clean = not repo.dirty()
306            repo_remotes = '%{nil}'
307            remotes = repo.remotes()
308            if 'origin' in remotes:
309                repo_remotes = '%s/origin' % (remotes['origin']['url'])
310            repo_id = repo_head
311            if not repo_clean:
312                repo_id += '-modified'
313            repo_mail = repo.email()
314        else:
315            repo_valid = '0'
316            repo_head = '%{nil}'
317            repo_clean = '%{nil}'
318            repo_remotes = '%{nil}'
319            repo_id = 'no-repo'
320            repo_mail = None
321        self.defaults['_sbgit_valid'] = repo_valid
322        self.defaults['_sbgit_head']  = repo_head
323        self.defaults['_sbgit_clean'] = str(repo_clean)
324        self.defaults['_sbgit_remotes'] = str(repo_remotes)
325        self.defaults['_sbgit_id']    = repo_id
326        if repo_mail is not None:
327            self.defaults['_sbgit_mail'] = repo_mail
328
329    def command(self):
330        return path.join(self.command_path, self.command_name)
331
332    def force(self):
333        return self.opts['force'] != '0'
334
335    def dry_run(self):
336        return self.opts['dry-run'] != '0'
337
338    def set_dry_run(self):
339        self.opts['dry-run'] = '1'
340
341    def quiet(self):
342        return self.opts['quiet'] != '0'
343
344    def trace(self):
345        return self.opts['trace'] != '0'
346
347    def warn_all(self):
348        return self.opts['warn-all'] != '0'
349
350    def keep_going(self):
351        return self.opts['keep-going'] != '0'
352
353    def no_clean(self):
354        return self.opts['no-clean'] != '0'
355
356    def always_clean(self):
357        return self.opts['always-clean'] != '0'
358
359    def no_install(self):
360        return self.opts['no-install'] != '0'
361
362    def user_macros(self):
363        #
364        # Return something even if it does not exist.
365        #
366        if self.opts['macros'] is None:
367            return None
368        um = []
369        configs = self.defaults.expand('%{_configdir}').split(':')
370        for m in self.opts['macros'].split(','):
371            if path.exists(m):
372                um += [m]
373            else:
374                # Get the expanded config macros then check them.
375                cm = path.expand(m, configs)
376                ccm = path.exists(cm)
377                if True in ccm:
378                    # Pick the first found
379                    um += [cm[ccm.index(True)]]
380                else:
381                    um += [m]
382        return um if len(um) else None
383
384    def jobs(self, cpus):
385        cpus = int(cpus)
386        if self.opts['jobs'] == 'none':
387            cpus = 0
388        elif self.opts['jobs'] == 'max':
389            pass
390        elif self.opts['jobs'] == 'half':
391            cpus = cpus / 2
392        else:
393            ok = False
394            try:
395                i = int(self.opts['jobs'])
396                cpus = i
397                ok = True
398            except:
399                pass
400            if not ok:
401                try:
402                    f = float(self.opts['jobs'])
403                    cpus = f * cpus
404                    ok = True
405                except:
406                    pass
407                if not ok:
408                    raise error.internal('bad jobs option: %s' % (self.opts['jobs']))
409        if cpus <= 0:
410            cpu = 1
411        return cpus
412
413    def params(self):
414        return self.opts['params']
415
416    def get_arg(self, arg):
417        if self.optargs is None or arg not in self.optargs:
418            raise error.internal('bad arg: %s' % (arg))
419        for a in self.args:
420            sa = a.split('=')
421            if sa[0].startswith(arg):
422                return sa
423        return None
424
425    def get_config_files(self, config):
426        #
427        # Convert to shell paths and return shell paths.
428        #
429        # @fixme should this use a passed in set of defaults and not
430        #        not the initial set of values ?
431        #
432        config = path.shell(config)
433        if '*' in config or '?' in config:
434            print config
435            configdir = path.dirname(config)
436            configbase = path.basename(config)
437            if len(configbase) == 0:
438                configbase = '*'
439            if not configbase.endswith('.cfg'):
440                configbase = configbase + '.cfg'
441            if len(configdir) == 0:
442                configdir = self.macros.expand(self.defaults['_configdir'])
443            configs = []
444            for cp in configdir.split(':'):
445                hostconfigdir = path.host(cp)
446                for f in glob.glob(os.path.join(hostconfigdir, configbase)):
447                    configs += path.shell(f)
448        else:
449            configs = [config]
450        return configs
451
452    def config_files(self):
453        configs = []
454        for config in self.opts['params']:
455            configs.extend(self.get_config_files(config))
456        return configs
457
458    def logfiles(self):
459        if 'log' in self.opts and self.opts['log'] is not None:
460            return self.opts['log'].split(',')
461        return ['stdout']
462
463    def urls(self):
464        if self.opts['url'] is not None:
465            return self.opts['url'].split(',')
466        return None
467
468    def download_disabled(self):
469        return self.opts['no-download'] != '0'
470
471    def info(self):
472        s = ' Command Line: %s%s' % (' '.join(self.argv), os.linesep)
473        s += ' Python: %s' % (sys.version.replace('\n', ''))
474        return s
475
476    def log_info(self):
477        log.output(self.info())
478
479def load(args, optargs = None, defaults = '%{_sbdir}/defaults.mc'):
480    """
481    Copy the defaults, get the host specific values and merge them overriding
482    any matching defaults, then create an options object to handle the command
483    line merging in any command line overrides. Finally post process the
484    command line.
485    """
486
487    global host_windows
488
489    #
490    # The path to this command.
491    #
492    command_path = path.dirname(args[0])
493    if len(command_path) == 0:
494        command_path = '.'
495
496    #
497    # The command line contains the base defaults object all build objects copy
498    # and modify by loading a configuration.
499    #
500    o = command_line(args,
501                     optargs,
502                     macros.macros(name = defaults,
503                                   sbdir = command_path),
504                     command_path)
505
506    overrides = None
507    if os.name == 'nt':
508        try:
509            import windows
510            overrides = windows.load()
511            host_windows = True
512        except:
513            raise error.general('failed to load Windows host support')
514    elif os.name == 'posix':
515        uname = os.uname()
516        try:
517            if uname[0].startswith('CYGWIN_NT'):
518                import windows
519                overrides = windows.load()
520            elif uname[0] == 'Darwin':
521                import darwin
522                overrides = darwin.load()
523            elif uname[0] == 'FreeBSD':
524                import freebsd
525                overrides = freebsd.load()
526            elif uname[0] == 'NetBSD':
527                import netbsd
528                overrides = netbsd.load()
529            elif uname[0] == 'Linux':
530                import linux
531                overrides = linux.load()
532            elif uname[0] == 'SunOS':
533                import solaris
534                overrides = solaris.load()
535        except:
536            raise error.general('failed to load %s host support' % (uname[0]))
537    else:
538        raise error.general('unsupported host type; please add')
539    if overrides is None:
540        raise error.general('no hosts defaults found; please add')
541    for k in overrides:
542        o.defaults[k] = overrides[k]
543
544    o.sb_git()
545    o.process()
546    o.post_process()
547
548    return o
549
550def run(args):
551    try:
552        _opts = load(args = args, defaults = 'defaults.mc')
553        log.notice('RTEMS Source Builder - Defaults, v%s' % (version.str()))
554        _opts.log_info()
555        log.notice('Options:')
556        log.notice(str(_opts))
557        log.notice('Defaults:')
558        log.notice(str(_opts.defaults))
559    except error.general, gerr:
560        print gerr
561        sys.exit(1)
562    except error.internal, ierr:
563        print ierr
564        sys.exit(1)
565    except error.exit, eerr:
566        pass
567    except KeyboardInterrupt:
568        _notice(opts, 'abort: user terminated')
569        sys.exit(1)
570    sys.exit(0)
571
572if __name__ == '__main__':
573    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.