aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsaturneric <[email protected]>2026-09-14 23:25:42 +0200
committersaturneric <[email protected]>2026-09-14 23:25:42 +0200
commit13ce0f5eb5dbeafc830eee41ca97c00fbcfe2bed (patch)
tree385938e678bf611b095ac61cf87ccf1ee26faeb4
parentfeat(module): expose SDK ABI version metadata (diff)
downloadmodules-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.h167
-rw-r--r--src/m_email/EMailModule.cpp3
-rw-r--r--src/m_gpg_info/GnuPGInfoGatheringModule.cpp9
-rw-r--r--src/m_key_server_sync/KeyServerSyncModule.cpp3
-rw-r--r--src/m_ver_check/VersionCheckingModule.cpp3
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");