OSDN Git Service

Reduce compilation time (2)
[winmerge-jp/winmerge-jp.git] / Src / ConfigLog.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    License (GPLv2+):
3 //    This program is free software; you can redistribute it and/or modify
4 //    it under the terms of the GNU General Public License as published by
5 //    the Free Software Foundation; either version 2 of the License, or (at
6 //    your option) any later version.
7 //    
8 //    This program is distributed in the hope that it will be useful, but
9 //    WITHOUT ANY WARRANTY; without even the implied warranty of
10 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //    GNU General Public License for more details.
12 //
13 //    You should have received a copy of the GNU General Public License
14 //    along with this program; if not, write to the Free Software
15 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 /////////////////////////////////////////////////////////////////////////////
17 /** 
18  * @file  ConfigLog.cpp
19  *
20  * @brief CConfigLog implementation
21  */
22
23 #include "pch.h"
24 #include "ConfigLog.h"
25 #include <cassert>
26 #include <windows.h>
27 #include <memory>
28 #include "Constants.h"
29 #include "VersionInfo.h"
30 #include "UniFile.h"
31 #include "Plugins.h"
32 #include "TFile.h"
33 #include "paths.h"
34 #include "locality.h"
35 #include "unicoder.h"
36 #include "Environment.h"
37 #include "MergeApp.h"
38 #include "OptionsMgr.h"
39 #include "TempFile.h"
40 #include "UniFile.h"
41 #include "RegKey.h"
42
43 CConfigLog::CConfigLog()
44 : m_pfile(new UniStdioFile())
45 {
46 }
47
48 CConfigLog::~CConfigLog()
49 {
50         CloseFile();
51 }
52
53
54 /** 
55  * @brief Get details of the Compiler Version from _MSC_VER, etc.
56  *              Infer the Visual Studio version
57  */
58 static String GetCompilerVersion()
59 {
60         String sVisualStudio = _T(""); 
61 #ifdef _MSC_VER
62 #if     _MSC_VER <  1800
63         # error "** Unknown OLD Version of Visual Studio **"
64 #elif   _MSC_VER == 1800
65         sVisualStudio = _T("VS.2013 (12.0) - "); 
66 #elif   _MSC_VER == 1900
67         sVisualStudio = _T("VS.2015 (14.0) - "); 
68 #elif   _MSC_VER == 1910
69         sVisualStudio = _T("VS.2017 (15.0) - "); 
70 #elif   _MSC_VER == 1911
71         sVisualStudio = _T("VS.2017 (15.3) - "); 
72 #elif   _MSC_VER >= 1912 && _MSC_VER <  1920
73         sVisualStudio = strutils::format(_T("VS.2017 (15.%d) - "), 5 + (_MSC_VER - 1912));
74 #elif   _MSC_VER >= 1920 && _MSC_VER <  2000
75         sVisualStudio = strutils::format(_T("VS.2019 (16.%d) - "), (_MSC_VER - 1920));
76 #elif   _MSC_VER >= 2000
77         # error "** Unknown NEW Version of Visual Studio **"
78 #endif
79 #endif
80
81         return strutils::format(_T("%sC/C++ Compiler %02i.%02i.%05i.%i"),
82                 sVisualStudio.c_str(),
83                 (int)(_MSC_VER / 100), (int)(_MSC_VER % 100), (int)(_MSC_FULL_VER % 100000), _MSC_BUILD
84         );
85 }
86
87
88 /** 
89  * @brief Return logfile name and path
90  */
91 String CConfigLog::GetFileName() const
92 {
93         return m_sFileName;
94 }
95
96
97 /** 
98  * @brief Get the Modified time of fully qualified file path and name
99  */
100 static String GetLastModified(const String &path) 
101 {
102         String sPath2 = path;
103         if (sPath2[0] == '.')
104         {
105                 CVersionInfo EXEversion;
106                 String sEXEPath = paths::GetPathOnly(paths::GetLongPath(EXEversion.GetFullFileName(), false));
107                 sPath2 = sEXEPath + _T("\\") + sPath2;
108         }
109         TFile file(sPath2);
110
111         String sModifiedTime = _T("");
112         if (file.exists())
113         {
114                 Poco::Timestamp mtime(file.getLastModified());
115
116                 const int64_t r = (mtime.epochTime());
117                 sModifiedTime = locality::TimeString(&r);
118         }
119         return sModifiedTime;
120 }
121
122 /** 
123  * @brief Write plugin names
124  */
125 void CConfigLog::WritePluginsInLogFile(const wchar_t *transformationEvent)
126 {
127         CVersionInfo EXEversion;
128         String sEXEPath = paths::GetPathOnly(paths::GetLongPath(EXEversion.GetFullFileName(), false));
129
130         // get an array with the available scripts
131         PluginArray * piPluginArray; 
132
133         piPluginArray = 
134                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(transformationEvent);
135
136         for (size_t iPlugin = 0 ; iPlugin < piPluginArray->size() ; iPlugin++)
137         {
138                 const PluginInfoPtr& plugin = piPluginArray->at(iPlugin);
139
140                 String sFileName = paths::GetLongPath(plugin->m_filepath);
141                 if (sFileName.length() > sEXEPath.length())
142                         if (sFileName.substr(0, sEXEPath.length()) == sEXEPath)
143                                 sFileName = _T(".") + sFileName.erase(0, sEXEPath.length());
144                 
145                 String sModifiedTime = _T("");
146                 sModifiedTime = GetLastModified(plugin->m_filepath);
147                 if (!sModifiedTime.empty())
148                         sModifiedTime = _T("[") + sModifiedTime + _T("]");
149                 
150                 String sPluginText = strutils::format
151                         (_T("\r\n  %s%-36s path=%s  %s"),
152                         plugin->m_disabled ? _T("!") : _T(" "),
153                         plugin->m_name.c_str(),
154                         sFileName.c_str(),
155                         sModifiedTime.c_str()
156                         );
157                 m_pfile->WriteString(sPluginText);
158         }
159 }
160
161 /**
162  * @brief String wrapper around API call GetLocaleInfo
163  */
164 static String GetLocaleString(LCID locid, LCTYPE lctype)
165 {
166         TCHAR buffer[512];
167         if (!GetLocaleInfo(locid, lctype, buffer, sizeof(buffer)/sizeof(buffer[0])))
168                 buffer[0] = 0;
169         return buffer;
170 }
171
172 /**
173  * @brief Write string item
174  */
175 void CConfigLog::WriteItem(int indent, const String& key, const TCHAR *value /*= nullptr*/)
176 {
177         String text = strutils::format(value ? _T("%*.0s%s: %s\r\n") : _T("%*.0s%s:\r\n"), indent, key.c_str(), key.c_str(), value);
178         m_pfile->WriteString(text);
179 }
180
181 /**
182  * @brief Write string item
183  */
184 void CConfigLog::WriteItem(int indent, const String& key, const String &str)
185 {
186         WriteItem(indent, key, str.c_str());
187 }
188
189 /**
190  * @brief Write int item
191  */
192 void CConfigLog::WriteItem(int indent, const String& key, long value)
193 {
194         String text = strutils::format(_T("%*.0s%s: %ld\r\n"), indent, key.c_str(), key.c_str(), value);
195         m_pfile->WriteString(text);
196 }
197
198 /**
199  * @brief Write out various possibly relevant windows locale information
200  */
201 void CConfigLog::WriteLocaleSettings(unsigned locid, const String& title)
202 {
203         WriteItem(1, title);
204         WriteItem(2, _T("Def ANSI codepage"), GetLocaleString(locid, LOCALE_IDEFAULTANSICODEPAGE));
205         WriteItem(2, _T("Def OEM codepage"), GetLocaleString(locid, LOCALE_IDEFAULTCODEPAGE));
206         WriteItem(2, _T("Country"), GetLocaleString(locid, LOCALE_SENGCOUNTRY));
207         WriteItem(2, _T("Language"), GetLocaleString(locid, LOCALE_SENGLANGUAGE));
208         WriteItem(2, _T("Language code"), GetLocaleString(locid, LOCALE_ILANGUAGE));
209         WriteItem(2, _T("ISO Language code"), GetLocaleString(locid, LOCALE_SISO639LANGNAME));
210 }
211
212 /**
213  * @brief Write version of a single executable file
214  */
215 void CConfigLog::WriteVersionOf1(int indent, const String& path)
216 {
217         String path2 = path;
218         if (path2.find(_T(".\\")) == 0)
219         {
220                 // Remove "relative path" info for Win API calls.
221                 const TCHAR *pf = path2.c_str();
222                 path2 = String(pf+2);
223         }
224         String name = paths::FindFileName(path2);
225         CVersionInfo vi(path2.c_str(), true);
226         String sModifiedTime = _T("");
227         if (name != path)
228         {
229                 sModifiedTime = GetLastModified(path);
230                 if (!sModifiedTime.empty())
231                         sModifiedTime = _T("  [") + sModifiedTime + _T("]");
232         }
233         String text = strutils::format
234         (
235                 name == path
236                         ?       _T(" %*s%-18s %s=%u.%02u %s=%04u\r\n")
237                         :       _T(" %*s%-18s %s=%u.%02u %s=%04u path=%s%s\r\n"),
238                 indent,
239                 // Tilde prefix for modules currently mapped into WinMerge
240                 GetModuleHandle(path2.c_str()) 
241                         ? _T("~") 
242                         : _T("")/*name*/,
243                 name.c_str(),
244                 vi.m_dvi.cbSize > FIELD_OFFSET(DLLVERSIONINFO, dwMajorVersion)
245                         ?       _T("dllversion")
246                         :       _T("version"),
247                 vi.m_dvi.dwMajorVersion,
248                 vi.m_dvi.dwMinorVersion,
249                 vi.m_dvi.cbSize > FIELD_OFFSET(DLLVERSIONINFO, dwBuildNumber)
250                         ?       _T("dllbuild")
251                         :       _T("build"),
252                 vi.m_dvi.dwBuildNumber,
253                 path.c_str(),
254                 sModifiedTime.c_str()
255         );
256         m_pfile->WriteString(text);
257 }
258
259 /**
260  * @brief Write winmerge configuration
261  */
262 void CConfigLog::WriteWinMergeConfig()
263 {
264         TempFile tmpfile;
265         String tmppath = tmpfile.Create();
266         GetOptionsMgr()->ExportOptions(tmppath, true);
267         UniMemFile ufile;
268         if (!ufile.OpenReadOnly(tmppath))
269                 return;
270         String line;
271         bool lossy;
272         while (ufile.ReadString(line, &lossy)) 
273         {
274                 String prefix = _T("  ");
275                 if (line[0] == _T('[') )
276                         prefix = _T(" ");
277                 FileWriteString(prefix + line + _T("\r\n"));
278         }
279         ufile.Close();
280 }
281
282 /** 
283  * @brief Write logfile
284  */
285 bool CConfigLog::DoFile(String &sError)
286 {
287         CVersionInfo version;
288         String text;
289
290         String sFileName = paths::ConcatPath(env::GetMyDocuments(), WinMergeDocumentsFolder);
291         paths::CreateIfNeeded(sFileName);
292         m_sFileName = paths::ConcatPath(sFileName, _T("WinMerge.txt"));
293
294         if (!m_pfile->OpenCreateUtf8(m_sFileName))
295         {
296                 const UniFile::UniError &err = m_pfile->GetLastUniError();
297                 sError = err.GetError();
298                 return false;
299         }
300         m_pfile->SetBom(true);
301         m_pfile->WriteBom();
302
303 // Begin log
304         FileWriteString(_T("WinMerge Configuration Log\r\n"));
305         FileWriteString(_T("--------------------------\r\n"));
306         FileWriteString(_T("\r\nLog Saved to:         "));
307         FileWriteString(m_sFileName);
308         FileWriteString(_T("\r\n                >> >> Please add this information (or attach this file) when reporting bugs << <<"));
309
310 // Platform stuff
311         
312         FileWriteString(_T("\r\n\r\nWindows Info:         "));
313         text = GetWindowsVer();
314         FileWriteString(text);
315         text = GetProcessorInfo();
316         if (text != _T(""))
317         {               
318                 FileWriteString(_T("\r\n Processor:           "));
319                 FileWriteString(text);
320         }
321
322 // WinMerge stuff
323
324         FileWriteString(_T("\r\n\r\nWinMerge Info:"));
325         String sEXEFullFileName = paths::GetLongPath(version.GetFullFileName(), false);
326         FileWriteString(_T("\r\n Code File:           "));
327         FileWriteString(sEXEFullFileName);
328
329         FileWriteString(_T("\r\n Version:             "));
330         FileWriteString(version.GetProductVersion());
331         String privBuild = version.GetPrivateBuild();
332         if (!privBuild.empty())
333         {
334                 FileWriteString(_T(" + ") + privBuild);
335         }
336
337         FileWriteString(_T("\r\n Code File Modified:  "));
338         FileWriteString(GetLastModified(sEXEFullFileName));
339
340         FileWriteString(_T("\r\n Build Config:       "));
341         FileWriteString(GetBuildFlags());
342
343         FileWriteString(_T("\r\n Build Software:      "));
344         FileWriteString(GetCompilerVersion());
345
346         LPCTSTR szCmdLine = ::GetCommandLine();
347         assert(szCmdLine != nullptr);
348
349         // Skip the quoted executable file name.
350         if (szCmdLine != nullptr)
351         {
352                 szCmdLine = _tcschr(szCmdLine, '"');
353                 if (szCmdLine != nullptr)
354                 {
355                         szCmdLine += 1; // skip the opening quote.
356                         szCmdLine = _tcschr(szCmdLine, '"');
357                         if (szCmdLine != nullptr)
358                         {
359                                 szCmdLine += 1; // skip the closing quote.
360                         }
361                 }
362         }
363
364         // The command line include a space after the executable file name,
365         // which mean that empty command line will have length of one.
366         if (!szCmdLine || lstrlen(szCmdLine) < 2)
367         {
368                 szCmdLine = _T(" none");
369         }
370
371         FileWriteString(_T("\r\n\r\nCommand Line:        "));
372         FileWriteString(szCmdLine);
373
374         String sEXEPathOnly = paths::GetPathOnly(sEXEFullFileName);
375
376         FileWriteString(_T("\r\n\r\nModule Names:         '~' prefix indicates module is loaded into the WinMerge process.\r\n"));
377         FileWriteString(_T(" Windows:\r\n"));
378         WriteVersionOf1(2, _T("kernel32.dll"));
379         WriteVersionOf1(2, _T("shell32.dll"));
380         WriteVersionOf1(2, _T("shlwapi.dll"));
381         WriteVersionOf1(2, _T("COMCTL32.dll"));
382         WriteVersionOf1(2, _T("msvcrt.dll"));
383         FileWriteString(_T(        " WinMerge:            Path names are relative to the Code File's directory.\r\n"));
384         WriteVersionOf1(2, _T(".\\ShellExtensionU.dll"));
385         WriteVersionOf1(2, _T(".\\ShellExtensionX64.dll"));
386         WriteVersionOf1(2, _T(".\\Frhed\\hekseditU.dll"));
387         WriteVersionOf1(2, _T(".\\WinIMerge\\WinIMergeLib.dll"));
388         WriteVersionOf1(2, _T(".\\Merge7z\\7z.dll"));
389
390 // System settings
391         FileWriteString(_T("\r\nSystem Settings:\r\n"));
392         FileWriteString(_T(" Codepage Settings:\r\n"));
393         WriteItem(2, _T("ANSI codepage"), GetACP());
394         WriteItem(2, _T("OEM codepage"), GetOEMCP());
395 #ifndef UNICODE
396         WriteItem(2, _T("multibyte codepage"), _getmbcp());
397 #endif
398         WriteLocaleSettings(GetThreadLocale(), _T("Locale (Thread)"));
399         WriteLocaleSettings(LOCALE_USER_DEFAULT, _T("Locale (User)"));
400         WriteLocaleSettings(LOCALE_SYSTEM_DEFAULT, _T("Locale (System)"));
401
402 // Plugins
403         FileWriteString(_T("\r\nPlugins:                                '!' Prefix indicates the plugin is Disabled.\r\n"));
404         FileWriteString(    _T(" Unpackers:                             Path names are relative to the Code File's directory."));
405         WritePluginsInLogFile(L"FILE_PACK_UNPACK");
406         WritePluginsInLogFile(L"BUFFER_PACK_UNPACK");
407         WritePluginsInLogFile(L"FILE_FOLDER_PACK_UNPACK");
408         FileWriteString(_T("\r\n Prediffers: "));
409         WritePluginsInLogFile(L"FILE_PREDIFF");
410         WritePluginsInLogFile(L"BUFFER_PREDIFF");
411         FileWriteString(_T("\r\n Editor scripts: "));
412         WritePluginsInLogFile(L"EDITOR_SCRIPT");
413         if (!plugin::IsWindowsScriptThere())
414                 FileWriteString(_T("\r\n .sct scripts disabled (Windows Script Host not found)\r\n"));
415
416         FileWriteString(_T("\r\n\r\n"));
417
418 // WinMerge settings
419         FileWriteString(_T("\r\nWinMerge configuration:\r\n"));
420         WriteWinMergeConfig();
421
422         CloseFile();
423
424         return true;
425 }
426
427 /** 
428  * @brief Parse Windows version data to string.
429  * @return String describing Windows version.
430  */
431 String CConfigLog::GetWindowsVer() const
432 {
433         CRegKeyEx key;
434         if (key.QueryRegMachine(_T("Software\\Microsoft\\Windows NT\\CurrentVersion")))
435                 return key.ReadString(_T("ProductName"), _T("Unknown OS"));
436         return _T("Unknown OS");
437 }
438
439
440 /** 
441  * @brief Parse Processor Information data to string.
442  * @return String describing Windows version.
443  */
444 String CConfigLog::GetProcessorInfo() const
445 {
446         CRegKeyEx key;
447         String sProductName = _T("");
448         if (key.QueryRegMachine(_T("Hardware\\Description\\System\\CentralProcessor\\0")))
449                 sProductName = key.ReadString(_T("Identifier"), _T(""));
450         if (sProductName != _T(""))
451         {
452                 // This is the full identifier of the processor
453                 //      (e.g. "Intel64 Family 6 Model 158 Stepping 9")
454                 //      but we'll only keep the first word (e.g. "Intel64")
455                 int x = (int)sProductName.find_first_of(_T(" "));
456                 sProductName = sProductName.substr(0, x);
457         }
458
459
460         // Number of processors, Amount of memory
461         SYSTEM_INFO siSysInfo;
462         ::GetSystemInfo(&siSysInfo); 
463
464         MEMORYSTATUSEX GlobalMemoryBuffer;
465         memset(&GlobalMemoryBuffer, 0, sizeof(GlobalMemoryBuffer));
466         GlobalMemoryBuffer.dwLength = sizeof (GlobalMemoryBuffer);
467         ::GlobalMemoryStatusEx(&GlobalMemoryBuffer);
468         ULONG lInstalledMemory = (ULONG)(GlobalMemoryBuffer.ullTotalPhys / (1024*1024));
469
470         TCHAR buf[MAX_PATH];
471         swprintf_s(buf, MAX_PATH, _T("%u Logical Processors, %u MB Memory"), 
472                         siSysInfo.dwNumberOfProcessors, lInstalledMemory); 
473
474         return sProductName + _T(", ") + String(buf);
475 }
476         
477 /** 
478  * @brief Return string representation of build flags (for reporting in config log)
479  */
480 String CConfigLog::GetBuildFlags() const
481 {
482         String flags;
483
484 #if defined WIN64
485         flags += _T(" WIN64 ");
486 #elif defined WIN32
487         flags += _T(" WIN32 ");
488 #endif
489
490 #if defined UNICODE
491         flags += _T(" UNICODE ");
492 #endif
493
494 #if defined _DEBUG
495         flags += _T(" _DEBUG ");
496 #endif
497
498 #if defined TEST_WINMERGE
499         flags += _T(" TEST_WINMERGE ");
500 #endif
501
502         return flags;
503 }
504
505 bool CConfigLog::WriteLogFile(String &sError)
506 {
507         CloseFile();
508
509         return DoFile(sError);
510 }
511
512 /// Write line to file (if writing configuration log)
513 void
514 CConfigLog::FileWriteString(const String& lpsz)
515 {
516         m_pfile->WriteString(lpsz);
517 }
518
519 /**
520  * @brief Close any open file
521  */
522 void
523 CConfigLog::CloseFile()
524 {
525         if (m_pfile->IsOpen())
526                 m_pfile->Close();
527 }
528