0
0
Fork 0
mirror of https://github.com/mumble-voip/mumble.git synced 2025-03-15 04:55:00 +00:00
mumble-voip_mumble/scripts/generateIceWrapper.py
Robert Adam 7271bcaf05 FIX(server): Respect Qt's desired initialization order
In a Qt application, the QApplication object should be the first QObject
that is created. However, the `Meta` class used to have a static member
called `mp`, which means that this member gets initialized _before_
main() runs and therefore before the QApplication is created.

This has caused an "Invalid nullptr in QObject::connect" warning
somewhere in Qt's internals (since the move to Qt 6). The impact of this
warning is unclear at this point.

This commit makes the mp parameter a std::unique_ptr that gets
explicitly initialized in the main function (more or less right after
the QApplication object is created). This guarantees that the MetaParams
object does not get created before the QApplication object, fixing the
observed warning.

It is worth noting that we do have a couple of other static QObject
variables in the main translation unit, but these seem to be
inconsequential (at least they don't seem to trigger a similar warning).

Fixes #6669
2025-01-11 17:56:39 +01:00

202 lines
8.3 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright 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 &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()