mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-09 17:18:18 +00:00
337 lines
8.3 KiB
C++
337 lines
8.3 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#include "cmCxxModuleUsageEffects.h"
|
|
|
|
#include <algorithm>
|
|
#include <set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cm/string_view>
|
|
#include <cmext/string_view>
|
|
|
|
#include "cmCryptoHash.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmTarget.h"
|
|
#include "cmValue.h"
|
|
|
|
namespace {
|
|
|
|
bool IsMSVCWarning(cm::string_view flag)
|
|
{
|
|
if (flag.size() < 2) {
|
|
return false;
|
|
}
|
|
|
|
if (flag.front() != '/' && flag.front() != '-') {
|
|
return false;
|
|
}
|
|
|
|
flag.remove_prefix(1);
|
|
|
|
if (flag.front() == 'w' || flag.front() == 'W') {
|
|
return true;
|
|
}
|
|
|
|
if (flag.substr(0, 9) == "external:") {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IsGNUWarning(cm::string_view flag)
|
|
{
|
|
if (flag.size() < 2) {
|
|
return false;
|
|
}
|
|
|
|
if (flag.front() != '-') {
|
|
return false;
|
|
}
|
|
|
|
flag.remove_prefix(1);
|
|
|
|
if (flag.front() == 'w' || flag.front() == 'W') {
|
|
return true;
|
|
}
|
|
|
|
if (flag.front() == '-') {
|
|
flag.remove_prefix(1);
|
|
}
|
|
|
|
static std::set<cm::string_view> const long_names{
|
|
"pedantic", "pedantic-errors", "all-warnings", "extra-warnings",
|
|
"no-warnings"
|
|
};
|
|
|
|
return long_names.find(flag) != long_names.end();
|
|
}
|
|
|
|
void AppendUsage(std::string& usageHashInput, std::string const& entry)
|
|
{
|
|
usageHashInput += entry;
|
|
usageHashInput.push_back('\0');
|
|
}
|
|
|
|
template <typename Range>
|
|
void AppendUsageEntries(std::string& usageHashInput, Range const& entries)
|
|
{
|
|
std::vector<std::string> entryValues;
|
|
entryValues.reserve(entries.size());
|
|
for (auto const& entry : entries) {
|
|
entryValues.push_back(entry.Value);
|
|
}
|
|
std::sort(entryValues.begin(), entryValues.end());
|
|
|
|
for (auto const& entryValue : entryValues) {
|
|
usageHashInput += entryValue;
|
|
usageHashInput.push_back('\n');
|
|
}
|
|
|
|
usageHashInput.push_back('\0');
|
|
}
|
|
|
|
enum class CompileOptionUsage
|
|
{
|
|
Hash,
|
|
Ignore,
|
|
SavePreprocessor,
|
|
};
|
|
|
|
struct CompileOptionClassification
|
|
{
|
|
CompileOptionClassification(
|
|
CompileOptionUsage usage = CompileOptionUsage::Hash,
|
|
bool saveFollowingArgument = false)
|
|
: Usage(usage)
|
|
, SaveFollowingArgument(saveFollowingArgument)
|
|
{
|
|
}
|
|
|
|
CompileOptionUsage Usage = CompileOptionUsage::Hash;
|
|
bool SaveFollowingArgument = false;
|
|
};
|
|
|
|
CompileOptionClassification SaveIfStartsWith(cm::string_view flag,
|
|
cm::string_view prefix)
|
|
{
|
|
if (flag.rfind(prefix, 0) == 0) {
|
|
return { CompileOptionUsage::SavePreprocessor,
|
|
flag.size() == prefix.size() };
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
CompileOptionClassification ClassifyGNUCompileOption(cm::string_view flag)
|
|
{
|
|
if (IsGNUWarning(flag)) {
|
|
return { CompileOptionUsage::Ignore, false };
|
|
}
|
|
|
|
if (flag.size() < 2 || flag.front() != '-') {
|
|
return {};
|
|
}
|
|
|
|
flag.remove_prefix(1);
|
|
switch (flag.front()) {
|
|
case 'D':
|
|
case 'I':
|
|
case 'U':
|
|
return { CompileOptionUsage::SavePreprocessor, flag.size() == 1 };
|
|
case 'i': {
|
|
CompileOptionClassification classification =
|
|
SaveIfStartsWith(flag, "isystem"_s);
|
|
if (classification.Usage != CompileOptionUsage::Hash) {
|
|
return classification;
|
|
}
|
|
}
|
|
{
|
|
CompileOptionClassification classification =
|
|
SaveIfStartsWith(flag, "iquote"_s);
|
|
if (classification.Usage != CompileOptionUsage::Hash) {
|
|
return classification;
|
|
}
|
|
}
|
|
{
|
|
CompileOptionClassification classification =
|
|
SaveIfStartsWith(flag, "idirafter"_s);
|
|
if (classification.Usage != CompileOptionUsage::Hash) {
|
|
return classification;
|
|
}
|
|
}
|
|
{
|
|
CompileOptionClassification classification =
|
|
SaveIfStartsWith(flag, "include"_s);
|
|
if (classification.Usage != CompileOptionUsage::Hash) {
|
|
return classification;
|
|
}
|
|
}
|
|
{
|
|
CompileOptionClassification classification =
|
|
SaveIfStartsWith(flag, "imacros"_s);
|
|
if (classification.Usage != CompileOptionUsage::Hash) {
|
|
return classification;
|
|
}
|
|
}
|
|
break;
|
|
case '-':
|
|
flag.remove_prefix(1);
|
|
return SaveIfStartsWith(flag, "embed-dir"_s);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
CompileOptionClassification ClassifyMSVCCompileOption(cm::string_view flag)
|
|
{
|
|
if (IsMSVCWarning(flag)) {
|
|
return { CompileOptionUsage::Ignore, false };
|
|
}
|
|
|
|
if (flag.size() < 2 || (flag.front() != '/' && flag.front() != '-')) {
|
|
return {};
|
|
}
|
|
|
|
flag.remove_prefix(1);
|
|
switch (flag.front()) {
|
|
case 'D':
|
|
case 'I':
|
|
case 'U':
|
|
return { CompileOptionUsage::SavePreprocessor, flag.size() == 1 };
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
CompileOptionClassification ClassifyUnknownCompileOption(cm::string_view flag)
|
|
{
|
|
static_cast<void>(flag);
|
|
return {};
|
|
}
|
|
|
|
using CompileOptionClassifier =
|
|
CompileOptionClassification (*)(cm::string_view flag);
|
|
|
|
CompileOptionClassifier GetCompileOptionClassifier(cmGeneratorTarget const* gt)
|
|
{
|
|
static CompileOptionClassifier classifier = nullptr;
|
|
if (classifier) {
|
|
return classifier;
|
|
}
|
|
|
|
cmValue frontendVariant =
|
|
gt->Makefile->GetDefinition("CMAKE_CXX_COMPILER_FRONTEND_VARIANT");
|
|
|
|
if (frontendVariant == "GNU") {
|
|
classifier = ClassifyGNUCompileOption;
|
|
} else if (frontendVariant == "MSVC") {
|
|
classifier = ClassifyMSVCCompileOption;
|
|
} else {
|
|
classifier = ClassifyUnknownCompileOption;
|
|
}
|
|
|
|
return classifier;
|
|
}
|
|
|
|
struct SplitCompileOptionsResult
|
|
{
|
|
std::vector<BT<std::string>> HashEntries;
|
|
std::vector<BT<std::string>> PreprocessorEntries;
|
|
};
|
|
|
|
template <typename Range>
|
|
SplitCompileOptionsResult SplitCompileOptions(Range const& entries,
|
|
CompileOptionClassifier classify)
|
|
{
|
|
SplitCompileOptionsResult result;
|
|
|
|
for (auto it = entries.begin(); it != entries.end(); ++it) {
|
|
BT<std::string> const& option = *it;
|
|
auto const classification = classify(option.Value);
|
|
|
|
switch (classification.Usage) {
|
|
case CompileOptionUsage::Ignore:
|
|
continue;
|
|
case CompileOptionUsage::SavePreprocessor:
|
|
result.PreprocessorEntries.push_back(option);
|
|
if (classification.SaveFollowingArgument) {
|
|
auto next = it;
|
|
++next;
|
|
if (next != entries.end()) {
|
|
result.PreprocessorEntries.push_back(*next);
|
|
it = next;
|
|
}
|
|
}
|
|
continue;
|
|
case CompileOptionUsage::Hash:
|
|
break;
|
|
}
|
|
|
|
result.HashEntries.push_back(option);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
cmCxxModuleUsageEffects::cmCxxModuleUsageEffects(cmGeneratorTarget const* gt,
|
|
std::string const& config)
|
|
{
|
|
auto const* tgt = gt->Target;
|
|
auto const classify = GetCompileOptionClassifier(gt);
|
|
|
|
std::string usageHashInput;
|
|
if (tgt->IsImported()) {
|
|
AppendUsageEntries(usageHashInput,
|
|
tgt->GetImportedCxxModulesCompileFeaturesEntries());
|
|
auto compileOptions = SplitCompileOptions(
|
|
tgt->GetImportedCxxModulesCompileOptionsEntries(), classify);
|
|
this->PreprocessorCompileOptions =
|
|
std::move(compileOptions.PreprocessorEntries);
|
|
AppendUsageEntries(usageHashInput, compileOptions.HashEntries);
|
|
} else {
|
|
auto compileOptions =
|
|
SplitCompileOptions(gt->GetCompileOptions(config, "CXX"), classify);
|
|
this->PreprocessorCompileOptions =
|
|
std::move(compileOptions.PreprocessorEntries);
|
|
AppendUsageEntries(usageHashInput, compileOptions.HashEntries);
|
|
|
|
cmValue langStd = gt->GetLanguageStandard("CXX", config);
|
|
if (!langStd) {
|
|
langStd = gt->Makefile->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
|
|
}
|
|
AppendUsage(usageHashInput,
|
|
cmStrCat("CXX_STANDARD:", langStd ? *langStd : "20"));
|
|
AppendUsage(
|
|
usageHashInput,
|
|
cmStrCat("CXX_EXTENSIONS:", *gt->GetLanguageExtensions("CXX")));
|
|
AppendUsage(
|
|
usageHashInput,
|
|
cmStrCat("CXX_STANDARD_REQUIRED:",
|
|
gt->GetLanguageStandardRequired("CXX") ? "TRUE" : "FALSE"));
|
|
}
|
|
|
|
cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
|
|
this->Hash = hasher.HashString(usageHashInput);
|
|
}
|
|
|
|
std::string const& cmCxxModuleUsageEffects::GetHash() const
|
|
{
|
|
return this->Hash;
|
|
}
|
|
|
|
std::vector<BT<std::string>> const&
|
|
cmCxxModuleUsageEffects::GetPreprocessorCompileOptions() const
|
|
{
|
|
return this->PreprocessorCompileOptions;
|
|
}
|