// Copyright 2011-2020 Molecular Matters GmbH, all rights reserved. #if LC_VERSION == 1 // BEGIN EPIC MOD //#include PCH_INCLUDE // END EPIC MOD #include "LC_AppSettings.h" #include "LC_Process.h" #include "LC_Filesystem.h" #include "LC_StringUtil.h" // BEGIN EPIC MOD //#include "LC_App.h" // END EPIC MOD // BEGIN EPIC MOD #include "LC_Logging.h" // END EPIC MOD // BEGIN EPIC MOD #include "Windows/AllowWindowsPlatformTypes.h" #include #include "Windows/HideWindowsPlatformTypes.h" // END EPIC MOD namespace { // cache for compiler and linker path #if LC_64_BIT static const wchar_t* const VS2017_COMPILER_PATH = L"bin\\hostx64\\x64\\cl.exe"; static const wchar_t* const VS2015_AND_EARLIER_COMPILER_PATH = L"bin\\amd64\\cl.exe"; static const wchar_t* const VS2017_LINKER_PATH = L"bin\\hostx64\\x64\\link.exe"; static const wchar_t* const VS2015_AND_EARLIER_LINKER_PATH = L"bin\\amd64\\link.exe"; #else static const wchar_t* const VS2017_COMPILER_PATH = L"bin\\hostx86\\x86\\cl.exe"; static const wchar_t* const VS2015_AND_EARLIER_COMPILER_PATH = L"bin\\cl.exe"; static const wchar_t* const VS2017_LINKER_PATH = L"bin\\hostx86\\x86\\link.exe"; static const wchar_t* const VS2015_AND_EARLIER_LINKER_PATH = L"bin\\link.exe"; #endif static const wchar_t* const COMPILER_EXE = L"cl.exe"; static const wchar_t* const LINKER_EXE = L"link.exe"; static std::wstring g_cachedCompilerPath; static std::wstring g_cachedLinkerPath; static types::vector g_cachedAmalgamatedCppFileExtensions; static std::wstring DeterminePath(const SettingString* setting, const wchar_t* const type, const wchar_t* const vs2017Path, const wchar_t* const vs2015AndEarlierPath, const wchar_t* exeName) { // absolute paths can be used as they are, relative paths are supposed to be relative to the Live++ executable std::wstring path; const bool isRelativePath = Filesystem::IsRelativePath(setting->GetValue()); if (isRelativePath) { path = Filesystem::GetDirectory(Process::Current::GetImagePath().GetString()).GetString(); path += L"\\"; path += setting->GetValue(); } else { path = setting->GetValue(); } const Filesystem::PathAttributes& attributes = Filesystem::GetAttributes(path.c_str()); if (!Filesystem::DoesExist(attributes)) { if (path.length() != 0u) { LC_ERROR_USER("Cannot determine %S at path %S", type, path.c_str()); } return path; } if (!Filesystem::IsDirectory(attributes)) { // this is not a directory, but a full path // EPIC REMOVED LC_SUCCESS_USER("Using %S at path %S", type, path.c_str()); return path; } // try to find the compiler/linker in the given directory or any of its child directories const types::vector& files = Filesystem::EnumerateFiles(path.c_str()); // walk over all files, grabbing only cl.exe and link.exe const size_t count = files.size(); for (size_t i = 0u; i < count; ++i) { Filesystem::Path originalPath; originalPath = path.c_str(); originalPath += L"\\"; originalPath += files[i]; const Filesystem::Path lowerCaseFilename = originalPath.ToLower(); if (string::Contains(lowerCaseFilename.GetString(), vs2017Path)) { // containing the proper sub-path is not enough, we also need to check the filename, // because Visual Studio has files named cl.exe.config and link.exe.config. const Filesystem::Path filenameOnly = Filesystem::GetFilename(lowerCaseFilename.GetString()); if (string::Matches(filenameOnly.GetString(), exeName)) { LC_SUCCESS_USER("Found %S at path %S", type, originalPath.GetString()); return std::wstring(originalPath.GetString()); } } else if (string::Contains(lowerCaseFilename.GetString(), vs2015AndEarlierPath)) { // containing the proper sub-path is not enough, we also need to check the filename, // because Visual Studio has files named cl.exe.config and link.exe.config. const Filesystem::Path filenameOnly = Filesystem::GetFilename(lowerCaseFilename.GetString()); if (string::Matches(filenameOnly.GetString(), exeName)) { LC_SUCCESS_USER("Found %S at path %S", type, originalPath.GetString()); return std::wstring(originalPath.GetString()); } } } LC_ERROR_USER("Could not find %S while recursing directory %S", exeName, path.c_str()); return path; } // helper function that tries to apply a new value to any setting template static SettingType* ApplySetting(SettingType* settings[], unsigned int settingsCount, const char* const settingName, T value) { const std::wstring wideSettingName(string::ToWideString(settingName)); for (unsigned int i = 0u; i < settingsCount; ++i) { const wchar_t* name = settings[i]->GetName(); if (string::Matches(name, wideSettingName.c_str())) { // found the correct setting, apply the new value settings[i]->SetValue(value); // BEGIN EPIC MOD - Removing UI // // tell the UI to update // g_theApp.GetMainFrame()->RefreshPropertyValue(settings[i]); // END EPIC MOD return settings[i]; } } return nullptr; } } void appSettings::Startup(const wchar_t* group) { // ensure that the directories exist ::SHCreateDirectoryExW(NULL, GetLppDirectory().c_str(), NULL); ::SHCreateDirectoryExW(NULL, GetSymbolsDirectory().c_str(), NULL); g_initialWindowMode = new SettingInt ( group, L"initial_window_mode", L"Initial window mode", L"Specifies how Live++ is launched", SW_SHOWNORMAL ); g_initialWindowModeProxy = new SettingIntProxy(g_initialWindowMode); (*g_initialWindowModeProxy) .AddMapping(L"Normal", SW_SHOWNORMAL) .AddMapping(L"Minimized", SW_SHOWMINIMIZED) .AddMapping(L"Maximized", SW_SHOWMAXIMIZED); g_showFullPathInTitle = new SettingBool ( group, L"show_full_path_in_title", L"Show full path in title", L"Specifies whether the full path will be shown in the window title", false ); g_showPathFirstInTitle = new SettingBool ( group, L"show_path_first_in_title", L"Show path first in title", L"Specifies whether the path will be shown first in the window title", false ); g_receiveFocusOnRecompile = new SettingInt ( group, L"receive_focus_on_recompile", L"Receive focus on re-compile", L"Specifies when Live++ should receive focus", FocusOnRecompile::ON_SHORTCUT ); g_receiveFocusOnRecompileProxy = new SettingIntProxy(g_receiveFocusOnRecompile); (*g_receiveFocusOnRecompileProxy) .AddMapping(L"On error", FocusOnRecompile::ON_ERROR) .AddMapping(L"On success", FocusOnRecompile::ON_SUCCESS) .AddMapping(L"On shortcut", FocusOnRecompile::ON_SHORTCUT) .AddMapping(L"Never", FocusOnRecompile::NEVER); g_showNotificationOnRecompile = new SettingBool ( group, L"show_notification_on_recompile", L"Show notifications on re-compile", L"Specifies whether Live++ shows notifications when compiling", true ); g_clearLogOnRecompile = new SettingBool ( group, L"clear_log_on_recompile", L"Clear log on re-compile", L"Specifies whether Live++ clears the log when compiling", false ); g_minimizeOnClose = new SettingBool ( group, L"minimize_to_tray_on_close", L"Minimize to tray on close", L"Specifies whether Live++ should be minimized into the system tray when being closed", false ); g_keepTrayIcon = new SettingBool ( group, L"keep_system_tray_icon", L"Keep system tray icon", L"Specifies whether the Live++ icon should stay in the system tray", false ); g_playSoundOnSuccess = new SettingString ( group, L"sound_on_success", L"Play sound on success", L"Specifies a .WAV to play on successful re-compiles", L"" ); g_playSoundOnError = new SettingString ( group, L"sound_on_error", L"Play sound on error", L"Specifies a .WAV to play on failed re-compiles", L"" ); g_compileShortcut = new SettingShortcut ( group, L"compile_shortcut", L"Compile shortcut", L"Shortcut that triggers a re-compile", 0x37A // Ctrl+Alt+F11 ); g_prewarmTimeout = new SettingInt ( group, L"prewarm_timeout", L"Prewarm timeout", L"Timeout in milliseconds when prewarming compiler/linker environments", 10000 ); g_showUndecoratedNames = new SettingBool ( group, L"show_undecorated_symbol_names", L"Show undecorated symbol names", L"Specifies whether output will show undecorated symbol names", false ); g_showTimestamps = new SettingBool ( group, L"show_timestamps", L"Show timestamps", L"Specifies whether output will show timestamps", false ); g_wordWrapOutput = new SettingBool ( group, L"enable_word_wrap_output", L"Enable word wrap for output", L"Specifies whether output will be word-wrapped", false ); g_enableDevLog = new SettingBool ( group, L"enable_dev_output", L"Enable Dev output", L"Specifies whether development logs will be generated", false ); g_enableTelemetryLog = new SettingBool ( group, L"enable_telemetry_output", L"Enable Telemetry output", L"Specifies whether telemetry logs will be generated", false ); g_enableDevLogCompilands = new SettingBool ( group, L"enable_dev_compiland_output", L"Enable Dev compiland output", L"Specifies whether dev logs for compiland info will be generated", false ); g_compilerPath = new SettingString ( group, L"override_compiler_path", L"Override compiler path", L"Overrides the compiler path found in the PDB", L"" ); g_useCompilerOverrideAsFallback = new SettingBool ( group, L"override_compiler_path_as_fallback", L"Override compiler path only as fallback", L"Specifies whether Live++ uses the override compiler path only as fallback", false ); g_useCompilerEnvironment = new SettingBool ( group, L"use_compiler_environment", L"Use compiler environment", L"Specifies whether Live++ tries to find and use the compiler environment", true ); g_compilerOptions = new SettingString ( group, L"additional_compiler_options", L"Additional compiler options", L"Additional compiler options passed to the compiler when creating a patch", L"" ); g_compilerForcePchPdbs = new SettingBool ( group, L"compiler_force_pch_pdbs", L"Force use of PCH PDBs", L"Forces Live++ to make each translation unit use the same PDB as the corresponding precompiled header when re-compiling", false ); g_linkerPath = new SettingString ( group, L"override_linker_path", L"Override linker path", L"Overrides the linker path found in the PDB", L"" ); g_useLinkerOverrideAsFallback = new SettingBool ( group, L"override_linker_path_as_fallback", L"Override linker path only as fallback", L"Specifies whether Live++ uses the override linker path only as fallback", false ); g_useLinkerEnvironment = new SettingBool ( group, L"use_linker_environment", L"Use linker environment", L"Specifies whether Live++ tries to find and use the linker environment", true ); g_linkerOptions = new SettingString ( group, L"additional_linker_options", L"Additional linker options", L"Additional linker options passed to the linker when creating a patch", L"" ); g_continuousCompilationEnabled = new SettingBool ( group, L"continuous_compilation_enabled", L"Enable continuous compilation", L"Specifies whether continuous compilation is enabled", false ); g_continuousCompilationPath = new SettingString ( group, L"continuous_compilation_path", L"Directory to watch", L"Directory to watch for changes when using continuous compilation", L"" ); g_continuousCompilationTimeout = new SettingInt ( group, L"continuous_compilation_timeout", L"Timeout (ms)", L"Timeout in milliseconds used when waiting for changes", 100 ); g_virtualDriveLetter = new SettingString ( group, L"virtual_drive_letter", L"Virtual drive letter", L"Drive letter of the virtual drive to use, e.g. Z:", L"" ); g_virtualDrivePath = new SettingString ( group, L"virtual_drive_path", L"Virtual drive path", L"Path to map to the virtual drive, e.g. C:\\MyPath", L"" ); g_installCompiledPatchesMultiProcess = new SettingBool ( group, L"install_compiled_patches_multi_process", L"Install compiled patches", L"Specifies whether compiled patches are installed into launched processes belonging to an existing process group", // BEGIN EPIC MOD - changing default for restart functionality true // END EPIC MOD ); g_amalgamationSplitIntoSingleParts = new SettingBool ( group, L"amalgamation_split_into_single_parts", L"Split into single parts", L"Specifies whether amalgamated/unity files are automatically split into single files", // BEGIN EPIC MOD true // END EPIC MOD ); g_amalgamationSplitMinCppCount = new SettingInt ( group, L"amalgamation_split_min_cpp_count", L"Split threshold", L"Minimum number of .cpp files that must be included in an amalgamated/unity file before it is split", 3 ); g_amalgamationCppFileExtensions = new SettingString ( group, L"amalgamation_cpp_file_extensions", L"C/C++ file extensions", L"File extensions treated as C/C++ files when splitting amalgamated/unity files", L".cpp;.c;.cc;.c++;.cp;.cxx" ); g_ue4EnableNatVisSupport = new SettingBool ( group, L"ue4_enable_natvis_support", L"Enable NatVis support", L"Specifies whether NatVis visualizers are supported by linking with a special helper library", false ); g_fastBuildDatabasePath = new SettingString ( group, L"fastbuild_database_path", L"FASTBuild database path", L"Path to the FASTBuild database to gather dependencies from", L"" ); g_fastBuildDllName = new SettingString ( group, L"fastbuild_dll_name", L"FASTBuild DLL name", L"Name of the DLL used to load FASTBuild databases", L"LPP_FASTBuild_1_01.dll" ); } void appSettings::Shutdown(void) { delete g_initialWindowMode; delete g_initialWindowModeProxy; delete g_showFullPathInTitle; delete g_showPathFirstInTitle; delete g_receiveFocusOnRecompile; delete g_receiveFocusOnRecompileProxy; delete g_showNotificationOnRecompile; delete g_clearLogOnRecompile; delete g_minimizeOnClose; delete g_keepTrayIcon; delete g_playSoundOnSuccess; delete g_playSoundOnError; delete g_compileShortcut; delete g_prewarmTimeout; delete g_showUndecoratedNames; delete g_showTimestamps; delete g_wordWrapOutput; delete g_enableDevLog; delete g_enableTelemetryLog; delete g_enableDevLogCompilands; delete g_compilerPath; delete g_useCompilerOverrideAsFallback; delete g_useCompilerEnvironment; delete g_compilerOptions; delete g_compilerForcePchPdbs; delete g_linkerPath; delete g_useLinkerOverrideAsFallback; delete g_useLinkerEnvironment; delete g_linkerOptions; delete g_continuousCompilationEnabled; delete g_continuousCompilationPath; delete g_continuousCompilationTimeout; delete g_virtualDriveLetter; delete g_virtualDrivePath; delete g_installCompiledPatchesMultiProcess; delete g_amalgamationSplitIntoSingleParts; delete g_amalgamationSplitMinCppCount; delete g_amalgamationCppFileExtensions; delete g_ue4EnableNatVisSupport; delete g_fastBuildDatabasePath; delete g_fastBuildDllName; } std::wstring appSettings::GetLppDirectory(void) { wchar_t* knownPath = nullptr; ::SHGetKnownFolderPath(FOLDERID_LocalAppData, 0u, NULL, &knownPath); std::wstring directory(knownPath); directory += L"\\Live++"; ::CoTaskMemFree(knownPath); return directory; } std::wstring appSettings::GetSymbolsDirectory(void) { std::wstring directory(GetLppDirectory()); directory += L"\\Symbols"; return directory; } std::wstring appSettings::GetUserSettingsPath(void) { // user settings are stored in the %localappdata%\Live++ directory std::wstring iniPath(GetLppDirectory()); #if LC_64_BIT iniPath += L"\\LPP_x64.ini"; #else iniPath += L"\\LPP_x86.ini"; #endif return Filesystem::NormalizePath(iniPath.c_str()).GetString(); } std::wstring appSettings::GetProjectSettingsPath(void) { // project settings are stored next to the Live++ executable const std::wstring& imagePath = Process::Current::GetImagePath().GetString(); std::wstring iniPath(Filesystem::GetDirectory(imagePath.c_str()).GetString()); #if LC_64_BIT iniPath += L"\\LPP_x64.ini"; #else iniPath += L"\\LPP_x86.ini"; #endif return Filesystem::NormalizePath(iniPath.c_str()).GetString(); } std::wstring appSettings::GetCompilerPath(void) { return g_cachedCompilerPath; } std::wstring appSettings::GetLinkerPath(void) { return g_cachedLinkerPath; } const types::vector& appSettings::GetAmalgamatedCppFileExtensions(void) { return g_cachedAmalgamatedCppFileExtensions; } void appSettings::UpdateCompilerPathCache(void) { g_cachedCompilerPath = DeterminePath(g_compilerPath, L"compiler", VS2017_COMPILER_PATH, VS2015_AND_EARLIER_COMPILER_PATH, COMPILER_EXE); } void appSettings::UpdateLinkerPathCache(void) { g_cachedLinkerPath = DeterminePath(g_linkerPath, L"linker", VS2017_LINKER_PATH, VS2015_AND_EARLIER_LINKER_PATH, LINKER_EXE); } void appSettings::UpdatePathCache(void) { UpdateCompilerPathCache(); UpdateLinkerPathCache(); } void appSettings::UpdateAmalgamatedCppFileExtensions(void) { g_cachedAmalgamatedCppFileExtensions.clear(); g_cachedAmalgamatedCppFileExtensions.reserve(16u); const wchar_t DELIMITER = L';'; const std::wstring extensions = g_amalgamationCppFileExtensions->GetValue(); size_t start = extensions.find_first_not_of(DELIMITER); size_t end = start; while (start != std::string::npos) { // first find the next occurrence of the delimiter, then push the token into the vector, // then skip all occurrences of the delimiter until we find the start of the next token end = extensions.find(DELIMITER, start); g_cachedAmalgamatedCppFileExtensions.push_back(extensions.substr(start, end - start)); start = extensions.find_first_not_of(DELIMITER, end); } } void appSettings::ApplySettingBool(const char* const settingName, bool value) { const unsigned int COUNT = 21u; SettingBool* settings[COUNT] = { g_showFullPathInTitle, g_showPathFirstInTitle, g_showNotificationOnRecompile, g_clearLogOnRecompile, g_minimizeOnClose, g_keepTrayIcon, g_showUndecoratedNames, g_showTimestamps, g_wordWrapOutput, g_enableDevLog, g_enableTelemetryLog, g_enableDevLogCompilands, g_useCompilerOverrideAsFallback, g_useCompilerEnvironment, g_compilerForcePchPdbs, g_useLinkerOverrideAsFallback, g_useLinkerEnvironment, g_continuousCompilationEnabled, g_installCompiledPatchesMultiProcess, g_amalgamationSplitIntoSingleParts, g_ue4EnableNatVisSupport }; const SettingBool* setting = ApplySetting(settings, COUNT, settingName, value); if (!setting) { LC_ERROR_USER("Cannot apply value for bool setting %s", settingName); } } void appSettings::ApplySettingInt(const char* const settingName, int value) { // try int settings first { const unsigned int COUNT = 5u; SettingInt* settings[COUNT] = { g_initialWindowMode, g_receiveFocusOnRecompile, g_continuousCompilationTimeout, g_prewarmTimeout, g_amalgamationSplitMinCppCount }; const SettingInt* setting = ApplySetting(settings, COUNT, settingName, value); if (setting) { return; } } // try shortcut setting second { const std::wstring wideSettingName(string::ToWideString(settingName)); const wchar_t* name = g_compileShortcut->GetName(); if (string::Matches(name, wideSettingName.c_str())) { // found the correct setting, apply the new value g_compileShortcut->SetValue(value); // BEGIN EPIC MOD - Removing UI // // tell the UI to update // g_theApp.GetMainFrame()->RefreshPropertyValue(g_compileShortcut); // END EPIC MOD return; } } LC_ERROR_USER("Cannot apply value for int setting %s", settingName); } void appSettings::ApplySettingString(const char* const settingName, const wchar_t* const value) { // try string settings first { const unsigned int COUNT = 12u; SettingString* settings[COUNT] = { g_playSoundOnSuccess, g_playSoundOnError, g_compilerPath, g_compilerOptions, g_linkerPath, g_linkerOptions, g_continuousCompilationPath, g_virtualDriveLetter, g_virtualDrivePath, g_amalgamationCppFileExtensions, g_fastBuildDatabasePath, g_fastBuildDllName }; // BEGIN EPIC MOD if (strcmp(settingName, "Vfs") == 0) { const wchar_t* semi = wcschr(value, ';'); Filesystem::Path virtualPath(value, semi - value); Filesystem::AddVfsEntry(virtualPath.GetString(), semi + 1); return; } // END EPIC MOD const SettingString* setting = ApplySetting(settings, COUNT, settingName, value); if (setting) { // update cache when compiler path or linker path changes if (setting == g_compilerPath) { UpdateCompilerPathCache(); } else if (setting == g_linkerPath) { UpdateLinkerPathCache(); } // update cache when amalgamated C++ file extensions change else if (setting == g_amalgamationCppFileExtensions) { UpdateAmalgamatedCppFileExtensions(); } return; } } // try proxies second { const unsigned int COUNT = 2u; SettingIntProxy* settings[COUNT] = { g_initialWindowModeProxy, g_receiveFocusOnRecompileProxy }; for (unsigned int i = 0u; i < COUNT; ++i) { const int mappedValue = settings[i]->MapStringToInt(value); if (mappedValue != -1) { // found the correct setting settings[i]->GetSetting()->SetValue(mappedValue); // BEGIN EPIC MOD - Removing UI // // tell the UI to update // g_theApp.GetMainFrame()->RefreshPropertyValue(settings[i]); // END EPIC MOD return; } } } LC_ERROR_USER("Cannot apply value for string setting %s", settingName); } SettingInt* appSettings::g_initialWindowMode = nullptr; SettingIntProxy* appSettings::g_initialWindowModeProxy = nullptr; SettingBool* appSettings::g_showFullPathInTitle = nullptr; SettingBool* appSettings::g_showPathFirstInTitle = nullptr; SettingInt* appSettings::g_receiveFocusOnRecompile = nullptr; SettingIntProxy* appSettings::g_receiveFocusOnRecompileProxy = nullptr; SettingBool* appSettings::g_showNotificationOnRecompile = nullptr; SettingBool* appSettings::g_clearLogOnRecompile = nullptr; SettingBool* appSettings::g_minimizeOnClose = nullptr; SettingBool* appSettings::g_keepTrayIcon = nullptr; SettingString* appSettings::g_playSoundOnSuccess = nullptr; SettingString* appSettings::g_playSoundOnError = nullptr; SettingShortcut* appSettings::g_compileShortcut = nullptr; SettingInt* appSettings::g_prewarmTimeout = nullptr; SettingBool* appSettings::g_showUndecoratedNames = nullptr; SettingBool* appSettings::g_showTimestamps = nullptr; SettingBool* appSettings::g_wordWrapOutput = nullptr; SettingBool* appSettings::g_enableDevLog = nullptr; SettingBool* appSettings::g_enableTelemetryLog = nullptr; SettingBool* appSettings::g_enableDevLogCompilands = nullptr; SettingString* appSettings::g_compilerPath = nullptr; SettingBool* appSettings::g_useCompilerOverrideAsFallback = nullptr; SettingBool* appSettings::g_useCompilerEnvironment = nullptr; SettingString* appSettings::g_compilerOptions = nullptr; SettingBool* appSettings::g_compilerForcePchPdbs = nullptr; SettingString* appSettings::g_linkerPath = nullptr; SettingBool* appSettings::g_useLinkerOverrideAsFallback = nullptr; SettingBool* appSettings::g_useLinkerEnvironment = nullptr; SettingString* appSettings::g_linkerOptions = nullptr; SettingBool* appSettings::g_continuousCompilationEnabled = nullptr; SettingString* appSettings::g_continuousCompilationPath = nullptr; SettingInt* appSettings::g_continuousCompilationTimeout = nullptr; SettingString* appSettings::g_virtualDriveLetter = nullptr; SettingString* appSettings::g_virtualDrivePath = nullptr; SettingBool* appSettings::g_installCompiledPatchesMultiProcess = nullptr; SettingBool* appSettings::g_amalgamationSplitIntoSingleParts = nullptr; SettingInt* appSettings::g_amalgamationSplitMinCppCount = nullptr; SettingString* appSettings::g_amalgamationCppFileExtensions = nullptr; SettingBool* appSettings::g_ue4EnableNatVisSupport = nullptr; SettingString* appSettings::g_fastBuildDatabasePath = nullptr; SettingString* appSettings::g_fastBuildDllName = nullptr; #endif