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/timer.h>
28#include <wx/utils.h>
29
31#include <api/api_server.h>
32#include <api/api_utils.h>
33#include <gestfich.h>
34#include <paths.h>
35#include <pgm_base.h>
36#include <python_manager.h>
39
40
41wxDEFINE_EVENT( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxCommandEvent );
43
44
45API_PLUGIN_MANAGER::API_PLUGIN_MANAGER( wxEvtHandler* aEvtHandler ) :
46 wxEvtHandler(),
47 m_parent( aEvtHandler ),
48 m_lastPid( 0 ),
49 m_raiseTimer( nullptr )
50{
51 // Read and store pcm schema
52 wxFileName schemaFile( PATHS::GetStockDataPath( true ), wxS( "api.v1.schema.json" ) );
53 schemaFile.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
54 schemaFile.AppendDir( wxS( "schemas" ) );
55
56 m_schema_validator = std::make_unique<JSON_SCHEMA_VALIDATOR>( schemaFile );
57
58 Bind( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, &API_PLUGIN_MANAGER::processNextJob, this );
59}
60
61
62class PLUGIN_TRAVERSER : public wxDirTraverser
63{
64private:
65 std::function<void( const wxFileName& )> m_action;
66
67public:
68 explicit PLUGIN_TRAVERSER( std::function<void( const wxFileName& )> aAction )
69 : m_action( std::move( aAction ) )
70 {
71 }
72
73 wxDirTraverseResult OnFile( const wxString& aFilePath ) override
74 {
75 wxFileName file( aFilePath );
76
77 if( file.GetFullName() == wxS( "plugin.json" ) )
78 m_action( file );
79
80 return wxDIR_CONTINUE;
81 }
82
83 wxDirTraverseResult OnDir( const wxString& dirPath ) override
84 {
85 return wxDIR_CONTINUE;
86 }
87};
88
89
91{
92 m_plugins.clear();
93 m_pluginsCache.clear();
94 m_actionsCache.clear();
95 m_environmentCache.clear();
96 m_buttonBindings.clear();
97 m_menuBindings.clear();
98 m_readyPlugins.clear();
99
100 PLUGIN_TRAVERSER loader(
101 [&]( const wxFileName& aFile )
102 {
103 wxLogTrace( traceApi, wxString::Format( "Manager: loading plugin from %s",
104 aFile.GetFullPath() ) );
105
106 auto plugin = std::make_unique<API_PLUGIN>( aFile, *m_schema_validator );
107
108 if( plugin->IsOk() )
109 {
110 if( m_pluginsCache.count( plugin->Identifier() ) )
111 {
112 wxLogTrace( traceApi,
113 wxString::Format( "Manager: identifier %s already present!",
114 plugin->Identifier() ) );
115 return;
116 }
117 else
118 {
119 m_pluginsCache[plugin->Identifier()] = plugin.get();
120 }
121
122 for( const PLUGIN_ACTION& action : plugin->Actions() )
123 m_actionsCache[action.identifier] = &action;
124
125 m_plugins.insert( std::move( plugin ) );
126 }
127 else
128 {
129 wxLogTrace( traceApi, "Manager: loading failed" );
130 }
131 } );
132
133 wxDir systemPluginsDir( PATHS::GetStockPluginsPath() );
134
135 if( systemPluginsDir.IsOpened() )
136 {
137 wxLogTrace( traceApi, wxString::Format( "Manager: scanning system path (%s) for plugins...",
138 systemPluginsDir.GetName() ) );
139 systemPluginsDir.Traverse( loader );
140 }
141
142 wxString thirdPartyPath;
143 const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables();
144
145 if( std::optional<wxString> v = ENV_VAR::GetVersionedEnvVarValue( env, wxT( "3RD_PARTY" ) ) )
146 thirdPartyPath = *v;
147 else
148 thirdPartyPath = PATHS::GetDefault3rdPartyPath();
149
150 wxDir thirdParty( thirdPartyPath );
151
152 if( thirdParty.IsOpened() )
153 {
154 wxLogTrace( traceApi, wxString::Format( "Manager: scanning PCM path (%s) for plugins...",
155 thirdParty.GetName() ) );
156 thirdParty.Traverse( loader );
157 }
158
159 wxDir userPluginsDir( PATHS::GetUserPluginsPath() );
160
161 if( userPluginsDir.IsOpened() )
162 {
163 wxLogTrace( traceApi, wxString::Format( "Manager: scanning user path (%s) for plugins...",
164 userPluginsDir.GetName() ) );
165 userPluginsDir.Traverse( loader );
166 }
167
169
170 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED, wxID_ANY );
171 m_parent->QueueEvent( evt );
172}
173
174
175void API_PLUGIN_MANAGER::RecreatePluginEnvironment( const wxString& aIdentifier )
176{
177 if( !m_pluginsCache.contains( aIdentifier ) )
178 return;
179
180 const API_PLUGIN* plugin = m_pluginsCache.at( aIdentifier );
181 wxCHECK( plugin, /* void */ );
182
183 std::optional<wxString> env = PYTHON_MANAGER::GetPythonEnvironment( plugin->Identifier() );
184 wxCHECK( env.has_value(), /* void */ );
185
186 wxFileName envConfigPath( *env, wxS( "pyvenv.cfg" ) );
187 envConfigPath.MakeAbsolute();
188
189 if( envConfigPath.DirExists() && envConfigPath.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
190 {
191 wxLogTrace( traceApi,
192 wxString::Format( "Manager: Removed existing Python environment at %s for %s",
193 envConfigPath.GetPath(), plugin->Identifier() ) );
194
195 JOB job;
197 job.identifier = plugin->Identifier();
198 job.plugin_path = plugin->BasePath();
199 job.env_path = envConfigPath.GetPath();
200 m_jobs.emplace_back( job );
201
202 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
203 QueueEvent( evt );
204 }
205}
206
207
208std::optional<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetAction( const wxString& aIdentifier )
209{
210 if( !m_actionsCache.contains( aIdentifier ) )
211 return std::nullopt;
212
213 return m_actionsCache.at( aIdentifier );
214}
215
216
217void API_PLUGIN_MANAGER::InvokeAction( const wxString& aIdentifier )
218{
219 if( !m_actionsCache.contains( aIdentifier ) )
220 return;
221
222 const PLUGIN_ACTION* action = m_actionsCache.at( aIdentifier );
223 const API_PLUGIN& plugin = action->plugin;
224
225 if( !m_readyPlugins.count( plugin.Identifier() ) )
226 {
227 wxLogTrace( traceApi, wxString::Format( "Manager: Plugin %s is not ready",
228 plugin.Identifier() ) );
229 return;
230 }
231
232 wxFileName pluginFile( plugin.BasePath(), action->entrypoint );
233 pluginFile.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_SHORTCUT | wxPATH_NORM_DOTS
234 | wxPATH_NORM_TILDE, plugin.BasePath() );
235 wxString pluginPath = pluginFile.GetFullPath();
236
237 std::vector<const wchar_t*> args;
238 std::optional<wxString> py;
239
240 switch( plugin.Runtime().type )
241 {
242 case PLUGIN_RUNTIME_TYPE::PYTHON:
243 {
244 py = PYTHON_MANAGER::GetVirtualPython( plugin.Identifier() );
245
246 if( !py )
247 {
248 wxLogTrace( traceApi, wxString::Format( "Manager: Python interpreter for %s not found",
249 plugin.Identifier() ) );
250 return;
251 }
252
253 if( !pluginFile.IsFileReadable() )
254 {
255 wxLogTrace( traceApi, wxString::Format( "Manager: Python entrypoint %s is not readable",
256 pluginFile.GetFullPath() ) );
257 return;
258 }
259
260 std::optional<wxString> pythonHome =
261 PYTHON_MANAGER::GetPythonEnvironment( plugin.Identifier() );
262
263 PYTHON_MANAGER manager( *py );
264 wxExecuteEnv env;
265 wxGetEnvMap( &env.env );
266 env.env[wxS( "KICAD_API_SOCKET" )] = Pgm().GetApiServer().SocketPath();
267 env.env[wxS( "KICAD_API_TOKEN" )] = Pgm().GetApiServer().Token();
268 env.cwd = pluginFile.GetPath();
269
270#ifdef _WIN32
271 wxString systemRoot;
272 wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot );
273 env.env[wxS( "SYSTEMROOT" )] = systemRoot;
274
275 if( Pgm().GetCommonSettings()->m_Api.python_interpreter == FindKicadFile( "pythonw.exe" )
276 || wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
277 {
278 wxLogTrace( traceApi, "Configured Python is the KiCad one; erasing path overrides..." );
279 env.env.erase( "PYTHONHOME" );
280 env.env.erase( "PYTHONPATH" );
281 }
282#endif
283
284 if( pythonHome )
285 env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome;
286
287 [[maybe_unused]] long pid = manager.Execute( { pluginFile.GetFullPath() },
288 []( int aRetVal, const wxString& aOutput, const wxString& aError )
289 {
290 wxLogTrace( traceApi,
291 wxString::Format( "Manager: action exited with code %d", aRetVal ) );
292
293 if( !aError.IsEmpty() )
294 wxLogTrace( traceApi, wxString::Format( "Manager: action stderr: %s", aError ) );
295 },
296 &env, true );
297
298#ifdef __WXMAC__
299 if( pid )
300 {
301 if( !m_raiseTimer )
302 {
303 m_raiseTimer = new wxTimer( this );
304
305 Bind( wxEVT_TIMER,
306 [&]( wxTimerEvent& )
307 {
308 wxString script = wxString::Format(
309 wxS( "tell application \"System Events\"\n"
310 " set plist to every process whose unix id is %ld\n"
311 " repeat with proc in plist\n"
312 " set the frontmost of proc to true\n"
313 " end repeat\n"
314 "end tell" ), m_lastPid );
315
316 wxString cmd = wxString::Format( "osascript -e '%s'", script );
317 wxLogTrace( traceApi, wxString::Format( "Execute: %s", cmd ) );
318 wxExecute( cmd );
319 },
320 m_raiseTimer->GetId() );
321 }
322
323 m_lastPid = pid;
324 m_raiseTimer->StartOnce( 250 );
325 }
326#endif
327
328 break;
329 }
330
331 case PLUGIN_RUNTIME_TYPE::EXEC:
332 {
333 if( !pluginFile.IsFileExecutable() )
334 {
335 wxLogTrace( traceApi, wxString::Format( "Manager: Exec entrypoint %s is not executable",
336 pluginFile.GetFullPath() ) );
337 return;
338 }
339
340 args.emplace_back( pluginPath.wc_str() );
341
342 for( const wxString& arg : action->args )
343 args.emplace_back( arg.wc_str() );
344
345 args.emplace_back( nullptr );
346
347 wxExecuteEnv env;
348 wxGetEnvMap( &env.env );
349 env.env[wxS( "KICAD_API_SOCKET" )] = Pgm().GetApiServer().SocketPath();
350 env.env[wxS( "KICAD_API_TOKEN" )] = Pgm().GetApiServer().Token();
351 env.cwd = pluginFile.GetPath();
352
353 long p = wxExecute( const_cast<wchar_t**>( args.data() ),
354 wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE, nullptr, &env );
355
356 if( !p )
357 {
358 wxLogTrace( traceApi, wxString::Format( "Manager: launching action %s failed",
359 action->identifier ) );
360 }
361 else
362 {
363 wxLogTrace( traceApi, wxString::Format( "Manager: launching action %s -> pid %ld",
364 action->identifier, p ) );
365 }
366 break;
367 }
368
369 default:
370 wxLogTrace( traceApi, wxString::Format( "Manager: unhandled runtime for action %s",
371 action->identifier ) );
372 return;
373 }
374}
375
376
377std::vector<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetActionsForScope( PLUGIN_ACTION_SCOPE aScope )
378{
379 std::vector<const PLUGIN_ACTION*> actions;
380
381 for( auto& [identifier, action] : m_actionsCache )
382 {
383 if( !m_readyPlugins.count( action->plugin.Identifier() ) )
384 continue;
385
386 if( action->scopes.count( aScope ) )
387 actions.emplace_back( action );
388 }
389
390 return actions;
391}
392
393
395{
396 bool addedAnyJobs = false;
397
398 for( const std::unique_ptr<API_PLUGIN>& plugin : m_plugins )
399 {
400 if( m_busyPlugins.contains( plugin->Identifier() ) )
401 continue;
402
403 wxLogTrace( traceApi, wxString::Format( "Manager: processing dependencies for %s",
404 plugin->Identifier() ) );
405 m_environmentCache[plugin->Identifier()] = wxEmptyString;
406
407 if( plugin->Runtime().type != PLUGIN_RUNTIME_TYPE::PYTHON )
408 {
409 wxLogTrace( traceApi, wxString::Format( "Manager: %s is not a Python plugin, all set",
410 plugin->Identifier() ) );
411 m_readyPlugins.insert( plugin->Identifier() );
412 continue;
413 }
414
415 std::optional<wxString> env = PYTHON_MANAGER::GetPythonEnvironment( plugin->Identifier() );
416
417 if( !env )
418 {
419 wxLogTrace( traceApi, wxString::Format( "Manager: could not create env for %s",
420 plugin->Identifier() ) );
421 continue;
422 }
423
424 m_busyPlugins.insert( plugin->Identifier() );
425
426 wxFileName envConfigPath( *env, wxS( "pyvenv.cfg" ) );
427 envConfigPath.MakeAbsolute();
428
429 if( envConfigPath.IsFileReadable() )
430 {
431 wxLogTrace( traceApi, wxString::Format( "Manager: Python env for %s exists at %s",
432 plugin->Identifier(),
433 envConfigPath.GetPath() ) );
434 JOB job;
436 job.identifier = plugin->Identifier();
437 job.plugin_path = plugin->BasePath();
438 job.env_path = envConfigPath.GetPath();
439 m_jobs.emplace_back( job );
440 addedAnyJobs = true;
441 continue;
442 }
443
444 wxLogTrace( traceApi, wxString::Format( "Manager: will create Python env for %s at %s",
445 plugin->Identifier(), envConfigPath.GetPath() ) );
446 JOB job;
448 job.identifier = plugin->Identifier();
449 job.plugin_path = plugin->BasePath();
450 job.env_path = envConfigPath.GetPath();
451 m_jobs.emplace_back( job );
452 addedAnyJobs = true;
453 }
454
455 if( addedAnyJobs )
456 {
457 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
458 QueueEvent( evt );
459 }
460}
461
462
463void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent )
464{
465 if( m_jobs.empty() )
466 {
467 wxLogTrace( traceApi, "Manager: no more jobs to process" );
468 return;
469 }
470
471 wxLogTrace( traceApi, wxString::Format( "Manager: begin processing; %zu jobs left in queue",
472 m_jobs.size() ) );
473
474 JOB& job = m_jobs.front();
475
476 if( job.type == JOB_TYPE::CREATE_ENV )
477 {
478 wxLogTrace( traceApi, "Manager: Using Python interpreter at %s",
479 Pgm().GetCommonSettings()->m_Api.python_interpreter );
480 wxLogTrace( traceApi, wxString::Format( "Manager: creating Python env at %s",
481 job.env_path ) );
482 PYTHON_MANAGER manager( Pgm().GetCommonSettings()->m_Api.python_interpreter );
483 wxExecuteEnv env;
484
485#ifdef _WIN32
486 wxString systemRoot;
487 wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot );
488 env.env[wxS( "SYSTEMROOT" )] = systemRoot;
489
490 if( Pgm().GetCommonSettings()->m_Api.python_interpreter == FindKicadFile( "pythonw.exe" )
491 || wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
492 {
493 wxLogTrace( traceApi, "Configured Python is the KiCad one; erasing path overrides..." );
494 env.env.erase( "PYTHONHOME" );
495 env.env.erase( "PYTHONPATH" );
496 }
497#endif
498 std::vector<wxString> args = {
499 "-m",
500 "venv",
501 "--system-site-packages",
502 job.env_path
503 };
504
505 manager.Execute( args,
506 [this]( int aRetVal, const wxString& aOutput, const wxString& aError )
507 {
508 wxLogTrace( traceApi,
509 wxString::Format( "Manager: created venv (python returned %d)", aRetVal ) );
510
511 if( !aError.IsEmpty() )
512 wxLogTrace( traceApi, wxString::Format( "Manager: venv err: %s", aError ) );
513
514 wxCommandEvent* evt =
515 new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
516 QueueEvent( evt );
517 }, &env );
518
519 JOB nextJob( job );
520 nextJob.type = JOB_TYPE::SETUP_ENV;
521 m_jobs.emplace_back( nextJob );
522 }
523 else if( job.type == JOB_TYPE::SETUP_ENV )
524 {
525 wxLogTrace( traceApi, wxString::Format( "Manager: setting up environment for %s",
526 job.plugin_path ) );
527
528 std::optional<wxString> pythonHome = PYTHON_MANAGER::GetPythonEnvironment( job.identifier );
529 std::optional<wxString> python = PYTHON_MANAGER::GetVirtualPython( job.identifier );
530
531 if( !python )
532 {
533 wxLogTrace( traceApi, wxString::Format( "Manager: error: python not found at %s",
534 job.env_path ) );
535 }
536 else
537 {
538 PYTHON_MANAGER manager( *python );
539 wxExecuteEnv env;
540
541 if( pythonHome )
542 env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome;
543
544#ifdef _WIN32
545 wxString systemRoot;
546 wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot );
547 env.env[wxS( "SYSTEMROOT" )] = systemRoot;
548
549 if( Pgm().GetCommonSettings()->m_Api.python_interpreter
550 == FindKicadFile( "pythonw.exe" )
551 || wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
552 {
553 wxLogTrace( traceApi,
554 "Configured Python is the KiCad one; erasing path overrides..." );
555 env.env.erase( "PYTHONHOME" );
556 env.env.erase( "PYTHONPATH" );
557 }
558#endif
559
560 std::vector<wxString> args = {
561 "-m",
562 "pip",
563 "install",
564 "--upgrade",
565 "pip"
566 };
567
568 manager.Execute( args,
569 [this]( int aRetVal, const wxString& aOutput, const wxString& aError )
570 {
571 wxLogTrace( traceApi, wxString::Format( "Manager: upgrade pip returned %d",
572 aRetVal ) );
573
574 if( !aError.IsEmpty() )
575 {
576 wxLogTrace( traceApi,
577 wxString::Format( "Manager: upgrade pip stderr: %s", aError ) );
578 }
579
580 wxCommandEvent* evt =
581 new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
582 QueueEvent( evt );
583 }, &env );
584
585 JOB nextJob( job );
587 m_jobs.emplace_back( nextJob );
588 }
589 }
590 else if( job.type == JOB_TYPE::INSTALL_REQUIREMENTS )
591 {
592 wxLogTrace( traceApi, wxString::Format( "Manager: installing dependencies for %s",
593 job.plugin_path ) );
594
595 std::optional<wxString> pythonHome = PYTHON_MANAGER::GetPythonEnvironment( job.identifier );
596 std::optional<wxString> python = PYTHON_MANAGER::GetVirtualPython( job.identifier );
597 wxFileName reqs = wxFileName( job.plugin_path, "requirements.txt" );
598
599 if( !python )
600 {
601 wxLogTrace( traceApi, wxString::Format( "Manager: error: python not found at %s",
602 job.env_path ) );
603 }
604 else if( !reqs.IsFileReadable() )
605 {
606 wxLogTrace( traceApi,
607 wxString::Format( "Manager: error: requirements.txt not found at %s",
608 job.plugin_path ) );
609 }
610 else
611 {
612 wxLogTrace( traceApi, "Manager: Python exe '%s'", *python );
613
614 PYTHON_MANAGER manager( *python );
615 wxExecuteEnv env;
616
617#ifdef _WIN32
618 wxString systemRoot;
619 wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot );
620 env.env[wxS( "SYSTEMROOT" )] = systemRoot;
621
622 // If we are using the KiCad-shipped Python interpreter we have to do hacks
623 env.env.erase( "PYTHONHOME" );
624 env.env.erase( "PYTHONPATH" );
625#endif
626
627 if( pythonHome )
628 env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome;
629
630 std::vector<wxString> args = {
631 "-m",
632 "pip",
633 "install",
634 "--no-input",
635 "--isolated",
636 "--only-binary",
637 ":all:",
638 "--require-virtualenv",
639 "--exists-action",
640 "i",
641 "-r",
642 reqs.GetFullPath()
643 };
644
645 manager.Execute( args,
646 [this, job]( int aRetVal, const wxString& aOutput, const wxString& aError )
647 {
648 if( !aError.IsEmpty() )
649 wxLogTrace( traceApi, wxString::Format( "Manager: pip stderr: %s", aError ) );
650
651 if( aRetVal == 0 )
652 {
653 wxLogTrace( traceApi, wxString::Format( "Manager: marking %s as ready",
654 job.identifier ) );
655 m_readyPlugins.insert( job.identifier );
656 m_busyPlugins.erase( job.identifier );
657 wxCommandEvent* availabilityEvt =
658 new wxCommandEvent( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED, wxID_ANY );
659 wxTheApp->QueueEvent( availabilityEvt );
660 }
661
662 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED,
663 wxID_ANY );
664
665 QueueEvent( evt );
666 }, &env );
667 }
668
669 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
670 QueueEvent( evt );
671 }
672
673 m_jobs.pop_front();
674 wxLogTrace( traceApi, wxString::Format( "Manager: finished job; %zu left in queue",
675 m_jobs.size() ) );
676}
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