diff options
author | Chris Johns <chrisj@rtems.org> | 2012-11-06 10:09:40 +1100 |
---|---|---|
committer | Chris Johns <chrisj@rtems.org> | 2012-11-06 10:09:40 +1100 |
commit | c80560d9a485880ff9b6a107fab7097b89d539ac (patch) | |
tree | 1d5d9771b2f59f1a4efb100287e077d558224d8f /source-builder/sb | |
parent | Merge branch 'master' of ssh://kiwi/opt/work/sw/rtems/tb/source-builder (diff) | |
download | rtems-source-builder-c80560d9a485880ff9b6a107fab7097b89d539ac.tar.bz2 |
Move into the source-builder tree.
Diffstat (limited to 'source-builder/sb')
-rw-r--r-- | source-builder/sb/build.py | 483 | ||||
-rw-r--r-- | source-builder/sb/check.py | 157 | ||||
-rw-r--r-- | source-builder/sb/config.py | 873 | ||||
-rw-r--r-- | source-builder/sb/darwin.py | 59 | ||||
-rw-r--r-- | source-builder/sb/defaults.py | 575 | ||||
-rw-r--r-- | source-builder/sb/error.py | 54 | ||||
-rwxr-xr-x | source-builder/sb/execute.py | 363 | ||||
-rw-r--r-- | source-builder/sb/freebsd.py | 67 | ||||
-rw-r--r-- | source-builder/sb/linux.py | 63 | ||||
-rwxr-xr-x | source-builder/sb/log.py | 111 | ||||
-rw-r--r-- | source-builder/sb/path.py | 95 | ||||
-rw-r--r-- | source-builder/sb/setbuilder.py | 250 | ||||
-rw-r--r-- | source-builder/sb/windows.py | 91 |
13 files changed, 3241 insertions, 0 deletions
diff --git a/source-builder/sb/build.py b/source-builder/sb/build.py new file mode 100644 index 0000000..bd1305b --- /dev/null +++ b/source-builder/sb/build.py @@ -0,0 +1,483 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# This code builds a package given a config file. It only builds to be +# installed not to be package unless you run a packager around this. +# + +import getopt +import glob +import os +import shutil +import stat +import sys +import urllib2 +import urlparse + +import check +import config +import defaults +import error +import execute +import log +import path + +# +# Version of Sourcer Builder Build. +# +version = '0.1' + +def _notice(opts, text): + if not opts.quiet() and not log.default.has_stdout(): + print text + log.output(text) + log.flush() + +class script: + """Create and manage a shell script.""" + + def __init__(self, quiet = True): + self.quiet = quiet + self.reset() + + def reset(self): + self.body = [] + self.lc = 0 + + def append(self, text): + if type(text) is str: + text = text.splitlines() + if not self.quiet: + i = 0 + for l in text: + i += 1 + log.output('script:%3d: ' % (self.lc + i) + l) + self.lc += len(text) + self.body.extend(text) + + def write(self, name, check_for_errors = False): + s = None + try: + s = open(path.host(name), 'w') + s.write('\n'.join(self.body)) + s.close() + os.chmod(path.host(name), stat.S_IRWXU | \ + stat.S_IRGRP | stat.S_IXGRP | \ + stat.S_IROTH | stat.S_IXOTH) + except IOError, err: + raise error.general('creating script: ' + name) + except: + if s is not None: + s.close() + raise + if s is not None: + s.close() + +class build: + """Build a package given a config file.""" + + def __init__(self, name, _defaults, opts): + self.opts = opts + _notice(opts, 'building: ' + name) + self.config = config.file(name, _defaults = _defaults, opts = opts) + self.script = script(quiet = opts.quiet()) + + def _output(self, text): + if not self.opts.quiet(): + log.output(text) + + def rmdir(self, rmpath): + self._output('removing: %s' % (path.host(rmpath))) + if not self.opts.dry_run(): + if path.exists(rmpath): + try: + shutil.rmtree(path.host(rmpath)) + except IOError, err: + raise error.error('error removing: %s' % (rmpath)) + except WindowsError, err: + _notice(self.opts, 'warning: cannot remove: %s' % (rmpath)) + + def mkdir(self, mkpath): + self._output('making dir: %s' % (path.host(mkpath))) + if not self.opts.dry_run(): + try: + os.makedirs(path.host(mkpath)) + except IOError, err: + _notice(self.opts, 'warning: cannot make directory: %s' % (mkpath)) + except WindowsError, err: + _notice(self.opts, 'warning: cannot make directory: %s' % (mkpath)) + + def get_file(self, url, local): + if local is None: + raise error.general('source/patch path invalid') + if not path.isdir(path.dirname(local)): + if not self.opts.force(): + raise error.general('source path not found: %s; (--force to create)' \ + % (path.host(path.dirname(local)))) + self.mkdir(path.host(path.dirname(local))) + if not path.exists(local): + # + # Not localy found so we need to download it. Check if a URL has + # been provided on the command line. + # + url_bases = self.opts.urls() + urls = [] + if url_bases is not None: + for base in url_bases: + if base[-1:] != '/': + base += '/' + url_path = urlparse.urlsplit(url)[2] + slash = url_path.rfind('/') + if slash < 0: + url_file = url_path + else: + url_file = url_path[slash + 1:] + urls.append(urlparse.urljoin(base, url_file)) + urls.append(url) + if self.opts.trace(): + print '_url:', ','.join(urls), '->', local + for url in urls: + # + # Hack for GitHub. + # + if url.startswith('https://api.github.com'): + url = urlparse.urljoin(url, self.config.expand('tarball/%{version}')) + _notice(self.opts, 'download: %s -> %s' % (url, path.host(local))) + if not self.opts.dry_run(): + failed = False + _in = None + _out = None + try: + _in = urllib2.urlopen(url) + _out = open(path.host(local), 'wb') + _out.write(_in.read()) + except IOError, err: + msg = 'download: %s: error: %s' % (url, str(err)) + _notice(self.opts, msg) + if path.exists(local): + os.remove(path.host(local)) + failed = True + except: + print >> sys.stderr, msg + if _out is not None: + _out.close() + raise + if _out is not None: + _out.close() + if _in is not None: + del _in + if not failed: + if not path.isfile(local): + raise error.general('source is not a file: %s' % (path.host(local))) + return + if not self.opts.dry_run(): + raise error.general('downloading %s: all paths have failed, giving up' % (url)) + + def parse_url(self, url, pathkey): + # + # Split the source up into the parts we need. + # + source = {} + source['url'] = url + source['path'] = path.dirname(url) + source['file'] = path.basename(url) + source['name'], source['ext'] = path.splitext(source['file']) + # + # Get the file. Checks the local source directory first. + # + source['local'] = None + for p in self.config.define(pathkey).split(':'): + local = path.join(path.abspath(p), source['file']) + if source['local'] is None or path.exists(local): + source['local'] = local + break + # + # Is the file compressed ? + # + esl = source['ext'].split('.') + if esl[-1:][0] == 'gz': + source['compressed'] = '%{__gzip} -dc' + elif esl[-1:][0] == 'bz2': + source['compressed'] = '%{__bzip2} -dc' + elif esl[-1:][0] == 'bz2': + source['compressed'] = '%{__zip} -u' + elif esl[-1:][0] == 'xz': + source['compressed'] = '%{__xz} -dc' + source['script'] = '' + return source + + def source(self, package, source_tag): + # + # Scan the sources found in the config file for the one we are + # after. Infos or tags are lists. + # + sources = package.sources() + url = None + for s in sources: + tag = s[len('source'):] + if tag.isdigit(): + if int(tag) == source_tag: + url = sources[s][0] + break + if url is None: + raise error.general('source tag not found: source%d' % (source_tag)) + source = self.parse_url(url, '_sourcedir') + self.get_file(source['url'], source['local']) + if 'compressed' in source: + source['script'] = source['compressed'] + ' ' + \ + source['local'] + ' | %{__tar_extract} -' + else: + source['script'] = '%{__tar_extract} ' + source['local'] + return source + + def patch(self, package, args): + # + # Scan the patches found in the config file for the one we are + # after. Infos or tags are lists. + # + patches = package.patches() + url = None + for p in patches: + if args[0][1:].lower() == p: + url = patches[p][0] + break + if url is None: + raise error.general('patch tag not found: %s' % (args[0])) + # + # Parse the URL first in the source builder's patch directory. + # + patch = self.parse_url(url, '_patchdir') + # + # If not in the source builder package check the source directory. + # + if not path.exists(patch['local']): + patch = self.parse_url(url, '_sourcedir') + self.get_file(patch['url'], patch['local']) + if 'compressed' in patch: + patch['script'] = patch['compressed'] + ' ' + patch['local'] + else: + patch['script'] = '%{__cat} ' + patch['local'] + patch['script'] += ' | %{__patch} ' + ' '.join(args[1:]) + self.script.append(self.config.expand(patch['script'])) + + def setup(self, package, args): + self._output('prep: %s: %s' % (package.name(), ' '.join(args))) + opts, args = getopt.getopt(args[1:], 'qDcTn:b:a:') + source_tag = 0 + quiet = False + unpack_default_source = True + delete_before_unpack = True + create_dir = False + name = None + unpack_before_chdir = True + for o in opts: + if o[0] == '-q': + quiet = True + elif o[0] == '-D': + delete_before_unpack = False + elif o[0] == '-c': + create_dir = True + elif o[0] == '-T': + unpack_default_source = False + elif o[0] == '-n': + name = o[1] + elif o[0] == '-b': + unpack_before_chdir = True + if not o[1].isdigit(): + raise error.general('setup source tag no a number: %s' % (o[1])) + source_tag = int(o[1]) + elif o[0] == '-a': + unpack_before_chdir = False + source_tag = int(o[1]) + source0 = None + source = self.source(package, source_tag) + if name is None: + if source: + name = source['name'] + else: + name = source0['name'] + self.script.append(self.config.expand('cd %{_builddir}')) + if delete_before_unpack: + self.script.append(self.config.expand('%{__rm} -rf ' + name)) + if create_dir: + self.script.append(self.config.expand('%{__mkdir_p} ' + name)) + # + # If -a? then change directory before unpacking. + # + if not unpack_before_chdir: + self.script.append(self.config.expand('cd ' + name)) + # + # Unpacking the source. Note, treated the same as -a0. + # + if unpack_default_source and source_tag != 0: + source0 = self.source(package, 0) + if source0 is None: + raise error.general('no setup source0 tag found') + self.script.append(self.config.expand(source0['script'])) + self.script.append(self.config.expand(source['script'])) + if unpack_before_chdir: + self.script.append(self.config.expand('cd ' + name)) + self.script.append(self.config.expand('%{__setup_post}')) + if create_dir: + self.script.append(self.config.expand('cd ..')) + + def run(self, command, shell_opts = '', cwd = None): + e = execute.capture_execution(log = log.default, dump = self.opts.quiet()) + cmd = self.config.expand('%{___build_shell} -ex ' + shell_opts + ' ' + command) + self._output('run: ' + cmd) + exit_code, proc, output = e.shell(cmd, cwd = path.host(cwd)) + if exit_code != 0: + raise error.general('shell cmd failed: ' + cmd) + + def builddir(self): + builddir = self.config.abspath('_builddir') + self.rmdir(builddir) + if not self.opts.dry_run(): + self.mkdir(builddir) + + def prep(self, package): + self.script.append('echo "==> %prep:"') + _prep = package.prep() + for l in _prep: + args = l.split() + if args[0] == '%setup': + self.setup(package, args) + elif args[0].startswith('%patch'): + self.patch(package, args) + else: + self.script.append(' '.join(args)) + + def build(self, package): + self.script.append('echo "==> %build:"') + _build = package.build() + for l in _build: + args = l.split() + self.script.append(' '.join(args)) + + def install(self, package): + self.script.append('echo "==> %install:"') + _install = package.install() + for l in _install: + args = l.split() + self.script.append(' '.join(args)) + + def files(self, package): + self.script.append('echo "==> %files:"') + prefixbase = self.opts.prefixbase() + if prefixbase is None: + prefixbase = '' + inpath = path.join('%{buildroot}', prefixbase) + tardir = path.abspath(self.config.expand('%{_tardir}')) + self.script.append(self.config.expand('if test -d %s; then' % (inpath))) + self.script.append(' mkdir -p %s' % tardir) + self.script.append(self.config.expand(' cd ' + inpath)) + tar = path.join(tardir, package.long_name() + '.tar.bz2') + cmd = self.config.expand(' %{__tar} -cf - . ' + '| %{__bzip2} > ' + tar) + self.script.append(cmd) + self.script.append(self.config.expand(' cd %{_builddir}')) + self.script.append('fi') + + def clean(self, package): + self.script.append('echo "==> %clean:"') + _clean = package.clean() + if _clean is not None: + for l in _clean: + args = l.split() + self.script.append(' '.join(args)) + + def cleanup(self): + if not self.opts.no_clean(): + buildroot = self.config.abspath('buildroot') + builddir = self.config.abspath('_builddir') + _notice(self.opts, 'cleanup: %s' % (buildroot)) + self.rmdir(buildroot) + _notice(self.opts, 'cleanup: %s' % (builddir)) + self.rmdir(builddir) + + def make(self): + packages = self.config.packages() + package = packages['main'] + name = package.name() + _notice(self.opts, 'package: %s' % (name)) + self.script.reset() + self.script.append(self.config.expand('%{___build_template}')) + self.script.append('echo "=> ' + name + ':"') + self.prep(package) + self.build(package) + self.install(package) + self.files(package) + if not self.opts.no_clean(): + self.clean(package) + if not self.opts.dry_run(): + self.builddir() + sn = path.join(self.config.expand('%{_builddir}'), 'doit') + self._output('write script: ' + sn) + self.script.write(sn) + _notice(self.opts, 'building: ' + name) + self.run(sn) + + def name(self): + packages = self.config.packages() + package = packages['main'] + return package.name() + +def list_configs(opts, _defaults, ext = '.cfg'): + configs = [] + for cp in opts.expand('%{_configdir}', _defaults).split(':'): + print 'Examining: %s' % (path.host(path.abspath(cp))) + configs += glob.glob(path.host(path.join(cp, '*%s' % (ext)))) + for c in sorted(configs): + config = path.basename(c) + if config.endswith(ext): + config = config[:0 - len(ext)] + print ' ', config + +def run(args): + try: + optargs = { '--list-configs': 'List available configurations' } + opts, _defaults = defaults.load(args, optargs) + log.default = log.log(opts.logfiles()) + _notice(opts, 'Source Builder, Package Builder v%s' % (version)) + if not check.host_setup(opts, _defaults): + if not opts.force(): + raise error.general('host build environment is not set up correctly (use --force to proceed)') + _notice(opts, 'warning: forcing build with known host setup problems') + if opts.get_arg('--list-configs'): + list_configs(opts, _defaults) + else: + for config_file in opts.config_files(): + b = build(config_file, _defaults = _defaults, opts = opts) + b.make() + del b + except error.general, gerr: + print gerr + sys.exit(1) + except error.internal, ierr: + print ierr + sys.exit(1) + except error.exit, eerr: + pass + except KeyboardInterrupt: + _notice(opts, 'user terminated') + sys.exit(1) + sys.exit(0) + +if __name__ == "__main__": + run(sys.argv) diff --git a/source-builder/sb/check.py b/source-builder/sb/check.py new file mode 100644 index 0000000..f973683 --- /dev/null +++ b/source-builder/sb/check.py @@ -0,0 +1,157 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# Check the defaults for a specific host. +# + +import os + +import defaults +import error +import execute +import log +import path + +# +# Version of Sourcer Builder Check. +# +version = '0.1' + +def _notice(opts, text): + if not opts.quiet() and log.default and not log.default.has_stdout(): + print text + log.output(text) + log.flush() + + +def _check_none(_opts, macro, value, constraint): + return True + + +def _check_triplet(_opts, macro, value, constraint): + return True + + +def _check_dir(_opts, macro, value, constraint): + if constraint != 'none' and not path.isdir(value): + if constraint == 'required': + _notice(_opts, 'error: dir: not found: (%s) %s' % (macro, value)) + return False + if _opts.warn_all(): + _notice(_opts, 'warning: dir: not found: (%s) %s' % (macro, value)) + return True + + +def _check_exe(_opts, macro, value, constraint): + + if len(value) == 0 or constraint == 'none': + return True + + orig_value = value + + if path.isabspath(value): + if path.isfile(value): + return True + if os.name == 'nt': + if path.isfile('%s.exe' % (value)): + return True + value = path.basename(value) + absexe = True + else: + absexe = False + + paths = os.environ['PATH'].split(os.pathsep) + + if _check_paths(value, paths): + if absexe: + _notice(_opts, + 'warning: exe: absolute exe found in path: (%s) %s' % (macro, orig_value)) + return True + + if constraint == 'optional': + if _opts.trace(): + _notice(_opts, 'warning: exe: optional exe not found: (%s) %s' % (macro, orig_value)) + return True + + _notice(_opts, 'error: exe: not found: (%s) %s' % (macro, orig_value)) + return False + + +def _check_paths(name, paths): + for p in paths: + exe = path.join(p, name) + if path.isfile(exe): + return True + if os.name == 'nt': + if path.isfile('%s.exe' % (exe)): + return True + return False + +def host_setup(_opts, _defaults): + """ Basic sanity check. All executables and directories must exist.""" + + checks = { 'none': _check_none, + 'triplet': _check_triplet, + 'dir': _check_dir, + 'exe': _check_exe } + + sane = True + + for d in sorted(_defaults.iterkeys()): + try: + (test, constraint, value) = _defaults[d] + except: + raise error.general('invalid default: %s [%r]' % (d, _defaults[d])) + if test != 'none': + value = _opts.expand(value, _defaults) + if test not in checks: + raise error.general('invalid check test: %s [%r]' % (test, _defaults[d])) + ok = checks[test](_opts, d, value, constraint) + if _opts.trace(): + if ok: + tag = ' ' + else: + tag = '*' + _notice(_opts, '%c %15s: %r -> "%s"' % (tag, d, _defaults[d], value)) + if sane and not ok: + sane = False + + return sane + + +def run(): + import sys + try: + _opts, _defaults = defaults.load(args = sys.argv) + if host_setup(_opts, _defaults): + print 'Source Builder environent is ok' + else: + print 'Source Builder environent is not correctly set up' + except error.general, gerr: + print gerr + sys.exit(1) + except error.internal, ierr: + print ierr + sys.exit(1) + sys.exit(0) + + +if __name__ == '__main__': + run() diff --git a/source-builder/sb/config.py b/source-builder/sb/config.py new file mode 100644 index 0000000..a9724dc --- /dev/null +++ b/source-builder/sb/config.py @@ -0,0 +1,873 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# This code is based on a tool I wrote to parse RPM spec files in the RTEMS +# project. This is now a configuration file format that has moved away from the +# spec file format to support the specific needs of cross-compiling GCC. This +# module parses a configuration file into Python data types that can be used by +# other software modules. +# + +import os +import re +import sys + +import defaults +import error +import execute +import log +import path + +class package: + + def __init__(self, name, arch): + self._name = name + self._arch = arch + self.directives = {} + self.infos = {} + + def __str__(self): + + def _dictlist(dl): + s = '' + dll = dl.keys() + dll.sort() + for d in dll: + if d: + s += ' ' + d + ':\n' + for l in dl[d]: + s += ' ' + l + '\n' + return s + + s = '\npackage: ' + self._name + \ + '\n directives:\n' + _dictlist(self.directives) + \ + '\n infos:\n' + _dictlist(self.infos) + + return s + + def directive_extend(self, dir, data): + if dir not in self.directives: + self.directives[dir] = [] + for i in range(0, len(data)): + data[i] = data[i].strip() + self.directives[dir].extend(data) + + def info_append(self, info, data): + if info not in self.infos: + self.infos[info] = [] + self.infos[info].append(data) + + def get_info(self, info): + if not info in self.infos: + raise error.general('no %s in package "%s"' % (info, self.name)) + return self.info + + def version(self): + return self.get_info('Version') + + def extract_info(self, label): + infos = {} + for i in self.infos: + il = i.lower() + if il.startswith(label): + if il == label: + il = label + '0' + elif not il[len(label):].isdigit(): + continue + infos[il] = self.infos[i] + return infos + + def find_info(self, label): + for i in self.infos: + if i.lower() == label: + return self.infos[i] + return None + + def find_directive(self, label): + for d in self.directives: + if d.lower() == label: + return self.directives[d] + return None + + def name(self): + info = self.find_info('name') + if info: + return info[0] + return self._name + + def version(self): + info = self.find_info('version') + if not info: + return None + return info[0] + + def release(self): + info = self.find_info('release') + if not info: + return None + return info[0] + + def buildarch(self): + info = self.find_info('buildarch') + if not info: + return self._arch + return info[0] + + def sources(self): + return self.extract_info('source'); + + def patches(self): + return self.extract_info('patch') + + def prep(self): + return self.find_directive('%prep') + + def build(self): + return self.find_directive('%build') + + def install(self): + return self.find_directive('%install') + + def clean(self): + return self.find_directive('%clean') + + def post(self): + return self.find_directive('%post') + + def include(self): + return self.find_directive('%include') + + def long_name(self): + return self.name() + +class file: + """Parse a config file.""" + + _directive = [ '%description', + '%changelog', + '%prep', + '%build', + '%check', + '%include', + '%install', + '%clean', + '%post', + '%preun', + '%files' ] + + _ignore = [ re.compile('%setup'), + re.compile('%configure'), + re.compile('%source[0-9]*'), + re.compile('%patch[0-9]*') ] + + def __init__(self, name, _defaults, opts): + self.opts = opts + if self.opts.trace(): + print 'config: %s' % (name) + self.configpath = [] + self.wss = re.compile(r'\s+') + self.tags = re.compile(r':+') + self.sf = re.compile(r'%\([^\)]+\)') + self.default_defines = {} + for d in _defaults: + self.default_defines[self._label(d)] = _defaults[d][2] + for arg in self.opts.args: + if arg.startswith('--with-') or arg.startswith('--without-'): + label = arg[2:].lower().replace('-', '_') + self.default_defines[self._label(label)] = label + self.load_depth = 0 + self.load(name) + + def __str__(self): + + def _dict(dd): + s = '' + ddl = dd.keys() + ddl.sort() + for d in ddl: + s += ' ' + d + ': ' + dd[d] + '\n' + return s + + s = 'config: %s' % ('.'.join(self.configpath)) + \ + '\n' + str(self.opts) + \ + '\nlines parsed: %d' % (self.lc) + \ + '\nname: ' + self.name + \ + '\ndefines:\n' + _dict(self.defines) + for _package in self._packages: + s += str(self._packages[_package]) + return s + + def _name_line_msg(self, msg): + return '%s:%d: %s' % (path.basename(self.name), self.lc, msg) + + def _output(self, text): + if not self.opts.quiet(): + log.output(text) + + def _warning(self, msg): + self._output('warning: %s' % (self._name_line_msg(msg))) + + def _error(self, msg): + err = 'error: %s' % (self._name_line_msg(msg)) + print >> sys.stderr, err + self._output(err) + self.in_error = True + if not self.opts.dry_run(): + print >> sys.stderr, 'warning: switched to dry run due to errors' + self.opts.set_dry_run() + + def _label(self, name): + return '%{' + name.lower() + '}' + + def _macro_split(self, s): + '''Split the string (s) up by macros. Only split on the + outter level. Nested levels will need to split with futher calls.''' + trace_me = False + macros = [] + nesting = [] + has_braces = False + c = 0 + while c < len(s): + if trace_me: + print 'ms:', c, '"' + s[c:] + '"', has_braces, len(nesting), nesting + # + # We need to watch for shell type variables or the form '${var}' because + # they can upset the brace matching. + # + if s[c] == '%' or s[c] == '$': + start = s[c] + c += 1 + if c == len(s): + continue + # + # Do we have '%%' or '%(' or '$%' or '$(' or not '${' ? + # + if s[c] == '%' or s[c] == '(' or (start == '$' and s[c] != '{'): + continue + elif not s[c].isspace(): + # + # If this is a shell macro and we are at the outter + # level or is '$var' forget it and move on. + # + if start == '$' and (s[c] != '{' or len(nesting) == 0): + continue + if s[c] == '{': + this_has_braces = True + else: + this_has_braces = False + nesting.append((c - 1, has_braces)) + has_braces = this_has_braces + elif len(nesting) > 0: + if s[c] == '}' or (s[c].isspace() and not has_braces): + # + # Can have '%{?test: something %more}' where the + # nested %more ends with the '}' which also ends + # the outter macro. + # + if not has_braces: + if s[c] == '}': + macro_start, has_braces = nesting[len(nesting) - 1] + nesting = nesting[:-1] + if len(nesting) == 0: + macros.append(s[macro_start:c].strip()) + if len(nesting) > 0: + macro_start, has_braces = nesting[len(nesting) - 1] + nesting = nesting[:-1] + if len(nesting) == 0: + macros.append(s[macro_start:c + 1].strip()) + c += 1 + if trace_me: + print 'ms:', macros + return macros + + def _shell(self, line): + sl = self.sf.findall(line) + if len(sl): + e = execute.capture_execution() + for s in sl: + exit_code, proc, output = e.shell(s[2:-1]) + if exit_code == 0: + line = line.replace(s, output) + else: + raise error.general('shell macro failed: %s: %s' % (s, output)) + return line + + def _expand(self, s): + expanded = True + while expanded: + expanded = False + ms = self._macro_split(s) + for m in ms: + mn = m + # + # A macro can be '%{macro}' or '%macro'. Turn the later into + # the former. + # + show_warning = True + if mn[1] != '{': + for r in self._ignore: + if r.match(mn) is not None: + mn = None + break + else: + mn = self._label(mn[1:]) + show_warning = False + elif m.startswith('%{expand'): + colon = m.find(':') + if colon < 8: + self._warning('malformed expand macro, no colon found') + else: + e = self._expand(m[colon + 1:-1].strip()) + s = s.replace(m, e) + expanded = True + mn = None + elif m.startswith('%{with '): + # + # Change the ' ' to '_' because the macros have no spaces. + # + n = self._label('with_' + m[7:-1].strip()) + if n in self.defines: + s = s.replace(m, '1') + else: + s = s.replace(m, '0') + expanded = True + mn = None + elif m.startswith('%{echo'): + mn = None + elif m.startswith('%{defined'): + n = self._label(m[9:-1].strip()) + if n in self.defines: + s = s.replace(m, '1') + else: + s = s.replace(m, '0') + expanded = True + mn = None + elif m.startswith('%{?') or m.startswith('%{!?'): + if m[2] == '!': + start = 4 + else: + start = 3 + colon = m[start:].find(':') + if colon < 0: + if not m.endswith('}'): + self._warning("malform conditional macro '%s'" % (m)) + mn = None + else: + mn = self._label(m[start:-1]) + else: + mn = self._label(m[start:start + colon]) + if mn: + if m.startswith('%{?'): + if mn in self.defines: + if colon >= 0: + s = s.replace(m, m[start + colon + 1:-1]) + expanded = True + mn = None + else: + mn = '%{nil}' + else: + if mn not in self.defines: + if colon >= 0: + s = s.replace(m, m[start + colon + 1:-1]) + expanded = True + mn = None + else: + mn = '%{nil}' + if mn: + if mn.lower() in self.defines: + s = s.replace(m, self.defines[mn.lower()]) + expanded = True + elif show_warning: + self._error("macro '%s' not found" % (mn)) + return self._shell(s) + + def _define(self, config, ls): + if len(ls) <= 1: + self._warning('invalid macro definition') + else: + d = self._label(ls[1]) + if (d not in self.defines) or \ + (d in self.defines and len(self.defines[d]) == 0): + if len(ls) == 2: + self.defines[d] = '1' + else: + self.defines[d] = ls[2].strip() + else: + if self.opts.warn_all(): + self._warning("macro '%s' already defined" % (d)) + + def _undefine(self, config, ls): + if len(ls) <= 1: + self._warning('invalid macro definition') + else: + mn = self._label(ls[1]) + if mn in self.defines: + self._error("macro '%s' not defined" % (mn)) + del self.defines[mn] + + def _ifs(self, config, ls, label, iftrue, isvalid): + text = [] + in_iftrue = True + while True: + if isvalid and \ + ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)): + this_isvalid = True + else: + this_isvalid = False + r = self._parse(config, roc = True, isvalid = this_isvalid) + if r[0] == 'control': + if r[1] == '%end': + self._error(label + ' without %endif') + raise error.general('terminating build') + if r[1] == '%endif': + return text + if r[1] == '%else': + in_iftrue = False + elif r[0] == 'data': + if this_isvalid: + text.extend(r[1]) + + def _if(self, config, ls, isvalid, invert = False): + + def add(x, y): + return x + ' ' + str(y) + + def check_bool(value): + if value.isdigit(): + if int(value) == 0: + istrue = False + else: + istrue = True + else: + istrue = None + return istrue + + istrue = False + if isvalid: + if len(ls) == 2: + s = ls[1] + else: + s = (ls[1] + ' ' + ls[2]) + ifls = s.split() + if len(ifls) == 1: + # + # Check if '%if %{x} == %{nil}' has both parts as nothing + # which means '%if ==' is always True and '%if !=' is always false. + # + if ifls[0] == '==': + istrue = True + elif ifls[0] == '==': + istrue = False + else: + istrue = check_bool(ifls[0]) + if istrue == None: + self._error('invalid if bool value: ' + reduce(add, ls, '')) + istrue = False + elif len(ifls) == 2: + if ifls[0] == '!': + istrue = check_bool(ifls[1]) + if istrue == None: + self._error('invalid if bool value: ' + reduce(add, ls, '')) + istrue = False + else: + istrue = not istrue + else: + # + # Check is something is being checked against empty, + # ie '%if %{x} == %{nil}' + # The logic is 'something == nothing' is False and + # 'something != nothing' is True. + # + if ifls[1] == '==': + istrue = False + elif ifls[1] == '!=': + istrue = True + else: + self._error('invalid if bool operator: ' + reduce(add, ls, '')) + elif len(ifls) == 3: + if ifls[1] == '==': + if ifls[0] == ifls[2]: + istrue = True + else: + istrue = False + elif ifls[1] == '!=' or ifls[1] == '=!': + if ifls[0] != ifls[2]: + istrue = True + else: + istrue = False + elif ifls[1] == '>': + if ifls[0] > ifls[2]: + istrue = True + else: + istrue = False + elif ifls[1] == '>=' or ifls[1] == '=>': + if ifls[0] >= ifls[2]: + istrue = True + else: + istrue = False + elif ifls[1] == '<=' or ifls[1] == '=<': + if ifls[0] <= ifls[2]: + istrue = True + else: + istrue = False + elif ifls[1] == '<': + if ifls[0] < ifls[2]: + istrue = True + else: + istrue = False + else: + self._error('invalid %if operator: ' + reduce(add, ls, '')) + else: + self._error('malformed if: ' + reduce(add, ls, '')) + if invert: + istrue = not istrue + if self.opts.trace(): + print '_if: ', ifls, istrue + return self._ifs(config, ls, '%if', istrue, isvalid) + + def _ifos(self, config, ls, isvalid): + isos = False + if isvalid: + os = self.define('_os') + if ls[0].find(os) >= 0 or ls[1].find(os) >= 0: + isos = True + else: + isos = False + return self._ifs(config, ls, '%ifos', isos, isvalid) + + def _ifarch(self, config, positive, ls, isvalid): + isarch = False + if isvalid: + arch = self.define('_arch') + if ls[0].find(arch) >= 0 or ls[1].find(arch) >= 0: + isarch = True + else: + isarch = False + if not positive: + isarch = not isarch + return self._ifs(config, ls, '%ifarch', isarch, isvalid) + + def _parse(self, config, roc = False, isvalid = True): + # roc = return on control + + def _clean(line): + line = line[0:-1] + b = line.find('#') + if b >= 0: + line = line[1:b] + return line.strip() + + # + # Need to add code to count matching '{' and '}' and if they + # do not match get the next line and add to the string until + # they match. This closes an opening '{' that is on another + # line. + # + + for l in config: + self.lc += 1 + l = _clean(l) + if len(l) == 0: + continue + if self.opts.trace(): + print '%03d: %d %s' % (self.lc, isvalid, l) + if isvalid: + l = self._expand(l) + if len(l) == 0: + continue + if l[0] == '%': + ls = self.wss.split(l, 2) + if ls[0] == '%package': + if isvalid: + if ls[1] == '-n': + name = ls[2] + else: + name = self.name + '-' + ls[1] + return ('package', name) + elif ls[0] == '%error': + if isvalid: + return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))]) + elif ls[0] == '%warning': + if isvalid: + return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))]) + elif ls[0] == '%define' or ls[0] == '%global': + if isvalid: + self._define(config, ls) + elif ls[0] == '%undefine': + if isvalid: + self._undefine(config, ls) + elif ls[0] == '%if': + d = self._if(config, ls, isvalid) + if len(d): + return ('data', d) + elif ls[0] == '%ifn': + d = self._if(config, ls, isvalid, True) + if len(d): + return ('data', d) + elif ls[0] == '%ifos': + d = self._ifos(config, ls, isvalid) + if len(d): + return ('data', d) + elif ls[0] == '%ifarch': + d = self._ifarch(config, True, ls, isvalid) + if len(d): + return ('data', d) + elif ls[0] == '%ifnarch': + d = self._ifarch(config, False, ls, isvalid) + if len(d): + return ('data', d) + elif ls[0] == '%endif': + if roc: + return ('control', '%endif') + self._warning("unexpected '" + ls[0] + "'") + elif ls[0] == '%else': + if roc: + return ('control', '%else') + self._warning("unexpected '" + ls[0] + "'") + elif ls[0].startswith('%defattr'): + return ('data', [l]) + elif ls[0] == '%bcond_with': + if isvalid: + # + # Check if already defined. Would be by the command line or + # even a host specific default. + # + if self._label('with_' + ls[1]) not in self.defines: + self._define(config, (ls[0], 'without_' + ls[1])) + elif ls[0] == '%bcond_without': + if isvalid: + if self._label('without_' + ls[1]) not in self.defines: + self._define(config, (ls[0], 'with_' + ls[1])) + else: + for r in self._ignore: + if r.match(ls[0]) is not None: + return ('data', [l]) + if isvalid: + for d in self._directive: + if ls[0].strip() == d: + return ('directive', ls[0].strip(), ls[1:]) + self._warning("unknown directive: '" + ls[0] + "'") + return ('data', [l]) + else: + return ('data', [l]) + return ('control', '%end') + + def _set_package(self, _package): + if self.package == 'main' and \ + self._packages[self.package].name() != None: + if self._packages[self.package].name() == _package: + return + if _package not in self._packages: + self._packages[_package] = package(_package, + self.define('%{_arch}')) + self.package = _package + + def _directive_extend(self, dir, data): + self._packages[self.package].directive_extend(dir, data) + + def _info_append(self, info, data): + self._packages[self.package].info_append(info, data) + + def load(self, name): + + if self.load_depth == 0: + self.in_error = False + self.lc = 0 + self.name = name + self.defines = self.default_defines + self.conditionals = {} + self._packages = {} + self.package = 'main' + self._packages[self.package] = package(self.package, + self.define('%{_arch}')) + + self.load_depth += 1 + + save_name = self.name + save_lc = self.lc + + self.name = name + self.lc = 0 + + # + # Locate the config file. Expand any macors then Add the + # extension. Check if the file exists, therefore directly + # referenced. If not see if the file contains ':' or the path + # separator. If it does split the path else use the standard config dir + # path in the defaults. + # + + exname = self.expand(name) + + if exname.endswith('.cfg'): + configname = exname + else: + configname = '%s.cfg' % (exname) + + cfgname = path.basename(configname) + + if not path.exists(configname): + if ':' in configname: + configdirs = path.dirname(configname).split(':') + else: + configdirs = self.define('_configdir').split(':') + for cp in configdirs: + configname = path.join(path.abspath(cp), cfgname) + if path.exists(configname): + break + configname = None + if configname is None: + raise error.general('no config file found: %s' % (cfgname)) + + try: + if self.opts.trace(): + print '_open: %s' % (path.host(configname)) + config = open(path.host(configname), 'r') + except IOError, err: + raise error.general('error opening config file: %s' % (path.host(configname))) + self.configpath += [configname] + + try: + dir = None + data = [] + while True: + r = self._parse(config) + if r[0] == 'package': + self._set_package(r[1]) + dir = None + elif r[0] == 'control': + if r[1] == '%end': + break + self._warning("unexpected '" + r[1] + "'") + elif r[0] == 'directive': + new_data = [] + if r[1] == '%description': + new_data = [' '.join(r[2])] + elif r[1] == '%include': + self.load(r[2][0]) + continue + else: + if len(r[2]) == 0: + _package = 'main' + elif len(r[2]) == 1: + _package = r[2][0] + else: + if r[2][0].strip() != '-n': + self._warning("unknown directive option: '%s'" % (' '.join(r[2]))) + _package = r[2][1].strip() + self._set_package(_package) + if dir and dir != r[1]: + self._directive_extend(dir, data) + dir = r[1] + data = new_data + elif r[0] == 'data': + for l in r[1]: + l = self._expand(l) + if l.startswith('%error'): + raise error.general('config error: %s' % (l[7:])) + elif l.startswith('%warning'): + print >> sys.stderr, 'warning: %s' % (l[9:]) + self._warning(l[9:]) + if not dir: + ls = self.tags.split(l, 1) + if self.opts.trace(): + print '_tag: ', l, ls + if len(ls) > 1: + i = ls[0] + self._info_append(i, ls[1].strip()) + # It seems like the info's also appear as + # defines or can be accessed via macros. + if ls[0][len(ls[0]) - 1] == ':': + ls[0] = ls[0][:-1] + ls[0] = ls[0].lower() + self._define(None, ('', ls[0], ls[1])) + else: + self._warning("invalid format: '" + l[:-1] + "'") + else: + data.append(l) + else: + self._error("invalid parse state: '" + r[0] + "'") + if dir is not None: + self._directive_extend(dir, data) + except: + config.close() + raise + + config.close() + + self.name = save_name + self.lc = save_lc + + self.load_depth -= 1 + + def define(self, name): + if name.lower() in self.defines: + d = self.defines[name.lower()] + else: + n = self._label(name) + if n in self.defines: + d = self.defines[n] + else: + raise error.general('macro "' + name + '" not found') + return self._expand(d) + + def set_define(self, name, value): + self.defines[name.lower()] = value + + def expand(self, line): + return self._expand(line) + + def directive(self, _package, name): + if _package not in self._packages: + raise error.general('package "' + _package + '" not found') + if name not in self._packages[_package].directives: + raise error.general('directive "' + name + \ + '" not found in package "' + _package + '"') + return self._packages[_package].directives[name] + + def abspath(self, rpath): + return path.abspath(self.define(rpath)) + + def packages(self): + return self._packages + +def run(): + import sys + try: + opts, _defaults = defaults.load(sys.argv) + if opts.trace(): + print 'config: count %d' % (len(opts.config_files())) + for config_file in opts.config_files(): + s = file(config_file, _defaults = _defaults, opts = opts) + print s + del s + except error.general, gerr: + print gerr + sys.exit(1) + except error.internal, ierr: + print ierr + sys.exit(1) + except KeyboardInterrupt: + print 'user terminated' + sys.exit(1) + sys.exit(0) + +if __name__ == "__main__": + run() diff --git a/source-builder/sb/darwin.py b/source-builder/sb/darwin.py new file mode 100644 index 0000000..5f8ac7f --- /dev/null +++ b/source-builder/sb/darwin.py @@ -0,0 +1,59 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# This code is based on what ever doco about spec files I could find and +# RTEMS project's spec files. +# + +import pprint +import os + +import execute + +def load(): + uname = os.uname() + sysctl = '/usr/sbin/sysctl ' + e = execute.capture_execution() + exit_code, proc, output = e.shell(sysctl + 'hw.ncpu') + if exit_code == 0: + smp_mflags = '-j' + output.split(' ')[1].strip() + else: + smp_mflags = '' + defines = { + '_os': ('none', 'none', 'darwin'), + '_host': ('triplet', 'required', uname[4] + '-apple-darwin' + uname[2]), + '_host_vendor': ('none', 'none', 'apple'), + '_host_os': ('none', 'none', 'darwin'), + '_host_cpu': ('none', 'none', uname[4]), + '_host_alias': ('none', 'none', '%{nil}'), + '_host_arch': ('none', 'none', uname[4]), + '_usr': ('dir', 'optional', '/usr/local'), + '_var': ('dir', 'optional', '/usr/local/var'), + '_prefix': ('dir', 'optional', '%{_usr}'), + 'optflags': ('none', 'none', '-O2'), + '_smp_mflags': ('none', 'none', smp_mflags), + '__ldconfig': ('exe', 'none', ''), + '__xz': ('exe', 'required', '%{_usr}/bin/xz'), + 'with_zlib': ('none', 'none', '--with-zlib=no') + } + return defines + +if __name__ == '__main__': + pprint.pprint(load()) diff --git a/source-builder/sb/defaults.py b/source-builder/sb/defaults.py new file mode 100644 index 0000000..09b0922 --- /dev/null +++ b/source-builder/sb/defaults.py @@ -0,0 +1,575 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# Determine the defaults and load the specific file. +# + +import glob +import pprint +import re +import os + +import error +import execute +import path + +basepath = 'sb' + +# +# All paths in defaults must be Unix format. Do not store any Windows format +# paths in the defaults. +# +# Every entry must describe the type of checking a host must pass. +# + +defaults = { +# Nothing +'nil': ('none', 'none', ''), + +# Set to invalid values. +'_bset': ('none', 'none', ''), +'name': ('none', 'none', ''), +'version': ('none', 'none', ''), +'release': ('none', 'none', ''), + +# GNU triples needed to build packages +'_host': ('triplet', 'required', ''), +'_build': ('triplet', 'required', '%{_host}'), +'_target': ('none', 'optional', ''), + +# Paths +'_host_platform': ('none', 'none', '%{_host_cpu}-%{_host_vendor}-%{_host_os}%{?_gnu}'), +'_build': ('none', 'none', '%{_host}'), +'_arch': ('none', 'none', '%{_host_arch}'), +'_sbdir': ('none', 'none', ''), +'_topdir': ('dir', 'required', path.shell(os.getcwd())), +'_configdir': ('dir', 'optional', '%{_topdir}/config:%{_sbdir}/config'), +'_tardir': ('dir', 'optional', '%{_topdir}/tar'), +'_sourcedir': ('dir', 'optional', '%{_topdir}/sources'), +'_patchdir': ('dir', 'optional', '%{_topdir}/patches:%{_sbdir}/patches'), +'_builddir': ('dir', 'optional', '%{_topdir}/build/%{name}-%{version}-%{release}'), +'_docdir': ('dir', 'none', '%{_defaultdocdir}'), +'_tmppath': ('dir', 'none', '%{_topdir}/build/tmp'), +'_tmproot': ('dir', 'none', '%{_tmppath}/source-build-%(%{__id_u} -n)/%{_bset}'), +'buildroot:': ('dir', 'none', '%{_tmppath}/%{name}-root-%(%{__id_u} -n)'), +'_datadir': ('dir', 'none', '%{_prefix}/share'), +'_defaultdocdir': ('dir', 'none', '%{_prefix}/share/doc'), +'_exeext': ('none', 'none', ''), +'_exec_prefix': ('dir', 'none', '%{_prefix}'), +'_bindir': ('dir', 'none', '%{_exec_prefix}/bin'), +'_sbindir': ('dir', 'none', '%{_exec_prefix}/sbin'), +'_libexecdir': ('dir', 'none', '%{_exec_prefix}/libexec'), +'_datarootdir': ('dir', 'none', '%{_prefix}/share'), +'_datadir': ('dir', 'none', '%{_datarootdir}'), +'_sysconfdir': ('dir', 'none', '%{_prefix}/etc'), +'_sharedstatedir': ('dir', 'none', '%{_prefix}/com'), +'_localstatedir': ('dir', 'none', '%{prefix}/var'), +'_includedir': ('dir', 'none', '%{_prefix}/include'), +'_lib': ('dir', 'none', 'lib'), +'_libdir': ('dir', 'none', '%{_exec_prefix}/%{_lib}'), +'_libexecdir': ('dir', 'none', '%{_exec_prefix}/libexec'), +'_mandir': ('dir', 'none', '%{_datarootdir}/man'), +'_infodir': ('dir', 'none', '%{_datarootdir}/info'), +'_localedir': ('dir', 'none', '%{_datarootdir}/locale'), +'_localedir': ('dir', 'none', '%{_datadir}/locale'), +'_localstatedir': ('dir', 'none', '%{_prefix}/var'), +'_prefix': ('dir', 'none', '%{_usr}'), +'_usr': ('dir', 'none', '/usr/local'), +'_usrsrc': ('dir', 'none', '%{_usr}/src'), +'_var': ('dir', 'none', '/usr/local/var'), +'_varrun': ('dir', 'none', '%{_var}/run'), + +# Defaults, override in platform specific modules. +'___setup_shell': ('exe', 'required', '/bin/sh'), +'__aclocal': ('exe', 'optional', 'aclocal'), +'__ar': ('exe', 'required', 'ar'), +'__arch_install_post': ('exe', 'none', '%{nil}'), +'__as': ('exe', 'required', 'as'), +'__autoconf': ('exe', 'required', 'autoconf'), +'__autoheader': ('exe', 'required', 'autoheader'), +'__automake': ('exe', 'required', 'automake'), +'__awk': ('exe', 'required', 'awk'), +'__bash': ('exe', 'optional', '/bin/bash'), +'__bzip2': ('exe', 'required', '/usr/bin/bzip2'), +'__cat': ('exe', 'required', '/bin/cat'), +'__cc': ('exe', 'required', '/usr/bin/gcc'), +'__chgrp': ('exe', 'required', '/usr/bin/chgrp'), +'__chmod': ('exe', 'required', '/bin/chmod'), +'__chown': ('exe', 'required', '/usr/sbin/chown'), +'__cp': ('exe', 'required', '/bin/cp'), +'__cpp': ('exe', 'none', '%{__cc} -E'), +'__cxx': ('exe', 'required', '/usr/bin/g++'), +'__grep': ('exe', 'required', '/usr/bin/grep'), +'__gzip': ('exe', 'required', '/usr/bin/gzip'), +'__id': ('exe', 'required', '/usr/bin/id'), +'__id_u': ('exe', 'none', '%{__id} -u'), +'__install': ('exe', 'required', '/usr/bin/install'), +'__install_info': ('exe', 'optional', '/usr/bin/install-info'), +'__ld': ('exe', 'required', '/usr/bin/ld'), +'__ldconfig': ('exe', 'required', '/sbin/ldconfig'), +'__ln_s': ('exe', 'none', 'ln -s'), +'__make': ('exe', 'required', 'make'), +'__mkdir': ('exe', 'required', '/bin/mkdir'), +'__mkdir_p': ('exe', 'none', '/bin/mkdir -p'), +'__mv': ('exe', 'required', '/bin/mv'), +'__nm': ('exe', 'required', '/usr/bin/nm'), +'__objcopy': ('exe', 'optional', '/usr/bin//objcopy'), +'__objdump': ('exe', 'optional', '/usr/bin/objdump'), +'__patch': ('exe', 'required', '/usr/bin/patch'), +'__perl': ('exe', 'optional', 'perl'), +'__ranlib': ('exe', 'required', 'ranlib'), +'__rm': ('exe', 'required', '/bin/rm'), +'__sed': ('exe', 'required', '/usr/bin/sed'), +'__setup_post': ('exe', 'none', '%{__chmod} -R a+rX,g-w,o-w .'), +'__sh': ('exe', 'required', '/bin/sh'), +'__tar': ('exe', 'required', '/usr/bin/tar'), +'__tar_extract': ('exe', 'none', '%{__tar} -xvvf'), +'__unzip': ('exe', 'required', '/usr/bin/unzip'), +'__xz': ('exe', 'required', '/usr/bin/xz'), + +# Shell Build Settings. +'___build_args': ('none', 'none', '-e'), +'___build_cmd': ('none', 'none', '%{?_sudo:%{_sudo} }%{?_remsh:%{_remsh} %{_remhost} }%{?_remsudo:%{_remsudo} }%{?_remchroot:%{_remchroot} %{_remroot} }%{___build_shell} %{___build_args}'), +'___build_post': ('none', 'none', 'exit 0'), + +# Prebuild set up script. +'___build_pre': ('none', 'none', '''# ___build_pre in as set up in defaults.py +# Directories +SB_SOURCE_DIR="%{_sourcedir}" +SB_BUILD_DIR="%{_builddir}" +SB_OPT_FLAGS="%{optflags}" +SB_ARCH="%{_arch}" +SB_OS="%{_os}" +export SB_SOURCE_DIR SB_BUILD_DIR SB_OPT_FLAGS SB_ARCH SB_OS +# Documentation +SB_DOC_DIR="%{_docdir}" +export SB_DOC_DIR +# Packages +SB_PACKAGE_NAME="%{name}" +SB_PACKAGE_VERSION="%{version}" +SB_PACKAGE_RELEASE="%{release}" +export SBPACKAGE_NAME SB_PACKAGE_VERSION SB_PACKAGE_RELEASE +# Build root directory +%{?buildroot:SB_BUILD_ROOT="%{buildroot}"} +export SB_BUILD_ROOT +# The compiler flags +%{?_targetcflags:CFLAGS_FOR_TARGET="%{_targetcflags}"} +%{?_targetcxxflags:CXXFLAGS_FOR_TARGET="%{_targetcxxflags}"} +export CFLAGS_FOR_TARGET +# Default environment set up. +LANG=C +export LANG +unset DISPLAY || : +umask 022 +cd "%{_builddir}"'''), +'___build_shell': ('none', 'none', '%{?_buildshell:%{_buildshell}}%{!?_buildshell:/bin/sh}'), +'___build_template': ('none', 'none', '''#!%{___build_shell} +%{___build_pre} +%{nil}'''), + +# Configure command +'configure': ('none', 'none', ''' +CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ; +CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ; +FFLAGS="${FFLAGS:-%optflags}" ; export FFLAGS ; +./configure --build=%{_build} --host=%{_host} \ + --target=%{_target_platform} \ + --program-prefix=%{?_program_prefix} \ + --prefix=%{_prefix} \ + --exec-prefix=%{_exec_prefix} \ + --bindir=%{_bindir} \ + --sbindir=%{_sbindir} \ + --sysconfdir=%{_sysconfdir} \ + --datadir=%{_datadir} \ + --includedir=%{_includedir} \ + --libdir=%{_libdir} \ + --libexecdir=%{_libexecdir} \ + --localstatedir=%{_localstatedir} \ + --sharedstatedir=%{_sharedstatedir} \ + --mandir=%{_mandir} \ + --infodir=%{_infodir}''') +} + +class command_line: + """Process the command line in a common way for all Tool Builder commands.""" + + _defaults = { 'params' : [], + 'warn-all' : '0', + 'quiet' : '0', + 'force' : '0', + 'trace' : '0', + 'dry-run' : '0', + 'no-clean' : '0', + 'no-smp' : '0', + 'rebuild' : '0' } + + # + # The define and if it is a path and needs conversion. + # + _long_opts = { '--prefix' : ('_prefix', True), + '--prefixbase' : ('_prefixbase', True), + '--topdir' : ('_topdir', True), + '--configdir' : ('_configdir', True), + '--builddir' : ('_builddir', True), + '--sourcedir' : ('_sourcedir', True), + '--tmppath' : ('_tmppath', True), + '--log' : ('_logfile', False), + '--url' : ('_url_base', False), + '--targetcflags' : ('_targetcflags', False), + '--targetcxxflags' : ('_targetcxxflags', False), + '--libstdcxxflags' : ('_libstdcxxflags', False) } + + _long_true_opts = { '--force' : '_force', + '--trace' : '_trace', + '--dry-run' : '_dry_run', + '--warn-all' : '_warn_all', + '--no-clean' : '_no_clean', + '--no-smp' : '_no_smp', + '--rebuild' : '_rebuild' } + + _target_triplets = { '--host' : '_host', + '--build' : '_build', + '--target' : '_target' } + + def _help(self): + print '%s: [options] [args]' % (self.command_name) + print 'Source Builder, an RTEMS Tools Project (c) 2012 Chris Johns' + print 'Options and arguments:' + print '--force : Create directories that are not present' + print '--trace : Trace the execution (not current used)' + print '--dry-run : Do everything but actually run the build' + print '--warn-all : Generate warnings' + print '--no-clean : Do not clean up the build tree' + print '--no-smp : Run with 1 job and not as many as CPUs' + print '--rebuild : Rebuild (not used)' + print '--host : Set the host triplet' + print '--build : Set the build triplet' + print '--target : Set the target triplet' + print '--prefix path : Tools build prefix, ie where they are installed' + print '--prefixbase path : ' + print '--topdir path : Top of the build tree, default is $PWD' + print '--configdir path : Path to the configuration directory, default: ./config' + print '--builddir path : Path to the build directory, default: ./build' + print '--sourcedir path : Path to the source directory, default: ./source' + print '--tmppath path : Path to the temp directory, default: ./tmp' + print '--log file : Log file where all build out is written too' + print '--url url : URL to look for source' + print '--targetcflags flags : List of C flags for the target code' + print '--targetcxxflags flags : List of C++ flags for the target code' + print '--libstdcxxflags flags : List of C++ flags to build the target libstdc++ code' + print '--with-<label> : Add the --with-<label> to the build' + print '--without-<label> : Add the --without-<label> to the build' + if self.optargs: + for a in self.optargs: + print '%-22s : %s' % (a, self.optargs[a]) + raise error.exit() + + def __init__(self, argv, optargs): + self.command_path = path.dirname(argv[0]) + if len(self.command_path) == 0: + self.command_path = '.' + self.command_name = path.basename(argv[0]) + self.args = argv[1:] + self.optargs = optargs + self.defaults = {} + for to in command_line._long_true_opts: + self.defaults[command_line._long_true_opts[to]] = ('none', 'none', '0') + self.defaults['_sbdir'] = ('dir', 'required', path.shell(self.command_path)) + self._process() + + def __str__(self): + def _dict(dd): + s = '' + ddl = dd.keys() + ddl.sort() + for d in ddl: + s += ' ' + d + ': ' + str(dd[d]) + '\n' + return s + + s = 'command: ' + self.command() + \ + '\nargs: ' + str(self.args) + \ + '\nopts:\n' + _dict(self.opts) + + return s + + def _process(self): + + def _process_lopt(opt, arg, long_opts, args, values = False): + for lo in long_opts: + if values and opt.startswith(lo): + equals = opt.find('=') + if equals < 0: + if arg == len(args) - 1: + raise error.general('missing option value: ' + lo) + arg += 1 + value = args[arg] + else: + value = opt[equals + 1:] + if type(long_opts[lo]) is tuple: + if long_opts[lo][1]: + value = path.shell(value) + macro = long_opts[lo][0] + else: + macro = long_opts[lo] + return lo, macro, value, arg + elif opt == lo: + return lo, long_opts[lo], True, arg + return None, None, None, arg + + self.opts = command_line._defaults + i = 0 + while i < len(self.args): + a = self.args[i] + if a.startswith('-'): + if a.startswith('--'): + if a.startswith('--warn-all'): + self.opts['warn-all'] = True + elif a == '--help': + self._help() + else: + lo, macro, value, i = _process_lopt(a, i, + command_line._long_true_opts, + self.args) + if lo: + self.defaults[macro] = ('none', 'none', '1') + self.opts[lo[2:]] = '1' + else: + lo, macro, value, i = _process_lopt(a, i, + command_line._long_opts, + self.args, True) + if lo: + self.defaults[macro] = ('none', 'none', value) + self.opts[lo[2:]] = value + else: + # + # The target triplet is 'cpu-vendor-os'. + # + lo, macro, value, i = _process_lopt(a, i, + command_line._target_triplets, + self.args, True) + if lo: + # + # This is a target triplet. Run it past config.sub to make + # make sure it is ok. + # + e = execute.capture_execution() + config_sub = path.join(self.command_path, + basepath, 'config.sub') + exit_code, proc, output = e.shell(config_sub + ' ' + value) + if exit_code == 0: + value = output + self.defaults[macro] = ('triplet', 'none', value) + self.opts[lo[2:]] = value + _arch = macro + '_cpu' + _vendor = macro + '_vendor' + _os = macro + '_os' + _arch_value = '' + _vendor_value = '' + _os_value = '' + dash = value.find('-') + if dash >= 0: + _arch_value = value[:dash] + value = value[dash + 1:] + dash = value.find('-') + if dash >= 0: + _vendor_value = value[:dash] + value = value[dash + 1:] + if len(value): + _os_value = value + self.defaults[_arch] = ('none', 'none', _arch_value) + self.defaults[_vendor] = ('none', 'none', _vendor_value) + self.defaults[_os] = ('none', 'none', _os_value) + if not lo and a not in self.optargs: + raise error.general('invalid argument (try --help): %s' % (a)) + else: + if a == '-f': + self.opts['force'] = '1' + elif a == '-n': + self.opts['dry-run'] = '1' + elif a == '-q': + self.opts['quiet'] = '1' + elif a == '-?': + self._help() + else: + raise error.general('invalid argument (try --help): %s' % (a)) + else: + self.opts['params'].append(a) + i += 1 + + def _post_process(self, _defaults): + if self.no_smp(): + _defaults['_smp_mflags'] = ('none', 'none', _defaults['nil'][2]) + if _defaults['_host'][2] == _defaults['nil'][2]: + raise error.general('host not set') + return _defaults + + def expand(self, s, _defaults): + """Simple basic expander of config file macros.""" + mf = re.compile(r'%{[^}]+}') + expanded = True + while expanded: + expanded = False + for m in mf.findall(s): + name = m[2:-1] + if name in _defaults: + s = s.replace(m, _defaults[name][2]) + expanded = True + else: + raise error.general('cannot process default macro: ' + m) + return s + + def command(self): + return path.join(self.command_path, self.command_name) + + def force(self): + return self.opts['force'] != '0' + + def dry_run(self): + return self.opts['dry-run'] != '0' + + def set_dry_run(self): + self.opts['dry-run'] = '1' + + def quiet(self): + return self.opts['quiet'] != '0' + + def trace(self): + return self.opts['trace'] != '0' + + def warn_all(self): + return self.opts['warn-all'] != '0' + + def no_clean(self): + return self.opts['no-clean'] != '0' + + def no_smp(self): + return self.opts['no-smp'] != '0' + + def rebuild(self): + return self.opts['rebuild'] != '0' + + def params(self): + return self.opts['params'] + + def get_arg(self, arg): + if not arg in self.optargs: + raise error.internal('bad arg: %s' % (arg)) + for a in self.args: + if a.startswith(arg): + return a + return None + + def get_config_files(self, config): + # + # Convert to shell paths and return shell paths. + # + # @fixme should this use a passed in set of defaults and not + # not the initial set of values ? + # + config = path.shell(config) + if '*' in config or '?' in config: + print config + configdir = path.dirname(config) + configbase = path.basename(config) + if len(configbase) == 0: + configbase = '*' + if not configbase.endswith('.cfg'): + configbase = configbase + '.cfg' + if len(configdir) == 0: + configdir = self.expand(defaults['_configdir'][2], defaults) + configs = [] + for cp in configdir.split(':'): + hostconfigdir = path.host(cp) + for f in glob.glob(os.path.join(hostconfigdir, configbase)): + configs += path.shell(f) + else: + configs = [config] + return configs + + def config_files(self): + configs = [] + for config in self.opts['params']: + configs.extend(self.get_config_files(config)) + return configs + + def logfiles(self): + if 'log' in self.opts: + return self.opts['log'].split(',') + return ['stdout'] + + def urls(self): + if 'url' in self.opts: + return self.opts['url'].split(',') + return None + + def prefixbase(self): + if 'prefixbase' in self.opts: + return self.opts['prefixbase'] + return None + +def load(args, optargs = None): + """ + Copy the defaults, get the host specific values and merge them overriding + any matching defaults, then create an options object to handle the command + line merging in any command line overrides. Finally post process the + command line. + """ + d = defaults + overrides = None + if os.name == 'nt': + import windows + overrides = windows.load() + else: + uname = os.uname() + try: + if uname[0] == 'Darwin': + import darwin + overrides = darwin.load() + elif uname[0] == 'FreeBSD': + import freebsd + overrides = freebsd.load() + elif uname[0] == 'Linux': + import linux + overrides = linux.load() + except: + pass + if overrides is None: + raise error.general('no hosts defaults found; please add') + for k in overrides: + d[k] = overrides[k] + o = command_line(args, optargs) + for k in o.defaults: + d[k] = o.defaults[k] + d = o._post_process(d) + return o, d + +if __name__ == '__main__': + import sys + try: + _opts, _defaults = load(args = sys.argv) + print _opts + pprint.pprint(_defaults) + except error.general, gerr: + print gerr + sys.exit(1) + except error.internal, ierr: + print ierr + sys.exit(1) + sys.exit(0) diff --git a/source-builder/sb/error.py b/source-builder/sb/error.py new file mode 100644 index 0000000..5617c17 --- /dev/null +++ b/source-builder/sb/error.py @@ -0,0 +1,54 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# Various errors we can raise. +# + +class error(Exception): + """Base class for Builder exceptions.""" + def set_output(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class general(error): + """Raise for a general error.""" + def __init__(self, what): + self.set_output('error: ' + what) + +class internal(error): + """Raise for an internal error.""" + def __init__(self, what): + self.set_output('internal error: ' + what) + +class exit(error): + """Raise for to exit.""" + def __init__(self): + pass + +if __name__ == '__main__': + try: + raise general('a general error') + except general, gerr: + print 'caught:', gerr + try: + raise internal('an internal error') + except internal, ierr: + print 'caught:', ierr diff --git a/source-builder/sb/execute.py b/source-builder/sb/execute.py new file mode 100755 index 0000000..ad4ffde --- /dev/null +++ b/source-builder/sb/execute.py @@ -0,0 +1,363 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# Execute commands or scripts. +# +# Note, the subprocess module is only in Python 2.4 or higher. +# + +import os +import re +import sys +import subprocess +import threading + +import error +import log + +# Redefine the PIPE from subprocess +PIPE = subprocess.PIPE + +# Regular expression to find quotes. +qstr = re.compile('[rR]?\'([^\\n\'\\\\]|\\\\.)*\'|[rR]?"([^\\n"\\\\]|\\\\.)*"') + +def check_type(command): + """Checks the type of command we have. The types are spawn and + shell.""" + if command in ['spawn', 'shell']: + return True + return False + +def arg_list(args): + """Turn a string of arguments into a list suitable for + spawning a command. If the args are already a list return + it.""" + if type(args) is list: + return args + argstr = args + args = [] + while len(argstr): + qs = qstr.search(argstr) + if not qs: + args.extend(argstr.split()) + argstr= '' + else: + # We have a quoted string. Get the string before + # the quoted string and splt on white space then + # add the quoted string as an option then remove + # the first + quoted string and try again + front = argstr[:qs.start()] + args.extend(front.split()) + args.append(argstr[qs.start() + 1:qs.end() - 1]) + argstr = argstr[qs.end():] + return args + +def arg_subst(command, substs): + """Substitute the %[0-9] in the command with the subst values.""" + args = arg_list(command) + if substs: + for a in range(0, len(args)): + for r in range(0, len(substs)): + args[a] = re.compile(('%%%d' % (r))).sub(substs[r], args[a]) + return args + +def arg_subst_str(command, subst): + cmd = arg_subst(command, subst) + def add(x, y): return x + ' ' + str(y) + return reduce(add, cmd, '') + +class execute: + """Execute commands or scripts. The 'output' is a funtion + that handles the output from the process.""" + def __init__(self, output = None, error_prefix = '', verbose = False): + self.output = output + self.error_prefix = error_prefix + self.verbose = verbose + self.shell_exe = None + self.shell_commands = False + self.path = None + self.environment = None + + def capture(self, proc, timeout = None): + """Create 2 threads to read stdout and stderr and send to the + output handler. Based on the 'communicate' code in the subprocess + module.""" + def _readthread(fh, out, prefix = ''): + """Read from a file handle and write to the output handler + until the file closes.""" + while True: + line = fh.readline() + if len(line) == 0: + break + if out: + out(prefix + line) + else: + log.output(prefix + line) + def _timerthread(proc, timer): + """Timer thread calls the timer handler if one + is present once a second. The user provides a handler + and returns False to kill the process or True continue.""" + while True: + time.sleep(1) + if not timer(proc): + proc.stdout.close() + proc.stderr.close() + + if proc.stdout: + stdout_thread = threading.Thread(target = _readthread, + args = (proc.stdout, + self.output, + '')) + stdout_thread.setDaemon(True) + stdout_thread.start() + if proc.stderr: + stderr_thread = threading.Thread(target = _readthread, + args = (proc.stderr, + self.output, + self.error_prefix)) + stderr_thread.setDaemon(True) + stderr_thread.start() + if proc.stdout: + stdout_thread.join() + if proc.stderr: + stderr_thread.join() + return proc.wait() + + def open(self, command, capture = True, shell = False, + cwd = None, env = None, + stdin = None, stdout = None, stderr = None): + """Open a command with arguments. Provide the arguments as a list or + a string.""" + if self.verbose: + s = command + if type(command) is list: + def add(x, y): return x + ' ' + str(y) + s = reduce(add, command, '')[1:] + what = 'spawn' + if shell: + what = 'shell' + log.output(what + ': ' + s) + if shell and self.shell_exe: + command = arg_list(command) + command[:0] = self.shell_exe + if not stdout: + stdout = subprocess.PIPE + if not stderr: + stderr = subprocess.PIPE + proc = None + if cwd is None: + cwd = self.path + if env is None: + env = self.environment + try: + # Work around a problem on Windows with commands that + # have a '.' and no extension. Windows needs the full + # command name. + if sys.platform == "win32" and type(command) is list: + if command[0].find('.') >= 0: + r, e = os.path.splitext(command[0]) + if e not in ['.exe', '.com', '.bat']: + command[0] = command[0] + '.exe' + proc = subprocess.Popen(command, shell = shell, + cwd = cwd, env = env, + stdin = stdin, stdout = stdout, + stderr = stderr) + if not capture: + return (0, proc) + exit_code = self.capture(proc) + if self.verbose: + log.output('exit: ' + str(exit_code)) + except OSError, ose: + exit_code = ose.errno + if self.verbose: + log.output('exit: ' + str(ose)) + return (exit_code, proc) + + def spawn(self, command, capture = True, cwd = None, env = None, + stdin = None, stdout = None, stderr = None): + """Spawn a command with arguments. Provide the arguments as a list or + a string.""" + return self.open(command, capture, False, cwd, env, + stdin, stdout, stderr) + + def shell(self, command, capture = True, cwd = None, env = None, + stdin = None, stdout = None, stderr = None): + """Execute a command within a shell context. The command can contain + argumments. The shell is specific to the operating system. For example + it is cmd.exe on Windows XP.""" + return self.open(command, capture, True, cwd, env, + stdin, stdout, stderr) + + def command(self, command, args = None, capture = True, shell = False, + cwd = None, env = None, + stdin = None, stdout = None, stderr = None): + """Run the command with the args. The args can be a list + or a string.""" + if args and not type(args) is list: + args = arg_list(args) + cmd = [command] + if args: + cmd.extend(args) + return self.open(cmd, capture = capture, shell = shell, + cwd = cwd, env = env, + stdin = stdin, stdout = stdout, stderr = stderr) + + def command_subst(self, command, substs, capture = True, shell = False, + cwd = None, env = None, + stdin = None, stdout = None, stderr = None): + """Run the command from the config data with the + option format string subsituted with the subst variables.""" + args = arg_subst(command, substs) + return self.command(args[0], args[1:], capture = capture, + shell = shell or self.shell_commands, + cwd = cwd, env = env, + stdin = stdin, stdout = stdout, stderr = stderr) + + def set_shell(self, execute): + """Set the shell to execute when issuing a shell command.""" + args = arg_list(execute) + if len(args) == 0 or not os.path.isfile(args[0]): + raise error.general('could find shell: ' + execute) + self.shell_exe = args + + def command_use_shell(self): + """Force all commands to use a shell. This can be used with set_shell + to allow Unix commands be executed on Windows with a Unix shell such + as Cygwin or MSYS. This may cause piping to fail.""" + self.shell_commands = True + + def set_output(self, output): + """Set the output handler. The stdout of the last process in a pipe + line is passed to this handler.""" + old_output = self.output + self.output = output + return old_output + + def set_path(self, path): + """Set the path changed to before the child process is created.""" + old_path = self.path + self.path = path + return old_path + + def set_environ(self, environment): + """Set the environment passed to the child process when created.""" + old_environment = self.environment + self.environment = environment + return old_environment + +class capture_execution(execute): + """Capture all output as a string and return it.""" + + class _output_snapper: + def __init__(self, log = None, dump = False): + self.output = '' + self.log = log + self.dump = dump + + def handler(self, text): + if not self.dump: + if self.log is not None: + self.log.output(text) + else: + self.output += text + + def get_and_clear(self): + text = self.output + self.output = '' + return text.strip() + + def __init__(self, log = None, dump = False, error_prefix = '', verbose = False): + self.snapper = capture_execution._output_snapper(log = log, dump = dump) + execute.__init__(self, output = self.snapper.handler, + error_prefix = error_prefix, + verbose = verbose) + + def open(self, command, capture = True, shell = False, cwd = None, env = None, + stdin = None, stdout = None, stderr = None): + if not capture: + raise error.general('output capture must be true; leave as default') + #self.snapper.get_and_clear() + exit_code, proc = execute.open(self, command, capture = True, shell = shell, + cwd = cwd, env = env, + stdin = stdin, stdout = stdout, stderr = stderr) + return (exit_code, proc, self.snapper.get_and_clear()) + + def set_output(self, output): + raise error.general('output capture cannot be overrided') + +if __name__ == "__main__": + def run_tests(e, commands, use_shell): + for c in commands['shell']: + e.shell(c) + for c in commands['spawn']: + e.spawn(c) + for c in commands['cmd']: + if type(c) is str: + e.command(c, shell = use_shell) + else: + e.command(c[0], c[1], shell = use_shell) + for c in commands['csubsts']: + e.command_subst(c[0], c[1], shell = use_shell) + ec, proc = e.command(commands['pipe'][0], commands['pipe'][1], + capture = False, stdin = subprocess.PIPE) + if ec == 0: + print 'piping input into ' + commands['pipe'][0] + ': ' + \ + commands['pipe'][2] + proc.stdin.write(commands['pipe'][2]) + proc.stdin.close() + e.capture(proc) + del proc + + cmd_shell_test = 'if "%OS%" == "Windows_NT" (echo It is WinNT) else echo Is is not WinNT' + sh_shell_test = 'x="me"; if [ $x = "me" ]; then echo "It was me"; else "It was him"; fi' + + commands = {} + commands['windows'] = {} + commands['unix'] = {} + commands['windows']['shell'] = ['cd', 'dir /w', '.\\xyz', cmd_shell_test] + commands['windows']['spawn'] = ['hostname', 'hostnameZZ', ['netstat', '/e']] + commands['windows']['cmd'] = [('ipconfig'), ('nslookup', 'www.python.org')] + commands['windows']['csubsts'] = [('netstat %0', ['-a']), + ('netstat %0 %1', ['-a', '-n'])] + commands['windows']['pipe'] = ('ftp', None, 'help\nquit') + commands['unix']['shell'] = ['pwd', 'ls -las', './xyz', sh_shell_test] + commands['unix']['spawn'] = ['ls', 'execute.pyc', ['ls', '-i']] + commands['unix']['cmd'] = [('date'), ('date', '-R'), ('date', ['-u', '+%d %D']), + ('date', '-u "+%d %D %S"')] + commands['unix']['csubsts'] = [('date %0 "+%d %D %S"', ['-u']), + ('date %0 %1', ['-u', '+%d %D %S'])] + commands['unix']['pipe'] = ('grep', 'hello', 'hello world') + + print arg_list('cmd a1 a2 "a3 is a string" a4') + print arg_list('cmd b1 b2 "b3 is a string a4') + print arg_subst(['nothing', 'xx-%0-yyy', '%1', '%2-something'], + ['subst0', 'subst1', 'subst2']) + + e = execute(error_prefix = 'ERR: ', verbose = True) + if sys.platform == "win32": + run_tests(e, commands['windows'], False) + if os.path.exists('c:\\msys\\1.0\\bin\\sh.exe'): + e.set_shell('c:\\msys\\1.0\\bin\\sh.exe --login -c') + commands['unix']['pipe'] = ('c:\\msys\\1.0\\bin\\grep', + 'hello', 'hello world') + run_tests(e, commands['unix'], True) + else: + run_tests(e, commands['unix'], False) + del e diff --git a/source-builder/sb/freebsd.py b/source-builder/sb/freebsd.py new file mode 100644 index 0000000..18a2c84 --- /dev/null +++ b/source-builder/sb/freebsd.py @@ -0,0 +1,67 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# RTEMS Tools 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, either version 3 of the License, or +# (at your option) any later version. +# +# RTEMS Tools 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 RTEMS Tools. If not, see <http://www.gnu.org/licenses/>. +# + +# +# This code is based on what ever doco about spec files I could find and +# RTEMS project's spec files. +# + +import pprint +import os + +import execute + +def load(): + uname = os.uname() + sysctl = '/sbin/sysctl ' + e = execute.capture_execution() + exit_code, proc, output = e.shell(sysctl + 'hw.ncpu') + if exit_code == 0: + smp_mflags = '-j' + output.split(' ')[1].strip() + else: + smp_mflags = '' + if uname[4] == 'amd64': + cpu = 'x86_64' + else: + cpu = uname[4] + version = uname[2] + if version.find('-') > 0: + version = version.split('-')[0] + defines = { + '_os': ('none', 'none', 'freebsd'), + '_host': ('triplet', 'required', cpu + '-freebsd' + version), + '_host_vendor': ('none', 'none', 'pc'), + '_host_os': ('none', 'none', 'freebsd'), + '_host_cpu': ('none', 'none', cpu), + '_host_alias': ('none', 'none', '%{nil}'), + '_host_arch': ('none', 'none', cpu), + '_usr': ('dir', 'required', '/usr/local'), + '_var': ('dir', 'optional', '/usr/local/var'), + 'optflags': ('none', 'none', '-O2 -I/usr/local/include -L/usr/local/lib'), + '_smp_mflags': ('none', 'none', smp_mflags), + '__bash': ('exe', 'optional', '/usr/local//bin/bash'), + '__xz': ('exe', 'optional', '/usr/bin/xz'), + '__make': ('exe', 'required', 'gmake') + } + return defines + +if __name__ == '__main__': + pprint.pprint(load()) diff --git a/source-builder/sb/linux.py b/source-builder/sb/linux.py new file mode 100644 index 0000000..cbc1cb9 --- /dev/null +++ b/source-builder/sb/linux.py @@ -0,0 +1,63 @@ +#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2010-2012 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# This code is based on what ever doco about spec files I could find and
+# RTEMS project's spec files.
+#
+
+import pprint
+import os
+
+import execute
+
+def load():
+ uname = os.uname()
+ smp_mflags = ''
+ processors = '/bin/grep processor /proc/cpuinfo'
+ e = execute.capture_execution()
+ exit_code, proc, output = e.shell(processors)
+ if exit_code == 0:
+ cpus = 0
+ for l in output.split('\n'):
+ count = l.split(':')[1].strip()
+ if count > cpus:
+ cpus = int(count)
+ if cpus > 0:
+ smp_mflags = '-j%d' % (cpus)
+ defines = {
+ '_os': ('none', 'none', 'linux'),
+ '_host': ('triplet', 'required', uname[4] + '-linux-gnu'),
+ '_host_vendor': ('none', 'none', 'gnu'),
+ '_host_os': ('none', 'none', 'linux'),
+ '_host_cpu': ('none', 'none', uname[4]),
+ '_host_alias': ('none', 'none', '%{nil}'),
+ '_host_arch': ('none', 'none', uname[4]),
+ '_usr': ('dir', 'required', '/usr'),
+ '_var': ('dir', 'required', '/usr/var'),
+ 'optflags': ('none', 'none', '-O2 -fasynchronous-unwind-tables'),
+ '_smp_mflags': ('none', 'none', smp_mflags),
+ '__bzip2': ('exe', 'required', '/usr/bin/bzip2'),
+ '__gzip': ('exe', 'required', '/bin/gzip'),
+ '__tar': ('exe', 'required', '/bin/tar')
+ }
+ return defines
+
+if __name__ == '__main__':
+ pprint.pprint(load())
diff --git a/source-builder/sb/log.py b/source-builder/sb/log.py new file mode 100755 index 0000000..71c77f0 --- /dev/null +++ b/source-builder/sb/log.py @@ -0,0 +1,111 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-testing'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# Log output to stdout and/or a file. +# + +import os +import sys + +import error + +# +# A global log. +# +default = None + +def set_default_once(log): + if default is None: + default = log + +def output(text = os.linesep, log = None): + """Output the text to a log if provided else send it to stdout.""" + if text is None: + text = os.linesep + if type(text) is list: + _text = '' + for l in text: + _text += l + os.linesep + text = _text + if log: + log.output(text) + elif default is not None: + default.output(text) + else: + for l in text.replace(chr(13), '').splitlines(): + print l + +def flush(log = None): + if log: + log.flush() + elif default is not None: + default.flush() + +class log: + """Log output to stdout or a file.""" + def __init__(self, streams = None): + self.fhs = [None, None] + if streams: + for s in streams: + if s == 'stdout': + self.fhs[0] = sys.stdout + elif s == 'stderr': + self.fhs[1] = sys.stderr + else: + try: + self.fhs.append(file(s, 'w')) + except IOError, ioe: + raise error.general("creating log file '" + s + \ + "': " + str(ioe)) + + def __del__(self): + for f in range(2, len(self.fhs)): + self.fhs[f].close() + + def has_stdout(self): + return self.fhs[0] is not None + + def has_stderr(self): + return self.fhs[1] is not None + + def output(self, text): + """Output the text message to all the logs.""" + # Reformat the text to have local line types. + out = '' + for l in text.replace(chr(13), '').splitlines(): + out += l + os.linesep + for f in range(0, len(self.fhs)): + if self.fhs[f] is not None: + self.fhs[f].write(out) + + def flush(self): + """Flush the output.""" + for f in range(0, len(self.fhs)): + if self.fhs[f] is not None: + self.fhs[f].flush() + +if __name__ == "__main__": + l = log(['stdout', 'log.txt']) + for i in range(0, 10): + l.output('hello world: %d\n' % (i)) + l.output('hello world CRLF\r\n') + l.output('hello world NONE') + l.flush() + del l diff --git a/source-builder/sb/path.py b/source-builder/sb/path.py new file mode 100644 index 0000000..0a5f84c --- /dev/null +++ b/source-builder/sb/path.py @@ -0,0 +1,95 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# Manage paths locally. The internally the path is in Unix or shell format and +# we convert to the native format when performing operations at the Python +# level. This allows macro expansion to work. +# + +import os +import string + +windows = os.name == 'nt' + +def host(path): + if path is not None: + while '//' in path: + path = path.replace('//', '/') + if windows and len(path) > 2: + if path[0] == '/' and path[2] == '/' and \ + (path[1] in string.ascii_lowercase or \ + path[1] in string.ascii_uppercase): + path = ('%s:%s' % (path[1], path[2:])).replace('/', '\\') + return path + +def shell(path): + if path is not None: + if windows and len(path) > 1 and path[1] == ':': + path = ('/%s%s' % (path[0], path[2:])).replace('\\', '/') + while '//' in path: + path = path.replace('//', '/') + return path + +def basename(path): + return shell(os.path.basename(path)) + +def dirname(path): + return shell(os.path.dirname(path)) + +def join(path, *args): + path = shell(path) + for arg in args: + path += '/' + shell(arg) + return shell(path) + +def abspath(path): + return shell(os.path.abspath(host(path))) + +def splitext(path): + root, ext = os.path.splitext(host(path)) + return shell(root), ext + +def exists(path): + return os.path.exists(host(path)) + +def isdir(path): + return os.path.isdir(host(path)) + +def isfile(path): + return os.path.isfile(host(path)) + +def isabspath(path): + return path[0] == '/' + +if __name__ == '__main__': + print host('/a/b/c/d-e-f') + print host('//a/b//c/d-e-f') + print shell('/w/x/y/z') + print basename('/as/sd/df/fg/me.txt') + print dirname('/as/sd/df/fg/me.txt') + print join('/d', 'g', '/tyty/fgfg') + windows = True + print host('/a/b/c/d-e-f') + print host('//a/b//c/d-e-f') + print shell('/w/x/y/z') + print shell('w:/x/y/z') + print basename('x:/sd/df/fg/me.txt') + print dirname('x:/sd/df/fg/me.txt') + print join('s:/d/', '/g', '/tyty/fgfg') diff --git a/source-builder/sb/setbuilder.py b/source-builder/sb/setbuilder.py new file mode 100644 index 0000000..eca60e4 --- /dev/null +++ b/source-builder/sb/setbuilder.py @@ -0,0 +1,250 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# This code builds a cross-gcc compiler tool suite given a tool set. A tool +# set lists the various tools. These are specific tool configurations. +# + +import distutils.dir_util +import glob +import operator +import os + +import build +import check +import defaults +import error +import log +import path + +# +# Version of Source Builder Set Builder. +# +version = '0.1' + +def _trace(opts, text): + if opts.trace(): + print text + +def _notice(opts, text): + if not opts.quiet() and not log.default.has_stdout(): + print text + log.output(text) + log.flush() + +class buildset: + """Build a set builds a set of packages.""" + + def __init__(self, bset, _defaults, opts): + _trace(opts, '_bset:%s: init' % (bset)) + self.opts = opts + self.defaults = _defaults + self.bset = bset + self.bset_pkg = '%s-%s-set' % (self.opts.expand('%{_target}', _defaults), + self.bset) + + def _output(self, text): + if not self.opts.quiet(): + log.output(text) + + def copy(self, src, dst): + if os.path.isdir(path.host(src)): + topdir = self.opts.expand('%{_topdir}', self.defaults) + what = '%s -> %s' % \ + (path.host(src[len(topdir) + 1:]), path.host(dst[len(topdir) + 1:])) + _notice(self.opts, 'installing: %s' % (what)) + if not self.opts.dry_run(): + try: + files = distutils.dir_util.copy_tree(path.host(src), + path.host(dst)) + for f in files: + self._output(f) + except IOError, err: + raise error.general('installing tree: %s: %s' % (what, str(err))) + except distutils.errors.DistutilsFileError, err: + raise error.general('installing tree: %s' % (str(err))) + + def first_package(self, _build): + tmproot = path.abspath(_build.config.expand('%{_tmproot}')) + _build.rmdir(tmproot) + _build.mkdir(tmproot) + prefix = _build.config.expand('%{_prefix}') + if prefix[0] == os.sep: + prefix = prefix[1:] + tmpprefix = path.join(tmproot, prefix) + tmpbindir = path.join(tmpprefix, 'bin') + # exporting to the environment + os.environ['SB_TMPPREFIX'] = tmpprefix + os.environ['SB_TMPBINDIR'] = tmpbindir + os.environ['PATH'] = path.host(tmpbindir) + os.pathsep + os.environ['PATH'] + self._output('path: ' + os.environ['PATH']) + # shell format + return tmproot + + def every_package(self, _build, tmproot): + self.copy(_build.config.abspath('%{buildroot}'), tmproot) + + def last_package(self, _build, tmproot): + tar = path.join(_build.config.expand('%{_tardir}'), + _build.config.expand('%s.tar.bz2' % (self.bset_pkg))) + _notice(self.opts, 'tarball: %s' % path.host(tar)) + if not self.opts.dry_run(): + cmd = _build.config.expand("'cd " + tmproot + \ + " && %{__tar} -cf - . | %{__bzip2} > " + tar + "'") + _build.run(cmd, shell_opts = '-c', cwd = tmproot) + + def load(self): + + def _clean(line): + line = line[0:-1] + b = line.find('#') + if b >= 0: + line = line[1:b] + return line.strip() + + exbset = self.opts.expand(self.bset, self.defaults) + + self.defaults['_bset'] = ('none', 'none', exbset) + + root, ext = path.splitext(exbset) + + if exbset.endswith('.bset'): + bset = exbset + else: + bset = '%s.bset' % (exbset) + + bsetname = bset + + if not path.exists(bsetname): + for cp in self.opts.expand('%{_configdir}', self.defaults).split(':'): + configdir = path.abspath(cp) + bsetname = path.join(configdir, bset) + if path.exists(bsetname): + break + bsetname = None + if bsetname is None: + raise error.general('no build set file found: %s' % (bset)) + try: + if self.opts.trace(): + print '_bset:%s: open: %s' % (self.bset, bsetname) + bset = open(path.host(bsetname), 'r') + except IOError, err: + raise error.general('error opening bset file: %s' % (bsetname)) + + configs = [] + + try: + lc = 0 + for l in bset: + lc += 1 + l = _clean(l) + if len(l) == 0: + continue + if self.opts.trace(): + print '%03d: %s' % (lc, l) + if ':' in l: + ls = l.split(':') + if ls[0].strip() == 'package': + self.bset_pkg = self.opts.expand(ls[1].strip(), self.defaults) + self.defaults['package'] = ('none', 'none', self.bset_pkg) + elif l[0] == '%': + if l.startswith('%define'): + ls = l.split() + self.defaults[ls[1].strip()] = ('none', 'none', ls[2].strip()) + else: + raise error.general('invalid directive in build set files: %s' % (l)) + else: + configs += [l.strip()] + except: + bset.close() + raise + + bset.close() + + return configs + + def make(self): + + _trace(self.opts, '_bset:%s: make' % (self.bset)) + _notice(self.opts, 'Build Set: %s' % (self.bset)) + + configs = self.load() + + _trace(self.opts, '_bset:%s: configs: %s' % (self.bset, ','.join(configs))) + + current_path = os.environ['PATH'] + try: + builds = [] + for s in range(0, len(configs)): + b = build.build(configs[s], _defaults = self.defaults, opts = self.opts) + if s == 0: + tmproot = self.first_package(b) + b.make() + self.every_package(b, tmproot) + if s == len(configs) - 1: + self.last_package(b, tmproot) + builds += [b] + if not self.opts.no_clean(): + for b in builds: + _notice(self.opts, 'cleaning: %s' % (b.name())) + b.cleanup() + for b in builds: + del b + except: + os.environ['PATH'] = current_path + raise + os.environ['PATH'] = current_path + +def run(): + import sys + try: + optargs = { '--list-configs': 'List available configurations', + '--list-bsets': 'List available build sets'} + opts, _defaults = defaults.load(sys.argv, optargs) + log.default = log.log(opts.logfiles()) + _notice(opts, 'Source Builder - Set Builder, v%s' % (version)) + if not check.host_setup(opts, _defaults): + if not opts.force(): + raise error.general('host build environment is not set up correctly (use --force to proceed)') + _notice(opts, 'warning: forcing build with known host setup problems') + if opts.get_arg('--list-configs'): + build.list_configs(opts, _defaults) + elif opts.get_arg('--list-bsets'): + build.list_configs(opts, _defaults, ext = '.bset') + else: + for bset in opts.params(): + c = buildset(bset, _defaults = _defaults, opts = opts) + c.make() + del c + except error.general, gerr: + print gerr + sys.exit(1) + except error.internal, ierr: + print ierr + sys.exit(1) + except error.exit, eerr: + pass + except KeyboardInterrupt: + _notice(opts, 'user terminated') + sys.exit(1) + sys.exit(0) + +if __name__ == "__main__": + run() diff --git a/source-builder/sb/windows.py b/source-builder/sb/windows.py new file mode 100644 index 0000000..50f2052 --- /dev/null +++ b/source-builder/sb/windows.py @@ -0,0 +1,91 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2012 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# Windows specific support and overrides. +# + +import pprint +import os + +import execute + +def load(): + uname = 'win32' + if os.environ.has_key('NUMBER_OF_PROCESSORS'): + ncpus = int(os.environ['NUMBER_OF_PROCESSORS']) + else: + ncpus = 0 + if ncpus > 1: + smp_mflags = '-j' + str(ncpus) + else: + smp_mflags = '' + if os.environ.has_key('HOSTTYPE'): + hosttype = os.environ['HOSTTYPE'] + else: + hosttype = 'i686' + system = 'mingw32' + defines = { + '_os': ('none', 'none', 'win32'), + '_host': ('triplet', 'required', hosttype + '-pc-' + system), + '_host_vendor': ('none', 'none', 'microsoft'), + '_host_os': ('none', 'none', 'win32'), + '_host_cpu': ('none', 'none', hosttype), + '_host_alias': ('none', 'none', '%{nil}'), + '_host_arch': ('none', 'none', hosttype), + '_usr': ('dir', 'optional', '/opt/local'), + '_var': ('dir', 'optional', '/opt/local/var'), + '_smp_mflags': ('none', 'none', smp_mflags), + '__bash': ('exe', 'required', 'bash'), + '__bzip2': ('exe', 'required', 'bzip2'), + '__cat': ('exe', 'required', 'cat'), + '__cc': ('exe', 'required', 'gcc'), + '__chgrp': ('exe', 'required', 'chgrp'), + '__chmod': ('exe', 'required', 'chmod'), + '__chown': ('exe', 'required', 'chown'), + '__cp': ('exe', 'required', 'cp'), + '__cxx': ('exe', 'required', 'g++'), + '__grep': ('exe', 'required', 'grep'), + '__gzip': ('exe', 'required', 'gzip'), + '__id': ('exe', 'required', 'id'), + '__install': ('exe', 'required', 'install'), + '__install_info': ('exe', 'required', 'install-info'), + '__ld': ('exe', 'required', 'ld'), + '__ldconfig': ('exe', 'none', ''), + '__mkdir': ('exe', 'required', 'mkdir'), + '__mv': ('exe', 'required', 'mv'), + '__nm': ('exe', 'required', 'nm'), + '__nm': ('exe', 'required', 'nm'), + '__objcopy': ('exe', 'required', 'objcopy'), + '__objdump': ('exe', 'required', 'objdump'), + '__patch': ('exe', 'required', 'patch'), + '__rm': ('exe', 'required', 'rm'), + '__sed': ('exe', 'required', 'sed'), + '__sh': ('exe', 'required', 'sh'), + '__tar': ('exe', 'required', 'bsdtar'), + '__unzip': ('exe', 'required', 'unzip'), + '__xz': ('exe', 'required', 'xz'), + '_buildshell': ('exe', 'required', '%{__sh}'), + '___setup_shell': ('exe', 'required', '%{__sh}'), + 'optflags': ('none', 'none', '-O2 -pipe'), + } + return defines + +if __name__ == '__main__': + pprint.pprint(load()) |