KiCad PCB EDA Suite
Loading...
Searching...
No Matches
kicad_cli.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) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25
26#include <wx/filename.h>
27#include <wx/log.h>
28#include <wx/stdpaths.h>
29#include <wx/wxcrtvararg.h> //for wxPrintf
30
31#include <kiway.h>
33#include <string_utils.h>
34#include <paths.h>
37#include <systemdirsappend.h>
38#include <trace_helpers.h>
39
40#include <cctype>
41#include <set>
42#include <stdexcept>
43
44#include "pgm_kicad.h"
45#include "kicad_manager_frame.h"
46
47#include <build_version.h>
48#include <kiplatform/app.h>
50#include <locale_io.h>
51
52#include "cli/command_jobset.h"
54#include "cli/command_pcb.h"
56#include "cli/command_pcb_drc.h"
79#include "cli/command_fp.h"
83#include "cli/command_sch.h"
84#include "cli/command_sch_erc.h"
87#include "cli/command_sym.h"
91#include "cli/command_gerber.h"
96#include "cli/command_version.h"
97#include "cli/exit_codes.h"
98
99#ifdef KICAD_IPC_API
101#endif
102
103// Add this header after all others, to avoid a collision name in a Windows header
104// on mingw.
105#include <wx/app.h>
106
107// a dummy to quiet linking with EDA_BASE_FRAME::config();
108#include <kiface_base.h>
109#include <thread_pool.h>
110
111
113{
114 // This function should never be called. It is only referenced from
115 // EDA_BASE_FRAME::config() and this is only provided to satisfy the linker,
116 // not to be actually called.
117 wxLogFatalError( wxT( "Unexpected call to Kiface() in kicad/kicad.cpp" ) );
118
119 throw std::logic_error( "Unexpected call to Kiface() in kicad/kicad.cpp" );
120}
121
122
124{
126
127 std::vector<COMMAND_ENTRY> subCommands;
128
130 handler( aHandler ) {};
131 COMMAND_ENTRY( CLI::COMMAND* aHandler, std::vector<COMMAND_ENTRY> aSub ) :
132 handler( aHandler ),
133 subCommands( aSub ) {};
134};
135
145static CLI::PCB_EXPORT_3D_COMMAND exportPcbGlbCmd{ "glb", UTF8STDSTR( _( "Export GLB (binary GLTF)" ) ),
211
212#ifdef KICAD_IPC_API
213static CLI::API_SERVER_COMMAND apiServerCmd{};
214#endif
215
216// clang-format off
217static std::vector<COMMAND_ENTRY> commandStack = {
218 {
219 &jobsetCmd,
220 {
221 {
223 }
224 }
225 },
226 {
227 &fpCmd,
228 {
229 {
231 {
233 }
234 },
235 {
237 }
238 }
239 },
240 {
241 &pcbCmd,
242 {
243 {
244 &pcbDrcCmd
245 },
246 {
248 },
249 {
251 },
252 {
254 {
279 }
280 },
281 {
283 }
284 }
285 },
286 {
287 &schCmd,
288 {
289 {
290 &schErcCmd
291 },
292 {
294 {
304 }
305 },
306 {
308 }
309 }
310 },
311 {
312 &symCmd,
313 {
314 {
316 {
318 }
319 },
320 {
322 }
323 }
324 },
325 {
326 &gerberCmd,
327 {
328 {
330 {
331 {
333 }
334 }
335 },
336 {
338 },
339 {
341 }
342 }
343 },
344 {
345 &versionCmd,
346 }
347#ifdef KICAD_IPC_API
348 ,
349 {
350 &apiServerCmd,
351 }
352#endif
353};
354// clang-format on
355
356
357static void recurseArgParserBuild( argparse::ArgumentParser& aArgParser, COMMAND_ENTRY& aEntry )
358{
359 aArgParser.add_subparser( aEntry.handler->GetArgParser() );
360
361 for( COMMAND_ENTRY& subEntry : aEntry.subCommands )
362 {
363 recurseArgParserBuild( aEntry.handler->GetArgParser(), subEntry );
364 }
365}
366
367
368static COMMAND_ENTRY* recurseArgParserSubCommandUsed( argparse::ArgumentParser& aArgParser, COMMAND_ENTRY& aEntry )
369{
370 COMMAND_ENTRY* cliCmd = nullptr;
371
372 if( aArgParser.is_subcommand_used( aEntry.handler->GetName() ) )
373 {
374 for( COMMAND_ENTRY& subentry : aEntry.subCommands )
375 {
376 cliCmd = recurseArgParserSubCommandUsed( aEntry.handler->GetArgParser(), subentry );
377 if( cliCmd )
378 break;
379 }
380
381 if( !cliCmd )
382 cliCmd = &aEntry;
383 }
384
385 return cliCmd;
386}
387
388
389static void printHelp( argparse::ArgumentParser& argParser )
390{
391 std::stringstream ss;
392 ss << argParser;
393 wxPrintf( From_UTF8( ss.str().c_str() ) );
394}
395
396
403static bool looksLikeNegativeVectorValue( const std::string& aValue )
404{
405 if( aValue.empty() || aValue[0] != '-' )
406 return false;
407
408 if( aValue.find( ',' ) == std::string::npos )
409 return false;
410
411 for( size_t i = 1; i < aValue.size(); ++i )
412 {
413 char c = aValue[i];
414
415 if( !std::isdigit( c ) && c != '.' && c != ',' && c != '-' && c != '+' )
416 return false;
417 }
418
419 return true;
420}
421
422
434static std::vector<std::string> preprocessArgs( int argc, char** argv )
435{
436 std::vector<std::string> result;
437
438 static const std::set<std::string> vectorArgs = { "--rotate", "--pan", "--pivot" };
439
440 for( int i = 0; i < argc; ++i )
441 {
442 std::string current( argv[i] );
443
444 if( vectorArgs.count( current ) && i + 1 < argc )
445 {
446 std::string next( argv[i + 1] );
447
449 {
450 result.push_back( current + "='" + next + "'" );
451 ++i;
452 continue;
453 }
454 }
455
456 result.push_back( current );
457 }
458
459 return result;
460}
461
462
464{
466 App().SetAppDisplayName( wxT( "kicad-cli" ) );
467
468#if defined( DEBUG )
469 wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
470
471 if( !wxIsAbsolutePath( absoluteArgv0 ) )
472 {
473 wxLogError( wxT( "No meaningful argv[0]" ) );
474 return false;
475 }
476#endif
477
478 if( !InitPgm( true ) )
479 return false;
480
481 m_bm.InitSettings( new KICAD_SETTINGS );
484 m_bm.Init();
485
487
488 return true;
489}
490
491
493{
494 argparse::ArgumentParser argParser( std::string( "kicad-cli" ), GetMajorMinorVersion().ToStdString(),
495 argparse::default_arguments::none );
496
497 argParser.add_argument( "-v", ARG_VERSION )
498 .help( UTF8STDSTR( _( "prints version information and exits" ) ) )
499 .flag()
500 .nargs( 0 );
501
502 argParser.add_argument( ARG_HELP_SHORT, ARG_HELP ).help( UTF8STDSTR( ARG_HELP_DESC ) ).flag().nargs( 0 );
503
504 for( COMMAND_ENTRY& entry : commandStack )
505 {
506 recurseArgParserBuild( argParser, entry );
507 }
508
509 try
510 {
511 // Use the C locale to parse arguments
512 // Otherwise the decimal separator for the locale will be applied
513 LOCALE_IO dummy;
514
515 // Pre-process arguments to handle negative vector values (e.g., --rotate -45,0,45)
516 // which argparse would otherwise interpret as unknown options
517 std::vector<std::string> args = preprocessArgs( m_argcUtf8, m_argvUtf8 );
518 argParser.parse_args( args );
519 }
520 // std::runtime_error doesn't seem to be enough for the scan<>()
521 catch( const std::exception& err )
522 {
523 bool requestedHelp = false;
524
525 for( int i = 0; i < m_argcUtf8; ++i )
526 {
527 if( std::string arg( m_argvUtf8[i] ); arg == ARG_HELP_SHORT || arg == ARG_HELP )
528 {
529 requestedHelp = true;
530 break;
531 }
532 }
533
534 if( !requestedHelp )
535 wxPrintf( "%s\n", err.what() );
536
537 // find the correct argparser object to output the command usage info
538 COMMAND_ENTRY* cliCmd = nullptr;
539 for( COMMAND_ENTRY& entry : commandStack )
540 {
541 if( argParser.is_subcommand_used( entry.handler->GetName() ) )
542 {
543 cliCmd = recurseArgParserSubCommandUsed( argParser, entry );
544 }
545 }
546
547 // arg parser uses a stream overload for printing the help
548 // we want to intercept so we can wxString the utf8 contents
549 // because on windows our terminal codepage might not be utf8
550 if( cliCmd )
551 cliCmd->handler->PrintHelp();
552 else
553 {
554 printHelp( argParser );
555 }
556
557 return requestedHelp ? 0 : CLI::EXIT_CODES::ERR_ARGS;
558 }
559
560 if( argParser[ARG_HELP] == true )
561 {
562 std::stringstream ss;
563 ss << argParser;
564 wxPrintf( From_UTF8( ss.str().c_str() ) );
565
566 return 0;
567 }
568
569 CLI::COMMAND* cliCmd = nullptr;
570
571 // the version arg gets redirected to the version subcommand
572 if( argParser[ARG_VERSION] == true )
573 {
574 cliCmd = &versionCmd;
575 }
576
577 if( !cliCmd )
578 {
579 for( COMMAND_ENTRY& entry : commandStack )
580 {
581 if( argParser.is_subcommand_used( entry.handler->GetName() ) )
582 {
583 COMMAND_ENTRY* cmdSubEntry = recurseArgParserSubCommandUsed( argParser, entry );
584 if( cmdSubEntry != nullptr )
585 {
586 cliCmd = cmdSubEntry->handler;
587 break;
588 }
589 }
590 }
591 }
592
593 if( cliCmd )
594 {
595 int exitCode = cliCmd->Perform( Kiway );
596
597 if( exitCode != CLI::EXIT_CODES::AVOID_CLOSING )
598 {
599 return exitCode;
600 }
601 else
602 {
603 return 0;
604 }
605 }
606 else
607 {
608 printHelp( argParser );
609
611 }
612}
613
614
616{
617 // Abort and wait on any background jobs
618 GetKiCadThreadPool().purge();
619 GetKiCadThreadPool().wait();
620
622
624 {
626 m_settings_manager->Save();
627 }
628
629 // Destroy everything in PGM_KICAD,
630 // especially wxSingleInstanceCheckerImpl earlier than wxApp and earlier
631 // than static destruction would.
632 Destroy();
633}
634
635
636void PGM_KICAD::MacOpenFile( const wxString& aFileName )
637{
638#if defined( __WXMAC__ )
639 wxFAIL_MSG( "kicad-cli should not call MacOpenFile" );
640#endif
641}
642
643
645{
646 // unlike a normal destructor, this is designed to be called more
647 // than once safely:
648
649 m_bm.End();
650
652}
653
654
656
658
662struct APP_KICAD_CLI : public wxAppConsole
663{
665 wxAppConsole()
666 {
667 SetPgm( &program );
668
669 // Init the environment each platform wants
671 }
672
673
674 bool OnInit() override
675 {
676 // Perform platform-specific init tasks
677 if( !KIPLATFORM::APP::Init() )
678 return false;
679
680#ifndef DEBUG
681 // Enable logging traces to the console in release build.
682 // This is usually disabled, but it can be useful for users to run to help
683 // debug issues and other problems.
684 if( wxGetEnv( wxS( "KICAD_ENABLE_WXTRACE" ), nullptr ) )
685 {
686 wxLog::EnableLogging( true );
687 wxLog::SetLogLevel( wxLOG_Trace );
688 }
689#endif
690
691 if( !program.OnPgmInit() )
692 {
693 program.OnPgmExit();
694 return false;
695 }
696
697 return true;
698 }
699
700 int OnExit() override
701 {
702 // Drain any pending wx-managed objects before tearing down PGM_BASE
703 // singletons so destructors can still call into Pgm(). See
704 // https://gitlab.com/kicad/code/kicad/-/issues/23373 for the GUI variant
705 // of this hazard; kept consistent with the GUI apps for parity.
706 int ret = wxAppConsole::OnExit();
707
708#if defined( __FreeBSD__ )
709 // Avoid wxLog crashing when used in destructors invoked from OnPgmExit().
710 wxLog::EnableLogging( false );
711#endif
712
713 program.OnPgmExit();
714 return ret;
715 }
716
717 int OnRun() override
718 {
719 try
720 {
721 return program.OnPgmRun();
722 }
723 catch( ... )
724 {
725 Pgm().HandleException( std::current_exception() );
726 }
727
728 return -1;
729 }
730
731 int FilterEvent( wxEvent& aEvent ) override { return Event_Skip; }
732
733#if defined( DEBUG )
737 bool ProcessEvent( wxEvent& aEvent ) override
738 {
739 if( aEvent.GetEventType() == wxEVT_CHAR || aEvent.GetEventType() == wxEVT_CHAR_HOOK )
740 {
741 wxKeyEvent* keyEvent = static_cast<wxKeyEvent*>( &aEvent );
742
743 if( keyEvent )
744 {
745 wxLogTrace( kicadTraceKeyEvent, "APP_KICAD::ProcessEvent %s", dump( *keyEvent ) );
746 }
747 }
748
749 aEvent.Skip();
750 return false;
751 }
752
760 bool OnExceptionInMainLoop() override
761 {
762 try
763 {
764 throw;
765 }
766 catch( ... )
767 {
768 Pgm().HandleException( std::current_exception() );
769 }
770
771 return false; // continue on. Return false to abort program
772 }
773#endif
774};
775
776IMPLEMENT_APP_CONSOLE( APP_KICAD_CLI )
777
778
779// The C++ project manager supports one open PROJECT, so Prj() calls within
780// this link image need this function.
782{
783 return Kiway.Prj();
784}
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
argparse::ArgumentParser & GetArgParser()
Definition command.h:60
const std::string & GetName() const
Definition command.h:61
void PrintHelp()
Definition command.cpp:47
int Perform(KIWAY &aKiway)
Entry point to processing commands from args and doing work.
Definition command.cpp:55
A KIFACE implementation.
Definition kiface_base.h:39
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:315
void OnKiwayEnd()
Definition kiway.cpp:815
void LoadGlobalTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the global library tables in the given list, or all tables if no list is given
virtual wxApp & App()
Return a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition pgm_base.cpp:211
int m_argcUtf8
Definition pgm_base.h:449
std::unique_ptr< SETTINGS_MANAGER > m_settings_manager
Definition pgm_base.h:411
void Destroy()
Definition pgm_base.cpp:190
char ** m_argvUtf8
argv parameters converted to utf8 form because wxWidgets has opinions.
Definition pgm_base.h:447
void HandleException(std::exception_ptr aPtr, bool aUnhandled=false)
A exception handler to be used at the top level if exceptions bubble up that for.
Definition pgm_base.cpp:802
void BuildArgvUtf8()
Builds the UTF8 based argv variable.
Definition pgm_base.cpp:282
bool InitPgm(bool aHeadless=false, bool aIsUnitTest=false)
Initialize this program.
Definition pgm_base.cpp:327
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:130
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:132
void SaveCommonSettings()
Save the program (process) settings subset which are stored .kicad_common.
Definition pgm_base.cpp:532
PGM_KICAD extends PGM_BASE to bring in FileHistory() and PdfBrowser() which were moved from EDA_APP i...
Definition pgm_kicad.h:42
bool OnPgmInit()
Definition kicad.cpp:94
void Destroy()
Definition kicad.cpp:454
void MacOpenFile(const wxString &aFileName) override
Specific to MacOSX (not used under Linux or Windows).
Definition kicad.cpp:441
void OnPgmExit()
Definition kicad.cpp:408
APP_SETTINGS_BASE * PgmSettings()
Definition pgm_kicad.h:59
int OnPgmRun()
Definition kicad.cpp:402
BIN_MOD m_bm
Definition pgm_kicad.h:72
Container for project specific data.
Definition project.h:66
T * RegisterSettings(T *aSettings, bool aLoadNow=true)
Take ownership of the pointer passed in.
void SetKiway(KIWAY *aKiway)
Associate this setting manager with the given Kiway.
#define ARG_HELP
Definition command.h:30
#define UTF8STDSTR(s)
Definition command.h:27
#define ARG_HELP_DESC
Definition command.h:32
#define ARG_VERSION
Definition command.h:29
#define ARG_HELP_SHORT
Definition command.h:31
#define _(s)
const wxChar *const kicadTraceKeyEvent
Flag to enable wxKeyEvent debug tracing.
PROJECT & Prj()
Definition kicad.cpp:655
static CLI::PCB_EXPORT_SVG_COMMAND exportPcbSvgCmd
static CLI::SCH_EXPORT_PLOT_COMMAND exportSchHpglCmd
static CLI::SCH_EXPORT_PLOT_COMMAND exportSchSvgCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcbVrmlCmd
static CLI::GERBER_INFO_COMMAND gerberInfoCmd
static CLI::PCB_EXPORT_STATS_COMMAND exportPcbStatsCmd
static CLI::PCB_EXPORT_DXF_COMMAND exportPcbDxfCmd
static CLI::SCH_ERC_COMMAND schErcCmd
static CLI::PCB_EXPORT_HPGL_COMMAND exportPcbHpglCmd
static CLI::PCB_DRC_COMMAND pcbDrcCmd
static std::vector< std::string > preprocessArgs(int argc, char **argv)
Pre-process command line arguments to handle negative numeric values.
static CLI::PCB_IMPORT_COMMAND pcbImportCmd
static CLI::FP_EXPORT_SVG_COMMAND fpExportSvgCmd
static CLI::PCB_EXPORT_COMMAND exportPcbCmd
static CLI::PCB_RENDER_COMMAND pcbRenderCmd
static bool looksLikeNegativeVectorValue(const std::string &aValue)
Check if a string looks like a numeric vector value that happens to start with a minus sign.
static CLI::SYM_UPGRADE_COMMAND symUpgradeCmd
static CLI::FP_EXPORT_COMMAND fpExportCmd
static CLI::SCH_EXPORT_PYTHONBOM_COMMAND exportSchPythonBomCmd
static CLI::FP_UPGRADE_COMMAND fpUpgradeCmd
static CLI::SCH_EXPORT_PLOT_COMMAND exportSchPdfCmd
static void printHelp(argparse::ArgumentParser &argParser)
static CLI::JOBSET_RUN_COMMAND jobsetRunCmd
static CLI::PCB_UPGRADE_COMMAND pcbUpgradeCmd
static CLI::PCB_EXPORT_POS_COMMAND exportPcbPosCmd
static CLI::GERBER_DIFF_COMMAND gerberDiffCmd
static CLI::PCB_EXPORT_PS_COMMAND exportPcbPsCmd
static CLI::GERBER_COMMAND gerberCmd
static CLI::SYM_COMMAND symCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcbStepCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcbPlyCmd
static CLI::PCB_EXPORT_GERBERS_COMMAND exportPcbGerbersCmd
static PGM_KICAD program
static CLI::SCH_EXPORT_BOM_COMMAND exportSchBomCmd
static CLI::GERBER_CONVERT_PNG_COMMAND gerberConvertPngCmd
static CLI::PCB_EXPORT_IPCD356_COMMAND exportPcbIpcD356Cmd
static CLI::SCH_COMMAND schCmd
static CLI::PCB_COMMAND pcbCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcb3DPDFCmd
static std::vector< COMMAND_ENTRY > commandStack
static CLI::PCB_EXPORT_DRILL_COMMAND exportPcbDrillCmd
static CLI::SCH_EXPORT_PLOT_COMMAND exportSchPngCmd
static CLI::PCB_EXPORT_IPC2581_COMMAND exportPcbIpc2581Cmd
static CLI::SCH_EXPORT_PLOT_COMMAND exportSchDxfCmd
static CLI::JOBSET_COMMAND jobsetCmd
static CLI::SYM_EXPORT_COMMAND symExportCmd
static CLI::SYM_EXPORT_SVG_COMMAND symExportSvgCmd
static COMMAND_ENTRY * recurseArgParserSubCommandUsed(argparse::ArgumentParser &aArgParser, COMMAND_ENTRY &aEntry)
static CLI::PCB_EXPORT_3D_COMMAND exportPcbU3DCmd
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static CLI::PCB_EXPORT_ODB_COMMAND exportPcbOdbCmd
static CLI::VERSION_COMMAND versionCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcbStlCmd
static CLI::PCB_EXPORT_PDF_COMMAND exportPcbPdfCmd
static CLI::SCH_UPGRADE_COMMAND schUpgradeCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcbXaoCmd
static void recurseArgParserBuild(argparse::ArgumentParser &aArgParser, COMMAND_ENTRY &aEntry)
static CLI::SCH_EXPORT_NETLIST_COMMAND exportSchNetlistCmd
static CLI::PCB_EXPORT_PNG_COMMAND exportPcbPngCmd
static CLI::SCH_EXPORT_PLOT_COMMAND exportSchPostscriptCmd
static CLI::PCB_EXPORT_GENCAD_COMMAND exportPcbGencadCmd
static CLI::GERBER_CONVERT_COMMAND gerberConvertCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcbStepzCmd
static CLI::FP_COMMAND fpCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcbGlbCmd
static CLI::SCH_EXPORT_COMMAND exportSchCmd
static CLI::PCB_EXPORT_3D_COMMAND exportPcbBrepCmd
#define KFCTL_CPP_PROJECT_SUITE
Running under C++ project mgr, possibly with others.
Definition kiway.h:164
#define KFCTL_CLI
Running as CLI app.
Definition kiway.h:165
static const int ERR_ARGS
Definition exit_codes.h:31
static const int AVOID_CLOSING
Definition exit_codes.h:28
bool Init()
Perform application-specific initialization tasks.
Definition unix/app.cpp:40
void Init()
Perform environment initialization tasks.
void SetPgm(PGM_BASE *pgm)
PGM_BASE & Pgm()
The global program "get" accessor.
CITER next(CITER it)
Definition ptree.cpp:124
PGM_SINGLE_TOP program
KIWAY Kiway(KFCTL_STANDALONE)
std::vector< FAB_LAYER_COLOR > dummy
wxString From_UTF8(const char *cstring)
Not publicly visible because most of the action is in PGM_KICAD these days.
int OnExit() override
bool OnInit() override
int FilterEvent(wxEvent &aEvent) override
int OnRun() override
COMMAND_ENTRY(CLI::COMMAND *aHandler)
COMMAND_ENTRY(CLI::COMMAND *aHandler, std::vector< COMMAND_ENTRY > aSub)
CLI::COMMAND * handler
std::vector< COMMAND_ENTRY > subCommands
System directories search utilities.
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
wxLogTrace helper definitions.