#include "cmVisualStudioGeneratorOptions.h" #include #include #include #include #include #include #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 empty // for a project, to make our projects look the same put a new line // and space over for the closing 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::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= [-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 codes; if (!code.empty()) { codes = cmTokenize(code[0], ","); } if (codes.empty()) { codes.push_back(arch_name); // nvcc -arch= has a special case that allows a real // architecture to be specified instead of a virtual arch. // It translates to -arch= -code=. 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=, // -gencode=,[,] // -gencode=,"," 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 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 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 uacMap; uacMap["level"] = "UACExecutionLevel"; uacMap["uiAccess"] = "UACUIAccess"; std::map uacExecuteLevelMap; uacExecuteLevelMap["asInvoker"] = "AsInvoker"; uacExecuteLevelMap["highestAvailable"] = "HighestAvailable"; uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator"; for (std::string const& subopt : subOptions) { std::vector 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 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::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::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::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::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::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()); } }