21#include <fmt/format.h>
31#include <python_manager.h>
42 m_parent( aEvtHandler )
51 std::function<void(
const wxFileName& )>
m_action;
59 wxDirTraverseResult
OnFile(
const wxString& aFilePath )
override
61 wxFileName file( aFilePath );
63 if( file.GetFullName() == wxS(
"plugin.json" ) )
66 return wxDIR_CONTINUE;
69 wxDirTraverseResult
OnDir(
const wxString& dirPath )
override
71 return wxDIR_CONTINUE;
90 [&](
const wxFileName& aFile )
92 wxLogTrace(
traceApi, wxString::Format(
"Manager: loading plugin from %s",
93 aFile.GetFullPath() ) );
95 auto plugin = std::make_unique<API_PLUGIN>( aFile );
102 wxString::Format(
"Manager: identifier %s already present!",
103 plugin->Identifier() ) );
118 wxLogTrace(
traceApi,
"Manager: loading failed" );
122 if( userPluginsDir.IsOpened() )
124 wxLogTrace(
traceApi, wxString::Format(
"Manager: scanning user path (%s) for plugins...",
125 userPluginsDir.GetName() ) );
126 userPluginsDir.Traverse( loader );
145 wxLogTrace(
traceApi, wxString::Format(
"Manager: Plugin %s is not ready",
151 pluginFile.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_SHORTCUT | wxPATH_NORM_DOTS
152 | wxPATH_NORM_TILDE, plugin.
BasePath() );
153 wxString pluginPath = pluginFile.GetFullPath();
155 std::vector<const wchar_t*> args;
156 std::optional<wxString> py;
160 case PLUGIN_RUNTIME_TYPE::PYTHON:
162 py = PYTHON_MANAGER::GetVirtualPython( plugin.
Identifier() );
166 wxLogTrace(
traceApi, wxString::Format(
"Manager: Python interpreter for %s not found",
171 args.push_back( py->wc_str() );
173 if( !pluginFile.IsFileReadable() )
175 wxLogTrace(
traceApi, wxString::Format(
"Manager: Python entrypoint %s is not readable",
176 pluginFile.GetFullPath() ) );
183 case PLUGIN_RUNTIME_TYPE::EXEC:
185 if( !pluginFile.IsFileExecutable() )
187 wxLogTrace(
traceApi, wxString::Format(
"Manager: Exec entrypoint %s is not executable",
188 pluginFile.GetFullPath() ) );
196 wxLogTrace(
traceApi, wxString::Format(
"Manager: unhandled runtime for action %s",
201 args.emplace_back( pluginPath.wc_str() );
203 for(
const wxString& arg : action->
args )
204 args.emplace_back( arg.wc_str() );
206 args.emplace_back(
nullptr );
209 wxGetEnvMap( &env.env );
210 env.env[ wxS(
"KICAD_API_SOCKET" ) ] =
Pgm().GetApiServer().SocketPath();
211 env.env[ wxS(
"KICAD_API_TOKEN" ) ] =
Pgm().GetApiServer().Token();
212 env.cwd = pluginFile.GetPath();
214 long p = wxExecute(
const_cast<wchar_t**
>( args.data() ), wxEXEC_ASYNC,
nullptr, &env );
218 wxLogTrace(
traceApi, wxString::Format(
"Manager: launching action %s failed",
223 wxLogTrace(
traceApi, wxString::Format(
"Manager: launching action %s -> pid %ld",
231 std::vector<const PLUGIN_ACTION*> actions;
238 if( action->scopes.count( aScope ) )
239 actions.emplace_back( action );
248 for(
const std::unique_ptr<API_PLUGIN>& plugin :
m_plugins )
252 if( plugin->Runtime().type != PLUGIN_RUNTIME_TYPE::PYTHON )
258 std::optional<wxString> env = PYTHON_MANAGER::GetPythonEnvironment( plugin->Identifier() );
262 wxLogTrace(
traceApi, wxString::Format(
"Manager: could not create env for %s",
263 plugin->Identifier() ) );
267 wxFileName envConfigPath( *env, wxS(
"pyvenv.cfg" ) );
269 if( envConfigPath.IsFileReadable() )
271 wxLogTrace(
traceApi, wxString::Format(
"Manager: Python env for %s exists at %s",
272 plugin->Identifier(), *env ) );
278 m_jobs.emplace_back( job );
282 wxLogTrace(
traceApi, wxString::Format(
"Manager: will create Python env for %s at %s",
283 plugin->Identifier(), *env ) );
289 m_jobs.emplace_back( job );
301 wxLogTrace(
traceApi,
"Manager: cleared job queue" );
305 wxLogTrace(
traceApi, wxString::Format(
"Manager: begin processing; %zu jobs left in queue",
312 wxLogTrace(
traceApi,
"Manager: Python exe '%s'",
313 Pgm().GetCommonSettings()->m_Api.python_interpreter );
314 wxLogTrace(
traceApi, wxString::Format(
"Manager: creating Python env at %s",
316 PYTHON_MANAGER manager(
Pgm().GetCommonSettings()->m_Api.python_interpreter );
319 wxString::Format( wxS(
"-m venv %s" ), job.
env_path ),
320 [
this](
int aRetVal,
const wxString& aOutput,
const wxString& aError )
322 wxLogTrace( traceApi,
323 wxString::Format(
"Manager: venv (%d): %s", aRetVal, aOutput ) );
325 if( !aError.IsEmpty() )
326 wxLogTrace( traceApi, wxString::Format(
"Manager: venv err: %s", aError ) );
328 wxCommandEvent* evt =
329 new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
335 wxLogTrace(
traceApi, wxString::Format(
"Manager: installing dependencies for %s",
338 std::optional<wxString> pythonHome = PYTHON_MANAGER::GetPythonEnvironment( job.
identifier );
339 std::optional<wxString> python = PYTHON_MANAGER::GetVirtualPython( job.
identifier );
340 wxFileName reqs = wxFileName( job.
plugin_path,
"requirements.txt" );
344 wxLogTrace(
traceApi, wxString::Format(
"Manager: error: python not found at %s",
347 else if( !reqs.IsFileReadable() )
350 wxString::Format(
"Manager: error: requirements.txt not found at %s",
355 wxLogTrace(
traceApi,
"Manager: Python exe '%s'", *python );
357 PYTHON_MANAGER manager( *python );
361 env.env[wxS(
"VIRTUAL_ENV" )] = *pythonHome;
363 wxString cmd = wxS(
"-m ensurepip" );
364 wxLogTrace(
traceApi,
"Manager: calling python `%s`", cmd );
366 manager.Execute( cmd,
367 [=](
int aRetVal,
const wxString& aOutput,
const wxString& aError )
369 wxLogTrace(
traceApi, wxString::Format(
"Manager: ensurepip (%d): %s",
370 aRetVal, aOutput ) );
372 if( !aError.IsEmpty() )
375 wxString::Format(
"Manager: ensurepip err: %s", aError ) );
379 cmd = wxString::Format(
380 wxS(
"-m pip install --no-input --isolated --require-virtualenv "
381 "--exists-action i -r '%s'" ),
382 reqs.GetFullPath() );
384 wxLogTrace(
traceApi,
"Manager: calling python `%s`", cmd );
386 manager.Execute( cmd,
387 [
this, job](
int aRetVal,
const wxString& aOutput,
const wxString& aError )
389 wxLogTrace(
traceApi, wxString::Format(
"Manager: pip (%d): %s",
390 aRetVal, aOutput ) );
392 if( !aError.IsEmpty() )
393 wxLogTrace(
traceApi, wxString::Format(
"Manager: pip err: %s", aError ) );
397 wxLogTrace(
traceApi, wxString::Format(
"Manager: marking %s as ready",
400 wxCommandEvent* availabilityEvt =
402 wxTheApp->QueueEvent( availabilityEvt );
405 wxCommandEvent* evt =
new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED,
412 wxCommandEvent* evt =
new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
417 wxLogTrace(
traceApi, wxString::Format(
"Manager: done processing; %zu jobs left in queue",
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.
void processPluginDependencies()
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.
std::map< wxString, const API_PLUGIN * > m_pluginsCache
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)
A plugin that is invoked by KiCad and runs as an external process; communicating with KiCad via the I...
const PLUGIN_RUNTIME & Runtime() const
const wxString & Identifier() const
wxString BasePath() const
static wxString GetUserPluginsPath()
Gets the user path for plugins.
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
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
PGM_BASE & Pgm()
The global Program "get" accessor.
An action performed by a plugin via the IPC API (not to be confused with ACTION_PLUGIN,...
const API_PLUGIN & plugin
std::vector< wxString > args