source: rtems-source-builder/source-builder/sb/bootstrap.py @ e879c9f

4.104.114.9
Last change on this file since e879c9f was e879c9f, checked in by Chris Johns <chrisj@…>, on Apr 27, 2013 at 10:30:15 AM

Add a faster bootstrap for RTEMS.

This is a threading dispatcher to bootstrap RTEMS using the
available cores rather than the sequential standard script.

  • Property mode set to 100644
File size: 7.6 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2013 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Permission to use, copy, modify, and/or distribute this software for any
9# purpose with or without fee is hereby granted, provided that the above
10# copyright notice and this permission notice appear in all copies.
11#
12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19#
20
21import datetime
22import operator
23import os
24import re
25import subprocess
26import sys
27import threading
28import time
29
30import error
31import log
32import options
33import path
34import version
35
36def _collect(path_, file):
37    confs = []
38    for root, dirs, files in os.walk(path.host(path_), topdown = True):
39        for f in files:
40            if f == file:
41                confs += [path.shell(path.join(root, f))]
42    return confs
43
44class command:
45
46    def __init__(self, cmd, cwd):
47        self.exit_code = 0
48        self.thread = None
49        self.output = None
50        self.cmd = cmd
51        self.cwd = cwd
52
53    def runner(self):
54        self.start_time = datetime.datetime.now()
55        self.exit_code = 0
56        try:
57            self.output = subprocess.check_output(self.cmd, cwd = self.cwd)
58        except subprocess.CalledProcessError, cpe:
59            self.exit_code = cpe.returncode
60            self.output = cpe.output
61        self.end_time = datetime.datetime.now()
62
63    def run(self):
64        self.thread = threading.Thread(target = self.runner)
65        self.thread.start()
66
67    def is_alive(self):
68        return self.thread and self.thread.is_alive()
69
70class autoreconf:
71
72    def __init__(self, configure):
73        self.configure = configure
74        self.cwd = path.dirname(self.configure)
75        self.bspopts()
76        self.command = command(['autoreconf', '-i', '--no-recursive'], self.cwd)
77        self.command.run()
78
79    def bspopts(self):
80        try:
81            c = open(self.configure, 'r')
82            c_lines = c.readlines()
83            c.close()
84        except IOError, err:
85            raise error.general('error reading: %s' % (configure))
86        if 'RTEMS_CHECK_BSPDIR' in c_lines:
87            bsp_specs = _collect(self.cwd, 'bsp_specs')
88            try:
89                acinclude = path.join(self.cwd, acinclude.m4)
90                b = open(acinclude, 'w')
91                b.write('# RTEMS_CHECK_BSPDIR(RTEMS_BSP_FAMILY) ')
92                b.write('AC_DEFUN([RTEMS_CHECK_BSPDIR],')
93                b.write('[')
94                b.write(' case "$1" in')
95                for bs in bsp_specs:
96                    b.write('   %s )' % (path.dirname(bs)))
97                    b.write('     AC_CONFIG_SUBDIRS([%s]);;' % (path.dirname(bs)))
98                b.write('  *)')
99                b.write('    AC_MSG_ERROR([Invalid BSP]);;')
100                b.write('  esac')
101                b.write('])')
102                b.close()
103            except IOError, err:
104                raise error.general('error writing: %s' % (acinclude))
105
106    def is_alive(self):
107        return self.command.is_alive()
108
109    def post_process(self):
110        if self.command is not None:
111            if self.command.exit_code != 0:
112                raise error.general('error: autoreconf: %s' % (' '.join(self.command.cmd)))
113
114def generate(topdir, jobs):
115    if type(jobs) is str:
116        jobs = int(jobs)
117    start_time = datetime.datetime.now()
118    confs = _collect(topdir, 'configure.ac')
119    next = 0
120    autoreconfs = []
121    while next < len(confs) or len(autoreconfs) > 0:
122        if next < len(confs) and len(autoreconfs) < jobs:
123            log.notice('%3d/%3d: autoreconf: %s' % \
124                           (next + 1, len(confs), confs[next][len(topdir) + 1:]))
125            autoreconfs += [autoreconf(confs[next])]
126            next += 1
127        else:
128            for ac in autoreconfs:
129                if not ac.is_alive():
130                    ac.post_process()
131                    autoreconfs.remove(ac)
132                    del ac
133            if len(autoreconfs) >= jobs:
134                time.sleep(1)
135    end_time = datetime.datetime.now()
136    log.notice('Bootstrap time: %s' % (str(end_time - start_time)))
137
138class ampolish3:
139
140    def __init__(self, topdir, makefile):
141        self.makefile = makefile
142        self.topdir = topdir
143        self.preinstall = path.join(path.dirname(makefile), 'preinstall.am')
144        self.command = command([path.join(topdir, 'ampolish3'), makefile], self.topdir)
145        self.command.run()
146
147    def is_alive(self):
148        return self.command.is_alive()
149
150    def post_process(self):
151        if self.command is not None:
152            if self.command.exit_code != 0:
153                raise error.general('error: ampolish3: %s' % (' '.join(self.command.cmd)))
154            try:
155                p = open(self.preinstall, 'w')
156                for l in self.command.output:
157                    p.write(l)
158                p.close()
159            except IOError, err:
160                raise error.general('error writing: %s' % (self.preinstall))
161
162def preinstall(topdir, jobs):
163    if type(jobs) is str:
164        jobs = int(jobs)
165    start_time = datetime.datetime.now()
166    makes = []
167    pre = re.compile('include .*/preinstall\.am')
168    for am in _collect(topdir, 'Makefile.am'):
169        try:
170            m = open(am, 'r')
171            am_lines = m.readlines()
172            m.close()
173        except IOError, err:
174            raise error.general('error reading: %s' % (am))
175        ml = [pre.match(l) != None for l in am_lines]
176        if True in ml:
177            makes += [am]
178    next = 0
179    ampolish3s = []
180    while next < len(makes) or len(ampolish3s) > 0:
181        if next < len(makes) and len(ampolish3s) < jobs:
182            log.notice('%3d/%3d: ampolish3: %s' % \
183                           (next + 1, len(makes), makes[next][len(topdir) + 1:]))
184            ampolish3s += [ampolish3(topdir, makes[next])]
185            next += 1
186        else:
187            for ap in ampolish3s:
188                if not ap.is_alive():
189                    ap.post_process()
190                    ampolish3s.remove(ap)
191                    del ap
192            if len(ampolish3s) >= jobs:
193                time.sleep(1)
194    end_time = datetime.datetime.now()
195    log.notice('Preinstall time: %s' % (str(end_time - start_time)))
196
197def run(args):
198    try:
199        optargs = { '--rtems':       'The RTEMS source directory',
200                    '--preinstall':  'Preinstall AM generation' }
201        log.notice('RTEMS Source Builder - RTEMS Bootstrap, v%s' % (version.str()))
202        opts = options.load(sys.argv, optargs)
203        if opts.get_arg('--rtems'):
204            topdir = opts.get_arg('--rtems')
205        else:
206            topdir = os.getcwd()
207        if opts.get_arg('--preinstall'):
208            preinstall(topdir, opts.defaults['_ncpus'])
209        else:
210            generate(topdir, opts.defaults['_ncpus'])
211    except error.general, gerr:
212        print gerr
213        print >> sys.stderr, 'Bootstrap FAILED'
214        sys.exit(1)
215    except error.internal, ierr:
216        print ierr
217        print >> sys.stderr, 'Bootstrap FAILED'
218        sys.exit(1)
219    except error.exit, eerr:
220        pass
221    except KeyboardInterrupt:
222        log.notice('abort: user terminated')
223        sys.exit(1)
224    sys.exit(0)
225
226if __name__ == "__main__":
227    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.