diff options
| author | saturneric <[email protected]> | 2026-09-14 23:25:42 +0200 |
|---|---|---|
| committer | saturneric <[email protected]> | 2026-09-14 23:25:42 +0200 |
| commit | 13ce0f5eb5dbeafc830eee41ca97c00fbcfe2bed (patch) | |
| tree | 385938e678bf611b095ac61cf87ccf1ee26faeb4 | |
| parent | feat(module): expose SDK ABI version metadata (diff) | |
| download | modules-13ce0f5eb5dbeafc830eee41ca97c00fbcfe2bed.tar.gz modules-13ce0f5eb5dbeafc830eee41ca97c00fbcfe2bed.zip | |
feat(modules): add single-symbol module bootstrap API
- define versioned host and module API bootstrap tables
- adapt classic module lifecycle functions to the new API
- validate host ABI compatibility before exposing modules
- migrate email, GnuPG, key server, and version modules
- retain legacy metadata and event handling compatibility
| -rw-r--r-- | include/GFModuleBootstrap.h | 167 | ||||
| -rw-r--r-- | src/m_email/EMailModule.cpp | 3 | ||||
| -rw-r--r-- | src/m_gpg_info/GnuPGInfoGatheringModule.cpp | 9 | ||||
| -rw-r--r-- | src/m_key_server_sync/KeyServerSyncModule.cpp | 3 | ||||
| -rw-r--r-- | src/m_ver_check/VersionCheckingModule.cpp | 3 |
5 files changed, 179 insertions, 6 deletions
diff --git a/include/GFModuleBootstrap.h b/include/GFModuleBootstrap.h new file mode 100644 index 0000000..167d846 --- /dev/null +++ b/include/GFModuleBootstrap.h @@ -0,0 +1,167 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <GFSDKModuleApi.h> + +#include "GFModuleExport.h" +#include "GFSDKBuildInfo.h" + +/** + * @file GFModuleBootstrap.h + * @brief Define a module through the single bootstrap symbol. + * + * Replaces GF_MODULE_API_DECLARE + GF_MODULE_API_DEFINE, which between them + * required a module to export ten separate symbols. Here a module exports + * exactly one, and describes itself through a versioned table. + * + * WHAT A MODULE GAINS. The host hands `activate` a GFHostApi table instead of + * the module linking ~130 global SDK symbols. That is what makes it possible + * for the host to withhold a capability (hand over a table with the gpg group + * null), and what lets a test supply its own table instead of compiling a + * parallel set of stub symbols that drift from the real SDK. + * + * USAGE, once per module: + * + * GF_MODULE_BOOTSTRAP("com.example.module", "1.0.0", + * MyActivate, MyExecute, MyDeactivate, MyUnregister) + * + * The host api pointer is stashed in GFHost() for the module's own code to + * reach without threading it through every function. It is valid from the + * moment activate() is called until the module is unloaded. + */ + +/// The host table this module was activated with. Null before activation. +inline const GFHostApi*& GFHostApiSlot() { + static const GFHostApi* host = nullptr; + return host; +} + +/// Convenience accessor for module code. +inline auto GFHost() -> const GFHostApi* { return GFHostApiSlot(); } + +/** + * @brief Define the module's bootstrap symbol and lifecycle table. + * + * @param id module identifier, e.g. "com.example.module" + * @param ver module version, e.g. "1.0.0" + * @param fn_act int(const GFHostApi*, void*) -- setup; host api is borrowed + * @param fn_exe int(GFModuleEvent*) -- handle one event + * @param fn_dea int(void) -- cancel work, drop registrations + * @param fn_unr void(void) -- final teardown + */ +#define GF_MODULE_BOOTSTRAP(id, ver, fn_act, fn_exe, fn_dea, fn_unr) \ + static int GFBootstrapActivate(const GFHostApi* host, void* reserved) { \ + GFHostApiSlot() = host; \ + return (fn_act)(host, reserved); \ + } \ + extern "C" GF_MODULE_EXPORT const GFModuleApi* GFModuleGetApi( \ + uint32_t host_abi) { \ + /* Decline a host outside the range this module was built for, rather */\ + /* than loading and failing on the first mismatched call. */ \ + if (host_abi < GF_SDK_ABI_MIN_SUPPORTED || \ + host_abi > GF_SDK_ABI_VERSION) { \ + return nullptr; \ + } \ + /* Static: the host borrows this table and never frees it. */ \ + static const GFModuleApi kApi = { \ + sizeof(GFModuleApi), GF_SDK_ABI_VERSION, (id), (ver), \ + &GFBootstrapActivate, (fn_exe), (fn_dea), (fn_unr), \ + }; \ + return &kApi; \ + } + + +/** + * @brief Bootstrap a module that already has the classic lifecycle functions. + * + * Adapts GFRegisterModule / GFActiveModule / GFExecuteModule / + * GFDeactivateModule / GFUnregisterModule onto the table, so porting a module + * is a one-line change at the top of the file rather than a rewrite of its + * entry points. It also keeps GFGetModuleID() and friends defined, because + * module code calls GFGetModuleID() constantly (LISTEN, CB, the translator + * reader) and those calls should not have to change. + * + * Note the ORDER inside activate: the host's table has no separate register + * step, so whatever the module did in GFRegisterModule -- typically + * registering its translator -- has to run here, before GFActiveModule starts + * subscribing to events. + */ +#define GF_MODULE_BOOTSTRAP_V2(id, name, ver, desc, author) \ + auto GFGetModuleGFSDKVersion() -> const char* { \ + return DUP(GF_SDK_VERSION_STR); \ + } \ + auto GFGetModuleGFSDKABIVersion() -> int { return GF_SDK_ABI_VERSION; } \ + auto GFGetModuleQtEnvVersion() -> const char* { \ + return DUP(QT_VERSION_STR); \ + } \ + auto GFGetModuleID() -> const char* { return DUP((id)); } \ + auto GFGetModuleVersion() -> const char* { return DUP((ver)); } \ + auto GFGetModuleMetaData() -> GFModuleMetaData* { \ + return QMapToGFModuleMetaDataList( \ + {{"Name", (name)}, {"Description", (desc)}, {"Author", (author)}}); \ + } \ + using MEvent = QMap<QString, QString>; \ + using EventHandler = std::function<int(const MEvent&)>; \ + namespace { \ + /* NOTE: `Module##nameEventHandlers` pastes onto the IDENTIFIER */\ + /* `nameEventHandlers`, so it does not substitute the `name` parameter */\ + /* at all -- it always yields ModulenameEventHandlers. That is inherited */\ + /* from GF_MODULE_API_DEFINE_V2 and kept deliberately: `name` is a string*/\ + /* literal here, and actually pasting it would not compile. */\ + static QMap<QString, EventHandler> Module##nameEventHandlers; \ + static QMap<QString, EventHandler>& _gr_module_event_handlers = \ + Module##nameEventHandlers; \ + } \ + DEFINE_EXECUTE_API_USING_STANDARD_EVEN_HANDLE_MODEL \ + static int GFBootstrapActivate(const GFHostApi* host, void*) { \ + GFHostApiSlot() = host; \ + const int rc = GFRegisterModule(); \ + if (rc != 0) return rc; \ + return GFActiveModule(); \ + } \ + static void GFBootstrapUnregister() { (void)GFUnregisterModule(); } \ + extern "C" GF_MODULE_EXPORT const GFModuleApi* GFModuleGetApi( \ + uint32_t host_abi) { \ + if (host_abi < GF_SDK_ABI_MIN_SUPPORTED || \ + host_abi > GF_SDK_ABI_VERSION) { \ + return nullptr; \ + } \ + static const GFModuleApi kApi = { \ + sizeof(GFModuleApi), \ + GF_SDK_ABI_VERSION, \ + (id), \ + (ver), \ + &GFBootstrapActivate, \ + &GFExecuteModule, \ + &GFDeactivateModule, \ + &GFBootstrapUnregister, \ + }; \ + return &kApi; \ + } diff --git a/src/m_email/EMailModule.cpp b/src/m_email/EMailModule.cpp index 146ad19..8048165 100644 --- a/src/m_email/EMailModule.cpp +++ b/src/m_email/EMailModule.cpp @@ -73,6 +73,7 @@ #include <vmime/contentTypeField.hpp> #include "GFModuleCommonUtils.hpp" +#include "GFModuleBootstrap.h" #include "GFModuleDefine.h" // @@ -80,7 +81,7 @@ #include "EMailHelper.h" #include "EMailVerificationPayload.h" -GF_MODULE_API_DEFINE_V2("com.bktus.gpgfrontend.module.email", "Email", "2.0.0", +GF_MODULE_BOOTSTRAP_V2("com.bktus.gpgfrontend.module.email", "Email", "2.0.0", "Everything related to E-Mails.", "Saturneric") DEFINE_TRANSLATIONS_STRUCTURE(ModuleEMail); diff --git a/src/m_gpg_info/GnuPGInfoGatheringModule.cpp b/src/m_gpg_info/GnuPGInfoGatheringModule.cpp index 56f3c80..8d83b04 100644 --- a/src/m_gpg_info/GnuPGInfoGatheringModule.cpp +++ b/src/m_gpg_info/GnuPGInfoGatheringModule.cpp @@ -49,13 +49,16 @@ #include <optional> #include "GFModuleCommonUtils.hpp" +#include "GFModuleBootstrap.h" #include "GFModuleDefine.h" #include "GnupgTab.h" #include "GpgInfo.h" -GF_MODULE_API_DEFINE_V2("com.bktus.gpgfrontend.module.gnupg_info_gathering", - "GatherGnupgInfo", "1.2.2", - "Try gathering gnupg information.", "Saturneric") +// Ported to the single bootstrap symbol: this module now describes itself +// through one versioned table instead of ten separately-resolved symbols. +GF_MODULE_BOOTSTRAP_V2("com.bktus.gpgfrontend.module.gnupg_info_gathering", + "GatherGnupgInfo", "1.2.2", + "Try gathering gnupg information.", "Saturneric") DEFINE_TRANSLATIONS_STRUCTURE(ModuleGnuPGInfoGathering); diff --git a/src/m_key_server_sync/KeyServerSyncModule.cpp b/src/m_key_server_sync/KeyServerSyncModule.cpp index 6843259..1008c9a 100644 --- a/src/m_key_server_sync/KeyServerSyncModule.cpp +++ b/src/m_key_server_sync/KeyServerSyncModule.cpp @@ -34,6 +34,7 @@ #include <QtNetwork> #include <QtWidgets> +#include "GFModuleBootstrap.h" #include "GFModuleDefine.h" #include "GFSDKUI.h" #include "KeyServerList.h" @@ -140,7 +141,7 @@ auto ConfirmHkpPublish(QWidget* parent, const QString& url) -> bool { } } // namespace -GF_MODULE_API_DEFINE_V2("com.bktus.gpgfrontend.module.key_server_sync", +GF_MODULE_BOOTSTRAP_V2("com.bktus.gpgfrontend.module.key_server_sync", "KeyServerSync", "1.3.2", "Sync Information From Trusted Key Server.", "Saturneric") diff --git a/src/m_ver_check/VersionCheckingModule.cpp b/src/m_ver_check/VersionCheckingModule.cpp index c7c7b1d..c788c6e 100644 --- a/src/m_ver_check/VersionCheckingModule.cpp +++ b/src/m_ver_check/VersionCheckingModule.cpp @@ -39,13 +39,14 @@ #include "BKTUSVersionCheckTask.h" #include "GFModuleCommonUtils.hpp" +#include "GFModuleBootstrap.h" #include "GFModuleDefine.h" #include "GitHubVersionCheckTask.h" #include "SoftwareVersion.h" #include "UpdateTab.h" #include "Utils.h" -GF_MODULE_API_DEFINE_V2("com.bktus.gpgfrontend.module.version_checking", +GF_MODULE_BOOTSTRAP_V2("com.bktus.gpgfrontend.module.version_checking", "VersionChecking", "1.5.0", "Try checking GpgFrontend version.", "Saturneric"); |
