Cogs.Core
ExtensionRegistry.cpp
1#include "ExtensionRegistry.h"
2#include "Context.h"
3
4#include "Foundation/Logging/Logger.h"
5#include "Foundation/Platform/Module.h"
6
7#include <algorithm>
8#include <cassert>
9
10namespace
11{
12 class ExtensionModule : public Cogs::Module {
13 public:
14 typedef Cogs::Core::Extension* (*GETEXTFN)();
15
16 GETEXTFN getExtension = nullptr;
17
18 virtual void initPtrs() override {
19 getProcAddress(getExtension, "getExtension");
20 }
21 };
22
23 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ExtensionRegistry");
24
25 std::vector<Cogs::Module> extensionHandles;
26 constexpr Cogs::StringView internalVersion(COGS_CORE_VERSION_STRING);
27}
28
29std::vector<Cogs::Core::Context *> Cogs::Core::ExtensionRegistry::contexts;
30
31bool Cogs::Core::ExtensionRegistry::initialized = false;
32bool Cogs::Core::ExtensionRegistry::locked = false;
33
35{
36 LOG_INFO(logger, "ExtensionRegistry::add Extension: %s", extension->getExtensionKey());
37
38 if (version.empty()) {
39 LOG_WARNING(logger, "Extension %s has no versioning info. Update registration method.", extension->getExtensionKey());
40 }
41 else if (version != internalVersion) {
42 LOG_WARNING(logger, "Extension %s built against a different version (%s) than the current (%s).",
43 extension->getExtensionKey(),
44 version.data(),
45 internalVersion.data());
46 }
47
48 if (locked) {
49 getDelayedExtensions().push_back(extension);
50 }
51 else {
52 getExtensions().push_back(extension);
53
54 if (initialized) {
55 extension->initializeStatic();
56
57 for (auto c : contexts) {
58 extension->initialize(c);
59 }
60 }
61 }
62}
63
65{
66 assert(key && "Extension search key cannot be nullptr.");
67
68 for (auto extension : getExtensions()) {
69 if (key == extension->getExtensionKey()) {
70 return true;
71 }
72 }
73
74 if (!silent) {
75 LOG_WARNING(logger, "No extension named %.*s found.", StringViewFormat(key));
76 }
77
78 return false;
79}
80
81void* Cogs::Core::ExtensionRegistry::getExtensionSymbol(const char* extension, const char* name)
82{
83 for (auto e : getExtensions()) {
84 if (strcmp(extension, e->getExtensionKey()) == 0) {
85 return e->getSymbolPointer(name);
86 }
87 }
88 return nullptr;
89}
90
92{
93 for (Cogs::Core::Extension* extension : getExtensions()) {
94 const char* key = extension->getExtensionKey();
95
96 if(key) {
97 LOG_INFO(logger, "Initialized static extension: %s.", key);
98 }
99
100 extension->initializeStatic();
101 }
102
103 initialized = true;
104}
105
107{
108 for (auto extension : getExtensions()) {
109 extension->initialize(context);
110 }
111
112 contexts.push_back(context);
113}
114
116{
117 for (auto & e : getExtensions()) {
118 e->cleanup(context);
119 }
120}
121
123{
124 contexts.erase(std::remove(std::begin(contexts), std::end(contexts), context), std::end(contexts));
125}
126
127const void * Cogs::Core::ExtensionRegistry::loadExtensionModule(const std::string & path, void ** modulehandle, ExtensionModuleLoadResult * result)
128{
129 const auto count = getExtensions().size();
130
131 // Locked merely prevents the extension from being initialised
132 // when it registers itself through a call to add (above).
133 locked = true;
134 auto module = Module::load<ExtensionModule>(path);
135 locked = false;
136
137 if (module.status == Module::LoadStatus::Failed) {
138 LOG_ERROR(logger, "Failed loading extension module %s.", path.c_str());
139
140 if (result) {
141 *result = ExtensionModuleLoadResult::Failure;
142 }
143 return nullptr;
144 }
145
146 // Although delayedExtensions is a deque, it will only ever
147 // contain a single item: The extension being loaded by this
148 // function. Therefore if any of the following initialisation
149 // calls fails, it is reasonably safe to return immediately.
150 std::deque<Extension*>& delayedExtensions = getDelayedExtensions();
151
152 while (!delayedExtensions.empty()) {
153 Extension* e = delayedExtensions.front();
154
155 delayedExtensions.pop_front();
156
157 if (!e->initializeStatic()) {
158 LOG_ERROR(logger, "Extension %s failed static initialisation.", path.c_str());
159
160 if (result) {
161 *result = ExtensionModuleLoadResult::Failure;
162 }
163 Module::unload(module.handle);
164 return nullptr;
165 }
166 for (auto context : contexts) {
167 if (!e->initialize(context)) {
168 LOG_ERROR(logger, "Extension %s failed to initialise.", path.c_str());
169
170 if (result) {
171 *result = ExtensionModuleLoadResult::Failure;
172 }
173 Module::unload(module.handle);
174 return nullptr;
175 }
176 }
177 getExtensions().push_back(e);
178 }
179
180 if (modulehandle) {
181 *modulehandle = module.handle;
182 }
183
184 if (std::find(std::begin(extensionHandles), std::end(extensionHandles), module) != std::end(extensionHandles)) {
185 // Already loaded. don't check for new extensions.
186 }
187 else if (getExtensions().size() <= count) {
188 if (path != "Cogs.Core.Extensions.Terrain") {
189 // Terrain module gets loaded before extension initialization. Avoid annoying Warning log.
190 LOG_WARNING(logger, "No extensions were found in module %s.", path.c_str());
191 }
192
193 if (result) {
194 *result = ExtensionModuleLoadResult::NoExtensionsFound;
195 }
196 return nullptr;
197 }
198 else {
199 LOG_INFO(logger, "Extension %s loaded successfully.", path.c_str());
200 extensionHandles.push_back(module);
201 }
202
203 return module.getExtension ? module.getExtension()->getPublicAPI() : reinterpret_cast<const void*>(-1);
204}
205
206std::vector<Cogs::Core::Extension *> & Cogs::Core::ExtensionRegistry::getExtensions()
207{
208 static std::vector<Extension *> extensions;
209
210 return extensions;
211}
212
213std::deque<Cogs::Core::Extension*>& Cogs::Core::ExtensionRegistry::getDelayedExtensions()
214{
215 static std::deque<Extension*> delayedExtenions;
216
217 return delayedExtenions;
218}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
static void add(Extension *extension, StringView version)
Adds the given extension to the registry, ensuring the initialization methods are called at appropria...
static void cleanup(Context *context)
Cleanup the given context.
static bool hasExtension(const StringView &key, bool silent=false)
Check if an extension with the given key is present in this build.
static void remove(Context *context)
Removes the given context.
static void initialize(Context *context)
Performs initialization of all extensions registered with add().
static void initializeStatic()
Performs static initialization of all extensions registered with add().
static std::vector< Extension * > & getExtensions()
Get the extension registry.
static const void * loadExtensionModule(const std::string &path, void **modulehandle=nullptr, ExtensionModuleLoadResult *result=nullptr)
Load the extension module with the given name.
ExtensionModuleLoadResult
Defines possible results of an extension module load.
static void * getExtensionSymbol(const char *extension, const char *name)
Retrieves the address of a symbol in the extension module.
Log implementation class.
Definition: LogManager.h:139
Base class for managing libraries dynamically loaded at runtime.
Definition: Module.h:23
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
Definition: StringView.h:171
constexpr bool empty() const noexcept
Check if the string is empty.
Definition: StringView.h:122
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Defines an extension to Cogs.Core and provides methods to override in order to initialize extension c...
virtual bool initialize(Context *)
Initialize extension for the given context.
virtual bool initializeStatic()
Initialize extension statically.