diff options
Diffstat (limited to 'Source/cmVisualStudioGeneratorOptions.cxx')
-rw-r--r-- | Source/cmVisualStudioGeneratorOptions.cxx | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx new file mode 100644 index 0000000..e1b0c70 --- /dev/null +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -0,0 +1,531 @@ +#include "cmVisualStudioGeneratorOptions.h" + +#include "cmAlgorithms.h" +#include "cmLocalVisualStudioGenerator.h" +#include "cmOutputConverter.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::VS10: + case cmGlobalVisualStudioGenerator::VS11: + case cmGlobalVisualStudioGenerator::VS12: + case cmGlobalVisualStudioGenerator::VS14: + case cmGlobalVisualStudioGenerator::VS15: + case cmGlobalVisualStudioGenerator::VS16: + // 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::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; +} + +cmVisualStudioGeneratorOptions::CudaRuntime +cmVisualStudioGeneratorOptions::GetCudaRuntime() const +{ + std::map<std::string, FlagValue>::const_iterator i = + this->FlagMap.find("CudaRuntime"); + if (i != this->FlagMap.end() && i->second.size() == 1) { + std::string const& cudaRuntime = i->second[0]; + if (cudaRuntime == "Static") { + return CudaRuntimeStatic; + } + if (cudaRuntime == "Shared") { + return CudaRuntimeShared; + } + if (cudaRuntime == "None") { + return CudaRuntimeNone; + } + } + // nvcc default is static + return CudaRuntimeStatic; +} + +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]; + std::vector<std::string> codes; + if (!code.empty()) { + codes = cmSystemTools::tokenize(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 = cmSystemTools::tokenize(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(0, cm::isize(keyValue[1]) - 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 = "rtMultiThreaded"; + rl += this->FortranRuntimeDebug ? "Debug" : ""; + rl += 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; + } + const char* tag = "PreprocessorDefinitions"; + if (lang == "CUDA") { + tag = "Defines"; + } + + std::ostringstream oss; + const char* sep = ""; + 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::VS10) { + define = this->LocalGenerator->EscapeForShell(di, true); + } else { + define = di; + } + // Escape this flag for the MSBuild. + if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { + cmVS10EscapeForMSBuild(define); + if (lang == "RC") { + cmSystemTools::ReplaceString(define, "\"", "\\\""); + } + } + // Store the flag in the project file. + oss << sep << define; + sep = ";"; + } + if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { + oss << ";%(" << tag << ")"; + } + + this->OutputFlag(fout, indent, tag, oss.str()); +} + +void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories( + std::ostream& fout, int indent, const std::string& lang) +{ + if (this->Includes.empty()) { + return; + } + + const char* 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::VS10) { + cmVS10EscapeForMSBuild(include); + } + oss << sep << include; + sep = ";"; + + if (lang == "Fortran") { + include += "/$(ConfigurationName)"; + oss << sep << include; + } + } + + if (this->Version >= cmGlobalVisualStudioGenerator::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::VS10) { + cmVS10EscapeForMSBuild(i); + } + oss << sep << i; + sep = ";"; + } + + this->OutputFlag(fout, indent, m.first.c_str(), oss.str()); + } +} |