source: rtems-source-builder/source-builder/sb/execute.py @ 69e5938

4.104.114.95
Last change on this file since 69e5938 was 4f26bdb, checked in by Chris Johns <chrisj@…>, on 04/01/13 at 04:19:56

Add Canadian Cross support.

Add support to build MinGW tools using Cygwin. This is a Canadian cross
build.

Do not expand the directives when parsing a configuration file. Hold
in the package object the text as read from the configuration file. Still
parse the logic but leave the macros. This allows a configuration to be
varied when the build happens. The Canadian cross uses this to build a
build compiler used to build a Cxc runtime.

Add Cxc support to the build module. In the defaults add rm and rmfile
macros, add Cxc paths and pre-build script code.

In the setbuilder check for a Cxc build and if so and the package
allow Cxc build the build host version then the host target
version.

Add cygiwn support to the defaults processing and to the Windows module.

  • Property mode set to 100755
File size: 14.5 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2012 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#
21# Execute commands or scripts.
22#
23# Note, the subprocess module is only in Python 2.4 or higher.
24#
25
26import os
27import re
28import sys
29import subprocess
30import threading
31
32import error
33import log
34
35# Redefine the PIPE from subprocess
36PIPE = subprocess.PIPE
37
38# Regular expression to find quotes.
39qstr = re.compile('[rR]?\'([^\\n\'\\\\]|\\\\.)*\'|[rR]?"([^\\n"\\\\]|\\\\.)*"')
40
41def check_type(command):
42    """Checks the type of command we have. The types are spawn and
43    shell."""
44    if command in ['spawn', 'shell']:
45        return True
46    return False
47
48def arg_list(args):
49    """Turn a string of arguments into a list suitable for
50    spawning a command. If the args are already a list return
51    it."""
52    if type(args) is list:
53        return args
54    argstr = args
55    args = []
56    while len(argstr):
57        qs = qstr.search(argstr)
58        if not qs:
59            args.extend(argstr.split())
60            argstr= ''
61        else:
62            # We have a quoted string. Get the string before
63            # the quoted string and splt on white space then
64            # add the quoted string as an option then remove
65            # the first + quoted string and try again
66            front = argstr[:qs.start()]
67            args.extend(front.split())
68            args.append(argstr[qs.start() + 1:qs.end() - 1])
69            argstr = argstr[qs.end():]
70    return args
71
72def arg_subst(command, substs):
73    """Substitute the %[0-9] in the command with the subst values."""
74    args = arg_list(command)
75    if substs:
76        for a in range(0, len(args)):
77            for r in range(0, len(substs)):
78                args[a] = re.compile(('%%%d' % (r))).sub(substs[r], args[a])
79    return args
80
81def arg_subst_str(command, subst):
82    cmd = arg_subst(command, subst)
83    def add(x, y): return x + ' ' + str(y)
84    return reduce(add, cmd, '')
85
86class execute:
87    """Execute commands or scripts. The 'output' is a funtion
88    that handles the output from the process."""
89    def __init__(self, output = None, error_prefix = '', verbose = False):
90        self.output = output
91        self.error_prefix = error_prefix
92        self.verbose = verbose
93        self.shell_exe = None
94        self.shell_commands = False
95        self.path = None
96        self.environment = None
97
98    def capture(self, proc, timeout = None):
99        """Create 2 threads to read stdout and stderr and send to the
100        output handler. Based on the 'communicate' code in the subprocess
101        module."""
102        def _readthread(fh, out, prefix = ''):
103            """Read from a file handle and write to the output handler
104            until the file closes."""
105            count = 0
106            while True:
107                line = fh.readline()
108                count += 1
109                if len(line) == 0:
110                    break
111                if out:
112                    out(prefix + line)
113                else:
114                    log.output(prefix + line)
115                    if count > 10:
116                        log.flush()
117                        count = 0
118
119        def _timerthread(proc, timer):
120            """Timer thread calls the timer handler if one
121            is present once a second. The user provides a handler
122            and returns False to kill the process or True continue."""
123            while True:
124                time.sleep(1)
125                if not timer(proc):
126                    proc.stdout.close()
127                    proc.stderr.close()
128
129        if proc.stdout:
130            stdout_thread = threading.Thread(target = _readthread,
131                                             args = (proc.stdout,
132                                                     self.output,
133                                                     ''))
134            stdout_thread.setDaemon(True)
135            stdout_thread.start()
136        if proc.stderr:
137            stderr_thread = threading.Thread(target = _readthread,
138                                             args = (proc.stderr,
139                                                     self.output,
140                                                     self.error_prefix))
141            stderr_thread.setDaemon(True)
142            stderr_thread.start()
143        if proc.stdout:
144            stdout_thread.join()
145        if proc.stderr:
146            stderr_thread.join()
147        return proc.wait()
148
149    def open(self, command, capture = True, shell = False,
150             cwd = None, env = None,
151             stdin = None, stdout = None, stderr = None):
152        """Open a command with arguments. Provide the arguments as a list or
153        a string."""
154        if self.verbose:
155            s = command
156            if type(command) is list:
157                def add(x, y): return x + ' ' + str(y)
158                s = reduce(add, command, '')[1:]
159            what = 'spawn'
160            if shell:
161                what = 'shell'
162            log.output(what + ': ' + s)
163        if shell and self.shell_exe:
164            command = arg_list(command)
165            command[:0] = self.shell_exe
166        if not stdout:
167            stdout = subprocess.PIPE
168        if not stderr:
169            stderr = subprocess.PIPE
170        proc = None
171        if cwd is None:
172            cwd = self.path
173        if env is None:
174            env = self.environment
175        try:
176            # Work around a problem on Windows with commands that
177            # have a '.' and no extension. Windows needs the full
178            # command name.
179            if sys.platform == "win32" and type(command) is list:
180                if command[0].find('.') >= 0:
181                    r, e = os.path.splitext(command[0])
182                    if e not in ['.exe', '.com', '.bat']:
183                        command[0] = command[0] + '.exe'
184            proc = subprocess.Popen(command, shell = shell,
185                                    cwd = cwd, env = env,
186                                    stdin = stdin, stdout = stdout,
187                                    stderr = stderr)
188            if not capture:
189                return (0, proc)
190            exit_code = self.capture(proc)
191            if self.verbose:
192                log.output('exit: ' + str(exit_code))
193        except OSError, ose:
194            exit_code = ose.errno
195            if self.verbose:
196                log.output('exit: ' + str(ose))
197        return (exit_code, proc)
198
199    def spawn(self, command, capture = True, cwd = None, env = None,
200              stdin = None, stdout = None, stderr = None):
201        """Spawn a command with arguments. Provide the arguments as a list or
202        a string."""
203        return self.open(command, capture, False, cwd, env,
204                         stdin, stdout, stderr)
205
206    def shell(self, command, capture = True, cwd = None, env = None,
207              stdin = None, stdout = None, stderr = None):
208        """Execute a command within a shell context. The command can contain
209        argumments. The shell is specific to the operating system. For example
210        it is cmd.exe on Windows XP."""
211        return self.open(command, capture, True, cwd, env,
212                         stdin, stdout, stderr)
213
214    def command(self, command, args = None, capture = True, shell = False,
215                cwd = None, env = None,
216                stdin = None, stdout = None, stderr = None):
217        """Run the command with the args. The args can be a list
218        or a string."""
219        if args and not type(args) is list:
220            args = arg_list(args)
221        cmd = [command]
222        if args:
223            cmd.extend(args)
224        return self.open(cmd, capture = capture, shell = shell,
225                         cwd = cwd, env = env,
226                         stdin = stdin, stdout = stdout, stderr = stderr)
227
228    def command_subst(self, command, substs, capture = True, shell = False,
229                      cwd = None, env = None,
230                      stdin = None, stdout = None, stderr = None):
231        """Run the command from the config data with the
232        option format string subsituted with the subst variables."""
233        args = arg_subst(command, substs)
234        return self.command(args[0], args[1:], capture = capture,
235                            shell = shell or self.shell_commands,
236                            cwd = cwd, env = env,
237                            stdin = stdin, stdout = stdout, stderr = stderr)
238
239    def set_shell(self, execute):
240        """Set the shell to execute when issuing a shell command."""
241        args = arg_list(execute)
242        if len(args) == 0 or not os.path.isfile(args[0]):
243            raise error.general('could find shell: ' + execute)
244        self.shell_exe = args
245
246    def command_use_shell(self):
247        """Force all commands to use a shell. This can be used with set_shell
248        to allow Unix commands be executed on Windows with a Unix shell such
249        as Cygwin or MSYS. This may cause piping to fail."""
250        self.shell_commands = True
251
252    def set_output(self, output):
253        """Set the output handler. The stdout of the last process in a pipe
254        line is passed to this handler."""
255        old_output = self.output
256        self.output = output
257        return old_output
258
259    def set_path(self, path):
260        """Set the path changed to before the child process is created."""
261        old_path = self.path
262        self.path = path
263        return old_path
264
265    def set_environ(self, environment):
266        """Set the environment passed to the child process when created."""
267        old_environment = self.environment
268        self.environment = environment
269        return old_environment
270
271class capture_execution(execute):
272    """Capture all output as a string and return it."""
273
274    class _output_snapper:
275        def __init__(self, log = None, dump = False):
276            self.output = ''
277            self.log = log
278            self.dump = dump
279
280        def handler(self, text):
281            if not self.dump:
282                if self.log is not None:
283                    self.log.output(text)
284                else:
285                    self.output += text
286
287        def get_and_clear(self):
288            text = self.output
289            self.output = ''
290            return text.strip()
291
292    def __init__(self, log = None, dump = False, error_prefix = '', verbose = False):
293        self.snapper = capture_execution._output_snapper(log = log, dump = dump)
294        execute.__init__(self, output = self.snapper.handler,
295                         error_prefix = error_prefix,
296                         verbose = verbose)
297
298    def open(self, command, capture = True, shell = False, cwd = None, env = None,
299             stdin = None, stdout = None, stderr = None):
300        if not capture:
301            raise error.general('output capture must be true; leave as default')
302        #self.snapper.get_and_clear()
303        exit_code, proc = execute.open(self, command, capture = True, shell = shell,
304                                       cwd = cwd, env = env,
305                                       stdin = stdin, stdout = stdout, stderr = stderr)
306        return (exit_code, proc, self.snapper.get_and_clear())
307
308    def set_output(self, output):
309        raise error.general('output capture cannot be overrided')
310
311if __name__ == "__main__":
312    def run_tests(e, commands, use_shell):
313        for c in commands['shell']:
314            e.shell(c)
315        for c in commands['spawn']:
316            e.spawn(c)
317        for c in commands['cmd']:
318            if type(c) is str:
319                e.command(c, shell = use_shell)
320            else:
321                e.command(c[0], c[1], shell = use_shell)
322        for c in commands['csubsts']:
323            e.command_subst(c[0], c[1], shell = use_shell)
324        ec, proc = e.command(commands['pipe'][0], commands['pipe'][1],
325                             capture = False, stdin = subprocess.PIPE)
326        if ec == 0:
327            print 'piping input into ' + commands['pipe'][0] + ': ' + \
328                  commands['pipe'][2]
329            proc.stdin.write(commands['pipe'][2])
330            proc.stdin.close()
331            e.capture(proc)
332            del proc
333
334    cmd_shell_test = 'if "%OS%" == "Windows_NT" (echo It is WinNT) else echo Is is not WinNT'
335    sh_shell_test = 'x="me"; if [ $x = "me" ]; then echo "It was me"; else "It was him"; fi'
336
337    commands = {}
338    commands['windows'] = {}
339    commands['unix'] = {}
340    commands['windows']['shell'] = ['cd', 'dir /w', '.\\xyz', cmd_shell_test]
341    commands['windows']['spawn'] = ['hostname', 'hostnameZZ', ['netstat', '/e']]
342    commands['windows']['cmd'] = [('ipconfig'), ('nslookup', 'www.python.org')]
343    commands['windows']['csubsts'] = [('netstat %0', ['-a']),
344                                      ('netstat %0 %1', ['-a', '-n'])]
345    commands['windows']['pipe'] = ('ftp', None, 'help\nquit')
346    commands['unix']['shell'] = ['pwd', 'ls -las', './xyz', sh_shell_test]
347    commands['unix']['spawn'] = ['ls', 'execute.pyc', ['ls', '-i']]
348    commands['unix']['cmd'] = [('date'), ('date', '-R'), ('date', ['-u', '+%d %D']),
349                               ('date', '-u "+%d %D %S"')]
350    commands['unix']['csubsts'] = [('date %0 "+%d %D %S"', ['-u']),
351                                   ('date %0 %1', ['-u', '+%d %D %S'])]
352    commands['unix']['pipe'] = ('grep', 'hello', 'hello world')
353
354    print arg_list('cmd a1 a2 "a3 is a string" a4')
355    print arg_list('cmd b1 b2 "b3 is a string a4')
356    print arg_subst(['nothing', 'xx-%0-yyy', '%1', '%2-something'],
357                    ['subst0', 'subst1', 'subst2'])
358
359    e = execute(error_prefix = 'ERR: ', verbose = True)
360    if sys.platform == "win32":
361        run_tests(e, commands['windows'], False)
362        if os.path.exists('c:\\msys\\1.0\\bin\\sh.exe'):
363            e.set_shell('c:\\msys\\1.0\\bin\\sh.exe --login -c')
364            commands['unix']['pipe'] = ('c:\\msys\\1.0\\bin\\grep',
365                                        'hello', 'hello world')
366            run_tests(e, commands['unix'], True)
367    else:
368        run_tests(e, commands['unix'], False)
369    del e
Note: See TracBrowser for help on using the repository browser.