summaryrefslogtreecommitdiffstats
path: root/site_scons/site_tools/command_output.py
diff options
context:
space:
mode:
Diffstat (limited to 'site_scons/site_tools/command_output.py')
-rw-r--r--site_scons/site_tools/command_output.py234
1 files changed, 0 insertions, 234 deletions
diff --git a/site_scons/site_tools/command_output.py b/site_scons/site_tools/command_output.py
deleted file mode 100644
index 795f3eb..0000000
--- a/site_scons/site_tools/command_output.py
+++ /dev/null
@@ -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.