~jc-berentsen/pidl/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import sys
import os
import subprocess
from optparse import OptionParser

from idlparser import IDLParser
#from gobjectemitter import GObjectEmitter
from vsemitter import VSEmitter
from vscpythonemitter import VSCPythonEmitter
from csharpemitter import CSharpEmitter
#from cxxemitter import CxxEmitter
from vscxxemitter import VSCxxEmitter
#from vscxyemitter import VSCxyEmitter
from cxydocemitter import CxyDocEmitter
from vapiemitter import VapiEmitter
from dotemitter import DotEmitter

class Pidl (object):
    EMITTERS = {
        #"c" : GObjectEmitter,
        "c" : VSEmitter,
        "dotnet" : CSharpEmitter,
        "python" : VSCPythonEmitter,
        #"cxx" : CxxEmitter,
        "cxx" : VSCxxEmitter,
        "cxy" : CxyDocEmitter,
        "vapi" : VapiEmitter,
        "dot" : DotEmitter,
    }

class PidlApp (object):
    def main (self):
        if self.options.verbosity > 0:
            print "PIDL running in %s" % os.getcwd ()

        if self.options.verbosity > 1:
            print "PIDL options: %s" % self.options

        if self.options.verbosity > 2:
            print "PIDL environment:"
            env_vars = os.environ.keys ()
            env_vars.sort ()
            for env_var in env_vars:
                print "    %s => '%s'" % (env_var, os.environ[env_var])
            print

        # process all idl files given i args
        print "Processing idl files: %s" % self.args
        # this appends all the pidl files into one so we see global deps
        idl = "".join ([file (filename, 'r').read () for filename in self.args])

        inputdir = self.calculate_inputdir ()

        ast = self.parse (idl)

        # hack:
        # self.options.emitters.append ('cxx')

        for emitter_name in self.options.emitters:
            emitter = Pidl.EMITTERS[emitter_name] (ast)
            emitter.inputdir = inputdir

            subdir = getattr(self.options, "%s_subdir" % emitter_name)
            if subdir is None:
                subdir = ""

            self.emit_doc_files (emitter, subdir)

    def calculate_inputdir (self):
        first_filename = self.args[0]
        return os.path.dirname (os.path.abspath (first_filename))

    def __init__(self):
        all_emitters = Pidl.EMITTERS.keys ()
        all_emitters.sort ()

        usage = "usage: %prog [options] pidlfile1 pidlfile2 ..."
        parser = OptionParser (usage=usage)
        parser.set_defaults (verbosity=0, force_overwrite=False, emitters=[])
        parser.add_option ("-v", "--verbose", action="count",
                           dest="verbosity", help="Increase verbosity")
        parser.add_option ("-f", "--force-overwrite", action="store_true",
                           dest="force_overwrite",
                           help="force overwrite of output files")
        parser.add_option ("-e", "--enable-emitter", action="append", dest="emitters",
                           help="enable emitter: one of 'all' [default], '%s'" \
                               % "', '".join(all_emitters),
                           metavar="EMITTER")
        parser.add_option ("-d", "--dir", dest="outdir",
                           help="output files to DIR", metavar="DIR")
        for e in all_emitters:
            parser.set_defaults (**{ "%s_subdir" % e : "" })
            parser.add_option ("--set-%s-subdir" % e, action="store", dest="%s_subdir" % e,
                               help="output %s files to SUBDIR" % e, metavar="SUBDIR")

        parser.add_option ("--glib-genmarshal", action="store", dest="genmarshal",
                           help="generate marshallers using glib-genmarshal " \
                               "instead of just outputting .list documents.",
                           metavar="PATH")

        (self.options, self.args) = parser.parse_args ()

        for emitter in self.options.emitters:
            if emitter != "all" and not emitter in all_emitters:
                print >> sys.stderr, "Emitter '%s' not one of '%s'." % \
                    (emitter, "', '".join (all_emitters))
                sys.exit(1)

        if "all" in self.options.emitters or not self.options.emitters:
            self.options.emitters = all_emitters

        self.options.emitters.sort ()

        if not self.args:
            parser.print_help ()
            sys.exit (1)

        if self.options.outdir is None:
            self.options.outdir = os.getcwd ()
        else:
            self.options.outdir = os.path.abspath (self.options.outdir)
        
    def parse(self, idl):
        parser = IDLParser ()
        ast = parser (idl)
        return ast

    def emit_doc_files(self, emitter, subdir_name):
        docs = emitter.emit()

        outnames = docs.keys()
        outnames.sort()

        for outname in outnames:
            doc = docs [outname]

            filedir = os.path.join (self.options.outdir, subdir_name)
            self._mkdir (filedir)
            filename = os.path.join (filedir, outname)

            action = "Generating"
            unchanged = False
            if os.path.isfile (filename):
                f = file (filename, "r")
                unchanged = doc == f.read ()
                f.close ()
                del f
                if unchanged:
                    action = "Verified"
                else:
                    action = "Updating"

            if unchanged and self.options.force_overwrite:
                action += ", but forcing overwrite"
                unchanged = False

            print "%s: '%s'" % (action, os.path.join(subdir_name, outname))
            if not unchanged:
                f = file (filename, 'w')
                f.write (doc)
                f.close ()
                del f

            genmarshal = self.options.genmarshal
            if outname.endswith ("-marshal.list") and genmarshal is not None:
                namespace = outname.split ("-marshal.list", 1)[0]

                marshaller_body_fn = "%s-marshal.c" % namespace
                marshaller_body_path = os.path.join (filedir, marshaller_body_fn)

                marshaller_header_fn = "%s-marshal.h" % namespace
                marshaller_header_path = os.path.join (filedir, marshaller_header_fn)

                base_cmd = "%s --prefix=%s --skip-source" % (genmarshal,
                    "%s_marshal" % namespace)

                marshallers_unchanged = unchanged

                if not os.path.isfile (marshaller_body_path) \
                    or not os.path.isfile (marshaller_header_path):
                    action = "Generating"
                    marshallers_unchanged = False
                elif not marshallers_unchanged:
                    action = "Updating"
                else:
                    action = "Verified"

                print "%s: '%s'" % (action, os.path.join (subdir_name, marshaller_body_fn))
                if not marshallers_unchanged:
                    cmd = "%s --body %s" % (base_cmd, filename)
                    self._run_redirected (cmd, marshaller_body_path)

                print "%s: '%s'" % (action, os.path.join (subdir_name, marshaller_header_fn))
                if not marshallers_unchanged:
                    cmd = "%s --header %s" % (base_cmd, filename)
                    self._run_redirected (cmd, marshaller_header_path)

    def _run_redirected (self, cmd, output_file):
        process = subprocess.Popen (cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        output = process.stdout.read ()
        ret = process.wait ()
        if ret != 0:
            raise RuntimeError ("Error running '%s', exit code %d: '%s'" % (cmd, ret, output))
        f = open (output_file, "wb")
        f.write (output)
        f.close ()
        return ret

    def _mkdir (self, newdir):
        """ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/82465

            Works the way a good mkdir should :)
             - already exists, silently complete
             - regular file in the way, raise an exception
             - parent directory(ies) does not exist, make them as well
        """
        if os.path.isdir (newdir):
            pass
        elif os.path.isfile (newdir):
            raise OSError("a file with the same name as the desired " \
                          "dir, '%s', already exists." % newdir)
        else:
            head, tail = os.path.split (newdir)
            if head and not os.path.isdir (head):
                self._mkdir (head)
            if tail:
                os.mkdir (newdir)

if __name__ == '__main__':
    app = PidlApp ()
    app.main ()