Edgewall Software

source: trunk/bitten/build/shtools.py @ 1001

Last change on this file since 1001 was 910, checked in by osimons, 13 years ago

Updated copyright to 2010.

  • Property svn:eol-style set to native
File size: 6.5 KB
CovLine 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
4# Copyright (C) 2007-2010 Edgewall Software
5# All rights reserved.
6#
7# This software is licensed as described in the file COPYING, which
8# you should have received as part of this distribution. The terms
9# are also available at http://bitten.edgewall.org/wiki/License.
10
111"""Generic recipe commands for executing external processes."""
12
113import codecs
114import logging
115import os
116import shlex
17
118from bitten.build import CommandLine
119from bitten.util import xmlio
20
121log = logging.getLogger('bitten.build.shtools')
22
123__docformat__ = 'restructuredtext en'
24
125def exec_(ctxt, executable=None, file_=None, output=None, args=None,
126          dir_=None, timeout=None):
27    """Execute a program or shell script.
28   
29    :param ctxt: the build context
30    :type ctxt: `Context`
31    :param executable: name of the executable to run
32    :param file\_: name of the script file, relative to the project directory,
33                  that should be run
34    :param output: name of the file to which the output of the script should be
35                   written
36    :param args: command-line arguments to pass to the script
37    :param dir\_: directory to change to before executing the command
38    :param timeout: the number of seconds before the external process should
39                    be aborted (has same constraints as CommandLine)
40    """
241    assert executable or file_, \
042        'Either "executable" or "file" attribute required'
43
244    returncode = execute(ctxt, executable=executable, file_=file_,
245                         output=output, args=args, dir_=dir_,
246                         timeout=timeout)
247    if returncode != 0:
048        ctxt.error('Executing %s failed (error code %s)' % (executable or file_,
049                                                            returncode))
50
151def pipe(ctxt, executable=None, file_=None, input_=None, output=None,
152         args=None, dir_=None):
53    """Pipe the contents of a file through a program or shell script.
54   
55    :param ctxt: the build context
56    :type ctxt: `Context`
57    :param executable: name of the executable to run
58    :param file\_: name of the script file, relative to the project directory,
59                  that should be run
60    :param input\_: name of the file containing the data that should be passed
61                   to the shell script on its standard input stream
62    :param output: name of the file to which the output of the script should be
63                   written
64    :param args: command-line arguments to pass to the script
65    :param dir\_: directory to change to before executing the command
66    """
067    assert executable or file_, \
068        'Either "executable" or "file" attribute required'
069    assert input_, 'Missing required attribute "input"'
70
071    returncode = execute(ctxt, executable=executable, file_=file_,
072                         input_=input_, output=output, args=args, dir_=dir_)
073    if returncode != 0:
074        ctxt.error('Piping through %s failed (error code %s)'
075                   % (executable or file_, returncode))
76
177def execute(ctxt, executable=None, file_=None, input_=None, output=None,
178            args=None, dir_=None, filter_=None, timeout=None):
79    """Generic external program execution.
80   
81    This function is not itself bound to a recipe command, but rather used from
82    other commands.
83   
84    :param ctxt: the build context
85    :type ctxt: `Context`
86    :param executable: name of the executable to run
87    :param file\_: name of the script file, relative to the project directory,
88                  that should be run
89    :param input\_: name of the file containing the data that should be passed
90                   to the shell script on its standard input stream
91    :param output: name of the file to which the output of the script should be
92                   written
93    :param args: command-line arguments to pass to the script
94    :param dir\_: directory to change to before executing the command
95    :param filter\_: function to filter out messages from the executable stdout
96    :param timeout: the number of seconds before the external process should
97                    be aborted (has same constraints as CommandLine)
98    """
299    if args:
2100        if isinstance(args, basestring):
2101            args = shlex.split(args)
2102    else:
0103        args = []
104
2105    if dir_:
0106        def resolve(*args):
0107            return ctxt.resolve(dir_, *args)
0108    else:
2109        resolve = ctxt.resolve
110
2111    if file_ and os.path.isfile(resolve(file_)):
0112        file_ = resolve(file_)
113
2114    shell = False
115
2116    if file_ and os.name == 'nt':
117        # Need to execute script files through a shell on Windows
0118        shell = True
119
2120    if executable is None:
0121        executable = file_
2122    elif file_:
0123        args[:0] = [file_]
124
125    # Support important Windows CMD.EXE built-ins (and it does its own quoting)
2126    if os.name == 'nt' and executable.upper() in ['COPY', 'DIR', 'ECHO',
0127                'ERASE', 'DEL', 'MKDIR', 'MD', 'MOVE', 'RMDIR', 'RD', 'TYPE']:
0128        shell = True
129
2130    if input_:
0131        input_file = codecs.open(resolve(input_), 'r', 'utf-8')
0132    else:
2133        input_file = None
134
2135    if output:
0136        output_file = codecs.open(resolve(output), 'w', 'utf-8')
0137    else:
2138        output_file = None
139
2140    if dir_ and os.path.isdir(ctxt.resolve(dir_)):
0141        dir_ = ctxt.resolve(dir_)
0142    else:
2143        dir_ = ctxt.basedir
144       
2145    if not filter_:
4146        filter_=lambda s: s
147
2148    if timeout:
0149        timeout = int(timeout)
150
2151    try:
2152        cmdline = CommandLine(executable, args, input=input_file,
2153                              cwd=dir_, shell=shell)
2154        log_elem = xmlio.Fragment()
4155        for out, err in cmdline.execute(timeout=timeout):
2156            if out is not None:
2157                log.info(out)
2158                info = filter_(out)
2159                if info:
2160                    log_elem.append(xmlio.Element('message', level='info')[
2161                        info.replace(ctxt.basedir + os.sep, '')
2162                            .replace(ctxt.basedir, '')
2163                ])
2164                if output:
0165                    output_file.write(out + os.linesep)
2166            if err is not None:
0167                log.error(err)
0168                log_elem.append(xmlio.Element('message', level='error')[
0169                    err.replace(ctxt.basedir + os.sep, '')
0170                       .replace(ctxt.basedir, '')
0171                ])
0172                if output:
0173                    output_file.write(err + os.linesep)
2174        ctxt.log(log_elem)
2175    finally:
2176        if input_:
0177            input_file.close()
2178        if output:
0179            output_file.close()
180
2181    return cmdline.returncode
Note: See TracBrowser for help on using the repository browser.