forked from mumble-voip/mumble
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerateIceWrapper.py
executable file
·203 lines (158 loc) · 8.23 KB
/
generateIceWrapper.py
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-2021 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 ::Murmur::" + 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 (Murmur.ice)
args.ice_file = os.path.join(rootDir, "src", "murmur", "Murmur.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", "Murmur.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 ¤t")
wrapperContent += generateFunction(targetClass, functionName, wrapArgs, callArgs) + "\n"
wrapperContent += "void ::Murmur::MetaI::getSlice_async(const ::Murmur::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()