1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# Copyright 2009 The Closure Library Authors. All Rights Reserved. 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License"); 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# you may not use this file except in compliance with the License. 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# You may obtain a copy of the License at 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# http://www.apache.org/licenses/LICENSE-2.0 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# distributed under the License is distributed on an "AS-IS" BASIS, 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# See the License for the specific language governing permissions and 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# limitations under the License. 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)"""Scans a source JS file for its provided and required namespaces. 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)Simple class to scan a JavaScript file and express its dependencies. 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)""" 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)__author__ = 'nnaze@google.com' 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import re 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)_BASE_REGEX_STRING = '^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)' 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)_PROVIDE_REGEX = re.compile(_BASE_REGEX_STRING % 'provide') 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)_REQUIRES_REGEX = re.compile(_BASE_REGEX_STRING % 'require') 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class Source(object): 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Scans a JavaScript source for its provided and required namespaces.""" 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) # Matches a "/* ... */" comment. 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) # Note: We can't definitively distinguish a "/*" in a string literal without a 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) # state machine tokenizer. We'll assume that a line starting with whitespace 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) # and "/*" is a comment. 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) _COMMENT_REGEX = re.compile( 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) r""" 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ^\s* # Start of a new line and whitespace 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /\* # Opening "/*" 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) .*? # Non greedy match of any characters (including newlines) 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) \*/ # Closing "*/""", 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) re.MULTILINE | re.DOTALL | re.VERBOSE) 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) def __init__(self, source): 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Initialize a source. 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Args: 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) source: str, The JavaScript source. 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """ 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) self.provides = set() 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) self.requires = set() 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) self._source = source 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) self._ScanSource() 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) def GetSource(self): 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Get the source as a string.""" 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return self._source 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) @classmethod 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) def _StripComments(cls, source): 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return cls._COMMENT_REGEX.sub('', source) 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) @classmethod 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) def _HasProvideGoogFlag(cls, source): 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Determines whether the @provideGoog flag is in a comment.""" 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for comment_content in cls._COMMENT_REGEX.findall(source): 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if '@provideGoog' in comment_content: 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return True 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return False 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) def _ScanSource(self): 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Fill in provides and requires by scanning the source.""" 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) stripped_source = self._StripComments(self.GetSource()) 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) source_lines = stripped_source.splitlines() 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for line in source_lines: 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) match = _PROVIDE_REGEX.match(line) 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if match: 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) self.provides.add(match.group(1)) 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) match = _REQUIRES_REGEX.match(line) 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if match: 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) self.requires.add(match.group(1)) 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) # Closure's base file implicitly provides 'goog'. 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) # This is indicated with the @provideGoog flag. 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if self._HasProvideGoogFlag(self.GetSource()): 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if len(self.provides) or len(self.requires): 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) raise Exception( 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'Base file should not provide or require namespaces.') 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) self.provides.add('goog') 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)def GetFileContents(path): 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Get a file's contents as a string. 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Args: 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) path: str, Path to file. 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Returns: 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) str, Contents of file. 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Raises: 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) IOError: An error occurred opening or reading the file. 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """ 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) fileobj = open(path) 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) try: 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return fileobj.read() 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) finally: 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) fileobj.close() 119