| #include "cmVisualStudioGeneratorOptions.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <map> | 
 | #include <sstream> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include <cm/iterator> | 
 |  | 
 | #include "cmAlgorithms.h" | 
 | #include "cmLocalVisualStudioGenerator.h" | 
 | #include "cmOutputConverter.h" | 
 | #include "cmRange.h" | 
 | #include "cmStringAlgorithms.h" | 
 | #include "cmSystemTools.h" | 
 |  | 
 | static void cmVS10EscapeForMSBuild(std::string& ret) | 
 | { | 
 |   cmSystemTools::ReplaceString(ret, ";", "%3B"); | 
 | } | 
 |  | 
 | cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions( | 
 |   cmLocalVisualStudioGenerator* lg, Tool tool, cmVS7FlagTable const* table, | 
 |   cmVS7FlagTable const* extraTable) | 
 |   : cmIDEOptions() | 
 |   , LocalGenerator(lg) | 
 |   , Version(lg->GetVersion()) | 
 |   , CurrentTool(tool) | 
 | { | 
 |   // Store the given flag tables. | 
 |   this->AddTable(table); | 
 |   this->AddTable(extraTable); | 
 |  | 
 |   // Preprocessor definitions are not allowed for linker tools. | 
 |   this->AllowDefine = (tool != Linker); | 
 |  | 
 |   // include directories are not allowed for linker tools. | 
 |   this->AllowInclude = (tool != Linker); | 
 |  | 
 |   // Slash options are allowed for VS. | 
 |   this->AllowSlash = true; | 
 |  | 
 |   this->FortranRuntimeDebug = false; | 
 |   this->FortranRuntimeDLL = false; | 
 |   this->FortranRuntimeMT = false; | 
 |  | 
 |   this->UnknownFlagField = "AdditionalOptions"; | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::AddTable(cmVS7FlagTable const* table) | 
 | { | 
 |   if (table) { | 
 |     for (int i = 0; i < FlagTableCount; ++i) { | 
 |       if (!this->FlagTable[i]) { | 
 |         this->FlagTable[i] = table; | 
 |         break; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::ClearTables() | 
 | { | 
 |   for (int i = 0; i < FlagTableCount; ++i) { | 
 |     this->FlagTable[i] = nullptr; | 
 |   } | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::FixExceptionHandlingDefault() | 
 | { | 
 |   // Exception handling is on by default because the platform file has | 
 |   // "/EHsc" in the flags.  Normally, that will override this | 
 |   // initialization to off, but the user has the option of removing | 
 |   // the flag to disable exception handling.  When the user does | 
 |   // remove the flag we need to override the IDE default of on. | 
 |   switch (this->Version) { | 
 |     case cmGlobalVisualStudioGenerator::VSVersion::VS10: | 
 |     case cmGlobalVisualStudioGenerator::VSVersion::VS11: | 
 |     case cmGlobalVisualStudioGenerator::VSVersion::VS12: | 
 |     case cmGlobalVisualStudioGenerator::VSVersion::VS14: | 
 |     case cmGlobalVisualStudioGenerator::VSVersion::VS15: | 
 |     case cmGlobalVisualStudioGenerator::VSVersion::VS16: | 
 |     case cmGlobalVisualStudioGenerator::VSVersion::VS17: | 
 |       // by default VS puts <ExceptionHandling></ExceptionHandling> empty | 
 |       // for a project, to make our projects look the same put a new line | 
 |       // and space over for the closing </ExceptionHandling> as the default | 
 |       // value | 
 |       this->FlagMap["ExceptionHandling"] = "\n      "; | 
 |       break; | 
 |     default: | 
 |       this->FlagMap["ExceptionHandling"] = "0"; | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::SetVerboseMakefile(bool verbose) | 
 | { | 
 |   // If verbose makefiles have been requested and the /nologo option | 
 |   // was not given explicitly in the flags we want to add an attribute | 
 |   // to the generated project to disable logo suppression.  Otherwise | 
 |   // the GUI default is to enable suppression. | 
 |   // | 
 |   // On Visual Studio 10 (and later!), the value of this attribute should be | 
 |   // an empty string, instead of "FALSE", in order to avoid a warning: | 
 |   //   "cl ... warning D9035: option 'nologo-' has been deprecated" | 
 |   // | 
 |   if (verbose && | 
 |       this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) { | 
 |     this->FlagMap["SuppressStartupBanner"] = | 
 |       this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10 ? "FALSE" | 
 |                                                                      : ""; | 
 |   } | 
 | } | 
 |  | 
 | bool cmVisualStudioGeneratorOptions::IsDebug() const | 
 | { | 
 |   if (this->CurrentTool != CSharpCompiler) { | 
 |     return this->FlagMap.find("DebugInformationFormat") != this->FlagMap.end(); | 
 |   } | 
 |   std::map<std::string, FlagValue>::const_iterator i = | 
 |     this->FlagMap.find("DebugType"); | 
 |   if (i != this->FlagMap.end()) { | 
 |     if (i->second.size() == 1) { | 
 |       return i->second[0] != "none"; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool cmVisualStudioGeneratorOptions::IsWinRt() const | 
 | { | 
 |   return this->FlagMap.find("CompileAsWinRT") != this->FlagMap.end(); | 
 | } | 
 |  | 
 | bool cmVisualStudioGeneratorOptions::IsManaged() const | 
 | { | 
 |   return this->FlagMap.find("CompileAsManaged") != this->FlagMap.end(); | 
 | } | 
 |  | 
 | bool cmVisualStudioGeneratorOptions::UsingUnicode() const | 
 | { | 
 |   // Look for a _UNICODE definition. | 
 |   for (std::string const& di : this->Defines) { | 
 |     if (di == "_UNICODE") { | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 | bool cmVisualStudioGeneratorOptions::UsingSBCS() const | 
 | { | 
 |   // Look for a _SBCS definition. | 
 |   for (std::string const& di : this->Defines) { | 
 |     if (di == "_SBCS") { | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration() | 
 | { | 
 |   // Extract temporary values stored by our flag table. | 
 |   FlagValue arch = this->TakeFlag("cmake-temp-arch"); | 
 |   FlagValue code = this->TakeFlag("cmake-temp-code"); | 
 |   FlagValue gencode = this->TakeFlag("cmake-temp-gencode"); | 
 |  | 
 |   // No -code allowed without -arch. | 
 |   if (arch.empty()) { | 
 |     code.clear(); | 
 |   } | 
 |  | 
 |   if (arch.empty() && gencode.empty()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Create a CodeGeneration field with [arch],[code] syntax in each entry. | 
 |   // CUDA will convert it to `-gencode=arch=[arch],code="[code],[arch]"`. | 
 |   FlagValue& result = this->FlagMap["CodeGeneration"]; | 
 |  | 
 |   // First entries for the -arch=<arch> [-code=<code>,...] pair. | 
 |   if (!arch.empty()) { | 
 |     std::string arch_name = arch[0]; | 
 |     if (arch_name == "all" || arch_name == "all-major") { | 
 |       AppendFlagString("AdditionalOptions", "-arch=" + arch_name); | 
 |       return; | 
 |     } | 
 |     std::vector<std::string> codes; | 
 |     if (!code.empty()) { | 
 |       codes = cmTokenize(code[0], ","); | 
 |     } | 
 |     if (codes.empty()) { | 
 |       codes.push_back(arch_name); | 
 |       // nvcc -arch=<arch> has a special case that allows a real | 
 |       // architecture to be specified instead of a virtual arch. | 
 |       // It translates to -arch=<virtual> -code=<real>. | 
 |       cmSystemTools::ReplaceString(arch_name, "sm_", "compute_"); | 
 |     } | 
 |     for (std::string const& c : codes) { | 
 |       std::string entry = arch_name + "," + c; | 
 |       result.push_back(entry); | 
 |     } | 
 |   } | 
 |  | 
 |   // Now add entries for the following signatures: | 
 |   // -gencode=<arch>,<code> | 
 |   // -gencode=<arch>,[<code1>,<code2>] | 
 |   // -gencode=<arch>,"<code1>,<code2>" | 
 |   for (std::string const& e : gencode) { | 
 |     std::string entry = e; | 
 |     cmSystemTools::ReplaceString(entry, "arch=", ""); | 
 |     cmSystemTools::ReplaceString(entry, "code=", ""); | 
 |     cmSystemTools::ReplaceString(entry, "[", ""); | 
 |     cmSystemTools::ReplaceString(entry, "]", ""); | 
 |     cmSystemTools::ReplaceString(entry, "\"", ""); | 
 |  | 
 |     std::vector<std::string> codes = cmTokenize(entry, ","); | 
 |     if (codes.size() >= 2) { | 
 |       auto gencode_arch = cm::cbegin(codes); | 
 |       for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) { | 
 |         std::string code_entry = *gencode_arch + "," + *ci; | 
 |         result.push_back(code_entry); | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::FixManifestUACFlags() | 
 | { | 
 |   static std::string const ENABLE_UAC = "EnableUAC"; | 
 |   if (!HasFlag(ENABLE_UAC)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   const std::string uacFlag = GetFlag(ENABLE_UAC); | 
 |   std::vector<std::string> subOptions; | 
 |   cmsys::SystemTools::Split(uacFlag, subOptions, ' '); | 
 |   if (subOptions.empty()) { | 
 |     AddFlag(ENABLE_UAC, "true"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (subOptions.size() == 1 && subOptions[0] == "NO") { | 
 |     AddFlag(ENABLE_UAC, "false"); | 
 |     return; | 
 |   } | 
 |  | 
 |   std::map<std::string, std::string> uacMap; | 
 |   uacMap["level"] = "UACExecutionLevel"; | 
 |   uacMap["uiAccess"] = "UACUIAccess"; | 
 |  | 
 |   std::map<std::string, std::string> uacExecuteLevelMap; | 
 |   uacExecuteLevelMap["asInvoker"] = "AsInvoker"; | 
 |   uacExecuteLevelMap["highestAvailable"] = "HighestAvailable"; | 
 |   uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator"; | 
 |  | 
 |   for (std::string const& subopt : subOptions) { | 
 |     std::vector<std::string> keyValue; | 
 |     cmsys::SystemTools::Split(subopt, keyValue, '='); | 
 |     if (keyValue.size() != 2 || (uacMap.find(keyValue[0]) == uacMap.end())) { | 
 |       // ignore none key=value option or unknown flags | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (keyValue[1].front() == '\'' && keyValue[1].back() == '\'') { | 
 |       keyValue[1] = keyValue[1].substr( | 
 |         1, std::max(std::string::size_type(0), keyValue[1].length() - 2)); | 
 |     } | 
 |  | 
 |     if (keyValue[0] == "level") { | 
 |       if (uacExecuteLevelMap.find(keyValue[1]) == uacExecuteLevelMap.end()) { | 
 |         // unknown level value | 
 |         continue; | 
 |       } | 
 |  | 
 |       AddFlag(uacMap[keyValue[0]], uacExecuteLevelMap[keyValue[1]]); | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (keyValue[0] == "uiAccess") { | 
 |       if (keyValue[1] != "true" && keyValue[1] != "false") { | 
 |         // unknown uiAccess value | 
 |         continue; | 
 |       } | 
 |       AddFlag(uacMap[keyValue[0]], keyValue[1]); | 
 |       continue; | 
 |     } | 
 |  | 
 |     // unknown sub option | 
 |   } | 
 |  | 
 |   AddFlag(ENABLE_UAC, "true"); | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::Parse(const std::string& flags) | 
 | { | 
 |   // Parse the input string as a windows command line since the string | 
 |   // is intended for writing directly into the build files. | 
 |   std::vector<std::string> args; | 
 |   cmSystemTools::ParseWindowsCommandLine(flags.c_str(), args); | 
 |  | 
 |   // Process flags that need to be represented specially in the IDE | 
 |   // project file. | 
 |   for (std::string const& ai : args) { | 
 |     this->HandleFlag(ai); | 
 |   } | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::ParseFinish() | 
 | { | 
 |   if (this->CurrentTool == FortranCompiler) { | 
 |     // "RuntimeLibrary" attribute values: | 
 |     //  "rtMultiThreaded", "0", /threads /libs:static | 
 |     //  "rtMultiThreadedDLL", "2", /threads /libs:dll | 
 |     //  "rtMultiThreadedDebug", "1", /threads /dbglibs /libs:static | 
 |     //  "rtMultiThreadedDebugDLL", "3", /threads /dbglibs /libs:dll | 
 |     // These seem unimplemented by the IDE: | 
 |     //  "rtSingleThreaded", "4", /libs:static | 
 |     //  "rtSingleThreadedDLL", "10", /libs:dll | 
 |     //  "rtSingleThreadedDebug", "5", /dbglibs /libs:static | 
 |     //  "rtSingleThreadedDebugDLL", "11", /dbglibs /libs:dll | 
 |     std::string rl = | 
 |       cmStrCat("rtMultiThreaded", this->FortranRuntimeDebug ? "Debug" : "", | 
 |                this->FortranRuntimeDLL ? "DLL" : ""); | 
 |     this->FlagMap["RuntimeLibrary"] = rl; | 
 |   } | 
 |  | 
 |   if (this->CurrentTool == CudaCompiler) { | 
 |     std::map<std::string, FlagValue>::iterator i = | 
 |       this->FlagMap.find("CudaRuntime"); | 
 |     if (i != this->FlagMap.end() && i->second.size() == 1) { | 
 |       std::string& cudaRuntime = i->second[0]; | 
 |       if (cudaRuntime == "static") { | 
 |         cudaRuntime = "Static"; | 
 |       } else if (cudaRuntime == "shared") { | 
 |         cudaRuntime = "Shared"; | 
 |       } else if (cudaRuntime == "none") { | 
 |         cudaRuntime = "None"; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::PrependInheritedString( | 
 |   std::string const& key) | 
 | { | 
 |   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key); | 
 |   if (i == this->FlagMap.end() || i->second.size() != 1) { | 
 |     return; | 
 |   } | 
 |   std::string& value = i->second[0]; | 
 |   value = "%(" + key + ") " + value; | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::Reparse(std::string const& key) | 
 | { | 
 |   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key); | 
 |   if (i == this->FlagMap.end() || i->second.size() != 1) { | 
 |     return; | 
 |   } | 
 |   std::string const original = i->second[0]; | 
 |   i->second[0] = ""; | 
 |   this->UnknownFlagField = key; | 
 |   this->Parse(original); | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag) | 
 | { | 
 |   // Look for Intel Fortran flags that do not map well in the flag table. | 
 |   if (this->CurrentTool == FortranCompiler) { | 
 |     if (flag == "/dbglibs" || flag == "-dbglibs") { | 
 |       this->FortranRuntimeDebug = true; | 
 |       return; | 
 |     } | 
 |     if (flag == "/threads" || flag == "-threads") { | 
 |       this->FortranRuntimeMT = true; | 
 |       return; | 
 |     } | 
 |     if (flag == "/libs:dll" || flag == "-libs:dll") { | 
 |       this->FortranRuntimeDLL = true; | 
 |       return; | 
 |     } | 
 |     if (flag == "/libs:static" || flag == "-libs:static") { | 
 |       this->FortranRuntimeDLL = false; | 
 |       return; | 
 |     } | 
 |   } | 
 |  | 
 |   // This option is not known.  Store it in the output flags. | 
 |   std::string const opts = cmOutputConverter::EscapeWindowsShellArgument( | 
 |     flag.c_str(), | 
 |     cmOutputConverter::Shell_Flag_AllowMakeVariables | | 
 |       cmOutputConverter::Shell_Flag_VSIDE); | 
 |   this->AppendFlagString(this->UnknownFlagField, opts); | 
 | } | 
 |  | 
 | cmIDEOptions::FlagValue cmVisualStudioGeneratorOptions::TakeFlag( | 
 |   std::string const& key) | 
 | { | 
 |   FlagValue value; | 
 |   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key); | 
 |   if (i != this->FlagMap.end()) { | 
 |     value = i->second; | 
 |     this->FlagMap.erase(i); | 
 |   } | 
 |   return value; | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::SetConfiguration( | 
 |   const std::string& config) | 
 | { | 
 |   this->Configuration = config; | 
 | } | 
 |  | 
 | const std::string& cmVisualStudioGeneratorOptions::GetConfiguration() const | 
 | { | 
 |   return this->Configuration; | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions( | 
 |   std::ostream& fout, int indent, const std::string& lang) | 
 | { | 
 |   if (this->Defines.empty()) { | 
 |     return; | 
 |   } | 
 |   std::string tag = "PreprocessorDefinitions"; | 
 |   if (lang == "CUDA") { | 
 |     tag = "Defines"; | 
 |   } | 
 |  | 
 |   std::ostringstream oss; | 
 |   if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { | 
 |     oss << "%(" << tag << ")"; | 
 |   } | 
 |   std::vector<std::string>::const_iterator de = | 
 |     cmRemoveDuplicates(this->Defines); | 
 |   for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) { | 
 |     // Escape the definition for the compiler. | 
 |     std::string define; | 
 |     if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10) { | 
 |       define = this->LocalGenerator->EscapeForShell(di, true); | 
 |     } else { | 
 |       define = di; | 
 |     } | 
 |     // Escape this flag for the MSBuild. | 
 |     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { | 
 |       cmVS10EscapeForMSBuild(define); | 
 |       if (lang == "RC") { | 
 |         cmSystemTools::ReplaceString(define, "\"", "\\\""); | 
 |       } | 
 |     } | 
 |     // Store the flag in the project file. | 
 |     oss << ';' << define; | 
 |   } | 
 |  | 
 |   this->OutputFlag(fout, indent, tag, oss.str()); | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories( | 
 |   std::ostream& fout, int indent, const std::string& lang) | 
 | { | 
 |   if (this->Includes.empty()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   std::string tag = "AdditionalIncludeDirectories"; | 
 |   if (lang == "CUDA") { | 
 |     tag = "Include"; | 
 |   } else if (lang == "ASM_MASM" || lang == "ASM_NASM") { | 
 |     tag = "IncludePaths"; | 
 |   } | 
 |  | 
 |   std::ostringstream oss; | 
 |   const char* sep = ""; | 
 |   for (std::string include : this->Includes) { | 
 |     // first convert all of the slashes | 
 |     std::string::size_type pos = 0; | 
 |     while ((pos = include.find('/', pos)) != std::string::npos) { | 
 |       include[pos] = '\\'; | 
 |       pos++; | 
 |     } | 
 |  | 
 |     if (lang == "ASM_NASM") { | 
 |       include += "\\"; | 
 |     } | 
 |  | 
 |     // Escape this include for the MSBuild. | 
 |     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { | 
 |       cmVS10EscapeForMSBuild(include); | 
 |     } | 
 |     oss << sep << include; | 
 |     sep = ";"; | 
 |  | 
 |     if (lang == "Fortran") { | 
 |       include += "/$(ConfigurationName)"; | 
 |       oss << sep << include; | 
 |     } | 
 |   } | 
 |  | 
 |   if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { | 
 |     oss << sep << "%(" << tag << ")"; | 
 |   } | 
 |  | 
 |   this->OutputFlag(fout, indent, tag, oss.str()); | 
 | } | 
 |  | 
 | void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout, | 
 |                                                    int indent) | 
 | { | 
 |   for (auto const& m : this->FlagMap) { | 
 |     std::ostringstream oss; | 
 |     const char* sep = ""; | 
 |     for (std::string i : m.second) { | 
 |       if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { | 
 |         cmVS10EscapeForMSBuild(i); | 
 |       } | 
 |       oss << sep << i; | 
 |       sep = ";"; | 
 |     } | 
 |  | 
 |     this->OutputFlag(fout, indent, m.first, oss.str()); | 
 |   } | 
 | } |