diff options
Diffstat (limited to 'Source/cmMakefile.cxx')
-rw-r--r-- | Source/cmMakefile.cxx | 4644 |
1 files changed, 4644 insertions, 0 deletions
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx new file mode 100644 index 0000000..0ad0e6e --- /dev/null +++ b/Source/cmMakefile.cxx @@ -0,0 +1,4644 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmMakefile.h" + +#include <algorithm> +#include <cassert> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <sstream> +#include <utility> + +#include <cm/iterator> +#include <cm/memory> +#include <cm/optional> +#include <cm/type_traits> // IWYU pragma: keep +#include <cm/vector> +#include <cmext/algorithm> +#include <cmext/string_view> + +#ifndef CMAKE_BOOTSTRAP +# include <cm3p/json/value.h> +# include <cm3p/json/writer.h> +#endif + +#include "cmsys/FStream.hxx" +#include "cmsys/RegularExpression.hxx" + +#include "cmCommandArgumentParserHelper.h" +#include "cmCustomCommand.h" +#include "cmCustomCommandLines.h" +#include "cmExecutionStatus.h" +#include "cmExpandedCommandArgument.h" // IWYU pragma: keep +#include "cmExportBuildFileGenerator.h" +#include "cmFileLockPool.h" +#include "cmFunctionBlocker.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionEvaluationFile.h" +#include "cmGlobalGenerator.h" +#include "cmInstallGenerator.h" // IWYU pragma: keep +#include "cmInstallSubdirectoryGenerator.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMessageType.h" +#include "cmRange.h" +#include "cmSourceFile.h" +#include "cmSourceFileLocation.h" +#include "cmState.h" +#include "cmStateDirectory.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetLinkLibraryType.h" +#include "cmTest.h" +#include "cmTestGenerator.h" // IWYU pragma: keep +#include "cmVersion.h" +#include "cmWorkingDirectory.h" +#include "cmake.h" + +#ifndef CMAKE_BOOTSTRAP +# include "cmMakefileProfilingData.h" +# include "cmVariableWatch.h" +#endif + +class cmMessenger; + +cmDirectoryId::cmDirectoryId(std::string s) + : String(std::move(s)) +{ +} + +// default is not to be building executables +cmMakefile::cmMakefile(cmGlobalGenerator* globalGenerator, + cmStateSnapshot const& snapshot) + : GlobalGenerator(globalGenerator) + , StateSnapshot(snapshot) +{ + this->IsSourceFileTryCompile = false; + + this->CheckSystemVars = this->GetCMakeInstance()->GetCheckSystemVars(); + + this->SuppressSideEffects = false; + + // Setup the default include complaint regular expression (match nothing). + this->ComplainFileRegularExpression = "^$"; + + this->DefineFlags = " "; + + this->cmDefineRegex.compile("#([ \t]*)cmakedefine[ \t]+([A-Za-z_0-9]*)"); + this->cmDefine01Regex.compile("#([ \t]*)cmakedefine01[ \t]+([A-Za-z_0-9]*)"); + this->cmAtVarRegex.compile("(@[A-Za-z_0-9/.+-]+@)"); + this->cmNamedCurly.compile("^[A-Za-z0-9/_.+-]+{"); + + this->StateSnapshot = + this->StateSnapshot.GetState()->CreatePolicyScopeSnapshot( + this->StateSnapshot); + this->RecursionDepth = 0; + + // Enter a policy level for this directory. + this->PushPolicy(); + + // push empty loop block + this->PushLoopBlockBarrier(); + + // By default the check is not done. It is enabled by + // cmListFileCache in the top level if necessary. + this->CheckCMP0000 = false; + +#if !defined(CMAKE_BOOTSTRAP) + this->AddSourceGroup("", "^.*$"); + this->AddSourceGroup("Source Files", CM_SOURCE_REGEX); + this->AddSourceGroup("Header Files", CM_HEADER_REGEX); + this->AddSourceGroup("Precompile Header File", CM_PCH_REGEX); + this->AddSourceGroup("CMake Rules", "\\.rule$"); + this->AddSourceGroup("Resources", CM_RESOURCE_REGEX); + this->AddSourceGroup("Object Files", "\\.(lo|o|obj)$"); + + this->ObjectLibrariesSourceGroupIndex = this->SourceGroups.size(); + this->SourceGroups.emplace_back("Object Libraries", "^MATCH_NO_SOURCES$"); +#endif +} + +cmMakefile::~cmMakefile() = default; + +cmDirectoryId cmMakefile::GetDirectoryId() const +{ + // Use the instance pointer value to uniquely identify this directory. + // If we ever need to expose this to CMake language code we should + // add a read-only property in cmMakefile::GetProperty. + char buf[32]; + snprintf(buf, sizeof(buf), "(%p)", + static_cast<void const*>(this)); // cast avoids format warning + return std::string(buf); +} + +void cmMakefile::IssueMessage(MessageType t, std::string const& text) const +{ + if (!this->ExecutionStatusStack.empty()) { + if ((t == MessageType::FATAL_ERROR) || + (t == MessageType::INTERNAL_ERROR)) { + this->ExecutionStatusStack.back()->SetNestedError(); + } + } + this->GetCMakeInstance()->IssueMessage(t, text, this->Backtrace); +} + +Message::LogLevel cmMakefile::GetCurrentLogLevel() const +{ + const cmake* cmakeInstance = this->GetCMakeInstance(); + + const Message::LogLevel logLevelCliOrDefault = cmakeInstance->GetLogLevel(); + assert("Expected a valid log level here" && + logLevelCliOrDefault != Message::LogLevel::LOG_UNDEFINED); + + Message::LogLevel result = logLevelCliOrDefault; + + // If the log-level was set via the command line option, it takes precedence + // over the CMAKE_MESSAGE_LOG_LEVEL variable. + if (!cmakeInstance->WasLogLevelSetViaCLI()) { + const Message::LogLevel logLevelFromVar = cmake::StringToLogLevel( + this->GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL")); + if (logLevelFromVar != Message::LogLevel::LOG_UNDEFINED) { + result = logLevelFromVar; + } + } + + return result; +} + +bool cmMakefile::CheckCMP0037(std::string const& targetName, + cmStateEnums::TargetType targetType) const +{ + MessageType messageType = MessageType::AUTHOR_WARNING; + std::ostringstream e; + bool issueMessage = false; + switch (this->GetPolicyStatus(cmPolicies::CMP0037)) { + case cmPolicies::WARN: + if (targetType != cmStateEnums::INTERFACE_LIBRARY) { + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0037) << "\n"; + issueMessage = true; + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + issueMessage = true; + messageType = MessageType::FATAL_ERROR; + break; + } + if (issueMessage) { + e << "The target name \"" << targetName + << "\" is reserved or not valid for certain " + "CMake features, such as generator expressions, and may result " + "in undefined behavior."; + this->IssueMessage(messageType, e.str()); + + if (messageType == MessageType::FATAL_ERROR) { + return false; + } + } + return true; +} + +void cmMakefile::MaybeWarnCMP0074(std::string const& pkg) +{ + // Warn if a <pkg>_ROOT variable we may use is set. + std::string const varName = pkg + "_ROOT"; + cmValue var = this->GetDefinition(varName); + std::string env; + cmSystemTools::GetEnv(varName, env); + + bool const haveVar = cmNonempty(var); + bool const haveEnv = !env.empty(); + if ((haveVar || haveEnv) && this->WarnedCMP0074.insert(varName).second) { + std::ostringstream w; + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0074) << "\n"; + if (haveVar) { + w << "CMake variable " << varName << " is set to:\n" + << " " << *var << "\n"; + } + if (haveEnv) { + w << "Environment variable " << varName << " is set to:\n" + << " " << env << "\n"; + } + w << "For compatibility, CMake is ignoring the variable."; + this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + } +} + +cmBTStringRange cmMakefile::GetIncludeDirectoriesEntries() const +{ + return this->StateSnapshot.GetDirectory().GetIncludeDirectoriesEntries(); +} + +cmBTStringRange cmMakefile::GetCompileOptionsEntries() const +{ + return this->StateSnapshot.GetDirectory().GetCompileOptionsEntries(); +} + +cmBTStringRange cmMakefile::GetCompileDefinitionsEntries() const +{ + return this->StateSnapshot.GetDirectory().GetCompileDefinitionsEntries(); +} + +cmBTStringRange cmMakefile::GetLinkOptionsEntries() const +{ + return this->StateSnapshot.GetDirectory().GetLinkOptionsEntries(); +} + +cmBTStringRange cmMakefile::GetLinkDirectoriesEntries() const +{ + return this->StateSnapshot.GetDirectory().GetLinkDirectoriesEntries(); +} + +cmListFileBacktrace cmMakefile::GetBacktrace() const +{ + return this->Backtrace; +} + +void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff, + cmListFileBacktrace const& bt, + CommandMissingFromStack missing) const +{ + // Check if current file in the list of requested to trace... + std::vector<std::string> const& trace_only_this_files = + this->GetCMakeInstance()->GetTraceSources(); + std::string const& full_path = bt.Top().FilePath; + std::string const& only_filename = cmSystemTools::GetFilenameName(full_path); + bool trace = trace_only_this_files.empty(); + if (!trace) { + for (std::string const& file : trace_only_this_files) { + std::string::size_type const pos = full_path.rfind(file); + trace = (pos != std::string::npos) && + ((pos + file.size()) == full_path.size()) && + (only_filename == cmSystemTools::GetFilenameName(file)); + if (trace) { + break; + } + } + // Do nothing if current file wasn't requested for trace... + if (!trace) { + return; + } + } + + std::ostringstream msg; + std::vector<std::string> args; + std::string temp; + bool expand = this->GetCMakeInstance()->GetTraceExpand(); + + args.reserve(lff.Arguments().size()); + for (cmListFileArgument const& arg : lff.Arguments()) { + if (expand && arg.Delim != cmListFileArgument::Bracket) { + temp = arg.Value; + this->ExpandVariablesInString(temp); + args.push_back(temp); + } else { + args.push_back(arg.Value); + } + } + cm::optional<std::string> const& deferId = bt.Top().DeferId; + + switch (this->GetCMakeInstance()->GetTraceFormat()) { + case cmake::TraceFormat::TRACE_JSON_V1: { +#ifndef CMAKE_BOOTSTRAP + Json::Value val; + Json::StreamWriterBuilder builder; + builder["indentation"] = ""; + val["file"] = full_path; + val["line"] = static_cast<Json::Value::Int64>(lff.Line()); + if (lff.Line() != lff.LineEnd()) { + val["line_end"] = static_cast<Json::Value::Int64>(lff.LineEnd()); + } + if (deferId) { + val["defer"] = *deferId; + } + val["cmd"] = lff.OriginalName(); + val["args"] = Json::Value(Json::arrayValue); + for (std::string const& arg : args) { + val["args"].append(arg); + } + val["time"] = cmSystemTools::GetTime(); + val["frame"] = (missing == CommandMissingFromStack::Yes ? 1 : 0) + + static_cast<Json::Value::UInt64>(this->ExecutionStatusStack.size()); + val["global_frame"] = (missing == CommandMissingFromStack::Yes ? 1 : 0) + + static_cast<Json::Value::UInt64>(this->RecursionDepth); + msg << Json::writeString(builder, val); +#endif + break; + } + case cmake::TraceFormat::TRACE_HUMAN: + msg << full_path << "(" << lff.Line() << "):"; + if (deferId) { + msg << "DEFERRED:" << *deferId << ":"; + } + msg << " " << lff.OriginalName() << "("; + + for (std::string const& arg : args) { + msg << arg << " "; + } + msg << ")"; + break; + case cmake::TraceFormat::TRACE_UNDEFINED: + msg << "INTERNAL ERROR: Trace format is TRACE_UNDEFINED"; + break; + } + + auto& f = this->GetCMakeInstance()->GetTraceFile(); + if (f) { + f << msg.str() << '\n'; + } else { + cmSystemTools::Message(msg.str()); + } +} + +// Helper class to make sure the call stack is valid. +class cmMakefileCall +{ +public: + cmMakefileCall(cmMakefile* mf, cmListFileFunction const& lff, + cm::optional<std::string> deferId, cmExecutionStatus& status) + : Makefile(mf) + { + cmListFileContext const& lfc = cmListFileContext::FromListFileFunction( + lff, this->Makefile->StateSnapshot.GetExecutionListFile(), + std::move(deferId)); + this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); + ++this->Makefile->RecursionDepth; + this->Makefile->ExecutionStatusStack.push_back(&status); +#if !defined(CMAKE_BOOTSTRAP) + this->ProfilingDataRAII = + this->Makefile->GetCMakeInstance()->CreateProfilingEntry( + "script", lff.LowerCaseName(), [&lff, &lfc]() -> Json::Value { + Json::Value argsValue = Json::objectValue; + if (!lff.Arguments().empty()) { + std::string args; + for (auto const& a : lff.Arguments()) { + args = cmStrCat(args, args.empty() ? "" : " ", a.Value); + } + argsValue["functionArgs"] = args; + } + argsValue["location"] = + cmStrCat(lfc.FilePath, ':', std::to_string(lfc.Line)); + return argsValue; + }); +#endif + } + + ~cmMakefileCall() + { +#if !defined(CMAKE_BOOTSTRAP) + this->ProfilingDataRAII.reset(); +#endif + this->Makefile->ExecutionStatusStack.pop_back(); + --this->Makefile->RecursionDepth; + this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); + } + + cmMakefileCall(const cmMakefileCall&) = delete; + cmMakefileCall& operator=(const cmMakefileCall&) = delete; + +private: + cmMakefile* Makefile; +#if !defined(CMAKE_BOOTSTRAP) + cm::optional<cmMakefileProfilingData::RAII> ProfilingDataRAII; +#endif +}; + +void cmMakefile::OnExecuteCommand(std::function<void()> callback) +{ + this->ExecuteCommandCallback = std::move(callback); +} + +bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, + cmExecutionStatus& status, + cm::optional<std::string> deferId) +{ + bool result = true; + + // quick return if blocked + if (this->IsFunctionBlocked(lff, status)) { + // No error. + return result; + } + + if (this->ExecuteCommandCallback) { + this->ExecuteCommandCallback(); + } + + // Place this call on the call stack. + cmMakefileCall stack_manager(this, lff, std::move(deferId), status); + static_cast<void>(stack_manager); + + // Check for maximum recursion depth. + int depth = CMake_DEFAULT_RECURSION_LIMIT; + cmValue depthStr = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH"); + if (depthStr) { + std::istringstream s(*depthStr); + int d; + if (s >> d) { + depth = d; + } + } + if (this->RecursionDepth > depth) { + std::ostringstream e; + e << "Maximum recursion depth of " << depth << " exceeded"; + this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + // Lookup the command prototype. + if (cmState::Command command = + this->GetState()->GetCommandByExactName(lff.LowerCaseName())) { + // Decide whether to invoke the command. + if (!cmSystemTools::GetFatalErrorOccurred()) { + // if trace is enabled, print out invoke information + if (this->GetCMakeInstance()->GetTrace()) { + this->PrintCommandTrace(lff, this->Backtrace); + } + // Try invoking the command. + bool invokeSucceeded = command(lff.Arguments(), status); + bool hadNestedError = status.GetNestedError(); + if (!invokeSucceeded || hadNestedError) { + if (!hadNestedError) { + // The command invocation requested that we report an error. + std::string const error = + std::string(lff.OriginalName()) + " " + status.GetError(); + this->IssueMessage(MessageType::FATAL_ERROR, error); + } + result = false; + if (this->GetCMakeInstance()->GetWorkingMode() != cmake::NORMAL_MODE) { + cmSystemTools::SetFatalErrorOccurred(); + } + } + } + } else { + if (!cmSystemTools::GetFatalErrorOccurred()) { + std::string error = + cmStrCat("Unknown CMake command \"", lff.OriginalName(), "\"."); + this->IssueMessage(MessageType::FATAL_ERROR, error); + result = false; + cmSystemTools::SetFatalErrorOccurred(); + } + } + + return result; +} + +bool cmMakefile::IsImportedTargetGlobalScope() const +{ + return this->CurrentImportedTargetScope == ImportedTargetScope::Global; +} + +class cmMakefile::IncludeScope +{ +public: + IncludeScope(cmMakefile* mf, std::string const& filenametoread, + bool noPolicyScope); + ~IncludeScope(); + void Quiet() { this->ReportError = false; } + + IncludeScope(const IncludeScope&) = delete; + IncludeScope& operator=(const IncludeScope&) = delete; + +private: + cmMakefile* Makefile; + bool NoPolicyScope; + bool CheckCMP0011 = false; + bool ReportError = true; + void EnforceCMP0011(); +}; + +cmMakefile::IncludeScope::IncludeScope(cmMakefile* mf, + std::string const& filenametoread, + bool noPolicyScope) + : Makefile(mf) + , NoPolicyScope(noPolicyScope) +{ + this->Makefile->Backtrace = this->Makefile->Backtrace.Push( + cmListFileContext::FromListFilePath(filenametoread)); + + this->Makefile->PushFunctionBlockerBarrier(); + + this->Makefile->StateSnapshot = + this->Makefile->GetState()->CreateIncludeFileSnapshot( + this->Makefile->StateSnapshot, filenametoread); + if (!this->NoPolicyScope) { + // Check CMP0011 to determine the policy scope type. + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0011)) { + case cmPolicies::WARN: + // We need to push a scope to detect whether the script sets + // any policies that would affect the includer and therefore + // requires a warning. We use a weak scope to simulate OLD + // behavior by allowing policy changes to affect the includer. + this->Makefile->PushPolicy(true); + this->CheckCMP0011 = true; + break; + case cmPolicies::OLD: + // OLD behavior is to not push a scope at all. + this->NoPolicyScope = true; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + // We should never make this policy required, but we handle it + // here just in case. + this->CheckCMP0011 = true; + CM_FALLTHROUGH; + case cmPolicies::NEW: + // NEW behavior is to push a (strong) scope. + this->Makefile->PushPolicy(); + break; + } + } +} + +cmMakefile::IncludeScope::~IncludeScope() +{ + if (!this->NoPolicyScope) { + // If we need to enforce policy CMP0011 then the top entry is the + // one we pushed above. If the entry is empty, then the included + // script did not set any policies that might affect the includer so + // we do not need to enforce the policy. + if (this->CheckCMP0011 && + !this->Makefile->StateSnapshot.HasDefinedPolicyCMP0011()) { + this->CheckCMP0011 = false; + } + + // Pop the scope we pushed for the script. + this->Makefile->PopPolicy(); + + // We enforce the policy after the script's policy stack entry has + // been removed. + if (this->CheckCMP0011) { + this->EnforceCMP0011(); + } + } + this->Makefile->PopSnapshot(this->ReportError); + + this->Makefile->PopFunctionBlockerBarrier(this->ReportError); + + this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); +} + +void cmMakefile::IncludeScope::EnforceCMP0011() +{ + // We check the setting of this policy again because the included + // script might actually set this policy for its includer. + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0011)) { + case cmPolicies::WARN: + // Warn because the user did not set this policy. + { + std::ostringstream w; + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0011) << "\n" + << "The included script\n " + << this->Makefile->GetBacktrace().Top().FilePath << "\n" + << "affects policy settings. " + << "CMake is implying the NO_POLICY_SCOPE option for compatibility, " + << "so the effects are applied to the including context."; + this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + } + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: { + std::ostringstream e; + /* clang-format off */ + e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0011) << "\n" + << "The included script\n " + << this->Makefile->GetBacktrace().Top().FilePath << "\n" + << "affects policy settings, so it requires this policy to be set."; + /* clang-format on */ + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } break; + case cmPolicies::OLD: + case cmPolicies::NEW: + // The script set this policy. We assume the purpose of the + // script is to initialize policies for its includer, and since + // the policy is now set for later scripts, we do not warn. + break; + } +} + +bool cmMakefile::ReadDependentFile(const std::string& filename, + bool noPolicyScope) +{ + if (cmValue def = this->GetDefinition("CMAKE_CURRENT_LIST_FILE")) { + this->AddDefinition("CMAKE_PARENT_LIST_FILE", *def); + } + std::string filenametoread = cmSystemTools::CollapseFullPath( + filename, this->GetCurrentSourceDirectory()); + + IncludeScope incScope(this, filenametoread, noPolicyScope); + + cmListFile listFile; + if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), + this->Backtrace)) { + return false; + } + + this->RunListFile(listFile, filenametoread); + if (cmSystemTools::GetFatalErrorOccurred()) { + incScope.Quiet(); + } + return true; +} + +class cmMakefile::ListFileScope +{ +public: + ListFileScope(cmMakefile* mf, std::string const& filenametoread) + : Makefile(mf) + { + this->Makefile->Backtrace = this->Makefile->Backtrace.Push( + cmListFileContext::FromListFilePath(filenametoread)); + + this->Makefile->StateSnapshot = + this->Makefile->GetState()->CreateInlineListFileSnapshot( + this->Makefile->StateSnapshot, filenametoread); + assert(this->Makefile->StateSnapshot.IsValid()); + + this->Makefile->PushFunctionBlockerBarrier(); + } + + ~ListFileScope() + { + this->Makefile->PopSnapshot(this->ReportError); + this->Makefile->PopFunctionBlockerBarrier(this->ReportError); + this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); + } + + void Quiet() { this->ReportError = false; } + + ListFileScope(const ListFileScope&) = delete; + ListFileScope& operator=(const ListFileScope&) = delete; + +private: + cmMakefile* Makefile; + bool ReportError = true; +}; + +class cmMakefile::DeferScope +{ +public: + DeferScope(cmMakefile* mf, std::string const& deferredInFile) + : Makefile(mf) + { + cmListFileContext lfc; + lfc.Line = cmListFileContext::DeferPlaceholderLine; + lfc.FilePath = deferredInFile; + this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); + this->Makefile->DeferRunning = true; + } + + ~DeferScope() + { + this->Makefile->DeferRunning = false; + this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); + } + + DeferScope(const DeferScope&) = delete; + DeferScope& operator=(const DeferScope&) = delete; + +private: + cmMakefile* Makefile; +}; + +class cmMakefile::DeferCallScope +{ +public: + DeferCallScope(cmMakefile* mf, std::string const& deferredFromFile) + : Makefile(mf) + { + this->Makefile->StateSnapshot = + this->Makefile->GetState()->CreateDeferCallSnapshot( + this->Makefile->StateSnapshot, deferredFromFile); + assert(this->Makefile->StateSnapshot.IsValid()); + } + + ~DeferCallScope() { this->Makefile->PopSnapshot(); } + + DeferCallScope(const DeferCallScope&) = delete; + DeferCallScope& operator=(const DeferCallScope&) = delete; + +private: + cmMakefile* Makefile; +}; + +bool cmMakefile::ReadListFile(const std::string& filename) +{ + std::string filenametoread = cmSystemTools::CollapseFullPath( + filename, this->GetCurrentSourceDirectory()); + + ListFileScope scope(this, filenametoread); + + cmListFile listFile; + if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), + this->Backtrace)) { + return false; + } + + this->RunListFile(listFile, filenametoread); + if (cmSystemTools::GetFatalErrorOccurred()) { + scope.Quiet(); + } + return true; +} + +bool cmMakefile::ReadListFileAsString(const std::string& content, + const std::string& virtualFileName) +{ + std::string filenametoread = cmSystemTools::CollapseFullPath( + virtualFileName, this->GetCurrentSourceDirectory()); + + ListFileScope scope(this, filenametoread); + + cmListFile listFile; + if (!listFile.ParseString(content.c_str(), virtualFileName.c_str(), + this->GetMessenger(), this->Backtrace)) { + return false; + } + + this->RunListFile(listFile, filenametoread); + if (cmSystemTools::GetFatalErrorOccurred()) { + scope.Quiet(); + } + return true; +} + +void cmMakefile::RunListFile(cmListFile const& listFile, + std::string const& filenametoread, + DeferCommands* defer) +{ + // add this list file to the list of dependencies + this->ListFiles.push_back(filenametoread); + + std::string currentParentFile = + this->GetSafeDefinition("CMAKE_PARENT_LIST_FILE"); + std::string currentFile = this->GetSafeDefinition("CMAKE_CURRENT_LIST_FILE"); + + this->AddDefinition("CMAKE_CURRENT_LIST_FILE", filenametoread); + this->AddDefinition("CMAKE_CURRENT_LIST_DIR", + cmSystemTools::GetFilenamePath(filenametoread)); + + this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE"); + this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE"); + this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR"); + + // Run the parsed commands. + const size_t numberFunctions = listFile.Functions.size(); + for (size_t i = 0; i < numberFunctions; ++i) { + cmExecutionStatus status(*this); + this->ExecuteCommand(listFile.Functions[i], status); + if (cmSystemTools::GetFatalErrorOccurred()) { + break; + } + if (status.GetReturnInvoked()) { + this->RaiseScope(status.GetReturnVariables()); + // Exit early due to return command. + break; + } + } + + // Run any deferred commands. + if (defer) { + // Add a backtrace level indicating calls are deferred. + DeferScope scope(this, filenametoread); + + // Iterate by index in case one deferred call schedules another. + // NOLINTNEXTLINE(modernize-loop-convert) + for (size_t i = 0; i < defer->Commands.size(); ++i) { + DeferCommand& d = defer->Commands[i]; + if (d.Id.empty()) { + // Canceled. + continue; + } + // Mark as executed. + std::string id = std::move(d.Id); + + // The deferred call may have come from another file. + DeferCallScope callScope(this, d.FilePath); + + cmExecutionStatus status(*this); + this->ExecuteCommand(d.Command, status, std::move(id)); + if (cmSystemTools::GetFatalErrorOccurred()) { + break; + } + } + } + + this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile); + this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile); + this->AddDefinition("CMAKE_CURRENT_LIST_DIR", + cmSystemTools::GetFilenamePath(currentFile)); + this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE"); + this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE"); + this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR"); +} + +void cmMakefile::EnforceDirectoryLevelRules() const +{ + // Diagnose a violation of CMP0000 if necessary. + if (this->CheckCMP0000) { + std::ostringstream msg; + msg << "No cmake_minimum_required command is present. " + << "A line of code such as\n" + << " cmake_minimum_required(VERSION " << cmVersion::GetMajorVersion() + << "." << cmVersion::GetMinorVersion() << ")\n" + << "should be added at the top of the file. " + << "The version specified may be lower if you wish to " + << "support older CMake versions for this project. " + << "For more information run " + << "\"cmake --help-policy CMP0000\"."; + switch (this->GetPolicyStatus(cmPolicies::CMP0000)) { + case cmPolicies::WARN: + // Warn because the user did not provide a minimum required + // version. + this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, + msg.str(), this->Backtrace); + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to use policy version 2.4 set in + // cmListFileCache. + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // NEW behavior is to issue an error. + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, + msg.str(), this->Backtrace); + cmSystemTools::SetFatalErrorOccurred(); + break; + } + } +} + +void cmMakefile::AddEvaluationFile( + const std::string& inputFile, const std::string& targetName, + std::unique_ptr<cmCompiledGeneratorExpression> outputName, + std::unique_ptr<cmCompiledGeneratorExpression> condition, + const std::string& newLineCharacter, mode_t permissions, bool inputIsContent) +{ + this->EvaluationFiles.push_back( + cm::make_unique<cmGeneratorExpressionEvaluationFile>( + inputFile, targetName, std::move(outputName), std::move(condition), + inputIsContent, newLineCharacter, permissions, + this->GetPolicyStatus(cmPolicies::CMP0070))); +} + +const std::vector<std::unique_ptr<cmGeneratorExpressionEvaluationFile>>& +cmMakefile::GetEvaluationFiles() const +{ + return this->EvaluationFiles; +} + +std::vector<std::unique_ptr<cmExportBuildFileGenerator>> const& +cmMakefile::GetExportBuildFileGenerators() const +{ + return this->ExportBuildFileGenerators; +} + +void cmMakefile::RemoveExportBuildFileGeneratorCMP0024( + cmExportBuildFileGenerator* gen) +{ + auto it = + std::find_if(this->ExportBuildFileGenerators.begin(), + this->ExportBuildFileGenerators.end(), + [gen](std::unique_ptr<cmExportBuildFileGenerator> const& p) { + return p.get() == gen; + }); + if (it != this->ExportBuildFileGenerators.end()) { + this->ExportBuildFileGenerators.erase(it); + } +} + +void cmMakefile::AddExportBuildFileGenerator( + std::unique_ptr<cmExportBuildFileGenerator> gen) +{ + this->ExportBuildFileGenerators.emplace_back(std::move(gen)); +} + +namespace { +struct file_not_persistent +{ + bool operator()(const std::string& path) const + { + return !(path.find("CMakeTmp") == std::string::npos && + cmSystemTools::FileExists(path)); + } +}; +} + +void cmMakefile::AddGeneratorAction(GeneratorAction&& action) +{ + assert(!this->GeneratorActionsInvoked); + this->GeneratorActions.emplace_back(std::move(action), this->Backtrace); +} + +void cmMakefile::GeneratorAction::operator()(cmLocalGenerator& lg, + const cmListFileBacktrace& lfbt) +{ + if (cc) { + CCAction(lg, lfbt, std::move(cc)); + } else { + assert(Action); + Action(lg, lfbt); + } +} + +void cmMakefile::DoGenerate(cmLocalGenerator& lg) +{ + // do all the variable expansions here + this->ExpandVariablesCMP0019(); + + // give all the commands a chance to do something + // after the file has been parsed before generation + for (auto& action : this->GeneratorActions) { + action.Value(lg, action.Backtrace); + } + this->GeneratorActionsInvoked = true; + + // go through all configured files and see which ones still exist. + // we don't want cmake to re-run if a configured file is created and deleted + // during processing as that would make it a transient file that can't + // influence the build process + cm::erase_if(this->OutputFiles, file_not_persistent()); + + // if a configured file is used as input for another configured file, + // and then deleted it will show up in the input list files so we + // need to scan those too + cm::erase_if(this->ListFiles, file_not_persistent()); +} + +// Generate the output file +void cmMakefile::Generate(cmLocalGenerator& lg) +{ + this->DoGenerate(lg); + cmValue oldValue = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); + if (oldValue && + cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, *oldValue, + "2.4")) { + this->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + "You have set CMAKE_BACKWARDS_COMPATIBILITY to a CMake version less " + "than 2.4. This version of CMake only supports backwards compatibility " + "with CMake 2.4 or later. For compatibility with older versions please " + "use any CMake 2.8.x release or lower.", + this->Backtrace); + } +} + +namespace { +// There are still too many implicit backtraces through cmMakefile. As a +// workaround we reset the backtrace temporarily. +struct BacktraceGuard +{ + BacktraceGuard(cmListFileBacktrace& lfbt, cmListFileBacktrace current) + : Backtrace(lfbt) + , Previous(lfbt) + { + this->Backtrace = std::move(current); + } + + ~BacktraceGuard() { this->Backtrace = std::move(this->Previous); } + +private: + cmListFileBacktrace& Backtrace; + cmListFileBacktrace Previous; +}; +} + +bool cmMakefile::ValidateCustomCommand( + const cmCustomCommandLines& commandLines) const +{ + // TODO: More strict? + for (cmCustomCommandLine const& cl : commandLines) { + if (!cl.empty() && !cl[0].empty() && cl[0][0] == '"') { + std::ostringstream e; + e << "COMMAND may not contain literal quotes:\n " << cl[0] << "\n"; + this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return false; + } + } + + return true; +} + +cmTarget* cmMakefile::GetCustomCommandTarget( + const std::string& target, cmObjectLibraryCommands objLibCommands, + const cmListFileBacktrace& lfbt) const +{ + // Find the target to which to add the custom command. + auto ti = this->Targets.find(target); + + if (ti == this->Targets.end()) { + MessageType messageType = MessageType::AUTHOR_WARNING; + bool issueMessage = false; + std::ostringstream e; + switch (this->GetPolicyStatus(cmPolicies::CMP0040)) { + case cmPolicies::WARN: + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0040) << "\n"; + issueMessage = true; + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + issueMessage = true; + messageType = MessageType::FATAL_ERROR; + break; + } + + if (issueMessage) { + if (cmTarget const* t = this->FindTargetToUse(target)) { + if (t->IsImported()) { + e << "TARGET '" << target + << "' is IMPORTED and does not build here."; + } else { + e << "TARGET '" << target << "' was not created in this directory."; + } + } else { + e << "No TARGET '" << target + << "' has been created in this directory."; + } + this->GetCMakeInstance()->IssueMessage(messageType, e.str(), lfbt); + } + + return nullptr; + } + + cmTarget* t = &ti->second; + if (objLibCommands == cmObjectLibraryCommands::Reject && + t->GetType() == cmStateEnums::OBJECT_LIBRARY) { + std::ostringstream e; + e << "Target \"" << target + << "\" is an OBJECT library " + "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands."; + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), + lfbt); + return nullptr; + } + if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + std::ostringstream e; + e << "Target \"" << target + << "\" is an INTERFACE library " + "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands."; + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), + lfbt); + return nullptr; + } + + return t; +} + +cmTarget* cmMakefile::AddCustomCommandToTarget( + const std::string& target, cmCustomCommandType type, + std::unique_ptr<cmCustomCommand> cc) +{ + const auto& byproducts = cc->GetByproducts(); + const auto& commandLines = cc->GetCommandLines(); + + cmTarget* t = this->GetCustomCommandTarget( + target, cmObjectLibraryCommands::Reject, this->Backtrace); + + // Validate custom commands. + if (!t || !this->ValidateCustomCommand(commandLines)) { + return t; + } + + // Always create the byproduct sources and mark them generated. + this->CreateGeneratedOutputs(byproducts); + + cc->RecordPolicyValues(this->GetStateSnapshot()); + + // Dispatch command creation to allow generator expressions in outputs. + this->AddGeneratorAction( + std::move(cc), + [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr<cmCustomCommand> tcc) { + BacktraceGuard guard(this->Backtrace, lfbt); + tcc->SetBacktrace(lfbt); + detail::AddCustomCommandToTarget(lg, cmCommandOrigin::Project, t, type, + std::move(tcc)); + }); + + return t; +} + +void cmMakefile::AddCustomCommandToOutput( + std::unique_ptr<cmCustomCommand> cc, const CommandSourceCallback& callback, + bool replace) +{ + const auto& outputs = cc->GetOutputs(); + const auto& byproducts = cc->GetByproducts(); + const auto& commandLines = cc->GetCommandLines(); + + // Make sure there is at least one output. + if (outputs.empty()) { + cmSystemTools::Error("Attempt to add a custom rule with no output!"); + return; + } + + // Validate custom commands. + if (!this->ValidateCustomCommand(commandLines)) { + return; + } + + // Always create the output sources and mark them generated. + this->CreateGeneratedOutputs(outputs); + this->CreateGeneratedOutputs(byproducts); + + cc->RecordPolicyValues(this->GetStateSnapshot()); + + // Dispatch command creation to allow generator expressions in outputs. + this->AddGeneratorAction( + std::move(cc), + [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr<cmCustomCommand> tcc) { + BacktraceGuard guard(this->Backtrace, lfbt); + tcc->SetBacktrace(lfbt); + cmSourceFile* sf = detail::AddCustomCommandToOutput( + lg, cmCommandOrigin::Project, std::move(tcc), replace); + if (callback && sf) { + callback(sf); + } + }); +} + +void cmMakefile::AddCustomCommandOldStyle( + const std::string& target, const std::vector<std::string>& outputs, + const std::vector<std::string>& depends, const std::string& source, + const cmCustomCommandLines& commandLines, const char* comment) +{ + auto cc = cm::make_unique<cmCustomCommand>(); + cc->SetDepends(depends); + cc->SetCommandLines(commandLines); + cc->SetComment(comment); + + // Translate the old-style signature to one of the new-style + // signatures. + if (source == target) { + // In the old-style signature if the source and target were the + // same then it added a post-build rule to the target. Preserve + // this behavior. + this->AddCustomCommandToTarget(target, cmCustomCommandType::POST_BUILD, + std::move(cc)); + return; + } + + auto ti = this->Targets.find(target); + cmTarget* t = ti != this->Targets.end() ? &ti->second : nullptr; + + auto addRuleFileToTarget = [=](cmSourceFile* sf) { + // If the rule was added to the source (and not a .rule file), + // then add the source to the target to make sure the rule is + // included. + if (!sf->GetPropertyAsBool("__CMAKE_RULE")) { + if (t) { + t->AddSource(sf->ResolveFullPath()); + } else { + cmSystemTools::Error("Attempt to add a custom rule to a target " + "that does not exist yet for target " + + target); + } + } + }; + + // Each output must get its own copy of this rule. + cmsys::RegularExpression sourceFiles( + "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|mpp|ixx|cppm|cu|m|mm|" + "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|" + "hm|hpp|hxx|in|txx|inl)$"); + + // Choose whether to use a main dependency. + if (sourceFiles.find(source)) { + // The source looks like a real file. Use it as the main dependency. + for (std::string const& output : outputs) { + auto cc1 = cm::make_unique<cmCustomCommand>(*cc); + cc1->SetOutputs(output); + cc1->SetMainDependency(source); + this->AddCustomCommandToOutput(std::move(cc1), addRuleFileToTarget); + } + } else { + cc->AppendDepends({ source }); + + // The source may not be a real file. Do not use a main dependency. + for (std::string const& output : outputs) { + auto cc1 = cm::make_unique<cmCustomCommand>(*cc); + cc1->SetOutputs(output); + this->AddCustomCommandToOutput(std::move(cc1), addRuleFileToTarget); + } + } +} + +void cmMakefile::AppendCustomCommandToOutput( + const std::string& output, const std::vector<std::string>& depends, + const cmImplicitDependsList& implicit_depends, + const cmCustomCommandLines& commandLines) +{ + // Validate custom commands. + if (this->ValidateCustomCommand(commandLines)) { + // Dispatch command creation to allow generator expressions in outputs. + this->AddGeneratorAction( + [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt) { + BacktraceGuard guard(this->Backtrace, lfbt); + detail::AppendCustomCommandToOutput(lg, lfbt, output, depends, + implicit_depends, commandLines); + }); + } +} + +cmTarget* cmMakefile::AddUtilityCommand(const std::string& utilityName, + bool excludeFromAll, + std::unique_ptr<cmCustomCommand> cc) +{ + const auto& depends = cc->GetDepends(); + const auto& byproducts = cc->GetByproducts(); + const auto& commandLines = cc->GetCommandLines(); + cmTarget* target = this->AddNewUtilityTarget(utilityName, excludeFromAll); + + // Validate custom commands. + if ((commandLines.empty() && depends.empty()) || + !this->ValidateCustomCommand(commandLines)) { + return target; + } + + // Always create the byproduct sources and mark them generated. + this->CreateGeneratedOutputs(byproducts); + + cc->RecordPolicyValues(this->GetStateSnapshot()); + + // Dispatch command creation to allow generator expressions in outputs. + this->AddGeneratorAction( + std::move(cc), + [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr<cmCustomCommand> tcc) { + BacktraceGuard guard(this->Backtrace, lfbt); + tcc->SetBacktrace(lfbt); + detail::AddUtilityCommand(lg, cmCommandOrigin::Project, target, + std::move(tcc)); + }); + + return target; +} + +static void s_AddDefineFlag(std::string const& flag, std::string& dflags) +{ + // remove any \n\r + std::string::size_type initSize = dflags.size(); + dflags += ' '; + dflags += flag; + std::string::iterator flagStart = dflags.begin() + initSize + 1; + std::replace(flagStart, dflags.end(), '\n', ' '); + std::replace(flagStart, dflags.end(), '\r', ' '); +} + +void cmMakefile::AddDefineFlag(std::string const& flag) +{ + if (flag.empty()) { + return; + } + + // Update the string used for the old DEFINITIONS property. + s_AddDefineFlag(flag, this->DefineFlagsOrig); + + // If this is really a definition, update COMPILE_DEFINITIONS. + if (this->ParseDefineFlag(flag, false)) { + return; + } + + // Add this flag that does not look like a definition. + s_AddDefineFlag(flag, this->DefineFlags); +} + +static void s_RemoveDefineFlag(std::string const& flag, std::string& dflags) +{ + std::string::size_type const len = flag.length(); + // Remove all instances of the flag that are surrounded by + // whitespace or the beginning/end of the string. + for (std::string::size_type lpos = dflags.find(flag, 0); + lpos != std::string::npos; lpos = dflags.find(flag, lpos)) { + std::string::size_type rpos = lpos + len; + if ((lpos <= 0 || isspace(dflags[lpos - 1])) && + (rpos >= dflags.size() || isspace(dflags[rpos]))) { + dflags.erase(lpos, len); + } else { + ++lpos; + } + } +} + +void cmMakefile::RemoveDefineFlag(std::string const& flag) +{ + // Check the length of the flag to remove. + if (flag.empty()) { + return; + } + + // Update the string used for the old DEFINITIONS property. + s_RemoveDefineFlag(flag, this->DefineFlagsOrig); + + // If this is really a definition, update COMPILE_DEFINITIONS. + if (this->ParseDefineFlag(flag, true)) { + return; + } + + // Remove this flag that does not look like a definition. + s_RemoveDefineFlag(flag, this->DefineFlags); +} + +void cmMakefile::AddCompileDefinition(std::string const& option) +{ + this->AppendProperty("COMPILE_DEFINITIONS", option); +} + +void cmMakefile::AddCompileOption(std::string const& option) +{ + this->AppendProperty("COMPILE_OPTIONS", option); +} + +void cmMakefile::AddLinkOption(std::string const& option) +{ + this->AppendProperty("LINK_OPTIONS", option); +} + +void cmMakefile::AddLinkDirectory(std::string const& directory, bool before) +{ + if (before) { + this->StateSnapshot.GetDirectory().PrependLinkDirectoriesEntry( + BT<std::string>(directory, this->Backtrace)); + } else { + this->StateSnapshot.GetDirectory().AppendLinkDirectoriesEntry( + BT<std::string>(directory, this->Backtrace)); + } +} + +bool cmMakefile::ParseDefineFlag(std::string const& def, bool remove) +{ + // Create a regular expression to match valid definitions. + static cmsys::RegularExpression valid("^[-/]D[A-Za-z_][A-Za-z0-9_]*(=.*)?$"); + + // Make sure the definition matches. + if (!valid.find(def)) { + return false; + } + + // Definitions with non-trivial values require a policy check. + static cmsys::RegularExpression trivial( + "^[-/]D[A-Za-z_][A-Za-z0-9_]*(=[A-Za-z0-9_.]+)?$"); + if (!trivial.find(def)) { + // This definition has a non-trivial value. + switch (this->GetPolicyStatus(cmPolicies::CMP0005)) { + case cmPolicies::WARN: + this->IssueMessage(MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0005)); + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to not escape the value. We should not + // convert the definition to use the property. + return false; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + this->IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0005)); + return false; + case cmPolicies::NEW: + // NEW behavior is to escape the value. Proceed to convert it + // to an entry in the property. + break; + } + } + + // Get the definition part after the flag. + const char* define = def.c_str() + 2; + + if (remove) { + if (cmValue cdefs = this->GetProperty("COMPILE_DEFINITIONS")) { + // Expand the list. + std::vector<std::string> defs = cmExpandedList(*cdefs); + + // Recompose the list without the definition. + auto defEnd = std::remove(defs.begin(), defs.end(), define); + auto defBegin = defs.begin(); + std::string ndefs = cmJoin(cmMakeRange(defBegin, defEnd), ";"); + + // Store the new list. + this->SetProperty("COMPILE_DEFINITIONS", ndefs); + } + } else { + // Append the definition to the directory property. + this->AppendProperty("COMPILE_DEFINITIONS", define); + } + + return true; +} + +void cmMakefile::InitializeFromParent(cmMakefile* parent) +{ + this->SystemIncludeDirectories = parent->SystemIncludeDirectories; + + // define flags + this->DefineFlags = parent->DefineFlags; + this->DefineFlagsOrig = parent->DefineFlagsOrig; + + // Include transform property. There is no per-config version. + { + const char* prop = "IMPLICIT_DEPENDS_INCLUDE_TRANSFORM"; + this->SetProperty(prop, parent->GetProperty(prop)); + } + + // compile definitions property and per-config versions + cmPolicies::PolicyStatus polSt = this->GetPolicyStatus(cmPolicies::CMP0043); + if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { + this->SetProperty("COMPILE_DEFINITIONS", + parent->GetProperty("COMPILE_DEFINITIONS")); + std::vector<std::string> configs = + this->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + for (std::string const& config : configs) { + std::string defPropName = + cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config)); + cmValue prop = parent->GetProperty(defPropName); + this->SetProperty(defPropName, prop); + } + } + + // labels + this->SetProperty("LABELS", parent->GetProperty("LABELS")); + + // link libraries + this->SetProperty("LINK_LIBRARIES", parent->GetProperty("LINK_LIBRARIES")); + + // the initial project name + this->StateSnapshot.SetProjectName(parent->StateSnapshot.GetProjectName()); + + // Copy include regular expressions. + this->ComplainFileRegularExpression = parent->ComplainFileRegularExpression; + + // Imported targets. + this->ImportedTargets = parent->ImportedTargets; + + // Non-global Alias targets. + this->AliasTargets = parent->AliasTargets; + + // Recursion depth. + this->RecursionDepth = parent->RecursionDepth; +} + +void cmMakefile::AddInstallGenerator(std::unique_ptr<cmInstallGenerator> g) +{ + if (g) { + this->InstallGenerators.push_back(std::move(g)); + } +} + +void cmMakefile::AddTestGenerator(std::unique_ptr<cmTestGenerator> g) +{ + if (g) { + this->TestGenerators.push_back(std::move(g)); + } +} + +void cmMakefile::PushFunctionScope(std::string const& fileName, + const cmPolicies::PolicyMap& pm) +{ + this->StateSnapshot = this->GetState()->CreateFunctionCallSnapshot( + this->StateSnapshot, fileName); + assert(this->StateSnapshot.IsValid()); + + this->PushLoopBlockBarrier(); + +#if !defined(CMAKE_BOOTSTRAP) + this->GetGlobalGenerator()->GetFileLockPool().PushFunctionScope(); +#endif + + this->PushFunctionBlockerBarrier(); + + this->PushPolicy(true, pm); +} + +void cmMakefile::PopFunctionScope(bool reportError) +{ + this->PopPolicy(); + + this->PopSnapshot(reportError); + + this->PopFunctionBlockerBarrier(reportError); + +#if !defined(CMAKE_BOOTSTRAP) + this->GetGlobalGenerator()->GetFileLockPool().PopFunctionScope(); +#endif + + this->PopLoopBlockBarrier(); +} + +void cmMakefile::PushMacroScope(std::string const& fileName, + const cmPolicies::PolicyMap& pm) +{ + this->StateSnapshot = + this->GetState()->CreateMacroCallSnapshot(this->StateSnapshot, fileName); + assert(this->StateSnapshot.IsValid()); + + this->PushFunctionBlockerBarrier(); + + this->PushPolicy(true, pm); +} + +void cmMakefile::PopMacroScope(bool reportError) +{ + this->PopPolicy(); + this->PopSnapshot(reportError); + + this->PopFunctionBlockerBarrier(reportError); +} + +bool cmMakefile::IsRootMakefile() const +{ + return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid(); +} + +class cmMakefile::BuildsystemFileScope +{ +public: + BuildsystemFileScope(cmMakefile* mf) + : Makefile(mf) + { + std::string currentStart = + cmStrCat(this->Makefile->StateSnapshot.GetDirectory().GetCurrentSource(), + "/CMakeLists.txt"); + this->Makefile->StateSnapshot.SetListFile(currentStart); + this->Makefile->StateSnapshot = + this->Makefile->StateSnapshot.GetState()->CreatePolicyScopeSnapshot( + this->Makefile->StateSnapshot); + this->Makefile->PushFunctionBlockerBarrier(); + + this->GG = mf->GetGlobalGenerator(); + this->CurrentMakefile = this->GG->GetCurrentMakefile(); + this->Snapshot = this->GG->GetCMakeInstance()->GetCurrentSnapshot(); + this->GG->GetCMakeInstance()->SetCurrentSnapshot(this->Snapshot); + this->GG->SetCurrentMakefile(mf); +#if !defined(CMAKE_BOOTSTRAP) + this->GG->GetFileLockPool().PushFileScope(); +#endif + } + + ~BuildsystemFileScope() + { + this->Makefile->PopFunctionBlockerBarrier(this->ReportError); + this->Makefile->PopSnapshot(this->ReportError); +#if !defined(CMAKE_BOOTSTRAP) + this->GG->GetFileLockPool().PopFileScope(); +#endif + this->GG->SetCurrentMakefile(this->CurrentMakefile); + this->GG->GetCMakeInstance()->SetCurrentSnapshot(this->Snapshot); + } + + void Quiet() { this->ReportError = false; } + + BuildsystemFileScope(const BuildsystemFileScope&) = delete; + BuildsystemFileScope& operator=(const BuildsystemFileScope&) = delete; + +private: + cmMakefile* Makefile; + cmGlobalGenerator* GG; + cmMakefile* CurrentMakefile; + cmStateSnapshot Snapshot; + bool ReportError = true; +}; + +void cmMakefile::Configure() +{ + std::string currentStart = cmStrCat( + this->StateSnapshot.GetDirectory().GetCurrentSource(), "/CMakeLists.txt"); + + // Add the bottom of all backtraces within this directory. + // We will never pop this scope because it should be available + // for messages during the generate step too. + this->Backtrace = + this->Backtrace.Push(cmListFileContext::FromListFilePath(currentStart)); + + BuildsystemFileScope scope(this); + + // make sure the CMakeFiles dir is there + std::string filesDir = cmStrCat( + this->StateSnapshot.GetDirectory().GetCurrentBinary(), "/CMakeFiles"); + cmSystemTools::MakeDirectory(filesDir); + + assert(cmSystemTools::FileExists(currentStart, true)); + this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentStart); + + cmListFile listFile; + if (!listFile.ParseFile(currentStart.c_str(), this->GetMessenger(), + this->Backtrace)) { + return; + } + if (this->IsRootMakefile()) { + bool hasVersion = false; + // search for the right policy command + for (cmListFileFunction const& func : listFile.Functions) { + if (func.LowerCaseName() == "cmake_minimum_required") { + hasVersion = true; + break; + } + } + // if no policy command is found this is an error if they use any + // non advanced functions or a lot of functions + if (!hasVersion) { + bool isProblem = true; + if (listFile.Functions.size() < 30) { + // the list of simple commands DO NOT ADD TO THIS LIST!!!!! + // these commands must have backwards compatibility forever and + // and that is a lot longer than your tiny mind can comprehend mortal + std::set<std::string> allowedCommands; + allowedCommands.insert("project"); + allowedCommands.insert("set"); + allowedCommands.insert("if"); + allowedCommands.insert("endif"); + allowedCommands.insert("else"); + allowedCommands.insert("elseif"); + allowedCommands.insert("add_executable"); + allowedCommands.insert("add_library"); + allowedCommands.insert("target_link_libraries"); + allowedCommands.insert("option"); + allowedCommands.insert("message"); + isProblem = false; + for (cmListFileFunction const& func : listFile.Functions) { + if (!cm::contains(allowedCommands, func.LowerCaseName())) { + isProblem = true; + break; + } + } + } + + if (isProblem) { + // Tell the top level cmMakefile to diagnose + // this violation of CMP0000. + this->SetCheckCMP0000(true); + + // Implicitly set the version for the user. + cmPolicies::ApplyPolicyVersion(this, 2, 4, 0, + cmPolicies::WarnCompat::Off); + } + } + bool hasProject = false; + // search for a project command + for (cmListFileFunction const& func : listFile.Functions) { + if (func.LowerCaseName() == "project") { + hasProject = true; + break; + } + } + // if no project command is found, add one + if (!hasProject) { + this->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, + "No project() command is present. The top-level CMakeLists.txt " + "file must contain a literal, direct call to the project() command. " + "Add a line of code such as\n" + " project(ProjectName)\n" + "near the top of the file, but after cmake_minimum_required().\n" + "CMake is pretending there is a \"project(Project)\" command on " + "the first line.", + this->Backtrace); + cmListFileFunction project{ "project", + 0, + 0, + { { "Project", cmListFileArgument::Unquoted, + 0 }, + { "__CMAKE_INJECTED_PROJECT_COMMAND__", + cmListFileArgument::Unquoted, 0 } } }; + listFile.Functions.insert(listFile.Functions.begin(), project); + } + } + + this->Defer = cm::make_unique<DeferCommands>(); + this->RunListFile(listFile, currentStart, this->Defer.get()); + this->Defer.reset(); + if (cmSystemTools::GetFatalErrorOccurred()) { + scope.Quiet(); + } + + // at the end handle any old style subdirs + std::vector<cmMakefile*> subdirs = this->UnConfiguredDirectories; + + // for each subdir recurse + auto sdi = subdirs.begin(); + for (; sdi != subdirs.end(); ++sdi) { + (*sdi)->StateSnapshot.InitializeFromParent_ForSubdirsCommand(); + this->ConfigureSubDirectory(*sdi); + } + + this->AddCMakeDependFilesFromUser(); +} + +void cmMakefile::ConfigureSubDirectory(cmMakefile* mf) +{ + mf->InitializeFromParent(this); + std::string currentStart = mf->GetCurrentSourceDirectory(); + if (this->GetCMakeInstance()->GetDebugOutput()) { + std::string msg = cmStrCat(" Entering ", currentStart); + cmSystemTools::Message(msg); + } + + std::string const currentStartFile = currentStart + "/CMakeLists.txt"; + if (!cmSystemTools::FileExists(currentStartFile, true)) { + // The file is missing. Check policy CMP0014. + std::ostringstream e; + /* clang-format off */ + e << "The source directory\n" + << " " << currentStart << "\n" + << "does not contain a CMakeLists.txt file."; + /* clang-format on */ + switch (this->GetPolicyStatus(cmPolicies::CMP0014)) { + case cmPolicies::WARN: + // Print the warning. + /* clang-format off */ + e << "\n" + << "CMake does not support this case but it used " + << "to work accidentally and is being allowed for " + << "compatibility." + << "\n" + << cmPolicies::GetPolicyWarning(cmPolicies::CMP0014); + /* clang-format on */ + this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior does not warn. + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + e << "\n" << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0014); + CM_FALLTHROUGH; + case cmPolicies::NEW: + // NEW behavior prints the error. + this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + break; + } + return; + } + // finally configure the subdir + mf->Configure(); + + if (this->GetCMakeInstance()->GetDebugOutput()) { + std::string msg = + cmStrCat(" Returning to ", this->GetCurrentSourceDirectory()); + cmSystemTools::Message(msg); + } +} + +void cmMakefile::AddSubDirectory(const std::string& srcPath, + const std::string& binPath, + bool excludeFromAll, bool immediate, + bool system) +{ + if (this->DeferRunning) { + this->IssueMessage( + MessageType::FATAL_ERROR, + "Subdirectories may not be created during deferred execution."); + return; + } + + // Make sure the binary directory is unique. + if (!this->EnforceUniqueDir(srcPath, binPath)) { + return; + } + + cmStateSnapshot newSnapshot = + this->GetState()->CreateBuildsystemDirectorySnapshot(this->StateSnapshot); + + newSnapshot.GetDirectory().SetCurrentSource(srcPath); + newSnapshot.GetDirectory().SetCurrentBinary(binPath); + + cmSystemTools::MakeDirectory(binPath); + + auto subMfu = + cm::make_unique<cmMakefile>(this->GlobalGenerator, newSnapshot); + auto* subMf = subMfu.get(); + this->GetGlobalGenerator()->AddMakefile(std::move(subMfu)); + + if (excludeFromAll) { + subMf->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); + } + if (system) { + subMf->SetProperty("SYSTEM", "TRUE"); + } + + if (immediate) { + this->ConfigureSubDirectory(subMf); + } else { + this->UnConfiguredDirectories.push_back(subMf); + } + + this->AddInstallGenerator(cm::make_unique<cmInstallSubdirectoryGenerator>( + subMf, binPath, this->GetBacktrace())); +} + +const std::string& cmMakefile::GetCurrentSourceDirectory() const +{ + return this->StateSnapshot.GetDirectory().GetCurrentSource(); +} + +const std::string& cmMakefile::GetCurrentBinaryDirectory() const +{ + return this->StateSnapshot.GetDirectory().GetCurrentBinary(); +} + +std::vector<cmTarget*> cmMakefile::GetImportedTargets() const +{ + std::vector<cmTarget*> tgts; + tgts.reserve(this->ImportedTargets.size()); + for (auto const& impTarget : this->ImportedTargets) { + tgts.push_back(impTarget.second); + } + return tgts; +} + +void cmMakefile::AddIncludeDirectories(const std::vector<std::string>& incs, + bool before) +{ + if (incs.empty()) { + return; + } + + std::string entryString = cmJoin(incs, ";"); + if (before) { + this->StateSnapshot.GetDirectory().PrependIncludeDirectoriesEntry( + BT<std::string>(entryString, this->Backtrace)); + } else { + this->StateSnapshot.GetDirectory().AppendIncludeDirectoriesEntry( + BT<std::string>(entryString, this->Backtrace)); + } + + // Property on each target: + for (auto& target : this->Targets) { + cmTarget& t = target.second; + t.InsertInclude(BT<std::string>(entryString, this->Backtrace), before); + } +} + +void cmMakefile::AddSystemIncludeDirectories(const std::set<std::string>& incs) +{ + if (incs.empty()) { + return; + } + + this->SystemIncludeDirectories.insert(incs.begin(), incs.end()); + + for (auto& target : this->Targets) { + cmTarget& t = target.second; + t.AddSystemIncludeDirectories(incs); + } +} + +void cmMakefile::AddDefinition(const std::string& name, cm::string_view value) +{ + this->StateSnapshot.SetDefinition(name, value); + +#ifndef CMAKE_BOOTSTRAP + cmVariableWatch* vv = this->GetVariableWatch(); + if (vv) { + vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, + value.data(), this); + } +#endif +} + +void cmMakefile::AddDefinitionBool(const std::string& name, bool value) +{ + this->AddDefinition(name, value ? "ON" : "OFF"); +} + +void cmMakefile::AddCacheDefinition(const std::string& name, const char* value, + const char* doc, + cmStateEnums::CacheEntryType type, + bool force) +{ + cmValue existingValue = this->GetState()->GetInitializedCacheValue(name); + // must be outside the following if() to keep it alive long enough + std::string nvalue; + + if (existingValue && + (this->GetState()->GetCacheEntryType(name) == + cmStateEnums::UNINITIALIZED)) { + // if this is not a force, then use the value from the cache + // if it is a force, then use the value being passed in + if (!force) { + value = existingValue->c_str(); + } + if (type == cmStateEnums::PATH || type == cmStateEnums::FILEPATH) { + std::vector<std::string>::size_type cc; + std::vector<std::string> files; + nvalue = value ? value : ""; + + cmExpandList(nvalue, files); + nvalue.clear(); + for (cc = 0; cc < files.size(); cc++) { + if (!cmIsOff(files[cc])) { + files[cc] = cmSystemTools::CollapseFullPath(files[cc]); + } + if (cc > 0) { + nvalue += ";"; + } + nvalue += files[cc]; + } + + this->GetCMakeInstance()->AddCacheEntry(name, nvalue, doc, type); + nvalue = *this->GetState()->GetInitializedCacheValue(name); + value = nvalue.c_str(); + } + } + this->GetCMakeInstance()->AddCacheEntry(name, value, doc, type); + switch (this->GetPolicyStatus(cmPolicies::CMP0126)) { + case cmPolicies::WARN: + if (this->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0126") && + this->IsNormalDefinitionSet(name)) { + this->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0126), + "\nFor compatibility with older versions of CMake, normal " + "variable \"", + name, "\" will be removed from the current scope.")); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + // if there was a definition then remove it + this->StateSnapshot.RemoveDefinition(name); + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + break; + } +} + +void cmMakefile::MarkVariableAsUsed(const std::string& var) +{ + this->StateSnapshot.GetDefinition(var); +} + +bool cmMakefile::VariableInitialized(const std::string& var) const +{ + return this->StateSnapshot.IsInitialized(var); +} + +void cmMakefile::MaybeWarnUninitialized(std::string const& variable, + const char* sourceFilename) const +{ + // check to see if we need to print a warning + // if strict mode is on and the variable has + // not been "cleared"/initialized with a set(foo ) call + if (this->GetCMakeInstance()->GetWarnUninitialized() && + !this->VariableInitialized(variable)) { + if (this->CheckSystemVars || + (sourceFilename && this->IsProjectFile(sourceFilename))) { + std::ostringstream msg; + msg << "uninitialized variable \'" << variable << "\'"; + this->IssueMessage(MessageType::AUTHOR_WARNING, msg.str()); + } + } +} + +void cmMakefile::RemoveDefinition(const std::string& name) +{ + this->StateSnapshot.RemoveDefinition(name); +#ifndef CMAKE_BOOTSTRAP + cmVariableWatch* vv = this->GetVariableWatch(); + if (vv) { + vv->VariableAccessed(name, cmVariableWatch::VARIABLE_REMOVED_ACCESS, + nullptr, this); + } +#endif +} + +void cmMakefile::RemoveCacheDefinition(const std::string& name) const +{ + this->GetState()->RemoveCacheEntry(name); +} + +void cmMakefile::SetProjectName(std::string const& p) +{ + this->StateSnapshot.SetProjectName(p); +} + +void cmMakefile::AddGlobalLinkInformation(cmTarget& target) +{ + // for these targets do not add anything + switch (target.GetType()) { + case cmStateEnums::UTILITY: + case cmStateEnums::GLOBAL_TARGET: + case cmStateEnums::INTERFACE_LIBRARY: + return; + default:; + } + + if (cmValue linkLibsProp = this->GetProperty("LINK_LIBRARIES")) { + std::vector<std::string> linkLibs = cmExpandedList(*linkLibsProp); + + for (auto j = linkLibs.begin(); j != linkLibs.end(); ++j) { + std::string libraryName = *j; + cmTargetLinkLibraryType libType = GENERAL_LibraryType; + if (libraryName == "optimized") { + libType = OPTIMIZED_LibraryType; + ++j; + libraryName = *j; + } else if (libraryName == "debug") { + libType = DEBUG_LibraryType; + ++j; + libraryName = *j; + } + // This is equivalent to the target_link_libraries plain signature. + target.AddLinkLibrary(*this, libraryName, libType); + target.AppendProperty( + "INTERFACE_LINK_LIBRARIES", + target.GetDebugGeneratorExpressions(libraryName, libType)); + } + } +} + +void cmMakefile::AddAlias(const std::string& lname, std::string const& tgtName, + bool globallyVisible) +{ + this->AliasTargets[lname] = tgtName; + if (globallyVisible) { + this->GetGlobalGenerator()->AddAlias(lname, tgtName); + } +} + +cmTarget* cmMakefile::AddLibrary(const std::string& lname, + cmStateEnums::TargetType type, + const std::vector<std::string>& srcs, + bool excludeFromAll) +{ + assert(type == cmStateEnums::STATIC_LIBRARY || + type == cmStateEnums::SHARED_LIBRARY || + type == cmStateEnums::MODULE_LIBRARY || + type == cmStateEnums::OBJECT_LIBRARY || + type == cmStateEnums::INTERFACE_LIBRARY); + + cmTarget* target = this->AddNewTarget(type, lname); + // Clear its dependencies. Otherwise, dependencies might persist + // over changes in CMakeLists.txt, making the information stale and + // hence useless. + target->ClearDependencyInformation(*this); + if (excludeFromAll) { + target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); + } + target->AddSources(srcs); + this->AddGlobalLinkInformation(*target); + return target; +} + +cmTarget* cmMakefile::AddExecutable(const std::string& exeName, + const std::vector<std::string>& srcs, + bool excludeFromAll) +{ + cmTarget* target = this->AddNewTarget(cmStateEnums::EXECUTABLE, exeName); + if (excludeFromAll) { + target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); + } + target->AddSources(srcs); + this->AddGlobalLinkInformation(*target); + return target; +} + +cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type, + const std::string& name) +{ + return &this->CreateNewTarget(name, type).first; +} + +std::pair<cmTarget&, bool> cmMakefile::CreateNewTarget( + const std::string& name, cmStateEnums::TargetType type, + cmTarget::PerConfig perConfig) +{ + auto ib = this->Targets.emplace( + name, cmTarget(name, type, cmTarget::VisibilityNormal, this, perConfig)); + auto it = ib.first; + if (!ib.second) { + return std::make_pair(std::ref(it->second), false); + } + this->OrderedTargets.push_back(&it->second); + this->GetGlobalGenerator()->IndexTarget(&it->second); + this->GetStateSnapshot().GetDirectory().AddNormalTargetName(name); + return std::make_pair(std::ref(it->second), true); +} + +cmTarget* cmMakefile::AddNewUtilityTarget(const std::string& utilityName, + bool excludeFromAll) +{ + cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName); + if (excludeFromAll) { + target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); + } + return target; +} + +namespace { +} + +#if !defined(CMAKE_BOOTSTRAP) +cmSourceGroup* cmMakefile::GetSourceGroup( + const std::vector<std::string>& name) const +{ + cmSourceGroup* sg = nullptr; + + // first look for source group starting with the same as the one we want + for (cmSourceGroup const& srcGroup : this->SourceGroups) { + std::string const& sgName = srcGroup.GetName(); + if (sgName == name[0]) { + sg = const_cast<cmSourceGroup*>(&srcGroup); + break; + } + } + + if (sg != nullptr) { + // iterate through its children to find match source group + for (unsigned int i = 1; i < name.size(); ++i) { + sg = sg->LookupChild(name[i]); + if (sg == nullptr) { + break; + } + } + } + return sg; +} + +void cmMakefile::AddSourceGroup(const std::string& name, const char* regex) +{ + std::vector<std::string> nameVector; + nameVector.push_back(name); + this->AddSourceGroup(nameVector, regex); +} + +void cmMakefile::AddSourceGroup(const std::vector<std::string>& name, + const char* regex) +{ + cmSourceGroup* sg = nullptr; + std::vector<std::string> currentName; + int i = 0; + const int lastElement = static_cast<int>(name.size() - 1); + for (i = lastElement; i >= 0; --i) { + currentName.assign(name.begin(), name.begin() + i + 1); + sg = this->GetSourceGroup(currentName); + if (sg != nullptr) { + break; + } + } + + // i now contains the index of the last found component + if (i == lastElement) { + // group already exists, replace its regular expression + if (regex && sg) { + // We only want to set the regular expression. If there are already + // source files in the group, we don't want to remove them. + sg->SetGroupRegex(regex); + } + return; + } + if (i == -1) { + // group does not exist nor belong to any existing group + // add its first component + this->SourceGroups.emplace_back(name[0], regex); + sg = this->GetSourceGroup(currentName); + i = 0; // last component found + } + if (!sg) { + cmSystemTools::Error("Could not create source group "); + return; + } + // build the whole source group path + for (++i; i <= lastElement; ++i) { + sg->AddChild(cmSourceGroup(name[i], nullptr, sg->GetFullName().c_str())); + sg = sg->LookupChild(name[i]); + } + + sg->SetGroupRegex(regex); +} + +cmSourceGroup* cmMakefile::GetOrCreateSourceGroup( + const std::vector<std::string>& folders) +{ + cmSourceGroup* sg = this->GetSourceGroup(folders); + if (sg == nullptr) { + this->AddSourceGroup(folders); + sg = this->GetSourceGroup(folders); + } + return sg; +} + +cmSourceGroup* cmMakefile::GetOrCreateSourceGroup(const std::string& name) +{ + std::string delimiters; + if (cmValue p = this->GetDefinition("SOURCE_GROUP_DELIMITER")) { + delimiters = *p; + } else { + delimiters = "/\\"; + } + return this->GetOrCreateSourceGroup(cmTokenize(name, delimiters)); +} + +/** + * Find a source group whose regular expression matches the filename + * part of the given source name. Search backward through the list of + * source groups, and take the first matching group found. This way + * non-inherited SOURCE_GROUP commands will have precedence over + * inherited ones. + */ +cmSourceGroup* cmMakefile::FindSourceGroup( + const std::string& source, std::vector<cmSourceGroup>& groups) const +{ + // First search for a group that lists the file explicitly. + for (auto sg = groups.rbegin(); sg != groups.rend(); ++sg) { + cmSourceGroup* result = sg->MatchChildrenFiles(source); + if (result) { + return result; + } + } + + // Now search for a group whose regex matches the file. + for (auto sg = groups.rbegin(); sg != groups.rend(); ++sg) { + cmSourceGroup* result = sg->MatchChildrenRegex(source); + if (result) { + return result; + } + } + + // Shouldn't get here, but just in case, return the default group. + return groups.data(); +} +#endif + +static bool mightExpandVariablesCMP0019(const char* s) +{ + return s && *s && strstr(s, "${") && strchr(s, '}'); +} + +void cmMakefile::ExpandVariablesCMP0019() +{ + // Drop this ancient compatibility behavior with a policy. + cmPolicies::PolicyStatus pol = this->GetPolicyStatus(cmPolicies::CMP0019); + if (pol != cmPolicies::OLD && pol != cmPolicies::WARN) { + return; + } + std::ostringstream w; + + cmValue includeDirs = this->GetProperty("INCLUDE_DIRECTORIES"); + if (includeDirs && mightExpandVariablesCMP0019(includeDirs->c_str())) { + std::string dirs = *includeDirs; + this->ExpandVariablesInString(dirs, true, true); + if (pol == cmPolicies::WARN && dirs != *includeDirs) { + /* clang-format off */ + w << "Evaluated directory INCLUDE_DIRECTORIES\n" + << " " << *includeDirs << "\n" + << "as\n" + << " " << dirs << "\n"; + /* clang-format on */ + } + this->SetProperty("INCLUDE_DIRECTORIES", dirs); + } + + // Also for each target's INCLUDE_DIRECTORIES property: + for (auto& target : this->Targets) { + cmTarget& t = target.second; + if (t.GetType() == cmStateEnums::INTERFACE_LIBRARY || + t.GetType() == cmStateEnums::GLOBAL_TARGET) { + continue; + } + includeDirs = t.GetProperty("INCLUDE_DIRECTORIES"); + if (includeDirs && mightExpandVariablesCMP0019(includeDirs->c_str())) { + std::string dirs = *includeDirs; + this->ExpandVariablesInString(dirs, true, true); + if (pol == cmPolicies::WARN && dirs != *includeDirs) { + /* clang-format off */ + w << "Evaluated target " << t.GetName() << " INCLUDE_DIRECTORIES\n" + << " " << *includeDirs << "\n" + << "as\n" + << " " << dirs << "\n"; + /* clang-format on */ + } + t.SetProperty("INCLUDE_DIRECTORIES", dirs); + } + } + + if (cmValue linkDirsProp = this->GetProperty("LINK_DIRECTORIES")) { + if (mightExpandVariablesCMP0019(linkDirsProp->c_str())) { + std::string d = *linkDirsProp; + const std::string orig = d; + this->ExpandVariablesInString(d, true, true); + if (pol == cmPolicies::WARN && d != orig) { + /* clang-format off */ + w << "Evaluated link directories\n" + << " " << orig << "\n" + << "as\n" + << " " << d << "\n"; + /* clang-format on */ + } + } + } + + if (cmValue linkLibsProp = this->GetProperty("LINK_LIBRARIES")) { + std::vector<std::string> linkLibs = cmExpandedList(*linkLibsProp); + + for (auto l = linkLibs.begin(); l != linkLibs.end(); ++l) { + std::string libName = *l; + if (libName == "optimized"_s || libName == "debug"_s) { + ++l; + libName = *l; + } + if (mightExpandVariablesCMP0019(libName.c_str())) { + const std::string orig = libName; + this->ExpandVariablesInString(libName, true, true); + if (pol == cmPolicies::WARN && libName != orig) { + /* clang-format off */ + w << "Evaluated link library\n" + << " " << orig << "\n" + << "as\n" + << " " << libName << "\n"; + /* clang-format on */ + } + } + } + } + + if (!w.str().empty()) { + std::ostringstream m; + /* clang-format off */ + m << cmPolicies::GetPolicyWarning(cmPolicies::CMP0019) + << "\n" + << "The following variable evaluations were encountered:\n" + << w.str(); + /* clang-format on */ + this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, + m.str(), this->Backtrace); + } +} + +bool cmMakefile::IsOn(const std::string& name) const +{ + return cmIsOn(this->GetDefinition(name)); +} + +bool cmMakefile::IsSet(const std::string& name) const +{ + cmValue value = this->GetDefinition(name); + if (!value) { + return false; + } + + if (value->empty()) { + return false; + } + + if (cmIsNOTFOUND(*value)) { + return false; + } + + return true; +} + +bool cmMakefile::PlatformIs32Bit() const +{ + if (cmValue plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { + if (*plat_abi == "ELF X32") { + return false; + } + } + if (cmValue sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { + return atoi(sizeof_dptr->c_str()) == 4; + } + return false; +} + +bool cmMakefile::PlatformIs64Bit() const +{ + if (cmValue sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { + return atoi(sizeof_dptr->c_str()) == 8; + } + return false; +} + +bool cmMakefile::PlatformIsx32() const +{ + if (cmValue plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { + if (*plat_abi == "ELF X32") { + return true; + } + } + return false; +} + +cmMakefile::AppleSDK cmMakefile::GetAppleSDKType() const +{ + std::string sdkRoot; + sdkRoot = this->GetSafeDefinition("CMAKE_OSX_SYSROOT"); + sdkRoot = cmSystemTools::LowerCase(sdkRoot); + + struct + { + std::string name; + AppleSDK sdk; + } const sdkDatabase[]{ + { "appletvos", AppleSDK::AppleTVOS }, + { "appletvsimulator", AppleSDK::AppleTVSimulator }, + { "iphoneos", AppleSDK::IPhoneOS }, + { "iphonesimulator", AppleSDK::IPhoneSimulator }, + { "watchos", AppleSDK::WatchOS }, + { "watchsimulator", AppleSDK::WatchSimulator }, + }; + + for (auto const& entry : sdkDatabase) { + if (cmHasPrefix(sdkRoot, entry.name) || + sdkRoot.find(std::string("/") + entry.name) != std::string::npos) { + return entry.sdk; + } + } + + return AppleSDK::MacOS; +} + +bool cmMakefile::PlatformIsAppleEmbedded() const +{ + return this->GetAppleSDKType() != AppleSDK::MacOS; +} + +const char* cmMakefile::GetSONameFlag(const std::string& language) const +{ + std::string name = "CMAKE_SHARED_LIBRARY_SONAME"; + if (!language.empty()) { + name += "_"; + name += language; + } + name += "_FLAG"; + return this->GetDefinition(name).GetCStr(); +} + +bool cmMakefile::CanIWriteThisFile(std::string const& fileName) const +{ + if (!this->IsOn("CMAKE_DISABLE_SOURCE_CHANGES")) { + return true; + } + // If we are doing an in-source build, then the test will always fail + if (cmSystemTools::SameFile(this->GetHomeDirectory(), + this->GetHomeOutputDirectory())) { + return !this->IsOn("CMAKE_DISABLE_IN_SOURCE_BUILD"); + } + + return !cmSystemTools::IsSubDirectory(fileName, this->GetHomeDirectory()) || + cmSystemTools::IsSubDirectory(fileName, this->GetHomeOutputDirectory()) || + cmSystemTools::SameFile(fileName, this->GetHomeOutputDirectory()); +} + +const std::string& cmMakefile::GetRequiredDefinition( + const std::string& name) const +{ + static std::string const empty; + cmValue def = this->GetDefinition(name); + if (!def) { + cmSystemTools::Error("Error required internal CMake variable not " + "set, cmake may not be built correctly.\n" + "Missing variable is:\n" + + name); + return empty; + } + return *def; +} + +bool cmMakefile::IsDefinitionSet(const std::string& name) const +{ + cmValue def = this->StateSnapshot.GetDefinition(name); + if (!def) { + def = this->GetState()->GetInitializedCacheValue(name); + } +#ifndef CMAKE_BOOTSTRAP + if (cmVariableWatch* vv = this->GetVariableWatch()) { + if (!def) { + vv->VariableAccessed( + name, cmVariableWatch::UNKNOWN_VARIABLE_DEFINED_ACCESS, nullptr, this); + } + } +#endif + return def != nullptr; +} + +bool cmMakefile::IsNormalDefinitionSet(const std::string& name) const +{ + cmValue def = this->StateSnapshot.GetDefinition(name); +#ifndef CMAKE_BOOTSTRAP + if (cmVariableWatch* vv = this->GetVariableWatch()) { + if (!def) { + vv->VariableAccessed( + name, cmVariableWatch::UNKNOWN_VARIABLE_DEFINED_ACCESS, nullptr, this); + } + } +#endif + return def != nullptr; +} + +cmValue cmMakefile::GetDefinition(const std::string& name) const +{ + cmValue def = this->StateSnapshot.GetDefinition(name); + if (!def) { + def = this->GetState()->GetInitializedCacheValue(name); + } +#ifndef CMAKE_BOOTSTRAP + cmVariableWatch* vv = this->GetVariableWatch(); + if (vv && !this->SuppressSideEffects) { + bool const watch_function_executed = + vv->VariableAccessed(name, + def ? cmVariableWatch::VARIABLE_READ_ACCESS + : cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS, + def.GetCStr(), this); + + if (watch_function_executed) { + // A callback was executed and may have caused re-allocation of the + // variable storage. Look it up again for now. + // FIXME: Refactor variable storage to avoid this problem. + def = this->StateSnapshot.GetDefinition(name); + if (!def) { + def = this->GetState()->GetInitializedCacheValue(name); + } + } + } +#endif + return def; +} + +const std::string& cmMakefile::GetSafeDefinition(const std::string& name) const +{ + return this->GetDefinition(name); +} + +bool cmMakefile::GetDefExpandList(const std::string& name, + std::vector<std::string>& out, + bool emptyArgs) const +{ + cmValue def = this->GetDefinition(name); + if (!def) { + return false; + } + cmExpandList(*def, out, emptyArgs); + return true; +} + +std::vector<std::string> cmMakefile::GetDefinitions() const +{ + std::vector<std::string> res = this->StateSnapshot.ClosureKeys(); + cm::append(res, this->GetState()->GetCacheEntryKeys()); + std::sort(res.begin(), res.end()); + return res; +} + +const std::string& cmMakefile::ExpandVariablesInString( + std::string& source) const +{ + return this->ExpandVariablesInString(source, false, false); +} + +const std::string& cmMakefile::ExpandVariablesInString( + std::string& source, bool escapeQuotes, bool noEscapes, bool atOnly, + const char* filename, long line, bool removeEmpty, bool replaceAt) const +{ + bool compareResults = false; + MessageType mtype = MessageType::LOG; + std::string errorstr; + std::string original; + + // Sanity check the @ONLY mode. + if (atOnly && (!noEscapes || !removeEmpty)) { + // This case should never be called. At-only is for + // configure-file/string which always does no escapes. + this->IssueMessage(MessageType::INTERNAL_ERROR, + "ExpandVariablesInString @ONLY called " + "on something with escapes."); + return source; + } + + // Variables used in the WARN case. + std::string newResult; + std::string newErrorstr; + MessageType newError = MessageType::LOG; + + switch (this->GetPolicyStatus(cmPolicies::CMP0053)) { + case cmPolicies::WARN: { + // Save the original string for the warning. + original = source; + newResult = source; + compareResults = true; + // Suppress variable watches to avoid calling hooks twice. Suppress new + // dereferences since the OLD behavior is still what is actually used. + this->SuppressSideEffects = true; + newError = this->ExpandVariablesInStringNew( + newErrorstr, newResult, escapeQuotes, noEscapes, atOnly, filename, + line, replaceAt); + this->SuppressSideEffects = false; + CM_FALLTHROUGH; + } + case cmPolicies::OLD: + mtype = this->ExpandVariablesInStringOld(errorstr, source, escapeQuotes, + noEscapes, atOnly, filename, + line, removeEmpty, true); + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + // Messaging here would be *very* verbose. + case cmPolicies::NEW: + mtype = this->ExpandVariablesInStringNew(errorstr, source, escapeQuotes, + noEscapes, atOnly, filename, + line, replaceAt); + break; + } + + // If it's an error in either case, just report the error... + if (mtype != MessageType::LOG) { + if (mtype == MessageType::FATAL_ERROR) { + cmSystemTools::SetFatalErrorOccurred(); + } + this->IssueMessage(mtype, errorstr); + } + // ...otherwise, see if there's a difference that needs to be warned about. + else if (compareResults && (newResult != source || newError != mtype)) { + std::string msg = + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0053), '\n'); + + std::string msg_input = original; + cmSystemTools::ReplaceString(msg_input, "\n", "\n "); + msg += "For input:\n '"; + msg += msg_input; + msg += "'\n"; + + std::string msg_old = source; + cmSystemTools::ReplaceString(msg_old, "\n", "\n "); + msg += "the old evaluation rules produce:\n '"; + msg += msg_old; + msg += "'\n"; + + if (newError == mtype) { + std::string msg_new = newResult; + cmSystemTools::ReplaceString(msg_new, "\n", "\n "); + msg += "but the new evaluation rules produce:\n '"; + msg += msg_new; + msg += "'\n"; + } else { + std::string msg_err = newErrorstr; + cmSystemTools::ReplaceString(msg_err, "\n", "\n "); + msg += "but the new evaluation rules produce an error:\n "; + msg += msg_err; + msg += "\n"; + } + + msg += + "Using the old result for compatibility since the policy is not set."; + + this->IssueMessage(MessageType::AUTHOR_WARNING, msg); + } + + return source; +} + +MessageType cmMakefile::ExpandVariablesInStringOld( + std::string& errorstr, std::string& source, bool escapeQuotes, + bool noEscapes, bool atOnly, const char* filename, long line, + bool removeEmpty, bool replaceAt) const +{ + // Fast path strings without any special characters. + if (source.find_first_of("$@\\") == std::string::npos) { + return MessageType::LOG; + } + + // Special-case the @ONLY mode. + if (atOnly) { + // Store an original copy of the input. + std::string input = source; + + // Start with empty output. + source.clear(); + + // Look for one @VAR@ at a time. + const char* in = input.c_str(); + while (this->cmAtVarRegex.find(in)) { + // Get the range of the string to replace. + const char* first = in + this->cmAtVarRegex.start(); + const char* last = in + this->cmAtVarRegex.end(); + + // Store the unchanged part of the string now. + source.append(in, first - in); + + // Lookup the definition of VAR. + std::string var(first + 1, last - first - 2); + if (cmValue val = this->GetDefinition(var)) { + // Store the value in the output escaping as requested. + if (escapeQuotes) { + source.append(cmEscapeQuotes(*val)); + } else { + source.append(*val); + } + } + + // Continue looking for @VAR@ further along the string. + in = last; + } + + // Append the rest of the unchanged part of the string. + source.append(in); + + return MessageType::LOG; + } + + // This method replaces ${VAR} and @VAR@ where VAR is looked up + // with GetDefinition(), if not found in the map, nothing is expanded. + // It also supports the $ENV{VAR} syntax where VAR is looked up in + // the current environment variables. + + cmCommandArgumentParserHelper parser; + parser.SetMakefile(this); + parser.SetLineFile(line, filename); + parser.SetEscapeQuotes(escapeQuotes); + parser.SetNoEscapeMode(noEscapes); + parser.SetReplaceAtSyntax(replaceAt); + parser.SetRemoveEmpty(removeEmpty); + int res = parser.ParseString(source, 0); + const char* emsg = parser.GetError(); + MessageType mtype = MessageType::LOG; + if (res && !emsg[0]) { + source = parser.GetResult(); + } else { + // Construct the main error message. + std::ostringstream error; + error << "Syntax error in cmake code "; + if (filename && line > 0) { + // This filename and line number may be more specific than the + // command context because one command invocation can have + // arguments on multiple lines. + error << "at\n" + << " " << filename << ":" << line << "\n"; + } + error << "when parsing string\n" + << " " << source << "\n"; + error << emsg; + + // If the parser failed ("res" is false) then this is a real + // argument parsing error, so the policy applies. Otherwise the + // parser reported an error message without failing because the + // helper implementation is unhappy, which has always reported an + // error. + mtype = MessageType::FATAL_ERROR; + if (!res) { + // This is a real argument parsing error. Use policy CMP0010 to + // decide whether it is an error. + switch (this->GetPolicyStatus(cmPolicies::CMP0010)) { + case cmPolicies::WARN: + error << "\n" << cmPolicies::GetPolicyWarning(cmPolicies::CMP0010); + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to just warn and continue. + mtype = MessageType::AUTHOR_WARNING; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + error << "\n" + << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0010); + break; + case cmPolicies::NEW: + // NEW behavior is to report the error. + break; + } + } + errorstr = error.str(); + } + return mtype; +} + +enum t_domain +{ + NORMAL, + ENVIRONMENT, + CACHE +}; + +struct t_lookup +{ + t_domain domain = NORMAL; + size_t loc = 0; +}; + +bool cmMakefile::IsProjectFile(const char* filename) const +{ + return cmSystemTools::IsSubDirectory(filename, this->GetHomeDirectory()) || + (cmSystemTools::IsSubDirectory(filename, this->GetHomeOutputDirectory()) && + !cmSystemTools::IsSubDirectory(filename, "/CMakeFiles")); +} + +int cmMakefile::GetRecursionDepth() const +{ + return this->RecursionDepth; +} + +void cmMakefile::SetRecursionDepth(int recursionDepth) +{ + this->RecursionDepth = recursionDepth; +} + +std::string cmMakefile::NewDeferId() const +{ + return this->GetGlobalGenerator()->NewDeferId(); +} + +bool cmMakefile::DeferCall(std::string id, std::string file, + cmListFileFunction lff) +{ + if (!this->Defer) { + return false; + } + this->Defer->Commands.emplace_back( + DeferCommand{ std::move(id), std::move(file), std::move(lff) }); + return true; +} + +bool cmMakefile::DeferCancelCall(std::string const& id) +{ + if (!this->Defer) { + return false; + } + for (DeferCommand& dc : this->Defer->Commands) { + if (dc.Id == id) { + dc.Id.clear(); + } + } + return true; +} + +cm::optional<std::string> cmMakefile::DeferGetCallIds() const +{ + cm::optional<std::string> ids; + if (this->Defer) { + ids = cmJoin( + cmMakeRange(this->Defer->Commands) + .filter([](DeferCommand const& dc) -> bool { return !dc.Id.empty(); }) + .transform( + [](DeferCommand const& dc) -> std::string const& { return dc.Id; }), + ";"); + } + return ids; +} + +cm::optional<std::string> cmMakefile::DeferGetCall(std::string const& id) const +{ + cm::optional<std::string> call; + if (this->Defer) { + std::string tmp; + for (DeferCommand const& dc : this->Defer->Commands) { + if (dc.Id == id) { + tmp = dc.Command.OriginalName(); + for (cmListFileArgument const& arg : dc.Command.Arguments()) { + tmp = cmStrCat(tmp, ';', arg.Value); + } + break; + } + } + call = std::move(tmp); + } + return call; +} + +MessageType cmMakefile::ExpandVariablesInStringNew( + std::string& errorstr, std::string& source, bool escapeQuotes, + bool noEscapes, bool atOnly, const char* filename, long line, + bool replaceAt) const +{ + // This method replaces ${VAR} and @VAR@ where VAR is looked up + // with GetDefinition(), if not found in the map, nothing is expanded. + // It also supports the $ENV{VAR} syntax where VAR is looked up in + // the current environment variables. + + const char* in = source.c_str(); + const char* last = in; + std::string result; + result.reserve(source.size()); + std::vector<t_lookup> openstack; + bool error = false; + bool done = false; + MessageType mtype = MessageType::LOG; + + cmState* state = this->GetCMakeInstance()->GetState(); + + static const std::string lineVar = "CMAKE_CURRENT_LIST_LINE"; + do { + char inc = *in; + switch (inc) { + case '}': + if (!openstack.empty()) { + t_lookup var = openstack.back(); + openstack.pop_back(); + result.append(last, in - last); + std::string const& lookup = result.substr(var.loc); + cmValue value = nullptr; + std::string varresult; + std::string svalue; + switch (var.domain) { + case NORMAL: + if (filename && lookup == lineVar) { + cmListFileContext const& top = this->Backtrace.Top(); + if (top.DeferId) { + varresult = cmStrCat("DEFERRED:"_s, *top.DeferId); + } else { + varresult = std::to_string(line); + } + } else { + value = this->GetDefinition(lookup); + } + break; + case ENVIRONMENT: + if (cmSystemTools::GetEnv(lookup, svalue)) { + value = cmValue(svalue); + } + break; + case CACHE: + value = state->GetCacheEntryValue(lookup); + break; + } + // Get the string we're meant to append to. + if (value) { + if (escapeQuotes) { + varresult = cmEscapeQuotes(*value); + } else { + varresult = *value; + } + } else if (!this->SuppressSideEffects) { + this->MaybeWarnUninitialized(lookup, filename); + } + result.replace(var.loc, result.size() - var.loc, varresult); + // Start looking from here on out. + last = in + 1; + } + break; + case '$': + if (!atOnly) { + t_lookup lookup; + const char* next = in + 1; + const char* start = nullptr; + char nextc = *next; + if (nextc == '{') { + // Looking for a variable. + start = in + 2; + lookup.domain = NORMAL; + } else if (nextc == '<') { + } else if (!nextc) { + result.append(last, next - last); + last = next; + } else if (cmHasLiteralPrefix(next, "ENV{")) { + // Looking for an environment variable. + start = in + 5; + lookup.domain = ENVIRONMENT; + } else if (cmHasLiteralPrefix(next, "CACHE{")) { + // Looking for a cache variable. + start = in + 7; + lookup.domain = CACHE; + } else { + if (this->cmNamedCurly.find(next)) { + errorstr = "Syntax $" + + std::string(next, this->cmNamedCurly.end()) + + "{} is not supported. Only ${}, $ENV{}, " + "and $CACHE{} are allowed."; + mtype = MessageType::FATAL_ERROR; + error = true; + } + } + if (start) { + result.append(last, in - last); + last = start; + in = start - 1; + lookup.loc = result.size(); + openstack.push_back(lookup); + } + break; + } + CM_FALLTHROUGH; + case '\\': + if (!noEscapes) { + const char* next = in + 1; + char nextc = *next; + if (nextc == 't') { + result.append(last, in - last); + result.append("\t"); + last = next + 1; + } else if (nextc == 'n') { + result.append(last, in - last); + result.append("\n"); + last = next + 1; + } else if (nextc == 'r') { + result.append(last, in - last); + result.append("\r"); + last = next + 1; + } else if (nextc == ';' && openstack.empty()) { + // Handled in ExpandListArgument; pass the backslash literally. + } else if (isalnum(nextc) || nextc == '\0') { + errorstr += "Invalid character escape '\\"; + if (nextc) { + errorstr += nextc; + errorstr += "'."; + } else { + errorstr += "' (at end of input)."; + } + error = true; + } else { + // Take what we've found so far, skipping the escape character. + result.append(last, in - last); + // Start tracking from the next character. + last = in + 1; + } + // Skip the next character since it was escaped, but don't read past + // the end of the string. + if (*last) { + ++in; + } + } + break; + case '\n': + // Onto the next line. + ++line; + break; + case '\0': + done = true; + break; + case '@': + if (replaceAt) { + const char* nextAt = strchr(in + 1, '@'); + if (nextAt && nextAt != in + 1 && + nextAt == + in + 1 + + strspn(in + 1, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789/_.+-")) { + std::string variable(in + 1, nextAt - in - 1); + + std::string varresult; + if (filename && variable == lineVar) { + varresult = std::to_string(line); + } else { + cmValue def = this->GetDefinition(variable); + if (def) { + varresult = *def; + } else if (!this->SuppressSideEffects) { + this->MaybeWarnUninitialized(variable, filename); + } + } + + if (escapeQuotes) { + varresult = cmEscapeQuotes(varresult); + } + // Skip over the variable. + result.append(last, in - last); + result.append(varresult); + in = nextAt; + last = in + 1; + break; + } + } + // Failed to find a valid @ expansion; treat it as literal. + CM_FALLTHROUGH; + default: { + if (!openstack.empty() && + !(isalnum(inc) || inc == '_' || inc == '/' || inc == '.' || + inc == '+' || inc == '-')) { + errorstr += "Invalid character (\'"; + errorstr += inc; + result.append(last, in - last); + errorstr += "\') in a variable name: " + "'" + + result.substr(openstack.back().loc) + "'"; + mtype = MessageType::FATAL_ERROR; + error = true; + } + break; + } + } + // Look at the next character. + } while (!error && !done && *++in); + + // Check for open variable references yet. + if (!error && !openstack.empty()) { + // There's an open variable reference waiting. Policy CMP0010 flags + // whether this is an error or not. The new parser now enforces + // CMP0010 as well. + errorstr += "There is an unterminated variable reference."; + error = true; + } + + if (error) { + std::ostringstream emsg; + emsg << "Syntax error in cmake code "; + if (filename) { + // This filename and line number may be more specific than the + // command context because one command invocation can have + // arguments on multiple lines. + emsg << "at\n" + << " " << filename << ":" << line << "\n"; + } + emsg << "when parsing string\n" + << " " << source << "\n"; + emsg << errorstr; + mtype = MessageType::FATAL_ERROR; + errorstr = emsg.str(); + } else { + // Append the rest of the unchanged part of the string. + result.append(last); + + source = result; + } + + return mtype; +} + +void cmMakefile::RemoveVariablesInString(std::string& source, + bool atOnly) const +{ + if (!atOnly) { + cmsys::RegularExpression var("(\\${[A-Za-z_0-9]*})"); + while (var.find(source)) { + source.erase(var.start(), var.end() - var.start()); + } + } + + if (!atOnly) { + cmsys::RegularExpression varb("(\\$ENV{[A-Za-z_0-9]*})"); + while (varb.find(source)) { + source.erase(varb.start(), varb.end() - varb.start()); + } + } + cmsys::RegularExpression var2("(@[A-Za-z_0-9]*@)"); + while (var2.find(source)) { + source.erase(var2.start(), var2.end() - var2.start()); + } +} + +void cmMakefile::InitCMAKE_CONFIGURATION_TYPES(std::string const& genDefault) +{ + if (this->GetDefinition("CMAKE_CONFIGURATION_TYPES")) { + return; + } + std::string initConfigs; + if (this->GetCMakeInstance()->GetIsInTryCompile() || + !cmSystemTools::GetEnv("CMAKE_CONFIGURATION_TYPES", initConfigs)) { + initConfigs = genDefault; + } + this->AddCacheDefinition( + "CMAKE_CONFIGURATION_TYPES", initConfigs, + "Semicolon separated list of supported configuration types, " + "only supports Debug, Release, MinSizeRel, and RelWithDebInfo, " + "anything else will be ignored.", + cmStateEnums::STRING); +} + +std::string cmMakefile::GetDefaultConfiguration() const +{ + if (this->GetGlobalGenerator()->IsMultiConfig()) { + return std::string{}; + } + return this->GetSafeDefinition("CMAKE_BUILD_TYPE"); +} + +std::vector<std::string> cmMakefile::GetGeneratorConfigs( + GeneratorConfigQuery mode) const +{ + std::vector<std::string> configs; + if (this->GetGlobalGenerator()->IsMultiConfig()) { + this->GetDefExpandList("CMAKE_CONFIGURATION_TYPES", configs); + } else if (mode != cmMakefile::OnlyMultiConfig) { + const std::string& buildType = this->GetSafeDefinition("CMAKE_BUILD_TYPE"); + if (!buildType.empty()) { + configs.emplace_back(buildType); + } + } + if (mode == cmMakefile::IncludeEmptyConfig && configs.empty()) { + configs.emplace_back(); + } + return configs; +} + +bool cmMakefile::IsFunctionBlocked(const cmListFileFunction& lff, + cmExecutionStatus& status) +{ + // if there are no blockers get out of here + if (this->FunctionBlockers.empty()) { + return false; + } + + return this->FunctionBlockers.top()->IsFunctionBlocked(lff, status); +} + +void cmMakefile::PushFunctionBlockerBarrier() +{ + this->FunctionBlockerBarriers.push_back(this->FunctionBlockers.size()); +} + +void cmMakefile::PopFunctionBlockerBarrier(bool reportError) +{ + // Remove any extra entries pushed on the barrier. + FunctionBlockersType::size_type barrier = + this->FunctionBlockerBarriers.back(); + while (this->FunctionBlockers.size() > barrier) { + std::unique_ptr<cmFunctionBlocker> fb( + std::move(this->FunctionBlockers.top())); + this->FunctionBlockers.pop(); + if (reportError) { + // Report the context in which the unclosed block was opened. + cmListFileContext const& lfc = fb->GetStartingContext(); + std::ostringstream e; + /* clang-format off */ + e << "A logical block opening on the line\n" + << " " << lfc << "\n" + << "is not closed."; + /* clang-format on */ + this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + reportError = false; + } + } + + // Remove the barrier. + this->FunctionBlockerBarriers.pop_back(); +} + +void cmMakefile::PushLoopBlock() +{ + assert(!this->LoopBlockCounter.empty()); + this->LoopBlockCounter.top()++; +} + +void cmMakefile::PopLoopBlock() +{ + assert(!this->LoopBlockCounter.empty()); + assert(this->LoopBlockCounter.top() > 0); + this->LoopBlockCounter.top()--; +} + +void cmMakefile::PushLoopBlockBarrier() +{ + this->LoopBlockCounter.push(0); +} + +void cmMakefile::PopLoopBlockBarrier() +{ + assert(!this->LoopBlockCounter.empty()); + assert(this->LoopBlockCounter.top() == 0); + this->LoopBlockCounter.pop(); +} + +bool cmMakefile::IsLoopBlock() const +{ + assert(!this->LoopBlockCounter.empty()); + return !this->LoopBlockCounter.empty() && this->LoopBlockCounter.top() > 0; +} + +bool cmMakefile::ExpandArguments(std::vector<cmListFileArgument> const& inArgs, + std::vector<std::string>& outArgs) const +{ + std::string const& filename = this->GetBacktrace().Top().FilePath; + std::string value; + outArgs.reserve(inArgs.size()); + for (cmListFileArgument const& i : inArgs) { + // No expansion in a bracket argument. + if (i.Delim == cmListFileArgument::Bracket) { + outArgs.push_back(i.Value); + continue; + } + // Expand the variables in the argument. + value = i.Value; + this->ExpandVariablesInString(value, false, false, false, filename.c_str(), + i.Line, false, false); + + // If the argument is quoted, it should be one argument. + // Otherwise, it may be a list of arguments. + if (i.Delim == cmListFileArgument::Quoted) { + outArgs.push_back(value); + } else { + cmExpandList(value, outArgs); + } + } + return !cmSystemTools::GetFatalErrorOccurred(); +} + +bool cmMakefile::ExpandArguments( + std::vector<cmListFileArgument> const& inArgs, + std::vector<cmExpandedCommandArgument>& outArgs) const +{ + std::string const& filename = this->GetBacktrace().Top().FilePath; + std::string value; + outArgs.reserve(inArgs.size()); + for (cmListFileArgument const& i : inArgs) { + // No expansion in a bracket argument. + if (i.Delim == cmListFileArgument::Bracket) { + outArgs.emplace_back(i.Value, true); + continue; + } + // Expand the variables in the argument. + value = i.Value; + this->ExpandVariablesInString(value, false, false, false, filename.c_str(), + i.Line, false, false); + + // If the argument is quoted, it should be one argument. + // Otherwise, it may be a list of arguments. + if (i.Delim == cmListFileArgument::Quoted) { + outArgs.emplace_back(value, true); + } else { + std::vector<std::string> stringArgs = cmExpandedList(value); + for (std::string const& stringArg : stringArgs) { + outArgs.emplace_back(stringArg, false); + } + } + } + return !cmSystemTools::GetFatalErrorOccurred(); +} + +void cmMakefile::AddFunctionBlocker(std::unique_ptr<cmFunctionBlocker> fb) +{ + if (!this->ExecutionStatusStack.empty()) { + // Record the context in which the blocker is created. + fb->SetStartingContext(this->Backtrace.Top()); + } + + this->FunctionBlockers.push(std::move(fb)); +} + +std::unique_ptr<cmFunctionBlocker> cmMakefile::RemoveFunctionBlocker() +{ + assert(!this->FunctionBlockers.empty()); + assert(this->FunctionBlockerBarriers.empty() || + this->FunctionBlockers.size() > this->FunctionBlockerBarriers.back()); + + auto b = std::move(this->FunctionBlockers.top()); + this->FunctionBlockers.pop(); + return b; +} + +std::string const& cmMakefile::GetHomeDirectory() const +{ + return this->GetCMakeInstance()->GetHomeDirectory(); +} + +std::string const& cmMakefile::GetHomeOutputDirectory() const +{ + return this->GetCMakeInstance()->GetHomeOutputDirectory(); +} + +void cmMakefile::SetScriptModeFile(std::string const& scriptfile) +{ + this->AddDefinition("CMAKE_SCRIPT_MODE_FILE", scriptfile); +} + +void cmMakefile::SetArgcArgv(const std::vector<std::string>& args) +{ + this->AddDefinition("CMAKE_ARGC", std::to_string(args.size())); + // this->MarkVariableAsUsed("CMAKE_ARGC"); + + for (unsigned int t = 0; t < args.size(); ++t) { + std::ostringstream tmpStream; + tmpStream << "CMAKE_ARGV" << t; + this->AddDefinition(tmpStream.str(), args[t]); + // this->MarkVariableAsUsed(tmpStream.str().c_str()); + } +} + +cmSourceFile* cmMakefile::GetSource(const std::string& sourceName, + cmSourceFileLocationKind kind) const +{ + // First check "Known" paths (avoids the creation of cmSourceFileLocation) + if (kind == cmSourceFileLocationKind::Known) { + auto sfsi = this->KnownFileSearchIndex.find(sourceName); + if (sfsi != this->KnownFileSearchIndex.end()) { + return sfsi->second; + } + } + + cmSourceFileLocation sfl(this, sourceName, kind); + auto name = this->GetCMakeInstance()->StripExtension(sfl.GetName()); +#if defined(_WIN32) || defined(__APPLE__) + name = cmSystemTools::LowerCase(name); +#endif + auto sfsi = this->SourceFileSearchIndex.find(name); + if (sfsi != this->SourceFileSearchIndex.end()) { + for (auto* sf : sfsi->second) { + if (sf->Matches(sfl)) { + return sf; + } + } + } + return nullptr; +} + +cmSourceFile* cmMakefile::CreateSource(const std::string& sourceName, + bool generated, + cmSourceFileLocationKind kind) +{ + auto sf = cm::make_unique<cmSourceFile>(this, sourceName, generated, kind); + auto name = + this->GetCMakeInstance()->StripExtension(sf->GetLocation().GetName()); +#if defined(_WIN32) || defined(__APPLE__) + name = cmSystemTools::LowerCase(name); +#endif + this->SourceFileSearchIndex[name].push_back(sf.get()); + // for "Known" paths add direct lookup (used for faster lookup in GetSource) + if (kind == cmSourceFileLocationKind::Known) { + this->KnownFileSearchIndex[sourceName] = sf.get(); + } + + this->SourceFiles.push_back(std::move(sf)); + + return this->SourceFiles.back().get(); +} + +cmSourceFile* cmMakefile::GetOrCreateSource(const std::string& sourceName, + bool generated, + cmSourceFileLocationKind kind) +{ + if (cmSourceFile* esf = this->GetSource(sourceName, kind)) { + return esf; + } + return this->CreateSource(sourceName, generated, kind); +} + +cmSourceFile* cmMakefile::GetOrCreateGeneratedSource( + const std::string& sourceName) +{ + cmSourceFile* sf = + this->GetOrCreateSource(sourceName, true, cmSourceFileLocationKind::Known); + sf->MarkAsGenerated(); // In case we did not create the source file. + return sf; +} + +void cmMakefile::CreateGeneratedOutputs( + const std::vector<std::string>& outputs) +{ + for (std::string const& o : outputs) { + if (cmGeneratorExpression::Find(o) == std::string::npos) { + this->GetOrCreateGeneratedSource(o); + } + } +} + +void cmMakefile::AddTargetObject(std::string const& tgtName, + std::string const& objFile) +{ + cmSourceFile* sf = + this->GetOrCreateSource(objFile, true, cmSourceFileLocationKind::Known); + sf->SetObjectLibrary(tgtName); + sf->SetProperty("EXTERNAL_OBJECT", "1"); +#if !defined(CMAKE_BOOTSTRAP) + this->SourceGroups[this->ObjectLibrariesSourceGroupIndex].AddGroupFile( + sf->ResolveFullPath()); +#endif +} + +void cmMakefile::EnableLanguage(std::vector<std::string> const& languages, + bool optional) +{ + if (this->DeferRunning) { + this->IssueMessage( + MessageType::FATAL_ERROR, + "Languages may not be enabled during deferred execution."); + return; + } + if (const char* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) { + this->AddDefinition("CMAKE_CFG_INTDIR", def); + } + + std::vector<std::string> unique_languages; + { + std::vector<std::string> duplicate_languages; + for (std::string const& language : languages) { + if (!cm::contains(unique_languages, language)) { + unique_languages.push_back(language); + } else if (!cm::contains(duplicate_languages, language)) { + duplicate_languages.push_back(language); + } + } + if (!duplicate_languages.empty()) { + auto quantity = duplicate_languages.size() == 1 ? std::string(" has") + : std::string("s have"); + this->IssueMessage(MessageType::AUTHOR_WARNING, + "Languages to be enabled may not be specified more " + "than once at the same time. The following language" + + quantity + " been specified multiple times: " + + cmJoin(duplicate_languages, ", ")); + } + } + + // If RC is explicitly listed we need to do it after other languages. + // On some platforms we enable RC implicitly while enabling others. + // Do not let that look like recursive enable_language(RC). + std::vector<std::string> languages_without_RC; + std::vector<std::string> languages_for_RC; + languages_without_RC.reserve(unique_languages.size()); + for (std::string const& language : unique_languages) { + if (language == "RC") { + languages_for_RC.push_back(language); + } else { + languages_without_RC.push_back(language); + } + } + if (!languages_without_RC.empty()) { + this->GetGlobalGenerator()->EnableLanguage(languages_without_RC, this, + optional); + } + if (!languages_for_RC.empty()) { + this->GetGlobalGenerator()->EnableLanguage(languages_for_RC, this, + optional); + } +} + +int cmMakefile::TryCompile(const std::string& srcdir, + const std::string& bindir, + const std::string& projectName, + const std::string& targetName, bool fast, int jobs, + const std::vector<std::string>* cmakeArgs, + std::string& output) +{ + this->IsSourceFileTryCompile = fast; + // does the binary directory exist ? If not create it... + if (!cmSystemTools::FileIsDirectory(bindir)) { + cmSystemTools::MakeDirectory(bindir); + } + + // change to the tests directory and run cmake + // use the cmake object instead of calling cmake + cmWorkingDirectory workdir(bindir); + if (workdir.Failed()) { + this->IssueMessage(MessageType::FATAL_ERROR, + "Failed to set working directory to " + bindir + " : " + + std::strerror(workdir.GetLastResult())); + cmSystemTools::SetFatalErrorOccurred(); + this->IsSourceFileTryCompile = false; + return 1; + } + + // make sure the same generator is used + // use this program as the cmake to be run, it should not + // be run that way but the cmake object requires a vailid path + cmake cm(cmake::RoleProject, cmState::Project, + cmState::ProjectKind::TryCompile); + auto gg = cm.CreateGlobalGenerator(this->GetGlobalGenerator()->GetName()); + if (!gg) { + this->IssueMessage(MessageType::INTERNAL_ERROR, + "Global generator '" + + this->GetGlobalGenerator()->GetName() + + "' could not be created."); + cmSystemTools::SetFatalErrorOccurred(); + this->IsSourceFileTryCompile = false; + return 1; + } + gg->RecursionDepth = this->RecursionDepth; + cm.SetGlobalGenerator(std::move(gg)); + + // copy trace state + cm.SetTraceRedirect(this->GetCMakeInstance()); + + // do a configure + cm.SetHomeDirectory(srcdir); + cm.SetHomeOutputDirectory(bindir); + cm.SetGeneratorInstance(this->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE")); + cm.SetGeneratorPlatform(this->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM")); + cm.SetGeneratorToolset(this->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET")); + cm.LoadCache(); + if (!cm.GetGlobalGenerator()->IsMultiConfig()) { + if (cmValue config = + this->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION")) { + // Tell the single-configuration generator which one to use. + // Add this before the user-provided CMake arguments in case + // one of the arguments is -DCMAKE_BUILD_TYPE=... + cm.AddCacheEntry("CMAKE_BUILD_TYPE", config, "Build configuration", + cmStateEnums::STRING); + } + } + cmValue recursionDepth = + this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH"); + if (recursionDepth) { + cm.AddCacheEntry("CMAKE_MAXIMUM_RECURSION_DEPTH", recursionDepth, + "Maximum recursion depth", cmStateEnums::STRING); + } + // if cmake args were provided then pass them in + if (cmakeArgs) { + // FIXME: Workaround to ignore unused CLI variables in try-compile. + // + // Ideally we should use SetArgs for options like --no-warn-unused-cli. + // However, there is a subtle problem when certain arguments are passed to + // a macro wrapping around try_compile or try_run that does not escape + // semicolons in its parameters but just passes ${ARGV} or ${ARGN}. In + // this case a list argument like "-DVAR=a;b" gets split into multiple + // cmake arguments "-DVAR=a" and "b". Currently SetCacheArgs ignores + // argument "b" and uses just "-DVAR=a", leading to a subtle bug in that + // the try_compile or try_run does not get the proper value of VAR. If we + // call SetArgs here then it would treat "b" as the source directory and + // cause an error such as "The source directory .../CMakeFiles/CMakeTmp/b + // does not exist", thus breaking the try_compile or try_run completely. + // + // Strictly speaking the bug is in the wrapper macro because the CMake + // language has always flattened nested lists and the macro should escape + // the semicolons in its arguments before forwarding them. However, this + // bug is so subtle that projects typically work anyway, usually because + // the value VAR=a is sufficient for the try_compile or try_run to get the + // correct result. Calling SetArgs here would break such projects that + // previously built. Instead we work around the issue by never reporting + // unused arguments and ignoring options such as --no-warn-unused-cli. + cm.SetWarnUnusedCli(false); + // cm.SetArgs(*cmakeArgs, true); + + cm.SetCacheArgs(*cmakeArgs); + } + // to save time we pass the EnableLanguage info directly + cm.GetGlobalGenerator()->EnableLanguagesFromGenerator( + this->GetGlobalGenerator(), this); + if (this->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { + cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", "TRUE", "", + cmStateEnums::INTERNAL); + } else { + cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", "FALSE", "", + cmStateEnums::INTERNAL); + } + if (cm.Configure() != 0) { + this->IssueMessage(MessageType::FATAL_ERROR, + "Failed to configure test project build system."); + cmSystemTools::SetFatalErrorOccurred(); + this->IsSourceFileTryCompile = false; + return 1; + } + + if (cm.Generate() != 0) { + this->IssueMessage(MessageType::FATAL_ERROR, + "Failed to generate test project build system."); + cmSystemTools::SetFatalErrorOccurred(); + this->IsSourceFileTryCompile = false; + return 1; + } + + // finally call the generator to actually build the resulting project + int ret = this->GetGlobalGenerator()->TryCompile( + jobs, srcdir, bindir, projectName, targetName, fast, output, this); + + this->IsSourceFileTryCompile = false; + return ret; +} + +bool cmMakefile::GetIsSourceFileTryCompile() const +{ + return this->IsSourceFileTryCompile; +} + +cmake* cmMakefile::GetCMakeInstance() const +{ + return this->GlobalGenerator->GetCMakeInstance(); +} + +cmMessenger* cmMakefile::GetMessenger() const +{ + return this->GetCMakeInstance()->GetMessenger(); +} + +cmGlobalGenerator* cmMakefile::GetGlobalGenerator() const +{ + return this->GlobalGenerator; +} + +#ifndef CMAKE_BOOTSTRAP +cmVariableWatch* cmMakefile::GetVariableWatch() const +{ + if (this->GetCMakeInstance() && + this->GetCMakeInstance()->GetVariableWatch()) { + return this->GetCMakeInstance()->GetVariableWatch(); + } + return nullptr; +} +#endif + +cmState* cmMakefile::GetState() const +{ + return this->GetCMakeInstance()->GetState(); +} + +void cmMakefile::DisplayStatus(const std::string& message, float s) const +{ + cmake* cm = this->GetCMakeInstance(); + if (cm->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + // don't output any STATUS message in FIND_PACKAGE_MODE, since they will + // directly be fed to the compiler, which will be confused. + return; + } + cm->UpdateProgress(message, s); +} + +std::string cmMakefile::GetModulesFile(const std::string& filename, + bool& system, bool debug, + std::string& debugBuffer) const +{ + std::string result; + + // We search the module always in CMAKE_ROOT and in CMAKE_MODULE_PATH, + // and then decide based on the policy setting which one to return. + // See CMP0017 for more details. + // The specific problem was that KDE 4.5.0 installs a + // FindPackageHandleStandardArgs.cmake which doesn't have the new features + // of FPHSA.cmake introduced in CMake 2.8.3 yet, and by setting + // CMAKE_MODULE_PATH also e.g. FindZLIB.cmake from cmake included + // FPHSA.cmake from kdelibs and not from CMake, and tried to use the + // new features, which were not there in the version from kdelibs, and so + // failed (" + std::string moduleInCMakeRoot; + std::string moduleInCMakeModulePath; + + // Always search in CMAKE_MODULE_PATH: + cmValue cmakeModulePath = this->GetDefinition("CMAKE_MODULE_PATH"); + if (cmakeModulePath) { + std::vector<std::string> modulePath = cmExpandedList(*cmakeModulePath); + + // Look through the possible module directories. + for (std::string itempl : modulePath) { + cmSystemTools::ConvertToUnixSlashes(itempl); + itempl += "/"; + itempl += filename; + if (cmSystemTools::FileExists(itempl)) { + moduleInCMakeModulePath = itempl; + break; + } + if (debug) { + debugBuffer = cmStrCat(debugBuffer, " ", itempl, "\n"); + } + } + } + + // Always search in the standard modules location. + moduleInCMakeRoot = + cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/", filename); + cmSystemTools::ConvertToUnixSlashes(moduleInCMakeRoot); + if (!cmSystemTools::FileExists(moduleInCMakeRoot)) { + if (debug) { + debugBuffer = cmStrCat(debugBuffer, " ", moduleInCMakeRoot, "\n"); + } + moduleInCMakeRoot.clear(); + } + + // Normally, prefer the files found in CMAKE_MODULE_PATH. Only when the file + // from which we are being called is located itself in CMAKE_ROOT, then + // prefer results from CMAKE_ROOT depending on the policy setting. + system = false; + result = moduleInCMakeModulePath; + if (result.empty()) { + system = true; + result = moduleInCMakeRoot; + } + + if (!moduleInCMakeModulePath.empty() && !moduleInCMakeRoot.empty()) { + cmValue currentFile = this->GetDefinition("CMAKE_CURRENT_LIST_FILE"); + std::string mods = cmSystemTools::GetCMakeRoot() + "/Modules/"; + if (currentFile && cmSystemTools::IsSubDirectory(*currentFile, mods)) { + switch (this->GetPolicyStatus(cmPolicies::CMP0017)) { + case cmPolicies::WARN: { + std::ostringstream e; + /* clang-format off */ + e << "File " << *currentFile << " includes " + << moduleInCMakeModulePath + << " (found via CMAKE_MODULE_PATH) which shadows " + << moduleInCMakeRoot << ". This may cause errors later on .\n" + << cmPolicies::GetPolicyWarning(cmPolicies::CMP0017); + /* clang-format on */ + + this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + CM_FALLTHROUGH; + } + case cmPolicies::OLD: + system = false; + result = moduleInCMakeModulePath; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + system = true; + result = moduleInCMakeRoot; + break; + } + } + } + + return result; +} + +void cmMakefile::ConfigureString(const std::string& input, std::string& output, + bool atOnly, bool escapeQuotes) const +{ + // Split input to handle one line at a time. + std::string::const_iterator lineStart = input.begin(); + while (lineStart != input.end()) { + // Find the end of this line. + std::string::const_iterator lineEnd = lineStart; + while (lineEnd != input.end() && *lineEnd != '\n') { + ++lineEnd; + } + + // Copy the line. + std::string line(lineStart, lineEnd); + + // Skip the newline character. + bool haveNewline = (lineEnd != input.end()); + if (haveNewline) { + ++lineEnd; + } + + // Replace #cmakedefine instances. + if (this->cmDefineRegex.find(line)) { + cmValue def = this->GetDefinition(this->cmDefineRegex.match(2)); + if (!cmIsOff(def)) { + const std::string indentation = this->cmDefineRegex.match(1); + cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine", + "#" + indentation + "define"); + output += line; + } else { + output += "/* #undef "; + output += this->cmDefineRegex.match(2); + output += " */"; + } + } else if (this->cmDefine01Regex.find(line)) { + const std::string indentation = this->cmDefine01Regex.match(1); + cmValue def = this->GetDefinition(this->cmDefine01Regex.match(2)); + cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine01", + "#" + indentation + "define"); + output += line; + if (!cmIsOff(def)) { + output += " 1"; + } else { + output += " 0"; + } + } else { + output += line; + } + + if (haveNewline) { + output += "\n"; + } + + // Move to the next line. + lineStart = lineEnd; + } + + // Perform variable replacements. + const char* filename = nullptr; + long lineNumber = -1; + if (!this->Backtrace.Empty()) { + const auto& currentTrace = this->Backtrace.Top(); + filename = currentTrace.FilePath.c_str(); + lineNumber = currentTrace.Line; + } + this->ExpandVariablesInString(output, escapeQuotes, true, atOnly, filename, + lineNumber, true, true); +} + +int cmMakefile::ConfigureFile(const std::string& infile, + const std::string& outfile, bool copyonly, + bool atOnly, bool escapeQuotes, + mode_t permissions, cmNewLineStyle newLine) +{ + int res = 1; + if (!this->CanIWriteThisFile(outfile)) { + cmSystemTools::Error("Attempt to write file: " + outfile + + " into a source directory."); + return 0; + } + if (!cmSystemTools::FileExists(infile)) { + cmSystemTools::Error("File " + infile + " does not exist."); + return 0; + } + std::string soutfile = outfile; + const std::string& sinfile = infile; + this->AddCMakeDependFile(sinfile); + cmSystemTools::ConvertToUnixSlashes(soutfile); + + // Re-generate if non-temporary outputs are missing. + // when we finalize the configuration we will remove all + // output files that now don't exist. + this->AddCMakeOutputFile(soutfile); + + if (permissions == 0) { + cmSystemTools::GetPermissions(sinfile, permissions); + } + + std::string::size_type pos = soutfile.rfind('/'); + if (pos != std::string::npos) { + std::string path = soutfile.substr(0, pos); + cmSystemTools::MakeDirectory(path); + } + + if (copyonly) { + if (!cmSystemTools::CopyFileIfDifferent(sinfile, soutfile)) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmSystemTools::GetLastSystemError()); + return 0; + } + if (!cmSystemTools::SetPermissions(soutfile, permissions)) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmSystemTools::GetLastSystemError()); + return 0; + } + } else { + std::string newLineCharacters; + std::ios::openmode omode = std::ios::out | std::ios::trunc; + if (newLine.IsValid()) { + newLineCharacters = newLine.GetCharacters(); + omode |= std::ios::binary; + } else { + newLineCharacters = "\n"; + } + std::string tempOutputFile = cmStrCat(soutfile, ".tmp"); + cmsys::ofstream fout(tempOutputFile.c_str(), omode); + if (!fout) { + cmSystemTools::Error("Could not open file for write in copy operation " + + tempOutputFile); + cmSystemTools::ReportLastSystemError(""); + return 0; + } + cmsys::ifstream fin(sinfile.c_str()); + if (!fin) { + cmSystemTools::Error("Could not open file for read in copy operation " + + sinfile); + return 0; + } + + cmsys::FStream::BOM bom = cmsys::FStream::ReadBOM(fin); + if (bom != cmsys::FStream::BOM_None && bom != cmsys::FStream::BOM_UTF8) { + std::ostringstream e; + e << "File starts with a Byte-Order-Mark that is not UTF-8:\n " + << sinfile; + this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return 0; + } + // rewind to copy BOM to output file + fin.seekg(0); + + // now copy input to output and expand variables in the + // input file at the same time + std::string inLine; + std::string outLine; + while (cmSystemTools::GetLineFromStream(fin, inLine)) { + outLine.clear(); + this->ConfigureString(inLine, outLine, atOnly, escapeQuotes); + fout << outLine << newLineCharacters; + } + // close the files before attempting to copy + fin.close(); + fout.close(); + if (!cmSystemTools::CopyFileIfDifferent(tempOutputFile, soutfile)) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmSystemTools::GetLastSystemError()); + res = 0; + } else { + if (!cmSystemTools::SetPermissions(soutfile, permissions)) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmSystemTools::GetLastSystemError()); + res = 0; + } + } + cmSystemTools::RemoveFile(tempOutputFile); + } + return res; +} + +void cmMakefile::SetProperty(const std::string& prop, const char* value) +{ + this->StateSnapshot.GetDirectory().SetProperty(prop, value, this->Backtrace); +} +void cmMakefile::SetProperty(const std::string& prop, cmValue value) +{ + this->StateSnapshot.GetDirectory().SetProperty(prop, value, this->Backtrace); +} + +void cmMakefile::AppendProperty(const std::string& prop, + const std::string& value, bool asString) +{ + this->StateSnapshot.GetDirectory().AppendProperty(prop, value, asString, + this->Backtrace); +} + +cmValue cmMakefile::GetProperty(const std::string& prop) const +{ + // Check for computed properties. + static std::string output; + if (prop == "TESTS") { + std::vector<std::string> keys; + // get list of keys + const auto* t = this; + std::transform( + t->Tests.begin(), t->Tests.end(), std::back_inserter(keys), + [](decltype(t->Tests)::value_type const& pair) { return pair.first; }); + output = cmJoin(keys, ";"); + return cmValue(output); + } + + return this->StateSnapshot.GetDirectory().GetProperty(prop); +} + +cmValue cmMakefile::GetProperty(const std::string& prop, bool chain) const +{ + return this->StateSnapshot.GetDirectory().GetProperty(prop, chain); +} + +bool cmMakefile::GetPropertyAsBool(const std::string& prop) const +{ + return cmIsOn(this->GetProperty(prop)); +} + +std::vector<std::string> cmMakefile::GetPropertyKeys() const +{ + return this->StateSnapshot.GetDirectory().GetPropertyKeys(); +} + +cmTarget* cmMakefile::FindLocalNonAliasTarget(const std::string& name) const +{ + auto i = this->Targets.find(name); + if (i != this->Targets.end()) { + return &i->second; + } + return nullptr; +} + +cmTest* cmMakefile::CreateTest(const std::string& testName) +{ + cmTest* test = this->GetTest(testName); + if (test) { + return test; + } + auto newTest = cm::make_unique<cmTest>(this); + test = newTest.get(); + newTest->SetName(testName); + this->Tests[testName] = std::move(newTest); + return test; +} + +cmTest* cmMakefile::GetTest(const std::string& testName) const +{ + auto mi = this->Tests.find(testName); + if (mi != this->Tests.end()) { + return mi->second.get(); + } + return nullptr; +} + +void cmMakefile::GetTests(const std::string& config, + std::vector<cmTest*>& tests) const +{ + for (const auto& generator : this->GetTestGenerators()) { + if (generator->TestsForConfig(config)) { + tests.push_back(generator->GetTest()); + } + } +} + +void cmMakefile::AddCMakeDependFilesFromUser() +{ + std::vector<std::string> deps; + if (cmValue deps_str = this->GetProperty("CMAKE_CONFIGURE_DEPENDS")) { + cmExpandList(*deps_str, deps); + } + for (std::string const& dep : deps) { + if (cmSystemTools::FileIsFullPath(dep)) { + this->AddCMakeDependFile(dep); + } else { + std::string f = cmStrCat(this->GetCurrentSourceDirectory(), '/', dep); + this->AddCMakeDependFile(f); + } + } +} + +std::string cmMakefile::FormatListFileStack() const +{ + std::vector<std::string> listFiles; + cmStateSnapshot snp = this->StateSnapshot; + while (snp.IsValid()) { + listFiles.push_back(snp.GetExecutionListFile()); + snp = snp.GetCallStackParent(); + } + std::reverse(listFiles.begin(), listFiles.end()); + std::ostringstream tmp; + size_t depth = listFiles.size(); + if (depth > 0) { + auto it = listFiles.end(); + do { + if (depth != listFiles.size()) { + tmp << "\n "; + } + --it; + tmp << "["; + tmp << depth; + tmp << "]\t"; + tmp << *it; + depth--; + } while (it != listFiles.begin()); + } + return tmp.str(); +} + +void cmMakefile::PushScope() +{ + this->StateSnapshot = + this->GetState()->CreateVariableScopeSnapshot(this->StateSnapshot); + this->PushLoopBlockBarrier(); + +#if !defined(CMAKE_BOOTSTRAP) + this->GetGlobalGenerator()->GetFileLockPool().PushFunctionScope(); +#endif +} + +void cmMakefile::PopScope() +{ +#if !defined(CMAKE_BOOTSTRAP) + this->GetGlobalGenerator()->GetFileLockPool().PopFunctionScope(); +#endif + + this->PopLoopBlockBarrier(); + + this->PopSnapshot(); +} + +void cmMakefile::RaiseScope(const std::string& var, const char* varDef) +{ + if (var.empty()) { + return; + } + + if (!this->StateSnapshot.RaiseScope(var, varDef)) { + std::ostringstream m; + m << "Cannot set \"" << var << "\": current scope has no parent."; + this->IssueMessage(MessageType::AUTHOR_WARNING, m.str()); + return; + } + +#ifndef CMAKE_BOOTSTRAP + cmVariableWatch* vv = this->GetVariableWatch(); + if (vv) { + vv->VariableAccessed(var, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, + varDef, this); + } +#endif +} + +void cmMakefile::RaiseScope(const std::vector<std::string>& variables) +{ + for (auto const& varName : variables) { + if (this->IsNormalDefinitionSet(varName)) { + this->RaiseScope(varName, this->GetDefinition(varName)); + } else { + // unset variable in parent scope + this->RaiseScope(varName, nullptr); + } + } +} + +cmTarget* cmMakefile::AddImportedTarget(const std::string& name, + cmStateEnums::TargetType type, + bool global) +{ + // Create the target. + std::unique_ptr<cmTarget> target( + new cmTarget(name, type, + global ? cmTarget::VisibilityImportedGlobally + : cmTarget::VisibilityImported, + this, cmTarget::PerConfig::Yes)); + + // Add to the set of available imported targets. + this->ImportedTargets[name] = target.get(); + this->GetGlobalGenerator()->IndexTarget(target.get()); + this->GetStateSnapshot().GetDirectory().AddImportedTargetName(name); + + // Transfer ownership to this cmMakefile object. + this->ImportedTargetsOwned.push_back(std::move(target)); + return this->ImportedTargetsOwned.back().get(); +} + +cmTarget* cmMakefile::FindTargetToUse(const std::string& name, + bool excludeAliases) const +{ + // Look for an imported target. These take priority because they + // are more local in scope and do not have to be globally unique. + auto targetName = name; + if (!excludeAliases) { + // Look for local alias targets. + auto alias = this->AliasTargets.find(name); + if (alias != this->AliasTargets.end()) { + targetName = alias->second; + } + } + auto imported = this->ImportedTargets.find(targetName); + if (imported != this->ImportedTargets.end()) { + return imported->second; + } + + // Look for a target built in this directory. + if (cmTarget* t = this->FindLocalNonAliasTarget(name)) { + return t; + } + + // Look for a target built in this project. + return this->GetGlobalGenerator()->FindTarget(name, excludeAliases); +} + +bool cmMakefile::IsAlias(const std::string& name) const +{ + if (cm::contains(this->AliasTargets, name)) { + return true; + } + return this->GetGlobalGenerator()->IsAlias(name); +} + +bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg, + bool isCustom) const +{ + if (this->IsAlias(name)) { + std::ostringstream e; + e << "cannot create target \"" << name + << "\" because an alias with the same name already exists."; + msg = e.str(); + return false; + } + if (cmTarget* existing = this->FindTargetToUse(name)) { + // The name given conflicts with an existing target. Produce an + // error in a compatible way. + if (existing->IsImported()) { + // Imported targets were not supported in previous versions. + // This is new code, so we can make it an error. + std::ostringstream e; + e << "cannot create target \"" << name + << "\" because an imported target with the same name already exists."; + msg = e.str(); + return false; + } + // target names must be globally unique + switch (this->GetPolicyStatus(cmPolicies::CMP0002)) { + case cmPolicies::WARN: + this->IssueMessage(MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0002)); + CM_FALLTHROUGH; + case cmPolicies::OLD: + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + this->IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0002)); + return true; + case cmPolicies::NEW: + break; + } + + // The conflict is with a non-imported target. + // Allow this if the user has requested support. + cmake* cm = this->GetCMakeInstance(); + if (isCustom && existing->GetType() == cmStateEnums::UTILITY && + this != existing->GetMakefile() && + cm->GetState()->GetGlobalPropertyAsBool( + "ALLOW_DUPLICATE_CUSTOM_TARGETS")) { + return true; + } + + // Produce an error that tells the user how to work around the + // problem. + std::ostringstream e; + e << "cannot create target \"" << name + << "\" because another target with the same name already exists. " + << "The existing target is "; + switch (existing->GetType()) { + case cmStateEnums::EXECUTABLE: + e << "an executable "; + break; + case cmStateEnums::STATIC_LIBRARY: + e << "a static library "; + break; + case cmStateEnums::SHARED_LIBRARY: + e << "a shared library "; + break; + case cmStateEnums::MODULE_LIBRARY: + e << "a module library "; + break; + case cmStateEnums::UTILITY: + e << "a custom target "; + break; + case cmStateEnums::INTERFACE_LIBRARY: + e << "an interface library "; + break; + default: + break; + } + e << "created in source directory \"" + << existing->GetMakefile()->GetCurrentSourceDirectory() << "\". " + << "See documentation for policy CMP0002 for more details."; + msg = e.str(); + return false; + } + return true; +} + +bool cmMakefile::EnforceUniqueDir(const std::string& srcPath, + const std::string& binPath) const +{ + // Make sure the binary directory is unique. + cmGlobalGenerator* gg = this->GetGlobalGenerator(); + if (gg->BinaryDirectoryIsNew(binPath)) { + return true; + } + std::ostringstream e; + switch (this->GetPolicyStatus(cmPolicies::CMP0013)) { + case cmPolicies::WARN: + // Print the warning. + /* clang-format off */ + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0013) + << "\n" + << "The binary directory\n" + << " " << binPath << "\n" + << "is already used to build a source directory. " + << "This command uses it to build source directory\n" + << " " << srcPath << "\n" + << "which can generate conflicting build files. " + << "CMake does not support this use case but it used " + << "to work accidentally and is being allowed for " + << "compatibility."; + /* clang-format on */ + this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior does not warn. + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0013) << "\n"; + CM_FALLTHROUGH; + case cmPolicies::NEW: + // NEW behavior prints the error. + /* clang-format off */ + e << "The binary directory\n" + << " " << binPath << "\n" + << "is already used to build a source directory. " + << "It cannot be used to build source directory\n" + << " " << srcPath << "\n" + << "Specify a unique binary directory name."; + /* clang-format on */ + this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + break; + } + + return false; +} + +static std::string const matchVariables[] = { + "CMAKE_MATCH_0", "CMAKE_MATCH_1", "CMAKE_MATCH_2", "CMAKE_MATCH_3", + "CMAKE_MATCH_4", "CMAKE_MATCH_5", "CMAKE_MATCH_6", "CMAKE_MATCH_7", + "CMAKE_MATCH_8", "CMAKE_MATCH_9" +}; + +static std::string const nMatchesVariable = "CMAKE_MATCH_COUNT"; + +void cmMakefile::ClearMatches() +{ + cmValue nMatchesStr = this->GetDefinition(nMatchesVariable); + if (!nMatchesStr) { + return; + } + int nMatches = atoi(nMatchesStr->c_str()); + for (int i = 0; i <= nMatches; i++) { + std::string const& var = matchVariables[i]; + std::string const& s = this->GetSafeDefinition(var); + if (!s.empty()) { + this->AddDefinition(var, ""); + this->MarkVariableAsUsed(var); + } + } + this->AddDefinition(nMatchesVariable, "0"); + this->MarkVariableAsUsed(nMatchesVariable); +} + +void cmMakefile::StoreMatches(cmsys::RegularExpression& re) +{ + char highest = 0; + for (int i = 0; i < 10; i++) { + std::string const& m = re.match(i); + if (!m.empty()) { + std::string const& var = matchVariables[i]; + this->AddDefinition(var, m); + this->MarkVariableAsUsed(var); + highest = static_cast<char>('0' + i); + } + } + char nMatches[] = { highest, '\0' }; + this->AddDefinition(nMatchesVariable, nMatches); + this->MarkVariableAsUsed(nMatchesVariable); +} + +cmStateSnapshot cmMakefile::GetStateSnapshot() const +{ + return this->StateSnapshot; +} + +const char* cmMakefile::GetDefineFlagsCMP0059() const +{ + return this->DefineFlagsOrig.c_str(); +} + +cmPolicies::PolicyStatus cmMakefile::GetPolicyStatus(cmPolicies::PolicyID id, + bool parent_scope) const +{ + return this->StateSnapshot.GetPolicy(id, parent_scope); +} + +bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) const +{ + // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting. + if (cmValue val = this->GetDefinition(var)) { + return cmIsOn(val); + } + // Enable optional policy warnings with --debug-output, --trace, + // or --trace-expand. + cmake* cm = this->GetCMakeInstance(); + return cm->GetDebugOutput() || cm->GetTrace(); +} + +bool cmMakefile::SetPolicy(const char* id, cmPolicies::PolicyStatus status) +{ + cmPolicies::PolicyID pid; + if (!cmPolicies::GetPolicyID(id, /* out */ pid)) { + std::ostringstream e; + e << "Policy \"" << id << "\" is not known to this version of CMake."; + this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return false; + } + return this->SetPolicy(pid, status); +} + +bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, + cmPolicies::PolicyStatus status) +{ + // A REQUIRED_ALWAYS policy may be set only to NEW. + if (status != cmPolicies::NEW && + cmPolicies::GetPolicyStatus(id) == cmPolicies::REQUIRED_ALWAYS) { + std::string msg = cmPolicies::GetRequiredAlwaysPolicyError(id); + this->IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + + // Deprecate old policies. + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0114 && + !(this->GetCMakeInstance()->GetIsInTryCompile() && + ( + // Policies set by cmCoreTryCompile::TryCompileCode. + id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 || + id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104)) && + (!this->IsSet("CMAKE_WARN_DEPRECATED") || + this->IsOn("CMAKE_WARN_DEPRECATED"))) { + this->IssueMessage(MessageType::DEPRECATION_WARNING, + cmPolicies::GetPolicyDeprecatedWarning(id)); + } + + this->StateSnapshot.SetPolicy(id, status); + return true; +} + +cmMakefile::PolicyPushPop::PolicyPushPop(cmMakefile* m) + : Makefile(m) +{ + this->Makefile->PushPolicy(); +} + +cmMakefile::PolicyPushPop::~PolicyPushPop() +{ + this->Makefile->PopPolicy(); +} + +void cmMakefile::PushPolicy(bool weak, cmPolicies::PolicyMap const& pm) +{ + this->StateSnapshot.PushPolicy(pm, weak); +} + +void cmMakefile::PopPolicy() +{ + if (!this->StateSnapshot.PopPolicy()) { + this->IssueMessage(MessageType::FATAL_ERROR, + "cmake_policy POP without matching PUSH"); + } +} + +void cmMakefile::PopSnapshot(bool reportError) +{ + // cmStateSnapshot manages nested policy scopes within it. + // Since the scope corresponding to the snapshot is closing, + // reject any still-open nested policy scopes with an error. + while (this->StateSnapshot.CanPopPolicyScope()) { + if (reportError) { + this->IssueMessage(MessageType::FATAL_ERROR, + "cmake_policy PUSH without matching POP"); + reportError = false; + } + this->PopPolicy(); + } + + this->StateSnapshot = this->GetState()->Pop(this->StateSnapshot); + assert(this->StateSnapshot.IsValid()); +} + +bool cmMakefile::SetPolicyVersion(std::string const& version_min, + std::string const& version_max) +{ + return cmPolicies::ApplyPolicyVersion(this, version_min, version_max, + cmPolicies::WarnCompat::On); +} + +cmMakefile::VariablePushPop::VariablePushPop(cmMakefile* m) + : Makefile(m) +{ + this->Makefile->StateSnapshot = + this->Makefile->GetState()->CreateVariableScopeSnapshot( + this->Makefile->StateSnapshot); +} + +cmMakefile::VariablePushPop::~VariablePushPop() +{ + this->Makefile->PopSnapshot(); +} + +bool cmMakefile::HasCMP0054AlreadyBeenReported( + cmListFileContext const& context) const +{ + return !this->CMP0054ReportedIds.insert(context).second; +} + +void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) const +{ + /* Record the setting of every policy. */ + using PolicyID = cmPolicies::PolicyID; + for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT; + pid = static_cast<PolicyID>(pid + 1)) { + pm.Set(pid, this->GetPolicyStatus(pid)); + } +} + +bool cmMakefile::IgnoreErrorsCMP0061() const +{ + bool ignoreErrors = true; + switch (this->GetPolicyStatus(cmPolicies::CMP0061)) { + case cmPolicies::WARN: + // No warning for this policy! + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + ignoreErrors = false; + break; + } + return ignoreErrors; +} + +cmMakefile::FunctionPushPop::FunctionPushPop(cmMakefile* mf, + const std::string& fileName, + cmPolicies::PolicyMap const& pm) + : Makefile(mf) +{ + this->Makefile->PushFunctionScope(fileName, pm); +} + +cmMakefile::FunctionPushPop::~FunctionPushPop() +{ + this->Makefile->PopFunctionScope(this->ReportError); +} + +cmMakefile::MacroPushPop::MacroPushPop(cmMakefile* mf, + const std::string& fileName, + const cmPolicies::PolicyMap& pm) + : Makefile(mf) +{ + this->Makefile->PushMacroScope(fileName, pm); +} + +cmMakefile::MacroPushPop::~MacroPushPop() +{ + this->Makefile->PopMacroScope(this->ReportError); +} + +cmMakefile::DebugFindPkgRAII::DebugFindPkgRAII(cmMakefile* mf, + std::string const& pkg) + : Makefile(mf) + , OldValue(this->Makefile->DebugFindPkg) +{ + this->Makefile->DebugFindPkg = + this->Makefile->GetCMakeInstance()->GetDebugFindPkgOutput(pkg); +} + +cmMakefile::DebugFindPkgRAII::~DebugFindPkgRAII() +{ + this->Makefile->DebugFindPkg = this->OldValue; +} + +bool cmMakefile::GetDebugFindPkgMode() const +{ + return this->DebugFindPkg; +} |