Imported Upstream version 3.25.0
[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::VS11:
79     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
80     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
81     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
82     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
83     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
84       // by default VS puts <ExceptionHandling></ExceptionHandling> empty
85       // for a project, to make our projects look the same put a new line
86       // and space over for the closing </ExceptionHandling> as the default
87       // value
88       this->FlagMap["ExceptionHandling"] = "\n      ";
89       break;
90     default:
91       this->FlagMap["ExceptionHandling"] = "0";
92       break;
93   }
94 }
95
96 void cmVisualStudioGeneratorOptions::SetVerboseMakefile(bool verbose)
97 {
98   // If verbose makefiles have been requested and the /nologo option
99   // was not given explicitly in the flags we want to add an attribute
100   // to the generated project to disable logo suppression.  Otherwise
101   // the GUI default is to enable suppression.
102   //
103   // On Visual Studio 9, the value of this attribute should be
104   // "FALSE", instead of an empty string.
105   if (verbose &&
106       this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) {
107     this->FlagMap["SuppressStartupBanner"] =
108       this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9 ? "FALSE"
109                                                                      : "";
110   }
111 }
112
113 bool cmVisualStudioGeneratorOptions::IsDebug() const
114 {
115   if (this->CurrentTool != CSharpCompiler) {
116     return this->FlagMap.find("DebugInformationFormat") != this->FlagMap.end();
117   }
118   std::map<std::string, FlagValue>::const_iterator i =
119     this->FlagMap.find("DebugType");
120   if (i != this->FlagMap.end()) {
121     if (i->second.size() == 1) {
122       return i->second[0] != "none";
123     }
124   }
125   return false;
126 }
127
128 bool cmVisualStudioGeneratorOptions::IsWinRt() const
129 {
130   return this->FlagMap.find("CompileAsWinRT") != this->FlagMap.end();
131 }
132
133 bool cmVisualStudioGeneratorOptions::IsManaged() const
134 {
135   return this->FlagMap.find("CompileAsManaged") != this->FlagMap.end();
136 }
137
138 bool cmVisualStudioGeneratorOptions::UsingUnicode() const
139 {
140   // Look for a _UNICODE definition.
141   for (std::string const& di : this->Defines) {
142     if (di == "_UNICODE") {
143       return true;
144     }
145   }
146   return false;
147 }
148 bool cmVisualStudioGeneratorOptions::UsingSBCS() const
149 {
150   // Look for a _SBCS definition.
151   for (std::string const& di : this->Defines) {
152     if (di == "_SBCS") {
153       return true;
154     }
155   }
156   return false;
157 }
158
159 void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
160 {
161   // Create an empty CodeGeneration field, and pass the the actual
162   // compile flags via additional options so that we have consistent
163   // behavior and avoid issues with MSBuild extensions injecting
164   // virtual code when we request real only.
165   FlagValue& code_gen_flag = this->FlagMap["CodeGeneration"];
166   code_gen_flag = "";
167 }
168
169 void cmVisualStudioGeneratorOptions::FixManifestUACFlags()
170 {
171   static std::string const ENABLE_UAC = "EnableUAC";
172   if (!HasFlag(ENABLE_UAC)) {
173     return;
174   }
175
176   const std::string uacFlag = GetFlag(ENABLE_UAC);
177   std::vector<std::string> subOptions;
178   cmsys::SystemTools::Split(uacFlag, subOptions, ' ');
179   if (subOptions.empty()) {
180     AddFlag(ENABLE_UAC, "true");
181     return;
182   }
183
184   if (subOptions.size() == 1 && subOptions[0] == "NO") {
185     AddFlag(ENABLE_UAC, "false");
186     return;
187   }
188
189   std::map<std::string, std::string> uacMap;
190   uacMap["level"] = "UACExecutionLevel";
191   uacMap["uiAccess"] = "UACUIAccess";
192
193   std::map<std::string, std::string> uacExecuteLevelMap;
194   uacExecuteLevelMap["asInvoker"] = "AsInvoker";
195   uacExecuteLevelMap["highestAvailable"] = "HighestAvailable";
196   uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator";
197
198   for (std::string const& subopt : subOptions) {
199     std::vector<std::string> keyValue;
200     cmsys::SystemTools::Split(subopt, keyValue, '=');
201     if (keyValue.size() != 2 || (uacMap.find(keyValue[0]) == uacMap.end())) {
202       // ignore none key=value option or unknown flags
203       continue;
204     }
205
206     if (keyValue[1].front() == '\'' && keyValue[1].back() == '\'') {
207       keyValue[1] = keyValue[1].substr(
208         1, std::max(std::string::size_type(0), keyValue[1].length() - 2));
209     }
210
211     if (keyValue[0] == "level") {
212       if (uacExecuteLevelMap.find(keyValue[1]) == uacExecuteLevelMap.end()) {
213         // unknown level value
214         continue;
215       }
216
217       AddFlag(uacMap[keyValue[0]], uacExecuteLevelMap[keyValue[1]]);
218       continue;
219     }
220
221     if (keyValue[0] == "uiAccess") {
222       if (keyValue[1] != "true" && keyValue[1] != "false") {
223         // unknown uiAccess value
224         continue;
225       }
226       AddFlag(uacMap[keyValue[0]], keyValue[1]);
227       continue;
228     }
229
230     // unknown sub option
231   }
232
233   AddFlag(ENABLE_UAC, "true");
234 }
235
236 void cmVisualStudioGeneratorOptions::Parse(const std::string& flags)
237 {
238   // Parse the input string as a windows command line since the string
239   // is intended for writing directly into the build files.
240   std::vector<std::string> args;
241   cmSystemTools::ParseWindowsCommandLine(flags.c_str(), args);
242
243   // Process flags that need to be represented specially in the IDE
244   // project file.
245   for (std::string const& ai : args) {
246     this->HandleFlag(ai);
247   }
248 }
249
250 void cmVisualStudioGeneratorOptions::ParseFinish()
251 {
252   if (this->CurrentTool == FortranCompiler) {
253     // "RuntimeLibrary" attribute values:
254     //  "rtMultiThreaded", "0", /threads /libs:static
255     //  "rtMultiThreadedDLL", "2", /threads /libs:dll
256     //  "rtMultiThreadedDebug", "1", /threads /dbglibs /libs:static
257     //  "rtMultiThreadedDebugDLL", "3", /threads /dbglibs /libs:dll
258     // These seem unimplemented by the IDE:
259     //  "rtSingleThreaded", "4", /libs:static
260     //  "rtSingleThreadedDLL", "10", /libs:dll
261     //  "rtSingleThreadedDebug", "5", /dbglibs /libs:static
262     //  "rtSingleThreadedDebugDLL", "11", /dbglibs /libs:dll
263     std::string rl =
264       cmStrCat("rtMultiThreaded", this->FortranRuntimeDebug ? "Debug" : "",
265                this->FortranRuntimeDLL ? "DLL" : "");
266     this->FlagMap["RuntimeLibrary"] = rl;
267   }
268
269   if (this->CurrentTool == CudaCompiler) {
270     std::map<std::string, FlagValue>::iterator i =
271       this->FlagMap.find("CudaRuntime");
272     if (i != this->FlagMap.end() && i->second.size() == 1) {
273       std::string& cudaRuntime = i->second[0];
274       if (cudaRuntime == "static") {
275         cudaRuntime = "Static";
276       } else if (cudaRuntime == "shared") {
277         cudaRuntime = "Shared";
278       } else if (cudaRuntime == "none") {
279         cudaRuntime = "None";
280       }
281     }
282   }
283 }
284
285 void cmVisualStudioGeneratorOptions::PrependInheritedString(
286   std::string const& key)
287 {
288   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
289   if (i == this->FlagMap.end() || i->second.size() != 1) {
290     return;
291   }
292   std::string& value = i->second[0];
293   value = "%(" + key + ") " + value;
294 }
295
296 void cmVisualStudioGeneratorOptions::Reparse(std::string const& key)
297 {
298   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
299   if (i == this->FlagMap.end() || i->second.size() != 1) {
300     return;
301   }
302   std::string const original = i->second[0];
303   i->second[0] = "";
304   this->UnknownFlagField = key;
305   this->Parse(original);
306 }
307
308 void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag)
309 {
310   // Look for Intel Fortran flags that do not map well in the flag table.
311   if (this->CurrentTool == FortranCompiler) {
312     if (flag == "/dbglibs" || flag == "-dbglibs") {
313       this->FortranRuntimeDebug = true;
314       return;
315     }
316     if (flag == "/threads" || flag == "-threads") {
317       this->FortranRuntimeMT = true;
318       return;
319     }
320     if (flag == "/libs:dll" || flag == "-libs:dll") {
321       this->FortranRuntimeDLL = true;
322       return;
323     }
324     if (flag == "/libs:static" || flag == "-libs:static") {
325       this->FortranRuntimeDLL = false;
326       return;
327     }
328   }
329
330   // This option is not known.  Store it in the output flags.
331   std::string const opts = cmOutputConverter::EscapeWindowsShellArgument(
332     flag.c_str(),
333     cmOutputConverter::Shell_Flag_AllowMakeVariables |
334       cmOutputConverter::Shell_Flag_VSIDE);
335   this->AppendFlagString(this->UnknownFlagField, opts);
336 }
337
338 cmIDEOptions::FlagValue cmVisualStudioGeneratorOptions::TakeFlag(
339   std::string const& key)
340 {
341   FlagValue value;
342   std::map<std::string, FlagValue>::iterator i = this->FlagMap.find(key);
343   if (i != this->FlagMap.end()) {
344     value = i->second;
345     this->FlagMap.erase(i);
346   }
347   return value;
348 }
349
350 void cmVisualStudioGeneratorOptions::SetConfiguration(
351   const std::string& config)
352 {
353   this->Configuration = config;
354 }
355
356 const std::string& cmVisualStudioGeneratorOptions::GetConfiguration() const
357 {
358   return this->Configuration;
359 }
360
361 void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions(
362   std::ostream& fout, int indent, const std::string& lang)
363 {
364   if (this->Defines.empty()) {
365     return;
366   }
367   std::string tag = "PreprocessorDefinitions";
368   if (lang == "CUDA") {
369     tag = "Defines";
370   }
371
372   std::ostringstream oss;
373   if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
374     oss << "%(" << tag << ")";
375   }
376   std::vector<std::string>::const_iterator de =
377     cmRemoveDuplicates(this->Defines);
378   for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
379     // Escape the definition for the compiler.
380     std::string define;
381     if (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9) {
382       define = this->LocalGenerator->EscapeForShell(di, true);
383     } else {
384       define = di;
385     }
386     // Escape this flag for the MSBuild.
387     if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
388       cmVS10EscapeForMSBuild(define);
389       if (lang == "RC") {
390         cmSystemTools::ReplaceString(define, "\"", "\\\"");
391       }
392     }
393     // Store the flag in the project file.
394     oss << ';' << define;
395   }
396
397   this->OutputFlag(fout, indent, tag, oss.str());
398 }
399
400 void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories(
401   std::ostream& fout, int indent, const std::string& lang)
402 {
403   if (this->Includes.empty()) {
404     return;
405   }
406
407   std::string tag = "AdditionalIncludeDirectories";
408   if (lang == "CUDA") {
409     tag = "Include";
410   } else if (lang == "ASM_MASM" || lang == "ASM_NASM") {
411     tag = "IncludePaths";
412   }
413
414   std::ostringstream oss;
415   const char* sep = "";
416   for (std::string include : this->Includes) {
417     // first convert all of the slashes
418     std::string::size_type pos = 0;
419     while ((pos = include.find('/', pos)) != std::string::npos) {
420       include[pos] = '\\';
421       pos++;
422     }
423
424     if (lang == "ASM_NASM") {
425       include += "\\";
426     }
427
428     // Escape this include for the MSBuild.
429     if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
430       cmVS10EscapeForMSBuild(include);
431     }
432     oss << sep << include;
433     sep = ";";
434
435     if (lang == "Fortran") {
436       include += "/$(ConfigurationName)";
437       oss << sep << include;
438     }
439   }
440
441   if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
442     oss << sep << "%(" << tag << ")";
443   }
444
445   this->OutputFlag(fout, indent, tag, oss.str());
446 }
447
448 void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout,
449                                                    int indent)
450 {
451   for (auto const& m : this->FlagMap) {
452     std::ostringstream oss;
453     const char* sep = "";
454     for (std::string i : m.second) {
455       if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
456         cmVS10EscapeForMSBuild(i);
457       }
458       oss << sep << i;
459       sep = ";";
460     }
461
462     this->OutputFlag(fout, indent, m.first, oss.str());
463   }
464 }