15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Redistribution and use in source and binary forms, with or without 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# modification, are permitted provided that the following conditions 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# are met: 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 1. Redistributions of source code must retain the above copyright 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# notice, this list of conditions and the following disclaimer. 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 2. Redistributions in binary form must reproduce the above copyright 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# notice, this list of conditions and the following disclaimer in the 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# documentation and/or other materials provided with the distribution. 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import codecs 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import logging 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import sys 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import webkitpy.style.checker as checker 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.style.patchreader import PatchReader 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.style.checker import StyleProcessor 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.style.filereader import TextFileReader 315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.common.host import Host 325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)_log = logging.getLogger(__name__) 355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def change_directory(filesystem, checkout_root, paths): 385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) """Change the working directory to the WebKit checkout root, if possible. 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) If every path in the paths parameter is below the checkout root (or if 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) the paths parameter is empty or None), this method changes the current 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) working directory to the checkout root and converts the paths parameter 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) as described below. 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) This allows the paths being checked to be displayed relative to the 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) checkout root, and for path-specific style checks to work as expected. 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Path-specific checks include whether files should be skipped, whether 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) custom style rules should apply to certain files, etc. 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Returns: 505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paths: A copy of the paths parameter -- possibly converted, as follows. 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) If this method changed the current working directory to the 525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) checkout root, then the list is the paths parameter converted to 535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) normalized paths relative to the checkout root. 545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Args: 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paths: A list of paths to the files that should be checked for style. 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) This argument can be None or the empty list if a git commit 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) or all changes under the checkout root should be checked. 595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) checkout_root: The path to the root of the WebKit checkout. 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) """ 625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if paths is not None: 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paths = list(paths) 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if paths: 665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Then try converting all of the paths to paths relative to 675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # the checkout root. 685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rel_paths = [] 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for path in paths: 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rel_path = filesystem.relpath(path, checkout_root) 715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if rel_path.startswith(filesystem.pardir): 725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Then the path is not below the checkout root. Since all 735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # paths should be interpreted relative to the same root, 745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # do not interpret any of the paths as relative to the 755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # checkout root. Interpret all of them relative to the 765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # current working directory, and do not change the current 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # working directory. 785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.warn( 795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)"""Path-dependent style checks may not work correctly: 805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) One of the given paths is outside the WebKit checkout of the current 825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) working directory: 835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Path: %s 855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Checkout root: %s 865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Pass only files below the checkout root to ensure correct results. 885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) See the help documentation for more info. 895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)""" 905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) % (path, checkout_root)) 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return paths 935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rel_paths.append(rel_path) 945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # If we got here, the conversion was successful. 955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paths = rel_paths 965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug("Changing to checkout root: " + checkout_root) 985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) filesystem.chdir(checkout_root) 995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return paths 1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class CheckWebKitStyle(object): 1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def _engage_awesome_stderr_hacks(self): 1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Change stderr to write with replacement characters so we don't die 1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # if we try to print something containing non-ASCII characters. 1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) stderr = codecs.StreamReaderWriter(sys.stderr, 1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) codecs.getreader('utf8'), 1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) codecs.getwriter('utf8'), 1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 'replace') 1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Setting an "encoding" attribute on the stream is necessary to 1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # prevent the logging module from raising an error. See 1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # the checker.configure_logging() function for more information. 1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) stderr.encoding = "UTF-8" 1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # FIXME: Change webkitpy.style so that we do not need to overwrite 1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # the global sys.stderr. This involves updating the code to 1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # accept a stream parameter where necessary, and not calling 1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # sys.stderr explicitly anywhere. 1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) sys.stderr = stderr 1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return stderr 1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def main(self): 1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) args = sys.argv[1:] 1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) host = Host() 1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) host.initialize_scm() 1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) stderr = self._engage_awesome_stderr_hacks() 1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Checking for the verbose flag before calling check_webkit_style_parser() 1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # lets us enable verbose logging earlier. 1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) is_verbose = "-v" in args or "--verbose" in args 1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) checker.configure_logging(stream=stderr, is_verbose=is_verbose) 1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug("Verbose logging enabled.") 1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) parser = checker.check_webkit_style_parser() 1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) (paths, options) = parser.parse(args) 1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) configuration = checker.check_webkit_style_configuration(options) 1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paths = change_directory(host.filesystem, checkout_root=host.scm().checkout_root, paths=paths) 1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) style_processor = StyleProcessor(configuration) 1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) file_reader = TextFileReader(host.filesystem, style_processor) 1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if paths and not options.diff_files: 1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) file_reader.process_paths(paths) 1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else: 1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) changed_files = paths if options.diff_files else None 1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) patch = host.scm().create_patch(options.git_commit, changed_files=changed_files) 1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) patch_checker = PatchReader(file_reader) 1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) patch_checker.check(patch) 1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) error_count = style_processor.error_count 1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) file_count = file_reader.file_count 1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) delete_only_file_count = file_reader.delete_only_file_count 1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.info("Total errors found: %d in %d files" % (error_count, file_count)) 1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # We fail when style errors are found or there are no checked files. 1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return error_count > 0 or (file_count == 0 and delete_only_file_count == 0) 163