Audacity  3.0.3
ModuleManager.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ModuleManager.cpp
6 
7  Dominic Mazzoni
8  James Crook
9 
10 
11 *******************************************************************//*******************************************************************/
20 
21 
22 #include "ModuleManager.h"
23 #include "ModuleInterface.h"
24 
25 
26 
27 #include <wx/dynlib.h>
28 #include <wx/log.h>
29 #include <wx/string.h>
30 #include <wx/filename.h>
31 
32 #include "FileNames.h"
33 #include "MemoryX.h"
34 
35 #include "PluginInterface.h"
36 
37 #ifdef EXPERIMENTAL_MODULE_PREFS
38 #include "Prefs.h"
39 #include "ModuleSettings.h"
40 #endif
41 
42 #include "widgets/MultiDialog.h"
43 
45 
46 #define initFnName "ExtensionModuleInit"
47 #define versionFnName "GetVersionString"
48 
49 //typedef wxString (*tVersionFn)();
50 typedef wxChar * (*tVersionFn)();
51 
53  : mName{ name }
54 {
55  mLib = std::make_unique<wxDynamicLibrary>();
56  mDispatch = NULL;
57 }
58 
60 {
61 }
62 
63 void Module::ShowLoadFailureError(const wxString &Error)
64 {
65  auto ShortName = wxFileName(mName).GetName();
66  AudacityMessageBox(XO("Unable to load the \"%s\" module.\n\nError: %s").Format(ShortName, Error),
67  XO("Module Unsuitable"));
68  wxLogMessage(wxT("Unable to load the module \"%s\". Error: %s"), mName, Error);
69 }
70 
71 bool Module::Load(wxString &deferredErrorMessage)
72 {
73  deferredErrorMessage.clear();
74  // Will this ever happen???
75  if (mLib->IsLoaded()) {
76  if (mDispatch) {
77  return true;
78  }
79 
80  // Any messages should have already been generated the first time it was loaded.
81  return false;
82  }
83 
84  auto ShortName = wxFileName(mName).GetName();
85 
86  if (!mLib->Load(mName, wxDL_NOW | wxDL_QUIET | wxDL_GLOBAL)) {
87  // For this failure path, only, there is a possiblity of retrial
88  // after some other dependency of this module is loaded. So the
89  // error is not immediately reported.
90  deferredErrorMessage = wxString(wxSysErrorMsg());
91  return false;
92  }
93 
94  // Check version string matches. (For now, they must match exactly)
95  tVersionFn versionFn = (tVersionFn)(mLib->GetSymbol(wxT(versionFnName)));
96  if (versionFn == NULL){
98  XO("The module \"%s\" does not provide a version string.\n\nIt will not be loaded.")
99  .Format( ShortName),
100  XO("Module Unsuitable"));
101  wxLogMessage(wxT("The module \"%s\" does not provide a version string. It will not be loaded."), mName);
102  mLib->Unload();
103  return false;
104  }
105 
106  wxString moduleVersion = versionFn();
107  if( moduleVersion != AUDACITY_VERSION_STRING) {
109  XO("The module \"%s\" is matched with Audacity version \"%s\".\n\nIt will not be loaded.")
110  .Format(ShortName, moduleVersion),
111  XO("Module Unsuitable"));
112  wxLogMessage(wxT("The module \"%s\" is matched with Audacity version \"%s\". It will not be loaded."), mName, moduleVersion);
113  mLib->Unload();
114  return false;
115  }
116 
117  mDispatch = (fnModuleDispatch) mLib->GetSymbol(wxT(ModuleDispatchName));
118  if (!mDispatch) {
119  // Module does not provide a dispatch function.
120  return true;
121  }
122 
123  // However if we do have it and it does not work,
124  // then the module is bad.
125  bool res = ((mDispatch(ModuleInitialize))!=0);
126  if (res) {
127  return true;
128  }
129 
130  mDispatch = NULL;
131 
133  XO("The module \"%s\" failed to initialize.\n\nIt will not be loaded.").Format(ShortName),
134  XO("Module Unsuitable"));
135  wxLogMessage(wxT("The module \"%s\" failed to initialize.\nIt will not be loaded."), mName);
136  mLib->Unload();
137 
138  return false;
139 }
140 
141 // This isn't yet used?
143 {
144  if (mLib->IsLoaded()) {
145  if (mDispatch)
147  }
148 
149  mLib->Unload();
150 }
151 
153 {
154  if (mLib->IsLoaded())
155  if( mDispatch != NULL )
156  return mDispatch(type);
157 
158  return 0;
159 }
160 
161 void * Module::GetSymbol(const wxString &name)
162 {
163  return mLib->GetSymbol(name);
164 }
165 
166 // ============================================================================
167 //
168 // ModuleManager
169 //
170 // ============================================================================
171 
172 // The one and only ModuleManager
173 std::unique_ptr<ModuleManager> ModuleManager::mInstance{};
174 
175 // Provide builtin modules a means to identify themselves
176 using BuiltinModuleList = std::vector<ModuleMain>;
177 namespace {
179  {
180  static BuiltinModuleList theList;
181  return theList;
182  }
183 }
184 
185 void RegisterProvider(ModuleMain moduleMain)
186 {
187  auto &list = builtinModuleList();
188  if ( moduleMain )
189  list.push_back(moduleMain);
190 
191  return;
192 }
193 
195 {
196  auto &list = builtinModuleList();
197  auto end = list.end(), iter = std::find(list.begin(), end, moduleMain);
198  if (iter != end)
199  list.erase(iter);
200 }
201 
202 // ----------------------------------------------------------------------------
203 // Creation/Destruction
204 // ----------------------------------------------------------------------------
205 
207 {
208 }
209 
211 {
212  mDynModules.clear();
213  builtinModuleList().clear();
214 }
215 
216 // static
218 {
219  const auto &audacityPathList = FileNames::AudacityPathList();
220  FilePaths pathList;
221  wxString pathVar;
222 
223  // Code from LoadLadspa that might be useful in load modules.
224  pathVar = wxGetenv(wxT("AUDACITY_MODULES_PATH"));
225  if (!pathVar.empty())
226  FileNames::AddMultiPathsToPathList(pathVar, pathList);
227 
228  for (const auto &path : audacityPathList) {
229  wxString prefix = path + wxFILE_SEP_PATH;
230  FileNames::AddUniquePathToPathList(prefix + wxT("modules"),
231  pathList);
232  if (files.size()) {
233  break;
234  }
235  }
236 
237  #if defined(__WXMSW__)
238  FileNames::FindFilesInPathList(wxT("*.dll"), pathList, files);
239  #else
240  FileNames::FindFilesInPathList(wxT("*.so"), pathList, files);
241  #endif
242 }
243 
245  const FilePaths &files, FilePaths &decided, DelayedErrors &errors)
246 {
247  FilePaths checked;
248  wxString saveOldCWD = ::wxGetCwd();
249  auto cleanup = finally([&]{ ::wxSetWorkingDirectory(saveOldCWD); });
250  for (const auto &file : files) {
251  // As a courtesy to some modules that might be bridges to
252  // open other modules, we set the current working
253  // directory to be the module's directory.
254  auto prefix = ::wxPathOnly(file);
255  ::wxSetWorkingDirectory(prefix);
256 
257  // Only process the first module encountered in the
258  // defined search sequence.
259  wxString ShortName = wxFileName( file ).GetName();
260  if( checked.Index( ShortName, false ) != wxNOT_FOUND )
261  continue;
262  checked.Add( ShortName );
263 
264  // Skip if a previous pass through this function decided it already
265  if( decided.Index( ShortName, false ) != wxNOT_FOUND )
266  continue;
267 
268 #ifdef EXPERIMENTAL_MODULE_PREFS
269  int iModuleStatus = ModuleSettings::GetModuleStatus( file );
270  if( iModuleStatus == kModuleDisabled )
271  continue;
272  if( iModuleStatus == kModuleFailed )
273  continue;
274  // New module? You have to go and explicitly enable it.
275  if( iModuleStatus == kModuleNew ){
276  // To ensure it is noted in config file and so
277  // appears on modules page.
279  continue;
280  }
281 
282  if( iModuleStatus == kModuleAsk )
283 #endif
284  // JKC: I don't like prompting for the plug-ins individually
285  // I think it would be better to show the module prefs page,
286  // and let the user decide for each one.
287  {
288  auto msg = XO("Module \"%s\" found.").Format( ShortName );
289  msg += XO("\n\nOnly use modules from trusted sources");
290  const TranslatableStrings buttons{
291  XO("Yes"), XO("No"),
292  }; // could add a button here for 'yes and remember that', and put it into the cfg file. Needs more thought.
293  int action;
294  action = ShowMultiDialog(msg, XO("Audacity Module Loader"),
295  buttons,
296  "",
297  XO("Try and load this module?"),
298  false);
299 #ifdef EXPERIMENTAL_MODULE_PREFS
300  // If we're not prompting always, accept the answer permanently
301  if( iModuleStatus == kModuleNew ){
302  iModuleStatus = (action==1)?kModuleDisabled : kModuleEnabled;
303  ModuleSettings::SetModuleStatus( file, iModuleStatus );
304  }
305 #endif
306  if(action == 1){ // "No"
307  decided.Add( ShortName );
308  continue;
309  }
310  }
311 #ifdef EXPERIMENTAL_MODULE_PREFS
312  // Before attempting to load, we set the state to bad.
313  // That way, if we crash, we won't try again.
315 #endif
316 
317  wxString Error;
318  auto umodule = std::make_unique<Module>(file);
319  if (umodule->Load(Error)) // it will get rejected if there are version problems
320  {
321  decided.Add( ShortName );
322  auto module = umodule.get();
323 
324  if (!module->HasDispatch())
325  {
326  auto ShortName = wxFileName(file).GetName();
328  XO("The module \"%s\" does not provide any of the required functions.\n\nIt will not be loaded.").Format(ShortName),
329  XO("Module Unsuitable"));
330  wxLogMessage(wxT("The module \"%s\" does not provide any of the required functions. It will not be loaded."), file);
331  module->Unload();
332  }
333  else
334  {
335  Get().mModules.push_back(std::move(umodule));
336 
337 #ifdef EXPERIMENTAL_MODULE_PREFS
338  // Loaded successfully, restore the status.
339  ModuleSettings::SetModuleStatus(file, iModuleStatus);
340 #endif
341  }
342  }
343  else if (!Error.empty()) {
344  // Module is not yet decided in this pass.
345  // Maybe it depends on another which has not yet been loaded.
346  // But don't take the kModuleAsk path again in a later pass.
348  errors.emplace_back( std::move( umodule ), Error );
349  }
350  }
351 }
352 
353 // static
355 {
356  FilePaths files;
357  FindModules(files);
358 
359  FilePaths decided;
360  DelayedErrors errors;
361  size_t numDecided = 0;
362 
363  // Multiple passes give modules multiple chances to load in case they
364  // depend on some other module not yet loaded
365  do {
366  numDecided = decided.size();
367  errors.clear();
368  TryLoadModules(files, decided, errors);
369  }
370  while ( errors.size() && numDecided < decided.size() );
371 
372  // Only now show accumulated errors of modules that failed to load
373  for ( const auto &pair : errors ) {
374  auto &pModule = pair.first;
375  pModule->ShowLoadFailureError(pair.second);
376  ModuleSettings::SetModuleStatus( pModule->GetName(), kModuleFailed );
377  }
378 }
379 
380 // static
382 {
383  for (const auto &module: mModules) {
384  module->Dispatch(type);
385  }
386  return 0;
387 }
388 
389 // ============================================================================
390 //
391 // Return reference to singleton
392 //
393 // (Thread-safe...no active threading during construction or after destruction)
394 // ============================================================================
396 {
397  if (!mInstance)
398  {
400  }
401 
402  return *mInstance;
403 }
404 
406 {
407  return L"Module";
408 }
409 
411 {
412  return wxString::Format(wxT("%s_%s_%s_%s_%s"),
414  wxEmptyString,
415  module->GetVendor().Internal(),
416  module->GetSymbol().Internal(),
417  module->GetPath());
418 }
419 
421 {
423 
424 // The commented out code loads modules whether or not they are enabled.
425 // none of our modules is a 'provider' of effects, so this code commented out.
426 #if 0
427  FilePaths provList;
428  FilePaths pathList;
429 
430  // Code from LoadLadspa that might be useful in load modules.
431  wxString pathVar = wxString::FromUTF8(getenv("AUDACITY_MODULES_PATH"));
432 
433  if (!pathVar.empty())
434  {
435  FileNames::AddMultiPathsToPathList(pathVar, pathList);
436  }
437  else
438  {
440  }
441 
442 #if defined(__WXMSW__)
443  FileNames::FindFilesInPathList(wxT("*.dll"), pathList, provList);
444 #elif defined(__WXMAC__)
445  FileNames::FindFilesInPathList(wxT("*.dylib"), pathList, provList);
446 #else
447  FileNames::FindFilesInPathList(wxT("*.so"), pathList, provList);
448 #endif
449 
450  for ( const auto &path : provList )
451  LoadModule(path);
452 #endif
453 
454  return true;
455 }
456 
458 {
459  for (auto moduleMain : builtinModuleList())
460  {
461  ModuleInterfaceHandle module {
462  moduleMain(), ModuleInterfaceDeleter{}
463  };
464 
465  if (module && module->Initialize())
466  {
467  // Register the provider
468  ModuleInterface *pInterface = module.get();
469  auto id = GetID(pInterface);
470 
471  // Need to remember it
472  mDynModules[id] = std::move(module);
473  }
474  else
475  {
476  // Don't leak! Destructor of module does that.
477  }
478  }
479 }
480 
482 {
483  if (pInterface)
484  {
485  pInterface->Terminate();
486  std::unique_ptr < ModuleInterface > { pInterface }; // DELETE it
487  }
488 }
489 
490 bool ModuleManager::RegisterEffectPlugin(const PluginID & providerID, const PluginPath & path, TranslatableString &errMsg)
491 {
492  errMsg = {};
493  if (mDynModules.find(providerID) == mDynModules.end())
494  {
495  return false;
496  }
497 
498  auto nFound = mDynModules[providerID]->DiscoverPluginsAtPath(path, errMsg, PluginManagerInterface::DefaultRegistrationCallback);
499 
500  return nFound > 0;
501 }
502 
504  const PluginPath & path)
505 {
506  if (path.empty() && mDynModules.find(providerID) != mDynModules.end())
507  {
508  return mDynModules[providerID].get();
509  }
510 
511  return nullptr;
512 }
513 
514 std::unique_ptr<ComponentInterface> ModuleManager::CreateInstance(
515  const PluginID & providerID, const PluginPath & path)
516 {
517  if (auto iter = mDynModules.find(providerID);
518  iter == mDynModules.end())
519  return nullptr;
520  else
521  return iter->second->CreateInstance(path);
522 }
523 
524 bool ModuleManager::IsProviderValid(const PluginID & WXUNUSED(providerID),
525  const PluginPath & path)
526 {
527  // Builtin modules do not have a path
528  if (path.empty())
529  {
530  return true;
531  }
532 
533  wxFileName lib(path);
534  if (lib.FileExists() || lib.DirExists())
535  {
536  return true;
537  }
538 
539  return false;
540 }
541 
542 bool ModuleManager::IsPluginValid(const PluginID & providerID,
543  const PluginPath & path,
544  bool bFast)
545 {
546  if (mDynModules.find(providerID) == mDynModules.end())
547  {
548  return false;
549  }
550 
551  return mDynModules[providerID]->IsPluginValid(path, bFast);
552 }
ModuleManager::TryLoadModules
static void TryLoadModules(const FilePaths &files, FilePaths &decided, DelayedErrors &errors)
Definition: ModuleManager.cpp:244
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
ModuleManager::mDynModules
ModuleMap mDynModules
Definition: ModuleManager.h:133
ModuleManager::~ModuleManager
~ModuleManager()
Definition: ModuleManager.cpp:210
fnModuleDispatch
int(* fnModuleDispatch)(ModuleDispatchTypes type)
Definition: ModuleManager.h:37
Module::mName
const FilePath mName
Definition: ModuleManager.h:54
ModuleSettings::GetModuleStatus
int GetModuleStatus(const FilePath &fname)
Definition: ModuleSettings.cpp:28
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
ModuleManager::mModules
std::vector< std::unique_ptr< Module > > mModules
Definition: ModuleManager.h:137
ComponentInterface::GetSymbol
virtual ComponentInterfaceSymbol GetSymbol()=0
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
Module::GetSymbol
void * GetSymbol(const wxString &name)
Definition: ModuleManager.cpp:161
kModuleEnabled
@ kModuleEnabled
Definition: ModuleSettings.h:18
ModuleManager.h
ModuleManager::GetID
static PluginID GetID(ModuleInterface *module)
Definition: ModuleManager.cpp:410
ModuleSettings.h
PluginPath
wxString PluginPath
type alias for identifying a Plugin supplied by a module, each module defining its own interpretation...
Definition: Identifier.h:214
Module::~Module
virtual ~Module()
Definition: ModuleManager.cpp:59
kModuleFailed
@ kModuleFailed
Definition: ModuleSettings.h:20
Format
Abstract base class used in importing a file.
ModuleManager::InitializeBuiltins
void InitializeBuiltins()
Definition: ModuleManager.cpp:457
Module::mDispatch
fnModuleDispatch mDispatch
Definition: ModuleManager.h:56
Module::ShowLoadFailureError
void ShowLoadFailureError(const wxString &Error)
Definition: ModuleManager.cpp:63
anonymous_namespace{ModuleManager.cpp}::builtinModuleList
BuiltinModuleList & builtinModuleList()
Definition: ModuleManager.cpp:178
ModuleInterface.h
ModuleManager::ModuleManager
ModuleManager()
Definition: ModuleManager.cpp:206
XO
#define XO(s)
Definition: Internat.h:31
ModuleManager::RegisterEffectPlugin
bool RegisterEffectPlugin(const PluginID &provider, const PluginPath &path, TranslatableString &errMsg)
Definition: ModuleManager.cpp:490
ModuleManager::CreateInstance
std::unique_ptr< ComponentInterface > CreateInstance(const PluginID &provider, const PluginPath &path)
Definition: ModuleManager.cpp:514
ModuleManager::IsPluginValid
bool IsPluginValid(const PluginID &provider, const PluginPath &path, bool bFast)
Definition: ModuleManager.cpp:542
RegisterProvider
void RegisterProvider(ModuleMain moduleMain)
Definition: ModuleManager.cpp:185
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
ModuleManager::DiscoverProviders
bool DiscoverProviders()
Definition: ModuleManager.cpp:420
tVersionFn
wxChar *(* tVersionFn)()
Definition: ModuleManager.cpp:50
FileNames::AddUniquePathToPathList
FILES_API void AddUniquePathToPathList(const FilePath &path, FilePaths &pathList)
ComponentInterface::GetVendor
virtual VendorSymbol GetVendor()=0
ModuleInterface
Definition: ModuleInterface.h:70
ModuleManager::GetPluginTypeString
static wxString GetPluginTypeString()
Definition: ModuleManager.cpp:405
ModuleManager::DelayedErrors
std::vector< std::pair< std::unique_ptr< Module >, wxString > > DelayedErrors
Definition: ModuleManager.h:89
ModuleManager::Dispatch
int Dispatch(ModuleDispatchTypes type)
Definition: ModuleManager.cpp:381
ModuleManager::IsProviderValid
bool IsProviderValid(const PluginID &provider, const PluginPath &path)
Definition: ModuleManager.cpp:524
ModuleManager
Definition: ModuleManager.h:71
FilePath
wxString FilePath
Definition: Project.h:20
ComponentInterface::GetPath
virtual PluginPath GetPath()=0
Module::Unload
void Unload()
Definition: ModuleManager.cpp:142
PluginID
wxString PluginID
Definition: EffectManager.h:30
ModuleManager::Get
static ModuleManager & Get()
Definition: ModuleManager.cpp:395
name
const TranslatableString name
Definition: Distortion.cpp:98
BuiltinModuleList
std::vector< ModuleMain > BuiltinModuleList
Definition: ModuleManager.cpp:176
Module::Module
Module(const FilePath &name)
Definition: ModuleManager.cpp:52
kModuleNew
@ kModuleNew
Definition: ModuleSettings.h:21
FileNames::ModulesDir
FILES_API FilePath ModulesDir()
FileNames::FindFilesInPathList
FILES_API void FindFilesInPathList(const wxString &pattern, const FilePaths &pathList, FilePaths &results, int flags=wxDIR_FILES)
ModuleDispatchTypes
ModuleDispatchTypes
Definition: ModuleConstants.h:27
ShowMultiDialog
int ShowMultiDialog(const TranslatableString &message, const TranslatableString &title, const TranslatableStrings &buttons, const wxString &helpPage, const TranslatableString &boxMsg, bool log)
Definition: MultiDialog.cpp:180
FileNames::AddMultiPathsToPathList
FILES_API void AddMultiPathsToPathList(const wxString &multiPathString, FilePaths &pathList)
UnregisterProvider
void UnregisterProvider(ModuleMain moduleMain)
Definition: ModuleManager.cpp:194
id
int id
Definition: WaveTrackControls.cpp:591
Module::mLib
std::unique_ptr< wxDynamicLibrary > mLib
Definition: ModuleManager.h:55
PluginManagerInterface::DefaultRegistrationCallback
static const PluginID & DefaultRegistrationCallback(ModuleInterface *provider, ComponentInterface *ident)
Definition: PluginManager.cpp:329
ModuleTerminate
@ ModuleTerminate
Definition: ModuleConstants.h:29
ModuleMain
ModuleInterface *(*)() ModuleMain
Definition: ModuleManager.h:143
FileNames::AudacityPathList
FILES_API const FilePaths & AudacityPathList()
A list of directories that should be searched for Audacity files (plug-ins, help files,...
ModuleManager::FindModules
static void FindModules(FilePaths &files)
Definition: ModuleManager.cpp:217
ModuleSettings::SetModuleStatus
void SetModuleStatus(const FilePath &fname, int iStatus)
Definition: ModuleSettings.cpp:75
MultiDialog.h
MemoryX.h
ModuleManager::mInstance
static std::unique_ptr< ModuleManager > mInstance
Definition: ModuleManager.h:128
versionFnName
#define versionFnName
Definition: ModuleManager.cpp:47
ModuleManager::Initialize
void Initialize()
Definition: ModuleManager.cpp:354
ModuleInterfaceDeleter::operator()
void operator()(ModuleInterface *pInterface) const
Definition: ModuleManager.cpp:481
FileNames.h
AudacityMessageBox.h
Module::Dispatch
int Dispatch(ModuleDispatchTypes type)
Definition: ModuleManager.cpp:152
ModuleInterfaceHandle
std::unique_ptr< ModuleInterface, ModuleInterfaceDeleter > ModuleInterfaceHandle
Definition: ModuleManager.h:65
ComponentInterfaceSymbol::Internal
const wxString & Internal() const
Definition: ComponentInterfaceSymbol.h:55
kModuleDisabled
@ kModuleDisabled
Definition: ModuleSettings.h:17
PluginInterface.h
Prefs.h
ModuleDispatchName
#define ModuleDispatchName
Definition: ModuleConstants.h:16
ModuleManager::CreateProviderInstance
ModuleInterface * CreateProviderInstance(const PluginID &provider, const PluginPath &path)
Definition: ModuleManager.cpp:503
safenew
#define safenew
Definition: MemoryX.h:10
ModuleInterfaceDeleter
Definition: ModuleManager.h:59
ModuleInterface::Terminate
virtual void Terminate()=0
kModuleAsk
@ kModuleAsk
Definition: ModuleSettings.h:19
Module::Load
bool Load(wxString &deferredErrorMessage)
Definition: ModuleManager.cpp:71
ModuleInitialize
@ ModuleInitialize
Definition: ModuleConstants.h:28