1 #include "cmVisualStudioGeneratorOptions.h"
11 #include "cmAlgorithms.h"
12 #include "cmLocalVisualStudioGenerator.h"
13 #include "cmOutputConverter.h"
15 #include "cmStringAlgorithms.h"
16 #include "cmSystemTools.h"
18 static void cmVS10EscapeForMSBuild(std::string& ret)
20 cmSystemTools::ReplaceString(ret, ";", "%3B");
23 cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions(
24 cmLocalVisualStudioGenerator* lg, Tool tool, cmVS7FlagTable const* table,
25 cmVS7FlagTable const* extraTable)
28 , Version(lg->GetVersion())
31 // Store the given flag tables.
32 this->AddTable(table);
33 this->AddTable(extraTable);
35 // Preprocessor definitions are not allowed for linker tools.
36 this->AllowDefine = (tool != Linker);
38 // include directories are not allowed for linker tools.
39 this->AllowInclude = (tool != Linker);
41 // Slash options are allowed for VS.
42 this->AllowSlash = true;
44 this->FortranRuntimeDebug = false;
45 this->FortranRuntimeDLL = false;
46 this->FortranRuntimeMT = false;
48 this->UnknownFlagField = "AdditionalOptions";
51 void cmVisualStudioGeneratorOptions::AddTable(cmVS7FlagTable const* table)
54 for (int i = 0; i < FlagTableCount; ++i) {
55 if (!this->FlagTable[i]) {
56 this->FlagTable[i] = table;
63 void cmVisualStudioGeneratorOptions::ClearTables()
65 for (int i = 0; i < FlagTableCount; ++i) {
66 this->FlagTable[i] = nullptr;
70 void cmVisualStudioGeneratorOptions::FixExceptionHandlingDefault()
72 // Exception handling is on by default because the platform file has
73 // "/EHsc" in the flags. Normally, that will override this
74 // initialization to off, but the user has the option of removing
75 // the flag to disable exception handling. When the user does
76 // remove the flag we need to override the IDE default of on.
77 switch (this->Version) {
78 case cmGlobalVisualStudioGenerator::VSVersion::VS10:
79 case cmGlobalVisualStudioGenerator::VSVersion::VS11:
80 case cmGlobalVisualStudioGenerator::VSVersion::VS12:
81 case cmGlobalVisualStudioGenerator::VSVersion::VS14:
82 case cmGlobalVisualStudioGenerator::VSVersion::VS15:
83 case cmGlobalVisualStudioGenerator::VSVersion::VS16:
84 case cmGlobalVisualStudioGenerator::VSVersion::VS17:
85 // by default VS puts <ExceptionHandling></ExceptionHandling> empty
86 // for a project, to make our projects look the same put a new line
87 // and space over for the closing </ExceptionHandling> as the default
89 this->FlagMap["ExceptionHandling"] = "\n ";
92 this->FlagMap["ExceptionHandling"] = "0";
97 void cmVisualStudioGeneratorOptions::SetVerboseMakefile(bool verbose)
99 // If verbose makefiles have been requested and the /nologo option
100 // was not given explicitly in the flags we want to add an attribute
101 // to the generated project to disable logo suppression. Otherwise
102 // the GUI default is to enable suppression.
104 // On Visual Studio 10 (and later!), the value of this attribute should be
105 // an empty string, instead of "FALSE", in order to avoid a warning:
106 // "cl ... warning D9035: option 'nologo-' has been deprecated"
109 this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) {
110 this->FlagMap["SuppressStartupBanner"] =
111 this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10 ? "FALSE"
116 bool cmVisualStudioGeneratorOptions::IsDebug() const
118 if (this->CurrentTool != CSharpCompiler) {
119 return this->FlagMap.find("DebugInformationFormat") != this->FlagMap.end();
121 std::map<std::string, FlagValue>::const_iterator i =
122 this->FlagMap.find("DebugType");
123 if (i != this->FlagMap.end()) {
124 if (i->second.size() == 1) {
125 return i->second[0] != "none";
131 bool cmVisualStudioGeneratorOptions::IsWinRt() const
133 return this->FlagMap.find("CompileAsWinRT") != this->FlagMap.end();
136 bool cmVisualStudioGeneratorOptions::IsManaged() const
138 return this->FlagMap.find("CompileAsManaged") != this->FlagMap.end();
141 bool cmVisualStudioGeneratorOptions::UsingUnicode() const
143 // Look for a _UNICODE definition.
144 for (std::string const& di : this->Defines) {
145 if (di == "_UNICODE") {
151 bool cmVisualStudioGeneratorOptions::UsingSBCS() const
153 // Look for a _SBCS definition.
154 for (std::string const& di : this->Defines) {
162 void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
164 // Extract temporary values stored by our flag table.
165 FlagValue arch = this->TakeFlag("cmake-temp-arch");
166 FlagValue code = this->TakeFlag("cmake-temp-code");
167 FlagValue gencode = this->TakeFlag("cmake-temp-gencode");
169 // No -code allowed without -arch.
174 // Create a CodeGeneration field with [arch],[code] syntax in each entry.
175 // CUDA will convert it to `-gencode=arch=[arch],code="[code],[arch]"`.
176 FlagValue& result = this->FlagMap["CodeGeneration"];
178 // If there are no flags, leave the CodeGeneration field empty.
179 if (arch.empty() && gencode.empty()) {
183 // First entries for the -arch=<arch> [-code=<code>,...] pair.
185 std::string arch_name = arch[0];
186 if (arch_name == "all" || arch_name == "all-major" ||
187 arch_name == "native") {
188 AppendFlagString("AdditionalOptions", "-arch=" + arch_name);
191 std::vector<std::string> codes;
193 codes = cmTokenize(code[0], ",");
196 codes.push_back(arch_name);
197 // nvcc -arch=<arch> has a special case that allows a real
198 // architecture to be specified instead of a virtual arch.
199 // It translates to -arch=<virtual> -code=<real>.
200 cmSystemTools::ReplaceString(arch_name, "sm_", "compute_");
202 for (std::string const& c : codes) {
203 std::string entry = arch_name + "," + c;
204 result.push_back(entry);
208 // Now add entries for the following signatures:
209 // -gencode=<arch>,<code>
210 // -gencode=<arch>,[<code1>,<code2>]
211 // -gencode=<arch>,"<code1>,<code2>"
212 for (std::string const& e : gencode) {
213 std::string entry = e;
214 cmSystemTools::ReplaceString(entry, "arch=", "");
215 cmSystemTools::ReplaceString(entry, "code=", "");
216 cmSystemTools::ReplaceString(entry, "[", "");
217 cmSystemTools::ReplaceString(entry, "]", "");
218 cmSystemTools::ReplaceString(entry, "\"", "");
220 std::vector<std::string> codes = cmTokenize(entry, ",");
221 if (codes.size() >= 2) {
222 auto gencode_arch = cm::cbegin(codes);
223 for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) {
224 std::string code_entry = *gencode_arch + "," + *ci;
225 result.push_back(code_entry);
231 void cmVisualStudioGeneratorOptions::FixManifestUACFlags()
233 static std::string const ENABLE_UAC = "EnableUAC";
234 if (!HasFlag(ENABLE_UAC)) {
238 const std::string uacFlag = GetFlag(ENABLE_UAC);
239 std::vector<std::string> subOptions;
240 cmsys::SystemTools::Split(uacFlag, subOptions, ' ');
241 if (subOptions.empty()) {
242 AddFlag(ENABLE_UAC, "true");
246 if (subOptions.size() == 1 && subOptions[0] == "NO") {
247 AddFlag(ENABLE_UAC, "false");
251 std::map<std::string, std::string> uacMap;
252 uacMap["level"] = "UACExecutionLevel";
253 uacMap["uiAccess"] = "UACUIAccess";
255 std::map<std::string, std::string> uacExecuteLevelMap;
256 uacExecuteLevelMap["asInvoker"] = "AsInvoker";
257 uacExecuteLevelMap["highestAvailable"] = "HighestAvailable";
258 uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator";
260 for (std::string const& subopt : subOptions) {
261 std::vector<std::string> keyValue;
262 cmsys::SystemTools::Split(subopt, keyValue, '=');
263 if (keyValue.size() != 2 || (uacMap.find(keyValue[0]) == uacMap.end())) {
264 // ignore none key=value option or unknown flags
268 if (keyValue[1].front() == '\'' && keyValue[1].back() == '\'') {
269 keyValue[1] = keyValue[1].substr(
270 1, std::max(std::string::size_type(0), keyValue[1].length() - 2));
273 if (keyValue[0] == "level") {
274 if (uacExecuteLevelMap.find(keyValue[1]) == uacExecuteLevelMap.end()) {
275 // unknown level value
279 AddFlag(uacMap[keyValue[0]], uacExecuteLevelMap[keyValue[1]]);
283 if (keyValue[0] == "uiAccess") {
284 if (keyValue[1] != "true" && keyValue[1] != "false") {
285 // unknown uiAccess value
288 AddFlag(uacMap[keyValue[0]], keyValue[1]);
292 // unknown sub option
295 AddFlag(ENABLE_UAC, "true");
298 void cmVisualStudioGeneratorOptions::Parse(const std::string& flags)
300 // Parse the input string as a windows command line since the string
301 // is intended for writing directly into the build files.
302 std::vector<std::string> args;
303 cmSystemTools::ParseWindowsCommandLine(flags.c_str(), args);
305 // Process flags that need to be represented specially in the IDE
307 for (std::string const& ai : args) {
308 this->HandleFlag(ai);
312 void cmVisualStudioGeneratorOptions::ParseFinish()
314 if (this->CurrentTool == FortranCompiler) {
315 // "RuntimeLibrary" attribute values:
316 // "rtMultiThreaded", "0", /threads /libs:static
317 // "rtMultiThreadedDLL", "2", /threads /libs:dll
318 // "rtMultiThreadedDebug", "1", /threads /dbglibs /libs:static
319 // "rtMultiThreadedDebugDLL", "3", /threads /dbglibs /libs:dll
320 // These seem unimplemented by the IDE:
321 // "rtSingleThreaded", "4", /libs:static
322 // "rtSingleThreadedDLL", "10", /libs:dll
323 // "rtSingleThreadedDebug", "5", /dbglibs /libs:static
324 // "rtSingleThreadedDebugDLL", "11", /dbglibs /libs:dll
326 cmStrCat("rtMultiThreaded", this->FortranRuntimeDebug ? "Debug" : "",
327 this->FortranRuntimeDLL ? "DLL" : "");
328 this->FlagMap["RuntimeLibrary"] = rl;
331 if (this->CurrentTool == CudaCompiler) {
332 std::map<std::string, FlagValue>::iterator i =
333 this->FlagMap.find("CudaRuntime");
334 if (i != this->FlagMap.end() && i->second.size() == 1) {
335 std::string& cudaRuntime = i->second[0];
336 if (cudaRuntime == "static") {
337 cudaRuntime = "Static";
338 } else if (cudaRuntime == "shared") {
339 cudaRuntime = "Shared";
340 } else if (cudaRuntime == "none") {
341 cudaRuntime = "None";
347 void cmVisualStudioGeneratorOptions::PrependInheritedString(
348 std::string const& key)
350 std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
351 if (i == this->FlagMap.end() || i->second.size() != 1) {
354 std::string& value = i->second[0];
355 value = "%(" + key + ") " + value;
358 void cmVisualStudioGeneratorOptions::Reparse(std::string const& key)
360 std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
361 if (i == this->FlagMap.end() || i->second.size() != 1) {
364 std::string const original = i->second[0];
366 this->UnknownFlagField = key;
367 this->Parse(original);
370 void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag)
372 // Look for Intel Fortran flags that do not map well in the flag table.
373 if (this->CurrentTool == FortranCompiler) {
374 if (flag == "/dbglibs" || flag == "-dbglibs") {
375 this->FortranRuntimeDebug = true;
378 if (flag == "/threads" || flag == "-threads") {
379 this->FortranRuntimeMT = true;
382 if (flag == "/libs:dll" || flag == "-libs:dll") {
383 this->FortranRuntimeDLL = true;
386 if (flag == "/libs:static" || flag == "-libs:static") {
387 this->FortranRuntimeDLL = false;
392 // This option is not known. Store it in the output flags.
393 std::string const opts = cmOutputConverter::EscapeWindowsShellArgument(
395 cmOutputConverter::Shell_Flag_AllowMakeVariables |
396 cmOutputConverter::Shell_Flag_VSIDE);
397 this->AppendFlagString(this->UnknownFlagField, opts);
400 cmIDEOptions::FlagValue cmVisualStudioGeneratorOptions::TakeFlag(
401 std::string const& key)
404 std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
405 if (i != this->FlagMap.end()) {
407 this->FlagMap.erase(i);
412 void cmVisualStudioGeneratorOptions::SetConfiguration(
413 const std::string& config)
415 this->Configuration = config;
418 const std::string& cmVisualStudioGeneratorOptions::GetConfiguration() const
420 return this->Configuration;
423 void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions(
424 std::ostream& fout, int indent, const std::string& lang)
426 if (this->Defines.empty()) {
429 std::string tag = "PreprocessorDefinitions";
430 if (lang == "CUDA") {
434 std::ostringstream oss;
435 if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
436 oss << "%(" << tag << ")";
438 std::vector<std::string>::const_iterator de =
439 cmRemoveDuplicates(this->Defines);
440 for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
441 // Escape the definition for the compiler.
443 if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10) {
444 define = this->LocalGenerator->EscapeForShell(di, true);
448 // Escape this flag for the MSBuild.
449 if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
450 cmVS10EscapeForMSBuild(define);
452 cmSystemTools::ReplaceString(define, "\"", "\\\"");
455 // Store the flag in the project file.
456 oss << ';' << define;
459 this->OutputFlag(fout, indent, tag, oss.str());
462 void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories(
463 std::ostream& fout, int indent, const std::string& lang)
465 if (this->Includes.empty()) {
469 std::string tag = "AdditionalIncludeDirectories";
470 if (lang == "CUDA") {
472 } else if (lang == "ASM_MASM" || lang == "ASM_NASM") {
473 tag = "IncludePaths";
476 std::ostringstream oss;
477 const char* sep = "";
478 for (std::string include : this->Includes) {
479 // first convert all of the slashes
480 std::string::size_type pos = 0;
481 while ((pos = include.find('/', pos)) != std::string::npos) {
486 if (lang == "ASM_NASM") {
490 // Escape this include for the MSBuild.
491 if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
492 cmVS10EscapeForMSBuild(include);
494 oss << sep << include;
497 if (lang == "Fortran") {
498 include += "/$(ConfigurationName)";
499 oss << sep << include;
503 if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
504 oss << sep << "%(" << tag << ")";
507 this->OutputFlag(fout, indent, tag, oss.str());
510 void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout,
513 for (auto const& m : this->FlagMap) {
514 std::ostringstream oss;
515 const char* sep = "";
516 for (std::string i : m.second) {
517 if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
518 cmVS10EscapeForMSBuild(i);
524 this->OutputFlag(fout, indent, m.first, oss.str());