| /* 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 "cmCustomCommandTypes.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 "cmList.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 |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| # include "cmDebuggerAdapter.h" |
| #endif |
| |
| #ifndef __has_feature |
| # define __has_feature(x) 0 |
| #endif |
| |
| // Select a recursion limit that fits within the stack size. |
| // See stack size flags in '../CompileFlags.cmake'. |
| #ifndef CMake_DEFAULT_RECURSION_LIMIT |
| # if __has_feature(address_sanitizer) |
| # define CMake_DEFAULT_RECURSION_LIMIT 400 |
| # elif defined(_MSC_VER) && defined(_DEBUG) |
| # define CMake_DEFAULT_RECURSION_LIMIT 600 |
| # elif defined(__ibmxl__) && defined(__linux) |
| # define CMake_DEFAULT_RECURSION_LIMIT 600 |
| # else |
| # define CMake_DEFAULT_RECURSION_LIMIT 1000 |
| # endif |
| #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); |
| |
| // 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& rootVar, cmValue rootDef, |
| cm::optional<std::string> const& rootEnv) |
| { |
| // Warn if a <PackageName>_ROOT variable we may use is set. |
| if ((rootDef || rootEnv) && this->WarnedCMP0074.insert(rootVar).second) { |
| std::ostringstream w; |
| w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0074) << "\n"; |
| if (rootDef) { |
| w << "CMake variable " << rootVar << " is set to:\n" |
| << " " << *rootDef << "\n"; |
| } |
| if (rootEnv) { |
| w << "Environment variable " << rootVar << " is set to:\n" |
| << " " << *rootEnv << "\n"; |
| } |
| w << "For compatibility, CMake is ignoring the variable."; |
| this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); |
| } |
| } |
| |
| void cmMakefile::MaybeWarnCMP0144(std::string const& rootVAR, cmValue rootDEF, |
| cm::optional<std::string> const& rootENV) |
| { |
| // Warn if a <PACKAGENAME>_ROOT variable we may use is set. |
| if ((rootDEF || rootENV) && this->WarnedCMP0144.insert(rootVAR).second) { |
| std::ostringstream w; |
| w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0144) << "\n"; |
| if (rootDEF) { |
| w << "CMake variable " << rootVAR << " is set to:\n" |
| << " " << *rootDEF << "\n"; |
| } |
| if (rootENV) { |
| w << "Environment variable " << rootVAR << " is set to:\n" |
| << " " << *rootENV << "\n"; |
| } |
| w << "For compatibility, find_package is ignoring the variable, but " |
| "code in a .cmake module might still use it."; |
| 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::JSONv1: { |
| #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::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::Undefined: |
| msg << "INTERNAL ERROR: Trace format is 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 |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->Makefile->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->Makefile->GetCMakeInstance() |
| ->GetDebugAdapter() |
| ->OnBeginFunctionCall(mf, lfc.FilePath, lff); |
| } |
| #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(); |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->Makefile->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->Makefile->GetCMakeInstance() |
| ->GetDebugAdapter() |
| ->OnEndFunctionCall(); |
| } |
| #endif |
| } |
| |
| 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. |
| size_t depthLimit = this->GetRecursionDepthLimit(); |
| if (this->RecursionDepth > depthLimit) { |
| std::ostringstream e; |
| e << "Maximum recursion depth of " << depthLimit << " 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 = |
| cmStrCat(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); |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( |
| this, filenametoread); |
| } |
| #endif |
| |
| cmListFile listFile; |
| if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), |
| this->Backtrace)) { |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
| } |
| #endif |
| |
| return false; |
| } |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
| this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( |
| filenametoread, listFile.Functions); |
| } |
| #endif |
| |
| 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); |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( |
| this, filenametoread); |
| } |
| #endif |
| |
| cmListFile listFile; |
| if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), |
| this->Backtrace)) { |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
| } |
| #endif |
| |
| return false; |
| } |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
| this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( |
| filenametoread, listFile.Functions); |
| } |
| #endif |
| |
| 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; |
| } |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( |
| filenametoread, listFile.Functions); |
| } |
| #endif |
| |
| 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|ccm|cxxm|c\\+\\+m|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. |
| cmList defs{ *cdefs }; |
| |
| // Recompose the list without the definition. |
| defs.remove_items({ define }); |
| |
| // Store the new list. |
| this->SetProperty("COMPILE_DEFINITIONS", defs.to_string()); |
| } |
| } 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); |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( |
| this, currentStart); |
| } |
| #endif |
| |
| cmListFile listFile; |
| if (!listFile.ParseFile(currentStart.c_str(), this->GetMessenger(), |
| this->Backtrace)) { |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
| } |
| #endif |
| |
| return; |
| } |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { |
| this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
| this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( |
| currentStart, listFile.Functions); |
| } |
| #endif |
| |
| if (this->IsRootMakefile()) { |
| bool hasVersion = false; |
| // search for the right policy command |
| for (cmListFileFunction const& func : listFile.Functions) { |
| if (func.LowerCaseName() == "cmake_minimum_required"_s) { |
| 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"_s) { |
| 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 = |
| cmStrCat(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 = cmList::to_string(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, cmValue value, |
| cmValue 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; |
| } |
| if (type == cmStateEnums::PATH || type == cmStateEnums::FILEPATH) { |
| cmList files(value); |
| for (auto& file : files) { |
| if (!cmIsOff(file)) { |
| file = cmSystemTools::CollapseFullPath(file); |
| } |
| } |
| nvalue = files.to_string(); |
| value = cmValue{ nvalue }; |
| |
| this->GetCMakeInstance()->AddCacheEntry(name, value, doc, type); |
| value = this->GetState()->GetInitializedCacheValue(name); |
| } |
| } |
| 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")) { |
| cmList linkLibs{ *linkLibsProp }; |
| |
| for (auto j = linkLibs.begin(); j != linkLibs.end(); ++j) { |
| std::string libraryName = *j; |
| cmTargetLinkLibraryType libType = GENERAL_LibraryType; |
| if (libraryName == "optimized"_s) { |
| libType = OPTIMIZED_LibraryType; |
| ++j; |
| libraryName = *j; |
| } else if (libraryName == "debug"_s) { |
| 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; |
| } |
| |
| cmTarget* cmMakefile::AddSynthesizedTarget(cmStateEnums::TargetType type, |
| const std::string& name) |
| { |
| return &this |
| ->CreateNewTarget(name, type, cmTarget::PerConfig::Yes, |
| cmTarget::Visibility::Generated) |
| .first; |
| } |
| |
| std::pair<cmTarget&, bool> cmMakefile::CreateNewTarget( |
| const std::string& name, cmStateEnums::TargetType type, |
| cmTarget::PerConfig perConfig, cmTarget::Visibility vis) |
| { |
| auto ib = |
| this->Targets.emplace(name, cmTarget(name, type, vis, 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")) { |
| cmList linkLibs{ *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"_s) { |
| 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"_s) { |
| 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 }, |
| { "xros", AppleSDK::XROS }, |
| { "xrsimulator", AppleSDK::XRSimulator }, |
| }; |
| |
| for (auto const& entry : sdkDatabase) { |
| if (cmHasPrefix(sdkRoot, entry.name) || |
| sdkRoot.find(cmStrCat('/', entry.name)) != std::string::npos) { |
| return entry.sdk; |
| } |
| } |
| |
| return AppleSDK::MacOS; |
| } |
| |
| bool cmMakefile::PlatformIsAppleEmbedded() const |
| { |
| return this->GetAppleSDKType() != AppleSDK::MacOS; |
| } |
| |
| bool cmMakefile::PlatformIsAppleSimulator() const |
| { |
| return std::set<AppleSDK>{ |
| AppleSDK::AppleTVSimulator, |
| AppleSDK::IPhoneSimulator, |
| AppleSDK::WatchSimulator, |
| AppleSDK::XRSimulator, |
| } |
| .count(this->GetAppleSDKType()); |
| } |
| |
| bool cmMakefile::PlatformSupportsAppleTextStubs() const |
| { |
| return this->IsOn("APPLE") && this->IsSet("CMAKE_TAPI"); |
| } |
| |
| 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); |
| } |
| |
| 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")); |
| } |
| |
| size_t cmMakefile::GetRecursionDepthLimit() const |
| { |
| size_t depth = CMake_DEFAULT_RECURSION_LIMIT; |
| if (cmValue depthStr = |
| this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH")) { |
| unsigned long depthUL; |
| if (cmStrToULong(depthStr.GetCStr(), &depthUL)) { |
| depth = depthUL; |
| } |
| } else if (cm::optional<std::string> depthEnv = |
| cmSystemTools::GetEnvVar("CMAKE_MAXIMUM_RECURSION_DEPTH")) { |
| unsigned long depthUL; |
| if (cmStrToULong(*depthEnv, &depthUL)) { |
| depth = depthUL; |
| } |
| } |
| return depth; |
| } |
| |
| size_t cmMakefile::GetRecursionDepth() const |
| { |
| return this->RecursionDepth; |
| } |
| |
| void cmMakefile::SetRecursionDepth(size_t 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 = cmList::to_string( |
| 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.push_back('\t'); |
| last = next + 1; |
| } else if (nextc == 'n') { |
| result.append(last, in - last); |
| result.push_back('\n'); |
| last = next + 1; |
| } else if (nextc == 'r') { |
| result.append(last, in - last); |
| result.push_back('\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 += cmStrCat("\') 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 |
| { |
| cmList configs; |
| if (this->GetGlobalGenerator()->IsMultiConfig()) { |
| configs.assign(this->GetDefinition("CMAKE_CONFIGURATION_TYPES")); |
| } 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 std::move(configs.data()); |
| } |
| |
| 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 { |
| cmList stringArgs{ 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 ? " has"_s : "s have"_s; |
| this->IssueMessage( |
| MessageType::AUTHOR_WARNING, |
| cmStrCat("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"_s) { |
| 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, |
| cmStrCat("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); |
| |
| #ifdef CMake_ENABLE_DEBUGGER |
| if (cm->GetDebugAdapter() != nullptr) { |
| cm->GetDebugAdapter()->OnMessageOutput(MessageType::MESSAGE, message); |
| } |
| #endif |
| } |
| |
| 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) { |
| cmList modulePath{ *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 = cmStrCat(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, |
| cmStrCat("#", indentation, "cmakedefine"), |
| cmStrCat("#", 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, |
| cmStrCat("#", indentation, "cmakedefine01"), |
| cmStrCat("#", 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(cmStrCat("Attempt to write file: ", outfile, |
| " into a source directory.")); |
| return 0; |
| } |
| if (!cmSystemTools::FileExists(infile)) { |
| cmSystemTools::Error(cmStrCat("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, 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"_s) { |
| 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 = cmList::to_string(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() |
| { |
| cmList deps; |
| if (cmValue deps_str = this->GetProperty("CMAKE_CONFIGURE_DEPENDS")) { |
| deps.assign(*deps_str); |
| } |
| for (auto 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::Visibility::ImportedGlobally |
| : cmTarget::Visibility::Imported, |
| 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::CMP0120 && |
| !(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; |
| } |