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
|
#!/usr/bin/env python3
# Copyright 2020-2023 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 = ""
for currentLine in generatedIceHeader.split("\n"):
currentLine = currentLine.strip()
if not currentLine:
# Skip empty lines
continue
# find class name
match = re.match(r"^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(r"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(r"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 ::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()
|