Qt Creator源码分析系列——extensionsystem::PluginSpec

PluginArgumentDescription

插件处理的命令行参数

struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription
{
    QString name;
    QString parameter;
    QString description;
};

PluginDependency

ExtensionSystem::PluginDependencyPluginDependency类包含插件依赖项的名称和所需的兼容版本号。这将在插件的元数据中反映依赖项对象的数据。名称和版本用于解析依赖关系。搜索具有给定名称和插件{兼容版本<=依赖版本<=插件版本}的插件。另请参见ExtensionSystem :: IPlugin,以获取有关插件依赖性和版本匹配的更多信息。

struct EXTENSIONSYSTEM_EXPORT PluginDependency
{
    enum Type { Required, Optional, Test };
    PluginDependency() : type(Required) {}
    QString name;
    QString version;
    Type type;
    bool operator==(const PluginDependency &other) const
    {
    	return name == other.name && version == other.version && type == other.type;
	}
    QString toString() const;
};
uint qHash(const ExtensionSystem::PluginDependency &value);

ExtensionSystem::PluginDependency::Type依赖性是必需的还是可选的。
必需:依赖关系必须存在。
可选:依赖不一定是必需的。您需要确保在未安装此依赖项的情况下能够加载该插件。因此例如,您可能无法链接到依赖项的库。
测试:需要强制加载依赖项才能运行插件的测试。
ExtensionSystem::PluginDependency::name插件的字符串标识符
ExtensionSystem::PluginDependency::version插件必须匹配以填充此依赖性的版本字符串
ExtensionSystem::PluginDependency::type定义依赖性是必需的还是可选的

uint ExtensionSystem::qHash(const PluginDependency &value)
{
    return qHash(value.name);
}

QHash类是提供基于散列表的字典的模板类。QHash <Key,T>是Qt的通用容器类之一。 它存储(键,值)对,并提供与键关联的值的快速查找。QHash提供与QMap非常相似的功能。 不同之处在于:QHash提供比QMap更快的查找。 (有关详细信息,请参见算法复杂度。)遍历QMap时,项目始终按键排序。 使用QHash,可以任意订购商品。QMap的键类型必须提供operator <()。 QHash的键类型必须提供operator ==()和称为qHash()的全局哈希函数(请参阅qHash)。
在这里插入图片描述
返回键的哈希值,使用seed为计算添加种子。

将name、version和Type转为QString

QString PluginDependency::toString() const
{
    return name + " (" + version + typeString(type) + ")";
}
static QString typeString(PluginDependency::Type type)
{
    switch (type) {
    case PluginDependency::Optional:
        return QString(", optional");
    case PluginDependency::Test:
        return QString(", test");
    case PluginDependency::Required:
    default:
        return QString();
    }
}

PluginSpecPrivate

定义的metaData中的键所对应的字符串

namespace {
    const char PLUGIN_METADATA[] = "MetaData";
    const char PLUGIN_NAME[] = "Name";
    const char PLUGIN_VERSION[] = "Version";
    const char PLUGIN_COMPATVERSION[] = "CompatVersion";
    const char PLUGIN_REQUIRED[] = "Required";
    const char PLUGIN_HIDDEN_BY_DEFAULT[] = "HiddenByDefault";
    const char PLUGIN_EXPERIMENTAL[] = "Experimental";
    const char PLUGIN_DISABLED_BY_DEFAULT[] = "DisabledByDefault";
    const char VENDOR[] = "Vendor";
    const char COPYRIGHT[] = "Copyright";
    const char LICENSE[] = "License";
    const char DESCRIPTION[] = "Description";
    const char URL[] = "Url";
    const char CATEGORY[] = "Category";
    const char PLATFORM[] = "Platform";
    const char DEPENDENCIES[] = "Dependencies";
    const char DEPENDENCY_NAME[] = "Name";
    const char DEPENDENCY_VERSION[] = "Version";
    const char DEPENDENCY_TYPE[] = "Type";
    const char DEPENDENCY_TYPE_SOFT[] = "optional";
    const char DEPENDENCY_TYPE_HARD[] = "required";
    const char DEPENDENCY_TYPE_TEST[] = "test";
    const char ARGUMENTS[] = "Arguments";
    const char ARGUMENT_NAME[] = "Name";
    const char ARGUMENT_PARAMETER[] = "Parameter";
    const char ARGUMENT_DESCRIPTION[] = "Description";
}
class EXTENSIONSYSTEM_EXPORT PluginSpecPrivate : public QObject
{
    Q_OBJECT
public:
    PluginSpecPrivate(PluginSpec *spec) : q(spec) {}
    bool read(const QString &fileName);  // 初始化相关数据和QPluginLoader,判别插件是否存在
    bool provides(const QString &pluginName, const QString &version) const;
    bool resolveDependencies(const QVector<PluginSpec *> &specs);
    bool loadLibrary();
    bool initializePlugin();
    bool initializeExtensions();
    bool delayedInitialize();
    IPlugin::ShutdownFlag stop();
    void kill();
    void setEnabledBySettings(bool value);
    void setEnabledByDefault(bool value);
    void setForceEnabled(bool value);
    void setForceDisabled(bool value);
    QPluginLoader loader;
    QString name;
    QString version;
    QString compatVersion;
    bool required = false;
    bool hiddenByDefault = false;
    bool experimental = false;
    bool enabledByDefault = true;
    QString vendor;
    QString copyright;
    QString license;
    QString description;
    QString url;
    QString category;
    QRegExp platformSpecification;
    QVector<PluginDependency> dependencies;
    QJsonObject metaData;
    bool enabledBySettings = true;
    bool enabledIndirectly = false;
    bool forceEnabled = false;
    bool forceDisabled = false;
    QString location;  // 插件文件的路径绝对路径
    QString filePath;  // 包含插件文件名的绝对路径
    QStringList arguments;
    QHash<PluginDependency, PluginSpec *> dependencySpecs;
    PluginSpec::PluginArgumentDescriptions argumentDescriptions;
    IPlugin *plugin = nullptr;
    PluginSpec::State state = PluginSpec::Invalid;
    bool hasError = false;
    QString errorString;
    static bool isValidVersion(const QString &version);
    static int versionCompare(const QString &version1, const QString &version2);
    QVector<PluginSpec *> enableDependenciesIndirectly(bool enableTestDependencies = false);
    bool readMetaData(const QJsonObject &pluginMetaData);
private:
    PluginSpec *q;
    bool reportError(const QString &err);
    static const QRegExp &versionRegExp();
};

关于插件版本的相关函数:

const QRegExp &PluginSpecPrivate::versionRegExp()
{
    static const QRegExp reg(QLatin1String("([0-9]+)(?:[.]([0-9]+))?(?:[.]([0-9]+))?(?:_([0-9]+))?"));
    return reg;
}
bool PluginSpecPrivate::isValidVersion(const QString &version)
{
    return versionRegExp().exactMatch(version);
}
// 版本比较函数
int PluginSpecPrivate::versionCompare(const QString &version1, const QString &version2)
{
    QRegExp reg1 = versionRegExp();
    QRegExp reg2 = versionRegExp();
    if (!reg1.exactMatch(version1))
        return 0;
    if (!reg2.exactMatch(version2))
        return 0;
    int number1;
    int number2;
    for (int i = 0; i < 4; ++i) {
        number1 = reg1.cap(i+1).toInt();
        number2 = reg2.cap(i+1).toInt();
        if (number1 < number2)
            return -1;
        if (number1 > number2)
            return 1;
    }
    return 0;
}
// 插件名和版本比较函数
bool PluginSpecPrivate::provides(const QString &pluginName, const QString &pluginVersion) const
{
    if (QString::compare(pluginName, name, Qt::CaseInsensitive) != 0)
        return false;
    return (versionCompare(version, pluginVersion) >= 0) && (versionCompare(compatVersion, pluginVersion) <= 0);
}

在这里插入图片描述
如果str与此正则表达式完全匹配,则返回true;否则返回false。 您可以通过调用matchedLength()来确定匹配了多少字符串。
对于给定的regexp字符串R,exactMatch(“ R”)等同于indexIn(“ ^ R $”),因为精确匹配()有效地将regexp括在字符串的开头和结尾处,除非它设置了matchLength( )不同。

Read

bool PluginSpecPrivate::read(const QString &fileName)如果文件不代表Qt Creator插件,则返回false。
在这里插入图片描述
返回日志记录类别中调试消息的输出流。宏扩展为检查QLoggingCategory :: isDebugEnabled()是否评估为true的代码。 如果是这样,将处理流参数并将其发送到消息处理程序。

QLoggingCategory category("driver.usb");
qCDebug(category) << "a debug message";
bool PluginSpecPrivate::read(const QString &fileName)
{
    qCDebug(pluginLog) << "\nReading meta data of" << fileName;
    name
        = version
        = compatVersion
        = vendor
        = copyright
        = license
        = description
        = url
        = category
        = location
        = QString(); // 初始化
    state = PluginSpec::Invalid;
    hasError = false;
    errorString.clear();
    dependencies.clear();
    metaData = QJsonObject();
    QFileInfo fileInfo(fileName);
    location = fileInfo.absolutePath(); // 返回文件的路径绝对路径
    filePath = fileInfo.absoluteFilePath(); // 返回包含文件名的绝对路径。
    loader.setFileName(filePath);
    if (loader.fileName().isEmpty()) {
        qCDebug(pluginLog) << "Cannot open file";
        return false;
    }

    if (!readMetaData(loader.metaData()))
        return false;

    state = PluginSpec::Read;  // 成功将state设置为PluginSpec::Read;
    return true;
}

QPluginLoader类在运行时加载插件。 QPluginLoader提供对Qt插件的访问。 Qt插件存储在共享库(DLL)中,与使用QLibrary访问的共享库相比,它们具有以下优点:
QPluginLoader类检查插件是否与应用程序相同的Qt版本链接。
QPluginLoader提供对根组件对象(instance())的直接访问,而不是强迫您手动解析C函数。
QPluginLoader对象的实例在单个共享库文件(我们称为插件)上运行。它以独立于平台的方式提供对插件中功能的访问。要指定要加载的插件,请在构造函数中传递文件名,或使用setFileName()进行设置。
最重要的功能是:load()用于动态加载插件文件; isLoaded()用于检查加载是否成功; instance()用于访问插件中的根组件。如果尚未加载插件,instance()函数将隐式尝试加载该插件。可以使用QPluginLoader的多个实例来访问同一物理插件。
加载后,插件将保留在内存中,直到所有QPluginLoader实例都已卸载,或者直到应用程序终止为止。您可以尝试使用unload()卸载插件,但是如果QPluginLoader的其他实例使用相同的库,则调用将失败,并且仅当每个实例都调用了unload()时才进行卸载。在卸载之前,根组件也将被删除。
有关如何使应用程序可通过插件扩展的更多信息,请参见如何创建Qt插件。请注意,如果您的应用程序与Qt静态链接,则无法使用QPluginLoader。在这种情况下,您还必须静态链接到插件。如果需要在静态链接的应用程序中加载动态库,则可以使用QLibrary。
在这里插入图片描述
返回此插件的元数据。 元数据是在编译插件时使用Q_PLUGIN_METADATA()宏以json格式指定的数据。无需实际加载插件即可以快速,廉价的方式查询元数据。 这使得例如 在那里存储插件的功能,并根据此元数据来决定是否加载插件。

bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
{
    qCDebug(pluginLog) << "MetaData:" << QJsonDocument(pluginMetaData).toJson(); // 输出Json的内容
    // 验证插件plugin IID
    QJsonValue value;
    value = pluginMetaData.value(QLatin1String("IID"));
    if (!value.isString()) {
        qCDebug(pluginLog) << "Not a plugin (no string IID found)";
        return false;
    }
    if (value.toString() != PluginManager::pluginIID()) {
        qCDebug(pluginLog) << "Plugin ignored (IID does not match)";
        return false;
    }
	// 验证插件PLUGIN_METADATA,并赋值metaData
    value = pluginMetaData.value(QLatin1String(PLUGIN_METADATA));
    if (!value.isObject())
        return reportError(tr("Plugin meta data not found"));
    metaData = value.toObject();
	// 从metaData中读取PLUGIN_NAME,并赋值name
    value = metaData.value(QLatin1String(PLUGIN_NAME));
    if (value.isUndefined())
        return reportError(msgValueMissing(PLUGIN_NAME));
    if (!value.isString())
        return reportError(msgValueIsNotAString(PLUGIN_NAME));
    name = value.toString();
	// 验证插件PLUGIN_VERSION,并赋值version
    value = metaData.value(QLatin1String(PLUGIN_VERSION));
    if (value.isUndefined())
        return reportError(msgValueMissing(PLUGIN_VERSION));
    if (!value.isString())
        return reportError(msgValueIsNotAString(PLUGIN_VERSION));
    version = value.toString();
    if (!isValidVersion(version))
        return reportError(msgInvalidFormat(PLUGIN_VERSION, version));
	// 验证插件PLUGIN_COMPATVERSION,并赋值compatVersion
    value = metaData.value(QLatin1String(PLUGIN_COMPATVERSION));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(PLUGIN_COMPATVERSION));
    compatVersion = value.toString(version);
    if (!value.isUndefined() && !isValidVersion(compatVersion))
        return reportError(msgInvalidFormat(PLUGIN_COMPATVERSION, compatVersion));
	// 验证插件PLUGIN_REQUIRED,并赋值required
    value = metaData.value(QLatin1String(PLUGIN_REQUIRED));
    if (!value.isUndefined() && !value.isBool())
        return reportError(msgValueIsNotABool(PLUGIN_REQUIRED));
    required = value.toBool(false);
    qCDebug(pluginLog) << "required =" << required;
	// 验证插件PLUGIN_HIDDEN_BY_DEFAULT,并赋值hiddenByDefault
    value = metaData.value(QLatin1String(PLUGIN_HIDDEN_BY_DEFAULT));
    if (!value.isUndefined() && !value.isBool())
        return reportError(msgValueIsNotABool(PLUGIN_HIDDEN_BY_DEFAULT));
    hiddenByDefault = value.toBool(false);
    qCDebug(pluginLog) << "hiddenByDefault =" << hiddenByDefault;
	// 验证插件PLUGIN_EXPERIMENTAL,并赋值experimental
    value = metaData.value(QLatin1String(PLUGIN_EXPERIMENTAL));
    if (!value.isUndefined() && !value.isBool())
        return reportError(msgValueIsNotABool(PLUGIN_EXPERIMENTAL));
    experimental = value.toBool(false);
    qCDebug(pluginLog) << "experimental =" << experimental;
	// 验证插件PLUGIN_DISABLED_BY_DEFAULT,并赋值enabledByDefault
    value = metaData.value(QLatin1String(PLUGIN_DISABLED_BY_DEFAULT));
    if (!value.isUndefined() && !value.isBool())
        return reportError(msgValueIsNotABool(PLUGIN_DISABLED_BY_DEFAULT));
    enabledByDefault = !value.toBool(false);
    qCDebug(pluginLog) << "enabledByDefault =" << enabledByDefault;
	// 如果enabledByDefault为true,默认enabledByDefault为false
    if (experimental)
        enabledByDefault = false;
    // 默认enabledBySettings为enabledByDefault
    enabledBySettings = enabledByDefault;
	// 验证插件VENDOR,并赋值vendor
    value = metaData.value(QLatin1String(VENDOR));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(VENDOR));
    vendor = value.toString();
	// 验证插件COPYRIGHT,并赋值copyright
    value = metaData.value(QLatin1String(COPYRIGHT));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(COPYRIGHT));
    copyright = value.toString();
	// 验证插件DESCRIPTION,并赋值description
    value = metaData.value(QLatin1String(DESCRIPTION));
    if (!value.isUndefined() && !Utils::readMultiLineString(value, &description))
        return reportError(msgValueIsNotAString(DESCRIPTION));
	// 验证插件URL,并赋值url
    value = metaData.value(QLatin1String(URL));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(URL));
    url = value.toString();
	// 验证插件CATEGORY,并赋值category 
    value = metaData.value(QLatin1String(CATEGORY));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(CATEGORY));
    category = value.toString();
	// 验证插件LICENSE,并赋值license
    value = metaData.value(QLatin1String(LICENSE));
    if (!value.isUndefined() && !Utils::readMultiLineString(value, &license))
        return reportError(msgValueIsNotAMultilineString(LICENSE));
	// 验证插件PLATFORM,并将字符串赋值platformSpecification正则类
    value = metaData.value(QLatin1String(PLATFORM));
    if (!value.isUndefined() && !value.isString())
        return reportError(msgValueIsNotAString(PLATFORM));
    const QString platformSpec = value.toString().trimmed();
    if (!platformSpec.isEmpty()) {
        platformSpecification.setPattern(platformSpec);
        if (!platformSpecification.isValid())
            return reportError(tr("Invalid platform specification \"%1\": %2")
                               .arg(platformSpec, platformSpecification.errorString()));
    }
	// 验证插件DEPENDENCIES,并将字符串赋值dependencies
    value = metaData.value(QLatin1String(DEPENDENCIES));
    if (!value.isUndefined() && !value.isArray())
        return reportError(msgValueIsNotAObjectArray(DEPENDENCIES));
    if (!value.isUndefined()) {
        QJsonArray array = value.toArray();
        foreach (const QJsonValue &v, array) {
            if (!v.isObject())
                return reportError(msgValueIsNotAObjectArray(DEPENDENCIES));
            QJsonObject dependencyObject = v.toObject();
            PluginDependency dep;
            value = dependencyObject.value(QLatin1String(DEPENDENCY_NAME));
            if (value.isUndefined())
                return reportError(tr("Dependency: %1").arg(msgValueMissing(DEPENDENCY_NAME)));
            if (!value.isString())
                return reportError(tr("Dependency: %1").arg(msgValueIsNotAString(DEPENDENCY_NAME)));
            dep.name = value.toString();
            value = dependencyObject.value(QLatin1String(DEPENDENCY_VERSION));
            if (!value.isUndefined() && !value.isString())
                return reportError(tr("Dependency: %1").arg(msgValueIsNotAString(DEPENDENCY_VERSION)));
            dep.version = value.toString();
            if (!isValidVersion(dep.version))
                return reportError(tr("Dependency: %1").arg(msgInvalidFormat(DEPENDENCY_VERSION,
                                                                             dep.version)));
            dep.type = PluginDependency::Required;
            value = dependencyObject.value(QLatin1String(DEPENDENCY_TYPE));
            if (!value.isUndefined() && !value.isString())
                return reportError(tr("Dependency: %1").arg(msgValueIsNotAString(DEPENDENCY_TYPE)));
            if (!value.isUndefined()) {
                const QString typeValue = value.toString();
                if (typeValue.toLower() == QLatin1String(DEPENDENCY_TYPE_HARD)) {
                    dep.type = PluginDependency::Required;
                } else if (typeValue.toLower() == QLatin1String(DEPENDENCY_TYPE_SOFT)) {
                    dep.type = PluginDependency::Optional;
                } else if (typeValue.toLower() == QLatin1String(DEPENDENCY_TYPE_TEST)) {
                    dep.type = PluginDependency::Test;
                } else {
                    return reportError(tr("Dependency: \"%1\" must be \"%2\" or \"%3\" (is \"%4\").")
                                       .arg(QLatin1String(DEPENDENCY_TYPE),
                                            QLatin1String(DEPENDENCY_TYPE_HARD),
                                            QLatin1String(DEPENDENCY_TYPE_SOFT),
                                            typeValue));
                }
            }
            dependencies.append(dep);
        }
    }
	// 验证插件ARGUMENTS,并将字符串赋值argumentDescriptions
    value = metaData.value(QLatin1String(ARGUMENTS));
    if (!value.isUndefined() && !value.isArray())
        return reportError(msgValueIsNotAObjectArray(ARGUMENTS));
    if (!value.isUndefined()) {
        QJsonArray array = value.toArray();
        foreach (const QJsonValue &v, array) {
            if (!v.isObject())
                return reportError(msgValueIsNotAObjectArray(ARGUMENTS));
            QJsonObject argumentObject = v.toObject();
            PluginArgumentDescription arg;
            value = argumentObject.value(QLatin1String(ARGUMENT_NAME));
            if (value.isUndefined())
                return reportError(tr("Argument: %1").arg(msgValueMissing(ARGUMENT_NAME)));
            if (!value.isString())
                return reportError(tr("Argument: %1").arg(msgValueIsNotAString(ARGUMENT_NAME)));
            arg.name = value.toString();
            if (arg.name.isEmpty())
                return reportError(tr("Argument: \"%1\" is empty").arg(QLatin1String(ARGUMENT_NAME)));
            value = argumentObject.value(QLatin1String(ARGUMENT_DESCRIPTION));
            if (!value.isUndefined() && !value.isString())
                return reportError(tr("Argument: %1").arg(msgValueIsNotAString(ARGUMENT_DESCRIPTION)));
            arg.description = value.toString();
            value = argumentObject.value(QLatin1String(ARGUMENT_PARAMETER));
            if (!value.isUndefined() && !value.isString())
                return reportError(tr("Argument: %1").arg(msgValueIsNotAString(ARGUMENT_PARAMETER)));
            arg.parameter = value.toString();
            argumentDescriptions.append(arg);
            qCDebug(pluginLog) << "Argument:" << arg.name << "Parameter:" << arg.parameter
                               << "Description:" << arg.description;
        }
    }

    return true;
}
bool PluginSpecPrivate::reportError(const QString &err)
{
    errorString = err;
    hasError = true;
    return true;
}

Resolved

QHash类是提供基于散列表的字典的模板类。QHash <Key,T>是Qt的通用容器类之一。 它存储(键,值)对,并提供与键关联的值的快速查找。Resolved就是将本插件dependencies所列出的依赖的必需插件项PluginDependency与相应的插件的PluginSpec指针关联,存入QHash字典中(也就是存储在本插件的dependencySpecs变量中)。

bool PluginSpecPrivate::resolveDependencies(const QVector<PluginSpec *> &specs)
{
    if (hasError)
        return false;
    // 处于Read阶段才能进入Resolved阶段
    if (state == PluginSpec::Resolved)
        state = PluginSpec::Read; // Go back, so we just re-resolve the dependencies.
    if (state != PluginSpec::Read) {
        errorString = QCoreApplication::translate("PluginSpec", "Resolving dependencies failed because state != Read");
        hasError = true;
        return false;
    }
    QHash<PluginDependency, PluginSpec *> resolvedDependencies;
    foreach (const PluginDependency &dependency, dependencies) {
    	// 本插件dependencies所列出的依赖插件项形参容器中有没有
        PluginSpec * const found = Utils::findOrDefault(specs, [&dependency](PluginSpec *spec) {
            return spec->provides(dependency.name, dependency.version);
        });
        if (!found) {  //如果没有找到
        	// 如果不是必需项,可以继续解析
            if (dependency.type == PluginDependency::Required) {
                hasError = true;
                if (!errorString.isEmpty())
                    errorString.append(QLatin1Char('\n'));
                errorString.append(QCoreApplication::translate("PluginSpec", "Could not resolve dependency '%1(%2)'")
                    .arg(dependency.name).arg(dependency.version));
            }
            continue;
        }
        resolvedDependencies.insert(dependency, found);
    }
    if (hasError)
        return false;

    dependencySpecs = resolvedDependencies;

    state = PluginSpec::Resolved;

    return true;
}

//返回实际上间接启用的插件

QVector<PluginSpec *> PluginSpecPrivate::enableDependenciesIndirectly(bool enableTestDependencies)
{
    if (!q->isEffectivelyEnabled()) // plugin not enabled, nothing to do 插件还未使能
        return {};
    QVector<PluginSpec *> enabled;
    // QHash<PluginDependency, PluginSpec *> dependencySpecs
    for (auto it = dependencySpecs.cbegin(), end = dependencySpecs.cend(); it != end; ++it) {
        if (it.key().type != PluginDependency::Required
                && (!enableTestDependencies || it.key().type != PluginDependency::Test))
            continue;
        PluginSpec *dependencySpec = it.value();
        if (!dependencySpec->isEffectivelyEnabled()) {
            dependencySpec->d->enabledIndirectly = true;
            enabled << dependencySpec;
        }
    }
    return enabled;
}

Loaded

加载插件,处于Resolved阶段的插件才能调用。

bool PluginSpecPrivate::loadLibrary()
{
    if (hasError)
        return false;
    if (state != PluginSpec::Resolved) {
        if (state == PluginSpec::Loaded)
            return true;
        errorString = QCoreApplication::translate("PluginSpec", "Loading the library failed because state != Resolved");
        hasError = true;
        return false;
    }
    if (!loader.load()) {
        hasError = true;
        errorString = QDir::toNativeSeparators(filePath)
            + QString::fromLatin1(": ") + loader.errorString();
        return false;
    }
    auto *pluginObject = qobject_cast<IPlugin*>(loader.instance());
    if (!pluginObject) {
        hasError = true;
        errorString = QCoreApplication::translate("PluginSpec", "Plugin is not valid (does not derive from IPlugin)");
        loader.unload();
        return false;
    }
    state = PluginSpec::Loaded;
    plugin = pluginObject;
    plugin->d->pluginSpec = q;
    return true;
}

在这里插入图片描述
加载插件,如果插件加载成功,则返回true;否则,返回true。 否则返回false。 由于instance()始终在解析任何符号之前调用此函数,因此无需显式调用它。 在某些情况下,您可能需要预先加载插件,在这种情况下,您将使用此功能。
在这里插入图片描述
返回插件的根组件对象。 如果需要,将加载插件。 如果无法加载插件或无法实例化根组件对象,则该函数返回0。如果根组件对象被破坏,则调用此函数将创建一个新实例。
销毁QPluginLoader时,不会删除此函数返回的根组件。 如果要确保删除根组件,则无需再访问核心组件时应立即调用unload()。 当库最终被卸载时,根组件将被自动删除。
组件对象是QObject。 使用qobject_cast()访问您感兴趣的接口。

Initialized

initializePlugin函数:如果状态不是Loaded阶段而是在Initialized,则直接返回。如果是Loaded阶段,则进入Initialize操作。主要是调用插件自己实现的初始化函数。

bool PluginSpecPrivate::initializePlugin()
{
    if (hasError)
        return false;
    if (state != PluginSpec::Loaded) {
        if (state == PluginSpec::Initialized)
            return true;
        errorString = QCoreApplication::translate("PluginSpec", "Initializing the plugin failed because state != Loaded");
        hasError = true;
        return false;
    }
    if (!plugin) {
        errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to initialize");
        hasError = true;
        return false;
    }
    QString err;
    if (!plugin->initialize(arguments, &err)) {
        errorString = QCoreApplication::translate("PluginSpec", "Plugin initialization failed: %1").arg(err);
        hasError = true;
        return false;
    }
    state = PluginSpec::Initialized;
    return true;
}

Running

initializeExtensions函数:如果state不处于Initialized状态,只有在Running状态直接返回,否则出错。如果处于Initialized状态,则调用插件的extensionsInitialized函数,并进入Running状态。

bool PluginSpecPrivate::initializeExtensions()
{
    if (hasError)
        return false;
    if (state != PluginSpec::Initialized) {
        if (state == PluginSpec::Running)
            return true;
        errorString = QCoreApplication::translate("PluginSpec", "Cannot perform extensionsInitialized because state != Initialized");
        hasError = true;
        return false;
    }
    if (!plugin) {
        errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to perform extensionsInitialized");
        hasError = true;
        return false;
    }
    plugin->extensionsInitialized();
    state = PluginSpec::Running;
    return true;
}
bool PluginSpecPrivate::delayedInitialize()
{
    if (hasError)
        return false;
    if (state != PluginSpec::Running)
        return false;
    if (!plugin) {
        errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to perform delayedInitialize");
        hasError = true;
        return false;
    }
    return plugin->delayedInitialize();
}

delayedInitialize函数:调用该函数,状态必需处于Running,最终需要调用delayedInitialize函数。

Stopped

如果plugin变为nullptr,则返回Synchronous同步标志。状态变为Stopped状态,并调用插件函数aboutToShutdown。

IPlugin::ShutdownFlag PluginSpecPrivate::stop()
{
    if (!plugin)
        return IPlugin::SynchronousShutdown;
    state = PluginSpec::Stopped;
    return plugin->aboutToShutdown();
}

Deleted

最终状态置为Deleted

void PluginSpecPrivate::kill()
{
    if (!plugin)
        return;
    delete plugin;
    plugin = nullptr;
    state = PluginSpec::Deleted;
}

PluginSpec

class EXTENSIONSYSTEM_EXPORT PluginSpec
{
public:
    enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
    ~PluginSpec();
    // information from the xml file, valid after 'Read' state is reached xml文件中的信息,到达Read状态后有效
    // 插件名称 在达到PluginSpec::Read状态之后才有效。
    QString name() const { return d->name; } 
    // 插件版本 在达到PluginSpec::Read状态之后才有效。
    QString version() const { return d->version; }
    // 插件兼容性版本 在达到PluginSpec::Read状态之后才有效。
    QString compatVersion() const { return d->compatVersion; }
    // 插件供应商 在达到PluginSpec::Read状态之后才有效。
    QString vendor() const { return d->vendor; }
    // 插件版权 在达到PluginSpec::Read状态之后才有效。
    QString copyright() const { return d->copyright; }
    // 插件许可证 在达到PluginSpec::Read状态之后才有效。
    QString license() const { return d->license; }
    // 插件说明 在达到PluginSpec::Read状态之后才有效。
    QString description() const { return d->description; }
    // 插件URL 可以找到有关插件的更多信息,在达到PluginSpec::Read状态之后才有效。
    QString url() const { return d->url; }
    // 插件所属的类别 类别是一组插件,可让它们在UI中保持在一起,如果插件不属于类别,则返回一个空字符串。
    QString category() const { return d->category; }
    // 与该插件适用的平台匹配的QRegExp。 空模式表示所有平台。
    QRegExp platformSpecification() const { return d->platformSpecification; }
    bool isAvailableForHostPlatform() const { return d->platformSpecification.isEmpty()
            || d->platformSpecification.indexIn(PluginManager::platformName()) >= 0; } 
    bool isRequired() const { return d->required; }
    bool isHiddenByDefault() const { return d->hiddenByDefault; }
    // 返回插件是否设置了实验性标志
    bool isExperimental() const { return d->experimental; }
    // 返回默认情况下是否启用该插件。 插件可能由于该插件是试验性插件而被禁用,或者因为安装设置默认将其定义为禁用。
    bool isEnabledByDefault() const { return d->enabledByDefault; }
    // 返回是否应在启动时加载插件,并考虑默认的启用状态和用户的设置。 即使该插件是另一个已启用插件的要求而加载的,此函数也可能返回false。
    bool isEnabledBySettings() const { return d->enabledBySettings; }
    // 如果由于用户取消选择此插件或其依赖项而未完成加载,则返回true。    
    bool isEnabledIndirectly() const { return d->enabledIndirectly; }
    // 返回是否通过命令行上的-load选项启用了插件。
    bool isForceEnabled() const { return d->forceEnabled; }
    // 返回是否通过命令行上的-noload选项禁用了插件。
    bool isForceDisabled() const { return d->forceDisabled; }
    // 返回是否在启动时加载插件。
    bool isEffectivelyEnabled() const { 
    	if (!isAvailableForHostPlatform())
        	return false;
    	if (isForceEnabled() || isEnabledIndirectly())
        	return true;
    	if (isForceDisabled())
        	return false;
    	return isEnabledBySettings(); 
    }
    // 插件依赖性 在达到PluginSpec :: Read状态之后才有效。
    QVector<PluginDependency> dependencies() const { return d->dependencies; }
    QJsonObject metaData() const { return d->metaData; }
    using PluginArgumentDescriptions = QVector<PluginArgumentDescription>;
    // 返回插件处理的命令行参数的描述列表。
    PluginArgumentDescriptions argumentDescriptions() const { return d->argumentDescriptions; }
    // other information, valid after 'Read' state is reached 其他信息
    // 包含此PluginSpec所对应的插件XML描述文件的目录的绝对路径。
    QString location() const { return d->location; }
    // 此PluginSpec所对应的插件XML描述文件(包括文件名)的绝对路径。
    QString filePath() const { return d->filePath; }
    // 特定于插件的命令行参数。 在启动时设置。
    QStringList arguments() const { return d->arguments; }
    // 将特定于插件的命令行参数设置为\ a参数。
    void setArguments(const QStringList &arguments) { d->arguments = arguments; }
    // 在插件特定的命令行参数中添加\ a参数。
    void addArgument(const QString &argument) { d->arguments.push_back(argument); }
    // 返回此插件是否可用于填写给定插件名称和版本的依赖项。
    bool provides(const QString &pluginName, const QString &version) const { return d->provides(pluginName, version); }
    // dependency specs, valid after 'Resolved' state is reached
    // 返回已经解决现有插件规范的依赖项列表。 如果达到PluginSpec :: Resolved状态,则有效。
    QHash<PluginDependency, PluginSpec *> dependencySpecs() const { return d->dependencySpecs; }
    bool requiresAny(const QSet<PluginSpec *> &plugins) const
    {
		return Utils::anyOf(d->dependencySpecs.keys(), [this, &plugins](const PluginDependency &dep) {
        return dep.type == PluginDependency::Required
               && plugins.contains(d->dependencySpecs.value(dep));
    	});
	}
    // linked plugin instance, valid after 'Loaded' state is reached 链接的插件实例,在达到“已加载”状态后有效。如果插件库已成功加载,则返回对应的IPlugin实例。
    IPlugin *plugin() const { return d->plugin; }
    // state 插件当前所在的状态。 有关详细信息,请参见PluginSpec::State枚举的描述。
    State state() const { return d->state; }
    // 返回在读取/启动插件时是否发生错误。
    bool hasError() const { return d->hasError; }
    // 发生错误时,可能会有多行详细的错误描述。
    QString errorString() const { return d->errorString; }
    void setEnabledBySettings(bool value) { d->setEnabledBySettings(value); }
private:
    PluginSpec() : d(new PluginSpecPrivate(this)) {};
    Internal::PluginSpecPrivate *d;
    friend class PluginView;
    friend class Internal::OptionsParser;
    friend class Internal::PluginManagerPrivate;
    friend class Internal::PluginSpecPrivate;
};

在这里插入图片描述
尝试从位置偏移量(默认为0)找到str中的匹配项。 如果offset为-1,则搜索从最后一个字符开始;否则,搜索从最后一个字符开始。 如果为-2,则在最后一个字符的后面; 等等
返回第一个匹配项的位置;如果没有匹配项,则返回-1。
caretMode参数可用于指示^是否应在索引0或偏移量处匹配。您可能更喜欢使用QString :: indexOf(),QString :: contains()甚至QStringList :: filter()。 要替换匹配项,请使用QString :: replace()。

QString str = "offsets: 1.23 .50 71.00 6.00";
QRegExp rx("\\d*\\.\\d+");    // primitive floating point matching
int count = 0;
int pos = 0;
while ((pos = rx.indexIn(str, pos)) != -1) {
    ++count;
    pos += rx.matchedLength();
}
// pos will be 9, 14, 18 and finally 24; count will end up as 4
QHash<PluginDependency, PluginSpec *> dependencySpecs() const { return d->dependencySpecs; }
    bool requiresAny(const QSet<PluginSpec *> &plugins) const
    {
		return Utils::anyOf(d->dependencySpecs.keys(), [this, &plugins](const PluginDependency &dep) {
        return dep.type == PluginDependency::Required
               && plugins.contains(d->dependencySpecs.value(dep));
    	});
	}
发布了134 篇原创文章 · 获赞 141 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/asmartkiller/article/details/104457938
今日推荐