| Index: site_scons/site_tools/command_output.py
|
| ===================================================================
|
| --- site_scons/site_tools/command_output.py (revision 12583)
|
| +++ site_scons/site_tools/command_output.py (working copy)
|
| @@ -1,234 +0,0 @@
|
| -#!/usr/bin/python2.4
|
| -# Copyright 2008, Google Inc.
|
| -# All rights reserved.
|
| -#
|
| -# Redistribution and use in source and binary forms, with or without
|
| -# modification, are permitted provided that the following conditions are
|
| -# met:
|
| -#
|
| -# * Redistributions of source code must retain the above copyright
|
| -# notice, this list of conditions and the following disclaimer.
|
| -# * Redistributions in binary form must reproduce the above
|
| -# copyright notice, this list of conditions and the following disclaimer
|
| -# in the documentation and/or other materials provided with the
|
| -# distribution.
|
| -# * Neither the name of Google Inc. nor the names of its
|
| -# contributors may be used to endorse or promote products derived from
|
| -# this software without specific prior written permission.
|
| -#
|
| -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| -
|
| -"""Command output builder for SCons."""
|
| -
|
| -
|
| -import os
|
| -import signal
|
| -import subprocess
|
| -import sys
|
| -import threading
|
| -import time
|
| -import SCons.Script
|
| -
|
| -
|
| -# TODO(rspangler): Move KillProcessTree() and RunCommand() into their own
|
| -# module.
|
| -
|
| -
|
| -def KillProcessTree(pid):
|
| - """Kills the process and all of its child processes.
|
| -
|
| - Args:
|
| - pid: process to kill.
|
| -
|
| - Raises:
|
| - OSError: Unsupported OS.
|
| - """
|
| -
|
| - if sys.platform in ('win32', 'cygwin'):
|
| - # Use Windows' taskkill utility
|
| - killproc_path = '%s;%s\\system32;%s\\system32\\wbem' % (
|
| - (os.environ['SYSTEMROOT'],) * 3)
|
| - killproc_cmd = 'taskkill /F /T /PID %d' % pid
|
| - killproc_task = subprocess.Popen(killproc_cmd, shell=True,
|
| - stdout=subprocess.PIPE,
|
| - env={'PATH':killproc_path})
|
| - killproc_task.communicate()
|
| -
|
| - elif sys.platform in ('linux', 'linux2', 'darwin'):
|
| - # Use ps to get a list of processes
|
| - ps_task = subprocess.Popen(['/bin/ps', 'x', '-o', 'pid,ppid'], stdout=subprocess.PIPE)
|
| - ps_out = ps_task.communicate()[0]
|
| -
|
| - # Parse out a dict of pid->ppid
|
| - ppid = {}
|
| - for ps_line in ps_out.split('\n'):
|
| - w = ps_line.strip().split()
|
| - if len(w) < 2:
|
| - continue # Not enough words in this line to be a process list
|
| - try:
|
| - ppid[int(w[0])] = int(w[1])
|
| - except ValueError:
|
| - pass # Header or footer
|
| -
|
| - # For each process, kill it if it or any of its parents is our child
|
| - for p in ppid:
|
| - p2 = p
|
| - while p2:
|
| - if p2 == pid:
|
| - os.kill(p, signal.SIGKILL)
|
| - break
|
| - p2 = ppid.get(p2)
|
| -
|
| - else:
|
| - raise OSError('Unsupported OS for KillProcessTree()')
|
| -
|
| -
|
| -def RunCommand(cmdargs, cwdir=None, env=None, echo_output=True, timeout=None,
|
| - timeout_errorlevel=14):
|
| - """Runs an external command.
|
| -
|
| - Args:
|
| - cmdargs: A command string, or a tuple containing the command and its
|
| - arguments.
|
| - cwdir: Working directory for the command, if not None.
|
| - env: Environment variables dict, if not None.
|
| - echo_output: If True, output will be echoed to stdout.
|
| - timeout: If not None, timeout for command in seconds. If command times
|
| - out, it will be killed and timeout_errorlevel will be returned.
|
| - timeout_errorlevel: The value to return if the command times out.
|
| -
|
| - Returns:
|
| - The integer errorlevel from the command.
|
| - The combined stdout and stderr as a string.
|
| - """
|
| - # Force unicode string in the environment to strings.
|
| - if env:
|
| - env = dict([(k, str(v)) for k, v in env.items()])
|
| - start_time = time.time()
|
| - child = subprocess.Popen(cmdargs, cwd=cwdir, env=env, shell=True,
|
| - universal_newlines=True,
|
| - stdin=subprocess.PIPE,
|
| - stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
| - child_out = []
|
| - child_retcode = None
|
| -
|
| - def _ReadThread():
|
| - """Thread worker function to read output from child process.
|
| -
|
| - Necessary since there is no cross-platform way of doing non-blocking
|
| - reads of the output pipe.
|
| - """
|
| - read_run = True
|
| - while read_run:
|
| - # Need to have a delay of 1 cycle between child completing and
|
| - # thread exit, to pick up the final output from the child.
|
| - if child_retcode is not None:
|
| - read_run = False
|
| - new_out = child.stdout.read()
|
| - if new_out:
|
| - if echo_output:
|
| - print new_out,
|
| - child_out.append(new_out)
|
| -
|
| - read_thread = threading.Thread(target=_ReadThread)
|
| - read_thread.start()
|
| -
|
| - # Wait for child to exit or timeout
|
| - while child_retcode is None:
|
| - time.sleep(1) # So we don't poll too frequently
|
| - child_retcode = child.poll()
|
| - if timeout and child_retcode is None:
|
| - elapsed = time.time() - start_time
|
| - if elapsed > timeout:
|
| - print '*** RunCommand() timeout:', cmdargs
|
| - KillProcessTree(child.pid)
|
| - child_retcode = timeout_errorlevel
|
| -
|
| - # Wait for worker thread to pick up final output and die
|
| - read_thread.join(5)
|
| - if read_thread.isAlive():
|
| - print '*** Error: RunCommand() read thread did not exit.'
|
| - sys.exit(1)
|
| -
|
| - if echo_output:
|
| - print # end last line of output
|
| - return child_retcode, ''.join(child_out)
|
| -
|
| -
|
| -def CommandOutputBuilder(target, source, env):
|
| - """Command output builder.
|
| -
|
| - Args:
|
| - self: Environment in which to build
|
| - target: List of target nodes
|
| - source: List of source nodes
|
| -
|
| - Returns:
|
| - None or 0 if successful; nonzero to indicate failure.
|
| -
|
| - Runs the command specified in the COMMAND_OUTPUT_CMDLINE environment variable
|
| - and stores its output in the first target file. Additional target files
|
| - should be specified if the command creates additional output files.
|
| -
|
| - Runs the command in the COMMAND_OUTPUT_RUN_DIR subdirectory.
|
| - """
|
| - env = env.Clone()
|
| -
|
| - cmdline = env.subst('$COMMAND_OUTPUT_CMDLINE', target=target, source=source)
|
| - cwdir = env.subst('$COMMAND_OUTPUT_RUN_DIR', target=target, source=source)
|
| - if cwdir:
|
| - cwdir = os.path.normpath(cwdir)
|
| - env.AppendENVPath('PATH', cwdir)
|
| - env.AppendENVPath('LD_LIBRARY_PATH', cwdir)
|
| - else:
|
| - cwdir = None
|
| - cmdecho = env.get('COMMAND_OUTPUT_ECHO', True)
|
| - timeout = env.get('COMMAND_OUTPUT_TIMEOUT')
|
| - timeout_errorlevel = env.get('COMMAND_OUTPUT_TIMEOUT_ERRORLEVEL')
|
| -
|
| - retcode, output = RunCommand(cmdline, cwdir=cwdir, env=env['ENV'],
|
| - echo_output=cmdecho, timeout=timeout,
|
| - timeout_errorlevel=timeout_errorlevel)
|
| -
|
| - # Save command line output
|
| - output_file = open(str(target[0]), 'w')
|
| - output_file.write(output)
|
| - output_file.close()
|
| -
|
| - return retcode
|
| -
|
| -
|
| -def generate(env):
|
| - # NOTE: SCons requires the use of this name, which fails gpylint.
|
| - """SCons entry point for this tool."""
|
| -
|
| - # Add the builder and tell it which build environment variables we use.
|
| - action = SCons.Script.Action(
|
| - CommandOutputBuilder,
|
| - 'Output "$COMMAND_OUTPUT_CMDLINE" to $TARGET',
|
| - varlist=[
|
| - 'COMMAND_OUTPUT_CMDLINE',
|
| - 'COMMAND_OUTPUT_RUN_DIR',
|
| - 'COMMAND_OUTPUT_TIMEOUT',
|
| - 'COMMAND_OUTPUT_TIMEOUT_ERRORLEVEL',
|
| - # We use COMMAND_OUTPUT_ECHO also, but that doesn't change the
|
| - # command being run or its output.
|
| - ], )
|
| - builder = SCons.Script.Builder(action = action)
|
| - env.Append(BUILDERS={'CommandOutput': builder})
|
| -
|
| - # Default command line is to run the first input
|
| - env['COMMAND_OUTPUT_CMDLINE'] = '$SOURCE'
|
| -
|
| - # TODO(rspangler): add a pseudo-builder which takes an additional command
|
| - # line as an argument.
|
|
|