Welcome to mirror list, hosted at ThFree Co, Russian Federation.

generateIceWrapper.py « scripts - github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: e6cb786727242dcb99e46d16fe2fa15e2b3501b5 (plain)
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
#!/usr/bin/env python3

# Copyright 2020-2022 The Mumble Developers. All rights reserved.
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file at the root of the
# Mumble source tree or at <https://www.mumble.info/LICENSE>.


import argparse
import re
from datetime import datetime
import os

def comment_remover(text):
    def replacer(match):
        s = match.group(0)
        if s.startswith('/'):
            return ""
        else:
            return s
    pattern = re.compile(
        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, replacer, text)

def fix_lineEnding(text):
    # Convert from Windows to Unix
    text = text.replace("\r\n", "\n")
    # Convert from old Mac to Unix
    text = text.replace("\r", "\n")

    return text

def create_disclaimerComment():
    return "// This file was auto-generated by scripts/generateIceWrapper.py on " + datetime.now().strftime("%Y-%m-%d") + " -- DO NOT EDIT MANUALLY!\n"

def generateFunction(className, functionName, wrapArgs, callArgs):
    function = "void ::MumbleServer::" + className + "I::" + functionName + "_async(" + (", ".join(wrapArgs)) + ") {\n"
    function += "\t// qWarning() << \"" + functionName + "\" << meta->mp.qsIceSecretRead.isNull() << meta->mp.qsIceSecretRead.isEmpty();\n"
    function += "#ifndef ACCESS_" + className + "_" + functionName + "_ALL\n"
    function += "#\tifdef ACCESS_" + className + "_" + functionName + "_READ\n"
    function += "\tif (!meta->mp.qsIceSecretRead.isNull()) {\n"
    function += "\t\tbool ok = !meta->mp.qsIceSecretRead.isEmpty();\n"
    function += "#\telse\n"
    function += "\tif (!meta->mp.qsIceSecretRead.isNull() || !meta->mp.qsIceSecretWrite.isNull()) {\n"
    function += "\t\tbool ok = !meta->mp.qsIceSecretWrite.isEmpty();\n"
    function += "#\tendif // ACCESS_" + className + "_" + functionName + "_READ\n"
    function += "\t\t::Ice::Context::const_iterator i = current.ctx.find(\"secret\");\n"
    function += "\t\tok = ok && (i != current.ctx.end());\n"
    function += "\t\tif (ok) {\n"
    function += "\t\t\tconst QString &secret = u8((*i).second);\n"
    function += "#\tifdef ACCESS_" + className + "_" + functionName + "_READ\n"
    function += "\t\t\tok = ((secret == meta->mp.qsIceSecretRead) || (secret == meta->mp.qsIceSecretWrite));\n"
    function += "#\telse\n"
    function += "\t\t\tok = (secret == meta->mp.qsIceSecretWrite);\n"
    function += "#\tendif // ACCESS_" + className + "_" + functionName + "_READ\n"
    function += "\t\t}\n"
    function += "\n"
    function += "\t\tif (!ok) {\n"
    function += "\t\t\tcb->ice_exception(InvalidSecretException());\n"
    function += "\t\t\treturn;\n"
    function += "\t\t}\n"
    function += "\t}\n"
    function += "#endif // ACCESS_" + className + "_" + functionName + "_ALL\n"
    function += "\n"
    function += "\tExecEvent *ie = new ExecEvent(boost::bind(&impl_" + className + "_" + functionName + ", " + ", ".join(callArgs) + "));\n"
    function += "\tQCoreApplication::instance()->postEvent(mi, ie);\n"
    function += "}\n"

    return function


def main():
    parser = argparse.ArgumentParser(description="Generates the wrapper files needed for the ICE server-interface")
    parser.add_argument("-i", "--ice-file", help="Path to the ICE specification file (*.ice)", metavar="PATH")
    parser.add_argument("-g", "--generated-ice-header", help="Path to the header file that was generated by ICE", metavar="PATH")
    parser.add_argument("-o", "--out-file", help="Path to the file to write the generated output to. If omitted, the content will be written to std::out", metavar="PATH")
    parser.add_argument("-q", "--quiet", action="store_true", help="Don't display used file paths")

    args = parser.parse_args()

    scriptPath = os.path.realpath(__file__)
    rootDir = os.path.dirname(os.path.dirname(scriptPath))

    if args.ice_file is None:
        # Try to figure out the path to the ice-file (MumbleServer.ice)
        args.ice_file = os.path.join(rootDir, "src", "murmur", "MumbleServer.ice")
    if args.generated_ice_header is None:
        # Try to figure out path to the generated header file (in the build dir)
        args.generated_ice_header = os.path.join(rootDir, "build", "src", "murmur", "MumbleServer.h")

    if not args.quiet:
        print("Using ICE-file at                   \"%s\"" % args.ice_file)
        print("Using ICE-generated header file at  \"%s\"" % args.generated_ice_header)


    iceSpec = fix_lineEnding(open(args.ice_file, "r").read())
    generatedIceHeader = fix_lineEnding(open(args.generated_ice_header, "r").read())

    # remove comments from the iceSpec
    iceSpec = comment_remover(iceSpec)
    # Remove all tabs from iceSpec
    iceSpec = iceSpec.replace("\t", "")
    # Remove empty lines form iceSpec
    iceSpec = iceSpec.replace("\n\n", "\n")

    # Escape all special characters so that iceSpec can be used in a std::string ctor
    iceSpec = iceSpec.replace("\"", "\\\"") # quotes
    iceSpec = iceSpec.replace("\n", "\\n")  # newlines

    wrapperContent = create_disclaimerComment()

    # Include boost-bind as we'll need it later
    wrapperContent += "\n#include <boost/bind/bind.hpp>\n\n"


    className = ""
    responseTypes = {}
    for currentLine in generatedIceHeader.split("\n"):
        currentLine = currentLine.strip()

        if not currentLine:
            # Skip empty lines
            continue

        # find class name
        match = re.match("^class\s+AMD_(.+)\s+:\s+(?:public\svirtual|virtual\s+public)\s+::Ice(?:::AMDCallback|Util::Shared)", currentLine)
        if match:
            className = "AMD_" + match.group(1)

        match = re.match("virtual\s+void\s+ice_response\\((.*)\\)\s+=\s+0;", currentLine)
        if match:
            if not className:
                raise RuntimeError("Expected a className to be found at this time")
        match = re.match("virtual\s+void\s+(.+)_async\(const\s+(.+?)&\s*\w*,(.*)\s+const\s+::Ice::Current&", currentLine)
        if match:
            functionName = match.group(1)
            objectName = match.group(2)
            arguments = match.group(3)

            if functionName == "getSlice":
                # getSlice is handled separately
                continue

            targetClass = "Server" if "AMD_Server" in objectName else "Meta"

            wrapArgs = []
            callArgs = []
            argIndex = 0

            wrapArgs.append("const %s &cb" % objectName)
            callArgs.append("cb")

            if targetClass == "Server":
                callArgs.append("QString::fromStdString(current.id.name).toInt()")
            else:
                callArgs.append("current.adapter")

            for currentArg in arguments.split(","):
                if not currentArg:
                    # skip empty entries
                    continue

                parts = currentArg.split()
                if len(parts) > 1:
                    lastPart = parts[len(parts) - 1]

                    if not ":" in lastPart and not "&" in lastPart:
                        # Omit the last part as it is only a parameter name. We however want the parameters
                        # to be named p1, p2, ... which we'll do below
                        currentArg = " ".join(parts[:len(parts) - 1])

                    if len(currentArg.split()) == 1 and currentArg == "const":
                        # Failsafe in order for us to not only leave const as the type
                        # We have to include lastPart after all
                        currentArg += " " + lastPart

                argIndex += 1
                wrapArgs.append("%s p%d" % (currentArg, argIndex))
                callArgs.append("p%d" % argIndex)

            wrapArgs.append("const ::Ice::Current &current")

            wrapperContent += generateFunction(targetClass, functionName, wrapArgs, callArgs) + "\n"


    wrapperContent += "void ::MumbleServer::MetaI::getSlice_async(const ::MumbleServer::AMD_Meta_getSlicePtr &cb, const Ice::Current&) {\n"
    wrapperContent += "\tcb->ice_response(std::string(\"" + iceSpec + "\"));\n"
    wrapperContent += "}\n"


    if args.out_file is None:
        # Write to std::out
        print(wrapperContent)
    else:
        # Write to file
        outFile = open(args.out_file, "w")
        outFile.write(wrapperContent)



main()