diff options
author | George Burgess IV <gbiv@google.com> | 2019-05-08 13:06:05 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-05-17 18:37:28 -0700 |
commit | adb9f7a366cb5e8ae5eb5c860da191e0418e8901 (patch) | |
tree | 6ab898b08b69081385b5b2e3fe9269cf6a3ac858 /clang_tidy | |
parent | f7e10ed99a41c8cded9cf1ce01aef2c7116318c4 (diff) | |
download | toolchain-utils-adb9f7a366cb5e8ae5eb5c860da191e0418e8901.tar.gz |
clang_tidy: DCE and simplify
This patch contains a few things:
- We've synced to a fixed |warn.py|, so we can remove an old workaround.
- Our remaining call to clang_tidy_execute can apparently be replaced by
a call to |subprocess.check_call|, which removes what is apparently
our last use of clang_tidy_execute.
- This slightly restructures code to just |shutil.rmtree| instead of
|os.remove|, since it lets us avoid try/except blocks with its
|ignore_errors| param.
BUG=chromium:960495
TEST=llvm-clang-tidy-toolchain-tryjob
Change-Id: Ia7b20db03c884d244b15198c4c9995b5d1e71220
Reviewed-on: https://chromium-review.googlesource.com/1601198
Commit-Ready: George Burgess <gbiv@chromium.org>
Tested-by: George Burgess <gbiv@chromium.org>
Reviewed-by: Caroline Tice <cmtice@chromium.org>
Diffstat (limited to 'clang_tidy')
-rwxr-xr-x | clang_tidy/clang-tidy-parse-build-log.py | 44 | ||||
-rwxr-xr-x | clang_tidy/clang_tidy_execute.py | 506 |
2 files changed, 20 insertions, 530 deletions
diff --git a/clang_tidy/clang-tidy-parse-build-log.py b/clang_tidy/clang-tidy-parse-build-log.py index e65fcc94..d207483d 100755 --- a/clang_tidy/clang-tidy-parse-build-log.py +++ b/clang_tidy/clang-tidy-parse-build-log.py @@ -11,10 +11,10 @@ from __future__ import print_function import argparse import datetime import os +import shutil +import subprocess import sys -import clang_tidy_execute - def Main(argv): parser = argparse.ArgumentParser() @@ -89,29 +89,25 @@ def Main(argv): warnfile = os.path.join(output_dir, html_filename) warnfile_csv = os.path.join(output_dir, csv_filename) - result = clang_tidy_execute.Execute( - 'python %s %s ' % (warn_script, logfile) + - '--csvpath %s --url http://cs/android --separator "?l=" > %s' % - (warnfile_csv, warnfile)) - - # Handle if we are running on an older version of warn.py - # that does not have support for --csvpath added in - # aosp/369755 - if result.returncode == 2: - result = clang_tidy_execute.Execute( - 'python %s %s ' % (warn_script, logfile) + - '--url http://cs/android --separator "?l=" > %s' % warnfile) - - if result.returncode != 0: + run_warn_py = [ + 'python', + warn_script, + logfile, + '--csvpath', + warnfile_csv, + '--url', + 'http://cs/android', + '--separator', + '?l=', + ] + + try: + with open('/dev/null') as stdin, open(warnfile, 'w') as stdout: + subprocess.check_call(run_warn_py, stdin=stdin, stdout=stdout) + except subprocess.CalledProcessError: print("Couldn't generate warnings.html", file=sys.stderr) - try: - os.remove(warnfile) - except EnvironmentError: - pass - try: - os.remove(warnfile_csv) - except EnvironmentError: - pass + shutil.rmtree(warnfile, ignore_errors=True) + shutil.rmtree(warnfile_csv, ignore_errors=True) return 1 return 0 diff --git a/clang_tidy/clang_tidy_execute.py b/clang_tidy/clang_tidy_execute.py deleted file mode 100755 index ec49b922..00000000 --- a/clang_tidy/clang_tidy_execute.py +++ /dev/null @@ -1,506 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -# Copyright 2018 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""For executing subcommands.""" - -from __future__ import print_function - -import os -import signal -import subprocess -import tempfile -import threading - -_BUFFER_SIZE = 1 - - -class ExecOutput(object): - """The results of any Execute operation. - - Attributes: - returncode: The return code for the process. - cmd: The actual command that was run. This may include extra - arguments that were not asked for but needed for proper - operation, like shell wrappers. - cwd: Current working directory. - rawcmd: The command that was asked to be run. - stdout: The stdout of the command. - sdterr: The stderr of the command. - """ - - def __init__(self, - returncode, - cmd=None, - cwd=None, - stdout=None, - stderr=None, - rawcmd=None): - self.returncode = returncode - self.cmd = cmd - self.cwd = cwd - self.rawcmd = rawcmd - self.stdout = stdout - self.stderr = stderr - - -class _Muxer(threading.Thread): - """A single thread that reads and collects both mixed and unmixed output.""" - - def __init__(self, unmixed, mixed, lock): - """Create a new _Muxer. - - Args: - unmixed: The stream to write unmixed content to. - mixed: The stream to write mixed content to. - lock: The lock to use to protect access to the mixed content. - """ - threading.Thread.__init__(self) - self._unmixed = unmixed - self._mixed = mixed - self._lock = lock - self._buffer = '' - (self._reader, self.writer) = os.pipe() - self._complete_event = threading.Event() - - def _ProcessData(self, data): - """Buffer the data and emit it when we reach a full line.""" - # Append the data to the buffer - self._buffer += data - # If newline, flush the buffer. - if data == '\n': - self._FlushBuffer() - - def run(self): - data = os.read(self._reader, _BUFFER_SIZE) - while data: - self._ProcessData(data) - data = os.read(self._reader, _BUFFER_SIZE) - # The underlying filehandle has been closed, so there is no more - # data to read. Flush any data we still have in the buffer to our - # files, and then have the os flush the data to disk. This will - # ensure anybody trying to read the data later has it all. - self._FlushBuffer() - self._unmixed.flush() - with self._lock: - self._mixed.flush() - - def _FlushBuffer(self): - if self._buffer: - self._unmixed.write(self._buffer) - # The mixed buffer is shared between Muxers, so requires a lock. - with self._lock: - self._mixed.write(self._buffer) - self._buffer = '' - - def Stop(self): - """Stop the muxer, and wait for the background thread to complete.""" - # Closing the self.writer will cause the os.read() inside run to - # return EOF, which will cause the thread to exit. - os.close(self.writer) - self.join() - os.close(self._reader) - - -class _CommandOutputMuxer(object): - """A context to use when collecting stdout/stderr logs. - - This class provides an easy way to manage collecting logs from a - process. It provides ways to collect stdout, stderr, and a mixed - log which intermixes stdout and stderr. - """ - - def __init__(self, stdout=None, stderr=None, mixed=None): - """Create a new _CommandOutputMuxer. - - Args: - stdout: The file stream used to collect stdout. - stderr: The file stream used to collect stderr. - mixed: The file stream used to collect the mixed stdout/stderr output. - """ - self._stdout = stdout or tempfile.TemporaryFile(mode='w+') - self._stdout_offset = self._stdout.tell() - self._stderr = stderr or tempfile.TemporaryFile(mode='w+') - self._stderr_offset = self._stderr.tell() - self._mixed = mixed or tempfile.TemporaryFile(mode='w+') - - self._mixed_lock = threading.Lock() - - self._stdout_muxer = _Muxer( - unmixed=self._stdout, mixed=self._mixed, lock=self._mixed_lock) - self._stderr_muxer = _Muxer( - unmixed=self._stderr, mixed=self._mixed, lock=self._mixed_lock) - - def Run(self, command, timeout, **kwargs): - """Wrapper method for running the command. - - This allows CommandOutputMuxer to better control the lifecycle of - the underlying command to make sure data is buffered correctly and - everything is cleaned up. - - Args: - command: The command to run - timeout: How long (in seconds) to wait for the command to - finish, or 0 to wait forever - **kwargs: Arguments to pass to subprocess.Popen() - - Returns: - Command return code. - - Raises: - TimeoutException: If the command takes too long to execute. - """ - # Specify some local args that are required for this class to work. - local_args = dict( - args=command, - close_fds=True, - stdin=subprocess.PIPE, - stdout=self._stdout_muxer.writer, - stderr=self._stderr_muxer.writer) - local_args.update(**kwargs) - - p = subprocess.Popen(**local_args) - p.stdin.close() - try: - return _PopenWaitWithTimeout(p, timeout) - finally: - # Now that we are done running, stop the background threads to - # ensure that all the data they are collecting is properly - # collected and buffered to disk before we return - self._stdout_muxer.Stop() - self._stderr_muxer.Stop() - - def _Read(self, fh, offset): - fh.seek(offset) - data = fh.read() - return data - - def ReadStdout(self): - return self._Read(self._stdout, self._stdout_offset) - - def ReadStderr(self): - return self._Read(self._stderr, self._stderr_offset) - - def __enter__(self): - self._stdout_muxer.start() - self._stderr_muxer.start() - return self - - def __exit__(self, exc_type, exc_value, traceback): - exc_type = exc_type # unused - exc_value = exc_value # unused - traceback = traceback # unused - - self._stdout.close() - self._stderr.close() - self._mixed.close() - - -class TimeoutException(Exception): - """Raised when an Execute call times out.""" - - -# This error code is the same code that alarm returns on linux for -# timeout. -TIMEOUT_ERROR_CODE = 142 - - -def _PopenWaitWithTimeout(process, timeout): - """Do Popen.wait, but with a timeout. - - Args: - process: the Popen object itself. - timeout: How long to wait. - - Returns: - The error code from the child process - - Raises: - TimeoutException: if the command takes too long. - """ - - def Kill(): - print('Killing process %d after %s seconds', process.pid, timeout) - # process.termiante doesn't seem to always kill the process. This - # uses the same method the alarm command used. - pgid = os.getpgid(process.id) - os.killpg(pgid, signal.KILL) - - timer = threading.Timer(timeout, Kill) - timer.start() - try: - returncode = process.wait() - finally: - if timer.is_alive(): - timer.cancel() - else: - raise TimeoutException() - return returncode - - -def ExecuteWithTimeout(cmd, timeout, cwd=None, env=None, ignore_output=False): - """Execute a command from shell. - - Args: - cmd: A string command to be executed. - timeout: How many seconds to wait, or forever if not specified - cwd: The current working directory, or buildbot_root if not specified. - env: A dict for the envrionment to pass to the subprocess. - ignore_output: If specified, ExecOuput.stderr and - ExecOutput.stdout are not collected nor returned to - the caller. This is useful in cases where - stdout/stderr are known to be huge, or in cases - where the caller knows it doesn't need the output. - - Returns: - ExecOutput object with results of command - """ - return _Execute( - cmd=cmd, cwd=cwd, env=env, timeout=timeout, ignore_output=ignore_output) - - -def Execute(cmd, cwd=None, env=None, ignore_output=False): - """Execute a command from shell. - - Args: - cmd: A string command to be executed. - cwd: The current working directory, or buildbot_root if not specified. - env: A dict for the envrionment to pass to the subprocess. - ignore_output: If specified, ExecOuput.stderr and - ExecOutput.stdout are not collected nor returned to - the caller. This is useful in cases where - stdout/stderr are known to be huge, or in cases - where the caller knows it doesn't need the output. - - Returns: - ExecOutput object with results of command - """ - return _Execute(cmd=cmd, cwd=cwd, env=env, ignore_output=ignore_output) - - -def ExecuteWithTimeoutAndLogfile(cmd, - timeout, - logfile, - cwd=None, - env=None, - ignore_output=False): - """Execute a command from shell with a timeout and logfile. - - The logfile in this function is a mixture of stdout and stderr from - the command. Since the results are written out to a logfile, stdout - and stderr are NOT available from the ExecOutput returned from this - function. - - Args: - cmd: A string command to be executed. - timeout: How many seconds to wait, or forever if not specified - logfile: Where to stdout and stderr from the command. - cwd: The current working directory, or buildbot_root if not specified. - env: A dict for the envrionment to pass to the subprocess. - ignore_output: If specified, ExecOuput.stderr and - ExecOutput.stdout are not collected nor returned to - the caller. This is useful in cases where - stdout/stderr are known to be huge, or in cases - where the caller knows it doesn't need the output. - - Returns: - ExecOutput object with results of command - """ - return _Execute( - cmd=cmd, - cwd=cwd, - env=env, - timeout=timeout, - logfile=logfile, - ignore_output=ignore_output) - - -def ExecuteWithLogfile(cmd, logfile, cwd=None, env=None, ignore_output=False): - """Execute a command from shell with a logfile. - - The logfile in this function is a mixture of stdout and stderr from - the command. Since the results are written out to a logfile, stdout - and stderr are NOT available from the ExecOutput returned from this - function. - - Args: - cmd: A string command to be executed. - logfile: Where to stdout and stderr from the command. If - specified, the results of stdout and stderr are not provided - in the result of this call. Keep in mind that if you don't - specify a logfile, the results are read back into memory. - cwd: The current working directory, or buildbot_root if not specified. - env: A dict for the envrionment to pass to the subprocess. - ignore_output: If specified, ExecOuput.stderr and - ExecOutput.stdout are not collected nor returned to - the caller. This is useful in cases where - stdout/stderr are known to be huge, or in cases - where the caller knows it doesn't need the output. - - Returns: - ExecOutput object with results of command - """ - return _Execute( - cmd=cmd, cwd=cwd, env=env, logfile=logfile, ignore_output=ignore_output) - - -def ExecuteWithTimeoutAndStderrLogfile(cmd, - timeout, - logfile, - stderr_logfile, - cwd=None, - env=None, - ignore_output=False): - """Execute a command from shell with logfiles and a timeout. - - The logfile in this function is a mixture of stdout and stderr from - the command. The stderr_logfile just contains the stderr output. - Since the results are written out to logfiles, stdout and stderr are - NOT available from the ExecOutput returned from this function. - - Args: - cmd: A string command to be executed. - timeout: How many seconds to wait, or forever if not specified - logfile: Where to stdout and stderr from the command. - stderr_logfile: Where to log stderr from the command. - cwd: The current working directory, or buildbot_root if not specified. - env: A dict for the envrionment to pass to the subprocess. - ignore_output: If specified, ExecOuput.stderr and - ExecOutput.stdout are not collected nor returned to - the caller. This is useful in cases where - stdout/stderr are known to be huge, or in cases - where the caller knows it doesn't need the output. - - Returns: - ExecOutput object with results of command - """ - return _Execute( - cmd=cmd, - cwd=cwd, - env=env, - timeout=timeout, - logfile=logfile, - stderr_logfile=stderr_logfile, - ignore_output=ignore_output) - - -def ExecuteWithStderrLogfile(cmd, logfile, stderr_logfile, cwd=None, env=None): - """Execute a command from shell with logfiles and a timeout. - - The logfile in this function is a mixture of stdout and stderr from - the command. The stderr_logfile just contains the stderr output. - Since the results are written out to logfiles, stdout and stderr are - NOT available from the ExecOutput returned from this function. - - Args: - cmd: A string command to be executed. - logfile: Where to stdout and stderr from the command. - stderr_logfile: Where to log stderr from the command. - cwd: The current working directory, or buildbot_root if not specified. - env: A dict for the envrionment to pass to the subprocess. - - Returns: - ExecOutput object with results of command - """ - return _Execute( - cmd=cmd, cwd=cwd, env=env, logfile=logfile, stderr_logfile=stderr_logfile) - - -# It's nice to have all the different ways of calling Exceute -# implemented here inside this function. Except with all it's options -# and flags and stuff, it exposes a pretty bad API interface. So -# rather than inflict that on the rest of the buildbot, we'll wrap -# this ugly API into nicer APIs above. -def _Execute(cmd, - cwd=None, - env=None, - timeout=None, - logfile=None, - stderr_logfile=None, - raise_on_timeout=False, - ignore_output=False): - """Execute a command from shell and returns the result. - - Args: - cmd: A string command to be executed. - cwd: The current working directory, or buildbot_root if not specified. - env: A dict for the envrionment to pass to the subprocess. - timeout: How many seconds to wait, or forever if not specified - logfile: Where to stdout and stderr from the command. If - specified, the results of stdout and stderr are not provided - in the result of this call. Keep in mind that if you don't - specify a logfile, the results are read back into memory. - stderr_logfile: Where to log stderr from the command. Requires - logfile to be set. - raise_on_timeout: controls if we raise TimeoutException on timeout - or return TIMEOUT_ERROR_CODE. - ignore_output: If specified, ExecOuput.stderr and - ExecOutput.stdout are not collected nor returned to - the caller. This is useful in cases where - stdout/stderr are known to be huge, or in cases - where the caller knows it doesn't need the output. - - Returns: - ExecOutput object with results of command - - Raises: - TimeoutException: if timeout specified and command did not - complete in time if raise_on_timeout is true. - """ - cwd = cwd or os.getcwd() - - # Python provides no way to override which shell is used for - # shell=True (it uses /bin/sh by default), so we'll override it here - # to ensure we use bash. - command = ['/bin/bash', '-c', cmd] - info_line = 'Executing: %s from %s\n' % (command, cwd) - print(info_line) - - # Default values - stdout = None - stderr = None - if logfile: - # Open stdout and write header line - stdout = open(logfile, 'a+') - stdout.write(info_line) - stdout.flush() - - # If also log stderr to it's own logfile, set that up here. - if stderr_logfile: - stderr = open(stderr_logfile, 'a+') - # Seek to end of file. _CommandOutputMuxer assumes the - # filehandle's offset is already at the end. It turns out that - # when opening a file in 'a', the offset isn't set until data is - # written. - stderr.seek(0, 2) - - with _CommandOutputMuxer(mixed=stdout, stderr=stderr) as mux: - try: - returncode = mux.Run(command=command, timeout=timeout, env=env, cwd=cwd) - except TimeoutException as e: - if raise_on_timeout: - raise e - returncode = TIMEOUT_ERROR_CODE - - info_line = 'Return Code: %d' % returncode - print(info_line) - if stdout: - stdout.write(info_line + '\n') - - # Setup stdout and stderr for returning to the caller. Note that - # stdout is explicitly JUST stdout here and not the mixed output. - # This enables callers to easily be able to run a command and - # parse the results without having to worry about whatever the - # command put on stderr. - stdout_return = None if ignore_output else mux.ReadStdout() - stderr_return = None if ignore_output else mux.ReadStderr() - - return ExecOutput( - cmd=command, - rawcmd=cmd, - cwd=cwd, - returncode=returncode, - stdout=stdout_return, - stderr=stderr_return) |