MSVC: Always define a character set

When targeting the MSVC ABI, define `_MBCS` by default if the project
does not define `_SBCS` or `_UNICODE`.  Visual Studio has long defined
one of the three character set macros automatically.  For consistency,
define it when compiling for the MSVC ABI with other generators.
Add policy CMP0204 for compatibility.

Fixes: #27275
This commit is contained in:
AJIOB
2025-09-24 18:54:02 +03:00
committed by Brad King
parent 2cae68ecbe
commit 6874efb592
86 changed files with 819 additions and 137 deletions
+11
View File
@@ -1538,6 +1538,17 @@ public:
std::string BuildDatabasePath(std::string const& lang,
std::string const& config) const;
enum class MsvcCharSet
{
None,
Unicode,
MultiByte,
SingleByte,
};
// Detect if the current define selects any known charset entry or not
static MsvcCharSet GetMsvcCharSet(std::string const& singleDefine);
private:
void BuildFileSetInfoCache(std::string const& config) const;
struct InfoByConfig
+94
View File
@@ -38,6 +38,12 @@ enum class OptionsParse
Shell
};
struct MsvcCharSetInfo
{
cmGeneratorTarget::MsvcCharSet CharSet;
bool IsNeedToAddDefine;
};
namespace {
auto const DL_BEGIN = "<DEVICE_LINK>"_s;
auto const DL_END = "</DEVICE_LINK>"_s;
@@ -205,6 +211,45 @@ std::vector<BT<std::string>> wrapOptions(
}
return result;
}
cm::string_view const UNICODE_DEFINITION = "_UNICODE"_s;
cm::string_view const MBCS_DEFINITION = "_MBCS"_s;
cm::string_view const SBCS_DEFINITION = "_SBCS"_s;
constexpr char UNICODE_DEFINITION_PREFIX[] = "_UNICODE=";
constexpr char MBCS_DEFINITION_PREFIX[] = "_MBCS=";
constexpr char SBCS_DEFINITION_PREFIX[] = "_SBCS=";
MsvcCharSetInfo GetMsvcCharSetInfo(
cmGeneratorTarget const& tgt, std::string const& lang,
EvaluatedTargetPropertyEntries const& entries)
{
using MsvcCharSet = cmGeneratorTarget::MsvcCharSet;
if (tgt.Makefile->GetSafeDefinition(
cmStrCat("CMAKE_", lang, "_COMPILER_ID")) != "MSVC"_s &&
tgt.Makefile->GetSafeDefinition(
cmStrCat("CMAKE_", lang, "_SIMULATE_ID")) != "MSVC"_s) {
// Only MSVC ABI uses this feature
return { MsvcCharSet::None, false };
}
for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) {
for (std::string const& value : entry.Values) {
MsvcCharSet charSet = cmGeneratorTarget::GetMsvcCharSet(value);
if (charSet != MsvcCharSet::None) {
return { charSet, false };
}
}
}
// Default to multi-byte, similar to the Visual Studio generator
// Define the default charset for Visual Studio too:
// it should filter it out if need
return { MsvcCharSet::MultiByte, true };
}
}
void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result,
@@ -343,6 +388,34 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
AddInterfaceEntries(this, "INTERFACE_COMPILE_DEFINITIONS", context,
&dagChecker, entries, IncludeRuntimeInterface::Yes);
// Add the character set definition
MsvcCharSetInfo charSetInfo = GetMsvcCharSetInfo(*this, language, entries);
if (charSetInfo.IsNeedToAddDefine &&
this->GetPolicyStatusCMP0204() == cmPolicies::NEW) {
cm::string_view define;
switch (charSetInfo.CharSet) {
case MsvcCharSet::None:
// Nothing to set
break;
case MsvcCharSet::Unicode:
define = UNICODE_DEFINITION;
break;
case MsvcCharSet::MultiByte:
define = MBCS_DEFINITION;
break;
case MsvcCharSet::SingleByte:
define = SBCS_DEFINITION;
break;
}
if (!define.empty()) {
std::unique_ptr<TargetPropertyEntry> property =
TargetPropertyEntry::Create(*this->LocalGenerator->GetCMakeInstance(),
std::string{ define });
entries.Entries.emplace_back(
EvaluateTargetPropertyEntry(this, context, &dagChecker, *property));
}
}
processOptions(this, entries, list, uniqueOptions, debugDefines,
"compile definitions", OptionsParse::None);
@@ -677,3 +750,24 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
return result;
}
cmGeneratorTarget::MsvcCharSet cmGeneratorTarget::GetMsvcCharSet(
std::string const& singleDefine)
{
if (singleDefine == UNICODE_DEFINITION ||
cmHasLiteralPrefix(singleDefine, UNICODE_DEFINITION_PREFIX)) {
return MsvcCharSet::Unicode;
}
if (singleDefine == MBCS_DEFINITION ||
cmHasLiteralPrefix(singleDefine, MBCS_DEFINITION_PREFIX)) {
return MsvcCharSet::MultiByte;
}
if (singleDefine == SBCS_DEFINITION ||
cmHasLiteralPrefix(singleDefine, SBCS_DEFINITION_PREFIX)) {
return MsvcCharSet::SingleByte;
}
return MsvcCharSet::None;
}
+5
View File
@@ -4193,6 +4193,11 @@ bool cmLocalGenerator::IsNinjaMulti() const
return this->GetState()->UseNinjaMulti();
}
bool cmLocalGenerator::IsWindowsVSIDE() const
{
return this->GetState()->UseWindowsVSIDE();
}
namespace {
std::string relativeIfUnder(std::string const& top, std::string const& cur,
std::string const& path)
+1
View File
@@ -544,6 +544,7 @@ public:
bool IsMinGWMake() const;
bool IsNMake() const;
bool IsNinjaMulti() const;
bool IsWindowsVSIDE() const;
void IssueMessage(MessageType t, std::string const& text) const;
+6 -2
View File
@@ -609,7 +609,10 @@ class cmMakefile;
4, 2, 0, WARN) \
SELECT(POLICY, CMP0203, \
"_WINDLL is defined for shared libraries targeting the MSVC ABI.", \
4, 2, 0, WARN)
4, 2, 0, WARN) \
SELECT(POLICY, CMP0204, \
"A character set is always defined when targeting the MSVC ABI.", 4, \
2, 0, WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
@@ -661,7 +664,8 @@ class cmMakefile;
F(CMP0199) \
F(CMP0200) \
F(CMP0202) \
F(CMP0203)
F(CMP0203) \
F(CMP0204)
#define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \
F(CMP0116) \
+33 -10
View File
@@ -134,21 +134,44 @@ bool cmVisualStudioGeneratorOptions::IsManaged() const
return this->FlagMap.find("CompileAsManaged") != this->FlagMap.end();
}
void cmVisualStudioGeneratorOptions::CacheCharsetValue() const
{
using MsvcCharSet = cmGeneratorTarget::MsvcCharSet;
if (this->CachedCharset.has_value()) {
return;
}
MsvcCharSet newValue = MsvcCharSet::None;
// Look for a any charset definition.
// Real charset will be updated if something is found.
auto it = std::find_if(this->Defines.begin(), this->Defines.end(),
[&newValue](std::string const& define) {
newValue =
cmGeneratorTarget::GetMsvcCharSet(define);
return newValue != MsvcCharSet::None;
});
if (it == this->Defines.end()) {
// Default to multi-byte, as Visual Studio does
newValue = MsvcCharSet::MultiByte;
}
this->CachedCharset = newValue;
}
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=");
});
this->CacheCharsetValue();
return this->CachedCharset.value() ==
cmGeneratorTarget::MsvcCharSet::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=");
});
this->CacheCharsetValue();
return this->CachedCharset.value() ==
cmGeneratorTarget::MsvcCharSet::SingleByte;
}
void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
+5
View File
@@ -9,6 +9,7 @@
#include <cm/optional>
#include "cmGeneratorTarget.h"
#include "cmGlobalVisualStudioGenerator.h"
#include "cmIDEFlagTable.h"
#include "cmIDEOptions.h"
@@ -97,7 +98,11 @@ private:
std::string UnknownFlagField;
mutable cm::optional<cmGeneratorTarget::MsvcCharSet> CachedCharset;
void StoreUnknownFlag(std::string const& flag) override;
FlagValue TakeFlag(std::string const& key);
void CacheCharsetValue() const;
};