| # 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) |