Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Side by Side Diff: tools-webrtc/check_package_boundaries.py

Issue 2629723004: Add presubmit check to prevent package boundary violations. (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« PRESUBMIT.py ('K') | « PRESUBMIT.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
4 #
5 # Use of this source code is governed by a BSD-style license
6 # that can be found in the LICENSE file in the root of the source
7 # tree. An additional intellectual property rights grant can be found
8 # in the file PATENTS. All contributing project authors may
9 # be found in the AUTHORS file in the root of the source tree.
10
11 import argparse
12 import logging
13 import os
14 import sys
15 import re
kjellander_webrtc 2017/01/16 07:26:10 sort
ehmaldonado_webrtc 2017/01/16 08:52:50 Acknowledged.
16
17 from collections import defaultdict
18
19
20 DISPLAY_LEVEL = 1
21 IGNORE_LEVEL = 0
22
23 TARGET_RE = re.compile(r'\s*\w*\("(\w*)"\) {$')
24 SOURCES_RE = re.compile(r'\s*sources \+?= \[$')
25 SOURCE_FILE_RE = re.compile(r'\s*"([\w\./]*)",$')
26 INLINE_SOURCES_RE = re.compile(r'\s*sources \+?= \[ "([\w\./]*)" ]$')
27
28 LOG_FORMAT = '%(message)s'
29 ERROR_MESSAGE = ("{}:{} in target '{}'\n"
30 "\tSource file '{}'\n"
31 "\tCrosses boundary of package '{}'.")
32
33
34 class Logger(object):
35 def __init__(self, messagesLeft=None):
36 self.logLevel = DISPLAY_LEVEL
kjellander_webrtc 2017/01/16 07:26:10 The style guide says local variables and class/glo
ehmaldonado_webrtc 2017/01/16 08:52:51 Acknowledged.
37 self.messagesLeft = messagesLeft
38
39 def log(self, message):
40 if self.messagesLeft is not None:
41 if not self.messagesLeft:
42 self.logLevel = IGNORE_LEVEL
43 else:
44 self.messagesLeft -= 1
45 logging.log(self.logLevel, message)
46
47
48 class RegexAux(object):
kjellander_webrtc 2017/01/16 07:26:10 I'm a bit confused by the name Aux, is RegexUtil b
ehmaldonado_webrtc 2017/01/16 08:52:51 Acknowledged.
49 def __init__(self):
50 self.match = None
51
52 def matches(self, regex, target):
kjellander_webrtc 2017/01/16 07:26:10 Rename target->line to avoid confusion here since
ehmaldonado_webrtc 2017/01/16 08:52:51 Acknowledged.
53 match_obj = regex.match(target)
54 if match_obj and match_obj.groups():
55 self.match = match_obj.groups()[0]
56 return match_obj
57
58
59 def _BuildTrie(sequences):
60 trie = defaultdict(list)
61 for sequence in sequences:
62 if sequence:
63 head = sequence.pop()
64 trie[head].append(sequence)
65 return dict((k, _BuildTrie(v)) for k, v in trie.items())
66
67 def _LongestPrefix(trie, query):
kjellander_webrtc 2017/01/16 07:26:10 +1 blank line for top level methods
ehmaldonado_webrtc 2017/01/16 08:52:50 Acknowledged.
68 if not query:
69 return []
70 head = query.pop()
71 if head not in trie:
72 return []
73 return [head] + _LongestPrefix(trie[head], query)
74
75 def _CheckBuildFile(trie, buildFilePath, logger):
76 targetName = None
77 foundErrors = False
78 processingSourceFiles = False
79
80 regexAux = RegexAux()
81 package = os.path.dirname(buildFilePath)
82
83 with open(buildFilePath) as buildFile:
84 for lineNumber, line in enumerate(buildFile):
85 sourceFile = None
86
87 if regexAux.matches(TARGET_RE, line):
kjellander_webrtc 2017/01/16 07:26:10 I find it hard to follow what's going on here sinc
ehmaldonado_webrtc 2017/01/16 08:52:51 I'll try to make this clearer. You're right, this
88 targetName = regexAux.match
89 elif regexAux.matches(SOURCES_RE, line):
90 processingSourceFiles = True
91 elif (regexAux.matches(INLINE_SOURCES_RE, line) or
92 (processingSourceFiles and regexAux.matches(SOURCE_FILE_RE, line))):
93 sourceFile = regexAux.match
94 else:
95 processingSourceFiles = False
96
97 if not sourceFile:
98 continue
99
100 query = os.path.join(package, sourceFile).split(os.path.sep)[::-1]
101 realPackage = os.path.sep.join(_LongestPrefix(trie, query))
102 if realPackage != package:
103 foundErrors = True
104 logger.log(ERROR_MESSAGE.format(
105 buildFilePath, lineNumber, targetName, sourceFile, realPackage))
106
107 return foundErrors
108
109
110 def main():
111 parser = argparse.ArgumentParser(
112 description='Script that checks package boundary violations in GN '
113 'build files.')
114
115 parser.add_argument('root_dir', metavar='ROOT_DIR',
116 help='The root directory that contains all BUILD.gn '
117 'files to be processed.')
118 parser.add_argument('build_files', metavar='BUILD_FILE', nargs='*',
119 help='A list of BUILD.gn files to be processed. If no '
120 'files are given, all BUILD.gn files under ROOT_DIR '
121 'will be processed.')
122 parser.add_argument('--max_messages', type=int, default=None,
123 help='If set, the maximum number of violations to be '
kjellander_webrtc 2017/01/16 07:26:10 Does this exist only as a performance optimization
ehmaldonado_webrtc 2017/01/16 08:52:50 No, it's not for performance optimization. If you
124 'displayed.')
125
126 args = parser.parse_args()
127
128 logging.basicConfig(format=LOG_FORMAT)
129 logging.getLogger().setLevel(DISPLAY_LEVEL)
130 logger = Logger(args.max_messages)
131
132 packages = [root for root, _, files in os.walk(args.root_dir)
133 if 'BUILD.gn' in files]
134 trie = _BuildTrie(package.split(os.path.sep)[::-1]
135 for package in packages)
136 defaultBuildFiles = [os.path.join(package, 'BUILD.gn')
137 for package in packages]
138
139 buildFiles = args.build_files or defaultBuildFiles
140 return any(_CheckBuildFile(trie, buildFilePath, logger)
141 for buildFilePath in buildFiles)
142
143 if __name__ == '__main__':
144 sys.exit(main())
OLDNEW
« PRESUBMIT.py ('K') | « PRESUBMIT.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698