| # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) | |
| # | |
| # Redistribution and use in source and binary forms, with or without | |
| # modification, are permitted provided that the following conditions | |
| # are met: | |
| # 1. Redistributions of source code must retain the above copyright | |
| # notice, this list of conditions and the following disclaimer. | |
| # 2. 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. | |
| # | |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. | |
| import codecs | |
| import logging | |
| import sys | |
| from webkitpy.common.host import Host | |
| from webkitpy.style import checker | |
| from webkitpy.style.checker import StyleProcessor | |
| from webkitpy.style.filereader import TextFileReader | |
| from webkitpy.style.patchreader import PatchReader | |
| _log = logging.getLogger(__name__) | |
| def change_directory(filesystem, checkout_root, paths): | |
| """Change the working directory to the checkout root, if possible. | |
| If every path in the paths parameter is below the checkout root (or if | |
| the paths parameter is empty or None), this method changes the current | |
| working directory to the checkout root and converts the paths parameter | |
| as described below. | |
| This allows the paths being checked to be displayed relative to the | |
| checkout root, and for path-specific style checks to work as expected. | |
| Path-specific checks include whether files should be skipped, whether | |
| custom style rules should apply to certain files, etc. | |
| Args: | |
| paths: A list of paths to the files that should be checked for style. | |
| This argument can be None or the empty list if a git commit | |
| or all changes under the checkout root should be checked. | |
| checkout_root: The path to the root of the repository. | |
| Returns: | |
| A copy of the paths parameter -- possibly converted, as follows. | |
| If this method changed the current working directory to the | |
| checkout root, then the list is the paths parameter converted to | |
| normalized paths relative to the checkout root. | |
| """ | |
| if paths is not None: | |
| paths = list(paths) | |
| if paths: | |
| # Then try converting all of the paths to paths relative to | |
| # the checkout root. | |
| rel_paths = [] | |
| for path in paths: | |
| rel_path = filesystem.relpath(path, checkout_root) | |
| if rel_path.startswith(filesystem.pardir): | |
| # Then the path is not below the checkout root. Since all | |
| # paths should be interpreted relative to the same root, | |
| # do not interpret any of the paths as relative to the | |
| # checkout root. Interpret all of them relative to the | |
| # current working directory, and do not change the current | |
| # working directory. | |
| _log.warning( | |
| """Path-dependent style checks may not work correctly: | |
| One of the given paths is outside the repository of the current | |
| working directory: | |
| Path: %s | |
| Checkout root: %s | |
| Pass only files below the checkout root to ensure correct results. | |
| See the help documentation for more info. | |
| """, | |
| path, checkout_root) | |
| return paths | |
| rel_paths.append(rel_path) | |
| # If we got here, the conversion was successful. | |
| paths = rel_paths | |
| _log.debug('Changing to checkout root: ' + checkout_root) | |
| filesystem.chdir(checkout_root) | |
| return paths | |
| class CheckWebKitStyle(object): | |
| def _engage_awesome_stderr_hacks(self): | |
| # Change stderr to write with replacement characters so we don't die | |
| # if we try to print something containing non-ASCII characters. | |
| stderr = codecs.StreamReaderWriter(sys.stderr, | |
| codecs.getreader('utf8'), | |
| codecs.getwriter('utf8'), | |
| 'replace') | |
| # Setting an "encoding" attribute on the stream is necessary to | |
| # prevent the logging module from raising an error. See | |
| # the checker.configure_logging() function for more information. | |
| stderr.encoding = 'UTF-8' | |
| # FIXME: Change webkitpy.style so that we do not need to overwrite | |
| # the global sys.stderr. This involves updating the code to | |
| # accept a stream parameter where necessary, and not calling | |
| # sys.stderr explicitly anywhere. | |
| sys.stderr = stderr | |
| return stderr | |
| def main(self): | |
| args = sys.argv[1:] | |
| host = Host() | |
| stderr = self._engage_awesome_stderr_hacks() | |
| # Checking for the verbose flag before calling check_webkit_style_parser() | |
| # lets us enable verbose logging earlier. | |
| is_verbose = '-v' in args or '--verbose' in args | |
| checker.configure_logging(stream=stderr, is_verbose=is_verbose) | |
| _log.debug('Verbose logging enabled.') | |
| parser = checker.check_webkit_style_parser() | |
| (paths, options) = parser.parse(args) | |
| configuration = checker.check_webkit_style_configuration(options) | |
| paths = change_directory(host.filesystem, checkout_root=host.git().checkout_root, paths=paths) | |
| style_processor = StyleProcessor(configuration) | |
| file_reader = TextFileReader(host.filesystem, style_processor) | |
| if paths and not options.diff_files: | |
| file_reader.process_paths(paths) | |
| else: | |
| changed_files = paths if options.diff_files else None | |
| patch = host.git().create_patch(options.git_commit, changed_files=changed_files) | |
| patch_checker = PatchReader(file_reader) | |
| patch_checker.check(patch) | |
| error_count = style_processor.error_count | |
| file_count = file_reader.file_count | |
| delete_only_file_count = file_reader.delete_only_file_count | |
| _log.info('Total errors found: %d in %d files', error_count, file_count) | |
| # We fail when style errors are found or there are no checked files. | |
| return error_count > 0 or (file_count == 0 and delete_only_file_count == 0) |