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

4.11
Last change on this file since f88fcf3 was f88fcf3, checked in by Chris Johns <chrisj@…>, on Mar 7, 2016 at 12:56:02 AM

sb: Update code base to support Python3 and Python2.

Fix Windows support to allow MSYS2 Python to be used.

Updates #2619.

  • Property mode set to 100644
File size: 10.5 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2013-2016 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
21from __future__ import print_function
22
23import datetime
24import operator
25import os
26import re
27import sys
28import threading
29import time
30
31import error
32import log
33import options
34import path
35import version
36
37def _collect(path_, file):
38    confs = []
39    for root, dirs, files in os.walk(path.host(path_), topdown = True):
40        for f in files:
41            if f == file:
42                confs += [path.shell(path.join(root, f))]
43    return confs
44
45def _grep(file, pattern):
46    rege = re.compile(pattern)
47    try:
48        f = open(path.host(file), 'r')
49        matches = [rege.match(l) != None for l in f.readlines()]
50        f.close()
51    except IOError as err:
52        raise error.general('reading: %s' % (file))
53    return True in matches
54
55class command:
56
57    def __init__(self, cmd, cwd):
58        self.exit_code = 0
59        self.thread = None
60        self.output = None
61        self.cmd = cmd
62        self.cwd = cwd
63        self.result = None
64
65    def runner(self):
66
67        import subprocess
68
69        #
70        # Support Python 2.6
71        #
72        if "check_output" not in dir(subprocess):
73            def f(*popenargs, **kwargs):
74                if 'stdout' in kwargs:
75                    raise ValueError('stdout argument not allowed, it will be overridden.')
76                process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
77                output, unused_err = process.communicate()
78                retcode = process.poll()
79                if retcode:
80                    cmd = kwargs.get("args")
81                    if cmd is None:
82                        cmd = popenargs[0]
83                    raise subprocess.CalledProcessError(retcode, cmd)
84                return output
85            subprocess.check_output = f
86
87        self.start_time = datetime.datetime.now()
88        self.exit_code = 0
89        try:
90            try:
91                if os.name == 'nt':
92                    cmd = ['sh', '-c'] + self.cmd
93                else:
94                    cmd = self.cmd
95                self.output = subprocess.check_output(cmd, cwd = path.host(self.cwd))
96            except subprocess.CalledProcessError as cpe:
97                self.exit_code = cpe.returncode
98                self.output = cpe.output
99            except OSError as ose:
100                raise error.general('bootstrap failed: %s in %s: %s' % \
101                                        (' '.join(cmd), path.host(self.cwd), (str(ose))))
102            except KeyboardInterrupt:
103                pass
104            except:
105                raise
106        except:
107            self.result = sys.exc_info()
108        self.end_time = datetime.datetime.now()
109
110    def run(self):
111        self.thread = threading.Thread(target = self.runner)
112        self.thread.start()
113
114    def is_alive(self):
115        return self.thread and self.thread.is_alive()
116
117    def reraise(self):
118        if self.result is not None:
119            raise self.result[0](self.result[1]).with_traceback(self.result[2])
120
121class autoreconf:
122
123    def __init__(self, topdir, configure):
124        self.topdir = topdir
125        self.configure = configure
126        self.cwd = path.dirname(self.configure)
127        self.bspopts()
128        self.command = command(['autoreconf', '-i', '--no-recursive'], self.cwd)
129        self.command.run()
130
131    def bspopts(self):
132        if _grep(self.configure, 'RTEMS_CHECK_BSPDIR'):
133            bsp_specs = _collect(self.cwd, 'bsp_specs')
134            try:
135                acinclude = path.join(self.cwd, 'acinclude.m4')
136                b = open(path.host(acinclude), 'w')
137                b.write('# RTEMS_CHECK_BSPDIR(RTEMS_BSP_FAMILY)' + os.linesep)
138                b.write('AC_DEFUN([RTEMS_CHECK_BSPDIR],' + os.linesep)
139                b.write('[' + os.linesep)
140                b.write('  case "$1" in' + os.linesep)
141                for bs in sorted(bsp_specs):
142                    dir = path.dirname(bs)[len(self.cwd) + 1:]
143                    b.write(%s )%s' % (dir, os.linesep))
144                    b.write('    AC_CONFIG_SUBDIRS([%s]);;%s' % (dir, os.linesep))
145                b.write('  *)' + os.linesep)
146                b.write('    AC_MSG_ERROR([Invalid BSP]);;' + os.linesep)
147                b.write('  esac' + os.linesep)
148                b.write('])' + os.linesep)
149                b.close()
150            except IOError as err:
151                raise error.general('writing: %s' % (acinclude))
152
153    def is_alive(self):
154        return self.command.is_alive()
155
156    def post_process(self):
157        if self.command is not None:
158            self.command.reraise()
159            if self.command.exit_code != 0:
160                raise error.general('error: autoreconf: %s' % (' '.join(self.command.cmd)))
161            makefile = path.join(self.cwd, 'Makefile.am')
162            if path.exists(makefile):
163                if _grep(makefile, 'stamp-h\.in'):
164                    stamp_h = path.join(self.cwd, 'stamp-h.in')
165                    try:
166                        t = open(path.host(stamp_h), 'w')
167                        t.write('timestamp')
168                        t.close()
169                    except IOError as err:
170                        raise error.general('writing: %s' % (stamp_h))
171
172def generate(topdir, jobs):
173    if type(jobs) is str:
174        jobs = int(jobs)
175    start_time = datetime.datetime.now()
176    confs = _collect(topdir, 'configure.ac')
177    next = 0
178    autoreconfs = []
179    while next < len(confs) or len(autoreconfs) > 0:
180        if next < len(confs) and len(autoreconfs) < jobs:
181            log.notice('%3d/%3d: autoreconf: %s' % \
182                           (next + 1, len(confs), confs[next][len(topdir) + 1:]))
183            autoreconfs += [autoreconf(topdir, confs[next])]
184            next += 1
185        else:
186            for ac in autoreconfs:
187                if not ac.is_alive():
188                    ac.post_process()
189                    autoreconfs.remove(ac)
190                    del ac
191            if len(autoreconfs) >= jobs:
192                time.sleep(1)
193    end_time = datetime.datetime.now()
194    log.notice('Bootstrap time: %s' % (str(end_time - start_time)))
195
196class ampolish3:
197
198    def __init__(self, topdir, makefile):
199        self.topdir = topdir
200        self.makefile = makefile
201        self.preinstall = path.join(path.dirname(makefile), 'preinstall.am')
202        self.command = command([path.join(topdir, 'ampolish3'), makefile], self.topdir)
203        self.command.run()
204
205    def is_alive(self):
206        return self.command.is_alive()
207
208    def post_process(self):
209        if self.command is not None:
210            if self.command.exit_code != 0:
211                raise error.general('error: ampolish3: %s' % (' '.join(self.command.cmd)))
212            try:
213                p = open(path.host(self.preinstall), 'w')
214                for l in self.command.output:
215                    p.write(l)
216                p.close()
217            except IOError as err:
218                raise error.general('writing: %s' % (self.preinstall))
219
220def preinstall(topdir, jobs):
221    if type(jobs) is str:
222        jobs = int(jobs)
223    start_time = datetime.datetime.now()
224    makes = []
225    for am in _collect(topdir, 'Makefile.am'):
226        if _grep(am, 'include .*/preinstall\.am'):
227            makes += [am]
228    next = 0
229    ampolish3s = []
230    while next < len(makes) or len(ampolish3s) > 0:
231        if next < len(makes) and len(ampolish3s) < jobs:
232            log.notice('%3d/%3d: ampolish3: %s' % \
233                           (next + 1, len(makes), makes[next][len(topdir) + 1:]))
234            ampolish3s += [ampolish3(topdir, makes[next])]
235            next += 1
236        else:
237            for ap in ampolish3s:
238                if not ap.is_alive():
239                    ap.post_process()
240                    ampolish3s.remove(ap)
241                    del ap
242            if len(ampolish3s) >= jobs:
243                time.sleep(1)
244    end_time = datetime.datetime.now()
245    log.notice('Preinstall time: %s' % (str(end_time - start_time)))
246
247def run(args):
248    try:
249        #
250        # On Windows MSYS2 prepends a path to itself to the environment
251        # path. This means the RTEMS specific automake is not found and which
252        # breaks the bootstrap. We need to remove the prepended path. Also
253        # remove any ACLOCAL paths from the environment.
254        #
255        if os.name == 'nt':
256            cspath = os.environ['PATH'].split(os.pathsep)
257            if 'msys' in cspath[0] and cspath[0].endswith('bin'):
258                os.environ['PATH'] = os.pathsep.join(cspath[1:])
259            if 'ACLOCAL_PATH' in os.environ:
260                #
261                # The clear fails on a current MSYS2 python (Feb 2016). Delete
262                # the entry if the clear fails.
263                #
264                try:
265                    os.environ['ACLOCAL_PATH'].clear()
266                except:
267                    del os.environ['ACLOCAL_PATH']
268        optargs = { '--rtems':       'The RTEMS source directory',
269                    '--preinstall':  'Preinstall AM generation' }
270        log.notice('RTEMS Source Builder - RTEMS Bootstrap, %s' % (version.str()))
271        opts = options.load(sys.argv, optargs)
272        if opts.get_arg('--rtems'):
273            topdir = opts.get_arg('--rtems')
274        else:
275            topdir = os.getcwd()
276        if opts.get_arg('--preinstall'):
277            preinstall(topdir, opts.jobs(opts.defaults['_ncpus']))
278        else:
279            generate(topdir, opts.jobs(opts.defaults['_ncpus']))
280    except error.general as gerr:
281        print(gerr)
282        print('Bootstrap FAILED', file = sys.stderr)
283        sys.exit(1)
284    except error.internal as ierr:
285        print(ierr)
286        print('Bootstrap FAILED', file = sys.stderr)
287        sys.exit(1)
288    except error.exit as eerr:
289        pass
290    except KeyboardInterrupt:
291        log.notice('abort: user terminated')
292        sys.exit(1)
293    sys.exit(0)
294
295if __name__ == "__main__":
296    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.