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: 11525 $
6  * $Id: compilerMINGW.cpp 11525 2018-12-15 16:16:44Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/compilergcc/compilerMINGW.cpp $
8  */
9 
10 #include <sdk.h>
11 #include "compilerMINGW.h"
12 #include <wx/intl.h>
13 #include <wx/regex.h>
14 #include <wx/config.h>
15 #include <wx/fileconf.h>
16 #include <wx/msgdlg.h>
17 #include <wx/filename.h>
18 #include <wx/filefn.h>
19 #include "manager.h"
20 #include "macrosmanager.h"
21 #include "logmanager.h"
22 #include "compilerMINGWgenerator.h"
23 
24 #include <configmanager.h>
25 
26 #ifdef __WXMSW__
27     #include <wx/dir.h>
28     #include <wx/msw/registry.h>
29 #endif
30 
CompilerMINGW(const wxString & name,const wxString & ID)31 CompilerMINGW::CompilerMINGW(const wxString& name, const wxString& ID)
32     : Compiler(name, ID)
33 {
34     m_Weight = 4;
35     Reset();
36 }
37 
~CompilerMINGW()38 CompilerMINGW::~CompilerMINGW()
39 {
40     //dtor
41 }
42 
CreateCopy()43 Compiler * CompilerMINGW::CreateCopy()
44 {
45     return (new CompilerMINGW(*this));
46 }
47 
GetCommandGenerator(cbProject * project)48 CompilerCommandGenerator* CompilerMINGW::GetCommandGenerator(cbProject *project)
49 {
50     CompilerMINGWGenerator *generator = new CompilerMINGWGenerator;
51     generator->Init(project);
52     return generator;
53 }
54 
AutoDetectInstallationDir()55 AutoDetectResult CompilerMINGW::AutoDetectInstallationDir()
56 {
57     // try to find MinGW in environment variable PATH first
58     wxString pathValues;
59     wxGetEnv(_T("PATH"), &pathValues);
60     if (!pathValues.IsEmpty())
61     {
62         wxString sep = platform::windows ? _T(";") : _T(":");
63         wxChar pathSep = platform::windows ? _T('\\') : _T('/');
64         wxArrayString pathArray = GetArrayFromString(pathValues, sep);
65         for (size_t i = 0; i < pathArray.GetCount(); ++i)
66         {
67             if (wxFileExists(pathArray[i] + pathSep + m_Programs.C))
68             {
69                 if (pathArray[i].AfterLast(pathSep).IsSameAs(_T("bin")))
70                 {
71                     m_MasterPath = pathArray[i].BeforeLast(pathSep);
72                     return adrDetected;
73                 }
74             }
75         }
76     }
77 
78     wxString sep = wxFileName::GetPathSeparator();
79     if (platform::windows)
80     {
81         // look first if MinGW was installed with Code::Blocks (new in beta6)
82         m_MasterPath = ConfigManager::GetExecutableFolder();
83         if (!wxFileExists(m_MasterPath + sep + _T("bin") + sep + m_Programs.C))
84             // if that didn't do it, look under C::B\MinGW, too (new in 08.02)
85             m_MasterPath += sep + _T("MinGW");
86         if (!wxFileExists(m_MasterPath + sep + _T("bin") + sep + m_Programs.C))
87         {
88             // no... search for MinGW installation dir
89             wxString windir = wxGetOSDirectory();
90             wxFileConfig ini(_T(""), _T(""), windir + _T("/MinGW.ini"), _T(""), wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_NO_ESCAPE_CHARACTERS);
91             m_MasterPath = ini.Read(_T("/InstallSettings/InstallPath"), _T("C:\\MinGW"));
92             if (!wxFileExists(m_MasterPath + sep + _T("bin") + sep + m_Programs.C))
93             {
94 #ifdef __WXMSW__ // for wxRegKey
95                 // not found...
96                 // look for dev-cpp installation
97                 wxRegKey key; // defaults to HKCR
98                 key.SetName(_T("HKEY_LOCAL_MACHINE\\Software\\Dev-C++"));
99                 if (key.Exists() && key.Open(wxRegKey::Read))
100                 {
101                     // found; read it
102                     key.QueryValue(_T("Install_Dir"), m_MasterPath);
103                 }
104                 else
105                 {
106                     // installed by inno-setup
107                     // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Minimalist GNU for Windows 4.1_is1
108                     // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\TDM-GCC
109                     wxString name;
110                     long index;
111                     key.SetName(_T("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"));
112                     for (int i = 0; i < 2; ++i)
113                     {
114                         bool ok = key.GetFirstKey(name, index);
115                         while (ok && !name.StartsWith(wxT("Minimalist GNU for Windows")) && name != wxT("TDM-GCC"))
116                         {
117                             ok = key.GetNextKey(name, index);
118                         }
119                         if (ok)
120                         {
121                             name = key.GetName() + wxT("\\") + name;
122                             key.SetName(name);
123                             if (key.Exists() && key.Open(wxRegKey::Read))
124                             {
125                                 key.QueryValue(wxT("InstallLocation"), m_MasterPath);
126                                 // determine configuration, eg: "x86_64-w64-mingw32-gcc.exe"
127                                 wxDir binFolder(m_MasterPath + sep + wxT("bin"));
128                                 if (binFolder.IsOpened() && binFolder.GetFirst(&name, wxT("*mingw32-gcc*.exe"), wxDIR_FILES))
129                                 {
130                                     m_Programs.C = name;
131                                     while (binFolder.GetNext(&name))
132                                     {
133                                         if (name.Length() < m_Programs.C.Length())
134                                             m_Programs.C = name; // avoid "x86_64-w64-mingw32-gcc-4.8.1.exe"
135                                     }
136                                     m_Programs.CPP = m_Programs.C;
137                                     m_Programs.CPP.Replace(wxT("mingw32-gcc"), wxT("mingw32-g++"));
138                                     m_Programs.LD = m_Programs.CPP;
139                                     break;
140                                 }
141                             }
142                         }
143                         // on 64 bit Windows
144                         key.SetName(wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"));
145                     }
146                 }
147 #endif
148             }
149             // check for PortableApps.com installation
150             if (!wxFileExists(m_MasterPath + sep + _T("bin") + sep + m_Programs.C))
151             {
152                 wxString drive = wxFileName(ConfigManager::GetExecutableFolder()).GetVolume() + wxT(":\\");
153                 if (wxFileExists(drive + wxT("PortableApps\\CommonFiles\\MinGW\\bin\\") + m_Programs.C))
154                     m_MasterPath = drive + wxT("PortableApps\\CommonFiles\\MinGW");
155                 else if (wxFileExists(drive + wxT("CommonFiles\\MinGW\\bin\\") + m_Programs.C))
156                     m_MasterPath = drive + wxT("CommonFiles\\MinGW");
157                 else if (wxFileExists(drive + wxT("MinGW\\bin\\") + m_Programs.C))
158                     m_MasterPath = drive + wxT("MinGW");
159             }
160         }
161     }
162     else
163         m_MasterPath = _T("/usr");
164 
165     AutoDetectResult ret = wxFileExists(m_MasterPath + sep + _T("bin") + sep + m_Programs.C) ? adrDetected : adrGuessed;
166     // don't add lib/include dirs. GCC knows where its files are located
167 
168     SetVersionString();
169     return ret;
170 }
171 
SetVersionString()172 void CompilerMINGW::SetVersionString()
173 {
174 //    Manager::Get()->GetLogManager()->DebugLog(_T("Compiler detection for compiler ID: '") + GetID() + _T("' (parent ID= '") + GetParentID() + _T("')"));
175 
176     wxArrayString output, errors;
177     wxString sep = wxFileName::GetPathSeparator();
178     wxString master_path = m_MasterPath;
179     wxString compiler_exe = m_Programs.C;
180 
181     /* We should read the master path from the configuration manager as
182      * the m_MasterPath is empty if AutoDetectInstallationDir() is not
183      * called
184      */
185     ConfigManager* cmgr = Manager::Get()->GetConfigManager(_T("compiler"));
186     if (cmgr)
187     {
188         wxString settings_path;
189         wxString compiler_path;
190         /* Differ between user-defined compilers (copies of base compilers) */
191         if (GetParentID().IsEmpty())
192         {
193             settings_path = _T("/sets/")      + GetID() + _T("/master_path");
194             compiler_path = _T("/sets/")      + GetID() + _T("/c_compiler");
195         }
196         else
197         {
198             settings_path = _T("/user_sets/") + GetID() + _T("/master_path");
199             compiler_path = _T("/user_sets/") + GetID() + _T("/c_compiler");
200         }
201         cmgr->Read(settings_path, &master_path);
202         cmgr->Read(compiler_path, &compiler_exe);
203     }
204     if (master_path.IsEmpty())
205     {
206         /* Notice: In general this is bad luck as e.g. all copies of a
207          * compiler have a different path, most likely.
208          * Thus the following might even return a wrong command!
209          */
210         if (platform::windows)
211             master_path = _T("C:\\MinGW");
212         else
213             master_path = _T("/usr");
214     }
215     wxString gcc_command = master_path + sep + _T("bin") + sep + compiler_exe;
216 
217     Manager::Get()->GetMacrosManager()->ReplaceMacros(gcc_command);
218     if (!wxFileExists(gcc_command))
219     {
220 //        Manager::Get()->GetLogManager()->DebugLog(_T("Compiler version detection: Compiler not found: ") + gcc_command);
221         return;
222     }
223 
224 //    Manager::Get()->GetLogManager()->DebugLog(_T("Compiler version detection: Issuing command: ") + gcc_command);
225 
226     int flags = wxEXEC_SYNC;
227 #if wxCHECK_VERSION(3, 0, 0)
228     // Stop event-loop while wxExecute runs, to avoid a deadlock on startup,
229     // that occurs from time to time on wx3
230     flags |= wxEXEC_NOEVENTS;
231 #else
232     flags |= wxEXEC_NODISABLE;
233 #endif
234     long result = wxExecute(gcc_command + _T(" --version"), output, errors, flags );
235     if(result != 0)
236     {
237 //        Manager::Get()->GetLogManager()->DebugLog(_T("Compiler version detection: Error executing command."));
238     }
239     else
240     {
241         if (output.GetCount() > 0)
242         {
243 //            Manager::Get()->GetLogManager()->DebugLog(_T("Extracting compiler version from: ") + output[0]);
244             wxRegEx reg_exp;
245             if (reg_exp.Compile(_T("[0-9]+[.][0-9]+[.][0-9]+")) && reg_exp.Matches(output[0]))
246             {
247                 m_VersionString = reg_exp.GetMatch(output[0]);
248 //                Manager::Get()->GetLogManager()->DebugLog(_T("Compiler version via RegExp: ") + m_VersionString);
249             }
250             else
251             {
252                 m_VersionString = output[0].Mid(10);
253                 m_VersionString = m_VersionString.Left(5);
254                 m_VersionString.Trim(false);
255 //                Manager::Get()->GetLogManager()->DebugLog(_T("Compiler version: ") + m_VersionString);
256             }
257         }
258     }
259 }
260