Imported Upstream version 3.23.2
[platform/upstream/cmake.git] / Source / cmVisualStudioGeneratorOptions.cxx
1 #include "cmVisualStudioGeneratorOptions.h"
2
3 #include <algorithm>
4 #include <map>
5 #include <sstream>
6 #include <utility>
7 #include <vector>
8
9 #include <cm/iterator>
10
11 #include "cmAlgorithms.h"
12 #include "cmLocalVisualStudioGenerator.h"
13 #include "cmOutputConverter.h"
14 #include "cmRange.h"
15 #include "cmStringAlgorithms.h"
16 #include "cmSystemTools.h"
17
18 static void cmVS10EscapeForMSBuild(std::string& ret)
19 {
20   cmSystemTools::ReplaceString(ret, ";", "%3B");
21 }
22
23 cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions(
24   cmLocalVisualStudioGenerator* lg, Tool tool, cmVS7FlagTable const* table,
25   cmVS7FlagTable const* extraTable)
26   : cmIDEOptions()
27   , LocalGenerator(lg)
28   , Version(lg->GetVersion())
29   , CurrentTool(tool)
30 {
31   // Store the given flag tables.
32   this->AddTable(table);
33   this->AddTable(extraTable);
34
35   // Preprocessor definitions are not allowed for linker tools.
36   this->AllowDefine = (tool != Linker);
37
38   // include directories are not allowed for linker tools.
39   this->AllowInclude = (tool != Linker);
40
41   // Slash options are allowed for VS.
42   this->AllowSlash = true;
43
44   this->FortranRuntimeDebug = false;
45   this->FortranRuntimeDLL = false;
46   this->FortranRuntimeMT = false;
47
48   this->UnknownFlagField = "AdditionalOptions";
49 }
50
51 void cmVisualStudioGeneratorOptions::AddTable(cmVS7FlagTable const* table)
52 {
53   if (table) {
54     for (int i = 0; i < FlagTableCount; ++i) {
55       if (!this->FlagTable[i]) {
56         this->FlagTable[i] = table;
57         break;
58       }
59     }
60   }
61 }
62
63 void cmVisualStudioGeneratorOptions::ClearTables()
64 {
65   for (int i = 0; i < FlagTableCount; ++i) {
66     this->FlagTable[i] = nullptr;
67   }
68 }
69
70 void cmVisualStudioGeneratorOptions::FixExceptionHandlingDefault()
71 {
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
88       // value
89       this->FlagMap["ExceptionHandling"] = "\n      ";
90       break;
91     default:
92       this->FlagMap["ExceptionHandling"] = "0";
93       break;
94   }
95 }
96
97 void cmVisualStudioGeneratorOptions::SetVerboseMakefile(bool verbose)
98 {
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.
103   //
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"
107   //
108   if (verbose &&
109       this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) {
110     this->FlagMap["SuppressStartupBanner"] =
111       this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10 ? "FALSE"
112                                                                      : "";
113   }
114 }
115
116 bool cmVisualStudioGeneratorOptions::IsDebug() const
117 {
118   if (this->CurrentTool != CSharpCompiler) {
119     return this->FlagMap.find("DebugInformationFormat") != this->FlagMap.end();
120   }
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";
126     }
127   }
128   return false;
129 }
130
131 bool cmVisualStudioGeneratorOptions::IsWinRt() const
132 {
133   return this->FlagMap.find("CompileAsWinRT") != this->FlagMap.end();
134 }
135
136 bool cmVisualStudioGeneratorOptions::IsManaged() const
137 {
138   return this->FlagMap.find("CompileAsManaged") != this->FlagMap.end();
139 }
140
141 bool cmVisualStudioGeneratorOptions::UsingUnicode() const
142 {
143   // Look for a _UNICODE definition.
144   for (std::string const& di : this->Defines) {
145     if (di == "_UNICODE") {
146       return true;
147     }
148   }
149   return false;
150 }
151 bool cmVisualStudioGeneratorOptions::UsingSBCS() const
152 {
153   // Look for a _SBCS definition.
154   for (std::string const& di : this->Defines) {
155     if (di == "_SBCS") {
156       return true;
157     }
158   }
159   return false;
160 }
161
162 void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
163 {
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");
168
169   // No -code allowed without -arch.
170   if (arch.empty()) {
171     code.clear();
172   }
173
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"];
177
178   // If there are no flags, leave the CodeGeneration field empty.
179   if (arch.empty() && gencode.empty()) {
180     return;
181   }
182
183   // First entries for the -arch=<arch> [-code=<code>,...] pair.
184   if (!arch.empty()) {
185     std::string arch_name = arch[0];
186     if (arch_name == "all" || arch_name == "all-major") {
187       AppendFlagString("AdditionalOptions", "-arch=" + arch_name);
188       return;
189     }
190     std::vector<std::string> codes;
191     if (!code.empty()) {
192       codes = cmTokenize(code[0], ",");
193     }
194     if (codes.empty()) {
195       codes.push_back(arch_name);
196       // nvcc -arch=<arch> has a special case that allows a real
197       // architecture to be specified instead of a virtual arch.
198       // It translates to -arch=<virtual> -code=<real>.
199       cmSystemTools::ReplaceString(arch_name, "sm_", "compute_");
200     }
201     for (std::string const& c : codes) {
202       std::string entry = arch_name + "," + c;
203       result.push_back(entry);
204     }
205   }
206
207   // Now add entries for the following signatures:
208   // -gencode=<arch>,<code>
209   // -gencode=<arch>,[<code1>,<code2>]
210   // -gencode=<arch>,"<code1>,<code2>"
211   for (std::string const& e : gencode) {
212     std::string entry = e;
213     cmSystemTools::ReplaceString(entry, "arch=", "");
214     cmSystemTools::ReplaceString(entry, "code=", "");
215     cmSystemTools::ReplaceString(entry, "[", "");
216     cmSystemTools::ReplaceString(entry, "]", "");
217     cmSystemTools::ReplaceString(entry, "\"", "");
218
219     std::vector<std::string> codes = cmTokenize(entry, ",");
220     if (codes.size() >= 2) {
221       auto gencode_arch = cm::cbegin(codes);
222       for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) {
223         std::string code_entry = *gencode_arch + "," + *ci;
224         result.push_back(code_entry);
225       }
226     }
227   }
228 }
229
230 void cmVisualStudioGeneratorOptions::FixManifestUACFlags()
231 {
232   static std::string const ENABLE_UAC = "EnableUAC";
233   if (!HasFlag(ENABLE_UAC)) {
234     return;
235   }
236
237   const std::string uacFlag = GetFlag(ENABLE_UAC);
238   std::vector<std::string> subOptions;
239   cmsys::SystemTools::Split(uacFlag, subOptions, ' ');
240   if (subOptions.empty()) {
241     AddFlag(ENABLE_UAC, "true");
242     return;
243   }
244
245   if (subOptions.size() == 1 && subOptions[0] == "NO") {
246     AddFlag(ENABLE_UAC, "false");
247     return;
248   }
249
250   std::map<std::string, std::string> uacMap;
251   uacMap["level"] = "UACExecutionLevel";
252   uacMap["uiAccess"] = "UACUIAccess";
253
254   std::map<std::string, std::string> uacExecuteLevelMap;
255   uacExecuteLevelMap["asInvoker"] = "AsInvoker";
256   uacExecuteLevelMap["highestAvailable"] = "HighestAvailable";
257   uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator";
258
259   for (std::string const& subopt : subOptions) {
260     std::vector<std::string> keyValue;
261     cmsys::SystemTools::Split(subopt, keyValue, '=');
262     if (keyValue.size() != 2 || (uacMap.find(keyValue[0]) == uacMap.end())) {
263       // ignore none key=value option or unknown flags
264       continue;
265     }
266
267     if (keyValue[1].front() == '\'' && keyValue[1].back() == '\'') {
268       keyValue[1] = keyValue[1].substr(
269         1, std::max(std::string::size_type(0), keyValue[1].length() - 2));
270     }
271
272     if (keyValue[0] == "level") {
273       if (uacExecuteLevelMap.find(keyValue[1]) == uacExecuteLevelMap.end()) {
274         // unknown level value
275         continue;
276       }
277
278       AddFlag(uacMap[keyValue[0]], uacExecuteLevelMap[keyValue[1]]);
279       continue;
280     }
281
282     if (keyValue[0] == "uiAccess") {
283       if (keyValue[1] != "true" && keyValue[1] != "false") {
284         // unknown uiAccess value
285         continue;
286       }
287       AddFlag(uacMap[keyValue[0]], keyValue[1]);
288       continue;
289     }
290
291     // unknown sub option
292   }
293
294   AddFlag(ENABLE_UAC, "true");
295 }
296
297 void cmVisualStudioGeneratorOptions::Parse(const std::string& flags)
298 {
299   // Parse the input string as a windows command line since the string
300   // is intended for writing directly into the build files.
301   std::vector<std::string> args;
302   cmSystemTools::ParseWindowsCommandLine(flags.c_str(), args);
303
304   // Process flags that need to be represented specially in the IDE
305   // project file.
306   for (std::string const& ai : args) {
307     this->HandleFlag(ai);
308   }
309 }
310
311 void cmVisualStudioGeneratorOptions::ParseFinish()
312 {
313   if (this->CurrentTool == FortranCompiler) {
314     // "RuntimeLibrary" attribute values:
315     //  "rtMultiThreaded", "0", /threads /libs:static
316     //  "rtMultiThreadedDLL", "2", /threads /libs:dll
317     //  "rtMultiThreadedDebug", "1", /threads /dbglibs /libs:static
318     //  "rtMultiThreadedDebugDLL", "3", /threads /dbglibs /libs:dll
319     // These seem unimplemented by the IDE:
320     //  "rtSingleThreaded", "4", /libs:static
321     //  "rtSingleThreadedDLL", "10", /libs:dll
322     //  "rtSingleThreadedDebug", "5", /dbglibs /libs:static
323     //  "rtSingleThreadedDebugDLL", "11", /dbglibs /libs:dll
324     std::string rl =
325       cmStrCat("rtMultiThreaded", this->FortranRuntimeDebug ? "Debug" : "",
326                this->FortranRuntimeDLL ? "DLL" : "");
327     this->FlagMap["RuntimeLibrary"] = rl;
328   }
329
330   if (this->CurrentTool == CudaCompiler) {
331     std::map<std::string, FlagValue>::iterator i =
332       this->FlagMap.find("CudaRuntime");
333     if (i != this->FlagMap.end() && i->second.size() == 1) {
334       std::string& cudaRuntime = i->second[0];
335       if (cudaRuntime == "static") {
336         cudaRuntime = "Static";
337       } else if (cudaRuntime == "shared") {
338         cudaRuntime = "Shared";
339       } else if (cudaRuntime == "none") {
340         cudaRuntime = "None";
341       }
342     }
343   }
344 }
345
346 void cmVisualStudioGeneratorOptions::PrependInheritedString(
347   std::string const& key)
348 {
349   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
350   if (i == this->FlagMap.end() || i->second.size() != 1) {
351     return;
352   }
353   std::string& value = i->second[0];
354   value = "%(" + key + ") " + value;
355 }
356
357 void cmVisualStudioGeneratorOptions::Reparse(std::string const& key)
358 {
359   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
360   if (i == this->FlagMap.end() || i->second.size() != 1) {
361     return;
362   }
363   std::string const original = i->second[0];
364   i->second[0] = "";
365   this->UnknownFlagField = key;
366   this->Parse(original);
367 }
368
369 void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag)
370 {
371   // Look for Intel Fortran flags that do not map well in the flag table.
372   if (this->CurrentTool == FortranCompiler) {
373     if (flag == "/dbglibs" || flag == "-dbglibs") {
374       this->FortranRuntimeDebug = true;
375       return;
376     }
377     if (flag == "/threads" || flag == "-threads") {
378       this->FortranRuntimeMT = true;
379       return;
380     }
381     if (flag == "/libs:dll" || flag == "-libs:dll") {
382       this->FortranRuntimeDLL = true;
383       return;
384     }
385     if (flag == "/libs:static" || flag == "-libs:static") {
386       this->FortranRuntimeDLL = false;
387       return;
388     }
389   }
390
391   // This option is not known.  Store it in the output flags.
392   std::string const opts = cmOutputConverter::EscapeWindowsShellArgument(
393     flag.c_str(),
394     cmOutputConverter::Shell_Flag_AllowMakeVariables |
395       cmOutputConverter::Shell_Flag_VSIDE);
396   this->AppendFlagString(this->UnknownFlagField, opts);
397 }
398
399 cmIDEOptions::FlagValue cmVisualStudioGeneratorOptions::TakeFlag(
400   std::string const& key)
401 {
402   FlagValue value;
403   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
404   if (i != this->FlagMap.end()) {
405     value = i->second;
406     this->FlagMap.erase(i);
407   }
408   return value;
409 }
410
411 void cmVisualStudioGeneratorOptions::SetConfiguration(
412   const std::string& config)
413 {
414   this->Configuration = config;
415 }
416
417 const std::string& cmVisualStudioGeneratorOptions::GetConfiguration() const
418 {
419   return this->Configuration;
420 }
421
422 void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions(
423   std::ostream& fout, int indent, const std::string& lang)
424 {
425   if (this->Defines.empty()) {
426     return;
427   }
428   std::string tag = "PreprocessorDefinitions";
429   if (lang == "CUDA") {
430     tag = "Defines";
431   }
432
433   std::ostringstream oss;
434   if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
435     oss << "%(" << tag << ")";
436   }
437   std::vector<std::string>::const_iterator de =
438     cmRemoveDuplicates(this->Defines);
439   for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
440     // Escape the definition for the compiler.
441     std::string define;
442     if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10) {
443       define = this->LocalGenerator->EscapeForShell(di, true);
444     } else {
445       define = di;
446     }
447     // Escape this flag for the MSBuild.
448     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
449       cmVS10EscapeForMSBuild(define);
450       if (lang == "RC") {
451         cmSystemTools::ReplaceString(define, "\"", "\\\"");
452       }
453     }
454     // Store the flag in the project file.
455     oss << ';' << define;
456   }
457
458   this->OutputFlag(fout, indent, tag, oss.str());
459 }
460
461 void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories(
462   std::ostream& fout, int indent, const std::string& lang)
463 {
464   if (this->Includes.empty()) {
465     return;
466   }
467
468   std::string tag = "AdditionalIncludeDirectories";
469   if (lang == "CUDA") {
470     tag = "Include";
471   } else if (lang == "ASM_MASM" || lang == "ASM_NASM") {
472     tag = "IncludePaths";
473   }
474
475   std::ostringstream oss;
476   const char* sep = "";
477   for (std::string include : this->Includes) {
478     // first convert all of the slashes
479     std::string::size_type pos = 0;
480     while ((pos = include.find('/', pos)) != std::string::npos) {
481       include[pos] = '\\';
482       pos++;
483     }
484
485     if (lang == "ASM_NASM") {
486       include += "\\";
487     }
488
489     // Escape this include for the MSBuild.
490     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
491       cmVS10EscapeForMSBuild(include);
492     }
493     oss << sep << include;
494     sep = ";";
495
496     if (lang == "Fortran") {
497       include += "/$(ConfigurationName)";
498       oss << sep << include;
499     }
500   }
501
502   if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
503     oss << sep << "%(" << tag << ")";
504   }
505
506   this->OutputFlag(fout, indent, tag, oss.str());
507 }
508
509 void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout,
510                                                    int indent)
511 {
512   for (auto const& m : this->FlagMap) {
513     std::ostringstream oss;
514     const char* sep = "";
515     for (std::string i : m.second) {
516       if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
517         cmVS10EscapeForMSBuild(i);
518       }
519       oss << sep << i;
520       sep = ";";
521     }
522
523     this->OutputFlag(fout, indent, m.first, oss.str());
524   }
525 }