source: rtems-source-builder/source-builder/sb/build.py @ f077b2b

4.104.114.95
Last change on this file since f077b2b was f077b2b, checked in by Chris Johns <chrisj@…>, on 04/22/13 at 12:28:27

Fixes for CVS to work. Add RTEMS build for sparc/sis.

  • Property mode set to 100644
File size: 15.8 KB
RevLine 
[bf13d27]1#
2# RTEMS Tools Project (http://www.rtems.org/)
[649a64c]3# Copyright 2010-2013 Chris Johns (chrisj@rtems.org)
[bf13d27]4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Permission to use, copy, modify, and/or distribute this software for any
9# purpose with or without fee is hereby granted, provided that the above
10# copyright notice and this permission notice appear in all copies.
11#
12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#
21# This code builds a package given a config file. It only builds to be
22# installed not to be package unless you run a packager around this.
23#
24
25import getopt
[015fa1b]26import glob
[bf13d27]27import os
28import shutil
29import stat
30import sys
31import urllib2
32import urlparse
33
[efb6688]34try:
35    import check
36    import config
[649a64c]37    import download
[efb6688]38    import error
39    import execute
40    import log
[cb12e48]41    import options
[efb6688]42    import path
43    import version
44except KeyboardInterrupt:
45    print 'abort: user terminated'
46    sys.exit(1)
47except:
48    print 'error: unknown application load error'
49    sys.exit(1)
[bf13d27]50
51class script:
52    """Create and manage a shell script."""
53
[5142bec]54    def __init__(self):
[bf13d27]55        self.reset()
56
57    def reset(self):
58        self.body = []
59        self.lc = 0
60
61    def append(self, text):
62        if type(text) is str:
63            text = text.splitlines()
[5142bec]64        if not log.quiet:
[bf13d27]65            i = 0
66            for l in text:
67                i += 1
[cafbcc6]68                log.output('script:%3d: %s' % (self.lc + i, l))
[bf13d27]69        self.lc += len(text)
70        self.body.extend(text)
71
72    def write(self, name, check_for_errors = False):
73        s = None
74        try:
[ab8319a]75            s = open(path.host(name), 'w')
[bf13d27]76            s.write('\n'.join(self.body))
77            s.close()
[ab8319a]78            os.chmod(path.host(name), stat.S_IRWXU | \
[bf13d27]79                         stat.S_IRGRP | stat.S_IXGRP | \
80                         stat.S_IROTH | stat.S_IXOTH)
81        except IOError, err:
82            raise error.general('creating script: ' + name)
83        except:
84            if s is not None:
85                s.close()
86            raise
87        if s is not None:
88            s.close()
89
90class build:
91    """Build a package given a config file."""
92
[cb12e48]93    def __init__(self, name, create_tar_files, opts, macros = None):
[bf13d27]94        self.opts = opts
[cb12e48]95        if macros is None:
96            self.macros = opts.defaults
97        else:
98            self.macros = macros
[cafbcc6]99        self.create_tar_files = create_tar_files
[5142bec]100        log.notice('config: ' + name)
[cb12e48]101        self.config = config.file(name, opts, self.macros)
[5142bec]102        self.script = script()
[bf13d27]103
[ab8319a]104    def rmdir(self, rmpath):
[5142bec]105        log.output('removing: %s' % (path.host(rmpath)))
[bf13d27]106        if not self.opts.dry_run():
[ab8319a]107            if path.exists(rmpath):
[ee47d72]108                path.removeall(rmpath)
[bf13d27]109
[ab8319a]110    def mkdir(self, mkpath):
[5142bec]111        log.output('making dir: %s' % (path.host(mkpath)))
[bf13d27]112        if not self.opts.dry_run():
[ee47d72]113            path.mkdir(mkpath)
[bf13d27]114
115    def source(self, package, source_tag):
116        #
117        # Scan the sources found in the config file for the one we are
[7ed2835]118        # after. Infos or tags are lists. Merge in any macro defined
119        # sources as these may be overridden by user loaded macros.
[bf13d27]120        #
121        sources = package.sources()
122        url = None
123        for s in sources:
124            tag = s[len('source'):]
125            if tag.isdigit():
126                if int(tag) == source_tag:
127                    url = sources[s][0]
128                    break
129        if url is None:
[ab8319a]130            raise error.general('source tag not found: source%d' % (source_tag))
[649a64c]131        source = download.parse_url(url, '_sourcedir', self.config, self.opts)
132        download.get_file(source['url'], source['local'], self.opts, self.config)
133        if 'symlink' in source:
[f077b2b]134            source['script'] = '%%{__ln_s} %s ${source_dir_%d}' % (source['symlink'], source_tag)
[649a64c]135        elif 'compressed' in source:
[bf13d27]136            source['script'] = source['compressed'] + ' ' + \
137                source['local'] + ' | %{__tar_extract} -'
138        else:
139            source['script'] = '%{__tar_extract} ' + source['local']
140        return source
141
142    def patch(self, package, args):
143        #
144        # Scan the patches found in the config file for the one we are
145        # after. Infos or tags are lists.
146        #
147        patches = package.patches()
148        url = None
149        for p in patches:
150            if args[0][1:].lower() == p:
151                url = patches[p][0]
152                break
153        if url is None:
[ab8319a]154            raise error.general('patch tag not found: %s' % (args[0]))
[5260449]155        #
156        # Parse the URL first in the source builder's patch directory.
157        #
[649a64c]158        patch = download.parse_url(url, '_patchdir', self.config, self.opts)
[5260449]159        #
160        # If not in the source builder package check the source directory.
161        #
[ec26725]162        if not path.exists(patch['local']):
[649a64c]163            patch = download.parse_url(url, '_patchdir', self.config, self.opts)
164        download.get_file(patch['url'], patch['local'], self.opts, self.config)
[bf13d27]165        if 'compressed' in patch:
166            patch['script'] = patch['compressed'] + ' ' +  patch['local']
167        else:
168            patch['script'] = '%{__cat} ' + patch['local']
169        patch['script'] += ' | %{__patch} ' + ' '.join(args[1:])
170        self.script.append(self.config.expand(patch['script']))
171
[4f26bdb]172    def canadian_cross(self):
173        _host = self.config.expand('%{_host}')
174        _build = self.config.expand('%{_build}')
175        _target = self.config.expand('%{_target}')
176        return self.config.defined('%{allow_cxc}') and \
177            _host != _build and _host != _target
178
[bf13d27]179    def setup(self, package, args):
[5142bec]180        log.output('prep: %s: %s' % (package.name(), ' '.join(args)))
[bf13d27]181        opts, args = getopt.getopt(args[1:], 'qDcTn:b:a:')
182        source_tag = 0
183        quiet = False
184        unpack_default_source = True
[7618a74c]185        unpack_before_chdir = True
[bf13d27]186        delete_before_unpack = True
187        create_dir = False
188        name = None
189        for o in opts:
190            if o[0] == '-q':
191                quiet = True
192            elif o[0] == '-D':
193                delete_before_unpack = False
194            elif o[0] == '-c':
195                create_dir = True
196            elif o[0] == '-T':
197                unpack_default_source = False
198            elif o[0] == '-n':
199                name = o[1]
200            elif o[0] == '-b':
201                unpack_before_chdir = True
202                if not o[1].isdigit():
[60f70d6]203                    raise error.general('setup -b source tag is not a number: %s' % (o[1]))
[bf13d27]204                source_tag = int(o[1])
205            elif o[0] == '-a':
206                unpack_before_chdir = False
[60f70d6]207                if not o[1].isdigit():
208                    raise error.general('setup -a source tag is not a number: %s' % (o[1]))
[bf13d27]209                source_tag = int(o[1])
210        source0 = None
211        source = self.source(package, source_tag)
212        if name is None:
213            if source:
214                name = source['name']
215            else:
[60f70d6]216                raise error.general('setup source tag not found: %d' % (source_tag))
[bf13d27]217        self.script.append(self.config.expand('cd %{_builddir}'))
218        if delete_before_unpack:
219            self.script.append(self.config.expand('%{__rm} -rf ' + name))
220        if create_dir:
221            self.script.append(self.config.expand('%{__mkdir_p} ' + name))
222        #
223        # If -a? then change directory before unpacking.
224        #
[a18e76b]225        if not unpack_before_chdir or create_dir:
[bf13d27]226            self.script.append(self.config.expand('cd ' + name))
227        #
228        # Unpacking the source. Note, treated the same as -a0.
229        #
230        if unpack_default_source and source_tag != 0:
231            source0 = self.source(package, 0)
232            if source0 is None:
233                raise error.general('no setup source0 tag found')
234            self.script.append(self.config.expand(source0['script']))
235        self.script.append(self.config.expand(source['script']))
[7618a74c]236        if unpack_before_chdir and not create_dir:
[bf13d27]237            self.script.append(self.config.expand('cd ' + name))
238        self.script.append(self.config.expand('%{__setup_post}'))
239
240    def run(self, command, shell_opts = '', cwd = None):
241        e = execute.capture_execution(log = log.default, dump = self.opts.quiet())
242        cmd = self.config.expand('%{___build_shell} -ex ' + shell_opts + ' ' + command)
[5142bec]243        log.output('run: ' + cmd)
[ab8319a]244        exit_code, proc, output = e.shell(cmd, cwd = path.host(cwd))
[bf13d27]245        if exit_code != 0:
[ee47d72]246            raise error.general('shell cmd failed: %s' % (cmd))
[bf13d27]247
248    def builddir(self):
249        builddir = self.config.abspath('_builddir')
250        self.rmdir(builddir)
251        if not self.opts.dry_run():
252            self.mkdir(builddir)
253
254    def prep(self, package):
255        self.script.append('echo "==> %prep:"')
256        _prep = package.prep()
257        for l in _prep:
258            args = l.split()
259            if args[0] == '%setup':
260                self.setup(package, args)
261            elif args[0].startswith('%patch'):
262                self.patch(package, args)
263            else:
264                self.script.append(' '.join(args))
265
266    def build(self, package):
[4f26bdb]267        self.script.append('echo "==> clean %{buildroot}: ${SB_BUILD_ROOT}"')
268        self.script.append('%s ${SB_BUILD_ROOT}' %
269                           (self.config.expand('%{__rmdir}')))
270        self.script.append('%s ${SB_BUILD_ROOT}' %
271                           (self.config.expand('%{__mkdir_p}')))
[bf13d27]272        self.script.append('echo "==> %build:"')
273        _build = package.build()
274        for l in _build:
275            args = l.split()
276            self.script.append(' '.join(args))
277
278    def install(self, package):
279        self.script.append('echo "==> %install:"')
280        _install = package.install()
281        for l in _install:
282            args = l.split()
283            self.script.append(' '.join(args))
284
285    def files(self, package):
[cafbcc6]286        if self.create_tar_files:
287            self.script.append('echo "==> %files:"')
[0add2ea]288            inpath = path.abspath(self.config.expand('%{buildroot}'))
[cafbcc6]289            tardir = path.abspath(self.config.expand('%{_tardir}'))
290            self.script.append(self.config.expand('if test -d %s; then' % (inpath)))
[4f26bdb]291            self.script.append(self.config.expand('  %%{__mkdir_p} %s' % tardir))
[cafbcc6]292            self.script.append(self.config.expand('  cd ' + inpath))
293            tar = path.join(tardir, package.long_name() + '.tar.bz2')
294            cmd = self.config.expand('  %{__tar} -cf - . ' + '| %{__bzip2} > ' + tar)
295            self.script.append(cmd)
296            self.script.append(self.config.expand('  cd %{_builddir}'))
297            self.script.append('fi')
[bf13d27]298
299    def clean(self, package):
300        self.script.append('echo "==> %clean:"')
301        _clean = package.clean()
302        if _clean is not None:
303            for l in _clean:
304                args = l.split()
305                self.script.append(' '.join(args))
306
[4f26bdb]307    def build_package(self, package):
308        if self.canadian_cross():
309            self.script.append('echo "==> Candian-cross build/target:"')
310            self.script.append('SB_CXC="yes"')
311        else:
312            self.script.append('SB_CXC="no"')
313        self.build(package)
314        self.install(package)
315        self.files(package)
316        if not self.opts.no_clean():
317            self.clean(package)
318
[bf13d27]319    def cleanup(self):
320        if not self.opts.no_clean():
321            buildroot = self.config.abspath('buildroot')
322            builddir = self.config.abspath('_builddir')
[4f26bdb]323            buildcxcdir = self.config.abspath('_buildcxcdir')
[8d7624e]324            tmproot = self.config.abspath('_tmproot')
[5142bec]325            log.trace('cleanup: %s' % (buildroot))
[bf13d27]326            self.rmdir(buildroot)
[5142bec]327            log.trace('cleanup: %s' % (builddir))
[bf13d27]328            self.rmdir(builddir)
[4f26bdb]329            if self.canadian_cross():
[5142bec]330                log.trace('cleanup: %s' % (buildcxcdir))
[4f26bdb]331                self.rmdir(buildcxcdir)
[5142bec]332            log.trace('cleanup: %s' % (tmproot))
[8d7624e]333            self.rmdir(tmproot)
[bf13d27]334
[54d76bb]335    def main_package(self):
[bf13d27]336        packages = self.config.packages()
[54d76bb]337        return packages['main']
338
339    def make(self):
340        package = self.main_package()
[bf13d27]341        name = package.name()
[4f26bdb]342        if self.canadian_cross():
[5142bec]343            log.notice('package: (Cxc) %s' % (name))
[4f26bdb]344        else:
[5142bec]345            log.notice('package: %s' % (name))
346        log.trace('---- macro maps %s' % ('-' * 55))
347        log.trace('%s' % (str(self.config.macros)))
348        log.trace('-' * 70)
[bf13d27]349        self.script.reset()
350        self.script.append(self.config.expand('%{___build_template}'))
351        self.script.append('echo "=> ' + name + ':"')
352        self.prep(package)
[4f26bdb]353        self.build_package(package)
[bf13d27]354        if not self.opts.dry_run():
355            self.builddir()
[ab8319a]356            sn = path.join(self.config.expand('%{_builddir}'), 'doit')
[5142bec]357            log.output('write script: ' + sn)
[bf13d27]358            self.script.write(sn)
[4f26bdb]359            if self.canadian_cross():
[5142bec]360                log.notice('building: (Cxc) %s' % (name))
[4f26bdb]361            else:
[5142bec]362                log.notice('building: %s' % (name))
[bf13d27]363            self.run(sn)
364
365    def name(self):
366        packages = self.config.packages()
367        package = packages['main']
368        return package.name()
369
[cb12e48]370def get_configs(opts):
[e908afb]371
372    def _scan(_path, ext):
373        configs = []
374        for root, dirs, files in os.walk(_path):
375            prefix = root[len(_path) + 1:]
376            for file in files:
[fba1136]377                for e in ext:
378                    if file.endswith(e):
379                        configs += [path.join(prefix, file)]
[e908afb]380        return configs
381
[fba1136]382    configs = { 'paths': [], 'files': [] }
[cb12e48]383    for cp in opts.defaults.expand('%{_configdir}').split(':'):
[bc71066]384        hcp = path.host(path.abspath(cp))
385        configs['paths'] += [hcp]
386        configs['files'] += _scan(hcp, ['.cfg', '.bset'])
[fba1136]387    configs['files'] = sorted(configs['files'])
388    return configs
[71b8893]389
[0759d98]390def find_config(config, configs):
391    config_root, config_ext = path.splitext(config)
392    if config_ext not in ['', '.bset', '.cfg']:
393        config_root = config
394        config_ext = ''
395    for c in configs['files']:
396        r, e = path.splitext(c)
397        if config_root == r:
398            if config_ext == '' or config_ext == e:
399                return c
400    return None
401
[bf13d27]402def run(args):
403    try:
[71b8893]404        optargs = { '--list-configs': 'List available configurations' }
[cb12e48]405        opts = options.load(args, optargs)
[5142bec]406        log.notice('RTEMS Source Builder, Package Builder v%s' % (version.str()))
[cb12e48]407        if not check.host_setup(opts):
[71b8893]408            if not opts.force():
[0add2ea]409                raise error.general('host build environment is not set up' +
[4f26bdb]410                                    ' correctly (use --force to proceed)')
[5142bec]411            log.notice('warning: forcing build with known host setup problems')
[71b8893]412        if opts.get_arg('--list-configs'):
[cb12e48]413            configs = get_configs(opts)
[cafbcc6]414            for p in configs['paths']:
415                print 'Examining: %s' % (os.path.relpath(p))
416                for c in configs['files']:
417                    if c.endswith('.cfg'):
418                        print '    %s' % (c)
[71b8893]419        else:
420            for config_file in opts.config_files():
[cb12e48]421                b = build(config_file, True, opts)
[71b8893]422                b.make()
423                del b
[bf13d27]424    except error.general, gerr:
425        print gerr
[aa4f8f6]426        print >> sys.stderr, 'Build FAILED'
[bf13d27]427        sys.exit(1)
428    except error.internal, ierr:
429        print ierr
[aa4f8f6]430        print >> sys.stderr, 'Build FAILED'
[bf13d27]431        sys.exit(1)
432    except error.exit, eerr:
433        pass
434    except KeyboardInterrupt:
[5142bec]435        log.notice('abort: user terminated')
[bf13d27]436        sys.exit(1)
437    sys.exit(0)
438
439if __name__ == "__main__":
440    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.