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