00c65edd7985ee384a9575f58971ce4d37a37207
[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         arch_name == "native") {
188       AppendFlagString("AdditionalOptions", "-arch=" + arch_name);
189       return;
190     }
191     std::vector<std::string> codes;
192     if (!code.empty()) {
193       codes = cmTokenize(code[0], ",");
194     }
195     if (codes.empty()) {
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_");
201     }
202     for (std::string const& c : codes) {
203       std::string entry = arch_name + "," + c;
204       result.push_back(entry);
205     }
206   }
207
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, "\"", "");
219
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);
226       }
227     }
228   }
229 }
230
231 void cmVisualStudioGeneratorOptions::FixManifestUACFlags()
232 {
233   static std::string const ENABLE_UAC = "EnableUAC";
234   if (!HasFlag(ENABLE_UAC)) {
235     return;
236   }
237
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");
243     return;
244   }
245
246   if (subOptions.size() == 1 && subOptions[0] == "NO") {
247     AddFlag(ENABLE_UAC, "false");
248     return;
249   }
250
251   std::map<std::string, std::string> uacMap;
252   uacMap["level"] = "UACExecutionLevel";
253   uacMap["uiAccess"] = "UACUIAccess";
254
255   std::map<std::string, std::string> uacExecuteLevelMap;
256   uacExecuteLevelMap["asInvoker"] = "AsInvoker";
257   uacExecuteLevelMap["highestAvailable"] = "HighestAvailable";
258   uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator";
259
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
265       continue;
266     }
267
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));
271     }
272
273     if (keyValue[0] == "level") {
274       if (uacExecuteLevelMap.find(keyValue[1]) == uacExecuteLevelMap.end()) {
275         // unknown level value
276         continue;
277       }
278
279       AddFlag(uacMap[keyValue[0]], uacExecuteLevelMap[keyValue[1]]);
280       continue;
281     }
282
283     if (keyValue[0] == "uiAccess") {
284       if (keyValue[1] != "true" && keyValue[1] != "false") {
285         // unknown uiAccess value
286         continue;
287       }
288       AddFlag(uacMap[keyValue[0]], keyValue[1]);
289       continue;
290     }
291
292     // unknown sub option
293   }
294
295   AddFlag(ENABLE_UAC, "true");
296 }
297
298 void cmVisualStudioGeneratorOptions::Parse(const std::string& flags)
299 {
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);
304
305   // Process flags that need to be represented specially in the IDE
306   // project file.
307   for (std::string const& ai : args) {
308     this->HandleFlag(ai);
309   }
310 }
311
312 void cmVisualStudioGeneratorOptions::ParseFinish()
313 {
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
325     std::string rl =
326       cmStrCat("rtMultiThreaded", this->FortranRuntimeDebug ? "Debug" : "",
327                this->FortranRuntimeDLL ? "DLL" : "");
328     this->FlagMap["RuntimeLibrary"] = rl;
329   }
330
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";
342       }
343     }
344   }
345 }
346
347 void cmVisualStudioGeneratorOptions::PrependInheritedString(
348   std::string const& key)
349 {
350   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
351   if (i == this->FlagMap.end() || i->second.size() != 1) {
352     return;
353   }
354   std::string& value = i->second[0];
355   value = "%(" + key + ") " + value;
356 }
357
358 void cmVisualStudioGeneratorOptions::Reparse(std::string const& key)
359 {
360   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
361   if (i == this->FlagMap.end() || i->second.size() != 1) {
362     return;
363   }
364   std::string const original = i->second[0];
365   i->second[0] = "";
366   this->UnknownFlagField = key;
367   this->Parse(original);
368 }
369
370 void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag)
371 {
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;
376       return;
377     }
378     if (flag == "/threads" || flag == "-threads") {
379       this->FortranRuntimeMT = true;
380       return;
381     }
382     if (flag == "/libs:dll" || flag == "-libs:dll") {
383       this->FortranRuntimeDLL = true;
384       return;
385     }
386     if (flag == "/libs:static" || flag == "-libs:static") {
387       this->FortranRuntimeDLL = false;
388       return;
389     }
390   }
391
392   // This option is not known.  Store it in the output flags.
393   std::string const opts = cmOutputConverter::EscapeWindowsShellArgument(
394     flag.c_str(),
395     cmOutputConverter::Shell_Flag_AllowMakeVariables |
396       cmOutputConverter::Shell_Flag_VSIDE);
397   this->AppendFlagString(this->UnknownFlagField, opts);
398 }
399
400 cmIDEOptions::FlagValue cmVisualStudioGeneratorOptions::TakeFlag(
401   std::string const& key)
402 {
403   FlagValue value;
404   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
405   if (i != this->FlagMap.end()) {
406     value = i->second;
407     this->FlagMap.erase(i);
408   }
409   return value;
410 }
411
412 void cmVisualStudioGeneratorOptions::SetConfiguration(
413   const std::string& config)
414 {
415   this->Configuration = config;
416 }
417
418 const std::string& cmVisualStudioGeneratorOptions::GetConfiguration() const
419 {
420   return this->Configuration;
421 }
422
423 void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions(
424   std::ostream& fout, int indent, const std::string& lang)
425 {
426   if (this->Defines.empty()) {
427     return;
428   }
429   std::string tag = "PreprocessorDefinitions";
430   if (lang == "CUDA") {
431     tag = "Defines";
432   }
433
434   std::ostringstream oss;
435   if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
436     oss << "%(" << tag << ")";
437   }
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.
442     std::string define;
443     if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10) {
444       define = this->LocalGenerator->EscapeForShell(di, true);
445     } else {
446       define = di;
447     }
448     // Escape this flag for the MSBuild.
449     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
450       cmVS10EscapeForMSBuild(define);
451       if (lang == "RC") {
452         cmSystemTools::ReplaceString(define, "\"", "\\\"");
453       }
454     }
455     // Store the flag in the project file.
456     oss << ';' << define;
457   }
458
459   this->OutputFlag(fout, indent, tag, oss.str());
460 }
461
462 void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories(
463   std::ostream& fout, int indent, const std::string& lang)
464 {
465   if (this->Includes.empty()) {
466     return;
467   }
468
469   std::string tag = "AdditionalIncludeDirectories";
470   if (lang == "CUDA") {
471     tag = "Include";
472   } else if (lang == "ASM_MASM" || lang == "ASM_NASM") {
473     tag = "IncludePaths";
474   }
475
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) {
482       include[pos] = '\\';
483       pos++;
484     }
485
486     if (lang == "ASM_NASM") {
487       include += "\\";
488     }
489
490     // Escape this include for the MSBuild.
491     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
492       cmVS10EscapeForMSBuild(include);
493     }
494     oss << sep << include;
495     sep = ";";
496
497     if (lang == "Fortran") {
498       include += "/$(ConfigurationName)";
499       oss << sep << include;
500     }
501   }
502
503   if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
504     oss << sep << "%(" << tag << ")";
505   }
506
507   this->OutputFlag(fout, indent, tag, oss.str());
508 }
509
510 void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout,
511                                                    int indent)
512 {
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);
519       }
520       oss << sep << i;
521       sep = ";";
522     }
523
524     this->OutputFlag(fout, indent, m.first, oss.str());
525   }
526 }