mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-01 11:22:21 -06:00
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
774 lines
26 KiB
C++
774 lines
26 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
/* clang-format off */
|
|
#include "cmGeneratorTarget.h"
|
|
/* clang-format on */
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cm/string_view>
|
|
#include <cmext/algorithm>
|
|
#include <cmext/string_view>
|
|
|
|
#include "cmEvaluatedTargetProperty.h"
|
|
#include "cmGenExContext.h"
|
|
#include "cmGeneratorExpressionDAGChecker.h"
|
|
#include "cmList.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmPolicies.h"
|
|
#include "cmRange.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmValue.h"
|
|
#include "cmake.h"
|
|
|
|
enum class OptionsParse
|
|
{
|
|
None,
|
|
Shell
|
|
};
|
|
|
|
struct MsvcCharSetInfo
|
|
{
|
|
cmGeneratorTarget::MsvcCharSet CharSet;
|
|
bool IsNeedToAddDefine;
|
|
};
|
|
|
|
namespace {
|
|
auto const DL_BEGIN = "<DEVICE_LINK>"_s;
|
|
auto const DL_END = "</DEVICE_LINK>"_s;
|
|
|
|
void processOptions(cmGeneratorTarget const* tgt,
|
|
EvaluatedTargetPropertyEntries const& entries,
|
|
std::vector<BT<std::string>>& options,
|
|
std::unordered_set<std::string>& uniqueOptions,
|
|
bool debugOptions, char const* logName, OptionsParse parse,
|
|
bool processDeviceOptions = false)
|
|
{
|
|
bool splitOption = !processDeviceOptions;
|
|
for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) {
|
|
std::string usedOptions;
|
|
for (std::string const& opt : entry.Values) {
|
|
if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) {
|
|
options.emplace_back(opt, entry.Backtrace);
|
|
splitOption = opt == DL_BEGIN;
|
|
continue;
|
|
}
|
|
|
|
if (uniqueOptions.insert(opt).second) {
|
|
if (parse == OptionsParse::Shell &&
|
|
cmHasLiteralPrefix(opt, "SHELL:")) {
|
|
if (splitOption) {
|
|
std::vector<std::string> tmp;
|
|
cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp);
|
|
for (std::string& o : tmp) {
|
|
options.emplace_back(std::move(o), entry.Backtrace);
|
|
}
|
|
} else {
|
|
options.emplace_back(std::string(opt.c_str() + 6),
|
|
entry.Backtrace);
|
|
}
|
|
} else {
|
|
options.emplace_back(opt, entry.Backtrace);
|
|
}
|
|
if (debugOptions) {
|
|
usedOptions += cmStrCat(" * ", opt, '\n');
|
|
}
|
|
}
|
|
}
|
|
if (!usedOptions.empty()) {
|
|
tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
|
|
MessageType::LOG,
|
|
cmStrCat("Used ", logName, " for target ", tgt->GetName(), ":\n",
|
|
usedOptions),
|
|
entry.Backtrace);
|
|
}
|
|
}
|
|
}
|
|
|
|
enum class NestedLinkerFlags
|
|
{
|
|
PreserveAsSpelled,
|
|
Normalize
|
|
};
|
|
|
|
std::vector<BT<std::string>> wrapOptions(
|
|
std::vector<std::string>& options, cmListFileBacktrace const& bt,
|
|
std::vector<std::string> const& wrapperFlag, std::string const& wrapperSep,
|
|
bool concatFlagAndArgs, NestedLinkerFlags nestedLinkerFlags)
|
|
{
|
|
std::vector<BT<std::string>> result;
|
|
|
|
if (options.empty()) {
|
|
return result;
|
|
}
|
|
|
|
if (wrapperFlag.empty()) {
|
|
// nothing specified, insert elements as is
|
|
result.reserve(options.size());
|
|
for (std::string& o : options) {
|
|
result.emplace_back(std::move(o), bt);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto insertWrapped = [&](std::vector<std::string>& opts) {
|
|
if (!wrapperSep.empty()) {
|
|
if (concatFlagAndArgs) {
|
|
// insert flag elements except last one
|
|
for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) {
|
|
result.emplace_back(*i, bt);
|
|
}
|
|
// concatenate last flag element and all list values
|
|
// in one option
|
|
result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt);
|
|
} else {
|
|
for (std::string const& i : wrapperFlag) {
|
|
result.emplace_back(i, bt);
|
|
}
|
|
// concatenate all list values in one option
|
|
result.emplace_back(cmJoin(opts, wrapperSep), bt);
|
|
}
|
|
} else {
|
|
// prefix each element of list with wrapper
|
|
if (concatFlagAndArgs) {
|
|
std::transform(opts.begin(), opts.end(), opts.begin(),
|
|
[&wrapperFlag](std::string const& o) -> std::string {
|
|
return wrapperFlag.back() + o;
|
|
});
|
|
}
|
|
for (std::string& o : opts) {
|
|
for (auto i = wrapperFlag.begin(),
|
|
e = concatFlagAndArgs ? wrapperFlag.end() - 1
|
|
: wrapperFlag.end();
|
|
i != e; ++i) {
|
|
result.emplace_back(*i, bt);
|
|
}
|
|
result.emplace_back(std::move(o), bt);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (nestedLinkerFlags == NestedLinkerFlags::PreserveAsSpelled) {
|
|
insertWrapped(options);
|
|
return result;
|
|
}
|
|
|
|
for (std::vector<std::string>::size_type index = 0; index < options.size();
|
|
index++) {
|
|
if (cmHasLiteralPrefix(options[index], "LINKER:")) {
|
|
// LINKER wrapper specified, insert elements as is
|
|
result.emplace_back(std::move(options[index]), bt);
|
|
continue;
|
|
}
|
|
if (cmHasLiteralPrefix(options[index], "-Wl,")) {
|
|
// replace option by LINKER wrapper
|
|
result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt);
|
|
continue;
|
|
}
|
|
if (cmHasLiteralPrefix(options[index], "-Xlinker=")) {
|
|
// replace option by LINKER wrapper
|
|
result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt);
|
|
continue;
|
|
}
|
|
if (options[index] == "-Xlinker") {
|
|
// replace option by LINKER wrapper
|
|
if (index + 1 < options.size()) {
|
|
result.emplace_back("LINKER:" + options[++index], bt);
|
|
} else {
|
|
result.emplace_back(std::move(options[index]), bt);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// collect all options which must be transformed
|
|
std::vector<std::string> opts;
|
|
while (index < options.size()) {
|
|
if (!cmHasLiteralPrefix(options[index], "LINKER:") &&
|
|
!cmHasLiteralPrefix(options[index], "-Wl,") &&
|
|
!cmHasLiteralPrefix(options[index], "-Xlinker")) {
|
|
opts.emplace_back(std::move(options[index++]));
|
|
} else {
|
|
--index;
|
|
break;
|
|
}
|
|
}
|
|
if (opts.empty()) {
|
|
continue;
|
|
}
|
|
|
|
insertWrapped(opts);
|
|
}
|
|
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,
|
|
std::string const& config,
|
|
std::string const& language) const
|
|
{
|
|
std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language);
|
|
result.reserve(tmp.size());
|
|
for (BT<std::string>& v : tmp) {
|
|
result.emplace_back(std::move(v.Value));
|
|
}
|
|
}
|
|
|
|
std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions(
|
|
std::string const& config, std::string const& language) const
|
|
{
|
|
ConfigAndLanguage cacheKey(config, language);
|
|
{
|
|
auto it = this->CompileOptionsCache.find(cacheKey);
|
|
if (it != this->CompileOptionsCache.end()) {
|
|
return it->second;
|
|
}
|
|
}
|
|
std::vector<BT<std::string>> result;
|
|
std::unordered_set<std::string> uniqueOptions;
|
|
|
|
cm::GenEx::Context context(this->LocalGenerator, config, language);
|
|
|
|
cmGeneratorExpressionDAGChecker dagChecker{
|
|
this, "COMPILE_OPTIONS", nullptr, nullptr, context,
|
|
};
|
|
|
|
cmList debugProperties{ this->Makefile->GetDefinition(
|
|
"CMAKE_DEBUG_TARGET_PROPERTIES") };
|
|
bool debugOptions = !this->DebugCompileOptionsDone &&
|
|
cm::contains(debugProperties, "COMPILE_OPTIONS");
|
|
|
|
this->DebugCompileOptionsDone = true;
|
|
|
|
EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
|
|
this, context, &dagChecker, this->CompileOptionsEntries);
|
|
|
|
AddInterfaceEntries(this, "INTERFACE_COMPILE_OPTIONS", context, &dagChecker,
|
|
entries, IncludeRuntimeInterface::Yes);
|
|
|
|
processOptions(this, entries, result, uniqueOptions, debugOptions,
|
|
"compile options", OptionsParse::Shell);
|
|
|
|
CompileOptionsCache.emplace(cacheKey, result);
|
|
return result;
|
|
}
|
|
|
|
void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result,
|
|
std::string const& config) const
|
|
{
|
|
std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config);
|
|
result.reserve(tmp.size());
|
|
for (BT<std::string>& v : tmp) {
|
|
result.emplace_back(std::move(v.Value));
|
|
}
|
|
}
|
|
|
|
std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures(
|
|
std::string const& config) const
|
|
{
|
|
std::vector<BT<std::string>> result;
|
|
std::unordered_set<std::string> uniqueFeatures;
|
|
|
|
cm::GenEx::Context context(this->LocalGenerator, config,
|
|
/*language=*/std::string());
|
|
|
|
cmGeneratorExpressionDAGChecker dagChecker{
|
|
this, "COMPILE_FEATURES", nullptr, nullptr, context,
|
|
};
|
|
|
|
cmList debugProperties{ this->Makefile->GetDefinition(
|
|
"CMAKE_DEBUG_TARGET_PROPERTIES") };
|
|
bool debugFeatures = !this->DebugCompileFeaturesDone &&
|
|
cm::contains(debugProperties, "COMPILE_FEATURES");
|
|
|
|
this->DebugCompileFeaturesDone = true;
|
|
|
|
EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
|
|
this, context, &dagChecker, this->CompileFeaturesEntries);
|
|
|
|
AddInterfaceEntries(this, "INTERFACE_COMPILE_FEATURES", context, &dagChecker,
|
|
entries, IncludeRuntimeInterface::Yes);
|
|
|
|
processOptions(this, entries, result, uniqueFeatures, debugFeatures,
|
|
"compile features", OptionsParse::None);
|
|
|
|
return result;
|
|
}
|
|
|
|
void cmGeneratorTarget::GetCompileDefinitions(
|
|
std::vector<std::string>& result, std::string const& config,
|
|
std::string const& language) const
|
|
{
|
|
std::vector<BT<std::string>> tmp =
|
|
this->GetCompileDefinitions(config, language);
|
|
result.reserve(result.size() + tmp.size());
|
|
for (BT<std::string>& v : tmp) {
|
|
result.emplace_back(std::move(v.Value));
|
|
}
|
|
}
|
|
|
|
std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
|
|
std::string const& config, std::string const& language) const
|
|
{
|
|
ConfigAndLanguage cacheKey(config, language);
|
|
{
|
|
auto it = this->CompileDefinitionsCache.find(cacheKey);
|
|
if (it != this->CompileDefinitionsCache.end()) {
|
|
return it->second;
|
|
}
|
|
}
|
|
std::vector<BT<std::string>> list;
|
|
std::unordered_set<std::string> uniqueOptions;
|
|
|
|
cm::GenEx::Context context(this->LocalGenerator, config, language);
|
|
|
|
cmGeneratorExpressionDAGChecker dagChecker{
|
|
this, "COMPILE_DEFINITIONS", nullptr, nullptr, context,
|
|
};
|
|
|
|
cmList debugProperties{ this->Makefile->GetDefinition(
|
|
"CMAKE_DEBUG_TARGET_PROPERTIES") };
|
|
bool debugDefines = !this->DebugCompileDefinitionsDone &&
|
|
cm::contains(debugProperties, "COMPILE_DEFINITIONS");
|
|
|
|
this->DebugCompileDefinitionsDone = true;
|
|
|
|
EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
|
|
this, context, &dagChecker, this->CompileDefinitionsEntries);
|
|
|
|
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);
|
|
|
|
this->CompileDefinitionsCache.emplace(cacheKey, list);
|
|
return list;
|
|
}
|
|
|
|
std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
|
|
std::string const& config, std::string const& language) const
|
|
{
|
|
ConfigAndLanguage cacheKey(config, language);
|
|
{
|
|
auto it = this->PrecompileHeadersCache.find(cacheKey);
|
|
if (it != this->PrecompileHeadersCache.end()) {
|
|
return it->second;
|
|
}
|
|
}
|
|
std::unordered_set<std::string> uniqueOptions;
|
|
|
|
cm::GenEx::Context context(this->LocalGenerator, config, language);
|
|
|
|
cmGeneratorExpressionDAGChecker dagChecker{
|
|
this, "PRECOMPILE_HEADERS", nullptr, nullptr, context,
|
|
};
|
|
|
|
cmList debugProperties{ this->Makefile->GetDefinition(
|
|
"CMAKE_DEBUG_TARGET_PROPERTIES") };
|
|
bool debugDefines = !this->DebugPrecompileHeadersDone &&
|
|
std::find(debugProperties.begin(), debugProperties.end(),
|
|
"PRECOMPILE_HEADERS") != debugProperties.end();
|
|
|
|
this->DebugPrecompileHeadersDone = true;
|
|
|
|
EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
|
|
this, context, &dagChecker, this->PrecompileHeadersEntries);
|
|
|
|
AddInterfaceEntries(this, "INTERFACE_PRECOMPILE_HEADERS", context,
|
|
&dagChecker, entries, IncludeRuntimeInterface::Yes);
|
|
|
|
std::vector<BT<std::string>> list;
|
|
processOptions(this, entries, list, uniqueOptions, debugDefines,
|
|
"precompile headers", OptionsParse::None);
|
|
|
|
this->PrecompileHeadersCache.emplace(cacheKey, list);
|
|
return list;
|
|
}
|
|
|
|
void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
|
|
std::string const& config,
|
|
std::string const& language) const
|
|
{
|
|
if (this->IsDeviceLink() &&
|
|
this->GetPolicyStatusCMP0105() != cmPolicies::NEW) {
|
|
// link options are not propagated to the device link step
|
|
return;
|
|
}
|
|
|
|
std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language);
|
|
result.reserve(tmp.size());
|
|
for (BT<std::string>& v : tmp) {
|
|
result.emplace_back(std::move(v.Value));
|
|
}
|
|
}
|
|
|
|
std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
|
|
std::string const& config, std::string const& language) const
|
|
{
|
|
ConfigAndLanguage cacheKey(
|
|
config, cmStrCat(language, this->IsDeviceLink() ? "-device" : ""));
|
|
{
|
|
auto it = this->LinkOptionsCache.find(cacheKey);
|
|
if (it != this->LinkOptionsCache.end()) {
|
|
return it->second;
|
|
}
|
|
}
|
|
std::vector<BT<std::string>> result;
|
|
std::unordered_set<std::string> uniqueOptions;
|
|
|
|
cm::GenEx::Context context(this->LocalGenerator, config, language);
|
|
|
|
cmGeneratorExpressionDAGChecker dagChecker{
|
|
this, "LINK_OPTIONS", nullptr, nullptr, context,
|
|
};
|
|
|
|
cmList debugProperties{ this->Makefile->GetDefinition(
|
|
"CMAKE_DEBUG_TARGET_PROPERTIES") };
|
|
bool debugOptions = !this->DebugLinkOptionsDone &&
|
|
cm::contains(debugProperties, "LINK_OPTIONS");
|
|
|
|
this->DebugLinkOptionsDone = true;
|
|
|
|
EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
|
|
this, context, &dagChecker, this->LinkOptionsEntries);
|
|
|
|
AddInterfaceEntries(this, "INTERFACE_LINK_OPTIONS", context, &dagChecker,
|
|
entries, IncludeRuntimeInterface::Yes,
|
|
this->GetPolicyStatusCMP0099() == cmPolicies::NEW
|
|
? UseTo::Link
|
|
: UseTo::Compile);
|
|
|
|
processOptions(this, entries, result, uniqueOptions, debugOptions,
|
|
"link options", OptionsParse::Shell, this->IsDeviceLink());
|
|
|
|
if (this->IsDeviceLink()) {
|
|
// wrap host link options
|
|
std::string const wrapper(this->Makefile->GetSafeDefinition(
|
|
"CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG"));
|
|
cmList wrapperFlag{ wrapper };
|
|
std::string const wrapperSep(this->Makefile->GetSafeDefinition(
|
|
"CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP"));
|
|
bool concatFlagAndArgs = true;
|
|
if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
|
|
concatFlagAndArgs = false;
|
|
wrapperFlag.pop_back();
|
|
}
|
|
|
|
auto it = result.begin();
|
|
while (it != result.end()) {
|
|
if (it->Value == DL_BEGIN) {
|
|
// device link options, no treatment
|
|
it = result.erase(it);
|
|
it = std::find_if(it, result.end(), [](const BT<std::string>& item) {
|
|
return item.Value == DL_END;
|
|
});
|
|
if (it != result.end()) {
|
|
it = result.erase(it);
|
|
}
|
|
} else {
|
|
// host link options must be wrapped
|
|
std::vector<std::string> options;
|
|
cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options);
|
|
auto hostOptions =
|
|
wrapOptions(options, it->Backtrace, wrapperFlag, wrapperSep,
|
|
concatFlagAndArgs, NestedLinkerFlags::Normalize);
|
|
it = result.erase(it);
|
|
// some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect
|
|
// C++11 standard: 'std::vector::insert()' do not returns an iterator,
|
|
// so need to recompute the iterator after insertion.
|
|
if (it == result.end()) {
|
|
cm::append(result, hostOptions);
|
|
it = result.end();
|
|
} else {
|
|
auto index = it - result.begin();
|
|
result.insert(it, hostOptions.begin(), hostOptions.end());
|
|
it = result.begin() + index + hostOptions.size();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Last step: replace "LINKER:" prefixed elements by
|
|
// actual linker wrapper
|
|
result = this->ResolveLinkerWrapper(result, language);
|
|
|
|
this->LinkOptionsCache.emplace(cacheKey, result);
|
|
return result;
|
|
}
|
|
|
|
std::vector<BT<std::string>>& cmGeneratorTarget::ResolvePrefixWrapper(
|
|
std::vector<BT<std::string>>& result, cm::string_view prefix,
|
|
std::string const& language, bool joinItems) const
|
|
{
|
|
// replace "LINKER:" or "ARCHIVER:" prefixed elements by actual linker or
|
|
// archiver wrapper
|
|
std::string const wrapper(this->Makefile->GetSafeDefinition(
|
|
cmStrCat("CMAKE_", language, (this->IsDeviceLink() ? "_DEVICE_" : "_"),
|
|
prefix, "_WRAPPER_FLAG")));
|
|
cmList wrapperFlag{ wrapper };
|
|
std::string const wrapperSep(this->Makefile->GetSafeDefinition(
|
|
cmStrCat("CMAKE_", language, (this->IsDeviceLink() ? "_DEVICE_" : "_"),
|
|
prefix, "_WRAPPER_FLAG_SEP")));
|
|
bool concatFlagAndArgs = true;
|
|
if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
|
|
concatFlagAndArgs = false;
|
|
wrapperFlag.pop_back();
|
|
}
|
|
|
|
std::string const PREFIX{ cmStrCat(prefix, ':') };
|
|
std::string const SHELL{ "SHELL:" };
|
|
std::string const PREFIX_SHELL = cmStrCat(PREFIX, SHELL);
|
|
|
|
for (auto entry = result.begin(); entry != result.end();) {
|
|
if (entry->Value.compare(0, PREFIX.length(), PREFIX) != 0) {
|
|
++entry;
|
|
continue;
|
|
}
|
|
|
|
std::string value = std::move(entry->Value);
|
|
cmListFileBacktrace bt = std::move(entry->Backtrace);
|
|
entry = result.erase(entry);
|
|
|
|
std::vector<std::string> options;
|
|
if (value.compare(0, PREFIX_SHELL.length(), PREFIX_SHELL) == 0) {
|
|
cmSystemTools::ParseUnixCommandLine(
|
|
value.c_str() + PREFIX_SHELL.length(), options);
|
|
} else {
|
|
options =
|
|
cmTokenize(value.substr(PREFIX.length()), ',', cmTokenizerMode::New);
|
|
}
|
|
|
|
if (options.empty()) {
|
|
continue;
|
|
}
|
|
|
|
// for now, raise an error if prefix SHELL: is part of arguments
|
|
if (std::find_if(options.begin(), options.end(),
|
|
[&SHELL](std::string const& item) -> bool {
|
|
return item.find(SHELL) != std::string::npos;
|
|
}) != options.end()) {
|
|
this->LocalGenerator->GetCMakeInstance()->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
cmStrCat("'SHELL:' prefix is not supported as part of '", prefix,
|
|
":' arguments."),
|
|
this->GetBacktrace());
|
|
return result;
|
|
}
|
|
|
|
// Very old versions of the C++ standard library return void for insert, so
|
|
// can't use it to get the new iterator
|
|
auto const index = entry - result.begin();
|
|
std::vector<BT<std::string>> processedOptions =
|
|
wrapOptions(options, bt, wrapperFlag, wrapperSep, concatFlagAndArgs,
|
|
NestedLinkerFlags::PreserveAsSpelled);
|
|
if (joinItems) {
|
|
result.insert(
|
|
entry,
|
|
cmJoin(cmMakeRange(processedOptions.begin(), processedOptions.end()),
|
|
" "_s));
|
|
entry = std::next(result.begin(), index + 1);
|
|
} else {
|
|
result.insert(entry, processedOptions.begin(), processedOptions.end());
|
|
entry = std::next(result.begin(), index + processedOptions.size());
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
|
|
std::vector<BT<std::string>>& result, std::string const& language,
|
|
bool joinItems) const
|
|
{
|
|
return this->ResolvePrefixWrapper(result, "LINKER"_s, language, joinItems);
|
|
}
|
|
|
|
void cmGeneratorTarget::GetStaticLibraryLinkOptions(
|
|
std::vector<std::string>& result, std::string const& config,
|
|
std::string const& language) const
|
|
{
|
|
std::vector<BT<std::string>> tmp =
|
|
this->GetStaticLibraryLinkOptions(config, language);
|
|
result.reserve(tmp.size());
|
|
for (BT<std::string>& v : tmp) {
|
|
result.emplace_back(std::move(v.Value));
|
|
}
|
|
}
|
|
|
|
std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions(
|
|
std::string const& config, std::string const& language) const
|
|
{
|
|
std::vector<BT<std::string>> result;
|
|
std::unordered_set<std::string> uniqueOptions;
|
|
|
|
cm::GenEx::Context context(this->LocalGenerator, config, language);
|
|
|
|
cmGeneratorExpressionDAGChecker dagChecker{
|
|
this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, context,
|
|
};
|
|
|
|
EvaluatedTargetPropertyEntries entries;
|
|
if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
|
|
std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create(
|
|
*this->LocalGenerator->GetCMakeInstance(), *linkOptions);
|
|
entries.Entries.emplace_back(
|
|
EvaluateTargetPropertyEntry(this, context, &dagChecker, *entry));
|
|
}
|
|
processOptions(this, entries, result, uniqueOptions, false,
|
|
"static library link options", OptionsParse::Shell);
|
|
|
|
// Last step: replace "ARCHIVER:" prefixed elements by
|
|
// actual archiver wrapper
|
|
this->ResolveArchiverWrapper(result, language);
|
|
|
|
return result;
|
|
}
|
|
|
|
std::vector<BT<std::string>>& cmGeneratorTarget::ResolveArchiverWrapper(
|
|
std::vector<BT<std::string>>& result, std::string const& language,
|
|
bool joinItems) const
|
|
{
|
|
return this->ResolvePrefixWrapper(result, "ARCHIVER"_s, language, joinItems);
|
|
}
|
|
|
|
void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result,
|
|
std::string const& config,
|
|
std::string const& language) const
|
|
{
|
|
std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language);
|
|
result.reserve(tmp.size());
|
|
for (BT<std::string>& v : tmp) {
|
|
result.emplace_back(std::move(v.Value));
|
|
}
|
|
}
|
|
|
|
std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
|
|
std::string const& config, std::string const& language) const
|
|
{
|
|
std::vector<BT<std::string>> result;
|
|
std::unordered_set<std::string> uniqueOptions;
|
|
cm::GenEx::Context context(this->LocalGenerator, config, language);
|
|
cmGeneratorExpressionDAGChecker dagChecker{
|
|
this, "LINK_DEPENDS", nullptr, nullptr, context,
|
|
};
|
|
|
|
EvaluatedTargetPropertyEntries entries;
|
|
if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) {
|
|
cmList depends{ *linkDepends };
|
|
for (auto const& depend : depends) {
|
|
std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create(
|
|
*this->LocalGenerator->GetCMakeInstance(), depend);
|
|
entries.Entries.emplace_back(
|
|
EvaluateTargetPropertyEntry(this, context, &dagChecker, *entry));
|
|
}
|
|
}
|
|
AddInterfaceEntries(this, "INTERFACE_LINK_DEPENDS", context, &dagChecker,
|
|
entries, IncludeRuntimeInterface::Yes,
|
|
this->GetPolicyStatusCMP0099() == cmPolicies::NEW
|
|
? UseTo::Link
|
|
: UseTo::Compile);
|
|
|
|
processOptions(this, entries, result, uniqueOptions, false, "link depends",
|
|
OptionsParse::None);
|
|
|
|
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;
|
|
}
|