1 #include "cmVisualStudioGeneratorOptions.h"
10 #include <cmext/string_view>
12 #include "cmAlgorithms.h"
13 #include "cmLocalVisualStudioGenerator.h"
14 #include "cmOutputConverter.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
)
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 (auto& flag
: this->FlagTable
) {
63 void cmVisualStudioGeneratorOptions::ClearTables()
65 for (auto& flag
: this->FlagTable
) {
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
82 this->FlagMap
["ExceptionHandling"] = "\n ";
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.
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
;
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;
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.
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.
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"];
164 void cmVisualStudioGeneratorOptions::FixManifestUACFlags()
166 static std::string
const ENABLE_UAC
= "EnableUAC";
167 if (!HasFlag(ENABLE_UAC
)) {
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");
179 if (subOptions
.size() == 1 && subOptions
[0] == "NO"_s
) {
180 AddFlag(ENABLE_UAC
, "false");
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
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
212 AddFlag(uacMap
[keyValue
[0]], uacExecuteLevelMap
[keyValue
[1]]);
216 if (keyValue
[0] == "uiAccess"_s
) {
217 if (keyValue
[1] != "true"_s
&& keyValue
[1] != "false"_s
) {
218 // unknown uiAccess value
221 AddFlag(uacMap
[keyValue
[0]], keyValue
[1]);
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
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
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) {
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) {
296 std::string
const original
= 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;
310 if (flag
== "/threads"_s
|| flag
== "-threads"_s
) {
311 this->FortranRuntimeMT
= true;
314 if (flag
== "/libs:dll"_s
|| flag
== "-libs:dll"_s
) {
315 this->FortranRuntimeDLL
= true;
318 if (flag
== "/libs:static"_s
|| flag
== "-libs:static"_s
) {
319 this->FortranRuntimeDLL
= false;
324 // This option is not known. Store it in the output flags.
325 std::string
const opts
= cmOutputConverter::EscapeWindowsShellArgument(
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
)
336 auto i
= this->FlagMap
.find(key
);
337 if (i
!= this->FlagMap
.end()) {
339 this->FlagMap
.erase(i
);
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()) {
361 std::string tag
= "PreprocessorDefinitions";
362 if (lang
== "CUDA"_s
) {
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
)) {
373 if (!this->LocalGenerator
->IsVFProj()) {
374 // Escape the definition for MSBuild.
376 cmVS10EscapeForMSBuild(define
);
377 if (lang
== "RC"_s
) {
378 cmSystemTools::ReplaceString(define
, "\"", "\\\"");
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()) {
398 std::string tag
= "AdditionalIncludeDirectories";
399 if (lang
== "CUDA"_s
) {
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
) {
415 if (lang
== "ASM_NASM"_s
) {
419 // Escape this include for the MSBuild.
420 if (!this->LocalGenerator
->IsVFProj()) {
421 cmVS10EscapeForMSBuild(include
);
423 oss
<< sep
<< include
;
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
,
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
);
453 this->OutputFlag(fout
, indent
, m
.first
, oss
.str());