KiCad PCB EDA Suite
Loading...
Searching...
No Matches
api_plugin_manager.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2024 Jon Evans <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <fstream>
22
23#include <env_vars.h>
24#include <fmt/format.h>
25#include <wx/dir.h>
26#include <wx/log.h>
27#include <wx/utils.h>
28
30#include <api/api_server.h>
31#include <api/api_utils.h>
32#include <gestfich.h>
33#include <paths.h>
34#include <pgm_base.h>
35#include <python_manager.h>
38
39
40wxDEFINE_EVENT( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxCommandEvent );
42
43
44API_PLUGIN_MANAGER::API_PLUGIN_MANAGER( wxEvtHandler* aEvtHandler ) :
45 wxEvtHandler(),
46 m_parent( aEvtHandler )
47{
48 // Read and store pcm schema
49 wxFileName schemaFile( PATHS::GetStockDataPath( true ), wxS( "api.v1.schema.json" ) );
50 schemaFile.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
51 schemaFile.AppendDir( wxS( "schemas" ) );
52
53 m_schema_validator = std::make_unique<JSON_SCHEMA_VALIDATOR>( schemaFile );
54
55 Bind( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, &API_PLUGIN_MANAGER::processNextJob, this );
56}
57
58
59class PLUGIN_TRAVERSER : public wxDirTraverser
60{
61private:
62 std::function<void( const wxFileName& )> m_action;
63
64public:
65 explicit PLUGIN_TRAVERSER( std::function<void( const wxFileName& )> aAction )
66 : m_action( std::move( aAction ) )
67 {
68 }
69
70 wxDirTraverseResult OnFile( const wxString& aFilePath ) override
71 {
72 wxFileName file( aFilePath );
73
74 if( file.GetFullName() == wxS( "plugin.json" ) )
75 m_action( file );
76
77 return wxDIR_CONTINUE;
78 }
79
80 wxDirTraverseResult OnDir( const wxString& dirPath ) override
81 {
82 return wxDIR_CONTINUE;
83 }
84};
85
86
88{
89 m_plugins.clear();
90 m_pluginsCache.clear();
91 m_actionsCache.clear();
92 m_environmentCache.clear();
93 m_buttonBindings.clear();
94 m_menuBindings.clear();
95 m_readyPlugins.clear();
96
97 PLUGIN_TRAVERSER loader(
98 [&]( const wxFileName& aFile )
99 {
100 wxLogTrace( traceApi, wxString::Format( "Manager: loading plugin from %s",
101 aFile.GetFullPath() ) );
102
103 auto plugin = std::make_unique<API_PLUGIN>( aFile, *m_schema_validator );
104
105 if( plugin->IsOk() )
106 {
107 if( m_pluginsCache.count( plugin->Identifier() ) )
108 {
109 wxLogTrace( traceApi,
110 wxString::Format( "Manager: identifier %s already present!",
111 plugin->Identifier() ) );
112 return;
113 }
114 else
115 {
116 m_pluginsCache[plugin->Identifier()] = plugin.get();
117 }
118
119 for( const PLUGIN_ACTION& action : plugin->Actions() )
120 m_actionsCache[action.identifier] = &action;
121
122 m_plugins.insert( std::move( plugin ) );
123 }
124 else
125 {
126 wxLogTrace( traceApi, "Manager: loading failed" );
127 }
128 } );
129
130 wxDir systemPluginsDir( PATHS::GetStockPluginsPath() );
131
132 if( systemPluginsDir.IsOpened() )
133 {
134 wxLogTrace( traceApi, wxString::Format( "Manager: scanning system path (%s) for plugins...",
135 systemPluginsDir.GetName() ) );
136 systemPluginsDir.Traverse( loader );
137 }
138
139 wxString thirdPartyPath;
140 const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables();
141
142 if( std::optional<wxString> v = ENV_VAR::GetVersionedEnvVarValue( env, wxT( "3RD_PARTY" ) ) )
143 thirdPartyPath = *v;
144 else
145 thirdPartyPath = PATHS::GetDefault3rdPartyPath();
146
147 wxDir thirdParty( thirdPartyPath );
148
149 if( thirdParty.IsOpened() )
150 {
151 wxLogTrace( traceApi, wxString::Format( "Manager: scanning PCM path (%s) for plugins...",
152 thirdParty.GetName() ) );
153 thirdParty.Traverse( loader );
154 }
155
156 wxDir userPluginsDir( PATHS::GetUserPluginsPath() );
157
158 if( userPluginsDir.IsOpened() )
159 {
160 wxLogTrace( traceApi, wxString::Format( "Manager: scanning user path (%s) for plugins...",
161 userPluginsDir.GetName() ) );
162 userPluginsDir.Traverse( loader );
163 }
164
166
167 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED, wxID_ANY );
168 m_parent->QueueEvent( evt );
169}
170
171
172void API_PLUGIN_MANAGER::RecreatePluginEnvironment( const wxString& aIdentifier )
173{
174 if( !m_pluginsCache.contains( aIdentifier ) )
175 return;
176
177 const API_PLUGIN* plugin = m_pluginsCache.at( aIdentifier );
178 wxCHECK( plugin, /* void */ );
179
180 std::optional<wxString> env = PYTHON_MANAGER::GetPythonEnvironment( plugin->Identifier() );
181 wxCHECK( env.has_value(), /* void */ );
182
183 wxFileName envConfigPath( *env, wxS( "pyvenv.cfg" ) );
184 envConfigPath.MakeAbsolute();
185
186 if( envConfigPath.DirExists() && envConfigPath.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
187 {
188 wxLogTrace( traceApi,
189 wxString::Format( "Manager: Removed existing Python environment at %s for %s",
190 envConfigPath.GetPath(), plugin->Identifier() ) );
191
192 JOB job;
194 job.identifier = plugin->Identifier();
195 job.plugin_path = plugin->BasePath();
196 job.env_path = envConfigPath.GetPath();
197 m_jobs.emplace_back( job );
198
199 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
200 QueueEvent( evt );
201 }
202}
203
204
205std::optional<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetAction( const wxString& aIdentifier )
206{
207 if( !m_actionsCache.contains( aIdentifier ) )
208 return std::nullopt;
209
210 return m_actionsCache.at( aIdentifier );
211}
212
213
214void API_PLUGIN_MANAGER::InvokeAction( const wxString& aIdentifier )
215{
216 if( !m_actionsCache.contains( aIdentifier ) )
217 return;
218
219 const PLUGIN_ACTION* action = m_actionsCache.at( aIdentifier );
220 const API_PLUGIN& plugin = action->plugin;
221
222 if( !m_readyPlugins.count( plugin.Identifier() ) )
223 {
224 wxLogTrace( traceApi, wxString::Format( "Manager: Plugin %s is not ready",
225 plugin.Identifier() ) );
226 return;
227 }
228
229 wxFileName pluginFile( plugin.BasePath(), action->entrypoint );
230 pluginFile.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_SHORTCUT | wxPATH_NORM_DOTS
231 | wxPATH_NORM_TILDE, plugin.BasePath() );
232 wxString pluginPath = pluginFile.GetFullPath();
233
234 std::vector<const wchar_t*> args;
235 std::optional<wxString> py;
236
237 switch( plugin.Runtime().type )
238 {
239 case PLUGIN_RUNTIME_TYPE::PYTHON:
240 {
241 py = PYTHON_MANAGER::GetVirtualPython( plugin.Identifier() );
242
243 if( !py )
244 {
245 wxLogTrace( traceApi, wxString::Format( "Manager: Python interpreter for %s not found",
246 plugin.Identifier() ) );
247 return;
248 }
249
250 if( !pluginFile.IsFileReadable() )
251 {
252 wxLogTrace( traceApi, wxString::Format( "Manager: Python entrypoint %s is not readable",
253 pluginFile.GetFullPath() ) );
254 return;
255 }
256
257 std::optional<wxString> pythonHome =
258 PYTHON_MANAGER::GetPythonEnvironment( plugin.Identifier() );
259
260 PYTHON_MANAGER manager( *py );
261 wxExecuteEnv env;
262 wxGetEnvMap( &env.env );
263 env.env[wxS( "KICAD_API_SOCKET" )] = Pgm().GetApiServer().SocketPath();
264 env.env[wxS( "KICAD_API_TOKEN" )] = Pgm().GetApiServer().Token();
265 env.cwd = pluginFile.GetPath();
266
267#ifdef _WIN32
268 wxString systemRoot;
269 wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot );
270 env.env[wxS( "SYSTEMROOT" )] = systemRoot;
271
272 if( Pgm().GetCommonSettings()->m_Api.python_interpreter == FindKicadFile( "pythonw.exe" )
273 || wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
274 {
275 wxLogTrace( traceApi, "Configured Python is the KiCad one; erasing path overrides..." );
276 env.env.erase( "PYTHONHOME" );
277 env.env.erase( "PYTHONPATH" );
278 }
279#endif
280
281 if( pythonHome )
282 env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome;
283
284 manager.Execute( pluginFile.GetFullPath(),
285 []( int aRetVal, const wxString& aOutput, const wxString& aError )
286 {
287 wxLogTrace( traceApi,
288 wxString::Format( "Manager: action exited with code %d", aRetVal ) );
289
290 if( !aError.IsEmpty() )
291 wxLogTrace( traceApi, wxString::Format( "Manager: action stderr: %s", aError ) );
292 },
293 &env, true );
294
295 break;
296 }
297
298 case PLUGIN_RUNTIME_TYPE::EXEC:
299 {
300 if( !pluginFile.IsFileExecutable() )
301 {
302 wxLogTrace( traceApi, wxString::Format( "Manager: Exec entrypoint %s is not executable",
303 pluginFile.GetFullPath() ) );
304 return;
305 }
306
307 args.emplace_back( pluginPath.wc_str() );
308
309 for( const wxString& arg : action->args )
310 args.emplace_back( arg.wc_str() );
311
312 args.emplace_back( nullptr );
313
314 wxExecuteEnv env;
315 wxGetEnvMap( &env.env );
316 env.env[wxS( "KICAD_API_SOCKET" )] = Pgm().GetApiServer().SocketPath();
317 env.env[wxS( "KICAD_API_TOKEN" )] = Pgm().GetApiServer().Token();
318 env.cwd = pluginFile.GetPath();
319
320 long p = wxExecute( const_cast<wchar_t**>( args.data() ),
321 wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE, nullptr, &env );
322
323 if( !p )
324 {
325 wxLogTrace( traceApi, wxString::Format( "Manager: launching action %s failed",
326 action->identifier ) );
327 }
328 else
329 {
330 wxLogTrace( traceApi, wxString::Format( "Manager: launching action %s -> pid %ld",
331 action->identifier, p ) );
332 }
333 break;
334 }
335
336 default:
337 wxLogTrace( traceApi, wxString::Format( "Manager: unhandled runtime for action %s",
338 action->identifier ) );
339 return;
340 }
341}
342
343
344std::vector<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetActionsForScope( PLUGIN_ACTION_SCOPE aScope )
345{
346 std::vector<const PLUGIN_ACTION*> actions;
347
348 for( auto& [identifier, action] : m_actionsCache )
349 {
350 if( !m_readyPlugins.count( action->plugin.Identifier() ) )
351 continue;
352
353 if( action->scopes.count( aScope ) )
354 actions.emplace_back( action );
355 }
356
357 return actions;
358}
359
360
362{
363 bool addedAnyJobs = false;
364
365 for( const std::unique_ptr<API_PLUGIN>& plugin : m_plugins )
366 {
367 if( m_busyPlugins.contains( plugin->Identifier() ) )
368 continue;
369
370 wxLogTrace( traceApi, wxString::Format( "Manager: processing dependencies for %s",
371 plugin->Identifier() ) );
372 m_environmentCache[plugin->Identifier()] = wxEmptyString;
373
374 if( plugin->Runtime().type != PLUGIN_RUNTIME_TYPE::PYTHON )
375 {
376 wxLogTrace( traceApi, wxString::Format( "Manager: %s is not a Python plugin, all set",
377 plugin->Identifier() ) );
378 m_readyPlugins.insert( plugin->Identifier() );
379 continue;
380 }
381
382 std::optional<wxString> env = PYTHON_MANAGER::GetPythonEnvironment( plugin->Identifier() );
383
384 if( !env )
385 {
386 wxLogTrace( traceApi, wxString::Format( "Manager: could not create env for %s",
387 plugin->Identifier() ) );
388 continue;
389 }
390
391 m_busyPlugins.insert( plugin->Identifier() );
392
393 wxFileName envConfigPath( *env, wxS( "pyvenv.cfg" ) );
394 envConfigPath.MakeAbsolute();
395
396 if( envConfigPath.IsFileReadable() )
397 {
398 wxLogTrace( traceApi, wxString::Format( "Manager: Python env for %s exists at %s",
399 plugin->Identifier(),
400 envConfigPath.GetPath() ) );
401 JOB job;
403 job.identifier = plugin->Identifier();
404 job.plugin_path = plugin->BasePath();
405 job.env_path = envConfigPath.GetPath();
406 m_jobs.emplace_back( job );
407 addedAnyJobs = true;
408 continue;
409 }
410
411 wxLogTrace( traceApi, wxString::Format( "Manager: will create Python env for %s at %s",
412 plugin->Identifier(), envConfigPath.GetPath() ) );
413 JOB job;
415 job.identifier = plugin->Identifier();
416 job.plugin_path = plugin->BasePath();
417 job.env_path = envConfigPath.GetPath();
418 m_jobs.emplace_back( job );
419 addedAnyJobs = true;
420 }
421
422 if( addedAnyJobs )
423 {
424 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
425 QueueEvent( evt );
426 }
427}
428
429
430void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent )
431{
432 if( m_jobs.empty() )
433 {
434 wxLogTrace( traceApi, "Manager: no more jobs to process" );
435 return;
436 }
437
438 wxLogTrace( traceApi, wxString::Format( "Manager: begin processing; %zu jobs left in queue",
439 m_jobs.size() ) );
440
441 JOB& job = m_jobs.front();
442
443 if( job.type == JOB_TYPE::CREATE_ENV )
444 {
445 wxLogTrace( traceApi, "Manager: Using Python interpreter at %s",
446 Pgm().GetCommonSettings()->m_Api.python_interpreter );
447 wxLogTrace( traceApi, wxString::Format( "Manager: creating Python env at %s",
448 job.env_path ) );
449 PYTHON_MANAGER manager( Pgm().GetCommonSettings()->m_Api.python_interpreter );
450 wxExecuteEnv env;
451
452#ifdef _WIN32
453 wxString systemRoot;
454 wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot );
455 env.env[wxS( "SYSTEMROOT" )] = systemRoot;
456
457 if( Pgm().GetCommonSettings()->m_Api.python_interpreter == FindKicadFile( "pythonw.exe" )
458 || wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
459 {
460 wxLogTrace( traceApi, "Configured Python is the KiCad one; erasing path overrides..." );
461 env.env.erase( "PYTHONHOME" );
462 env.env.erase( "PYTHONPATH" );
463 }
464#endif
465
466 manager.Execute(
467 wxString::Format( wxS( "-m venv --system-site-packages \"%s\"" ),
468 job.env_path ),
469 [this]( int aRetVal, const wxString& aOutput, const wxString& aError )
470 {
471 wxLogTrace( traceApi,
472 wxString::Format( "Manager: created venv (python returned %d)", aRetVal ) );
473
474 if( !aError.IsEmpty() )
475 wxLogTrace( traceApi, wxString::Format( "Manager: venv err: %s", aError ) );
476
477 wxCommandEvent* evt =
478 new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
479 QueueEvent( evt );
480 }, &env );
481
482 JOB nextJob( job );
483 nextJob.type = JOB_TYPE::SETUP_ENV;
484 m_jobs.emplace_back( nextJob );
485 }
486 else if( job.type == JOB_TYPE::SETUP_ENV )
487 {
488 wxLogTrace( traceApi, wxString::Format( "Manager: setting up environment for %s",
489 job.plugin_path ) );
490
491 std::optional<wxString> pythonHome = PYTHON_MANAGER::GetPythonEnvironment( job.identifier );
492 std::optional<wxString> python = PYTHON_MANAGER::GetVirtualPython( job.identifier );
493
494 if( !python )
495 {
496 wxLogTrace( traceApi, wxString::Format( "Manager: error: python not found at %s",
497 job.env_path ) );
498 }
499 else
500 {
501 PYTHON_MANAGER manager( *python );
502 wxExecuteEnv env;
503
504 if( pythonHome )
505 env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome;
506
507#ifdef _WIN32
508 wxString systemRoot;
509 wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot );
510 env.env[wxS( "SYSTEMROOT" )] = systemRoot;
511
512 if( Pgm().GetCommonSettings()->m_Api.python_interpreter
513 == FindKicadFile( "pythonw.exe" )
514 || wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
515 {
516 wxLogTrace( traceApi,
517 "Configured Python is the KiCad one; erasing path overrides..." );
518 env.env.erase( "PYTHONHOME" );
519 env.env.erase( "PYTHONPATH" );
520 }
521#endif
522
523 wxString cmd = wxS( "-m pip install --upgrade pip" );
524 wxLogTrace( traceApi, "Manager: calling python %s", cmd );
525
526 manager.Execute( cmd,
527 [this]( int aRetVal, const wxString& aOutput, const wxString& aError )
528 {
529 wxLogTrace( traceApi, wxString::Format( "Manager: upgrade pip returned %d",
530 aRetVal ) );
531
532 if( !aError.IsEmpty() )
533 {
534 wxLogTrace( traceApi,
535 wxString::Format( "Manager: upgrade pip stderr: %s", aError ) );
536 }
537
538 wxCommandEvent* evt =
539 new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
540 QueueEvent( evt );
541 }, &env );
542
543 JOB nextJob( job );
545 m_jobs.emplace_back( nextJob );
546 }
547 }
548 else if( job.type == JOB_TYPE::INSTALL_REQUIREMENTS )
549 {
550 wxLogTrace( traceApi, wxString::Format( "Manager: installing dependencies for %s",
551 job.plugin_path ) );
552
553 std::optional<wxString> pythonHome = PYTHON_MANAGER::GetPythonEnvironment( job.identifier );
554 std::optional<wxString> python = PYTHON_MANAGER::GetVirtualPython( job.identifier );
555 wxFileName reqs = wxFileName( job.plugin_path, "requirements.txt" );
556
557 if( !python )
558 {
559 wxLogTrace( traceApi, wxString::Format( "Manager: error: python not found at %s",
560 job.env_path ) );
561 }
562 else if( !reqs.IsFileReadable() )
563 {
564 wxLogTrace( traceApi,
565 wxString::Format( "Manager: error: requirements.txt not found at %s",
566 job.plugin_path ) );
567 }
568 else
569 {
570 wxLogTrace( traceApi, "Manager: Python exe '%s'", *python );
571
572 PYTHON_MANAGER manager( *python );
573 wxExecuteEnv env;
574
575#ifdef _WIN32
576 wxString systemRoot;
577 wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot );
578 env.env[wxS( "SYSTEMROOT" )] = systemRoot;
579
580 // If we are using the KiCad-shipped Python interpreter we have to do hacks
581 env.env.erase( "PYTHONHOME" );
582 env.env.erase( "PYTHONPATH" );
583#endif
584
585 if( pythonHome )
586 env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome;
587
588 wxString cmd = wxString::Format(
589 wxS( "-m pip install --no-input --isolated --only-binary :all: --require-virtualenv "
590 "--exists-action i -r \"%s\"" ),
591 reqs.GetFullPath() );
592
593 wxLogTrace( traceApi, "Manager: calling python %s", cmd );
594
595 manager.Execute( cmd,
596 [this, job]( int aRetVal, const wxString& aOutput, const wxString& aError )
597 {
598 if( !aError.IsEmpty() )
599 wxLogTrace( traceApi, wxString::Format( "Manager: pip stderr: %s", aError ) );
600
601 if( aRetVal == 0 )
602 {
603 wxLogTrace( traceApi, wxString::Format( "Manager: marking %s as ready",
604 job.identifier ) );
605 m_readyPlugins.insert( job.identifier );
606 m_busyPlugins.erase( job.identifier );
607 wxCommandEvent* availabilityEvt =
608 new wxCommandEvent( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED, wxID_ANY );
609 wxTheApp->QueueEvent( availabilityEvt );
610 }
611
612 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED,
613 wxID_ANY );
614
615 QueueEvent( evt );
616 }, &env );
617 }
618
619 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
620 QueueEvent( evt );
621 }
622
623 m_jobs.pop_front();
624 wxLogTrace( traceApi, wxString::Format( "Manager: finished job; %zu left in queue",
625 m_jobs.size() ) );
626}
PLUGIN_ACTION_SCOPE
Definition: api_plugin.h:56
wxDEFINE_EVENT(EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxCommandEvent)
const KICOMMON_API wxEventTypeTag< wxCommandEvent > EDA_EVT_PLUGIN_AVAILABILITY_CHANGED
Notifies other parts of KiCad when plugin availability changes.
std::set< std::unique_ptr< API_PLUGIN >, CompareApiPluginIdentifiers > m_plugins
std::map< wxString, wxString > m_environmentCache
Map of plugin identifier to a path for the plugin's virtual environment, if it has one.
std::unique_ptr< JSON_SCHEMA_VALIDATOR > m_schema_validator
std::deque< JOB > m_jobs
std::vector< const PLUGIN_ACTION * > GetActionsForScope(PLUGIN_ACTION_SCOPE aScope)
std::map< int, wxString > m_menuBindings
Map of menu wx item id to action identifier.
std::map< int, wxString > m_buttonBindings
Map of button wx item id to action identifier.
void RecreatePluginEnvironment(const wxString &aIdentifier)
std::map< wxString, const API_PLUGIN * > m_pluginsCache
std::optional< const PLUGIN_ACTION * > GetAction(const wxString &aIdentifier)
void processNextJob(wxCommandEvent &aEvent)
std::set< wxString > m_readyPlugins
std::map< wxString, const PLUGIN_ACTION * > m_actionsCache
void InvokeAction(const wxString &aIdentifier)
API_PLUGIN_MANAGER(wxEvtHandler *aParent)
std::set< wxString > m_busyPlugins
wxEvtHandler * m_parent
A plugin that is invoked by KiCad and runs as an external process; communicating with KiCad via the I...
Definition: api_plugin.h:106
const PLUGIN_RUNTIME & Runtime() const
Definition: api_plugin.cpp:226
const wxString & Identifier() const
Definition: api_plugin.cpp:208
wxString BasePath() const
Definition: api_plugin.cpp:238
static wxString GetUserPluginsPath()
Gets the user path for plugins.
Definition: paths.cpp:55
static wxString GetStockPluginsPath()
Gets the stock (install) plugins path.
Definition: paths.cpp:332
static wxString GetDefault3rdPartyPath()
Gets the default path for PCM packages.
Definition: paths.cpp:132
static wxString GetStockDataPath(bool aRespectRunFromBuildDir=true)
Gets the stock (install) data path, which is the base path for things like scripting,...
Definition: paths.cpp:196
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:935
std::function< void(const wxFileName &)> m_action
PLUGIN_TRAVERSER(std::function< void(const wxFileName &)> aAction)
wxDirTraverseResult OnDir(const wxString &dirPath) override
wxDirTraverseResult OnFile(const wxString &aFilePath) override
Functions related to environment variables, including help functions.
wxString FindKicadFile(const wxString &shortname)
Search the executable file shortname in KiCad binary path and return full file name if found or short...
Definition: gestfich.cpp:58
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition: api_utils.cpp:26
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
KICOMMON_API std::optional< wxString > GetVersionedEnvVarValue(const std::map< wxString, ENV_VAR_ITEM > &aMap, const wxString &aBaseName)
Attempt to retrieve the value of a versioned environment variable, such as KICAD8_TEMPLATE_DIR.
Definition: env_vars.cpp:83
STL namespace.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
An action performed by a plugin via the IPC API (not to be confused with ACTION_PLUGIN,...
Definition: api_plugin.h:82
const API_PLUGIN & plugin
Definition: api_plugin.h:97
wxString identifier
Definition: api_plugin.h:87
wxString entrypoint
Definition: api_plugin.h:91
std::vector< wxString > args
Definition: api_plugin.h:93
PLUGIN_RUNTIME_TYPE type
Definition: api_plugin.h:70
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition: wx_filename.h:39