KiCad PCB EDA Suite
Loading...
Searching...
No Matches
jobs_runner.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) 2024 Mark Roszko <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <common.h>
22#include <cli/exit_codes.h>
23#include <jobs_runner.h>
24#include <jobs/job_registry.h>
25#include <jobs/jobset.h>
28#include <kiway.h>
29#include <kiway_express.h>
30#include <reporter.h>
31#include <wx/process.h>
32#include <wx/txtstrm.h>
33#include <wx/sstream.h>
34#include <wx/wfstream.h>
35#include <gestfich.h>
36
37JOBS_RUNNER::JOBS_RUNNER( KIWAY* aKiway, JOBSET* aJobsFile, PROJECT* aProject,
38 REPORTER& aReporter, JOBS_PROGRESS_REPORTER* aProgressReporter ) :
39 m_kiway( aKiway ),
40 m_jobsFile( aJobsFile ),
41 m_reporter( aReporter ),
42 m_progressReporter( aProgressReporter ),
43 m_project( aProject )
44{
45}
46
47
49{
50 bool success = true;
51
52 for( JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
53 success &= RunJobsForDestination( &destination, aBail );
54
55 return success;
56}
57
58
60{
61 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( aJob->m_job.get() );
62 wxString cmd = ExpandEnvVarSubstitutions( specialJob->m_command, m_project );
63
64 // static cast required because wx uses `long` which is 64-bit on Linux but 32-bit on Windows
65 wxProcess process;
66 process.Redirect();
67
68 int result = static_cast<int>( wxExecute( cmd, wxEXEC_SYNC, &process ) );
69
70 if( specialJob->m_recordOutput )
71 {
72 if( specialJob->GetConfiguredOutputPath().IsEmpty() )
73 {
74 wxFileName fn( aJob->m_id );
75 fn.SetExt( wxT( "log" ) );
76 specialJob->SetConfiguredOutputPath( fn.GetFullPath() );
77 }
78
79 wxFFileOutputStream procOutput( specialJob->GetFullOutputPath( aProject ) );
80
81 if( !procOutput.IsOk() )
83
84 wxInputStream* inputStream = process.GetInputStream();
85
86 if( inputStream )
87 inputStream->Read( procOutput );
88
89 procOutput.Close();
90 }
91
92 if( specialJob->m_ignoreExitcode )
94
95 return result;
96}
97
98
100{
101 JOB_SPECIAL_COPYFILES* job = static_cast<JOB_SPECIAL_COPYFILES*>( aJob->m_job.get() );
102
103 wxString source = ExpandEnvVarSubstitutions( job->m_source, aProject );
104
105 if( source.IsEmpty() )
107
108 wxString projectPath = aProject->GetProjectPath();
109 wxFileName sourceFn( source );
110 sourceFn.MakeAbsolute( projectPath );
111
112 wxFileName destFn( job->GetFullOutputPath( aProject ) );
113
114 if( !job->m_dest.IsEmpty() )
115 destFn.AppendDir( job->m_dest );
116
117 std::vector<wxString> exclusions;
118
119 for( const JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
120 exclusions.push_back( projectPath + destination.m_outputHandler->GetOutputPath() );
121
122 wxString errors;
123 int copyCount = 0;
124 bool success = CopyFilesOrDirectory( sourceFn.GetFullPath(), destFn.GetFullPath(),
125 errors, copyCount, exclusions );
126
127 if( !success )
129
130 if( job->m_generateErrorOnNoCopy && copyCount == 0 )
132
133 return CLI::EXIT_CODES::OK;
134}
135
136
138{
139public:
140 JOBSET_OUTPUT_REPORTER( const wxString& aTempDirPath, PROGRESS_REPORTER* aProgressReporter ) :
141 m_tempDirPath( aTempDirPath ),
142 m_includeDebug( false ),
143 m_progressReporter( aProgressReporter )
144 {
145 }
146
147 REPORTER& Report( const wxString& aText, SEVERITY aSeverity ) override
148 {
149 wxString text( aText );
150
151 if( aSeverity == RPT_SEVERITY_DEBUG && !m_includeDebug )
152 return *this;
153
154 if( aSeverity == RPT_SEVERITY_ACTION )
155 text.Replace( m_tempDirPath, wxEmptyString );
156
158 {
161 }
162
163 return WX_STRING_REPORTER::Report( text, aSeverity );
164 }
165
166private:
170};
171
172
174{
175 bool genOutputs = true;
176 bool success = true;
177 std::vector<JOBSET_JOB> jobsForDestination = m_jobsFile->GetJobsForDestination( aDestination );
178 wxString msg;
179
180 wxFileName tmp;
181 tmp.AssignDir( wxFileName::GetTempDir() );
182 tmp.AppendDir( KIID().AsString() );
183
184 aDestination->m_lastRunSuccessMap.clear();
185
186 for( auto& [name, reporter] : aDestination->m_lastRunReporters )
187 delete reporter;
188
189 aDestination->m_lastRunReporters.clear();
190
191 wxString tempDirPath = tmp.GetFullPath();
192
193 if( !wxFileName::Mkdir( tempDirPath, wxS_DIR_DEFAULT ) )
194 {
195 msg = wxString::Format( wxT( "Failed to create temporary directory %s" ), tempDirPath );
197
198 aDestination->m_lastRunSuccess = false;
199
200 return false;
201 }
202
203 bool continueOuput = aDestination->m_outputHandler->OutputPrecheck();
204
205 if( !continueOuput )
206 {
207 msg = wxString::Format( wxT( "Destination precheck failed for destination %s" ),
208 aDestination->m_id );
210
211 aDestination->m_lastRunSuccess = false;
212 return false;
213 }
214
215 msg += wxT( "|--------------------------------\n" );
216 msg += wxT( "| " );
217 msg += wxString::Format( wxT( "Running jobs for destination %s" ), aDestination->m_id );
218 msg += wxT( "\n" );
219 msg += wxT( "|--------------------------------\n" );
220
221 msg += wxString::Format( wxT( "|%-5s | %-50s\n" ), wxT( "No." ), wxT( "Description" ) );
222
223 int jobNum = 1;
224
225 for( const JOBSET_JOB& job : jobsForDestination )
226 {
227 msg += wxString::Format( wxT( "|%-5d | %-50s\n" ), jobNum, job.GetDescription() );
228 jobNum++;
229 }
230
231 msg += wxT( "|--------------------------------\n" );
232 msg += wxT( "\n" );
233 msg += wxT( "\n" );
234
236
237 std::vector<JOB_OUTPUT> outputs;
238
239 jobNum = 1;
240 int failCount = 0;
241 int successCount = 0;
242
243 wxSetEnv( OUTPUT_TMP_PATH_VAR_NAME, tempDirPath );
244
245 for( const JOBSET_JOB& job : jobsForDestination )
246 {
247 msg = wxT( "|--------------------------------\n" );
248
249 msg += wxString::Format( wxT( "| Running job %d: %s" ), jobNum, job.GetDescription() );
250
251 msg += wxT( "\n" );
252 msg += wxT( "|--------------------------------\n" );
253
255
257 {
258 msg.Printf( _( "Running job %d: %s" ), jobNum, job.GetDescription() );
261 }
262
263 jobNum++;
264
265 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
266
267 job.m_job->SetTempOutputDirectory( tempDirPath );
268
269 REPORTER* reporterToUse = &m_reporter;
270
271 if( reporterToUse == &NULL_REPORTER::GetInstance() )
272 {
273 reporterToUse = new JOBSET_OUTPUT_REPORTER( tempDirPath, m_progressReporter );
274 aDestination->m_lastRunReporters[job.m_id] = reporterToUse;
275 }
276
277 int result = CLI::EXIT_CODES::SUCCESS;
278
279
280 if( iface < KIWAY::KIWAY_FACE_COUNT )
281 {
282 result = m_kiway->ProcessJob( iface, job.m_job.get(), reporterToUse, m_progressReporter );
283 }
284 else
285 {
286 // special jobs
287 if( job.m_job->GetType() == "special_execute" )
288 {
289 result = runSpecialExecute( &job, m_project );
290 }
291 else if( job.m_job->GetType() == "special_copyfiles" )
292 {
293 result = runSpecialCopyFiles( &job, m_project );
294 }
295 }
296
297 aDestination->m_lastRunSuccessMap[job.m_id] = ( result == CLI::EXIT_CODES::SUCCESS );
298
299 if( result == CLI::EXIT_CODES::SUCCESS )
300 {
301 wxString msg_fmt = wxT( "\033[32;1m%s\033[0m\n" );
302 msg = wxString::Format( msg_fmt, _( "Job successful" ) );
303
304 successCount++;
305 }
306 else
307 {
308 wxString msg_fmt = wxT( "\033[31;1m%s\033[0m\n" );
309 msg = wxString::Format( msg_fmt, _( "Job failed" ) );
310
311 failCount++;
312 }
313
314 msg += wxT( "\n\n" );
316
318 {
319 success = false;
320
321 if( aBail )
322 break;
323 }
324 else if( result != CLI::EXIT_CODES::SUCCESS )
325 {
326 genOutputs = false;
327 success = false;
328
329 if( aBail )
330 break;
331 }
332 }
333
334 wxUnsetEnv( OUTPUT_TMP_PATH_VAR_NAME );
335
336 if( genOutputs )
337 success &= aDestination->m_outputHandler->HandleOutputs( tempDirPath, m_project, outputs );
338
339 aDestination->m_lastRunSuccess = success;
340
341 msg = wxString::Format( wxT( "\n\n\033[33;1m%d %s, %d %s\033[0m\n" ),
342 successCount,
343 wxT( "jobs succeeded" ),
344 failCount,
345 wxT( "job failed" ) );
346
348
349 return success;
350}
const char * name
Definition: DXF_plotter.cpp:62
REPORTER & Report(const wxString &aText, SEVERITY aSeverity) override
Report a string with a given severity.
PROGRESS_REPORTER * m_progressReporter
JOBSET_OUTPUT_REPORTER(const wxString &aTempDirPath, PROGRESS_REPORTER *aProgressReporter)
Definition: jobset.h:108
std::vector< JOBSET_DESTINATION > & GetDestinations()
Definition: jobset.h:121
std::vector< JOBSET_JOB > GetJobsForDestination(JOBSET_DESTINATION *aDestination)
Definition: jobset.cpp:316
void AdvanceJob(const wxString &aMessage)
Definition: jobs_runner.h:44
bool RunJobsAllDestinations(bool aBail=false)
Definition: jobs_runner.cpp:48
REPORTER & m_reporter
Definition: jobs_runner.h:76
KIWAY * m_kiway
Definition: jobs_runner.h:74
JOBSET * m_jobsFile
Definition: jobs_runner.h:75
JOBS_PROGRESS_REPORTER * m_progressReporter
Definition: jobs_runner.h:77
bool RunJobsForDestination(JOBSET_DESTINATION *aDestination, bool aBail=false)
int runSpecialExecute(const JOBSET_JOB *aJob, PROJECT *aProject)
Definition: jobs_runner.cpp:59
PROJECT * m_project
Definition: jobs_runner.h:78
int runSpecialCopyFiles(const JOBSET_JOB *aJob, PROJECT *aProject)
Definition: jobs_runner.cpp:99
JOBS_RUNNER(KIWAY *aKiway, JOBSET *aJobsFile, PROJECT *aProject, REPORTER &aReporter, JOBS_PROGRESS_REPORTER *aProgressReporter)
Definition: jobs_runner.cpp:37
static KIWAY::FACE_T GetKifaceType(const wxString &aName)
void SetConfiguredOutputPath(const wxString &aPath)
Sets the configured output path for the job, this path is always saved to file.
Definition: job.cpp:153
wxString GetFullOutputPath(PROJECT *aProject) const
Returns the full output path for the job, taking into account the configured output path,...
Definition: job.cpp:100
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
Definition: job.h:227
Definition: kiid.h:49
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:286
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition: kiway.cpp:678
FACE_T
Known KIFACE implementations.
Definition: kiway.h:292
@ KIWAY_FACE_COUNT
Definition: kiway.h:302
static REPORTER & GetInstance()
Definition: reporter.cpp:108
bool KeepRefreshing(bool aWait=false) override
Update the UI dialog.
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
Container for project specific data.
Definition: project.h:65
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:148
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition: reporter.h:102
A wrapper for reporting to a wxString object.
Definition: reporter.h:190
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:74
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:353
The common library.
#define _(s)
bool CopyFilesOrDirectory(const wxString &aSourcePath, const wxString &aDestDir, wxString &aErrors, int &aFileCopiedCount, const std::vector< wxString > &aExclusions)
Definition: gestfich.cpp:411
#define OUTPUT_TMP_PATH_VAR_NAME
static const int ERR_ARGS
Definition: exit_codes.h:31
static const int OK
Definition: exit_codes.h:30
static const int ERR_RC_VIOLATIONS
Rules check violation count was greater than 0.
Definition: exit_codes.h:37
static const int SUCCESS
Definition: exit_codes.h:29
static const int ERR_INVALID_OUTPUT_CONFLICT
Definition: exit_codes.h:34
static const int ERR_UNKNOWN
Definition: exit_codes.h:32
static PGM_BASE * process
Definition: pgm_base.cpp:890
SEVERITY
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_DEBUG
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
std::unordered_map< wxString, REPORTER * > m_lastRunReporters
Definition: jobset.h:100
std::shared_ptr< JOBS_OUTPUT_HANDLER > m_outputHandler
Definition: jobset.h:89
std::optional< bool > m_lastRunSuccess
Definition: jobset.h:98
std::unordered_map< wxString, std::optional< bool > > m_lastRunSuccessMap
Definition: jobset.h:99
wxString m_id
Definition: jobset.h:86
wxString m_id
Definition: jobset.h:46
std::shared_ptr< JOB > m_job
Definition: jobset.h:49