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 The 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#include <wx/msgdlg.h>
33#include <wx/filefn.h>
34
35wxDEFINE_EVENT( wxEVT_THREAD_STDIN, wxThreadEvent );
36wxDEFINE_EVENT( wxEVT_THREAD_STDERR, wxThreadEvent );
37
42class STDSTREAM_THREAD : public wxThread
43{
44public:
45 STDSTREAM_THREAD( wxEvtHandler* aEventHandler, wxProcess* aProcess,
46 wxMessageQueue<DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE>& aMsgQueue ) :
47 wxThread( wxTHREAD_JOINABLE ),
48 m_queue( aMsgQueue )
49 {
50 m_process = aProcess;
51 m_handler = aEventHandler;
52 m_bufferSize = 1024 * 1024;
53 m_buffer = new char[m_bufferSize];
54 }
55
57 {
58 delete[] m_buffer;
59 }
60
61private:
62 ExitCode Entry() override;
63 void DrainInput();
64
65 wxMessageQueue<DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE>& m_queue;
66 wxEvtHandler* m_handler;
67 wxProcess* m_process;
68 char* m_buffer;
70};
71
72
73wxThread::ExitCode STDSTREAM_THREAD::Entry()
74{
75 ExitCode c;
76
77 while( 1 )
78 {
79 // Check if termination was requested.
80 if( TestDestroy() )
81 {
82 wxProcess::Kill( m_process->GetPid(), wxSIGKILL );
83 c = reinterpret_cast<ExitCode>( 1 );
84 break;
85 }
86
88 wxMessageQueueError e = m_queue.ReceiveTimeout( 10, m );
89
90 // Check if a message was received or we timed out.
91 if( e == wxMSGQUEUE_NO_ERROR )
92 {
94 {
95 DrainInput();
96 c = reinterpret_cast<ExitCode>( 0 );
97 break;
98 }
100 {
101 wxProcess::Kill( m_process->GetPid(), wxSIGKILL );
102 c = reinterpret_cast<ExitCode>( 1 );
103 break;
104 }
105 }
106 else if( e == wxMSGQUEUE_TIMEOUT )
107 {
108 DrainInput();
109 }
110 }
111
112 return c;
113}
114
115
117{
118 if( !m_process->IsInputOpened() )
119 {
120 return;
121 }
122
123 wxString fromInputStream, fromErrorStream;
124 wxInputStream* stream;
125
126 while( m_process->IsInputAvailable() )
127 {
128 stream = m_process->GetInputStream();
129 stream->Read( m_buffer, m_bufferSize );
130 fromInputStream << wxString( m_buffer, stream->LastRead() );
131 }
132
133 while( m_process->IsErrorAvailable() )
134 {
135 stream = m_process->GetErrorStream();
136 stream->Read( m_buffer, m_bufferSize );
137 fromErrorStream << wxString( m_buffer, stream->LastRead() );
138 }
139
140 if( !fromInputStream.IsEmpty() )
141 {
142 wxThreadEvent* event = new wxThreadEvent( wxEVT_THREAD_STDIN );
143 event->SetString( fromInputStream );
144 m_handler->QueueEvent( event );
145 }
146
147 if( !fromErrorStream.IsEmpty() )
148 {
149 wxThreadEvent* event = new wxThreadEvent( wxEVT_THREAD_STDERR );
150 event->SetString( fromErrorStream );
151 m_handler->QueueEvent( event );
152 }
153}
154
155
157{
158 // Ensure we start with a clean log window message
159 m_textCtrlLog->Clear();
160 m_textCtrlLog->AppendText( m_startMessage );
161 return true;
162}
163
164
165void DIALOG_EXPORT_STEP_LOG::appendMessage( const wxString& aMessage )
166{
167 m_textCtrlLog->AppendText( aMessage );
168}
169
170
171void DIALOG_EXPORT_STEP_LOG::onProcessTerminate( wxProcessEvent& aEvent )
172{
173 // We need to inform the thread that the process has died
174 // Since it can't receive the event from wx
175 if( m_stdioThread && m_stdioThread->IsRunning() )
176 {
178 m_stdioThread->Wait();
179 delete m_stdioThread;
180 m_stdioThread = nullptr;
181 m_sdbSizerOK->Enable( true );
182
183 int exitCode = aEvent.GetExitCode();
184
185 // set the progress bar to complete/incomplete base don status
186 m_activityGauge->SetRange( 1 );
187
188 if( exitCode != 0 )
189 {
190 m_textCtrlLog->SetForegroundColour( *wxRED );
191
192 m_textCtrlLog->AppendText( wxS( "\n*** " ) );
193 m_textCtrlLog->AppendText(
194 wxString::Format( _( "Process failed with exit code %d" ), exitCode ) );
195 m_textCtrlLog->AppendText( wxS( " ***\n" ) );
196
197 m_activityGauge->SetValue( 0 );
198 }
199 else
200 {
201 m_textCtrlLog->AppendText( wxS( "\n*** " ) );
202 m_textCtrlLog->AppendText( wxString::Format( _( "Success" ) ) );
203 m_textCtrlLog->AppendText( wxS( " ***\n" ) );
204
205 m_activityGauge->SetValue( 1 );
206 }
207 }
208
209 // The child has exited; it is safe to remove any temp inputs we were keeping alive for it.
211}
212
213
214void DIALOG_EXPORT_STEP_LOG::SetTempFilesToCleanup( std::vector<wxString> aPaths )
215{
216 m_tempFiles = std::move( aPaths );
217}
218
219
221{
223 return;
224
225 for( const wxString& path : m_tempFiles )
226 {
227 if( !path.IsEmpty() && wxFileExists( path ) )
228 wxRemoveFile( path );
229 }
230
231 m_tempFilesCleaned = true;
232}
233
234void DIALOG_EXPORT_STEP_LOG::onThreadInput( wxThreadEvent& aEvent )
235{
236 m_textCtrlLog->AppendText( aEvent.GetString() );
237 m_activityGauge->Pulse();
238}
239
240
241void DIALOG_EXPORT_STEP_LOG::onClose( wxCloseEvent& aEvent )
242{
243 if( m_stdioThread && m_stdioThread->IsRunning() )
244 {
245 if( aEvent.CanVeto() )
246 {
247 wxMessageDialog dlg( this, _( "Do you want to cancel the export process?" ),
248 _( "Cancel Export" ), wxYES_NO );
249
250 if( dlg.ShowModal() == wxID_NO )
251 {
252 aEvent.Veto();
253 return;
254 }
255 }
256
258 m_stdioThread->Delete();
259
260 m_process->DeletePendingEvents();
261 m_process->Unlink();
262 m_process->CloseOutput();
263 m_process->Detach();
264 }
265
266 // Best-effort cleanup. If the child was detached above and is still reading the temp files
267 // on Windows, wxRemoveFile will fail and leave them behind; unique filenames per export
268 // keep that from interfering with future runs.
270
271 // Clear log window message, storing the log data in config has no interest.
272 m_textCtrlLog->Clear();
273
274 aEvent.Skip();
275}
276
282
283
284DIALOG_EXPORT_STEP_LOG::DIALOG_EXPORT_STEP_LOG( wxWindow* aParent, const wxString& aStepCmd ) :
286{
287 m_sdbSizerOK->Enable( false );
288
289 m_process = new wxProcess( this );
290 m_process->Redirect();
291
292 Bind( wxEVT_END_PROCESS, &DIALOG_EXPORT_STEP_LOG::onProcessTerminate, this );
293
294 Bind( wxEVT_THREAD_STDIN, &DIALOG_EXPORT_STEP_LOG::onThreadInput, this );
295 Bind( wxEVT_THREAD_STDERR, &DIALOG_EXPORT_STEP_LOG::onThreadInput, this );
296 Bind( wxEVT_CLOSE_WINDOW, &DIALOG_EXPORT_STEP_LOG::onClose, this );
297
298 // Print the command line used to run kicad-cli.
299 // it can be useful if kicad-cli as a problem.
300 // However it cannot be printed in the Ctor, but only in TransferDataToWindow(),
301 // after DIALOG_SHIM initializations
302 m_startMessage.Append( _( "Command line:\n" ) );
303 m_startMessage.Append( aStepCmd );
304 m_startMessage.Append( wxT( "\n\n" ) );
305
307 m_stdioThread->Run();
308
309 if( !m_stdioThread->IsRunning() )
310 {
311 m_startMessage.Append( "Unable to launch stdstream thread.\n" );
312 delete m_stdioThread;
313 return;
314 }
315
316 m_activityGauge->Pulse();
317
318 wxExecute( aStepCmd, wxEXEC_ASYNC, m_process );
319}
void appendMessage(const wxString &aMessage)
void SetTempFilesToCleanup(std::vector< wxString > aPaths)
Register files that should be removed once the subprocess is known to have terminated (or been repare...
std::vector< wxString > m_tempFiles
DIALOG_EXPORT_STEP_LOG(wxWindow *aParent, const 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
DIALOG_EXPORT_STEP_PROCESS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("3D Export"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
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)
std::string path