1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3  * http://www.gnu.org/licenses/lgpl-3.0.html
4  *
5  * $Revision: 11182 $
6  * $Id: compiler.cpp 11182 2017-09-29 23:33:53Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/compiler.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13     #include "cbexception.h"
14     #include "compiler.h"
15     #include "manager.h"
16     #include "logmanager.h"
17     #include "configmanager.h"
18     #include "macrosmanager.h"
19     #include "globals.h"
20     #include "compilerfactory.h"
21 
22     #include <wx/intl.h>
23     #include <wx/regex.h>
24 #endif
25 
26 #include "compilercommandgenerator.h"
27 #include <wx/arrimpl.cpp>
28 #include <wx/filefn.h>
29 #include <wx/xml/xml.h>
30 
31 
32 // static
33 wxArrayString Compiler::m_CompilerIDs; // map to guarantee unique IDs
34 
35 // common regex that can be used by the different compiler for matching compiler output
36 // it can be used in the patterns for warnings, errors, ...
37 // NOTE : it is an approximation (for example the ':' can appear anywhere and several times)
38 const wxString Compiler::FilePathWithSpaces = _T("[][{}() \t#%$~[:alnum:]&_:+/\\.-]+");
39 
40 // version of compiler settings
41 // when this is different from what is saved in the config, a message appears
42 // to the user saying that default settings have changed and asks him if he wants to
43 // use his own settings or the new defaults
44 const wxString CompilerSettingsVersion = _T("0.0.3");
45 
46 const wxString EmptyString;
47 
CompilerSwitches()48 CompilerSwitches::CompilerSwitches()
49 {   // default based upon gnu
50     includeDirs             = _T("-I");
51     libDirs                 = _T("-L");
52     linkLibs                = _T("-l");
53     defines                 = _T("-D");
54     genericSwitch           = _T("-");
55     objectExtension         = _T("o");
56     needDependencies        = true;
57     forceFwdSlashes         = false;
58     forceCompilerUseQuotes  = false;
59     forceLinkerUseQuotes    = false;
60     logging                 = defaultLogging;
61     libPrefix               = _T("lib");
62     libExtension            = _T("a");
63     linkerNeedsLibPrefix    = false;
64     linkerNeedsLibExtension = false;
65     linkerNeedsPathResolved = false;
66     supportsPCH             = true;
67     PCHExtension            = _T("gch");
68     UseFlatObjects          = false;
69     UseFullSourcePaths      = false;
70     Use83Paths              = false;
71     includeDirSeparator     = _T(' ');
72     libDirSeparator         = _T(' ');
73     objectSeparator         = _T(' ');
74     statusSuccess           = 0;
75 }
76 
77 wxString Compiler::CommandTypeDescriptions[ctCount] =
78 {
79     // These are the strings that describe each CommandType enumerator...
80     // No need to say that it must have the same order as the enumerators!
81     _("Compile single file to object file"),
82     _("Generate dependencies for file"),
83     _("Compile Win32 resource file"),
84     _("Link object files to executable"),
85     _("Link object files to console executable"),
86     _("Link object files to dynamic library"),
87     _("Link object files to static library"),
88     _("Link object files to native executable")
89 };
90 
Compiler(const wxString & name,const wxString & ID,const wxString & parentID,int weight)91 Compiler::Compiler(const wxString& name, const wxString& ID, const wxString& parentID, int weight) :
92     m_Name(name),
93     m_MultiLineMessages(false),
94     m_ID(ID.Lower()),
95     m_ParentID(parentID.Lower()),
96     m_Valid(false),
97     m_NeedValidityCheck(true),
98     m_Mirrored(false)
99 {
100     //ctor
101     MakeValidID();
102 
103     m_Switches.supportsPCH = false;
104     m_Switches.forceFwdSlashes = false;
105     m_VersionString = wxEmptyString;
106     m_Weight = weight;
107     m_RegExes.reserve(100);
108     Manager::Get()->GetLogManager()->DebugLog(F(_T("Added compiler \"%s\""), m_Name.wx_str()));
109 }
110 
Compiler(const Compiler & other)111 Compiler::Compiler(const Compiler& other) :
112     CompileOptionsBase(other),
113     m_ParentID(other.m_ParentID.IsEmpty() ? other.m_ID : other.m_ParentID),
114     m_Mirror(other.m_Mirror),
115     m_Mirrored(other.m_Mirrored)
116 {
117     m_Name = _("Copy of ") + other.m_Name;
118     m_MultiLineMessages = other.m_MultiLineMessages;
119     // generate unique ID
120     // note that this copy constructor is protected and can only be called
121     // by our friend CompilerFactory. It knows what it's doing ;)
122     wxDateTime now = wxDateTime::UNow();
123     m_ID = now.Format(_T("%c"), wxDateTime::CET);
124     MakeValidID();
125 
126     m_MasterPath      = other.m_MasterPath;
127     m_ExtraPaths      = other.m_ExtraPaths;
128     m_Programs        = other.m_Programs;
129     m_Switches        = other.m_Switches;
130     m_Options         = other.m_Options;
131     m_SortOptions[0]  = other.m_SortOptions[0];
132     m_SortOptions[1]  = other.m_SortOptions[1];
133     m_IncludeDirs     = MakeUniqueArray(other.m_IncludeDirs,    true);
134     m_ResIncludeDirs  = MakeUniqueArray(other.m_ResIncludeDirs, true);
135     m_LibDirs         = MakeUniqueArray(other.m_LibDirs,        true);
136     m_CompilerOptions = other.m_CompilerOptions;
137     m_LinkerOptions   = other.m_LinkerOptions;
138     m_LinkLibs        = other.m_LinkLibs;
139     m_CmdsBefore      = other.m_CmdsBefore;
140     m_CmdsAfter       = other.m_CmdsAfter;
141     m_RegExes         = other.m_RegExes;
142     m_VersionString   = other.m_VersionString;
143     m_Weight          = 100; // place copied compilers at the end
144 
145     for (int i = 0; i < ctCount; ++i)
146         m_Commands[(CommandType)i] = other.m_Commands[(CommandType)i];
147 
148     m_Valid = other.m_Valid;
149     m_NeedValidityCheck = other.m_NeedValidityCheck;
150 }
151 
~Compiler()152 Compiler::~Compiler()
153 {
154     //dtor
155 }
156 
Reset()157 void Compiler::Reset()
158 {
159     m_Options.ClearOptions();
160     for (int i = 0; i < ctCount; ++i)
161         m_Commands[i].clear();
162     LoadDefaultOptions(GetID());
163 
164     LoadDefaultRegExArray();
165 
166     m_CompilerOptions.Clear();
167     m_LinkerOptions.Clear();
168     m_LinkLibs.Clear();
169     m_CmdsBefore.Clear();
170     m_CmdsAfter.Clear();
171     SetVersionString(); // Does nothing unless reimplemented
172 }
173 
ReloadOptions()174 void Compiler::ReloadOptions()
175 {
176     if (ConfigManager::LocateDataFile(wxT("compilers/options_") + GetID() + wxT(".xml"), sdDataUser | sdDataGlobal).IsEmpty())
177         return; // Do not clear if the options cannot be reloaded
178     m_Options.ClearOptions();
179     LoadDefaultOptions(GetID());
180     LoadDefaultRegExArray();
181 }
182 
LoadDefaultRegExArray(bool globalPrecedence)183 void Compiler::LoadDefaultRegExArray(bool globalPrecedence)
184 {
185     m_RegExes.clear();
186     LoadRegExArray(GetID(), globalPrecedence);
187 }
188 
189 // Keep in sync with the MakeInvalidCompilerMessages method.
IsValid()190 bool Compiler::IsValid()
191 {
192     if (!m_NeedValidityCheck)
193         return m_Valid;
194 
195     if (m_MasterPath.IsEmpty())
196         return true; // still initializing, don't try to test now
197 
198     m_NeedValidityCheck = false;
199 
200     if (!SupportsCurrentPlatform())
201     {
202         m_Valid = false;
203         return false;
204     }
205 
206     wxString tmp = m_MasterPath + _T("/bin/") + m_Programs.C;
207     MacrosManager *macros = Manager::Get()->GetMacrosManager();
208     macros->ReplaceMacros(tmp);
209     m_Valid = wxFileExists(tmp);
210     if (!m_Valid)
211     {
212         // and try without appending the 'bin'
213         tmp = m_MasterPath + _T("/") + m_Programs.C;
214         macros->ReplaceMacros(tmp);
215         m_Valid = wxFileExists(tmp);
216     }
217     if (!m_Valid)
218     {
219         // look in extra paths too
220         for (size_t i = 0; i < m_ExtraPaths.GetCount(); ++i)
221         {
222             tmp = m_ExtraPaths[i] + _T("/") + m_Programs.C;
223             macros->ReplaceMacros(tmp);
224             m_Valid = wxFileExists(tmp);
225             if (m_Valid)
226                 break;
227         }
228     }
229     return m_Valid;
230 }
231 
232 // Keep in sync with the IsValid method.
MakeInvalidCompilerMessages() const233 wxString Compiler::MakeInvalidCompilerMessages() const
234 {
235     if (!SupportsCurrentPlatform())
236         return _("Compiler doesn't support this platform!\n");
237 
238     MacrosManager *macros = Manager::Get()->GetMacrosManager();
239 
240     wxString triedPathsMsgs;
241     wxString tmp = m_MasterPath + _T("/bin/") + m_Programs.C;
242     macros->ReplaceMacros(tmp);
243     triedPathsMsgs += F(_T("Tried to run compiler executable '%s', but failed!\n"), tmp.wx_str());
244 
245     // and try without appending the 'bin'
246     tmp = m_MasterPath + _T("/") + m_Programs.C;
247     macros->ReplaceMacros(tmp);
248 
249     // look in extra paths too
250     for (size_t i = 0; i < m_ExtraPaths.GetCount(); ++i)
251     {
252         triedPathsMsgs += F(_T("Tried to run compiler executable '%s', but failed!\n"), tmp.wx_str());
253 
254         tmp = m_ExtraPaths[i] + _T("/") + m_Programs.C;
255         macros->ReplaceMacros(tmp);
256     }
257 
258     return triedPathsMsgs;
259 }
260 
MakeValidID()261 void Compiler::MakeValidID()
262 {
263     // basically, make it XML-element compatible
264     // only allow a-z, 0-9, _, and -
265     // (it is already lowercase)
266     // any non-conformant character will be removed
267 
268     wxString newID;
269     if (m_ID.IsEmpty())
270         m_ID = m_Name;
271 
272     size_t pos = 0;
273     while (pos < m_ID.Length())
274     {
275         wxChar ch = m_ID[pos];
276         if (wxIsalnum(ch) || ch == _T('_') || ch == _T('-')) // valid character
277             newID.Append(ch);
278         else if (wxIsspace(ch)) // convert spaces to underscores
279             newID.Append(_T('_'));
280         ++pos;
281     }
282 
283     // make sure it's not starting with a number or a '-'.
284     // if it is, prepend "cb"
285     if (wxIsdigit(newID.GetChar(0)) || newID.GetChar(0) == _T('-'))
286         newID.Prepend(_T("cb"));
287 
288     if (newID.IsEmpty()) // empty? wtf?
289         cbThrow(_T("Can't create a valid compiler ID for ") + m_Name);
290     m_ID = newID.Lower();
291 
292     // check for unique ID
293     if (!IsUniqueID(m_ID))
294         cbThrow(_T("Compiler ID already exists for ") + m_Name);
295     m_CompilerIDs.Add(m_ID);
296 }
297 
GetCommandGenerator(cbProject * project)298 CompilerCommandGenerator* Compiler::GetCommandGenerator(cbProject* project)
299 {
300     CompilerCommandGenerator* generator = new CompilerCommandGenerator;
301     generator->Init(project);
302     return generator;
303 }
304 
GetCommand(CommandType ct,const wxString & fileExtension) const305 const wxString& Compiler::GetCommand(CommandType ct, const wxString& fileExtension) const
306 {
307     const CompilerToolsVector& vec = m_Commands[ct];
308 
309     // no command?
310     if (vec.empty())
311         return EmptyString;
312 
313     size_t catchAll = 0;
314 
315     if (!fileExtension.IsEmpty())
316     {
317         for (size_t i = 0; i < vec.size(); ++i)
318         {
319             if (vec[i].extensions.GetCount() == 0)
320             {
321                 catchAll = i;
322                 continue;
323             }
324             for (size_t n = 0; n < vec[i].extensions.GetCount(); ++n)
325             {
326                 if (vec[i].extensions[n] == fileExtension)
327                     return vec[i].command;
328             }
329         }
330     }
331     return vec[catchAll].command;
332 }
333 
GetCompilerTool(CommandType ct,const wxString & fileExtension) const334 const CompilerTool* Compiler::GetCompilerTool(CommandType ct, const wxString& fileExtension) const
335 {
336     const CompilerToolsVector& vec = m_Commands[ct];
337     if (vec.empty())
338         return nullptr;
339 
340     size_t catchAll = 0;
341     if (!fileExtension.IsEmpty())
342     {
343         for (size_t i = 0; i < vec.size(); ++i)
344         {
345             if (vec[i].extensions.GetCount() == 0)
346             {
347                 catchAll = i;
348                 continue;
349             }
350             for (size_t n = 0; n < vec[i].extensions.GetCount(); ++n)
351             {
352                 if (vec[i].extensions[n] == fileExtension)
353                     return &vec[i];
354             }
355         }
356     }
357     return &vec[catchAll];
358 }
359 
MirrorCurrentSettings()360 void Compiler::MirrorCurrentSettings()
361 {
362     // run just once
363     if (m_Mirrored)
364         return;
365 
366     // keep the current settings safe
367     // so we can compare them when saving: this way we can only save what's
368     // different from the defaults
369 
370     m_Mirror.Name             = m_Name;
371     m_Mirror.MasterPath       = m_MasterPath;
372     m_Mirror.ExtraPaths       = m_ExtraPaths;
373     for (int i = 0; i < ctCount; ++i)
374         m_Mirror.Commands[i]  = m_Commands[i];
375     m_Mirror.Programs         = m_Programs;
376     m_Mirror.Switches         = m_Switches;
377     m_Mirror.Options          = m_Options;
378     m_Mirror.RegExes          = m_RegExes;
379 
380     m_Mirror.CompilerOptions_ = m_CompilerOptions;
381     m_Mirror.LinkerOptions    = m_LinkerOptions;
382     m_Mirror.IncludeDirs      = MakeUniqueArray(m_IncludeDirs,    true);
383     m_Mirror.ResIncludeDirs   = MakeUniqueArray(m_ResIncludeDirs, true);
384     m_Mirror.LibDirs          = MakeUniqueArray(m_LibDirs,        true);
385     m_Mirror.LinkLibs         = m_LinkLibs;
386     m_Mirror.CmdsBefore       = m_CmdsBefore;
387     m_Mirror.CmdsAfter        = m_CmdsAfter;
388 
389     m_Mirror.SortOptions[0] = m_SortOptions[0];
390     m_Mirror.SortOptions[1] = m_SortOptions[1];
391 
392     m_Mirrored                = true;
393 }
394 
SaveSettings(const wxString & baseKey)395 void Compiler::SaveSettings(const wxString& baseKey)
396 {
397     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("compiler"));
398 
399     // save settings version
400     cfg->Write(_T("settings_version"), CompilerSettingsVersion);
401 
402     wxString tmp;
403 
404     // delete old-style keys (using integer IDs)
405     tmp.Printf(_T("%s/set%3.3d"), baseKey.c_str(), CompilerFactory::GetCompilerIndex(this) + 1);
406     cfg->DeleteSubPath(tmp);
407 
408     tmp.Printf(_T("%s/%s"), baseKey.c_str(), m_ID.c_str());
409 
410     cfg->Write(tmp + _T("/name"),   m_Name);
411     cfg->Write(tmp + _T("/parent"), m_ParentID, true);
412 
413     if (m_Mirror.CompilerOptions_ != m_CompilerOptions)
414     {
415         wxString key = GetStringFromArray(m_CompilerOptions);
416         cfg->Write(tmp + _T("/compiler_options"), key, false);
417     }
418     if (m_Mirror.ResourceCompilerOptions != m_ResourceCompilerOptions)
419     {
420         wxString key = GetStringFromArray(m_ResourceCompilerOptions);
421         cfg->Write(tmp + _T("/resource_compiler_options"), key, false);
422     }
423     if (m_Mirror.LinkerOptions != m_LinkerOptions)
424     {
425         wxString key = GetStringFromArray(m_LinkerOptions);
426         cfg->Write(tmp + _T("/linker_options"),   key, false);
427     }
428     if (m_Mirror.IncludeDirs != m_IncludeDirs)
429     {
430         wxString key = GetStringFromArray( MakeUniqueArray(m_IncludeDirs, true) );
431         cfg->Write(tmp + _T("/include_dirs"),     key, false);
432     }
433     if (m_Mirror.ResIncludeDirs != m_ResIncludeDirs)
434     {
435         wxString key = GetStringFromArray( MakeUniqueArray(m_ResIncludeDirs, true) );
436         cfg->Write(tmp + _T("/res_include_dirs"), key, false);
437     }
438     if (m_Mirror.LibDirs != m_LibDirs)
439     {
440         wxString key = GetStringFromArray( MakeUniqueArray(m_LibDirs, true) );
441         cfg->Write(tmp + _T("/library_dirs"),     key, false);
442     }
443     if (m_Mirror.LinkLibs != m_LinkLibs)
444     {
445         wxString key = GetStringFromArray(m_LinkLibs);
446         cfg->Write(tmp + _T("/libraries"),        key, false);
447     }
448     if (m_Mirror.CmdsBefore != m_CmdsBefore)
449     {
450         wxString key = GetStringFromArray(m_CmdsBefore);
451         cfg->Write(tmp + _T("/commands_before"),  key, true);
452     }
453     if (m_Mirror.CmdsAfter != m_CmdsAfter)
454     {
455         wxString key = GetStringFromArray(m_CmdsAfter);
456         cfg->Write(tmp + _T("/commands_after"),   key, true);
457     }
458 
459     if (m_Mirror.MasterPath != m_MasterPath)
460         cfg->Write(tmp + _T("/master_path"),     m_MasterPath,         true);
461     if (m_Mirror.ExtraPaths != m_ExtraPaths)
462         cfg->Write(tmp + _T("/extra_paths"),     GetStringFromArray( MakeUniqueArray(m_ExtraPaths, true), _T(";") ), true);
463     if (m_Mirror.Programs.C != m_Programs.C)
464         cfg->Write(tmp + _T("/c_compiler"),      m_Programs.C,         true);
465     if (m_Mirror.Programs.CPP != m_Programs.CPP)
466         cfg->Write(tmp + _T("/cpp_compiler"),    m_Programs.CPP,       true);
467     if (m_Mirror.Programs.LD != m_Programs.LD)
468         cfg->Write(tmp + _T("/linker"),          m_Programs.LD,        true);
469     if (m_Mirror.Programs.LIB != m_Programs.LIB)
470         cfg->Write(tmp + _T("/lib_linker"),      m_Programs.LIB,       true);
471     if (m_Mirror.Programs.WINDRES != m_Programs.WINDRES)
472         cfg->Write(tmp + _T("/res_compiler"),    m_Programs.WINDRES,   true);
473     if (m_Mirror.Programs.MAKE != m_Programs.MAKE)
474         cfg->Write(tmp + _T("/make"),            m_Programs.MAKE,      true);
475     if (m_Mirror.Programs.DBGconfig != m_Programs.DBGconfig)
476         cfg->Write(tmp + _T("/debugger_config"), m_Programs.DBGconfig, true);
477 
478     for (int i = 0; i < ctCount; ++i)
479     {
480         for (size_t n = 0; n < m_Commands[i].size(); ++n)
481         {
482             if (n >= m_Mirror.Commands[i].size() || m_Mirror.Commands[i][n] != m_Commands[i][n])
483             {
484                 wxString key = wxString::Format(_T("%s/macros/%s/tool%lu/"), tmp.c_str(), CommandTypeDescriptions[i].c_str(), static_cast<unsigned long>(n));
485                 cfg->Write(key + _T("command"), m_Commands[i][n].command);
486                 cfg->Write(key + _T("extensions"), m_Commands[i][n].extensions);
487                 cfg->Write(key + _T("generatedFiles"), m_Commands[i][n].generatedFiles);
488             }
489         }
490     }
491 
492     // switches
493     if (m_Mirror.Switches.includeDirs != m_Switches.includeDirs)
494         cfg->Write(tmp + _T("/switches/includes"),                m_Switches.includeDirs,     true);
495     if (m_Mirror.Switches.libDirs != m_Switches.libDirs)
496         cfg->Write(tmp + _T("/switches/libs"),                    m_Switches.libDirs,         true);
497     if (m_Mirror.Switches.linkLibs != m_Switches.linkLibs)
498         cfg->Write(tmp + _T("/switches/link"),                    m_Switches.linkLibs,        true);
499     if (m_Mirror.Switches.defines != m_Switches.defines)
500         cfg->Write(tmp + _T("/switches/define"),                  m_Switches.defines,         true);
501     if (m_Mirror.Switches.genericSwitch != m_Switches.genericSwitch)
502         cfg->Write(tmp + _T("/switches/generic"),                 m_Switches.genericSwitch,   true);
503     if (m_Mirror.Switches.objectExtension != m_Switches.objectExtension)
504         cfg->Write(tmp + _T("/switches/objectext"),               m_Switches.objectExtension, true);
505     if (m_Mirror.Switches.needDependencies != m_Switches.needDependencies)
506         cfg->Write(tmp + _T("/switches/deps"),                    m_Switches.needDependencies);
507     if (m_Mirror.Switches.forceCompilerUseQuotes != m_Switches.forceCompilerUseQuotes)
508         cfg->Write(tmp + _T("/switches/forceCompilerQuotes"),     m_Switches.forceCompilerUseQuotes);
509     if (m_Mirror.Switches.forceLinkerUseQuotes != m_Switches.forceLinkerUseQuotes)
510         cfg->Write(tmp + _T("/switches/forceLinkerQuotes"),       m_Switches.forceLinkerUseQuotes);
511     if (m_Mirror.Switches.logging != m_Switches.logging)
512         cfg->Write(tmp + _T("/switches/logging"),                 m_Switches.logging);
513     if (m_Mirror.Switches.libPrefix != m_Switches.libPrefix)
514         cfg->Write(tmp + _T("/switches/libPrefix"),               m_Switches.libPrefix,       true);
515     if (m_Mirror.Switches.libExtension != m_Switches.libExtension)
516         cfg->Write(tmp + _T("/switches/libExtension"),            m_Switches.libExtension,    true);
517     if (m_Mirror.Switches.linkerNeedsLibPrefix != m_Switches.linkerNeedsLibPrefix)
518         cfg->Write(tmp + _T("/switches/linkerNeedsLibPrefix"),    m_Switches.linkerNeedsLibPrefix);
519     if (m_Mirror.Switches.linkerNeedsLibExtension != m_Switches.linkerNeedsLibExtension)
520         cfg->Write(tmp + _T("/switches/linkerNeedsLibExtension"), m_Switches.linkerNeedsLibExtension);
521     if (m_Mirror.Switches.linkerNeedsPathResolved != m_Switches.linkerNeedsPathResolved)
522         cfg->Write(tmp + _T("/switches/linkerNeedsPathResolved"), m_Switches.linkerNeedsPathResolved);
523     if (m_Mirror.Switches.forceFwdSlashes != m_Switches.forceFwdSlashes)
524         cfg->Write(tmp + _T("/switches/forceFwdSlashes"),         m_Switches.forceFwdSlashes);
525     if (m_Mirror.Switches.supportsPCH != m_Switches.supportsPCH)
526         cfg->Write(tmp + _T("/switches/supportsPCH"),             m_Switches.supportsPCH);
527     if (m_Mirror.Switches.PCHExtension != m_Switches.PCHExtension)
528         cfg->Write(tmp + _T("/switches/pchExtension"),            m_Switches.PCHExtension);
529     if (m_Mirror.Switches.UseFlatObjects != m_Switches.UseFlatObjects)
530         cfg->Write(tmp + _T("/switches/UseFlatObjects"),          m_Switches.UseFlatObjects);
531     if (m_Mirror.Switches.UseFullSourcePaths != m_Switches.UseFullSourcePaths)
532         cfg->Write(tmp + _T("/switches/UseFullSourcePaths"),      m_Switches.UseFullSourcePaths);
533     if (m_Mirror.Switches.includeDirSeparator != m_Switches.includeDirSeparator)
534         cfg->Write(tmp + _T("/switches/includeDirSeparator"),     (int)m_Switches.includeDirSeparator);
535     if (m_Mirror.Switches.libDirSeparator != m_Switches.libDirSeparator)
536         cfg->Write(tmp + _T("/switches/libDirSeparator"),         (int)m_Switches.libDirSeparator);
537     if (m_Mirror.Switches.objectSeparator != m_Switches.objectSeparator)
538         cfg->Write(tmp + _T("/switches/objectSeparator"),         (int)m_Switches.objectSeparator);
539     if (m_Mirror.Switches.statusSuccess != m_Switches.statusSuccess)
540         cfg->Write(tmp + _T("/switches/statusSuccess"),           m_Switches.statusSuccess);
541     if (m_Mirror.Switches.Use83Paths != m_Switches.Use83Paths)
542         cfg->Write(tmp + _T("/switches/Use83Paths"),              m_Switches.Use83Paths);
543 
544     // regexes
545     cfg->DeleteSubPath(tmp + _T("/regex"));
546     wxString group;
547     for (size_t i = 0; i < m_RegExes.size(); ++i)
548     {
549         if (i < m_Mirror.RegExes.size() && m_Mirror.RegExes[i] == m_RegExes[i])
550             continue;
551 
552         group.Printf(_T("%s/regex/re%3.3lu"), tmp.c_str(), static_cast<unsigned long>(i + 1));
553         RegExStruct& rs = m_RegExes[i];
554         cfg->Write(group + _T("/description"),  rs.desc,  true);
555         if (rs.lt != 0)
556             cfg->Write(group + _T("/type"),     rs.lt);
557         cfg->Write(group + _T("/regex"),        rs.GetRegExString(), true);
558         if (rs.msg[0] != 0)
559             cfg->Write(group + _T("/msg1"),     rs.msg[0]);
560         if (rs.msg[1] != 0)
561             cfg->Write(group + _T("/msg2"),     rs.msg[1]);
562         if (rs.msg[2] != 0)
563             cfg->Write(group + _T("/msg3"),     rs.msg[2]);
564         if (rs.filename != 0)
565             cfg->Write(group + _T("/filename"), rs.filename);
566         if (rs.line != 0)
567             cfg->Write(group + _T("/line"),     rs.line);
568     }
569 
570     // sorted flags
571     if (m_Mirror.SortOptions[0] != GetCOnlyFlags())
572         cfg->Write(tmp + _T("/sort/C"),   GetCOnlyFlags());
573     if (m_Mirror.SortOptions[1] != GetCPPOnlyFlags())
574         cfg->Write(tmp + _T("/sort/CPP"), GetCPPOnlyFlags());
575 
576     // custom vars
577     wxString configpath = tmp + _T("/custom_variables/");
578     cfg->DeleteSubPath(configpath);
579     const StringHash& v = GetAllVars();
580     for (StringHash::const_iterator it = v.begin(); it != v.end(); ++it)
581         cfg->Write(configpath + it->first, it->second);
582 }
583 
LoadSettings(const wxString & baseKey)584 void Compiler::LoadSettings(const wxString& baseKey)
585 {
586     // before loading any compiler settings, keep the current settings safe
587     // so we can compare them when saving: this way we can only save what's
588     // different from the defaults
589     MirrorCurrentSettings();
590 
591     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("compiler"));
592 
593     // read settings version
594     wxString version = cfg->Read(_T("settings_version"));
595     bool versionMismatch = version != CompilerSettingsVersion;
596 
597     wxString tmp;
598 
599     // if using old-style keys (using integer IDs), notify user about the changes
600     static bool saidAboutCompilerIDs = false;
601     tmp.Printf(_T("%s/set%3.3d"), baseKey.c_str(), CompilerFactory::GetCompilerIndex(this) + 1);
602     if (cfg->Exists(tmp + _T("/name")))
603     {
604         if (!saidAboutCompilerIDs)
605         {
606             saidAboutCompilerIDs = true;
607             cbMessageBox(_("Compilers now use unique names instead of integer IDs.\n"
608                             "Projects will be updated accordingly on load, mostly automatic."),
609                             _("Information"),
610                             wxICON_INFORMATION);
611         }
612         // at this point, we 'll be using the old style configuration to load settings
613     }
614     else // it's OK to use new style
615         tmp.Printf(_T("%s/%s"), baseKey.c_str(), m_ID.c_str());
616 
617     if (!cfg->Exists(tmp + _T("/name")))
618     {
619         tmp.Replace(wxT("-"), wxEmptyString); // try again using previous id format
620         if (!cfg->Exists(tmp + _T("/name")))
621             return;
622     }
623 
624     wxString sep = wxFileName::GetPathSeparator();
625 
626     m_Name = cfg->Read(tmp + _T("/name"), m_Name);
627 
628     m_MasterPath         = cfg->Read(tmp + _T("/master_path"),     m_MasterPath);
629     m_ExtraPaths         = MakeUniqueArray(GetArrayFromString(cfg->Read(tmp + _T("/extra_paths"), _T("")), _T(";")), true);
630     m_Programs.C         = cfg->Read(tmp + _T("/c_compiler"),      m_Programs.C);
631     m_Programs.CPP       = cfg->Read(tmp + _T("/cpp_compiler"),    m_Programs.CPP);
632     m_Programs.LD        = cfg->Read(tmp + _T("/linker"),          m_Programs.LD);
633     m_Programs.LIB       = cfg->Read(tmp + _T("/lib_linker"),      m_Programs.LIB);
634     m_Programs.WINDRES   = cfg->Read(tmp + _T("/res_compiler"),    m_Programs.WINDRES);
635     m_Programs.MAKE      = cfg->Read(tmp + _T("/make"),            m_Programs.MAKE);
636     m_Programs.DBGconfig = cfg->Read(tmp + _T("/debugger_config"), m_Programs.DBGconfig);
637 
638     // set member variable containing the version string with the configuration toolchain executables, not only
639     // with the default ones, otherwise we might have an empty version-string
640     // Some MinGW installations do not include "mingw32-gcc" !!
641     SetVersionString();
642 
643     SetCompilerOptions    (GetArrayFromString(cfg->Read(tmp + _T("/compiler_options"), wxEmptyString)));
644     SetResourceCompilerOptions(GetArrayFromString(cfg->Read(tmp + _T("/resource_compiler_options"), wxEmptyString)));
645     SetLinkerOptions      (GetArrayFromString(cfg->Read(tmp + _T("/linker_options"),   wxEmptyString)));
646     SetIncludeDirs        (GetArrayFromString(cfg->Read(tmp + _T("/include_dirs"),     wxEmptyString)));
647     SetResourceIncludeDirs(GetArrayFromString(cfg->Read(tmp + _T("/res_include_dirs"), wxEmptyString)));
648     SetLibDirs            (GetArrayFromString(cfg->Read(tmp + _T("/library_dirs"),     wxEmptyString)));
649     SetLinkLibs           (GetArrayFromString(cfg->Read(tmp + _T("/libraries"),        wxEmptyString)));
650     SetCommandsBeforeBuild(GetArrayFromString(cfg->Read(tmp + _T("/commands_before"),  wxEmptyString)));
651     SetCommandsAfterBuild (GetArrayFromString(cfg->Read(tmp + _T("/commands_after"),   wxEmptyString)));
652 
653     for (int i = 0; i < ctCount; ++i)
654     {
655         wxArrayString keys = cfg->EnumerateSubPaths(tmp + _T("/macros/") + CommandTypeDescriptions[i]);
656         for (size_t n = 0; n < keys.size(); ++n)
657         {
658             unsigned long index = 0;
659             if (keys[n].Mid(4).ToULong(&index)) // skip 'tool'
660             {
661                 while (index >= m_Commands[i].size())
662                     m_Commands[i].push_back(CompilerTool());
663                 CompilerTool& tool = m_Commands[i][index];
664 
665                 wxString key        = wxString::Format(_T("%s/macros/%s/tool%lu/"), tmp.c_str(), CommandTypeDescriptions[i].c_str(), index);
666                 tool.command        = cfg->Read(key + _T("command"));
667                 tool.extensions     = cfg->ReadArrayString(key + _T("extensions"));
668                 tool.generatedFiles = cfg->ReadArrayString(key + _T("generatedFiles"));
669             }
670         }
671     }
672 
673     // switches
674     m_Switches.includeDirs             = cfg->Read(tmp + _T("/switches/includes"),                    m_Switches.includeDirs);
675     m_Switches.libDirs                 = cfg->Read(tmp + _T("/switches/libs"),                        m_Switches.libDirs);
676     m_Switches.linkLibs                = cfg->Read(tmp + _T("/switches/link"),                        m_Switches.linkLibs);
677     m_Switches.defines                 = cfg->Read(tmp + _T("/switches/define"),                      m_Switches.defines);
678     m_Switches.genericSwitch           = cfg->Read(tmp + _T("/switches/generic"),                     m_Switches.genericSwitch);
679     m_Switches.objectExtension         = cfg->Read(tmp + _T("/switches/objectext"),                   m_Switches.objectExtension);
680     m_Switches.needDependencies        = cfg->ReadBool(tmp + _T("/switches/deps"),                    m_Switches.needDependencies);
681     m_Switches.forceCompilerUseQuotes  = cfg->ReadBool(tmp + _T("/switches/forceCompilerQuotes"),     m_Switches.forceCompilerUseQuotes);
682     m_Switches.forceLinkerUseQuotes    = cfg->ReadBool(tmp + _T("/switches/forceLinkerQuotes"),       m_Switches.forceLinkerUseQuotes);
683     m_Switches.logging = (CompilerLoggingType)cfg->ReadInt(tmp + _T("/switches/logging"),             m_Switches.logging);
684     m_Switches.libPrefix               = cfg->Read(tmp + _T("/switches/libPrefix"),                   m_Switches.libPrefix);
685     m_Switches.libExtension            = cfg->Read(tmp + _T("/switches/libExtension"),                m_Switches.libExtension);
686     m_Switches.linkerNeedsLibPrefix    = cfg->ReadBool(tmp + _T("/switches/linkerNeedsLibPrefix"),    m_Switches.linkerNeedsLibPrefix);
687     m_Switches.linkerNeedsLibExtension = cfg->ReadBool(tmp + _T("/switches/linkerNeedsLibExtension"), m_Switches.linkerNeedsLibExtension);
688     m_Switches.linkerNeedsPathResolved = cfg->ReadBool(tmp + _T("/switches/linkerNeedsPathResolved"), m_Switches.linkerNeedsPathResolved);
689     m_Switches.forceFwdSlashes         = cfg->ReadBool(tmp + _T("/switches/forceFwdSlashes"),         m_Switches.forceFwdSlashes);
690     m_Switches.supportsPCH             = cfg->ReadBool(tmp + _T("/switches/supportsPCH"),             m_Switches.supportsPCH);
691     m_Switches.PCHExtension            = cfg->Read(tmp + _T("/switches/pchExtension"),                m_Switches.PCHExtension);
692     m_Switches.UseFlatObjects          = cfg->ReadBool(tmp + _T("/switches/UseFlatObjects"),          m_Switches.UseFlatObjects);
693     m_Switches.UseFullSourcePaths      = cfg->ReadBool(tmp + _T("/switches/UseFullSourcePaths"),      m_Switches.UseFullSourcePaths);
694     m_Switches.Use83Paths              = cfg->ReadBool(tmp + _T("/switches/Use83Paths"),              m_Switches.Use83Paths);
695     m_Switches.includeDirSeparator  = (wxChar)cfg->ReadInt(tmp + _T("/switches/includeDirSeparator"), (int)m_Switches.includeDirSeparator);
696     m_Switches.libDirSeparator         = (wxChar)cfg->ReadInt(tmp + _T("/switches/libDirSeparator"),  (int)m_Switches.libDirSeparator);
697     m_Switches.objectSeparator         = (wxChar)cfg->ReadInt(tmp + _T("/switches/objectSeparator"),  (int)m_Switches.objectSeparator);
698     m_Switches.statusSuccess           = cfg->ReadInt(tmp + _T("/switches/statusSuccess"),            m_Switches.statusSuccess);
699 
700     // regexes
701 
702     // because we 're only saving changed regexes, we can't just iterate like before.
703     // instead, we must iterate all child-keys and deduce the regex index number from
704     // the key name
705     wxArrayString keys = cfg->EnumerateSubPaths(tmp + _T("/regex/"));
706     wxString group;
707     long index = 0;
708     for (size_t i = 0; i < keys.GetCount(); ++i)
709     {
710         wxString key = keys[i];
711 
712         // reNNN
713         if (!key.StartsWith(_T("re")))
714             continue;
715         key.Remove(0, 2);
716         if (!key.ToLong(&index, 10))
717             continue;
718 
719         // 'index' now holds the regex index.
720         // read everything and either assign it to an existing regex
721         // if the index exists, or add a new regex
722 
723         group.Printf(_T("%s/regex/re%3.3ld"), tmp.c_str(), index);
724         if (!cfg->Exists(group+_T("/description")))
725             continue;
726 
727         RegExStruct rs(cfg->Read(group + _T("/description")),
728                        (CompilerLineType)cfg->ReadInt(group + _T("/type"), 0),
729                        cfg->Read(group + _T("/regex")),
730                        cfg->ReadInt(group + _T("/msg1"), 0),
731                        cfg->ReadInt(group + _T("/filename"), 0),
732                        cfg->ReadInt(group + _T("/line"), 0),
733                        cfg->ReadInt(group + _T("/msg2"), 0),
734                        cfg->ReadInt(group + _T("/msg3"), 0));
735 
736         if (index <= (long)m_RegExes.size())
737             m_RegExes[index - 1] = rs;
738         else
739             m_RegExes.push_back(rs);
740     }
741 
742     // sorted flags
743     m_SortOptions[0] = cfg->Read(tmp + _T("/sort/C"), m_SortOptions[0]);
744     m_SortOptions[1] = cfg->Read(tmp + _T("/sort/CPP"), m_SortOptions[1]);
745 
746     // custom vars
747     wxString configpath = tmp + _T("/custom_variables/");
748     UnsetAllVars();
749     wxArrayString list = cfg->EnumerateKeys(configpath);
750     for (unsigned int i = 0; i < list.GetCount(); ++i)
751         SetVar(list[i], cfg->Read(configpath + _T('/') + list[i]), false);
752 
753     if (versionMismatch)
754     {
755         wxString msg;
756         msg << _("Some compiler settings defaults have changed in this version.\n"
757                  "It is recommended that you allow updating of your settings to the new defaults.\n"
758                  "Only disallow this if you don't want to lose any customizations you have done to this compiler's settings.\n\n"
759                  "Note that the only settings that are affected are those found in \"Advanced compiler options\"...\n\n"
760                  "Do you want to update your current settings to the new defaults?");
761         // don't ask if the compiler is not valid (i.e. not installed), just update
762         if (!IsValid() || cbMessageBox(msg, m_Name, wxICON_QUESTION | wxYES_NO) == wxID_YES)
763         {
764             for (int i = 0; i < ctCount; ++i)
765                 m_Commands[i] = m_Mirror.Commands[i];
766             m_Switches = m_Mirror.Switches;
767             m_Options  = m_Mirror.Options;
768             m_RegExes  = m_Mirror.RegExes;
769         }
770     }
771 }
772 
CheckForWarningsAndErrors(const wxString & line)773 CompilerLineType Compiler::CheckForWarningsAndErrors(const wxString& line)
774 {
775     if (!m_MultiLineMessages || (m_MultiLineMessages && !m_Error.IsEmpty()))
776     {
777         m_ErrorFilename.Clear();
778         m_ErrorLine.Clear();
779         m_Error.Clear();
780     }
781 
782     for (size_t i = 0; i < m_RegExes.size(); ++i)
783     {
784         RegExStruct& rs = m_RegExes[i];
785         if (!rs.HasRegEx())
786             continue;
787         const wxRegEx &regex = rs.GetRegEx();
788         if (regex.Matches(line))
789         {
790             if (rs.filename > 0)
791                  m_ErrorFilename = UnixFilename(regex.GetMatch(line, rs.filename));
792             if (rs.line > 0)
793                 m_ErrorLine = regex.GetMatch(line, rs.line);
794             for (int x = 0; x < 3; ++x)
795             {
796                 if (rs.msg[x] > 0)
797                 {
798                     if (!m_Error.IsEmpty())
799                         m_Error << _T(" ");
800                     m_Error << regex.GetMatch(line, rs.msg[x]);
801                 }
802             }
803             return rs.lt;
804         }
805     }
806     return cltNormal; // default return value
807 }
808 
LoadDefaultOptions(const wxString & name,int recursion)809 void Compiler::LoadDefaultOptions(const wxString& name, int recursion)
810 {
811     wxXmlDocument options;
812     wxString doc = ConfigManager::LocateDataFile(wxT("compilers/options_") + name + wxT(".xml"), sdDataUser | sdDataGlobal);
813     if (doc.IsEmpty())
814     {
815         wxString msg(_("Error: file 'options_") + name + _(".xml' not found."));
816         Manager::Get()->GetLogManager()->Log(msg);
817         cbMessageBox(msg, _("Compiler options"), wxICON_ERROR);
818         return;
819     }
820     if (recursion > 5)
821     {
822         wxString msg(_("Warning: '") + doc + _("' not loaded due to excessive recursion."));
823         Manager::Get()->GetLogManager()->LogWarning(msg);
824         cbMessageBox(msg, _("Compiler options"), wxICON_EXCLAMATION);
825         return;
826     }
827     if (!options.Load(doc))
828     {
829         wxString msg(_("Error: Compiler options file '") + doc + _("' not found for compiler '") + name + wxT("'."));
830         Manager::Get()->GetLogManager()->Log(msg);
831         cbMessageBox(msg, _("Compiler options"), wxICON_ERROR);
832         return;
833     }
834     if (options.GetRoot()->GetName() != wxT("CodeBlocks_compiler_options"))
835     {
836         wxString msg(_("Error: Invalid Code::Blocks compiler options file for compiler '") + name + wxT("'."));
837         Manager::Get()->GetLogManager()->Log(msg);
838         cbMessageBox(msg, _("Compiler options"), wxICON_ERROR);
839         return;
840     }
841     wxString extends = options.GetRoot()->GetAttribute(wxT("extends"), wxEmptyString);
842     if (!extends.IsEmpty())
843         LoadDefaultOptions(extends, recursion + 1);
844     wxXmlNode* node = options.GetRoot()->GetChildren();
845     int depth = 0;
846     wxString categ;
847     bool exclu = false;
848 
849     wxString baseKey = GetParentID().IsEmpty() ? wxT("/sets") : wxT("/user_sets");
850     ConfigManager* cfg = Manager::Get()->GetConfigManager(wxT("compiler"));
851     wxString cmpKey;
852     cmpKey.Printf(wxT("%s/set%3.3d"), baseKey.c_str(), CompilerFactory::GetCompilerIndex(this) + 1);
853     if (!cfg->Exists(cmpKey + wxT("/name")))
854         cmpKey.Printf(wxT("%s/%s"), baseKey.c_str(), m_ID.c_str());
855     if (!cfg->Exists(cmpKey + wxT("/name")))
856         cmpKey.Replace(wxT("-"), wxEmptyString);
857 
858     while (node)
859     {
860         const wxString value = node->GetAttribute(wxT("value"), wxEmptyString);
861         if (node->GetName() == wxT("if") && node->GetChildren())
862         {
863             if (EvalXMLCondition(node))
864             {
865                 node = node->GetChildren();
866                 ++depth;
867                 continue;
868             }
869             else if (node->GetNext() && node->GetNext()->GetName() == wxT("else") &&
870                      node->GetNext()->GetChildren())
871             {
872                 node = node->GetNext()->GetChildren();
873                 ++depth;
874                 continue;
875             }
876         }
877         else if (node->GetName() == wxT("Program")) // configuration is read so execution of renamed programs work, m_Mirror is needed to reset before leaving this function
878         {
879             wxString prog = node->GetAttribute(wxT("name"), wxEmptyString);
880             if (prog == wxT("C"))
881             {
882                 m_Programs.C = cfg->Read(cmpKey + wxT("/c_compiler"), value);
883                 m_Mirror.Programs.C = value;
884             }
885             else if (prog == wxT("CPP"))
886             {
887                 m_Programs.CPP = cfg->Read(cmpKey + wxT("/cpp_compiler"), value);
888                 m_Mirror.Programs.CPP = value;
889             }
890             else if (prog == wxT("LD"))
891             {
892                 m_Programs.LD = cfg->Read(cmpKey + wxT("/linker"), value);
893                 m_Mirror.Programs.LD = value;
894             }
895             else if (prog == wxT("DBGconfig"))
896                 m_Programs.DBGconfig = value;
897             else if (prog == wxT("LIB"))
898             {
899                 m_Programs.LIB = cfg->Read(cmpKey + wxT("/lib_linker"), value);
900                 m_Mirror.Programs.LIB = value;
901             }
902             else if (prog == wxT("WINDRES"))
903             {
904                 m_Programs.WINDRES = cfg->Read(cmpKey + wxT("/res_compiler"), value);
905                 m_Mirror.Programs.WINDRES = value;
906             }
907             else if (prog == wxT("MAKE"))
908             {
909                 m_Programs.MAKE = cfg->Read(cmpKey + wxT("/make"), value);
910                 m_Mirror.Programs.MAKE = value;
911             }
912         }
913         else if (node->GetName() == wxT("Switch"))
914         {
915             wxString swi = node->GetAttribute(wxT("name"), wxEmptyString);
916             if (swi == wxT("includeDirs"))
917                 m_Switches.includeDirs = value;
918             else if (swi == wxT("libDirs"))
919                 m_Switches.libDirs = value;
920             else if (swi == wxT("linkLibs"))
921                 m_Switches.linkLibs = value;
922             else if (swi == wxT("defines"))
923                 m_Switches.defines = value;
924             else if (swi == wxT("genericSwitch"))
925                 m_Switches.genericSwitch = value;
926             else if (swi == wxT("objectExtension"))
927                 m_Switches.objectExtension = value;
928             else if (swi == wxT("forceFwdSlashes"))
929                 m_Switches.forceFwdSlashes = (value == wxT("true"));
930             else if (swi == wxT("forceLinkerUseQuotes"))
931                 m_Switches.forceLinkerUseQuotes = (value == wxT("true"));
932             else if (swi == wxT("forceCompilerUseQuotes"))
933                 m_Switches.forceCompilerUseQuotes = (value == wxT("true"));
934             else if (swi == wxT("needDependencies"))
935                 m_Switches.needDependencies = (value == wxT("true"));
936             else if (swi == wxT("logging"))
937             {
938                 if (value == wxT("full"))
939                     m_Switches.logging = clogFull;
940                 else if (value == wxT("simple"))
941                     m_Switches.logging = clogSimple;
942                 else if (value == wxT("none"))
943                     m_Switches.logging = clogNone;
944                 else
945                     m_Switches.logging = CompilerSwitches::defaultLogging;
946             }
947             else if (swi == wxT("libPrefix"))
948                 m_Switches.libPrefix = value;
949             else if (swi == wxT("libExtension"))
950                 m_Switches.libExtension = value;
951             else if (swi == wxT("linkerNeedsLibPrefix"))
952                 m_Switches.linkerNeedsLibPrefix = (value == wxT("true"));
953             else if (swi == wxT("linkerNeedsLibExtension"))
954                 m_Switches.linkerNeedsLibExtension = (value == wxT("true"));
955             else if (swi == wxT("linkerNeedsPathResolved"))
956                 m_Switches.linkerNeedsPathResolved = (value == wxT("true"));
957             else if (swi == wxT("supportsPCH"))
958                 m_Switches.supportsPCH = (value == wxT("true"));
959             else if (swi == wxT("PCHExtension"))
960                 m_Switches.PCHExtension = value;
961             else if (swi == wxT("UseFlatObjects"))
962                 m_Switches.UseFlatObjects = (value == wxT("true"));
963             else if (swi == wxT("UseFullSourcePaths"))
964                 m_Switches.UseFullSourcePaths = (value == wxT("true"));
965             else if (swi == wxT("includeDirSeparator") && !value.IsEmpty())
966                 m_Switches.includeDirSeparator = value[0];
967             else if (swi == wxT("libDirSeparator") && !value.IsEmpty())
968                 m_Switches.libDirSeparator = value[0];
969             else if (swi == wxT("objectSeparator") && !value.IsEmpty())
970                 m_Switches.objectSeparator = value[0];
971             else if (swi == wxT("statusSuccess") && !value.IsEmpty())
972             {
973                 long val;
974                 if (value.ToLong(&val))
975                     m_Switches.statusSuccess = val;
976             }
977             else if (swi == wxT("Use83Paths"))
978                 m_Switches.Use83Paths = (value == wxT("true"));
979         }
980         else if (node->GetName() == wxT("Category") && node->GetChildren())
981         {
982             categ = node->GetAttribute(wxT("name"), wxEmptyString);
983             exclu = (node->GetAttribute(wxT("exclusive"), wxEmptyString) == wxT("true"));
984             node = node->GetChildren();
985             ++depth;
986             continue;
987         }
988         else if (node->GetName() == wxT("Option"))
989         {
990             wxString category;
991             if (!node->GetAttribute(wxT("category"), &category))
992             {
993                 if (categ.IsEmpty())
994                     category = wxT("General");
995                 else
996                     category = categ;
997             }
998             wxString exclusive;
999             if (!node->GetAttribute(wxT("exclusive"), &exclusive))
1000                 exclusive = (exclu ? wxT("true") : wxT("false"));
1001             m_Options.AddOption(wxGetTranslation(node->GetAttribute(wxT("name"), wxEmptyString)),
1002                                 node->GetAttribute(wxT("option"), wxEmptyString),
1003                                 wxGetTranslation(category),
1004                                 node->GetAttribute(wxT("additionalLibs"), wxEmptyString),
1005                                 node->GetAttribute(wxT("checkAgainst"), wxEmptyString),
1006                                 wxGetTranslation(node->GetAttribute(wxT("checkMessage"), wxEmptyString)),
1007                                 node->GetAttribute(wxT("supersedes"), wxEmptyString),
1008                                 exclusive == wxT("true"));
1009         }
1010         else if (node->GetName() == wxT("Command"))
1011         {
1012             wxString cmd = node->GetAttribute(wxT("name"), wxEmptyString);
1013             wxString unEscape = value;
1014             unEscape.Replace(wxT("\\n"), wxT("\n")); // a single tool can support multiple commands
1015             CompilerTool tool(unEscape, node->GetAttribute(wxT("ext"), wxEmptyString),
1016                               node->GetAttribute(wxT("gen"), wxEmptyString));
1017             CommandType cmdTp = ctCount;
1018             if (cmd == wxT("CompileObject"))
1019                 cmdTp = ctCompileObjectCmd;
1020             else if (cmd == wxT("GenDependencies"))
1021                 cmdTp = ctGenDependenciesCmd;
1022             else if (cmd == wxT("CompileResource"))
1023                 cmdTp = ctCompileResourceCmd;
1024             else if (cmd == wxT("LinkExe"))
1025                 cmdTp = ctLinkExeCmd;
1026             else if (cmd == wxT("LinkConsoleExe"))
1027                 cmdTp = ctLinkConsoleExeCmd;
1028             else if (cmd == wxT("LinkDynamic"))
1029                 cmdTp = ctLinkDynamicCmd;
1030             else if (cmd == wxT("LinkStatic"))
1031                 cmdTp = ctLinkStaticCmd;
1032             else if (cmd == wxT("LinkNative"))
1033                 cmdTp = ctLinkNativeCmd;
1034             if  (cmdTp != ctCount)
1035             {
1036                 bool assigned = false;
1037                 CompilerToolsVector& tools = m_Commands[cmdTp];
1038                 for (size_t i = 0; i < tools.size(); ++i)
1039                 {
1040                     if (tools[i].extensions == tool.extensions)
1041                     {
1042                         tools[i] = tool;
1043                         assigned = true;
1044                         break;
1045                     }
1046                 }
1047                 if (!assigned)
1048                     tools.push_back(tool);
1049             }
1050         }
1051         else if (node->GetName() == wxT("Sort"))
1052         {
1053             wxString flags;
1054             if (node->GetAttribute(wxT("CFlags"), &flags))
1055             {
1056                 flags.Replace(wxT("\n"), wxT(" "));
1057                 flags.Replace(wxT("\r"), wxT(" "));
1058                 SetCOnlyFlags( MakeUniqueString(GetCOnlyFlags() + wxT(" ") + flags,
1059                                                 wxT(" ")) );
1060             }
1061             else if (node->GetAttribute(wxT("CPPFlags"), &flags))
1062             {
1063                 flags.Replace(wxT("\n"), wxT(" "));
1064                 flags.Replace(wxT("\r"), wxT(" "));
1065                 SetCPPOnlyFlags( MakeUniqueString(GetCPPOnlyFlags() + wxT(" ") + flags,
1066                                                   wxT(" ")) );
1067             }
1068         }
1069         else if (node->GetName() == wxT("Common"))
1070         {
1071             LoadDefaultOptions(wxT("common_") + node->GetAttribute(wxT("name"), wxEmptyString), recursion + 1);
1072         }
1073         while (!node->GetNext() && depth > 0)
1074         {
1075             node = node->GetParent();
1076             if (node->GetName() == wxT("Category"))
1077             {
1078                 categ = wxEmptyString;
1079                 exclu = false;
1080             }
1081             --depth;
1082         }
1083         node = node->GetNext();
1084     }
1085     if (recursion == 0) // reset programs to their actual defaults (customized settings are loaded in a different function)
1086     {
1087         m_Programs.C       = m_Mirror.Programs.C;
1088         m_Programs.CPP     = m_Mirror.Programs.CPP;
1089         m_Programs.LD      = m_Mirror.Programs.LD;
1090         m_Programs.LIB     = m_Mirror.Programs.LIB;
1091         m_Programs.WINDRES = m_Mirror.Programs.WINDRES;
1092         m_Programs.MAKE    = m_Mirror.Programs.MAKE;
1093     }
1094 }
1095 
LoadRegExArray(const wxString & name,bool globalPrecedence,int recursion)1096 void Compiler::LoadRegExArray(const wxString& name, bool globalPrecedence, int recursion)
1097 {
1098     wxXmlDocument options;
1099     wxString doc;
1100     const wxString fn = wxT("compilers/options_") + name + wxT(".xml");
1101     if (globalPrecedence)
1102     {
1103         doc = ConfigManager::LocateDataFile(fn, sdDataGlobal);
1104         if (doc.IsEmpty())
1105             doc = ConfigManager::LocateDataFile(fn, sdDataUser);
1106     }
1107     else
1108         doc = ConfigManager::LocateDataFile(fn, sdDataUser | sdDataGlobal);
1109     if (doc.IsEmpty())
1110     {
1111         Manager::Get()->GetLogManager()->Log(_("Error: file 'options_") + name + _(".xml' not found"));
1112         return;
1113     }
1114     if (recursion > 5)
1115     {
1116         Manager::Get()->GetLogManager()->LogWarning(_("Warning: '") + doc + _("' not loaded due to excessive recursion"));
1117         return;
1118     }
1119     if (!options.Load(doc))
1120     {
1121         Manager::Get()->GetLogManager()->Log(_("Error parsing ") + doc);
1122         return;
1123     }
1124     wxString extends = options.GetRoot()->GetAttribute(wxT("extends"), wxEmptyString);
1125     if (!extends.IsEmpty())
1126         LoadRegExArray(extends, globalPrecedence, recursion + 1);
1127     wxXmlNode* node = options.GetRoot()->GetChildren();
1128     int depth = 0;
1129     while (node)
1130     {
1131         const wxString value = node->GetAttribute(wxT("value"), wxEmptyString);
1132         if (node->GetName() == wxT("if") && node->GetChildren())
1133         {
1134             if (EvalXMLCondition(node))
1135             {
1136                 node = node->GetChildren();
1137                 ++depth;
1138                 continue;
1139             }
1140             else if (node->GetNext() && node->GetNext()->GetName() == wxT("else") &&
1141                      node->GetNext()->GetChildren())
1142             {
1143                 node = node->GetNext()->GetChildren();
1144                 ++depth;
1145                 continue;
1146             }
1147         }
1148         else if (node->GetName() == wxT("RegEx"))
1149         {
1150             wxString tp = node->GetAttribute(wxT("type"), wxEmptyString);
1151             CompilerLineType clt = cltNormal;
1152             if      (tp == wxT("warning"))
1153                 clt = cltWarning;
1154             else if (tp == wxT("error"))
1155                 clt = cltError;
1156             else if (tp == wxT("info"))
1157                 clt = cltInfo;
1158             wxArrayString msg = GetArrayFromString(node->GetAttribute(wxT("msg"), wxEmptyString) + wxT(";0;0"));
1159             m_RegExes.push_back(RegExStruct(wxGetTranslation(node->GetAttribute(wxT("name"), wxEmptyString)), clt,
1160                                       node->GetNodeContent().Trim().Trim(false), wxAtoi(msg[0]),
1161                                       wxAtoi(node->GetAttribute(wxT("file"), wxT("0"))),
1162                                       wxAtoi(node->GetAttribute(wxT("line"), wxT("0"))),
1163                                       wxAtoi(msg[1]), wxAtoi(msg[2]) ) );
1164         }
1165         else if (node->GetName() == wxT("Common"))
1166         {
1167             LoadRegExArray(wxT("common_") + node->GetAttribute(wxT("name"), wxEmptyString),
1168                            globalPrecedence, recursion + 1);
1169         }
1170         while (!node->GetNext() && depth > 0)
1171         {
1172             node = node->GetParent();
1173             --depth;
1174         }
1175         node = node->GetNext();
1176     }
1177 }
1178 
EvalXMLCondition(const wxXmlNode * node)1179 bool Compiler::EvalXMLCondition(const wxXmlNode* node)
1180 {
1181     bool val = false;
1182     wxString test;
1183     if (node->GetAttribute(wxT("platform"), &test))
1184     {
1185         if (test == wxT("windows"))
1186             val = platform::windows;
1187         else if (test == wxT("macosx"))
1188             val = platform::macosx;
1189         else if (test == wxT("linux"))
1190             val = platform::Linux;
1191         else if (test == wxT("freebsd"))
1192             val = platform::freebsd;
1193         else if (test == wxT("netbsd"))
1194             val = platform::netbsd;
1195         else if (test == wxT("openbsd"))
1196             val = platform::openbsd;
1197         else if (test == wxT("darwin"))
1198             val = platform::darwin;
1199         else if (test == wxT("solaris"))
1200             val = platform::solaris;
1201         else if (test == wxT("unix"))
1202             val = platform::Unix;
1203     }
1204     else if (node->GetAttribute(wxT("exec"), &test))
1205     {
1206         wxArrayString cmd = GetArrayFromString(test, wxT(" "));
1207         if (cmd.IsEmpty())
1208             return false;
1209         wxString path;
1210         wxGetEnv(wxT("PATH"), &path);
1211         const wxString origPath = path;
1212         {
1213             ConfigManager* cfg = Manager::Get()->GetConfigManager(wxT("compiler"));
1214             wxString masterPath;
1215             wxString loc = (m_ParentID.IsEmpty() ? wxT("/sets/") : wxT("/user_sets/")) + m_ID;
1216             wxArrayString extraPaths;
1217             if (cfg->Exists(loc + wxT("/name")))
1218             {
1219                 masterPath = cfg->Read(loc + wxT("/master_path"), wxEmptyString);
1220                 extraPaths = MakeUniqueArray(GetArrayFromString(cfg->Read(loc + wxT("/extra_paths"), wxEmptyString)), true);
1221             }
1222             for (size_t i = 0; i < extraPaths.GetCount(); ++i)
1223                 path.Prepend(extraPaths[i] + wxPATH_SEP);
1224             if (!masterPath.IsEmpty())
1225                 path.Prepend(masterPath + wxPATH_SEP + masterPath + wxFILE_SEP_PATH + wxT("bin") + wxPATH_SEP);
1226         }
1227         wxSetEnv(wxT("PATH"), path);
1228         cmd[0] = GetExecName(cmd[0]);
1229 
1230         long ret = -1;
1231         if ( !cmd[0].IsEmpty() ) // should never be empty
1232         {
1233             int flags = wxEXEC_SYNC;
1234             #if wxCHECK_VERSION(3, 0, 0)
1235                 // Stop event-loop while wxExecute runs, to avoid a deadlock on startup,
1236                 // that occurs from time to time on wx3
1237                 flags |= wxEXEC_NOEVENTS;
1238             #else
1239                 flags |= wxEXEC_NODISABLE;
1240             #endif
1241             wxLogNull logNo; // do not warn if execution fails
1242             ret = wxExecute(GetStringFromArray(cmd, wxT(" "), false), cmd, flags);
1243         }
1244 
1245         if (ret != 0) // execution failed
1246             val = (node->GetAttribute(wxT("default"), wxEmptyString) == wxT("true"));
1247         else if (node->GetAttribute(wxT("regex"), &test))
1248         {
1249             wxRegEx re;
1250             if (re.Compile(test))
1251             {
1252                 for (size_t i = 0; i < cmd.GetCount(); ++i)
1253                 {
1254                     if (re.Matches(cmd[i]))
1255                     {
1256                         val = true;
1257                         break;
1258                     }
1259                 }
1260             }
1261         }
1262         else // execution succeeded (and no regex test given)
1263             val = true;
1264 
1265         wxSetEnv(wxT("PATH"), origPath); // restore path
1266     }
1267     return val;
1268 }
1269 
GetExecName(const wxString & name)1270 wxString Compiler::GetExecName(const wxString& name)
1271 {
1272     wxString ret = name;
1273     if (name == wxT("C"))
1274         ret = m_Programs.C;
1275     else if (name == wxT("CPP"))
1276         ret = m_Programs.CPP;
1277     else if (name == wxT("LD"))
1278         ret = m_Programs.LD;
1279     else if (name == wxT("LIB"))
1280         ret = m_Programs.LIB;
1281     else if (name == wxT("WINDRES"))
1282         ret = m_Programs.WINDRES;
1283     else if (name == wxT("MAKE"))
1284         ret = m_Programs.MAKE;
1285     return ret;
1286 }
1287