KiCad PCB EDA Suite
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 (C) 2004-2021 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/app.h>
29#include <wx/stdpaths.h>
30#include <wx/msgdlg.h>
31#include <wx/wx.h>
32
33#include <kiway.h>
34#include <macros.h>
35#include <paths.h>
38#include <systemdirsappend.h>
39#include <trace_helpers.h>
40
41#include <stdexcept>
42
43#include "pgm_kicad.h"
44#include "kicad_manager_frame.h"
45
46#include <build_version.h>
47#include <kiplatform/app.h>
49
50#include "cli/command_pcb.h"
64#include "cli/command_fp.h"
68#include "cli/command_sch.h"
70#include "cli/command_sym.h"
74#include "cli/command_version.h"
75#include "cli/exit_codes.h"
76#include "cli/cli_names.h"
77
78// a dummy to quiet linking with EDA_BASE_FRAME::config();
79#include <kiface_base.h>
81{
82 // This function should never be called. It is only referenced from
83 // EDA_BASE_FRAME::config() and this is only provided to satisfy the linker,
84 // not to be actually called.
85 wxLogFatalError( wxT( "Unexpected call to Kiface() in kicad/kicad.cpp" ) );
86
87 throw std::logic_error( "Unexpected call to Kiface() in kicad/kicad.cpp" );
88}
89
90
92
93
95{
96 return program;
97}
98
99
100// Similar to PGM_BASE& Pgm(), but return nullptr when a *.ki_face is run from a python script.
102{
103 return &program;
104}
105
106
108{
109 return program;
110}
111
112
114{
116
117 std::vector<COMMAND_ENTRY> subCommands;
118
119 COMMAND_ENTRY( CLI::COMMAND* aHandler ) : handler( aHandler ){};
120 COMMAND_ENTRY( CLI::COMMAND* aHandler, std::vector<COMMAND_ENTRY> aSub ) :
121 handler( aHandler ), subCommands( aSub ){};
122};
123
124
150
151
152static std::vector<COMMAND_ENTRY> commandStack = {
153 {
154 &fpCmd,
155 {
156 {
158 {
160 }
161 },
162 {
164 }
165 }
166 },
167 {
168 &pcbCmd,
169 {
170 {
172 {
181 }
182 }
183 }
184 },
185 {
186 &schCmd,
187 {
188 {
190 {
195 }
196 }
197 }
198 },
199 {
200 &symCmd,
201 {
202 {
204 {
206 }
207 },
208 {
210 }
211 }
212 },
213 {
214 &versionCmd,
215 }
216};
217
218
219static void recurseArgParserBuild( argparse::ArgumentParser& aArgParser, COMMAND_ENTRY& aEntry )
220{
221 aArgParser.add_subparser( aEntry.handler->GetArgParser() );
222
223 for( COMMAND_ENTRY& subEntry : aEntry.subCommands )
224 {
225 recurseArgParserBuild( aEntry.handler->GetArgParser(), subEntry );
226 }
227}
228
229
230static COMMAND_ENTRY* recurseArgParserSubCommandUsed( argparse::ArgumentParser& aArgParser,
231 COMMAND_ENTRY& aEntry )
232{
233 COMMAND_ENTRY* cliCmd = nullptr;
234
235 if( aArgParser.is_subcommand_used( aEntry.handler->GetName() ) )
236 {
237 for( COMMAND_ENTRY& subentry : aEntry.subCommands )
238 {
239 cliCmd = recurseArgParserSubCommandUsed( aEntry.handler->GetArgParser(), subentry );
240 if( cliCmd )
241 break;
242 }
243
244 if(!cliCmd)
245 cliCmd = &aEntry;
246 }
247
248 return cliCmd;
249}
250
251
252static void printHelp( argparse::ArgumentParser& argParser )
253{
254 std::stringstream ss;
255 ss << argParser;
256 wxPrintf( FROM_UTF8( ss.str().c_str() ) );
257}
258
259
261{
263 App().SetAppDisplayName( wxT( "KiCad-cli" ) );
264 // App name can be used by internal code to know if this is a
265 // kicad CLI app or a GUI app that is running
266 App().SetClassName( KICAD_CLI_APP_NAME );
267
268#if defined( DEBUG )
269 wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
270
271 if( !wxIsAbsolutePath( absoluteArgv0 ) )
272 {
273 wxLogError( wxT( "No meaningful argv[0]" ) );
274 return false;
275 }
276#endif
277
278 if( !InitPgm( true, true) )
279 return false;
280
284 m_bm.Init();
285
286 return true;
287}
288
289
291{
292 argparse::ArgumentParser argParser( std::string( "kicad-cli" ), GetMajorMinorVersion().ToStdString(),
293 argparse::default_arguments::none );
294
295 argParser.add_argument( "-v", ARG_VERSION )
296 .default_value( false )
297 .help( UTF8STDSTR( _( "prints version information and exits" ) ) )
298 .implicit_value( true )
299 .nargs( 0 );
300
301 argParser.add_argument( ARG_HELP_SHORT, ARG_HELP )
302 .default_value( false )
303 .help( UTF8STDSTR( ARG_HELP_DESC ) )
304 .implicit_value( true )
305 .nargs( 0 );
306
307 for( COMMAND_ENTRY& entry : commandStack )
308 {
309 recurseArgParserBuild( argParser, entry );
310 }
311
312 try
313 {
314 argParser.parse_args( m_argcUtf8, m_argvUtf8 );
315 }
316 catch( const std::runtime_error& err )
317 {
318 wxPrintf( "%s\n", err.what() );
319
320 // find the correct argparser object to output the command usage info
321 COMMAND_ENTRY* cliCmd = nullptr;
322 for( COMMAND_ENTRY& entry : commandStack )
323 {
324 if( argParser.is_subcommand_used( entry.handler->GetName() ) )
325 {
326 cliCmd = recurseArgParserSubCommandUsed( argParser, entry );
327 }
328 }
329
330 // arg parser uses a stream overload for printing the help
331 // we want to intercept so we can wxString the utf8 contents
332 // because on windows our terminal codepage might not be utf8
333 if( cliCmd )
334 cliCmd->handler->PrintHelp();
335 else
336 {
337 printHelp( argParser );
338 }
339
341 }
342
343 if( argParser[ ARG_HELP ] == true )
344 {
345 std::stringstream ss;
346 ss << argParser;
347 wxPrintf( FROM_UTF8( ss.str().c_str() ) );
348
349 return 0;
350 }
351
352 CLI::COMMAND* cliCmd = nullptr;
353
354 // the version arg gets redirected to the version subcommand
355 if( argParser[ARG_VERSION] == true )
356 {
357 cliCmd = &versionCmd;
358 }
359
360 if( !cliCmd )
361 {
362 for( COMMAND_ENTRY& entry : commandStack )
363 {
364 if( argParser.is_subcommand_used( entry.handler->GetName() ) )
365 {
366 COMMAND_ENTRY* cmdSubEntry = recurseArgParserSubCommandUsed( argParser, entry );
367 if( cmdSubEntry != nullptr )
368 {
369 cliCmd = cmdSubEntry->handler;
370 break;
371 }
372 }
373 }
374 }
375
376 if( cliCmd )
377 {
378 int exitCode = cliCmd->Perform( Kiway );
379
380 if( exitCode != CLI::EXIT_CODES::AVOID_CLOSING )
381 {
382 return exitCode;
383 }
384 else
385 {
386 return 0;
387 }
388 }
389 else
390 {
391 printHelp( argParser );
392
394 }
395}
396
397
399{
401
403 {
405 m_settings_manager->Save();
406 }
407
408 // Destroy everything in PGM_KICAD,
409 // especially wxSingleInstanceCheckerImpl earlier than wxApp and earlier
410 // than static destruction would.
411 Destroy();
412}
413
414
415void PGM_KICAD::MacOpenFile( const wxString& aFileName )
416{
417#if defined( __WXMAC__ )
418 wxFAIL_MSG( "kicad-cli should not call MacOpenFile" );
419#endif
420}
421
422
424{
425 // unlike a normal destructor, this is designed to be called more
426 // than once safely:
427
428 m_bm.End();
429
431}
432
433
435
436
440struct APP_KICAD_CLI : public wxAppConsole
441{
442 APP_KICAD_CLI() : wxAppConsole()
443 {
444 // Init the environment each platform wants
446 }
447
448
449 bool OnInit() override
450 {
451 // Perform platform-specific init tasks
452 if( !KIPLATFORM::APP::Init() )
453 return false;
454
455 if( !program.OnPgmInit() )
456 {
458 return false;
459 }
460
461 return true;
462 }
463
464 int OnExit() override
465 {
467
468#if defined( __FreeBSD__ )
469 // Avoid wxLog crashing when used in destructors.
470 wxLog::EnableLogging( false );
471#endif
472
473 return wxAppConsole::OnExit();
474 }
475
476 int OnRun() override
477 {
478 try
479 {
480 return program.OnPgmRun();
481 }
482 catch( const std::exception& e )
483 {
484 wxLogError( wxT( "Unhandled exception class: %s what: %s" ),
485 FROM_UTF8( typeid( e ).name() ), FROM_UTF8( e.what() ) );
486 }
487 catch( const IO_ERROR& ioe )
488 {
489 wxLogError( ioe.What() );
490 }
491 catch( ... )
492 {
493 wxLogError( wxT( "Unhandled exception of unknown type" ) );
494 }
495
496 return -1;
497 }
498
499 int FilterEvent( wxEvent& aEvent ) override
500 {
501 if( aEvent.GetEventType() == wxEVT_SHOW )
502 {
503 wxShowEvent& event = static_cast<wxShowEvent&>( aEvent );
504 wxDialog* dialog = dynamic_cast<wxDialog*>( event.GetEventObject() );
505
506 std::vector<void*>& dlgs = Pgm().m_ModalDialogs;
507
508 if( dialog )
509 {
510 if( event.IsShown() && dialog->IsModal() )
511 {
512 dlgs.push_back( dialog );
513 }
514 // Under GTK, sometimes the modal flag is cleared before hiding
515 else if( !event.IsShown() && !dlgs.empty() )
516 {
517 // If we close the expected dialog, remove it from our stack
518 if( dlgs.back() == dialog )
519 dlgs.pop_back();
520 // If an out-of-order, remove all dialogs added after the closed one
521 else if( auto it = std::find( dlgs.begin(), dlgs.end(), dialog ) ; it != dlgs.end() )
522 dlgs.erase( it, dlgs.end() );
523 }
524 }
525 }
526
527 return Event_Skip;
528 }
529
530#if defined( DEBUG )
534 bool ProcessEvent( wxEvent& aEvent ) override
535 {
536 if( aEvent.GetEventType() == wxEVT_CHAR || aEvent.GetEventType() == wxEVT_CHAR_HOOK )
537 {
538 wxKeyEvent* keyEvent = static_cast<wxKeyEvent*>( &aEvent );
539
540 if( keyEvent )
541 {
542 wxLogTrace( kicadTraceKeyEvent, "APP_KICAD::ProcessEvent %s", dump( *keyEvent ) );
543 }
544 }
545
546 aEvent.Skip();
547 return false;
548 }
549
557 bool OnExceptionInMainLoop() override
558 {
559 try
560 {
561 throw;
562 }
563 catch( const std::exception& e )
564 {
565 wxLogError( "Unhandled exception class: %s what: %s", FROM_UTF8( typeid( e ).name() ),
566 FROM_UTF8( e.what() ) );
567 }
568 catch( const IO_ERROR& ioe )
569 {
570 wxLogError( ioe.What() );
571 }
572 catch( ... )
573 {
574 wxLogError( "Unhandled exception of unknown type" );
575 }
576
577 return false; // continue on. Return false to abort program
578 }
579#endif
580};
581
582IMPLEMENT_APP_CONSOLE( APP_KICAD_CLI )
583
584
585// The C++ project manager supports one open PROJECT, so Prj() calls within
586// this link image need this function.
588{
589 return Kiway.Prj();
590}
const char * name
Definition: DXF_plotter.cpp:56
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
argparse::ArgumentParser & GetArgParser()
Definition: command.h:54
const std::string & GetName() const
Definition: command.h:55
void PrintHelp()
Definition: command.cpp:41
int Perform(KIWAY &aKiway)
Entry point to processing commands from args and doing work.
Definition: command.cpp:49
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:76
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
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:274
void OnKiwayEnd()
Definition: kiway.cpp:683
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition: kiway.cpp:192
Container for data for KiCad programs.
Definition: pgm_base.h:95
virtual wxApp & App()
Returns a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition: pgm_base.cpp:163
int m_argcUtf8
argv parameters converted to utf8 form, because wxwidgets has opinions and will return argv as either...
Definition: pgm_base.h:355
std::unique_ptr< SETTINGS_MANAGER > m_settings_manager
Definition: pgm_base.h:331
void Destroy()
Definition: pgm_base.cpp:151
bool InitPgm(bool aHeadless=false, bool aSkipPyInit=false, bool aIsUnitTest=false)
Initialize this program.
Definition: pgm_base.cpp:401
char ** m_argvUtf8
Definition: pgm_base.h:352
void BuildArgvUtf8()
Builds the UTF8 based argv variable.
Definition: pgm_base.cpp:386
std::vector< void * > m_ModalDialogs
Definition: pgm_base.h:306
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:135
void SaveCommonSettings()
Save the program (process) settings subset which are stored .kicad_common.
Definition: pgm_base.cpp:609
PGM_KICAD extends PGM_BASE to bring in FileHistory() and PdfBrowser() which were moved from EDA_APP i...
Definition: pgm_kicad.h:38
bool OnPgmInit()
Definition: kicad.cpp:94
void Destroy()
Definition: kicad.cpp:396
void MacOpenFile(const wxString &aFileName) override
Specific to MacOSX (not used under Linux or Windows).
Definition: kicad.cpp:383
void OnPgmExit()
Definition: kicad.cpp:366
APP_SETTINGS_BASE * PgmSettings()
Definition: pgm_kicad.h:55
int OnPgmRun()
Definition: kicad.cpp:360
BIN_MOD m_bm
Definition: pgm_kicad.h:68
Container for project specific data.
Definition: project.h:64
T * RegisterSettings(T *aSettings, bool aLoadNow=true)
Takes ownership of the pointer passed in.
void SetKiway(KIWAY *aKiway)
Associate this setting manager with the given Kiway.
#define KICAD_CLI_APP_NAME
Definition: cli_names.h:23
#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.
static CLI::EXPORT_PCB_POS_COMMAND exportPcbPosCmd
Definition: kicad_cli.cpp:130
PGM_KICAD & PgmTop()
Definition: kicad_cli.cpp:107
static CLI::EXPORT_PCB_PDF_COMMAND exportPcbPdfCmd
Definition: kicad_cli.cpp:129
static CLI::EXPORT_PCB_DXF_COMMAND exportPcbDxfCmd
Definition: kicad_cli.cpp:126
static CLI::EXPORT_SCH_COMMAND exportSchCmd
Definition: kicad_cli.cpp:135
static CLI::FP_EXPORT_SVG_COMMAND fpExportSvgCmd
Definition: kicad_cli.cpp:143
static CLI::SYM_UPGRADE_COMMAND symUpgradeCmd
Definition: kicad_cli.cpp:148
static CLI::FP_EXPORT_COMMAND fpExportCmd
Definition: kicad_cli.cpp:142
static CLI::FP_UPGRADE_COMMAND fpUpgradeCmd
Definition: kicad_cli.cpp:144
static void printHelp(argparse::ArgumentParser &argParser)
Definition: kicad_cli.cpp:252
static CLI::EXPORT_PCB_GERBER_COMMAND exportPcbGerberCmd
Definition: kicad_cli.cpp:131
static CLI::EXPORT_PCB_COMMAND exportPcbCmd
Definition: kicad_cli.cpp:133
static CLI::EXPORT_SCH_NETLIST_COMMAND exportSchNetlistCmd
Definition: kicad_cli.cpp:138
static CLI::EXPORT_PCB_DRILL_COMMAND exportPcbDrillCmd
Definition: kicad_cli.cpp:125
static CLI::SYM_COMMAND symCmd
Definition: kicad_cli.cpp:145
PROJECT & Prj()
Definition: kicad_cli.cpp:587
static CLI::EXPORT_PCB_GERBERS_COMMAND exportPcbGerbersCmd
Definition: kicad_cli.cpp:132
static CLI::EXPORT_PCB_STEP_COMMAND exportPcbStepCmd
Definition: kicad_cli.cpp:127
static CLI::EXPORT_PCB_SVG_COMMAND exportPcbSvgCmd
Definition: kicad_cli.cpp:128
static PGM_KICAD program
Definition: kicad_cli.cpp:91
static CLI::EXPORT_SCH_PYTHONBOM_COMMAND exportSchPythonBomCmd
Definition: kicad_cli.cpp:137
static CLI::SCH_COMMAND schCmd
Definition: kicad_cli.cpp:136
static CLI::PCB_COMMAND pcbCmd
Definition: kicad_cli.cpp:134
static std::vector< COMMAND_ENTRY > commandStack
Definition: kicad_cli.cpp:152
static CLI::SYM_EXPORT_COMMAND symExportCmd
Definition: kicad_cli.cpp:146
static CLI::SYM_EXPORT_SVG_COMMAND symExportSvgCmd
Definition: kicad_cli.cpp:147
static COMMAND_ENTRY * recurseArgParserSubCommandUsed(argparse::ArgumentParser &aArgParser, COMMAND_ENTRY &aEntry)
Definition: kicad_cli.cpp:230
static CLI::EXPORT_SCH_PDF_COMMAND exportSchPdfCmd
Definition: kicad_cli.cpp:139
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad_cli.cpp:94
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
Definition: kicad_cli.cpp:80
static CLI::VERSION_COMMAND versionCmd
Definition: kicad_cli.cpp:149
PGM_BASE * PgmOrNull()
similar to PGM_BASE& Pgm(), but return a reference that can be nullptr when running a shared lib from...
Definition: kicad_cli.cpp:101
static CLI::EXPORT_SCH_SVG_COMMAND exportSchSvgCmd
Definition: kicad_cli.cpp:140
static void recurseArgParserBuild(argparse::ArgumentParser &aArgParser, COMMAND_ENTRY &aEntry)
Definition: kicad_cli.cpp:219
static CLI::FP_COMMAND fpCmd
Definition: kicad_cli.cpp:141
#define KFCTL_CPP_PROJECT_SUITE
Running under C++ project mgr, possibly with others.
Definition: kiway.h:159
KIWAY Kiway
#define KFCTL_CLI
Running as CLI app.
Definition: kiway.h:160
This file contains miscellaneous commonly used macros and functions.
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
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: gtk/app.cpp:40
void Init()
Perform environment initialization tasks.
Not publicly visible because most of the action is in PGM_KICAD these days.
Definition: kicad_cli.cpp:441
int OnExit() override
Definition: kicad_cli.cpp:464
bool OnInit() override
Definition: kicad_cli.cpp:449
int FilterEvent(wxEvent &aEvent) override
Definition: kicad_cli.cpp:499
int OnRun() override
Definition: kicad_cli.cpp:476
void End()
Definition: bin_mod.cpp:50
void Init()
Definition: bin_mod.cpp:38
void InitSettings(APP_SETTINGS_BASE *aPtr)
Takes ownership of a new application settings object.
Definition: bin_mod.h:53
Definition: kicad_cli.cpp:114
COMMAND_ENTRY(CLI::COMMAND *aHandler)
Definition: kicad_cli.cpp:119
COMMAND_ENTRY(CLI::COMMAND *aHandler, std::vector< COMMAND_ENTRY > aSub)
Definition: kicad_cli.cpp:120
CLI::COMMAND * handler
Definition: kicad_cli.cpp:115
std::vector< COMMAND_ENTRY > subCommands
Definition: kicad_cli.cpp:117
System directories search utilities.
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
wxLogTrace helper definitions.