1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3 * http://www.gnu.org/licenses/gpl-3.0.html
4 *
5 * $Revision: 11886 $
6 * $Id: directcommands.cpp 11886 2019-10-26 09:12:03Z fuscated $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/compilergcc/directcommands.cpp $
8 */
9
10 #include <sdk.h>
11 #include <wx/intl.h>
12 #include <wx/filename.h>
13 #include <wx/msgdlg.h>
14 #include <wx/stream.h>
15 #include <wx/wfstream.h>
16 #include <wx/txtstrm.h>
17 #include <wx/regex.h> // used in QUICK hack at line 574
18 #include <compiler.h>
19 #include <cbproject.h>
20 #include <projectbuildtarget.h>
21 #include <globals.h>
22 #include <manager.h>
23 #include <logmanager.h>
24 #include <configmanager.h>
25 #include <macrosmanager.h>
26 #include "directcommands.h"
27 #include "compilercommandgenerator.h"
28 #include "compilergcc.h"
29 #include "cbexception.h"
30 #include "filefilters.h"
31 #include <depslib.h>
32
33 const wxString COMPILER_SIMPLE_LOG(_T("SLOG:"));
34 const wxString COMPILER_NOTE_LOG(_T("SLOG:NLOG:"));
35 const wxString COMPILER_WARNING_LOG(_T("SLOG:WLOG:"));
36 const wxString COMPILER_ERROR_LOG(_T("SLOG:ELOG:"));
37 const wxString COMPILER_TARGET_CHANGE(_T("TGT:"));
38 const wxString COMPILER_WAIT(_T("WAIT"));
39 const wxString COMPILER_WAIT_LINK(_T("LINK"));
40
41 const wxString COMPILER_NOTE_ID_LOG = COMPILER_NOTE_LOG.AfterFirst(wxT(':'));
42 const wxString COMPILER_WARNING_ID_LOG = COMPILER_WARNING_LOG.AfterFirst(wxT(':'));
43 const wxString COMPILER_ERROR_ID_LOG = COMPILER_ERROR_LOG.AfterFirst(wxT(':'));
44
DirectCommands(CompilerGCC * compilerPlugin,Compiler * compiler,cbProject * project,int logPageIndex)45 DirectCommands::DirectCommands(CompilerGCC* compilerPlugin,
46 Compiler* compiler,
47 cbProject* project,
48 int logPageIndex) :
49 m_doYield(false),
50 m_PageIndex(logPageIndex),
51 m_pCompilerPlugin(compilerPlugin),
52 m_pCompiler(compiler),
53 m_pProject(project),
54 m_pGenerator(0)
55 {
56 // even if there is no project, the command generator need to be
57 // initialised for single file compilation to work.
58 // it can handle a NULL pointer argument... ;-)
59 m_pGenerator = m_pCompiler->GetCommandGenerator(m_pProject);
60
61 // ctor
62 if (!m_pProject)
63 return; // probably a compile file cmd without a project
64
65 depsStart();
66 wxFileName cwd;
67 cwd.Assign(m_pProject->GetBasePath());
68 // depslib does special handling on Windows in case the CWD is a root
69 // folder like "R:". But this ONLY works, if its just "R:", NOT e.g. "R:/"
70 wxString depsCWD = cwd.GetPath(wxPATH_GET_VOLUME);
71 Manager::Get()->GetLogManager()->DebugLog(F(_("CWD for depslib was: %s."), depsCWD.wx_str()));
72 if ( (depsCWD.Len()==3) && (depsCWD.GetChar(1)==':')
73 && ( (depsCWD.GetChar(2)=='\\') || (depsCWD.GetChar(2)=='/') ) )
74 {
75 depsCWD.RemoveLast();
76 }
77 Manager::Get()->GetLogManager()->DebugLog(F(_("CWD for depslib is: %s."), depsCWD.wx_str()));
78 depsSetCWD(depsCWD.mb_str());
79
80 wxFileName fname(m_pProject->GetFilename());
81 fname.SetExt(_T("depend"));
82 depsCacheRead(fname.GetFullPath().mb_str());
83 }
84
~DirectCommands()85 DirectCommands::~DirectCommands()
86 {
87 if (!m_pProject)
88 return; // probably a compile file cmd without a project
89
90 struct depsStats stats;
91 depsGetStats(&stats);
92 if (stats.cache_updated)
93 {
94 wxFileName fname(m_pProject->GetFilename());
95 fname.SetExt(_T("depend"));
96 depsCacheWrite(fname.GetFullPath().mb_str());
97 }
98
99 Manager::Get()->GetLogManager()->DebugLog(
100 F(_("Scanned %ld files for #includes, cache used %ld, cache updated %ld"),
101 stats.scanned, stats.cache_used, stats.cache_updated));
102
103 depsDone();
104
105 delete m_pGenerator;
106 }
107
AddCommandsToArray(const wxString & cmds,wxArrayString & array,bool isWaitCmd,bool isLinkCmd) const108 void DirectCommands::AddCommandsToArray(const wxString& cmds, wxArrayString& array, bool isWaitCmd, bool isLinkCmd) const
109 {
110 wxString cmd = cmds;
111 while (!cmd.IsEmpty())
112 {
113 int idx = cmd.Find(_T("\n"));
114 wxString cmdpart = idx != -1 ? cmd.Left(idx) : cmd;
115 cmdpart.Trim(false);
116 cmdpart.Trim(true);
117 if (!cmdpart.IsEmpty())
118 {
119 if (isWaitCmd)
120 array.Add(COMPILER_WAIT);
121 if (isLinkCmd)
122 array.Add(COMPILER_WAIT_LINK);
123 array.Add(cmdpart);
124 }
125 if (idx == -1)
126 break;
127 cmd.Remove(0, idx + 1);
128 }
129 }
130
MySortProjectFilesByWeight(ProjectFile ** one,ProjectFile ** two)131 static int MySortProjectFilesByWeight(ProjectFile** one, ProjectFile** two)
132 {
133 int diff = (*one)->weight - (*two)->weight;
134 diff = (diff == 0 ? (*one)->relativeFilename.CmpNoCase((*two)->relativeFilename) : diff);
135 return (diff == 0 ? (*one)->relativeFilename.Cmp((*two)->relativeFilename) : diff);
136 }
137
GetProjectFilesSortedByWeight(ProjectBuildTarget * target,bool compile,bool link) const138 MyFilesArray DirectCommands::GetProjectFilesSortedByWeight(ProjectBuildTarget* target, bool compile, bool link) const
139 {
140 MyFilesArray files;
141 for (FilesList::iterator it = m_pProject->GetFilesList().begin(); it != m_pProject->GetFilesList().end(); ++it)
142 {
143 ProjectFile* pf = *it;
144 // require compile
145 if (compile && !pf->compile)
146 continue;
147 // require link
148 if (link && !pf->link)
149 continue;
150 // if the file does not belong in this target (if we have a target), skip it
151 if (target && (pf->buildTargets.Index(target->GetTitle()) == wxNOT_FOUND))
152 continue;
153 files.Add(pf);
154 }
155 files.Sort(MySortProjectFilesByWeight);
156 return files;
157 }
158
CompileFile(ProjectBuildTarget * target,ProjectFile * pf,bool force) const159 wxArrayString DirectCommands::CompileFile(ProjectBuildTarget* target, ProjectFile* pf, bool force) const
160 {
161 wxArrayString ret;
162
163 // is it compilable?
164 if (!pf || !pf->compile || pf->compilerVar.IsEmpty())
165 return ret;
166
167 // might happen for single file compilation if user chose a target the file does NOT belong to:
168 if (target && pf->GetBuildTargets().Index(target->GetTitle()) == wxNOT_FOUND)
169 {
170 Manager::Get()->GetLogManager()->DebugLog(_("Invalid target selected to compile project file for: File does not belong to this target."));
171 return ret;
172 }
173
174 if (!force)
175 {
176 DepsSearchStart(target);
177
178 const pfDetails& pfd = pf->GetFileDetails(target);
179 wxString err;
180 if ( !IsObjectOutdated(target, pfd, &err) )
181 {
182 if ( !err.IsEmpty() )
183 ret.Add(COMPILER_WARNING_LOG + err);
184 return ret;
185 }
186 }
187
188 if (target)
189 ret.Add(COMPILER_TARGET_CHANGE + target->GetTitle());
190 AppendArray(GetCompileFileCommand(target, pf), ret);
191 return ret;
192 }
193
GetCompileFileCommand(ProjectBuildTarget * target,ProjectFile * pf) const194 wxArrayString DirectCommands::GetCompileFileCommand(ProjectBuildTarget* target, ProjectFile* pf) const
195 {
196 wxArrayString ret;
197 wxArrayString ret_generated;
198
199 // is it compilable?
200 if (!pf || !pf->compile)
201 return ret;
202
203 if (pf->compilerVar.IsEmpty())
204 {
205 Manager::Get()->GetLogManager()->DebugLog(_("Cannot resolve compiler var for project file."));
206 return ret;
207 }
208
209 Compiler* compiler = target
210 ? CompilerFactory::GetCompiler(target->GetCompilerID())
211 : m_pCompiler;
212 if (!compiler)
213 {
214 Manager::Get()->GetLogManager()->DebugLog(_("Can't access compiler for file."));
215 return ret;
216 }
217
218 const pfDetails& pfd = pf->GetFileDetails(target);
219 wxString object = (compiler->GetSwitches().UseFlatObjects)
220 ? pfd.object_file_flat : pfd.object_file;
221 wxString object_dir = (compiler->GetSwitches().UseFlatObjects)
222 ? pfd.object_dir_flat_native : pfd.object_dir_native;
223 // create output dir
224 if (!object_dir.IsEmpty() && !CreateDirRecursively(object_dir, 0755))
225 Manager::Get()->GetLogManager()->DebugLog(_("Can't create object output directory:\n") + object_dir);
226
227 // lookup file's type
228 const FileType ft = FileTypeOf(pf->relativeFilename);
229
230 bool is_resource = ft == ftResource;
231 bool is_header = ft == ftHeader;
232
233 // allowed resources under all platforms: makes sense when cross-compiling for
234 // windows under linux.
235 // and anyway, if the user is dumb enough to try to compile resources without
236 // having a resource compiler, (s)he deserves the upcoming build error ;)
237
238 wxString compiler_cmd;
239 if (!is_header || compiler->GetSwitches().supportsPCH)
240 {
241 const CompilerTool* tool = compiler->GetCompilerTool(is_resource ? ctCompileResourceCmd : ctCompileObjectCmd, pf->file.GetExt());
242
243 // does it generate other files to compile?
244 for (size_t i = 0; i < pf->generatedFiles.size(); ++i)
245 AppendArray(GetCompileFileCommand(target, pf->generatedFiles[i]), ret_generated); // recurse
246
247 pfCustomBuild& pcfb = pf->customBuild[compiler->GetID()];
248 if (pcfb.useCustomBuildCommand)
249 compiler_cmd = pcfb.buildCommand;
250 else if (tool)
251 compiler_cmd = tool->command;
252 else
253 compiler_cmd = wxEmptyString;
254
255 wxString source_file;
256 if (compiler->GetSwitches().UseFullSourcePaths)
257 source_file = UnixFilename(pfd.source_file_absolute_native);
258 else
259 source_file = pfd.source_file;
260
261 #ifdef command_line_generation
262 Manager::Get()->GetLogManager()->DebugLog(F(_T("GetCompileFileCommand[1]: compiler_cmd='%s', source_file='%s', object='%s', object_dir='%s'."),
263 compiler_cmd.wx_str(), source_file.wx_str(), object.wx_str(), object_dir.wx_str()));
264 #endif
265
266 // for resource files, use short from if path because if windres bug with spaces-in-paths
267 if (is_resource && compiler->GetSwitches().UseFullSourcePaths)
268 source_file = pf->file.GetShortPath();
269
270 QuoteStringIfNeeded(source_file);
271
272 #ifdef command_line_generation
273 Manager::Get()->GetLogManager()->DebugLog(F(_T("GetCompileFileCommand[2]: source_file='%s'."),
274 source_file.wx_str()));
275 #endif
276 m_pGenerator->GenerateCommandLine(compiler_cmd, target, pf, source_file, object,
277 pfd.object_file_flat, pfd.dep_file);
278 }
279
280 if (!is_header && compiler_cmd.IsEmpty())
281 {
282 ret.Add(COMPILER_SIMPLE_LOG + _("Skipping file (no compiler program set): ") + pfd.source_file_native );
283 return ret;
284 }
285
286 switch (compiler->GetSwitches().logging)
287 {
288 case clogFull:
289 ret.Add(COMPILER_SIMPLE_LOG + compiler_cmd);
290 break;
291
292 case clogSimple:
293 if (is_header)
294 ret.Add(COMPILER_SIMPLE_LOG + _("Pre-compiling header: ") + pfd.source_file_native );
295 else
296 ret.Add(COMPILER_SIMPLE_LOG + _("Compiling: ") + pfd.source_file_native );
297 break;
298
299 case clogNone: // fall-through
300 default:
301 break;
302 }
303
304 AddCommandsToArray(compiler_cmd, ret);
305
306 if (is_header)
307 ret.Add(COMPILER_WAIT);
308
309 if (ret_generated.GetCount())
310 {
311 // not only append commands for (any) generated files to be compiled
312 // but also insert a "pause" to allow this file to generate its files first
313 if (!is_header) // if is_header, the "pause" has already been added
314 ret.Add(COMPILER_WAIT);
315 AppendArray(ret_generated, ret);
316 }
317
318 // if it's a PCH, delete the previously generated PCH to avoid problems
319 // (it 'll be recreated anyway)
320 if ( (ft == ftHeader) && pf->compile )
321 {
322 wxString object_abs = (compiler->GetSwitches().UseFlatObjects)
323 ? pfd.object_file_flat_absolute_native
324 : pfd.object_file_absolute_native;
325
326 if ( wxFileExists(object_abs) && !wxRemoveFile(object_abs) )
327 Manager::Get()->GetLogManager()->DebugLog(_("Cannot remove old PCH file:\n") + object_abs);
328 }
329
330 return ret;
331 }
332
333 /// This is to be used *only* for files not belonging to a project!!!
GetCompileSingleFileCommand(const wxString & filename) const334 wxArrayString DirectCommands::GetCompileSingleFileCommand(const wxString& filename) const
335 {
336 wxArrayString ret;
337
338 // lookup file's type
339 FileType ft = FileTypeOf(filename);
340
341 // is it compilable?
342 if (ft != ftSource)
343 return ret;
344
345 wxFileName fname(filename);
346 fname.SetExt(m_pCompiler->GetSwitches().objectExtension);
347 wxString o_filename = fname.GetFullPath();
348 wxString srcExt = fname.GetExt();
349 fname.SetExt(FileFilters::EXECUTABLE_EXT);
350 wxString exe_filename = fname.GetFullPath();
351
352 wxString s_filename = filename;
353 QuoteStringIfNeeded(s_filename);
354 QuoteStringIfNeeded(o_filename);
355
356 Compiler* compiler = CompilerFactory::GetDefaultCompiler();
357 if (!compiler)
358 return ret;
359
360 // please leave this check here for convenience: single file compilation is "special"
361 if (!m_pGenerator) cbThrow(_T("Command generator not initialised through ctor!"));
362
363 wxString compilerCmd = compiler->GetCommand(ctCompileObjectCmd, srcExt);
364 CompilerCommandGenerator::Result compilerResult(&compilerCmd);
365 CompilerCommandGenerator::Params compilerParams;
366 compilerParams.file = s_filename;
367 compilerParams.object = o_filename;
368 compilerParams.flatObject = o_filename;
369 m_pGenerator->GenerateCommandLine(compilerResult, compilerParams);
370
371 wxString linkerCmd = compiler->GetCommand(ctLinkConsoleExeCmd, fname.GetExt());
372 CompilerCommandGenerator::Result linkerResult(&linkerCmd);
373 CompilerCommandGenerator::Params linkerParams;
374 linkerParams.object = o_filename;
375 linkerParams.flatObject = o_filename;
376 linkerParams.hasCppFilesToLink = compilerResult.processedCppFile;
377 m_pGenerator->GenerateCommandLine(linkerResult, linkerParams);
378
379 if (!compilerCmd.IsEmpty())
380 {
381 switch (m_pCompiler->GetSwitches().logging)
382 {
383 case clogFull:
384 ret.Add(COMPILER_SIMPLE_LOG + compilerCmd);
385 break;
386
387 case clogSimple:
388 ret.Add(COMPILER_SIMPLE_LOG + _("Compiling: ") + filename);
389 break;
390
391 case clogNone: // fall-through
392 default:
393 break;
394 }
395 AddCommandsToArray(compilerCmd, ret);
396 }
397 else
398 ret.Add(COMPILER_SIMPLE_LOG + _("Skipping file (no compiler program set): ") + filename);
399
400 if (!linkerCmd.IsEmpty())
401 {
402 switch (m_pCompiler->GetSwitches().logging)
403 {
404 case clogFull:
405 ret.Add(COMPILER_SIMPLE_LOG + linkerCmd);
406 break;
407
408 case clogSimple: // fall-through
409 case clogNone: // fall-through
410 default: // linker always simple log (if not full)
411 ret.Add(COMPILER_SIMPLE_LOG + _("Linking console executable: ") + exe_filename);
412 break;
413 }
414 AddCommandsToArray(linkerCmd, ret, true);
415 }
416 else
417 ret.Add(COMPILER_SIMPLE_LOG + _("Skipping linking (no linker program set): ") + exe_filename);
418 return ret;
419 }
420
421 /// This is to be used *only* for files not belonging to a project!!!
GetCleanSingleFileCommand(const wxString & filename) const422 wxArrayString DirectCommands::GetCleanSingleFileCommand(const wxString& filename) const
423 {
424 wxArrayString ret;
425
426 // lookup file's type
427 FileType ft = FileTypeOf(filename);
428
429 // is it compilable?
430 if (ft != ftSource)
431 return ret;
432
433 wxFileName fname(filename);
434 fname.SetExt(m_pCompiler->GetSwitches().objectExtension);
435 wxString o_filename = fname.GetFullPath();
436 fname.SetExt(FileFilters::EXECUTABLE_EXT);
437 wxString exe_filename = fname.GetFullPath();
438
439 ret.Add(o_filename);
440 ret.Add(exe_filename);
441
442 return ret;
443 }
444
GetCompileCommands(ProjectBuildTarget * target,bool force) const445 wxArrayString DirectCommands::GetCompileCommands(ProjectBuildTarget* target, bool force) const
446 {
447 wxArrayString ret;
448
449 if (target)
450 ret = GetTargetCompileCommands(target, force);
451 else
452 {
453 for (int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x)
454 {
455 ProjectBuildTarget* bt = m_pProject->GetBuildTarget(x);
456 if (bt->GetIncludeInTargetAll()) // only if target gets build with "all"
457 {
458 wxArrayString targetcompile = GetTargetCompileCommands(bt, force);
459 AppendArray(targetcompile, ret);
460 }
461 }
462 }
463 return ret;
464 }
465
GetTargetCompileCommands(ProjectBuildTarget * target,bool force) const466 wxArrayString DirectCommands::GetTargetCompileCommands(ProjectBuildTarget* target, bool force) const
467 {
468 wxArrayString ret;
469
470 // set list of #include directories
471 DepsSearchStart(target);
472
473 // iterate all files of the project/target and add them to the build process
474 size_t counter = ret.GetCount();
475 MyFilesArray files = GetProjectFilesSortedByWeight(target, true, false);
476 size_t fcount = files.GetCount();
477 bool hasWeight = false;
478 unsigned short int lastWeight = 0;
479 for (unsigned int i = 0; i < fcount; ++i)
480 {
481 ProjectFile* pf = files[i];
482 // auto-generated files are handled automatically in GetCompileFileCommand()
483 if (pf->AutoGeneratedBy())
484 continue;
485
486 const pfDetails& pfd = pf->GetFileDetails(target);
487 wxString err;
488 if (force || IsObjectOutdated(target, pfd, &err))
489 {
490 // Add a wait command if the weight of the current file is different from the previous one
491 // Because GetCompileFileCommand() already adds a wait command if it compiled a PCH we
492 // check the last command to prevent two consecutive wait commands
493 if (hasWeight && lastWeight != pf->weight && (ret.IsEmpty() || ret.Last() != COMPILER_WAIT))
494 ret.Add(COMPILER_WAIT);
495
496 // compile file
497 wxArrayString filecmd = GetCompileFileCommand(target, pf);
498 AppendArray(filecmd, ret);
499
500 // Update the weight
501 if (!hasWeight)
502 hasWeight = true;
503 lastWeight = pf->weight;
504 }
505 else
506 {
507 if (!err.IsEmpty())
508 ret.Add(COMPILER_WARNING_LOG + err);
509 }
510 if (m_doYield)
511 Manager::Yield();
512 }
513
514 // add link command
515 wxArrayString link = GetLinkCommands(target, ret.GetCount() != counter);
516 AppendArray(link, ret);
517
518 return ret;
519 }
520
GetPreBuildCommands(ProjectBuildTarget * target) const521 wxArrayString DirectCommands::GetPreBuildCommands(ProjectBuildTarget* target) const
522 {
523 Compiler* compiler = target ? CompilerFactory::GetCompiler(target->GetCompilerID()) : m_pCompiler;
524 wxArrayString buildcmds = target ? target->GetCommandsBeforeBuild() : m_pProject->GetCommandsBeforeBuild();
525 if (!buildcmds.IsEmpty())
526 {
527 wxString title = target ? target->GetTitle() : m_pProject->GetTitle();
528 wxArrayString tmp;
529 for (size_t i = 0; i < buildcmds.GetCount(); ++i)
530 {
531 if (compiler)
532 {
533 if (target)
534 {
535 m_pGenerator->GenerateCommandLine(buildcmds[i], target, 0, wxEmptyString,
536 wxEmptyString, wxEmptyString, wxEmptyString);
537 }
538 else
539 {
540 m_pGenerator->GenerateCommandLine(buildcmds[i],
541 m_pProject->GetCurrentlyCompilingTarget(), 0,
542 wxEmptyString, wxEmptyString, wxEmptyString,
543 wxEmptyString);
544 }
545 }
546
547 tmp.Add(COMPILER_WAIT); // all commands should wait for queue to empty first
548 tmp.Add(COMPILER_SIMPLE_LOG + buildcmds[i]);
549 tmp.Add(buildcmds[i]);
550 }
551 buildcmds = tmp;
552 if (target)
553 buildcmds.Insert(COMPILER_SIMPLE_LOG + _("Running target pre-build steps"), 0);
554 else
555 buildcmds.Insert(COMPILER_SIMPLE_LOG + _("Running project pre-build steps"), 0);
556 if (m_doYield)
557 Manager::Yield();
558 }
559 return buildcmds;
560 }
561
GetPostBuildCommands(ProjectBuildTarget * target) const562 wxArrayString DirectCommands::GetPostBuildCommands(ProjectBuildTarget* target) const
563 {
564 Compiler* compiler = target ? CompilerFactory::GetCompiler(target->GetCompilerID()) : m_pCompiler;
565 wxArrayString buildcmds = target ? target->GetCommandsAfterBuild() : m_pProject->GetCommandsAfterBuild();
566 if (!buildcmds.IsEmpty())
567 {
568 wxString title = target ? target->GetTitle() : m_pProject->GetTitle();
569 wxArrayString tmp;
570 for (size_t i = 0; i < buildcmds.GetCount(); ++i)
571 {
572 if (compiler)
573 {
574 CompilerCommandGenerator::Result result(&buildcmds[i]);
575 CompilerCommandGenerator::Params params;
576 params.target = (target ? target : m_pProject->GetCurrentlyCompilingTarget());
577 m_pGenerator->GenerateCommandLine(result, params);
578 }
579
580 tmp.Add(COMPILER_WAIT); // all commands should wait for queue to empty first
581 tmp.Add(COMPILER_SIMPLE_LOG + buildcmds[i]);
582 tmp.Add(buildcmds[i]);
583 }
584 buildcmds = tmp;
585 if (target)
586 buildcmds.Insert(COMPILER_SIMPLE_LOG + _("Running target post-build steps"), 0);
587 else
588 buildcmds.Insert(COMPILER_SIMPLE_LOG + _("Running project post-build steps"), 0);
589 if (m_doYield)
590 Manager::Yield();
591 }
592 return buildcmds;
593 }
594
GetLinkCommands(ProjectBuildTarget * target,bool force) const595 wxArrayString DirectCommands::GetLinkCommands(ProjectBuildTarget* target, bool force) const
596 {
597 wxArrayString ret;
598
599 if (target)
600 ret = GetTargetLinkCommands(target, force);
601 else
602 {
603 for (int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x)
604 {
605 ProjectBuildTarget* bt = m_pProject->GetBuildTarget(x);
606 if (bt->GetIncludeInTargetAll()) // only if target gets build with "all"
607 {
608 wxArrayString targetlink = GetTargetLinkCommands(bt, force);
609 AppendArray(targetlink, ret);
610 }
611 }
612 }
613 return ret;
614 }
615
GetTargetLinkCommands(ProjectBuildTarget * target,bool force) const616 wxArrayString DirectCommands::GetTargetLinkCommands(ProjectBuildTarget* target, bool force) const
617 {
618 wxArrayString ret;
619
620 wxString output = target->GetOutputFilename();
621 Manager::Get()->GetMacrosManager()->ReplaceMacros(output, target);
622
623 wxFileName out = UnixFilename(output);
624 wxString linkfiles;
625 wxString FlatLinkFiles;
626 wxString resfiles;
627 bool IsOpenWatcom = target->GetCompilerID().IsSameAs(_T("ow"));
628
629 time_t outputtime;
630 depsTimeStamp(output.mb_str(), &outputtime);
631 if (!outputtime)
632 force = true;
633 wxArrayString fileMissing;
634 if ( AreExternalDepsOutdated(target, out.GetFullPath(), &fileMissing) )
635 force = true;
636
637 if (!fileMissing.IsEmpty())
638 {
639 wxString warn;
640 #ifdef NO_TRANSLATION
641 warn.Printf(wxT("WARNING: Target '%s': Unable to resolve %lu external dependenc%s:"),
642 target->GetFullTitle().wx_str(), static_cast<unsigned long>(fileMissing.Count()), wxString(fileMissing.Count() == 1 ? wxT("y") : wxT("ies")).wx_str());
643 #else
644 warn.Printf(_("WARNING: Target '%s': Unable to resolve %lu external dependency/ies:"),
645 target->GetFullTitle().wx_str(), static_cast<unsigned long>(fileMissing.Count()));
646 #endif // NO_TRANSLATION
647 ret.Add(COMPILER_WARNING_LOG + warn);
648 for (size_t i = 0; i < fileMissing.Count(); ++i)
649 ret.Add(COMPILER_NOTE_LOG + wxString(wxT(' '), 8) + fileMissing[i]);
650 }
651
652 Compiler* compiler = target ? CompilerFactory::GetCompiler(target->GetCompilerID()) : m_pCompiler;
653
654 wxString prependHack; // part of the following hack
655 if (target->GetTargetType() == ttStaticLib)
656 {
657 // QUICK HACK: some linkers (e.g. bcc, dmc) require a - or + in front of
658 // object files for static library. What we 'll do here until we redesign
659 // the thing, is to accept this symbol as part of the $link_objects macro
660 // like this:
661 // $+link_objects
662 // $-link_objects
663 // $-+link_objects
664 // $+-link_objects
665 //
666 // So, we first scan the command for this special case and, if found,
667 // set a flag so that the linkfiles array is filled with the correct options
668 wxString compilerCmd = compiler ? compiler->GetCommand(ctLinkStaticCmd) : wxString(wxEmptyString);
669 wxRegEx re(_T("\\$([-+]+)link_objects"));
670 if (re.Matches(compilerCmd))
671 prependHack = re.GetMatch(compilerCmd, 1);
672 }
673
674 // get all the linkable objects for the target
675 MyFilesArray files = GetProjectFilesSortedByWeight(target, false, true);
676 if (files.GetCount() == 0)
677 {
678 if (target->GetTargetType() != ttCommandsOnly)
679 ret.Add(COMPILER_SIMPLE_LOG + _("Linking stage skipped (build target has no object files to link)"));
680 return ret;
681 }
682 if (IsOpenWatcom && target->GetTargetType() != ttStaticLib)
683 linkfiles << _T("file ");
684 bool subseq(false);
685 bool hasCppFilesToLink = false;
686 for (unsigned int i = 0; i < files.GetCount(); ++i)
687 {
688 ProjectFile* pf = files[i];
689
690 // we have to test again for each file if it is to be compiled
691 // and we can't check the file for existence because we 're still
692 // generating the command lines that will create the files...
693 wxString macro = _T("$compiler");
694 CompilerCommandGenerator::Result result(¯o);
695 CompilerCommandGenerator::Params params;
696 params.target = target;
697 params.pf = pf;
698 m_pGenerator->GenerateCommandLine(result, params);
699 if (macro.IsEmpty())
700 continue;
701
702 if (result.processedCppFile)
703 hasCppFilesToLink = true;
704
705 const pfDetails& pfd = pf->GetFileDetails(target);
706 wxString Object = (compiler->GetSwitches().UseFlatObjects) ? pfd.object_file_flat
707 : pfd.object_file;
708
709 if (FileTypeOf(pf->relativeFilename) == ftResource)
710 {
711 if (subseq)
712 resfiles << _T(" ");
713 // -----------------------------------------
714 // Following lines have been modified for OpenWatcom
715 if (IsOpenWatcom)
716 resfiles << _T("option resource=") << Object;
717 else
718 resfiles << Object;
719 // ------------------------------------------
720 }
721 else
722 {
723 // -----------------------------------------
724 // Following lines have been modified for OpenWatcom
725 if (IsOpenWatcom && target->GetTargetType() == ttStaticLib)
726 {
727 if (subseq)
728 {
729 linkfiles << _T(" ");
730 FlatLinkFiles << _T(" ");
731 }
732 linkfiles << prependHack << Object; // see QUICK HACK above (prependHack)
733 FlatLinkFiles << prependHack << pfd.object_file_flat; // see QUICK HACK above (prependHack)
734 }
735 else
736 {
737 if (subseq)
738 {
739 linkfiles << compiler->GetSwitches().objectSeparator;
740 FlatLinkFiles << compiler->GetSwitches().objectSeparator;
741 }
742 linkfiles << prependHack << Object; // see QUICK HACK above (prependHack)
743 FlatLinkFiles << prependHack << pfd.object_file_flat; // see QUICK HACK above (prependHack)
744 }
745 // -----------------------------------------
746 }
747 subseq = true;
748
749 // timestamp check
750 if (!force)
751 {
752 time_t objtime;
753 depsTimeStamp(pfd.object_file_native.mb_str(), &objtime);
754 // Honor compiler request to Use Flat Objects
755 // (Settings/compiler/otherSettings/advancedOptions/Others/UseFlatObjects)
756 if (compiler->GetSwitches().UseFlatObjects)
757 depsTimeStamp(pfd.object_file_flat.mb_str(), &objtime);
758
759 if (!objtime)
760 force = true;
761 if (objtime > outputtime)
762 force = true;
763 }
764 }
765 if (IsOpenWatcom)
766 {
767 linkfiles.Trim();
768 }
769
770 if (!force)
771 return ret;
772
773 // create output dir
774 out.MakeAbsolute(m_pProject->GetBasePath());
775 wxString dstname = out.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
776 Manager::Get()->GetMacrosManager()->ReplaceMacros(dstname, target);
777 if (!dstname.IsEmpty() && !CreateDirRecursively(dstname, 0755))
778 {
779 cbMessageBox(_("Can't create output directory ") + dstname);
780 }
781
782 // add actual link command
783 wxString kind_of_output;
784 CommandType ct = ctCount; // get rid of compiler warning
785 switch (target->GetTargetType())
786 {
787 case ttConsoleOnly:
788 ct = ctLinkConsoleExeCmd;
789 kind_of_output = _("console executable");
790 break;
791
792 case ttExecutable:
793 ct = ctLinkExeCmd;
794 kind_of_output = _("executable");
795 break;
796
797 case ttDynamicLib:
798 ct = ctLinkDynamicCmd;
799 kind_of_output = _("dynamic library");
800 break;
801
802 case ttStaticLib:
803 ct = ctLinkStaticCmd;
804 kind_of_output = _("static library");
805 break;
806
807 case ttNative:
808 ct = ctLinkNativeCmd;
809 kind_of_output = _("native");
810 break;
811
812 case ttCommandsOnly:
813 // add target post-build commands
814 ret.Clear();
815 AppendArray(GetPostBuildCommands(target), ret);
816 return ret;
817 break;
818 default:
819 wxString ex;
820 ex.Printf(_T("Encountered invalid TargetType (value = %d)"), target->GetTargetType());
821 cbThrow(ex);
822 break;
823 }
824
825 // Linker command
826 wxString compilerCmd = compiler->GetCommand(ct);
827 CompilerCommandGenerator::Result result(&compilerCmd);
828 CompilerCommandGenerator::Params params;
829 params.target = target;
830 params.object = linkfiles;
831 params.flatObject = FlatLinkFiles;
832 params.deps = resfiles;
833 params.hasCppFilesToLink = hasCppFilesToLink;
834 m_pGenerator->GenerateCommandLine(result, params);
835
836 if (!compilerCmd.IsEmpty())
837 {
838 switch (compiler->GetSwitches().logging)
839 {
840 case clogFull:
841 ret.Add(COMPILER_SIMPLE_LOG + compilerCmd);
842 break;
843
844 case clogSimple: // fall-through
845 case clogNone: // fall-through
846 default: // linker always simple log (if not full)
847 ret.Add(COMPILER_SIMPLE_LOG + _("Linking ") + kind_of_output + _T(": ") + output);
848 break;
849 }
850
851 // for an explanation of the following, see GetTargetCompileCommands()
852 if (target && ret.GetCount() != 0)
853 ret.Add(COMPILER_TARGET_CHANGE + target->GetTitle());
854
855 // the 'true' will make sure all commands will be prepended by
856 // COMPILER_WAIT signal
857 AddCommandsToArray(compilerCmd, ret, true, true);
858 }
859 else
860 ret.Add(COMPILER_SIMPLE_LOG + _("Skipping linking (no linker program set): ") + output);
861
862 return ret;
863 }
864
GetCleanCommands(ProjectBuildTarget * target,bool distclean) const865 wxArrayString DirectCommands::GetCleanCommands(ProjectBuildTarget* target, bool distclean) const
866 {
867 wxArrayString ret;
868
869 if (target)
870 ret = GetTargetCleanCommands(target);
871 else
872 {
873 for (int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x)
874 {
875 ProjectBuildTarget* bt = m_pProject->GetBuildTarget(x);
876 wxArrayString targetclear = GetTargetCleanCommands(bt, distclean);
877 AppendArray(targetclear, ret);
878 }
879 }
880 return ret;
881 }
882
GetTargetCleanCommands(ProjectBuildTarget * target,bool distclean) const883 wxArrayString DirectCommands::GetTargetCleanCommands(ProjectBuildTarget* target, bool distclean) const
884 {
885 wxArrayString ret;
886
887 // add object files
888 MyFilesArray files = GetProjectFilesSortedByWeight(target, true, false);
889 for (unsigned int i = 0; i < files.GetCount(); ++i)
890 {
891 ProjectFile* pf = files[i];
892 const pfDetails& pfd = pf->GetFileDetails(target);
893 Compiler* compiler = target ? CompilerFactory::GetCompiler(target->GetCompilerID()) : m_pCompiler;
894 if (compiler)
895 {
896 wxString ObjectAbs = (compiler->GetSwitches().UseFlatObjects)
897 ? pfd.object_file_flat_absolute_native
898 : pfd.object_file_absolute_native;
899 ret.Add(ObjectAbs);
900 // if this is an auto-generated file, delete it
901 if (pf->AutoGeneratedBy())
902 ret.Add(pf->file.GetFullPath());
903
904 if (distclean)
905 ret.Add(pfd.dep_file_absolute_native);
906 }
907 }
908
909 // add target output
910 wxString outputfilename = target->GetOutputFilename();
911
912 if (target->GetTargetType() != ttCommandsOnly)
913 {
914 Manager::Get()->GetMacrosManager()->ReplaceMacros(outputfilename, target);
915 ret.Add(outputfilename);
916 }
917
918 if (target->GetTargetType() == ttDynamicLib)
919 {
920 // for dynamic libs, delete static lib
921 outputfilename = target->GetStaticLibFilename();
922 Manager::Get()->GetMacrosManager()->ReplaceMacros(outputfilename, target);
923 ret.Add(outputfilename);
924 // .def exports file is not deleted, because it may be user-supplied
925 // ret.Add(target->GetDynamicLibDefFilename());
926 }
927
928 return ret;
929 }
930
931 /** external deps are manually set by the user
932 * e.g. a static library linked to the project is an external dep (if set as such by the user)
933 * so that a re-linking is forced if the static lib is updated
934 */
AreExternalDepsOutdated(ProjectBuildTarget * target,const wxString & buildOutput,wxArrayString * filesMissing) const935 bool DirectCommands::AreExternalDepsOutdated(ProjectBuildTarget* target,
936 const wxString& buildOutput,
937 wxArrayString* filesMissing) const
938 {
939 Compiler* compiler = CompilerFactory::GetCompiler(target->GetCompilerID());
940
941 // if no output, probably a commands-only target; nothing to relink
942 // but we have to check other dependencies
943 time_t timeOutput = 0;
944 if (!buildOutput.IsEmpty())
945 {
946 wxString output = buildOutput;
947 Manager::Get()->GetMacrosManager()->ReplaceMacros(output);
948 depsTimeStamp(output.mb_str(), &timeOutput);
949 // if build output exists, check for updated static libraries
950 if (timeOutput)
951 {
952 // look for static libraries in target/project library dirs
953 wxArrayString libs = target->GetLinkLibs();
954 const wxArrayString& prjLibs = target->GetParentProject()->GetLinkLibs();
955 const wxArrayString& cmpLibs = compiler->GetLinkLibs();
956 AppendArray(prjLibs, libs);
957 AppendArray(cmpLibs, libs);
958
959 const wxArrayString& prjLibDirs = target->GetParentProject()->GetLibDirs();
960 const wxArrayString& cmpLibDirs = compiler->GetLibDirs();
961 wxArrayString libDirs = target->GetLibDirs();
962 AppendArray(prjLibDirs, libDirs);
963 AppendArray(cmpLibDirs, libDirs);
964
965 for (size_t i = 0; i < libs.GetCount(); ++i)
966 {
967 wxString lib = libs[i];
968
969 // if user manually pointed to a library, without using the lib dirs,
970 // then just check the file directly w/out involving the search dirs...
971 if (lib.Contains(_T("/")) || lib.Contains(_T("\\")))
972 {
973 Manager::Get()->GetMacrosManager()->ReplaceMacros(lib, target);
974 lib = UnixFilename(lib);
975 time_t timeExtDep;
976 depsTimeStamp(lib.mb_str(), &timeExtDep);
977 if (timeExtDep > timeOutput)
978 {
979 // force re-link
980 Manager::Get()->GetLogManager()->DebugLog(F(_T("Forcing re-link of '%s/%s' because '%s' is newer"),
981 target->GetParentProject()->GetTitle().wx_str(),
982 target->GetTitle().wx_str(),
983 lib.wx_str()));
984 return true;
985 }
986 continue;
987 }
988
989 if (!lib.StartsWith(compiler->GetSwitches().libPrefix))
990 lib = compiler->GetSwitches().libPrefix + lib;
991 if (!lib.EndsWith(_T(".") + compiler->GetSwitches().libExtension))
992 lib += _T(".") + compiler->GetSwitches().libExtension;
993
994 for (size_t l = 0; l < libDirs.GetCount(); ++l)
995 {
996 wxString dir = libDirs[l] + wxFILE_SEP_PATH + lib;
997 Manager::Get()->GetMacrosManager()->ReplaceMacros(dir, target);
998 dir = UnixFilename(dir);
999 time_t timeExtDep;
1000 depsTimeStamp(dir.mb_str(), &timeExtDep);
1001 if (timeExtDep > timeOutput)
1002 {
1003 // force re-link
1004 Manager::Get()->GetLogManager()->DebugLog(F(_T("Forcing re-link of '%s/%s' because '%s' is newer"),
1005 target->GetParentProject()->GetTitle().wx_str(),
1006 target->GetTitle().wx_str(),
1007 dir.wx_str()));
1008 return true;
1009 }
1010 }
1011 }
1012 }
1013 }
1014
1015 // array is separated by ;
1016 wxArrayString extDeps = GetArrayFromString(target->GetExternalDeps(), _T(";"));
1017 wxArrayString addFiles = GetArrayFromString(target->GetAdditionalOutputFiles(), _T(";"));
1018 for (size_t i = 0; i < extDeps.GetCount(); ++i)
1019 {
1020 if (extDeps[i].IsEmpty())
1021 continue;
1022
1023 Manager::Get()->GetMacrosManager()->ReplaceMacros(extDeps[i]);
1024 time_t timeExtDep;
1025 depsTimeStamp(extDeps[i].mb_str(), &timeExtDep);
1026 // if external dep doesn't exist, no need to relink
1027 // but we have to check other dependencies
1028 if (!timeExtDep)
1029 {
1030 if (filesMissing) filesMissing->Add(extDeps[i]);
1031 continue;
1032 }
1033
1034 // let's check the additional output files
1035 for (size_t j = 0; j < addFiles.GetCount(); ++j)
1036 {
1037 if (addFiles[j].IsEmpty())
1038 continue;
1039
1040 Manager::Get()->GetMacrosManager()->ReplaceMacros(addFiles[j]);
1041 time_t timeAddFile;
1042 depsTimeStamp(addFiles[j].mb_str(), &timeAddFile);
1043 // if additional file doesn't exist, we can skip it
1044 if (!timeAddFile)
1045 {
1046 if (filesMissing) filesMissing->Add(addFiles[j]);
1047 continue;
1048 }
1049
1050 // if external dep is newer than additional file, relink
1051 if (timeExtDep > timeAddFile)
1052 return true;
1053 }
1054
1055 // if no output, probably a commands-only target; nothing to relink
1056 // but we have to check other dependencies
1057 if (buildOutput.IsEmpty())
1058 continue;
1059
1060 // now check the target's output
1061 // this is moved last because, for "commands only" targets,
1062 // it would return before we had a chance to check the
1063 // additional output files (above)
1064
1065 // if build output doesn't exist, relink
1066 if (!timeOutput)
1067 return true;
1068
1069 // if external dep is newer than build output, relink
1070 if (timeExtDep > timeOutput)
1071 return true;
1072 }
1073 return false; // no force relink
1074 }
1075
IsObjectOutdated(ProjectBuildTarget * target,const pfDetails & pfd,wxString * errorStr) const1076 bool DirectCommands::IsObjectOutdated(ProjectBuildTarget* target, const pfDetails& pfd, wxString* errorStr) const
1077 {
1078 // If the source file does not exist, then do not compile.
1079 time_t timeSrc;
1080 depsTimeStamp(pfd.source_file_absolute_native.mb_str(), &timeSrc);
1081 if (!timeSrc)
1082 {
1083 if (errorStr)
1084 *errorStr = _("WARNING: Can't read file's timestamp: ") + pfd.source_file_absolute_native;
1085
1086 if (wxFileExists(pfd.source_file_absolute_native))
1087 return true; // fall-back: Its better to compile in that case
1088
1089 return false;
1090 }
1091
1092 // If the object file does not exist, then it must be built. In this case
1093 // there is no need to scan the source file for headers.
1094 time_t timeObj;
1095 Compiler* compiler = target ? CompilerFactory::GetCompiler(target->GetCompilerID()) : m_pCompiler;
1096 if (!compiler)
1097 return false;
1098
1099 wxString ObjectAbs = (compiler->GetSwitches().UseFlatObjects)
1100 ? pfd.object_file_flat_absolute_native
1101 : pfd.object_file_absolute_native;
1102 depsTimeStamp(ObjectAbs.mb_str(), &timeObj);
1103 if (!timeObj)
1104 return true; // fall-back: Its better to compile in that case
1105
1106 // If the source file is newer than the object file, then the object file
1107 // must be built. In this case there is no need to scan the source file
1108 // for headers.
1109 if (timeSrc > timeObj)
1110 return true;
1111
1112 // Do the check for includes only, if not disabled by the user, e.g. in case of non C/C++ compilers
1113 if ( Manager::Get()->GetConfigManager(_T("compiler"))->ReadBool(_T("/skip_include_deps"), false) )
1114 return false;
1115
1116 // Scan the source file for headers. Result is NULL if the file does
1117 // not exist. If one of the descendent header files is newer than the
1118 // object file, then the object file must be built.
1119 depsRef ref = depsScanForHeaders(pfd.source_file_absolute_native.mb_str());
1120 if (ref)
1121 {
1122 time_t timeNewest;
1123 (void) depsGetNewest(ref, &timeNewest);
1124 return (timeNewest > timeObj);
1125 }
1126
1127 // object file is up to date with source file and does not depend on #includes
1128 return false;
1129 }
1130
DepsSearchStart(ProjectBuildTarget * target) const1131 void DirectCommands::DepsSearchStart(ProjectBuildTarget* target) const
1132 {
1133 depsSearchStart();
1134
1135 MacrosManager* mm = Manager::Get()->GetMacrosManager();
1136 wxArrayString incs = m_pGenerator->GetCompilerSearchDirs(target);
1137
1138 for (unsigned int i = 0; i < incs.GetCount(); ++i)
1139 {
1140 // replace custom vars in include dirs
1141 mm->ReplaceMacros(incs[i], target);
1142 // actually add search dirs for deps
1143 depsAddSearchDir(incs[i].mb_str());
1144 }
1145
1146 // We could add the "global" compiler directories too, but we normally
1147 // don't care about the modification times of system include files.
1148 }
1149