/* 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 #include #include #include #include #include #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 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 void AppendUsageEntries(std::string& usageHashInput, Range const& entries) { std::vector 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(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> HashEntries; std::vector> PreprocessorEntries; }; template SplitCompileOptionsResult SplitCompileOptions(Range const& entries, CompileOptionClassifier classify) { SplitCompileOptionsResult result; for (auto it = entries.begin(); it != entries.end(); ++it) { BT 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> const& cmCxxModuleUsageEffects::GetPreprocessorCompileOptions() const { return this->PreprocessorCompileOptions; }