1#!/usr/bin/env python
2#
3# Copyright 2018 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6#
7# Verify HEAD against origin/master to see if anything bad has been added to
8# HEAD that we would not want to roll.  For example, if HEAD introduces any
9# UNCHECKED_BITSTREAM_READERs, then we definitely want to fix that before
10# including HEAD in Chromium.
11#
12# Usage: check_merge.py [new_branch]
13# where |new_branch| is the branch that we'd like to compare to origin/master.
14
15import os
16import re
17import sys
18import subprocess
19
20# The current good ffmpeg + config that we're diffing against.
21EXISTING_COMMIT = "origin/master"
22
23# If any of these regexes match, it indicates failure.  Generally, we want to
24# catch any change in how these are defined.  These are checked against things
25# that are added with respect to |EXISTING_COMMIT| by the commit we're checking.
26#
27# Remember that we're going to check ffmpeg files and Chromium files (e.g.,
28# the build configs for each platform, ffmpeg_generated.gni, etc.).
29# TODO(liberato): consider making these a dictionary, with the value as some
30# descriptive text (e.g., the bug number) about why it's not allowed.
31INSERTION_TRIPWIRES = [
32    # In Chromium, all codecs should use the safe bitstream reader. Roller must
33    # undo any new usage of the unchecked reader.
34    "^.define.*UNCHECKED_BITSTREAM_READER.*1",
35
36    # In Chromium, explicitly skipping some matroskadec code blocks for
37    # the following CONFIG variables (which should remain defined as 0) is
38    # necessary to remove code that may be a security risk. Discuss with cevans@
39    # before removing these explicit skips or enabling these variables.
40    "^.define.*CONFIG_LZO.*1",
41    "^.define.*CONFIG_SIPR_DECODER.*1",
42    "^.define.*CONFIG_RA_288_DECODER.*1",
43    "^.define.*CONFIG_COOK_DECODER.*1",
44    "^.define.*CONFIG_ATRAC3_DECODER.*1",
45
46    # Miscellaneous tripwires.
47    "^.define.*CONFIG_SPDIF_DEMUXER.*1",
48    "^.define.*CONFIG_W64_DEMUXER.*1",
49    "^.define.*[Vv]4[Ll]2.*1",
50    "^.define.*CONFIG_PRORES_.*1",
51]
52
53# Filenames that will be excluded from the regex matching.
54EXCLUDED_FILENAMES = [
55    r"^configure$",
56    r"^chromium/scripts/",
57    r"^chromium/patches/",
58]
59
60def search_regexps(text, regexps):
61  return [r for r in regexps if re.search(r, text)]
62
63def main(argv):
64  # What we're considering merging, and would like to check.  Normally, this is
65  # HEAD, but you might want to verify some previous merge.
66  if len(argv) > 1:
67    new_commit = argv[1]
68  else:
69    new_commit = "HEAD"
70
71  print "Comparing %s to baseline %s..." % (new_commit, EXISTING_COMMIT)
72
73  diff = subprocess.Popen(["git", "diff", "-U0", EXISTING_COMMIT, new_commit],
74          stdout=subprocess.PIPE).communicate()[0]
75  filename=None
76  skip = False
77  files_encountered = 0
78  files_skipped = 0
79  failures = set()
80  for line in diff.splitlines():
81      if line.startswith("+++"):
82        # +++ b/filename => filename
83        filename = line.split("/",1)[1]
84        skip = False
85        files_encountered += 1
86        if search_regexps(filename, EXCLUDED_FILENAMES):
87          skip = True
88          files_skipped += 1
89      elif line.startswith("+") and not skip:
90        # |line| is an insertion into |new_commit|.
91        # Drop the leading "+" from the string being searched.
92        tripwire = search_regexps(line[1:], INSERTION_TRIPWIRES)
93        if tripwire:
94          failures.add("Tripwire '%s' found in %s" % (tripwire, filename))
95
96 # If we have failures, then print them and fail.
97  if failures:
98    for failure in failures:
99      print "Failure: %s" % failure
100    sys.exit(2)
101
102  print ("No problems found!  Checked %d files, skipped %d." %
103      (files_encountered - files_skipped, files_skipped))
104
105if __name__ == '__main__':
106  main(sys.argv)
107