KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <mark.roszko@gmail.com>
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 ) :
39 m_kiway( aKiway ),
40 m_jobsFile( aJobsFile ),
41 m_reporter( aReporter ),
42 m_project( aProject )
43{
44 if( !m_reporter )
45 {
47 }
48}
49
50
52{
53 bool success = true;
54
55 for( JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
56 success &= RunJobsForDestination( &destination, aBail );
57
58 return success;
59}
60
61
63{
64 JOB_SPECIAL_EXECUTE* specialJob = static_cast<JOB_SPECIAL_EXECUTE*>( aJob->m_job.get() );
65
66 wxString cmd = ExpandEnvVarSubstitutions( specialJob->m_command, m_project );
67
68 // static cast required because wx uses `long` which is 64-bit on Linux but 32-bit on Windows
69 wxProcess process;
70 process.Redirect();
71
72 int result = static_cast<int>( wxExecute( cmd, wxEXEC_SYNC, &process ) );
73
74 if( specialJob->m_recordOutput )
75 {
76 if( specialJob->GetConfiguredOutputPath().IsEmpty() )
77 {
78 wxFileName fn( aJob->m_id );
79 fn.SetExt( wxT( "log" ) );
80 specialJob->SetConfiguredOutputPath( fn.GetFullPath() );
81 }
82
83 wxFFileOutputStream procOutput( specialJob->GetFullOutputPath( aProject ) );
84
85 if( !procOutput.IsOk() )
87
88 wxInputStream* inputStream = process.GetInputStream();
89 if( inputStream )
90 {
91 inputStream->Read( procOutput );
92 }
93 procOutput.Close();
94 }
95
96 if( specialJob->m_ignoreExitcode )
97 {
99 }
100
101 return result;
102}
103
104
106{
107 JOB_SPECIAL_COPYFILES* job = static_cast<JOB_SPECIAL_COPYFILES*>( aJob->m_job.get() );
108
109 wxString source = ExpandEnvVarSubstitutions( job->m_source, aProject );
110
111 if( source.IsEmpty() )
113
114 wxString projectPath = aProject->GetProjectPath();
115 wxFileName sourceFn( source );
116 sourceFn.MakeAbsolute( projectPath );
117
118 wxFileName destFn( job->GetFullOutputPath( aProject ) );
119
120 if( !job->m_dest.IsEmpty() )
121 destFn.AppendDir( job->m_dest );
122
123 std::vector<wxString> exclusions;
124
125 for( const JOBSET_DESTINATION& destination : m_jobsFile->GetDestinations() )
126 exclusions.push_back( projectPath + destination.m_outputHandler->GetOutputPath() );
127
128 wxString errors;
129 int copyCount = 0;
130 bool success = CopyFilesOrDirectory( sourceFn.GetFullPath(), destFn.GetFullPath(),
131 errors, copyCount, exclusions );
132
133 if( !success )
135
136 if( job->m_generateErrorOnNoCopy && copyCount == 0 )
138
139 return CLI::EXIT_CODES::OK;
140}
141
142
144{
145public:
146 JOBSET_OUTPUT_REPORTER( const wxString& aTempDirPath ) :
147 m_tempDirPath( aTempDirPath )
148 {
149 }
150
151 REPORTER& Report( const wxString& aText, SEVERITY aSeverity ) override
152 {
153 wxString text( aText );
154
155 if( aSeverity == RPT_SEVERITY_ACTION )
156 text.Replace( m_tempDirPath, wxEmptyString );
157
158 return WX_STRING_REPORTER::Report( text, aSeverity );
159 }
160
161private:
163};
164
165
167{
168 bool genOutputs = true;
169 bool success = true;
170 std::vector<JOBSET_JOB> jobsForDestination = m_jobsFile->GetJobsForDestination( aDestination );
171 wxString msg;
172
173 wxFileName tmp;
174 tmp.AssignDir( wxFileName::GetTempDir() );
175 tmp.AppendDir( KIID().AsString() );
176
177 aDestination->m_lastRunSuccessMap.clear();
178
179 for( auto& [name, reporter] : aDestination->m_lastRunReporters )
180 delete reporter;
181
182 aDestination->m_lastRunReporters.clear();
183
184 wxString tempDirPath = tmp.GetFullPath();
185
186 if( !wxFileName::Mkdir( tempDirPath, wxS_DIR_DEFAULT ) )
187 {
188 if( m_reporter )
189 {
190 msg = wxString::Format( wxT( "Failed to create temporary directory %s" ), tempDirPath );
192 }
193
194 aDestination->m_lastRunSuccess = false;
195
196 return false;
197 }
198
199 bool continueOuput = aDestination->m_outputHandler->OutputPrecheck();
200
201 if( !continueOuput )
202 {
203 if( m_reporter )
204 {
205 msg = wxString::Format( wxT( "Output precheck failed for output %s" ),
206 aDestination->m_id );
208 }
209
210 aDestination->m_lastRunSuccess = false;
211 return false;
212 }
213
214 if( m_reporter != nullptr )
215 {
216 msg += wxT( "|--------------------------------\n" );
217 msg += wxT( "| " );
218 msg += wxString::Format( "Performing jobs for output %s", aDestination->m_id );
219 msg += wxT( "\n" );
220 msg += wxT( "|--------------------------------\n" );
221
222 msg += wxString::Format( wxT( "|%-5s | %-50s\n" ), wxT( "No." ), wxT( "Description" ) );
223
224 int jobNum = 1;
225
226 for( const JOBSET_JOB& job : jobsForDestination )
227 {
228 msg += wxString::Format( wxT( "|%-5d | %-50s\n" ), jobNum, job.GetDescription() );
229 jobNum++;
230 }
231
232 msg += wxT( "|--------------------------------\n" );
233 msg += wxT( "\n" );
234 msg += wxT( "\n" );
235
237 }
238
239 std::vector<JOB_OUTPUT> outputs;
240
241 int jobNum = 1;
242 int failCount = 0;
243 int successCount = 0;
244
245 for( const JOBSET_JOB& job : jobsForDestination )
246 {
247 if( m_reporter != nullptr )
248 {
249 msg = wxT( "|--------------------------------\n" );
250
251 msg += wxString::Format( wxT( "| Running job %d, %s" ), jobNum, job.GetDescription() );
252 jobNum++;
253
254 msg += wxT( "\n" );
255 msg += wxT( "|--------------------------------\n" );
256
258 }
259
260 KIWAY::FACE_T iface = JOB_REGISTRY::GetKifaceType( job.m_type );
261
262 job.m_job->SetTempOutputDirectory( tempDirPath );
263
264 REPORTER* reporterToUse = m_reporter;
265
266 if( !reporterToUse || reporterToUse == &NULL_REPORTER::GetInstance() )
267 {
268 reporterToUse = new JOBSET_OUTPUT_REPORTER( tempDirPath );
269 aDestination->m_lastRunReporters[job.m_id] = reporterToUse;
270 }
271
272 int result = CLI::EXIT_CODES::SUCCESS;
273
274 if( iface < KIWAY::KIWAY_FACE_COUNT )
275 {
276 result = m_kiway->ProcessJob( iface, job.m_job.get(), reporterToUse );
277 }
278 else
279 {
280 // special jobs
281 if( job.m_job->GetType() == "special_execute" )
282 {
283 result = runSpecialExecute( &job, m_project );
284 }
285 else if( job.m_job->GetType() == "special_copyfiles" )
286 {
287 result = runSpecialCopyFiles( &job, m_project );
288 }
289 }
290
291 aDestination->m_lastRunSuccessMap[job.m_id] = ( result == CLI::EXIT_CODES::SUCCESS );
292
293 if( m_reporter )
294 {
295 if( result == CLI::EXIT_CODES::SUCCESS )
296 {
297 wxString msg_fmt = wxT( "\033[32;1m%s\033[0m\n" );
298 msg = wxString::Format( msg_fmt, _( "Job successful" ) );
299
300 successCount++;
301 }
302 else
303 {
304 wxString msg_fmt = wxT( "\033[31;1m%s\033[0m\n" );
305 msg = wxString::Format( msg_fmt, _( "Job failed" ) );
306
307 failCount++;
308 }
309
310 msg += wxT( "\n\n" );
312 }
313
315 {
316 success = false;
317
318 if( aBail )
319 break;
320 }
321 else if( result != CLI::EXIT_CODES::SUCCESS )
322 {
323 genOutputs = false;
324 success = false;
325
326 if( aBail )
327 break;
328 }
329 }
330
331 if( genOutputs )
332 success &= aDestination->m_outputHandler->HandleOutputs( tempDirPath, m_project, outputs );
333
334 aDestination->m_lastRunSuccess = success;
335
336 if( m_reporter )
337 {
338 msg = wxString::Format( wxT( "\n\n\033[33;1m%d %s, %d %s\033[0m\n" ),
339 successCount,
340 wxT( "jobs succeeded" ),
341 failCount,
342 wxT( "job failed" ) );
343
345 }
346
347 return success;
348}
const char * name
Definition: DXF_plotter.cpp:59
REPORTER & Report(const wxString &aText, SEVERITY aSeverity) override
Report a string with a given severity.
JOBSET_OUTPUT_REPORTER(const wxString &aTempDirPath)
Definition: jobset.h:106
std::vector< JOBSET_DESTINATION > & GetDestinations()
Definition: jobset.h:119
std::vector< JOBSET_JOB > GetJobsForDestination(JOBSET_DESTINATION *aDestination)
Definition: jobset.cpp:301
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)
Definition: jobs_runner.cpp:51
KIWAY * m_kiway
Definition: jobs_runner.h:44
JOBSET * m_jobsFile
Definition: jobs_runner.h:45
bool RunJobsForDestination(JOBSET_DESTINATION *aDestination, bool aBail=false)
JOBS_RUNNER(KIWAY *aKiway, JOBSET *aJobsFile, PROJECT *aProject, REPORTER *aReporter=nullptr)
Definition: jobs_runner.cpp:37
int runSpecialExecute(const JOBSET_JOB *aJob, PROJECT *aProject)
Definition: jobs_runner.cpp:62
PROJECT * m_project
Definition: jobs_runner.h:47
REPORTER * m_reporter
Definition: jobs_runner.h:46
int runSpecialCopyFiles(const JOBSET_JOB *aJob, PROJECT *aProject)
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:226
Definition: kiid.h:49
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:285
FACE_T
Known KIFACE implementations.
Definition: kiway.h:291
@ KIWAY_FACE_COUNT
Definition: kiway.h:301
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr)
Definition: kiway.cpp:711
static REPORTER & GetInstance()
Definition: reporter.cpp:118
Container for project specific data.
Definition: project.h:64
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:146
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)=0
Report a string with a given severity.
A wrapper for reporting to a wxString object.
Definition: reporter.h:172
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:79
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:351
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
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:1068
SEVERITY
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
std::unordered_map< wxString, REPORTER * > m_lastRunReporters
Definition: jobset.h:98
JOBS_OUTPUT_HANDLER * m_outputHandler
Definition: jobset.h:89
std::optional< bool > m_lastRunSuccess
Definition: jobset.h:96
std::unordered_map< wxString, std::optional< bool > > m_lastRunSuccessMap
Definition: jobset.h:97
wxString m_id
Definition: jobset.h:86
wxString m_id
Definition: jobset.h:46
std::shared_ptr< JOB > m_job
Definition: jobset.h:49