KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_export_step_process.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) 2023 Kicad Developers, see AUTHORS.txt for contributors.
5 * Copyright (C) 2020 New Pagodi(https://stackoverflow.com/users/6846682/new-pagodi)
6 * from https://stackoverflow.com/a/63289812/1522001
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27#include <wx/textctrl.h>
28#include <wx/process.h>
29#include <wx/log.h>
30#include <wx/timer.h>
31#include <wx/txtstrm.h>
32
33wxDEFINE_EVENT( wxEVT_THREAD_STDIN, wxThreadEvent );
34wxDEFINE_EVENT( wxEVT_THREAD_STDERR, wxThreadEvent );
35
40class STDSTREAM_THREAD : public wxThread
41{
42public:
43 STDSTREAM_THREAD( wxEvtHandler* aEventHandler, wxProcess* aProcess,
44 wxMessageQueue<DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE>& aMsgQueue ) :
45 wxThread( wxTHREAD_JOINABLE ),
46 m_queue( aMsgQueue )
47 {
48 m_process = aProcess;
49 m_handler = aEventHandler;
50 m_bufferSize = 1024 * 1024;
51 m_buffer = new char[m_bufferSize];
52 }
53
55 {
56 delete[] m_buffer;
57 }
58
59private:
60 ExitCode Entry() override;
61 void DrainInput();
62
63 wxMessageQueue<DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE>& m_queue;
64 wxEvtHandler* m_handler;
65 wxProcess* m_process;
66 char* m_buffer;
68};
69
70
71wxThread::ExitCode STDSTREAM_THREAD::Entry()
72{
73 ExitCode c;
74
75 while( 1 )
76 {
77 // Check if termination was requested.
78 if( TestDestroy() )
79 {
80 wxProcess::Kill( m_process->GetPid() );
81 c = reinterpret_cast<ExitCode>( 1 );
82 break;
83 }
84
86 wxMessageQueueError e = m_queue.ReceiveTimeout( 10, m );
87
88 // Check if a message was received or we timed out.
89 if( e == wxMSGQUEUE_NO_ERROR )
90 {
92 {
93 DrainInput();
94 c = reinterpret_cast<ExitCode>( 0 );
95 break;
96 }
98 {
99 wxProcess::Kill( m_process->GetPid() );
100 c = reinterpret_cast<ExitCode>( 1 );
101 break;
102 }
103 }
104 else if( e == wxMSGQUEUE_TIMEOUT )
105 {
106 DrainInput();
107 }
108 }
109
110 return c;
111}
112
113
115{
116 if( !m_process->IsInputOpened() )
117 {
118 return;
119 }
120
121 wxString fromInputStream, fromErrorStream;
122 wxInputStream* stream;
123
124 while( m_process->IsInputAvailable() )
125 {
126 stream = m_process->GetInputStream();
127 stream->Read( m_buffer, m_bufferSize );
128 fromInputStream << wxString( m_buffer, stream->LastRead() );
129 }
130
131 while( m_process->IsErrorAvailable() )
132 {
133 stream = m_process->GetErrorStream();
134 stream->Read( m_buffer, m_bufferSize );
135 fromErrorStream << wxString( m_buffer, stream->LastRead() );
136 }
137
138 if( !fromInputStream.IsEmpty() )
139 {
140 wxThreadEvent* event = new wxThreadEvent( wxEVT_THREAD_STDIN );
141 event->SetString( fromInputStream );
142 m_handler->QueueEvent( event );
143 }
144
145 if( !fromErrorStream.IsEmpty() )
146 {
147 wxThreadEvent* event = new wxThreadEvent( wxEVT_THREAD_STDERR );
148 event->SetString( fromErrorStream );
149 m_handler->QueueEvent( event );
150 }
151}
152
153
154void DIALOG_EXPORT_STEP_LOG::appendMessage( const wxString& aMessage )
155{
156 m_textCtrlLog->AppendText( aMessage );
157}
158
159
160void DIALOG_EXPORT_STEP_LOG::onProcessTerminate( wxProcessEvent& aEvent )
161{
162 // We need to inform the thread that the process has died
163 // Since it can't receive the event from wx
164 if( m_stdioThread && m_stdioThread->IsRunning() )
165 {
167 m_stdioThread->Wait();
168 delete m_stdioThread;
169 m_stdioThread = nullptr;
170 m_sdbSizerOK->Enable( true );
171
172 int exitCode = aEvent.GetExitCode();
173
174 // set the progress bar to complete/incomplete base don status
175 m_activityGauge->SetRange( 1 );
176
177 if( exitCode != 0 )
178 {
179 m_textCtrlLog->SetForegroundColour( *wxRED );
180
181 m_textCtrlLog->AppendText( wxS( "\n*** " ) );
182 m_textCtrlLog->AppendText(
183 wxString::Format( _( "Process failed with exit code %d" ), exitCode ) );
184 m_textCtrlLog->AppendText( wxS( " ***\n" ) );
185
186 m_activityGauge->SetValue( 0 );
187 }
188 else
189 {
190 m_textCtrlLog->AppendText( wxS( "\n*** " ) );
191 m_textCtrlLog->AppendText( wxString::Format( _( "Success" ) ) );
192 m_textCtrlLog->AppendText( wxS( " ***\n" ) );
193
194 m_activityGauge->SetValue( 1 );
195 }
196 }
197}
198
199void DIALOG_EXPORT_STEP_LOG::onThreadInput( wxThreadEvent& aEvent )
200{
201 m_textCtrlLog->AppendText( aEvent.GetString() );
202 m_activityGauge->Pulse();
203}
204
205
206void DIALOG_EXPORT_STEP_LOG::onClose( wxCloseEvent& aEvent )
207{
208 if( m_stdioThread && m_stdioThread->IsRunning() )
209 {
211 m_stdioThread->Wait();
212
213 m_process->DeletePendingEvents();
214 m_process->Unlink();
215 m_process->CloseOutput();
216 m_process->Detach();
217
218 m_stdioThread->Delete();
219 }
220
221 aEvent.Skip();
222}
223
225{
226 delete m_stdioThread;
227}
228
229
230DIALOG_EXPORT_STEP_LOG::DIALOG_EXPORT_STEP_LOG( wxWindow* aParent, wxString aStepCmd ) :
232{
233 m_sdbSizerOK->Enable( false );
234
235 m_process = new wxProcess( this );
236 m_process->Redirect();
237
238 Bind( wxEVT_END_PROCESS, &DIALOG_EXPORT_STEP_LOG::onProcessTerminate, this );
239
240 Bind( wxEVT_THREAD_STDIN, &DIALOG_EXPORT_STEP_LOG::onThreadInput, this );
241 Bind( wxEVT_THREAD_STDERR, &DIALOG_EXPORT_STEP_LOG::onThreadInput, this );
242 Bind( wxEVT_CLOSE_WINDOW, &DIALOG_EXPORT_STEP_LOG::onClose, this );
243
244 // Print the command line used to run kicad-cli.
245 // it can be useful if kicad-cli as a problem.
246 m_textCtrlLog->AppendText( _( "Command line:\n" ) );
247 m_textCtrlLog->AppendText( aStepCmd );
248 m_textCtrlLog->AppendText( wxT( "\n\n" ) );
249
251 m_stdioThread->Run();
252
253 if( !m_stdioThread->IsRunning() )
254 {
255 m_textCtrlLog->AppendText( "Unable to launch stdstream thread.\n" );
256 delete m_stdioThread;
257 return;
258 }
259
260 m_activityGauge->Pulse();
261
262 wxExecute( aStepCmd, wxEXEC_ASYNC, m_process );
263}
void appendMessage(const wxString &aMessage)
DIALOG_EXPORT_STEP_LOG(wxWindow *aParent, wxString aStepCmd)
void onProcessTerminate(wxProcessEvent &aEvent)
@ PROCESS_COMPLETE
Informs the thread the process terminate event was received from wx.
@ REQUEST_EXIT
Asks the thread to exit and kill the process.
@ SENTINEL
Just a dummy entry for end of list.
void onClose(wxCloseEvent &event)
wxMessageQueue< STATE_MESSAGE > m_msgQueue
Class DIALOG_EXPORT_STEP_PROCESS_BASE.
This thread handles consuming the input streams from the launched process.
STDSTREAM_THREAD(wxEvtHandler *aEventHandler, wxProcess *aProcess, wxMessageQueue< DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE > &aMsgQueue)
wxMessageQueue< DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE > & m_queue
wxDEFINE_EVENT(wxEVT_THREAD_STDIN, wxThreadEvent)
#define _(s)