bugs 502003, 502634: compile multiple IPDL specs in one invocation
authorChris Jones <jones.chris.g@gmail.com>
Tue, 07 Jul 2009 15:52:10 -0500
changeset 35759 62eedc3a5f903c8eb9e82e72c05b47b09b55813c
parent 35758 825f78a292befc66d8ffec6bee75a562adb9b3f7
child 35760 c51a123ddba49c6641c59c92ebb6b1597a6ce60a
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs502003, 502634
milestone1.9.2a1pre
bugs 502003, 502634: compile multiple IPDL specs in one invocation
ipc/ipdl/ipdl.py
ipc/ipdl/ipdl/__init__.py
ipc/ipdl/ipdl/type.py
ipc/ipdl/ipdlc
rename from ipc/ipdl/ipdlc
rename to ipc/ipdl/ipdl.py
--- a/ipc/ipdl/ipdlc
+++ b/ipc/ipdl/ipdl.py
@@ -1,10 +1,8 @@
-#!/usr/bin/env python
-
 # ***** BEGIN LICENSE BLOCK *****
 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
 # The contents of this file are subject to the Mozilla Public License Version
 # 1.1 (the "License"); you may not use this file except in compliance with
 # the License. You may obtain a copy of the License at
 # http://www.mozilla.org/MPL/
 #
@@ -27,115 +25,85 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
-import getopt, os, re, sys
+import optparse, os, re, sys
+from cStringIO import StringIO
 
-_verbosity = 1
+import ipdl
+
 def log(minv, fmt, *args):
     if _verbosity >= minv:
         print >>sys.stderr, fmt % args
 
-def getcallerpath():
-    '''Return the absolute path of the file containing the code that
-**CALLED** this function.'''
-    return os.path.abspath(sys._getframe(1).f_code.co_filename)
-
-# auto-add the compiler modules to the pythonpath
-installdir, _ = os.path.split(getcallerpath())
-sys.path.append(installdir)
-
-import ipdl
-
 # process command line
 
-def usage(err):
-    if err:
-        out, rv = (sys.stderr, 1)
-    else:
-        out, rv = (sys.stdout, 0)
-    print >>out, '''
-Usage:  ipdlc [OPTIONS...] SPECIFICATION
+op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...')
+op.add_option('-d', '--output-dir', dest='outputdir', default='.',
+              help='Directory in which to put generated headers')
+op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
+              help='Verbose logging (specify -vv or -vvv for very verbose logging)')
+op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
+              help="Suppress logging output")
+
+options, files = op.parse_args()
+_verbosity = options.verbosity
+codedir = options.outputdir
+
+if not len(files):
+    op.error("No IPDL files specified")
 
-where OPTIONS are zero or more of
-  -h, --help
-          : print this message and exit
-  -d DIR, --output-dir=DIR
-          : directory in which to put generated code.
-              Created if it doesn't exist.
-            default: use current working directory.
-  -v N, --verbosity=N
-          : be as verbose as N.  set to 0 for no output, and higher than 1
-              for more output.
-            default: -v 1
+log(1, 'Generated headers will be placed in "%s"', codedir)
+
+allprotocols = []
 
-and SPECIFICATION is a single IPDL specification file.  '-' = read from stdin.
-  default : read specification from stdin
-'''
-    sys.exit(rv)
+for f in files:
+    log(1, 'Parsing specification %s', f)
+    if f == '-':
+        fd = sys.stdin
+        filename = '<stdin>'
+    else:
+        fd = open(f)
+        filename = f
 
-try:
-    opts, args = getopt.gnu_getopt(
-        sys.argv[1:],
-        'd:hv:',
-        ['help',
-         'output-dir=',
-         'verbosity='])
-except getopt.GetoptError, err:
-    print >>sys.stderr, str(err), '\n'
-    usage(err=True)
+    specstring = fd.read()
+    fd.close()
 
-codedir = os.getcwd()
-spec = '-'
+    ast = ipdl.parse(specstring, filename)
 
-for o, a in opts:
-    if o in ('-h', '--help'):
-        usage(err=False)
-    elif o in ('-d', '--output-dir'):
-        codedir = a
-    elif o in ('-v', '--verbosity'):
-        _verbosity = int(a)
+    allprotocols.append,('%sProtocolMsgStart' % ast.protocol.name)
+
+    log(2, 'checking types')
+    if not ipdl.typecheck(ast):
+        print >>sys.stderr, 'Specification is not well typed.'
+        sys.exit(1)
 
-if 1 < len(args):
-   usage(err=True)
-if 1 == len(args):
-    spec = args[0]
+    if _verbosity > 2:
+        log(3, '  pretty printed code:')
+        ipdl.genipdl(ast, codedir)
 
-specstring = None
-specfilename = None
-if spec != '-':
-    log (1, 'using specification %s', spec)
-    specstring = open(spec, 'r').read()
-    specfilename = spec
-else:
-    log(1, 'reading specification from stdin')
-    specstring = sys.stdin.read()
-    specfilename = '<stdin>'
+    ipdl.gencxx(ast, codedir)
+
+allprotocols.sort()
+
+ipcmsgstart = StringIO()
 
-log(3, '  specification:\n%s', specstring)
-
-log(1, 'parsing specification')
-ast = ipdl.parse(specstring, specfilename)
+print >>ipcmsgstart, """
+// CODE GENERATED by ipdl.py. Do not edit.
 
-log(1, 'checking types')
-if not ipdl.typecheck(ast):
-    print >>sys.stderr, 'Specification is not well typed.'
-    sys.exit(1)
+enum IPCMessageStart {
+"""
 
-if _verbosity >= 3:
-    log(3, '  pretty printed code:')
-    ipdl.genipdl(ast, codedir)
-
-log(1, 'generating code to "%s"', codedir)
-ipdl.gencxx(ast, codedir)
+for name in allprotocols:
+    print >>ipcmsgstart, "  %s," % name
 
-log(1, '''\nIMPORTANT: remember to add the new enum value
-
-  %sMsgStart,
+print >>ipcmsgstart, """
+  LastMsgIndex
+};
+"""
 
-to the |IPCMessageStart| enum in "ipc/glue/IPCMessageUtils.h".
-Your code will not compile until this value is added.
-'''% (ast.protocol.name))
+ipdl.writeifmodified(ipcmsgstart.getvalue(),
+                     os.path.join(codedir, 'IPCMessageStart.h'))
--- a/ipc/ipdl/ipdl/__init__.py
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -25,19 +25,20 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
-__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck' ]
+__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified' ]
 
 import os, sys
+from cStringIO import StringIO
 
 from ipdl.cgen import IPDLCodeGen
 from ipdl.lower import LowerToCxx
 from ipdl.parser import Parser
 from ipdl.type import TypeCheck
 
 from ipdl.cxx.cgen import CxxCodeGen
 
@@ -46,13 +47,31 @@ def parse(specstring, filename='<stdin>'
 
 def typecheck(ast, errout=sys.stderr):
     '''Returns True iff |ast| is well typed.  Print errors to |errout| if
     it is not.'''
     return TypeCheck().check(ast, errout)
 
 def gencxx(ast, outdir):
     for hdr in LowerToCxx().lower(ast):
-        path = os.path.join(outdir, hdr.filename)
-        CxxCodeGen(outf=open(path, 'wb')).cgen(hdr)
+        file = os.path.join(outdir,
+                            *([ns.namespace for ns in ast.protocol.namespaces] + [hdr.filename]))
+
+        tempfile = StringIO()
+        CxxCodeGen(tempfile).cgen(hdr)
+        writeifmodified(tempfile.getvalue(), file)
 
 def genipdl(ast, outdir):
     return IPDLCodeGen().cgen(ast)
+
+def writeifmodified(contents, file):
+    dir = os.path.dirname(file)
+    os.path.exists(dir) or os.makedirs(dir)
+
+    oldcontents = None
+    if os.path.exists(file):
+        fd = open(file, 'rb')
+        oldcontents = fd.read()
+        fd.close()
+    if oldcontents != contents:
+        fd = open(file, 'wb')
+        fd.write(contents)
+        fd.close()
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -109,16 +109,17 @@ class ImportedCxxType(CxxType):
 class GeneratedCxxType(CxxType):
     def isGenerated(self): return True
     def isVisible(self):   return False
 
 ##--------------------
 class IPDLType(Type):
     def isIPDL(self):  return True
     def isVisible(self): return True
+    def isProtocol(self): return False
 
     def isAsync(self): return self.sendSemantics is ASYNC
     def isSync(self): return self.sendSemantics is SYNC
     def isRpc(self): return self.sendSemantics is RPC
 
     def talksAsync(self): return True
     def talksSync(self): return self.isSync() or self.isRpc()
     def talksRpc(self): return self.isRpc()
@@ -302,24 +303,23 @@ With this information, it finally type c
             print >>errout, error
 
 
 class GatherDecls(Visitor):
     def __init__(self, builtinUsing, symtab, errors):
         self.builtinUsing = builtinUsing
         self.symtab = symtab
         self.errors = errors
-        self.visited = set()    # set(filename)
         self.depth = 0
 
     def visitTranslationUnit(self, tu):
         # all TranslationUnits declare symbols in global scope
-        if tu.filename in self.visited:
+        if hasattr(tu, '_tchecked') and tu._tchecked:
             return
-        self.visited.add(tu.filename)
+        tu._tchecked = True
         self.depth += 1
 
         # bit of a hack here --- we want the builtin |using|
         # statements to be added to the symbol table before anything
         # else, but we also want them in the top-level translation
         # unit's list of using stmts so that we can use them later
         # down the pipe.  so we add them to the symbol table before
         # anything else, and prepend them to the top-level TU after