CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmVisualStudioGeneratorOptions.cxx
bloba5f2cdfb631343671f8edaa78556ec271c4a2738
1 #include "cmVisualStudioGeneratorOptions.h"
3 #include <algorithm>
4 #include <map>
5 #include <sstream>
6 #include <utility>
7 #include <vector>
9 #include <cm/iterator>
10 #include <cmext/string_view>
12 #include "cmAlgorithms.h"
13 #include "cmLocalVisualStudioGenerator.h"
14 #include "cmOutputConverter.h"
15 #include "cmRange.h"
16 #include "cmStringAlgorithms.h"
17 #include "cmSystemTools.h"
19 static void cmVS10EscapeForMSBuild(std::string& ret)
21 cmSystemTools::ReplaceString(ret, ";", "%3B");
24 cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions(
25 cmLocalVisualStudioGenerator* lg, Tool tool, cmVS7FlagTable const* table,
26 cmVS7FlagTable const* extraTable)
27 : cmIDEOptions()
28 , LocalGenerator(lg)
29 , CurrentTool(tool)
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)
53 if (table) {
54 for (auto& flag : this->FlagTable) {
55 if (!flag) {
56 flag = table;
57 break;
63 void cmVisualStudioGeneratorOptions::ClearTables()
65 for (auto& flag : this->FlagTable) {
66 flag = 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 if (!this->LocalGenerator->IsVFProj()) {
78 // by default VS puts <ExceptionHandling></ExceptionHandling> empty
79 // for a project, to make our projects look the same put a new line
80 // and space over for the closing </ExceptionHandling> as the default
81 // value
82 this->FlagMap["ExceptionHandling"] = "\n ";
83 } else {
84 this->FlagMap["ExceptionHandling"] = "0";
88 void cmVisualStudioGeneratorOptions::SetVerboseMakefile(bool verbose)
90 // If verbose makefiles have been requested and the /nologo option
91 // was not given explicitly in the flags we want to add an attribute
92 // to the generated project to disable logo suppression. Otherwise
93 // the GUI default is to enable suppression.
95 // In '.vfproj' files, the value of this attribute should be
96 // "FALSE", instead of an empty string.
97 if (verbose &&
98 this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) {
99 this->FlagMap["SuppressStartupBanner"] =
100 !this->LocalGenerator->IsVFProj() ? "" : "FALSE";
104 bool cmVisualStudioGeneratorOptions::UsingDebugInfo() const
106 if (this->CurrentTool != CSharpCompiler) {
107 return this->FlagMap.find("DebugInformationFormat") != this->FlagMap.end();
109 auto i = this->FlagMap.find("DebugType");
110 if (i != this->FlagMap.end()) {
111 if (i->second.size() == 1) {
112 return i->second[0] != "none"_s;
115 return false;
118 cm::optional<bool> cmVisualStudioGeneratorOptions::UsingDebugRuntime() const
120 cm::optional<bool> result;
121 if (const char* rtl = this->GetFlag("RuntimeLibrary")) {
122 result = strstr(rtl, "Debug") != nullptr;
124 return result;
127 bool cmVisualStudioGeneratorOptions::IsWinRt() const
129 return this->FlagMap.find("CompileAsWinRT") != this->FlagMap.end();
132 bool cmVisualStudioGeneratorOptions::IsManaged() const
134 return this->FlagMap.find("CompileAsManaged") != this->FlagMap.end();
137 bool cmVisualStudioGeneratorOptions::UsingUnicode() const
139 // Look for a _UNICODE definition.
140 return std::any_of(
141 this->Defines.begin(), this->Defines.end(), [](std::string const& di) {
142 return di == "_UNICODE"_s || cmHasLiteralPrefix(di, "_UNICODE=");
145 bool cmVisualStudioGeneratorOptions::UsingSBCS() const
147 // Look for a _SBCS definition.
148 return std::any_of(
149 this->Defines.begin(), this->Defines.end(), [](std::string const& di) {
150 return di == "_SBCS"_s || cmHasLiteralPrefix(di, "_SBCS=");
154 void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
156 // Create an empty CodeGeneration field, and pass the the actual
157 // compile flags via additional options so that we have consistent
158 // behavior and avoid issues with MSBuild extensions injecting
159 // virtual code when we request real only.
160 FlagValue& code_gen_flag = this->FlagMap["CodeGeneration"];
161 code_gen_flag = "";
164 void cmVisualStudioGeneratorOptions::FixManifestUACFlags()
166 static std::string const ENABLE_UAC = "EnableUAC";
167 if (!HasFlag(ENABLE_UAC)) {
168 return;
171 const std::string uacFlag = GetFlag(ENABLE_UAC);
172 std::vector<std::string> subOptions;
173 cmsys::SystemTools::Split(uacFlag, subOptions, ' ');
174 if (subOptions.empty()) {
175 AddFlag(ENABLE_UAC, "true");
176 return;
179 if (subOptions.size() == 1 && subOptions[0] == "NO"_s) {
180 AddFlag(ENABLE_UAC, "false");
181 return;
184 std::map<std::string, std::string> uacMap;
185 uacMap["level"] = "UACExecutionLevel";
186 uacMap["uiAccess"] = "UACUIAccess";
188 std::map<std::string, std::string> uacExecuteLevelMap;
189 uacExecuteLevelMap["asInvoker"] = "AsInvoker";
190 uacExecuteLevelMap["highestAvailable"] = "HighestAvailable";
191 uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator";
193 for (std::string const& subopt : subOptions) {
194 std::vector<std::string> keyValue;
195 cmsys::SystemTools::Split(subopt, keyValue, '=');
196 if (keyValue.size() != 2 || (uacMap.find(keyValue[0]) == uacMap.end())) {
197 // ignore none key=value option or unknown flags
198 continue;
201 if (keyValue[1].front() == '\'' && keyValue[1].back() == '\'') {
202 keyValue[1] = keyValue[1].substr(
203 1, std::max(std::string::size_type(0), keyValue[1].length() - 2));
206 if (keyValue[0] == "level"_s) {
207 if (uacExecuteLevelMap.find(keyValue[1]) == uacExecuteLevelMap.end()) {
208 // unknown level value
209 continue;
212 AddFlag(uacMap[keyValue[0]], uacExecuteLevelMap[keyValue[1]]);
213 continue;
216 if (keyValue[0] == "uiAccess"_s) {
217 if (keyValue[1] != "true"_s && keyValue[1] != "false"_s) {
218 // unknown uiAccess value
219 continue;
221 AddFlag(uacMap[keyValue[0]], keyValue[1]);
222 continue;
225 // unknown sub option
228 AddFlag(ENABLE_UAC, "true");
231 void cmVisualStudioGeneratorOptions::Parse(const std::string& flags)
233 // Parse the input string as a windows command line since the string
234 // is intended for writing directly into the build files.
235 std::vector<std::string> args;
236 cmSystemTools::ParseWindowsCommandLine(flags.c_str(), args);
238 // Process flags that need to be represented specially in the IDE
239 // project file.
240 for (std::string const& ai : args) {
241 this->HandleFlag(ai);
245 void cmVisualStudioGeneratorOptions::ParseFinish()
247 if (this->CurrentTool == FortranCompiler) {
248 // "RuntimeLibrary" attribute values:
249 // "rtMultiThreaded", "0", /threads /libs:static
250 // "rtMultiThreadedDLL", "2", /threads /libs:dll
251 // "rtMultiThreadedDebug", "1", /threads /dbglibs /libs:static
252 // "rtMultiThreadedDebugDLL", "3", /threads /dbglibs /libs:dll
253 // These seem unimplemented by the IDE:
254 // "rtSingleThreaded", "4", /libs:static
255 // "rtSingleThreadedDLL", "10", /libs:dll
256 // "rtSingleThreadedDebug", "5", /dbglibs /libs:static
257 // "rtSingleThreadedDebugDLL", "11", /dbglibs /libs:dll
258 std::string rl =
259 cmStrCat("rtMultiThreaded", this->FortranRuntimeDebug ? "Debug" : "",
260 this->FortranRuntimeDLL ? "DLL" : "");
261 this->FlagMap["RuntimeLibrary"] = rl;
264 if (this->CurrentTool == CudaCompiler) {
265 auto i = this->FlagMap.find("CudaRuntime");
266 if (i != this->FlagMap.end() && i->second.size() == 1) {
267 std::string& cudaRuntime = i->second[0];
268 if (cudaRuntime == "static"_s) {
269 cudaRuntime = "Static";
270 } else if (cudaRuntime == "shared"_s) {
271 cudaRuntime = "Shared";
272 } else if (cudaRuntime == "none"_s) {
273 cudaRuntime = "None";
279 void cmVisualStudioGeneratorOptions::PrependInheritedString(
280 std::string const& key)
282 auto i = this->FlagMap.find(key);
283 if (i == this->FlagMap.end() || i->second.size() != 1) {
284 return;
286 std::string& value = i->second[0];
287 value = cmStrCat("%(", key, ") ", value);
290 void cmVisualStudioGeneratorOptions::Reparse(std::string const& key)
292 auto i = this->FlagMap.find(key);
293 if (i == this->FlagMap.end() || i->second.size() != 1) {
294 return;
296 std::string const original = i->second[0];
297 i->second[0] = "";
298 this->UnknownFlagField = key;
299 this->Parse(original);
302 void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag)
304 // Look for Intel Fortran flags that do not map well in the flag table.
305 if (this->CurrentTool == FortranCompiler) {
306 if (flag == "/dbglibs"_s || flag == "-dbglibs"_s) {
307 this->FortranRuntimeDebug = true;
308 return;
310 if (flag == "/threads"_s || flag == "-threads"_s) {
311 this->FortranRuntimeMT = true;
312 return;
314 if (flag == "/libs:dll"_s || flag == "-libs:dll"_s) {
315 this->FortranRuntimeDLL = true;
316 return;
318 if (flag == "/libs:static"_s || flag == "-libs:static"_s) {
319 this->FortranRuntimeDLL = false;
320 return;
324 // This option is not known. Store it in the output flags.
325 std::string const opts = cmOutputConverter::EscapeWindowsShellArgument(
326 flag.c_str(),
327 cmOutputConverter::Shell_Flag_AllowMakeVariables |
328 cmOutputConverter::Shell_Flag_VSIDE);
329 this->AppendFlagString(this->UnknownFlagField, opts);
332 cmIDEOptions::FlagValue cmVisualStudioGeneratorOptions::TakeFlag(
333 std::string const& key)
335 FlagValue value;
336 auto i = this->FlagMap.find(key);
337 if (i != this->FlagMap.end()) {
338 value = i->second;
339 this->FlagMap.erase(i);
341 return value;
344 void cmVisualStudioGeneratorOptions::SetConfiguration(
345 const std::string& config)
347 this->Configuration = config;
350 const std::string& cmVisualStudioGeneratorOptions::GetConfiguration() const
352 return this->Configuration;
355 void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions(
356 std::ostream& fout, int indent, const std::string& lang)
358 if (this->Defines.empty()) {
359 return;
361 std::string tag = "PreprocessorDefinitions";
362 if (lang == "CUDA"_s) {
363 tag = "Defines";
366 std::ostringstream oss;
367 if (!this->LocalGenerator->IsVFProj()) {
368 oss << "%(" << tag << ')';
370 auto de = cmRemoveDuplicates(this->Defines);
371 for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
372 std::string define;
373 if (!this->LocalGenerator->IsVFProj()) {
374 // Escape the definition for MSBuild.
375 define = di;
376 cmVS10EscapeForMSBuild(define);
377 if (lang == "RC"_s) {
378 cmSystemTools::ReplaceString(define, "\"", "\\\"");
380 } else {
381 // Escape the definition for the compiler.
382 define = this->LocalGenerator->EscapeForShell(di, true);
384 // Store the flag in the project file.
385 oss << ';' << define;
388 this->OutputFlag(fout, indent, tag, oss.str());
391 void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories(
392 std::ostream& fout, int indent, const std::string& lang)
394 if (this->Includes.empty()) {
395 return;
398 std::string tag = "AdditionalIncludeDirectories";
399 if (lang == "CUDA"_s) {
400 tag = "Include";
401 } else if (lang == "ASM_MASM"_s || lang == "ASM_NASM"_s) {
402 tag = "IncludePaths";
405 std::ostringstream oss;
406 const char* sep = "";
407 for (std::string include : this->Includes) {
408 // first convert all of the slashes
409 std::string::size_type pos = 0;
410 while ((pos = include.find('/', pos)) != std::string::npos) {
411 include[pos] = '\\';
412 pos++;
415 if (lang == "ASM_NASM"_s) {
416 include += '\\';
419 // Escape this include for the MSBuild.
420 if (!this->LocalGenerator->IsVFProj()) {
421 cmVS10EscapeForMSBuild(include);
423 oss << sep << include;
424 sep = ";";
426 if (lang == "Fortran"_s) {
427 include += "/$(ConfigurationName)";
428 oss << sep << include;
432 if (!this->LocalGenerator->IsVFProj()) {
433 oss << sep << "%(" << tag << ')';
436 this->OutputFlag(fout, indent, tag, oss.str());
439 void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout,
440 int indent)
442 for (auto const& m : this->FlagMap) {
443 std::ostringstream oss;
444 const char* sep = "";
445 for (std::string i : m.second) {
446 if (!this->LocalGenerator->IsVFProj()) {
447 cmVS10EscapeForMSBuild(i);
449 oss << sep << i;
450 sep = ";";
453 this->OutputFlag(fout, indent, m.first, oss.str());