text: # Copyright (c) 2021, Google Inc. All rights reserved.
  #
  # Redistribution and use in source and binary forms, with or without
  # modification, are permitted provided that the following conditions are
  # met:
  #
  #   * Redistributions of source code must retain the above copyright
  #     notice, this list of conditions and the following disclaimer.
  #
  #   * 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.
  #
  #   * Neither the name of Google nor the names of its contributors may
  #     be used to endorse or promote products derived from this software
  #     without specific prior written permission.
  #
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
  # HOLDER OR 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.
  """Top-level presubmit script for libwebp.
  
  See https://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
  details on the presubmit API built into depot_tools.
  """
  
  import re
  import subprocess2
  
  USE_PYTHON3 = True
  _BASH_INDENTATION = "2"
  _GIT_COMMIT_SUBJECT_LENGTH = 65
  _INCLUDE_BASH_FILES_ONLY = [r".*\.sh$"]
  _INCLUDE_MAN_FILES_ONLY = [r"man/.+\.1$"]
  _LIBWEBP_MAX_LINE_LENGTH = 80
  
  
  def _CheckCommitSubjectLength(input_api, output_api):
    """Ensures commit's subject length is no longer than 65 chars."""
    name = "git-commit subject"
    cmd = ["git", "log", "-1", "--pretty=%s"]
    start = input_api.time.time()
    proc = subprocess2.Popen(
        cmd,
        stderr=subprocess2.PIPE,
        stdout=subprocess2.PIPE,
        universal_newlines=True)
  
    stdout, _ = proc.communicate()
    duration = input_api.time.time() - start
  
    if not re.match(r"^Revert",
                    stdout) and (len(stdout) - 1) > _GIT_COMMIT_SUBJECT_LENGTH:
      failure_msg = (
          "The commit subject: %s is too long (%d chars)\n"
          "Try to keep this to 50 or less (up to 65 is permitted for "
          "non-reverts).\n"
          "https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-"
          "Project#_commit_guidelines") % (stdout, len(stdout) - 1)
      return output_api.PresubmitError("%s\n (%4.2fs) failed\n%s" %
                                       (name, duration, failure_msg))
  
    return output_api.PresubmitResult("%s\n (%4.2fs) success" % (name, duration))
  
  
  def _GetFilesToSkip(input_api):
    return list(input_api.DEFAULT_FILES_TO_SKIP) + [
        r"swig/.*\.py$",
        r"\.pylintrc$",
    ]
  
  
  def _RunManCmd(input_api, output_api, man_file):
    """man command wrapper."""
    cmd = ["man", "--warnings", "-EUTF-8", "-l", "-Tutf8", "-Z", man_file]
    name = "Check %s file." % man_file
    start = input_api.time.time()
    output, _ = subprocess2.communicate(
        cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
    duration = input_api.time.time() - start
    if output[1]:
      return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
                                       (name, " ".join(cmd), duration, output[1]))
    return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
                                      (name, " ".join(cmd), duration))
  
  
  def _RunShellCheckCmd(input_api, output_api, bash_file):
    """shellcheck command wrapper."""
    cmd = ["shellcheck", "-x", "-oall", "-sbash", bash_file]
    name = "Check %s file." % bash_file
    start = input_api.time.time()
    output, rc = subprocess2.communicate(
        cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
    duration = input_api.time.time() - start
    if rc == 0:
      return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
                                        (name, " ".join(cmd), duration))
    return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
                                     (name, " ".join(cmd), duration, output[1]))
  
  
  def _RunShfmtCheckCmd(input_api, output_api, bash_file):
    """shfmt command wrapper."""
    cmd = [
        "shfmt", "-i", _BASH_INDENTATION, "-bn", "-ci", "-sr", "-kp", "-d",
        bash_file
    ]
    name = "Check %s file." % bash_file
    start = input_api.time.time()
    output, rc = subprocess2.communicate(
        cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
    duration = input_api.time.time() - start
    if rc == 0:
      return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
                                        (name, " ".join(cmd), duration))
    return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
                                     (name, " ".join(cmd), duration, output[1]))
  
  
  def _RunCmdOnCheckedFiles(input_api, output_api, run_cmd, files_to_check):
    """Ensure that libwebp/ files are clean."""
    file_filter = lambda x: input_api.FilterSourceFile(
        x, files_to_check=files_to_check, files_to_skip=None)
  
    affected_files = input_api.change.AffectedFiles(file_filter=file_filter)
    results = [
        run_cmd(input_api, output_api, f.AbsoluteLocalPath())
        for f in affected_files
    ]
    return results
  
  
  def _CommonChecks(input_api, output_api):
    """Ensures this patch does not have trailing spaces, extra EOLs,
       or long lines.
    """
    results = []
    results.extend(
        input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol(
            input_api, output_api))
    results.extend(
        input_api.canned_checks.CheckChangeHasNoTabs(input_api, output_api))
    results.extend(
        input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
            input_api, output_api))
    results.append(_CheckCommitSubjectLength(input_api, output_api))
  
    source_file_filter = lambda x: input_api.FilterSourceFile(
        x, files_to_skip=_GetFilesToSkip(input_api))
    results.extend(
        input_api.canned_checks.CheckLongLines(
            input_api,
            output_api,
            maxlen=_LIBWEBP_MAX_LINE_LENGTH,
            source_file_filter=source_file_filter))
  
    results.extend(
        input_api.canned_checks.CheckPatchFormatted(
            input_api,
            output_api,
            check_clang_format=False,
            check_python=True,
            result_factory=output_api.PresubmitError))
    results.extend(
        _RunCmdOnCheckedFiles(input_api, output_api, _RunManCmd,
                              _INCLUDE_MAN_FILES_ONLY))
    # Run pylint.
    results.extend(
        input_api.canned_checks.RunPylint(
            input_api,
            output_api,
            files_to_skip=_GetFilesToSkip(input_api),
            pylintrc=".pylintrc",
            version="2.7"))
  
    # Binaries shellcheck and shfmt are not installed in depot_tools.
    # Installation is needed
    try:
      subprocess2.communicate(["shellcheck", "--version"])
      results.extend(
          _RunCmdOnCheckedFiles(input_api, output_api, _RunShellCheckCmd,
                                _INCLUDE_BASH_FILES_ONLY))
      print("shfmt")
      subprocess2.communicate(["shfmt", "-version"])
      results.extend(
          _RunCmdOnCheckedFiles(input_api, output_api, _RunShfmtCheckCmd,
                                _INCLUDE_BASH_FILES_ONLY))
    except OSError as os_error:
      results.append(
          output_api.PresubmitPromptWarning(
              "%s\nPlease install missing binaries locally." % os_error.args[0]))
    return results
  
  
  def CheckChangeOnUpload(input_api, output_api):
    results = []
    results.extend(_CommonChecks(input_api, output_api))
    return results
  
  
  def CheckChangeOnCommit(input_api, output_api):
    results = []
    results.extend(_CommonChecks(input_api, output_api))
    return results
  