diff options
Diffstat (limited to 'tb/build.py')
-rw-r--r-- | tb/build.py | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/tb/build.py b/tb/build.py new file mode 100644 index 0000000..46a847d --- /dev/null +++ b/tb/build.py @@ -0,0 +1,428 @@ +# +# 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 os +import shutil +import stat +import sys +import urllib2 +import urlparse + +import config +import defaults +import error +import execute +import log + +# +# Version of Tools Builder. +# +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(name, 'w') + s.write('\n'.join(self.body)) + s.close() + os.chmod(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, path): + self._output('removing: ' + path) + if not self.opts.dry_run(): + if os.path.exists(path): + try: + shutil.rmtree(path) + except IOError, err: + raise error.error('error removing: ' + path) + + def mkdir(self, path): + self._output('making dir: ' + path) + if not self.opts.dry_run(): + try: + os.makedirs(path) + except IOError, err: + raise error.general('error creating path: ' + path) + + def get_file(self, url, local): + if not os.path.isdir(os.path.dirname(local)): + raise error.general('source path not found: ' + os.path.dirname(local)) + if not os.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) + for url in urls: + _notice(self.opts, 'download: ' + url + ' -> ' + local) + if not self.opts.dry_run(): + failed = False + _in = None + _out = None + try: + _in = urllib2.urlopen(url) + _out = open(local, 'wb') + _out.write(_in.read()) + except IOError, err: + _notice(self.opts, 'download: ' + url + ': failed: ' + str(err)) + if os.path.exists(local): + os.remove(local) + failed = True + except: + 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 os.path.isfile(local): + raise error.general('source is not a file: ' + local) + return + raise error.general('downloading ' + url + ': all paths have failed, giving up') + + def parse_url(self, url): + # + # Split the source up into the parts we need. + # + source = {} + source['url'] = url + source['path'] = os.path.dirname(url) + source['file'] = os.path.basename(url) + source['name'], source['ext'] = os.path.splitext(source['file']) + # + # Get the file. Checks the local source directory first. + # + source['local'] = os.path.join(self.config.abspath('_sourcedir'), + source['file']) + # + # 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' + str(source_tag)) + source = self.parse_url(url) + 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: ' + args[0]) + patch = self.parse_url(url) + 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: ' + 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: ' + 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 = 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 = os.path.join('%{buildroot}', prefixbase) + tardir = os.path.abspath(self.config.expand('%{_tardir}')) + self.script.append('mkdir -p %s' % tardir) + self.script.append(self.config.expand('cd ' + inpath)) + tar = os.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}')) + + 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 = self.config.expand(os.path.join('%{_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 run(args): + try: + opts, _defaults = defaults.load(args) + log.default = log.log(opts.logfiles()) + _notice(opts, 'Tools Builder, v%s' % (version)) + 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) |