changeset 154:d72d68471c10

Improvements to program execution from recipe commands. Also, adds a `<sh:pipe>` command, but that doesn't really work yet (doesn't get input from stdin). Related to #34.
author cmlenz
date Mon, 22 Aug 2005 11:58:10 +0000
parents 3ab91c56d7bc
children 7780b8815ab2
files bitten/build/pythontools.py bitten/build/shtools.py bitten/util/cmdline.py
diffstat 3 files changed, 102 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/bitten/build/pythontools.py
+++ b/bitten/build/pythontools.py
@@ -64,13 +64,9 @@
             ctxt.error('Cannot execute Python module %s: %s' % (module, e))
             return
 
-    if args:
-        args = file_ + ' ' + args
-    else:
-        args = file_
-
     from bitten.build import shtools
-    shtools.exec_(ctxt, file_='python', output=output, args=args)
+    shtools.exec_(ctxt, executable='python', file_=file_, output=output,
+                  args=args)
 
 def pylint(ctxt, file_=None):
     """Extract data from a `pylint` run written to a file."""
--- a/bitten/build/shtools.py
+++ b/bitten/build/shtools.py
@@ -27,22 +27,28 @@
 
 log = logging.getLogger('bitten.build.shtools')
 
-def exec_(ctxt, file_=None, output=None, args=None):
+def exec_(ctxt, executable=None, file_=None, output=None, args=None):
     """Execute a shell script."""
-    assert file_, 'Missing required attribute "file"'
+    assert executable or file_, \
+        'Either "executable" or "file" attribute required'
 
     if args:
         args = shlex.split(args)
     else:
         args = []
 
+    if executable is None:
+        executable = file_
+    elif file_:
+        args[:0] = [file_]
+
     output_file = None
     if output:
         output = ctxt.resolve(output)
         output_file = file(output, 'w')
 
     try:
-        cmdline = Commandline(file_, args, cwd=ctxt.basedir)
+        cmdline = Commandline(executable, args, cwd=ctxt.basedir)
         log_elem = xmlio.Fragment()
         for out, err in cmdline.execute():
             if out is not None:
@@ -61,4 +67,55 @@
             output_file.close()
 
     if cmdline.returncode != 0:
-        ctxt.error('exec failed (%s)' % cmdline.returncode)
+        ctxt.error('Executing %s failed (%s)' % (file_, cmdline.returncode))
+
+def pipe(ctxt, executable=None, file_=None, input_=None, output=None,
+         args=None):
+    """Pipe the contents of a file through a script."""
+    assert file_, 'Missing required attribute "file"'
+    assert input_, 'Missing required attribute "file"'
+
+    if args:
+        args = shlex.split(args)
+    else:
+        args = []
+
+    if os.path.isfile(ctxt.resolve(file_)):
+        file_ = ctxt.resolve(file_)
+
+    if executable is None:
+        executable = file_
+    elif file_:
+        args[:0] = [file_]
+
+    input_file = file(ctxt.resolve(input_), 'r')
+
+    output_file = None
+    if output:
+        output = ctxt.resolve(output)
+        output_file = file(output, 'w')
+
+    try:
+        cmdline = Commandline(executable, args, input=input_file,
+                              cwd=ctxt.basedir)
+        log_elem = xmlio.Fragment()
+        for out, err in cmdline.execute():
+            if out is not None:
+                log.info(out)
+                xmlio.SubElement(log_elem, 'message', level='info')[out]
+                if output:
+                    output_file.write(out + os.linesep)
+            if err is not None:
+                log.error(err)
+                xmlio.SubElement(log_elem, 'message', level='error')[err]
+                if output:
+                    output_file.write(err + os.linesep)
+        ctxt.log(log_elem)
+    finally:
+        input_file.close()
+        if output:
+            output_file.close()
+
+    if cmdline.returncode != 0:
+        ctxt.error('Piping through %s failed (%s)' % (file_,
+                   cmdline.returncode))
--- a/bitten/util/cmdline.py
+++ b/bitten/util/cmdline.py
@@ -20,7 +20,8 @@
 
 import logging
 import os
-import os.path
+import shlex
+import shutil
 import time
 
 log = logging.getLogger('bitten.cmdline')
@@ -34,7 +35,7 @@
     """Simple helper for executing subprocesses."""
     # TODO: Use 'subprocess' module if available (Python >= 2.4)
 
-    def __init__(self, executable, args, cwd=None):
+    def __init__(self, executable, args, input=None, cwd=None):
         """Initialize the Commandline object.
         
         @param executable The name of the program to execute
@@ -44,11 +45,32 @@
         """
         self.executable = executable
         self.arguments = [str(arg) for arg in args]
+        self.input = input
         self.cwd = cwd
         if self.cwd:
             assert os.path.isdir(self.cwd)
         self.returncode = None
 
+        # TODO: On windows, map file name extension to application
+        if os.name == 'nt':
+            pass
+
+        # Shebang support for Posix systems
+        if os.path.isfile(self.executable):
+            executable_file = file(self.executable, 'r')
+            try:
+                for line in executable_file:
+                    if line.startswith('#!'):
+                        parts = shlex.split(line[2:])
+                        if len(parts) > 1:
+                            self.arguments[:0] = parts[1:] + [self.executable]
+                        else:
+                            self.arguments[:0] = [self.executable]
+                        self.executable = parts[0]
+                    break
+            finally:
+                executable_file.close()
+
     if os.name == 'nt': # windows
 
         def execute(self, timeout=None):
@@ -59,6 +81,7 @@
             log.debug('Executing %s', args)
 
             if self.cwd:
+                old_cwd = os.getcwd()
                 os.chdir(self.cwd)
 
             import tempfile
@@ -78,16 +101,25 @@
                 yield out_line and out_line.rstrip(), \
                       err_line and err_line.rstrip()
 
+            if self.cwd:
+                os.chdir(old_cwd)
+
     else: # posix
 
         def execute(self, timeout=None):
             import fcntl, popen2, select
             if self.cwd:
+                old_cwd = os.getcwd()
                 os.chdir(self.cwd)
 
             log.debug('Executing %s', [self.executable] + self.arguments)
             pipe = popen2.Popen3([self.executable] + self.arguments,
                                  capturestderr=True)
+            if self.input:
+                if isinstance(self.input, basestring):
+                    pipe.tochild.write(self.input)
+                else:
+                    shutil.copyfileobj(self.input, pipe.tochild)
             pipe.tochild.close()
 
             def make_non_blocking(fd):
@@ -125,7 +157,11 @@
                     yield out_line, err_line
                 time.sleep(.1)
             self.returncode = pipe.wait()
-            log.debug('Exited with code %s', self.returncode)
+            log.debug('%s exited with code %s', self.executable,
+                      self.returncode)
+
+            if self.cwd:
+                os.chdir(old_cwd)
 
     def _combine(self, *iterables):
         iterables = [iter(iterable) for iterable in iterables]
Copyright (C) 2012-2017 Edgewall Software