Cache parser model when possible.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Sun, 22 Feb 2009 21:50:40 -0500
changeset 156 3ae1e58c1a25
parent 155 6c4ae304d9a4
child 157 91ec8fafe9ce
push id91
push userbsmedberg@mozilla.com
push dateMon, 23 Feb 2009 02:50:50 +0000
Cache parser model when possible.
pymake/data.py
pymake/parser.py
--- a/pymake/data.py
+++ b/pymake/data.py
@@ -1167,19 +1167,18 @@ class Makefile(object):
     def include(self, path, required=True, loc=None):
         """
         Include the makefile at `path`.
         """
         self.included.append(path)
 
         fspath = os.path.join(self.workdir, path)
         if os.path.exists(fspath):
-            fd = open(fspath)
+            stmts = pymake.parser.parsefile(fspath)
             self.variables.append('MAKEFILE_LIST', Variables.SOURCE_AUTOMATIC, path, None, self)
-            stmts = pymake.parser.parsestream(fd, path)
             stmts.execute(self)
             self.gettarget(path).explicit = True
         elif required:
             raise DataError("Attempting to include file '%s' which doesn't exist." % (path,), loc)
 
     def addvpath(self, pattern, dirs):
         """
         Add a directory to the vpath search for the given pattern.
--- a/pymake/parser.py
+++ b/pymake/parser.py
@@ -16,17 +16,17 @@ Otherwise, they are parsed as makefile s
 
 After splitting data into parseable chunks, we use a recursive-descent parser to
 nest parenthesized syntax.
 
 This file parses into the data structures defined in the parserdata module. Those classes are what actually
 do the dirty work of "executing" the parsed data into a Makefile data structure.
 """
 
-import logging, re
+import logging, re, os
 import data, functions, util, parserdata
 
 log = logging.getLogger('pymake.parser')
 
 class SyntaxError(util.MakeError):
     pass
 
 def findlast(func, iterable):
@@ -443,16 +443,36 @@ conditionkeywords = {
 
 conditiontokens = tuple(conditionkeywords.iterkeys())
 directivestokenlist = TokenList.get(conditiontokens + \
     ('else', 'endif', 'define', 'endef', 'override', 'include', '-include', 'vpath', 'export', 'unexport'))
 conditionkeywordstokenlist = TokenList.get(conditiontokens)
 
 varsettokens = (':=', '+=', '?=', '=')
 
+_parsecache = {} # realpath -> (mtime, Statements)
+
+def parsefile(pathname):
+    pathname = os.path.realpath(pathname)
+
+    mtime = os.path.getmtime(pathname)
+
+    if pathname in _parsecache:
+        oldmtime, stmts = _parsecache[pathname]
+
+        if mtime == oldmtime:
+            log.debug("Using '%s' from the parser cache.", pathname)
+            return stmts
+
+        log.debug("Not using '%s' from the parser cache, mtimes don't match: was %s, now %s" % (pathname, oldmtime, mtime))
+
+    stmts = parsestream(open(pathname), pathname)
+    _parsecache[pathname] = mtime, stmts
+    return stmts
+
 def parsestream(fd, filename):
     """
     Parse a stream of makefile into a parser data structure.
 
     @param fd A file-like object containing the makefile data.
     """
 
     currule = False