|
|
|
#include "cmVisualStudioGeneratorOptions.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <map>
|
|
|
|
#include <sstream>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <cm/iterator>
|
|
|
|
#include <cmext/string_view>
|
|
|
|
|
|
|
|
#include "cmAlgorithms.h"
|
|
|
|
#include "cmLocalVisualStudioGenerator.h"
|
|
|
|
#include "cmOutputConverter.h"
|
|
|
|
#include "cmRange.h"
|
|
|
|
#include "cmStringAlgorithms.h"
|
|
|
|
#include "cmSystemTools.h"
|
|
|
|
|
|
|
|
static void cmVS10EscapeForMSBuild(std::string& ret)
|
|
|
|
{
|
|
|
|
cmSystemTools::ReplaceString(ret, ";", "%3B");
|
|
|
|
}
|
|
|
|
|
|
|
|
cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions(
|
|
|
|
cmLocalVisualStudioGenerator* lg, Tool tool, cmVS7FlagTable const* table,
|
|
|
|
cmVS7FlagTable const* extraTable)
|
|
|
|
: cmIDEOptions()
|
|
|
|
, LocalGenerator(lg)
|
|
|
|
, CurrentTool(tool)
|
|
|
|
{
|
|
|
|
// Store the given flag tables.
|
|
|
|
this->AddTable(table);
|
|
|
|
this->AddTable(extraTable);
|
|
|
|
|
|
|
|
// Preprocessor definitions are not allowed for linker tools.
|
|
|
|
this->AllowDefine = (tool != Linker);
|
|
|
|
|
|
|
|
// include directories are not allowed for linker tools.
|
|
|
|
this->AllowInclude = (tool != Linker);
|
|
|
|
|
|
|
|
// Slash options are allowed for VS.
|
|
|
|
this->AllowSlash = true;
|
|
|
|
|
|
|
|
this->FortranRuntimeDebug = false;
|
|
|
|
this->FortranRuntimeDLL = false;
|
|
|
|
this->FortranRuntimeMT = false;
|
|
|
|
|
|
|
|
this->UnknownFlagField = "AdditionalOptions";
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::AddTable(cmVS7FlagTable const* table)
|
|
|
|
{
|
|
|
|
if (table) {
|
|
|
|
for (auto& flag : this->FlagTable) {
|
|
|
|
if (!flag) {
|
|
|
|
flag = table;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::ClearTables()
|
|
|
|
{
|
|
|
|
for (auto& flag : this->FlagTable) {
|
|
|
|
flag = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::FixExceptionHandlingDefault()
|
|
|
|
{
|
|
|
|
// Exception handling is on by default because the platform file has
|
|
|
|
// "/EHsc" in the flags. Normally, that will override this
|
|
|
|
// initialization to off, but the user has the option of removing
|
|
|
|
// the flag to disable exception handling. When the user does
|
|
|
|
// remove the flag we need to override the IDE default of on.
|
|
|
|
if (!this->LocalGenerator->IsVFProj()) {
|
|
|
|
// by default VS puts <ExceptionHandling></ExceptionHandling> empty
|
|
|
|
// for a project, to make our projects look the same put a new line
|
|
|
|
// and space over for the closing </ExceptionHandling> as the default
|
|
|
|
// value
|
|
|
|
this->FlagMap["ExceptionHandling"] = "\n ";
|
|
|
|
} else {
|
|
|
|
this->FlagMap["ExceptionHandling"] = "0";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::SetVerboseMakefile(bool verbose)
|
|
|
|
{
|
|
|
|
// If verbose makefiles have been requested and the /nologo option
|
|
|
|
// was not given explicitly in the flags we want to add an attribute
|
|
|
|
// to the generated project to disable logo suppression. Otherwise
|
|
|
|
// the GUI default is to enable suppression.
|
|
|
|
//
|
|
|
|
// In '.vfproj' files, the value of this attribute should be
|
|
|
|
// "FALSE", instead of an empty string.
|
|
|
|
if (verbose &&
|
|
|
|
this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) {
|
|
|
|
this->FlagMap["SuppressStartupBanner"] =
|
|
|
|
!this->LocalGenerator->IsVFProj() ? "" : "FALSE";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmVisualStudioGeneratorOptions::UsingDebugInfo() const
|
|
|
|
{
|
|
|
|
if (this->CurrentTool != CSharpCompiler) {
|
|
|
|
return this->FlagMap.find("DebugInformationFormat") != this->FlagMap.end();
|
|
|
|
}
|
|
|
|
auto i = this->FlagMap.find("DebugType");
|
|
|
|
if (i != this->FlagMap.end()) {
|
|
|
|
if (i->second.size() == 1) {
|
|
|
|
return i->second[0] != "none"_s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
cm::optional<bool> cmVisualStudioGeneratorOptions::UsingDebugRuntime() const
|
|
|
|
{
|
|
|
|
cm::optional<bool> result;
|
|
|
|
if (const char* rtl = this->GetFlag("RuntimeLibrary")) {
|
|
|
|
result = strstr(rtl, "Debug") != nullptr;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmVisualStudioGeneratorOptions::IsWinRt() const
|
|
|
|
{
|
|
|
|
return this->FlagMap.find("CompileAsWinRT") != this->FlagMap.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmVisualStudioGeneratorOptions::IsManaged() const
|
|
|
|
{
|
|
|
|
return this->FlagMap.find("CompileAsManaged") != this->FlagMap.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmVisualStudioGeneratorOptions::UsingUnicode() const
|
|
|
|
{
|
|
|
|
// Look for a _UNICODE definition.
|
|
|
|
return std::any_of(
|
|
|
|
this->Defines.begin(), this->Defines.end(), [](std::string const& di) {
|
|
|
|
return di == "_UNICODE"_s || cmHasLiteralPrefix(di, "_UNICODE=");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
bool cmVisualStudioGeneratorOptions::UsingSBCS() const
|
|
|
|
{
|
|
|
|
// Look for a _SBCS definition.
|
|
|
|
return std::any_of(
|
|
|
|
this->Defines.begin(), this->Defines.end(), [](std::string const& di) {
|
|
|
|
return di == "_SBCS"_s || cmHasLiteralPrefix(di, "_SBCS=");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
|
|
|
|
{
|
|
|
|
// Create an empty CodeGeneration field, and pass the the actual
|
|
|
|
// compile flags via additional options so that we have consistent
|
|
|
|
// behavior and avoid issues with MSBuild extensions injecting
|
|
|
|
// virtual code when we request real only.
|
|
|
|
FlagValue& code_gen_flag = this->FlagMap["CodeGeneration"];
|
|
|
|
code_gen_flag = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::FixManifestUACFlags()
|
|
|
|
{
|
|
|
|
static std::string const ENABLE_UAC = "EnableUAC";
|
|
|
|
if (!HasFlag(ENABLE_UAC)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string uacFlag = GetFlag(ENABLE_UAC);
|
|
|
|
std::vector<std::string> subOptions;
|
|
|
|
cmsys::SystemTools::Split(uacFlag, subOptions, ' ');
|
|
|
|
if (subOptions.empty()) {
|
|
|
|
AddFlag(ENABLE_UAC, "true");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (subOptions.size() == 1 && subOptions[0] == "NO"_s) {
|
|
|
|
AddFlag(ENABLE_UAC, "false");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::map<std::string, std::string> uacMap;
|
|
|
|
uacMap["level"] = "UACExecutionLevel";
|
|
|
|
uacMap["uiAccess"] = "UACUIAccess";
|
|
|
|
|
|
|
|
std::map<std::string, std::string> uacExecuteLevelMap;
|
|
|
|
uacExecuteLevelMap["asInvoker"] = "AsInvoker";
|
|
|
|
uacExecuteLevelMap["highestAvailable"] = "HighestAvailable";
|
|
|
|
uacExecuteLevelMap["requireAdministrator"] = "RequireAdministrator";
|
|
|
|
|
|
|
|
for (std::string const& subopt : subOptions) {
|
|
|
|
std::vector<std::string> keyValue;
|
|
|
|
cmsys::SystemTools::Split(subopt, keyValue, '=');
|
|
|
|
if (keyValue.size() != 2 || (uacMap.find(keyValue[0]) == uacMap.end())) {
|
|
|
|
// ignore none key=value option or unknown flags
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyValue[1].front() == '\'' && keyValue[1].back() == '\'') {
|
|
|
|
keyValue[1] = keyValue[1].substr(
|
|
|
|
1, std::max(std::string::size_type(0), keyValue[1].length() - 2));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyValue[0] == "level"_s) {
|
|
|
|
if (uacExecuteLevelMap.find(keyValue[1]) == uacExecuteLevelMap.end()) {
|
|
|
|
// unknown level value
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
AddFlag(uacMap[keyValue[0]], uacExecuteLevelMap[keyValue[1]]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyValue[0] == "uiAccess"_s) {
|
|
|
|
if (keyValue[1] != "true"_s && keyValue[1] != "false"_s) {
|
|
|
|
// unknown uiAccess value
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
AddFlag(uacMap[keyValue[0]], keyValue[1]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// unknown sub option
|
|
|
|
}
|
|
|
|
|
|
|
|
AddFlag(ENABLE_UAC, "true");
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::Parse(const std::string& flags)
|
|
|
|
{
|
|
|
|
// Parse the input string as a windows command line since the string
|
|
|
|
// is intended for writing directly into the build files.
|
|
|
|
std::vector<std::string> args;
|
|
|
|
cmSystemTools::ParseWindowsCommandLine(flags.c_str(), args);
|
|
|
|
|
|
|
|
// Process flags that need to be represented specially in the IDE
|
|
|
|
// project file.
|
|
|
|
for (std::string const& ai : args) {
|
|
|
|
this->HandleFlag(ai);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::ParseFinish()
|
|
|
|
{
|
|
|
|
if (this->CurrentTool == FortranCompiler) {
|
|
|
|
// "RuntimeLibrary" attribute values:
|
|
|
|
// "rtMultiThreaded", "0", /threads /libs:static
|
|
|
|
// "rtMultiThreadedDLL", "2", /threads /libs:dll
|
|
|
|
// "rtMultiThreadedDebug", "1", /threads /dbglibs /libs:static
|
|
|
|
// "rtMultiThreadedDebugDLL", "3", /threads /dbglibs /libs:dll
|
|
|
|
// These seem unimplemented by the IDE:
|
|
|
|
// "rtSingleThreaded", "4", /libs:static
|
|
|
|
// "rtSingleThreadedDLL", "10", /libs:dll
|
|
|
|
// "rtSingleThreadedDebug", "5", /dbglibs /libs:static
|
|
|
|
// "rtSingleThreadedDebugDLL", "11", /dbglibs /libs:dll
|
|
|
|
std::string rl =
|
|
|
|
cmStrCat("rtMultiThreaded", this->FortranRuntimeDebug ? "Debug" : "",
|
|
|
|
this->FortranRuntimeDLL ? "DLL" : "");
|
|
|
|
this->FlagMap["RuntimeLibrary"] = rl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->CurrentTool == CudaCompiler) {
|
|
|
|
auto i = this->FlagMap.find("CudaRuntime");
|
|
|
|
if (i != this->FlagMap.end() && i->second.size() == 1) {
|
|
|
|
std::string& cudaRuntime = i->second[0];
|
|
|
|
if (cudaRuntime == "static"_s) {
|
|
|
|
cudaRuntime = "Static";
|
|
|
|
} else if (cudaRuntime == "shared"_s) {
|
|
|
|
cudaRuntime = "Shared";
|
|
|
|
} else if (cudaRuntime == "none"_s) {
|
|
|
|
cudaRuntime = "None";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::PrependInheritedString(
|
|
|
|
std::string const& key)
|
|
|
|
{
|
|
|
|
auto i = this->FlagMap.find(key);
|
|
|
|
if (i == this->FlagMap.end() || i->second.size() != 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::string& value = i->second[0];
|
|
|
|
value = cmStrCat("%(", key, ") ", value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::Reparse(std::string const& key)
|
|
|
|
{
|
|
|
|
auto i = this->FlagMap.find(key);
|
|
|
|
if (i == this->FlagMap.end() || i->second.size() != 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::string const original = i->second[0];
|
|
|
|
i->second[0] = "";
|
|
|
|
this->UnknownFlagField = key;
|
|
|
|
this->Parse(original);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag)
|
|
|
|
{
|
|
|
|
// Look for Intel Fortran flags that do not map well in the flag table.
|
|
|
|
if (this->CurrentTool == FortranCompiler) {
|
|
|
|
if (flag == "/dbglibs"_s || flag == "-dbglibs"_s) {
|
|
|
|
this->FortranRuntimeDebug = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (flag == "/threads"_s || flag == "-threads"_s) {
|
|
|
|
this->FortranRuntimeMT = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (flag == "/libs:dll"_s || flag == "-libs:dll"_s) {
|
|
|
|
this->FortranRuntimeDLL = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (flag == "/libs:static"_s || flag == "-libs:static"_s) {
|
|
|
|
this->FortranRuntimeDLL = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This option is not known. Store it in the output flags.
|
|
|
|
std::string const opts = cmOutputConverter::EscapeWindowsShellArgument(
|
|
|
|
flag.c_str(),
|
|
|
|
cmOutputConverter::Shell_Flag_AllowMakeVariables |
|
|
|
|
cmOutputConverter::Shell_Flag_VSIDE);
|
|
|
|
this->AppendFlagString(this->UnknownFlagField, opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmIDEOptions::FlagValue cmVisualStudioGeneratorOptions::TakeFlag(
|
|
|
|
std::string const& key)
|
|
|
|
{
|
|
|
|
FlagValue value;
|
|
|
|
auto i = this->FlagMap.find(key);
|
|
|
|
if (i != this->FlagMap.end()) {
|
|
|
|
value = i->second;
|
|
|
|
this->FlagMap.erase(i);
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::SetConfiguration(
|
|
|
|
const std::string& config)
|
|
|
|
{
|
|
|
|
this->Configuration = config;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string& cmVisualStudioGeneratorOptions::GetConfiguration() const
|
|
|
|
{
|
|
|
|
return this->Configuration;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions(
|
|
|
|
std::ostream& fout, int indent, const std::string& lang)
|
|
|
|
{
|
|
|
|
if (this->Defines.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::string tag = "PreprocessorDefinitions";
|
|
|
|
if (lang == "CUDA"_s) {
|
|
|
|
tag = "Defines";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
if (!this->LocalGenerator->IsVFProj()) {
|
|
|
|
oss << "%(" << tag << ')';
|
|
|
|
}
|
|
|
|
auto de = cmRemoveDuplicates(this->Defines);
|
|
|
|
for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
|
|
|
|
std::string define;
|
|
|
|
if (!this->LocalGenerator->IsVFProj()) {
|
|
|
|
// Escape the definition for MSBuild.
|
|
|
|
define = di;
|
|
|
|
cmVS10EscapeForMSBuild(define);
|
|
|
|
if (lang == "RC"_s) {
|
|
|
|
cmSystemTools::ReplaceString(define, "\"", "\\\"");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Escape the definition for the compiler.
|
|
|
|
define = this->LocalGenerator->EscapeForShell(di, true);
|
|
|
|
}
|
|
|
|
// Store the flag in the project file.
|
|
|
|
oss << ';' << define;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->OutputFlag(fout, indent, tag, oss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories(
|
|
|
|
std::ostream& fout, int indent, const std::string& lang)
|
|
|
|
{
|
|
|
|
if (this->Includes.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string tag = "AdditionalIncludeDirectories";
|
|
|
|
if (lang == "CUDA"_s) {
|
|
|
|
tag = "Include";
|
|
|
|
} else if (lang == "ASM_MASM"_s || lang == "ASM_NASM"_s) {
|
|
|
|
tag = "IncludePaths";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
const char* sep = "";
|
|
|
|
for (std::string include : this->Includes) {
|
|
|
|
// first convert all of the slashes
|
|
|
|
std::string::size_type pos = 0;
|
|
|
|
while ((pos = include.find('/', pos)) != std::string::npos) {
|
|
|
|
include[pos] = '\\';
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lang == "ASM_NASM"_s) {
|
|
|
|
include += '\\';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Escape this include for the MSBuild.
|
|
|
|
if (!this->LocalGenerator->IsVFProj()) {
|
|
|
|
cmVS10EscapeForMSBuild(include);
|
|
|
|
}
|
|
|
|
oss << sep << include;
|
|
|
|
sep = ";";
|
|
|
|
|
|
|
|
if (lang == "Fortran"_s) {
|
|
|
|
include += "/$(ConfigurationName)";
|
|
|
|
oss << sep << include;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this->LocalGenerator->IsVFProj()) {
|
|
|
|
oss << sep << "%(" << tag << ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
this->OutputFlag(fout, indent, tag, oss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout,
|
|
|
|
int indent)
|
|
|
|
{
|
|
|
|
for (auto const& m : this->FlagMap) {
|
|
|
|
std::ostringstream oss;
|
|
|
|
const char* sep = "";
|
|
|
|
for (std::string i : m.second) {
|
|
|
|
if (!this->LocalGenerator->IsVFProj()) {
|
|
|
|
cmVS10EscapeForMSBuild(i);
|
|
|
|
}
|
|
|
|
oss << sep << i;
|
|
|
|
sep = ";";
|
|
|
|
}
|
|
|
|
|
|
|
|
this->OutputFlag(fout, indent, m.first, oss.str());
|
|
|
|
}
|
|
|
|
}
|