From 804fddcdf7834a0bac25b7340519c8d7dc64f800 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 10 Nov 2020 13:57:27 +0000 Subject: [PATCH] Allow python extensions to be loaded from app folder as well as user * This will let us distribute extensions with the renderdoc builds. --- qrenderdoc/Code/CaptureContext.cpp | 244 +++++++++--------- qrenderdoc/Code/pyrenderdoc/PythonContext.cpp | 38 ++- qrenderdoc/Code/pyrenderdoc/PythonContext.h | 1 + 3 files changed, 152 insertions(+), 131 deletions(-) diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 8a250a036..633cf5816 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -169,149 +169,151 @@ rdcstr CaptureContext::TempCaptureFilename(const rdcstr &appname) rdcarray CaptureContext::GetInstalledExtensions() { - QString extensionFolder = configFilePath("extensions"); - rdcarray ret; - QDirIterator it(extensionFolder, QDirIterator::Subdirectories); - - while(it.hasNext()) + for(QString extensionFolder : PythonContext::GetApplicationExtensionsPaths()) { - QFileInfo fileinfo(it.next()); + QDirIterator it(extensionFolder, QDirIterator::Subdirectories); - if(fileinfo.fileName().toLower() == lit("extension.json")) + while(it.hasNext()) { - QFile f(fileinfo.absoluteFilePath()); + QFileInfo fileinfo(it.next()); - QString package = fileinfo.absolutePath() - .replace(extensionFolder, QString()) - .replace(QLatin1Char('/'), QLatin1Char('.')); - - while(package[0] == QLatin1Char('.')) - package.remove(0, 1); - - while(package[package.size() - 1] == QLatin1Char('.')) - package.remove(package.size() - 1, 1); - - if(f.exists() && f.open(QIODevice::ReadOnly | QIODevice::Text)) + if(fileinfo.fileName().toLower() == lit("extension.json")) { - QVariantMap json = JSONToVariant(QString::fromUtf8(f.readAll())); + QFile f(fileinfo.absoluteFilePath()); - if(json.empty()) - { - qCritical() << fileinfo.absoluteFilePath() << "is corrupt, cannot parse json"; - continue; - } + QString package = fileinfo.absolutePath() + .replace(extensionFolder, QString()) + .replace(QLatin1Char('/'), QLatin1Char('.')); - ExtensionMetadata ext; + while(package[0] == QLatin1Char('.')) + package.remove(0, 1); - ext.package = package; - ext.filePath = fileinfo.absolutePath(); + while(package[package.size() - 1] == QLatin1Char('.')) + package.remove(package.size() - 1, 1); - if(json.contains(lit("name"))) + if(f.exists() && f.open(QIODevice::ReadOnly | QIODevice::Text)) { - ext.name = json[lit("name")].toString(); - } - else - { - qCritical() << "Extension" << package << "is corrupt, no name entry"; - continue; - } + QVariantMap json = JSONToVariant(QString::fromUtf8(f.readAll())); - ext.extensionAPI = 1; - if(json.contains(lit("extension_api"))) - { - ext.extensionAPI = json[lit("extension_api")].toInt(); - } - else - { - qCritical() << "Extension" << QString(ext.name) << "is corrupt, no api version entry"; - continue; - } - - if(json.contains(lit("version"))) - { - ext.version = json[lit("version")].toString(); - } - else - { - qCritical() << "Extension" << QString(ext.name) << "is corrupt, no version entry"; - continue; - } - - if(json.contains(lit("description"))) - { - ext.description = json[lit("description")].toString(); - } - else - { - qCritical() << "Extension" << QString(ext.name) << "is corrupt, no description entry"; - continue; - } - - if(json.contains(lit("author"))) - { - ext.author = json[lit("author")].toString(); - } - else - { - qCritical() << "Extension" << QString(ext.name) << "is corrupt, no author entry"; - continue; - } - - if(json.contains(lit("url"))) - { - ext.extensionURL = json[lit("url")].toString(); - } - else - { - qCritical() << "Extension" << QString(ext.name) << "is corrupt, no URL entry"; - continue; - } - - if(json.contains(lit("minimum_renderdoc"))) - { - QString minVer = json[lit("minimum_renderdoc")].toString(); - - QRegularExpression re(lit("([0-9]*).([0-9]*)")); - QRegularExpressionMatch match = re.match(minVer); - - bool ok = false, badversion = false; - - if(match.hasMatch()) + if(json.empty()) { - int major = match.captured(1).toInt(&ok); + qCritical() << fileinfo.absoluteFilePath() << "is corrupt, cannot parse json"; + continue; + } - if(ok) + ExtensionMetadata ext; + + ext.package = package; + ext.filePath = fileinfo.absolutePath(); + + if(json.contains(lit("name"))) + { + ext.name = json[lit("name")].toString(); + } + else + { + qCritical() << "Extension" << package << "is corrupt, no name entry"; + continue; + } + + ext.extensionAPI = 1; + if(json.contains(lit("extension_api"))) + { + ext.extensionAPI = json[lit("extension_api")].toInt(); + } + else + { + qCritical() << "Extension" << QString(ext.name) << "is corrupt, no api version entry"; + continue; + } + + if(json.contains(lit("version"))) + { + ext.version = json[lit("version")].toString(); + } + else + { + qCritical() << "Extension" << QString(ext.name) << "is corrupt, no version entry"; + continue; + } + + if(json.contains(lit("description"))) + { + ext.description = json[lit("description")].toString(); + } + else + { + qCritical() << "Extension" << QString(ext.name) << "is corrupt, no description entry"; + continue; + } + + if(json.contains(lit("author"))) + { + ext.author = json[lit("author")].toString(); + } + else + { + qCritical() << "Extension" << QString(ext.name) << "is corrupt, no author entry"; + continue; + } + + if(json.contains(lit("url"))) + { + ext.extensionURL = json[lit("url")].toString(); + } + else + { + qCritical() << "Extension" << QString(ext.name) << "is corrupt, no URL entry"; + continue; + } + + if(json.contains(lit("minimum_renderdoc"))) + { + QString minVer = json[lit("minimum_renderdoc")].toString(); + + QRegularExpression re(lit("([0-9]*).([0-9]*)")); + QRegularExpressionMatch match = re.match(minVer); + + bool ok = false, badversion = false; + + if(match.hasMatch()) { - int minor = match.captured(2).toInt(&ok); + int major = match.captured(1).toInt(&ok); - // if it needs a higher major version, we can't load it - if(major > RENDERDOC_VERSION_MAJOR) - badversion = true; + if(ok) + { + int minor = match.captured(2).toInt(&ok); - // if major versions are the same and it needs a higher minor, we can't load it either - if(major == RENDERDOC_VERSION_MAJOR && minor > RENDERDOC_VERSION_MINOR) - badversion = true; + // if it needs a higher major version, we can't load it + if(major > RENDERDOC_VERSION_MAJOR) + badversion = true; + + // if major versions are the same and it needs a higher minor, we can't load it + // either + if(major == RENDERDOC_VERSION_MAJOR && minor > RENDERDOC_VERSION_MINOR) + badversion = true; + } + } + + if(!ok) + { + qCritical() << "Extension" << QString(ext.name) + << "is corrupt, minimum_renderdoc doesn't match a MAJOR.MINOR version"; + continue; + } + + if(badversion) + { + qInfo() << "Extension" << QString(ext.name) << "declares minimum_renderdoc" << minVer + << "so skipping"; + continue; } } - if(!ok) - { - qCritical() << "Extension" << QString(ext.name) - << "is corrupt, minimum_renderdoc doesn't match a MAJOR.MINOR version"; - continue; - } - - if(badversion) - { - qInfo() << "Extension" << QString(ext.name) << "declares minimum_renderdoc" << minVer - << "so skipping"; - continue; - } + ret.push_back(ext); } - - ret.push_back(ext); } } } diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp index a55484deb..2bd510158 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp @@ -509,6 +509,22 @@ void PythonContext::GlobalShutdown() Py_Finalize(); } +QStringList PythonContext::GetApplicationExtensionsPaths() +{ + QStringList ret; + + for(QString d : QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)) + { + QDir dir(d); + dir.cd(lit("extensions")); + + if(dir.exists()) + ret.append(dir.absolutePath()); + } + + return ret; +} + void PythonContext::ProcessExtensionWork(std::function callback) { PyGILState_STATE gil = PyGILState_Ensure(); @@ -524,18 +540,20 @@ bool PythonContext::LoadExtension(ICaptureContext &ctx, const rdcstr &extension) PyObject *syspath = PyObject_GetAttrString(sysobj, "path"); - QString configPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - QDir dir(configPath); + // add extensions directories in + for(QString p : PythonContext::GetApplicationExtensionsPaths()) + { + QDir dir(p); - dir.cd(lit("extensions")); + rdcstr path = dir.absolutePath(); - rdcstr path = dir.absolutePath(); - - PyObject *str = PyUnicode_FromString(path.c_str()); - - PyList_Append(syspath, str); - - Py_DecRef(str); + if(dir.exists()) + { + PyObject *str = PyUnicode_FromString(path.c_str()); + PyList_Append(syspath, str); + Py_DecRef(str); + } + } PyObject *ext = NULL; diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.h b/qrenderdoc/Code/pyrenderdoc/PythonContext.h index 74417e2da..0f7db6188 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.h +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.h @@ -55,6 +55,7 @@ public: static void GlobalInit(); static void GlobalShutdown(); + static QStringList GetApplicationExtensionsPaths(); static void ProcessExtensionWork(std::function callback); static bool LoadExtension(ICaptureContext &ctx, const rdcstr &extension); static void ConvertPyArgs(const ExtensionCallbackData &data,