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
59int JOBS_RUNNER::runSpecialExecute( const JOBSET_JOB* aJob, REPORTER* aReporter, PROJECT* aProject )
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 aReporter->Report( cmd, RPT_SEVERITY_INFO );
65 aReporter->Report( wxEmptyString, RPT_SEVERITY_INFO );
66
67 wxProcess process;
68 process.Redirect();
69
70 // static cast required because wx uses `long` which is 64-bit on Linux but 32-bit on Windows
71 int result = static_cast<int>( wxExecute( cmd, wxEXEC_SYNC, &process ) );
72
73 wxInputStream* inputStream = process.GetInputStream();
74 wxInputStream* errorStream = process.GetErrorStream();
75
76 if( inputStream && errorStream )
77 {
78 wxTextInputStream inputTextStream( *inputStream );
79 wxTextInputStream errorTextStream( *errorStream );
80
81 while( !inputStream->Eof() )
82 aReporter->Report( inputTextStream.ReadLine(), RPT_SEVERITY_INFO );
83
84 while( !errorStream->Eof() )
85 aReporter->Report( errorTextStream.ReadLine(), RPT_SEVERITY_ERROR );
86
87 if( specialJob->m_recordOutput )
88 {
89 if( specialJob->GetConfiguredOutputPath().IsEmpty() )
90 {
91 wxFileName fn( aJob->m_id );
92 fn.SetExt( wxT( "log" ) );
93 specialJob->SetConfiguredOutputPath( fn.GetFullPath() );
94 }
95
96 wxFFileOutputStream procOutput( specialJob->GetFullOutputPath( aProject ) );
97
98 if( !procOutput.IsOk() )
100
101 inputStream->Reset();
102 *inputStream >> procOutput;
103 }
104 }
105
106 if( specialJob->m_ignoreExitcode )
107 return CLI::EXIT_CODES::OK;
108
109 return result;
110}
111
112
114{
115 JOB_SPECIAL_COPYFILES* job = static_cast<JOB_SPECIAL_COPYFILES*>( aJob->m_job.get() );
116
117 wxString source = ExpandEnvVarSubstitutions( job->m_source, aProject );
118
119 if( source.IsEmpty() )
121
122 wxString projectPath = aProject->GetProjectPath();
123 wxFileName sourceFn( source );
124 sourceFn.MakeAbsolute( projectPath );
125
126 wxFileName destFn( job->GetFullOutputPath( aProject ) );
127
128 if( !job->m_dest.IsEmpty() )
129 destFn.AppendDir( job->m_dest );
130
131 std::vector<wxString> exclusions;
132
133 for( const JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
134 exclusions.push_back( projectPath + destination.m_outputHandler->GetOutputPath() );
135
136 wxString errors;
137 int copyCount = 0;
138 bool success = CopyFilesOrDirectory( sourceFn.GetFullPath(), destFn.GetFullPath(),
139 errors, copyCount, exclusions );
140
141 if( !success )
143
144 if( job->m_generateErrorOnNoCopy && copyCount == 0 )
146
147 return CLI::EXIT_CODES::OK;
148}
149
150
152{
153 bool genOutputs = true;
154 bool success = true;
155 std::vector<JOBSET_JOB> jobsForDestination = m_jobsFile->GetJobsForDestination( aDestination );
156 wxString msg;
157
158 wxFileName tmp;
159 tmp.AssignDir( wxFileName::GetTempDir() );
160 tmp.AppendDir( KIID().AsString() );
161
162 aDestination->m_lastRunSuccessMap.clear();
163 aDestination->m_lastRunReporters.clear();
164
165 wxString tempDirPath = tmp.GetFullPath();
166
167 if( !wxFileName::Mkdir( tempDirPath, wxS_DIR_DEFAULT ) )
168 {
169 msg = wxString::Format( wxT( "Failed to create temporary directory %s" ), tempDirPath );
170 m_reporter.Report( msg, RPT_SEVERITY_ERROR );
171
172 aDestination->m_lastRunSuccess = false;
173
174 return false;
175 }
176
177 bool continueOuput = aDestination->m_outputHandler->OutputPrecheck();
178
179 if( !continueOuput )
180 {
181 msg = wxString::Format( wxT( "Destination precheck failed for destination %s" ),
182 aDestination->m_id );
183 m_reporter.Report( msg, RPT_SEVERITY_ERROR );
184
185 aDestination->m_lastRunSuccess = false;
186 return false;
187 }
188
189 msg += wxT( "|--------------------------------\n" );
190 msg += wxT( "| " );
191 msg += wxString::Format( wxT( "Running jobs for destination %s" ), aDestination->m_id );
192 msg += wxT( "\n" );
193 msg += wxT( "|--------------------------------\n" );
194
195 msg += wxString::Format( wxT( "|%-5s | %-50s\n" ), wxT( "No." ), wxT( "Description" ) );
196
197 int jobNum = 1;
198
199 for( const JOBSET_JOB& job : jobsForDestination )
200 {
201 msg += wxString::Format( wxT( "|%-5d | %-50s\n" ), jobNum, job.GetDescription() );
202 jobNum++;
203 }
204
205 msg += wxT( "|--------------------------------\n" );
206 msg += wxT( "\n" );
207 msg += wxT( "\n" );
208
209 m_reporter.Report( msg, RPT_SEVERITY_INFO );
210
211 std::vector<JOB_OUTPUT> outputs;
212
213 jobNum = 1;
214 int failCount = 0;
215 int successCount = 0;
216
217 wxSetEnv( OUTPUT_TMP_PATH_VAR_NAME, tempDirPath );
218
219 for( const JOBSET_JOB& job : jobsForDestination )
220 {
221 msg = wxT( "|--------------------------------\n" );
222
223 msg += wxString::Format( wxT( "| Running job %d: %s" ), jobNum, job.GetDescription() );
224
225 msg += wxT( "\n" );
226 msg += wxT( "|--------------------------------\n" );
227
228 m_reporter.Report( msg, RPT_SEVERITY_INFO );
229
231 {
232 msg.Printf( _( "Running job %d: %s" ), jobNum, job.GetDescription() );
233 m_progressReporter->AdvanceJob( msg );
234 m_progressReporter->KeepRefreshing();
235 }
236
237 jobNum++;
238
239 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
240
241 job.m_job->SetTempOutputDirectory( tempDirPath );
242
243 REPORTER* targetReporter = &m_reporter;
244
245 if( targetReporter == &NULL_REPORTER::GetInstance() )
246 {
247 aDestination->m_lastRunReporters[job.m_id] =
248 std::make_shared<JOBSET_OUTPUT_REPORTER>( tempDirPath, m_progressReporter );
249
250 targetReporter = aDestination->m_lastRunReporters[job.m_id].get();
251 }
252
253 // Use a redirect reporter so we don't have error flags set after running previous jobs
254 REDIRECT_REPORTER isolatedReporter( targetReporter );
256
257 if( iface < KIWAY::KIWAY_FACE_COUNT )
258 {
259 result = m_kiway->ProcessJob( iface, job.m_job.get(), &isolatedReporter,
261 }
262 else
263 {
264 // special jobs
265 if( job.m_job->GetType() == "special_execute" )
266 {
267 result = runSpecialExecute( &job, &isolatedReporter, m_project );
268 }
269 else if( job.m_job->GetType() == "special_copyfiles" )
270 {
272 }
273 }
274
275 aDestination->m_lastRunSuccessMap[job.m_id] = ( result == CLI::EXIT_CODES::SUCCESS );
276
278 {
279 wxString msg_fmt = wxT( "\033[32;1m%s\033[0m\n" );
280 msg = wxString::Format( msg_fmt, _( "Job successful" ) );
281
282 successCount++;
283 }
284 else
285 {
286 wxString msg_fmt = wxT( "\033[31;1m%s\033[0m\n" );
287 msg = wxString::Format( msg_fmt, _( "Job failed" ) );
288
289 failCount++;
290 }
291
292 msg += wxT( "\n\n" );
293 m_reporter.Report( msg, RPT_SEVERITY_INFO );
294
296 {
297 success = false;
298
299 if( aBail )
300 break;
301 }
302 else if( result != CLI::EXIT_CODES::SUCCESS )
303 {
304 genOutputs = false;
305 success = false;
306
307 if( aBail )
308 break;
309 }
310 }
311
312 wxUnsetEnv( OUTPUT_TMP_PATH_VAR_NAME );
313
314 if( genOutputs )
315 success &= aDestination->m_outputHandler->HandleOutputs( tempDirPath, m_project, outputs );
316
317 aDestination->m_lastRunSuccess = success;
318
319 msg = wxString::Format( wxT( "\n\n\033[33;1m%d %s, %d %s\033[0m\n" ),
320 successCount,
321 wxT( "jobs succeeded" ),
322 failCount,
323 wxT( "job failed" ) );
324
325 m_reporter.Report( msg, RPT_SEVERITY_INFO );
326
327 return success;
328}
virtual bool OutputPrecheck()
Checks if the output process can proceed before doing anything else This can include user prompts.
Definition jobs_output.h:44
virtual bool HandleOutputs(const wxString &aBaseTempPath, PROJECT *aProject, const std::vector< JOB_OUTPUT > &aOutputsToHandle)=0
bool RunJobsAllDestinations(bool aBail=false)
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)
PROJECT * m_project
Definition jobs_runner.h:78
int runSpecialCopyFiles(const JOBSET_JOB *aJob, PROJECT *aProject)
int runSpecialExecute(const JOBSET_JOB *aJob, REPORTER *aReporter, PROJECT *aProject)
JOBS_RUNNER(KIWAY *aKiway, JOBSET *aJobsFile, PROJECT *aProject, REPORTER &aReporter, JOBS_PROGRESS_REPORTER *aProgressReporter)
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:232
Definition kiid.h:49
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:286
FACE_T
Known KIFACE implementations.
Definition kiway.h:292
@ KIWAY_FACE_COUNT
Definition kiway.h:302
static REPORTER & GetInstance()
Definition reporter.cpp:96
Container for project specific data.
Definition project.h:65
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:162
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
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:355
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:910
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
std::unordered_map< wxString, std::shared_ptr< JOBSET_OUTPUT_REPORTER > > m_lastRunReporters
Definition jobset.h:139
std::shared_ptr< JOBS_OUTPUT_HANDLER > m_outputHandler
Definition jobset.h:133
std::optional< bool > m_lastRunSuccess
Definition jobset.h:137
std::unordered_map< wxString, std::optional< bool > > m_lastRunSuccessMap
Definition jobset.h:138
wxString m_id
Definition jobset.h:130
wxString m_id
Definition jobset.h:84
std::shared_ptr< JOB > m_job
Definition jobset.h:87
wxString result
Test unit parsing edge cases and error handling.