aboutsummaryrefslogtreecommitdiff
path: root/clang_tidy
diff options
context:
space:
mode:
authorGeorge Burgess IV <gbiv@google.com>2019-05-08 13:06:05 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-05-17 18:37:28 -0700
commitadb9f7a366cb5e8ae5eb5c860da191e0418e8901 (patch)
tree6ab898b08b69081385b5b2e3fe9269cf6a3ac858 /clang_tidy
parentf7e10ed99a41c8cded9cf1ce01aef2c7116318c4 (diff)
downloadtoolchain-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-xclang_tidy/clang-tidy-parse-build-log.py44
-rwxr-xr-xclang_tidy/clang_tidy_execute.py506
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)