| /* 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 "cmsys/FStream.hxx" |
| #include "cmsys/RegularExpression.hxx" |
| |
| #include "cm_sys_stat.h" |
| |
| #include "cmAlgorithms.h" |
| #include "cmCommandArgumentParserHelper.h" |
| #include "cmCustomCommand.h" |
| #include "cmCustomCommandLines.h" |
| #include "cmExecutionStatus.h" |
| #include "cmExpandedCommandArgument.h" // IWYU pragma: keep |
| #include "cmFileLockPool.h" |
| #include "cmFunctionBlocker.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmGeneratorExpression.h" |
| #include "cmGeneratorExpressionEvaluationFile.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmInstallGenerator.h" // IWYU pragma: keep |
| #include "cmInstallSubdirectoryGenerator.h" |
| #include "cmListFileCache.h" |
| #include "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 "cmVariableWatch.h" |
| #endif |
| |
| class cmMessenger; |
| |
| cmDirectoryId::cmDirectoryId(std::string s) |
| : String(std::move(s)) |
| { |
| } |
| |
| // default is not to be building executables |
| cmMakefile::cmMakefile(cmGlobalGenerator* globalGenerator, |
| cmStateSnapshot const& snapshot) |
| : GlobalGenerator(globalGenerator) |
| , StateSnapshot(snapshot) |
| , Backtrace(snapshot) |
| { |
| this->IsSourceFileTryCompile = false; |
| |
| this->WarnUnused = this->GetCMakeInstance()->GetWarnUnused(); |
| this->CheckSystemVars = this->GetCMakeInstance()->GetCheckSystemVars(); |
| |
| this->SuppressSideEffects = false; |
| |
| // Setup the default include complaint regular expression (match nothing). |
| this->ComplainFileRegularExpression = "^$"; |
| |
| this->DefineFlags = " "; |
| |
| this->cmDefineRegex.compile("#([ \t]*)cmakedefine[ \t]+([A-Za-z_0-9]*)"); |
| this->cmDefine01Regex.compile("#([ \t]*)cmakedefine01[ \t]+([A-Za-z_0-9]*)"); |
| this->cmAtVarRegex.compile("(@[A-Za-z_0-9/.+-]+@)"); |
| this->cmNamedCurly.compile("^[A-Za-z0-9/_.+-]+{"); |
| |
| this->StateSnapshot = |
| this->StateSnapshot.GetState()->CreatePolicyScopeSnapshot( |
| this->StateSnapshot); |
| this->RecursionDepth = 0; |
| |
| // Enter a policy level for this directory. |
| this->PushPolicy(); |
| |
| // push empty loop block |
| this->PushLoopBlockBarrier(); |
| |
| // By default the check is not done. It is enabled by |
| // cmListFileCache in the top level if necessary. |
| this->CheckCMP0000 = false; |
| |
| #if !defined(CMAKE_BOOTSTRAP) |
| this->AddSourceGroup("", "^.*$"); |
| this->AddSourceGroup("Source Files", CM_SOURCE_REGEX); |
| this->AddSourceGroup("Header Files", CM_HEADER_REGEX); |
| this->AddSourceGroup("Precompile Header File", CM_PCH_REGEX); |
| this->AddSourceGroup("CMake Rules", "\\.rule$"); |
| this->AddSourceGroup("Resources", CM_RESOURCE_REGEX); |
| this->AddSourceGroup("Object Files", "\\.(lo|o|obj)$"); |
| |
| this->ObjectLibrariesSourceGroupIndex = this->SourceGroups.size(); |
| this->SourceGroups.emplace_back("Object Libraries", "^MATCH_NO_SOURCES$"); |
| #endif |
| } |
| |
| cmMakefile::~cmMakefile() |
| { |
| cmDeleteAll(this->InstallGenerators); |
| cmDeleteAll(this->TestGenerators); |
| cmDeleteAll(this->SourceFiles); |
| cmDeleteAll(this->Tests); |
| cmDeleteAll(this->ImportedTargetsOwned); |
| cmDeleteAll(this->EvaluationFiles); |
| } |
| |
| 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]; |
| sprintf(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->GetBacktrace()); |
| } |
| |
| bool cmMakefile::CheckCMP0037(std::string const& targetName, |
| cmStateEnums::TargetType targetType) const |
| { |
| MessageType messageType = MessageType::AUTHOR_WARNING; |
| std::ostringstream e; |
| bool issueMessage = false; |
| switch (this->GetPolicyStatus(cmPolicies::CMP0037)) { |
| case cmPolicies::WARN: |
| if (targetType != cmStateEnums::INTERFACE_LIBRARY) { |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0037) << "\n"; |
| issueMessage = true; |
| } |
| CM_FALLTHROUGH; |
| case cmPolicies::OLD: |
| break; |
| case cmPolicies::NEW: |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| issueMessage = true; |
| messageType = MessageType::FATAL_ERROR; |
| break; |
| } |
| if (issueMessage) { |
| e << "The target name \"" << targetName |
| << "\" is reserved or not valid for certain " |
| "CMake features, such as generator expressions, and may result " |
| "in undefined behavior."; |
| this->IssueMessage(messageType, e.str()); |
| |
| if (messageType == MessageType::FATAL_ERROR) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void cmMakefile::MaybeWarnCMP0074(std::string const& pkg) |
| { |
| // Warn if a <pkg>_ROOT variable we may use is set. |
| std::string const varName = pkg + "_ROOT"; |
| const char* var = this->GetDefinition(varName); |
| std::string env; |
| cmSystemTools::GetEnv(varName, env); |
| |
| bool const haveVar = var && *var; |
| bool const haveEnv = !env.empty(); |
| if ((haveVar || haveEnv) && this->WarnedCMP0074.insert(varName).second) { |
| std::ostringstream w; |
| w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0074) << "\n"; |
| if (haveVar) { |
| w << "CMake variable " << varName << " is set to:\n" |
| << " " << var << "\n"; |
| } |
| if (haveEnv) { |
| w << "Environment variable " << varName << " is set to:\n" |
| << " " << env << "\n"; |
| } |
| w << "For compatibility, CMake is ignoring the variable."; |
| this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); |
| } |
| } |
| |
| cmStringRange cmMakefile::GetIncludeDirectoriesEntries() const |
| { |
| return this->StateSnapshot.GetDirectory().GetIncludeDirectoriesEntries(); |
| } |
| |
| cmBacktraceRange cmMakefile::GetIncludeDirectoriesBacktraces() const |
| { |
| return this->StateSnapshot.GetDirectory() |
| .GetIncludeDirectoriesEntryBacktraces(); |
| } |
| |
| cmStringRange cmMakefile::GetCompileOptionsEntries() const |
| { |
| return this->StateSnapshot.GetDirectory().GetCompileOptionsEntries(); |
| } |
| |
| cmBacktraceRange cmMakefile::GetCompileOptionsBacktraces() const |
| { |
| return this->StateSnapshot.GetDirectory().GetCompileOptionsEntryBacktraces(); |
| } |
| |
| cmStringRange cmMakefile::GetCompileDefinitionsEntries() const |
| { |
| return this->StateSnapshot.GetDirectory().GetCompileDefinitionsEntries(); |
| } |
| |
| cmBacktraceRange cmMakefile::GetCompileDefinitionsBacktraces() const |
| { |
| return this->StateSnapshot.GetDirectory() |
| .GetCompileDefinitionsEntryBacktraces(); |
| } |
| |
| cmStringRange cmMakefile::GetLinkOptionsEntries() const |
| { |
| return this->StateSnapshot.GetDirectory().GetLinkOptionsEntries(); |
| } |
| |
| cmBacktraceRange cmMakefile::GetLinkOptionsBacktraces() const |
| { |
| return this->StateSnapshot.GetDirectory().GetLinkOptionsEntryBacktraces(); |
| } |
| |
| cmStringRange cmMakefile::GetLinkDirectoriesEntries() const |
| { |
| return this->StateSnapshot.GetDirectory().GetLinkDirectoriesEntries(); |
| } |
| |
| cmBacktraceRange cmMakefile::GetLinkDirectoriesBacktraces() const |
| { |
| return this->StateSnapshot.GetDirectory() |
| .GetLinkDirectoriesEntryBacktraces(); |
| } |
| |
| cmListFileBacktrace cmMakefile::GetBacktrace() const |
| { |
| return this->Backtrace; |
| } |
| |
| cmListFileBacktrace cmMakefile::GetBacktrace(cmCommandContext const& cc) const |
| { |
| cmListFileContext lfc; |
| lfc.Name = cc.Name.Original; |
| lfc.Line = cc.Line; |
| lfc.FilePath = this->StateSnapshot.GetExecutionListFile(); |
| return this->Backtrace.Push(lfc); |
| } |
| |
| cmListFileContext cmMakefile::GetExecutionContext() const |
| { |
| cmListFileContext const& cur = this->Backtrace.Top(); |
| cmListFileContext lfc; |
| lfc.Name = cur.Name; |
| lfc.Line = cur.Line; |
| lfc.FilePath = this->StateSnapshot.GetExecutionListFile(); |
| return lfc; |
| } |
| |
| void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) 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 = this->GetExecutionFilePath(); |
| 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; |
| msg << full_path << "(" << lff.Line << "): "; |
| msg << lff.Name.Original << "("; |
| bool expand = this->GetCMakeInstance()->GetTraceExpand(); |
| std::string temp; |
| for (cmListFileArgument const& arg : lff.Arguments) { |
| if (expand) { |
| temp = arg.Value; |
| this->ExpandVariablesInString(temp); |
| msg << temp; |
| } else { |
| msg << arg.Value; |
| } |
| msg << " "; |
| } |
| msg << ")"; |
| |
| 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, cmCommandContext const& cc, |
| cmExecutionStatus& status) |
| : Makefile(mf) |
| { |
| cmListFileContext const& lfc = cmListFileContext::FromCommandContext( |
| cc, this->Makefile->StateSnapshot.GetExecutionListFile()); |
| this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); |
| ++this->Makefile->RecursionDepth; |
| this->Makefile->ExecutionStatusStack.push_back(&status); |
| } |
| |
| ~cmMakefileCall() |
| { |
| this->Makefile->ExecutionStatusStack.pop_back(); |
| --this->Makefile->RecursionDepth; |
| this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); |
| } |
| |
| cmMakefileCall(const cmMakefileCall&) = delete; |
| cmMakefileCall& operator=(const cmMakefileCall&) = delete; |
| |
| private: |
| cmMakefile* Makefile; |
| }; |
| |
| void cmMakefile::OnExecuteCommand(std::function<void()> callback) |
| { |
| this->ExecuteCommandCallback = std::move(callback); |
| } |
| |
| bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, |
| cmExecutionStatus& status) |
| { |
| 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, status); |
| static_cast<void>(stack_manager); |
| |
| // Check for maximum recursion depth. |
| int depth = CMake_DEFAULT_RECURSION_LIMIT; |
| const char* depthStr = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH"); |
| if (depthStr) { |
| std::istringstream s(depthStr); |
| int d; |
| if (s >> d) { |
| depth = d; |
| } |
| } |
| if (this->RecursionDepth > depth) { |
| std::ostringstream e; |
| e << "Maximum recursion depth of " << depth << " exceeded"; |
| this->IssueMessage(MessageType::FATAL_ERROR, e.str()); |
| cmSystemTools::SetFatalErrorOccured(); |
| return false; |
| } |
| |
| // Lookup the command prototype. |
| if (cmState::Command command = |
| this->GetState()->GetCommandByExactName(lff.Name.Lower)) { |
| // Decide whether to invoke the command. |
| if (!cmSystemTools::GetFatalErrorOccured()) { |
| // if trace is enabled, print out invoke information |
| if (this->GetCMakeInstance()->GetTrace()) { |
| this->PrintCommandTrace(lff); |
| } |
| // Try invoking the command. |
| bool invokeSucceeded = command(lff.Arguments, status); |
| bool hadNestedError = status.GetNestedError(); |
| if (!invokeSucceeded || hadNestedError) { |
| if (!hadNestedError) { |
| // The command invocation requested that we report an error. |
| std::string const error = |
| std::string(lff.Name.Original) + " " + status.GetError(); |
| this->IssueMessage(MessageType::FATAL_ERROR, error); |
| } |
| result = false; |
| if (this->GetCMakeInstance()->GetWorkingMode() != cmake::NORMAL_MODE) { |
| cmSystemTools::SetFatalErrorOccured(); |
| } |
| } |
| } |
| } else { |
| if (!cmSystemTools::GetFatalErrorOccured()) { |
| std::string error = |
| cmStrCat("Unknown CMake command \"", lff.Name.Original, "\"."); |
| this->IssueMessage(MessageType::FATAL_ERROR, error); |
| result = false; |
| cmSystemTools::SetFatalErrorOccured(); |
| } |
| } |
| |
| return result; |
| } |
| |
| 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; |
| bool ReportError; |
| void EnforceCMP0011(); |
| }; |
| |
| cmMakefile::IncludeScope::IncludeScope(cmMakefile* mf, |
| std::string const& filenametoread, |
| bool noPolicyScope) |
| : Makefile(mf) |
| , NoPolicyScope(noPolicyScope) |
| , CheckCMP0011(false) |
| , ReportError(true) |
| { |
| this->Makefile->Backtrace = this->Makefile->Backtrace.Push(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->GetExecutionFilePath() << "\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->GetExecutionFilePath() << "\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 (const char* def = this->GetDefinition("CMAKE_CURRENT_LIST_FILE")) { |
| this->AddDefinition("CMAKE_PARENT_LIST_FILE", def); |
| } |
| std::string filenametoread = cmSystemTools::CollapseFullPath( |
| filename, this->GetCurrentSourceDirectory()); |
| |
| IncludeScope incScope(this, filenametoread, noPolicyScope); |
| |
| cmListFile listFile; |
| if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), |
| this->Backtrace)) { |
| return false; |
| } |
| |
| this->ReadListFile(listFile, filenametoread); |
| if (cmSystemTools::GetFatalErrorOccured()) { |
| incScope.Quiet(); |
| } |
| return true; |
| } |
| |
| class cmMakefile::ListFileScope |
| { |
| public: |
| ListFileScope(cmMakefile* mf, std::string const& filenametoread) |
| : Makefile(mf) |
| , ReportError(true) |
| { |
| this->Makefile->Backtrace = this->Makefile->Backtrace.Push(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; |
| }; |
| |
| bool cmMakefile::ReadListFile(const std::string& filename) |
| { |
| std::string filenametoread = cmSystemTools::CollapseFullPath( |
| filename, this->GetCurrentSourceDirectory()); |
| |
| ListFileScope scope(this, filenametoread); |
| |
| cmListFile listFile; |
| if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), |
| this->Backtrace)) { |
| return false; |
| } |
| |
| this->ReadListFile(listFile, filenametoread); |
| if (cmSystemTools::GetFatalErrorOccured()) { |
| scope.Quiet(); |
| } |
| return true; |
| } |
| |
| void cmMakefile::ReadListFile(cmListFile const& listFile, |
| std::string const& filenametoread) |
| { |
| // 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::GetFatalErrorOccured()) { |
| break; |
| } |
| if (status.GetReturnInvoked()) { |
| // Exit early due to return command. |
| break; |
| } |
| } |
| this->CheckForUnusedVariables(); |
| |
| 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); |
| 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::SetFatalErrorOccured(); |
| return; |
| } |
| } |
| } |
| |
| void cmMakefile::AddEvaluationFile( |
| const std::string& inputFile, |
| std::unique_ptr<cmCompiledGeneratorExpression> outputName, |
| std::unique_ptr<cmCompiledGeneratorExpression> condition, |
| bool inputIsContent) |
| { |
| this->EvaluationFiles.push_back(new cmGeneratorExpressionEvaluationFile( |
| inputFile, std::move(outputName), std::move(condition), inputIsContent, |
| this->GetPolicyStatus(cmPolicies::CMP0070))); |
| } |
| |
| std::vector<cmGeneratorExpressionEvaluationFile*> |
| cmMakefile::GetEvaluationFiles() const |
| { |
| return this->EvaluationFiles; |
| } |
| |
| std::vector<cmExportBuildFileGenerator*> |
| cmMakefile::GetExportBuildFileGenerators() const |
| { |
| return this->ExportBuildFileGenerators; |
| } |
| |
| void cmMakefile::RemoveExportBuildFileGeneratorCMP0024( |
| cmExportBuildFileGenerator* gen) |
| { |
| auto it = std::find(this->ExportBuildFileGenerators.begin(), |
| this->ExportBuildFileGenerators.end(), gen); |
| if (it != this->ExportBuildFileGenerators.end()) { |
| this->ExportBuildFileGenerators.erase(it); |
| } |
| } |
| |
| void cmMakefile::AddExportBuildFileGenerator(cmExportBuildFileGenerator* gen) |
| { |
| this->ExportBuildFileGenerators.push_back(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::AddFinalAction(FinalAction action) |
| { |
| this->FinalActions.push_back(std::move(action)); |
| } |
| |
| void cmMakefile::FinalPass() |
| { |
| // 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 (FinalAction& action : this->FinalActions) { |
| action(*this); |
| } |
| |
| // 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 |
| cmEraseIf(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 |
| cmEraseIf(this->ListFiles, file_not_persistent()); |
| } |
| |
| // Generate the output file |
| void cmMakefile::ConfigureFinalPass() |
| { |
| this->FinalPass(); |
| const char* 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); |
| } |
| } |
| |
| 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 |
| { |
| // 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; |
| case cmPolicies::OLD: |
| break; |
| case cmPolicies::NEW: |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| issueMessage = true; |
| messageType = MessageType::FATAL_ERROR; |
| } |
| |
| 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->IssueMessage(messageType, e.str()); |
| } |
| |
| 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->IssueMessage(MessageType::FATAL_ERROR, e.str()); |
| 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->IssueMessage(MessageType::FATAL_ERROR, e.str()); |
| return nullptr; |
| } |
| |
| return t; |
| } |
| |
| cmTarget* cmMakefile::AddCustomCommandToTarget( |
| const std::string& target, const std::vector<std::string>& byproducts, |
| const std::vector<std::string>& depends, |
| const cmCustomCommandLines& commandLines, cmCustomCommandType type, |
| const char* comment, const char* workingDir, bool escapeOldStyle, |
| bool uses_terminal, const std::string& depfile, const std::string& job_pool, |
| bool command_expand_lists, cmObjectLibraryCommands objLibCommands) |
| { |
| cmTarget* t = this->GetCustomCommandTarget(target, objLibCommands); |
| |
| // Validate custom commands. |
| if (!t || !this->ValidateCustomCommand(commandLines)) { |
| return t; |
| } |
| |
| // Always create the byproduct sources and mark them generated. |
| this->CreateGeneratedSources(byproducts); |
| |
| this->CommitCustomCommandToTarget( |
| t, byproducts, depends, commandLines, type, comment, workingDir, |
| escapeOldStyle, uses_terminal, depfile, job_pool, command_expand_lists); |
| |
| return t; |
| } |
| |
| void cmMakefile::CommitCustomCommandToTarget( |
| cmTarget* target, const std::vector<std::string>& byproducts, |
| const std::vector<std::string>& depends, |
| const cmCustomCommandLines& commandLines, cmCustomCommandType type, |
| const char* comment, const char* workingDir, bool escapeOldStyle, |
| bool uses_terminal, const std::string& depfile, const std::string& job_pool, |
| bool command_expand_lists) |
| { |
| // Add the command to the appropriate build step for the target. |
| std::vector<std::string> no_output; |
| cmCustomCommand cc(this, no_output, byproducts, depends, commandLines, |
| comment, workingDir); |
| cc.SetEscapeOldStyle(escapeOldStyle); |
| cc.SetEscapeAllowMakeVars(true); |
| cc.SetUsesTerminal(uses_terminal); |
| cc.SetCommandExpandLists(command_expand_lists); |
| cc.SetDepfile(depfile); |
| cc.SetJobPool(job_pool); |
| switch (type) { |
| case cmCustomCommandType::PRE_BUILD: |
| target->AddPreBuildCommand(std::move(cc)); |
| break; |
| case cmCustomCommandType::PRE_LINK: |
| target->AddPreLinkCommand(std::move(cc)); |
| break; |
| case cmCustomCommandType::POST_BUILD: |
| target->AddPostBuildCommand(std::move(cc)); |
| break; |
| } |
| |
| this->AddTargetByproducts(target, byproducts); |
| } |
| |
| cmSourceFile* cmMakefile::AddCustomCommandToOutput( |
| const std::string& output, const std::vector<std::string>& depends, |
| const std::string& main_dependency, const cmCustomCommandLines& commandLines, |
| const char* comment, const char* workingDir, bool replace, |
| bool escapeOldStyle, bool uses_terminal, bool command_expand_lists, |
| const std::string& depfile, const std::string& job_pool) |
| { |
| std::vector<std::string> outputs; |
| outputs.push_back(output); |
| std::vector<std::string> no_byproducts; |
| cmImplicitDependsList no_implicit_depends; |
| return this->AddCustomCommandToOutput( |
| outputs, no_byproducts, depends, main_dependency, no_implicit_depends, |
| commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal, |
| command_expand_lists, depfile, job_pool); |
| } |
| |
| cmSourceFile* cmMakefile::AddCustomCommandToOutput( |
| const std::vector<std::string>& outputs, |
| const std::vector<std::string>& byproducts, |
| const std::vector<std::string>& depends, const std::string& main_dependency, |
| const cmImplicitDependsList& implicit_depends, |
| const cmCustomCommandLines& commandLines, const char* comment, |
| const char* workingDir, bool replace, bool escapeOldStyle, |
| bool uses_terminal, bool command_expand_lists, const std::string& depfile, |
| const std::string& job_pool) |
| { |
| // Make sure there is at least one output. |
| if (outputs.empty()) { |
| cmSystemTools::Error("Attempt to add a custom rule with no output!"); |
| return nullptr; |
| } |
| |
| // Validate custom commands. |
| if (!this->ValidateCustomCommand(commandLines)) { |
| return nullptr; |
| } |
| |
| // Always create the output sources and mark them generated. |
| this->CreateGeneratedSources(outputs); |
| this->CreateGeneratedSources(byproducts); |
| |
| return this->CommitCustomCommandToOutput( |
| outputs, byproducts, depends, main_dependency, implicit_depends, |
| commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal, |
| command_expand_lists, depfile, job_pool); |
| } |
| |
| cmSourceFile* cmMakefile::CommitCustomCommandToOutput( |
| const std::vector<std::string>& outputs, |
| const std::vector<std::string>& byproducts, |
| const std::vector<std::string>& depends, const std::string& main_dependency, |
| const cmImplicitDependsList& implicit_depends, |
| const cmCustomCommandLines& commandLines, const char* comment, |
| const char* workingDir, bool replace, bool escapeOldStyle, |
| bool uses_terminal, bool command_expand_lists, const std::string& depfile, |
| const std::string& job_pool) |
| { |
| // Choose a source file on which to store the custom command. |
| cmSourceFile* file = nullptr; |
| if (!commandLines.empty() && !main_dependency.empty()) { |
| // The main dependency was specified. Use it unless a different |
| // custom command already used it. |
| file = this->GetSource(main_dependency); |
| if (file && file->GetCustomCommand() && !replace) { |
| // The main dependency already has a custom command. |
| if (commandLines == file->GetCustomCommand()->GetCommandLines()) { |
| // The existing custom command is identical. Silently ignore |
| // the duplicate. |
| return file; |
| } |
| // The existing custom command is different. We need to |
| // generate a rule file for this new command. |
| file = nullptr; |
| } else if (!file) { |
| file = this->CreateSource(main_dependency); |
| } |
| } |
| |
| // Generate a rule file if the main dependency is not available. |
| if (!file) { |
| cmGlobalGenerator* gg = this->GetGlobalGenerator(); |
| |
| // Construct a rule file associated with the first output produced. |
| std::string outName = gg->GenerateRuleFile(outputs[0]); |
| |
| // Check if the rule file already exists. |
| file = this->GetSource(outName, cmSourceFileLocationKind::Known); |
| if (file && file->GetCustomCommand() && !replace) { |
| // The rule file already exists. |
| if (commandLines != file->GetCustomCommand()->GetCommandLines()) { |
| cmSystemTools::Error("Attempt to add a custom rule to output \"" + |
| outName + "\" which already has a custom rule."); |
| } |
| return file; |
| } |
| |
| // Create a cmSourceFile for the rule file. |
| if (!file) { |
| file = |
| this->CreateSource(outName, true, cmSourceFileLocationKind::Known); |
| } |
| file->SetProperty("__CMAKE_RULE", "1"); |
| } |
| |
| // Attach the custom command to the file. |
| if (file) { |
| // Construct a complete list of dependencies. |
| std::vector<std::string> depends2(depends); |
| if (!main_dependency.empty()) { |
| depends2.push_back(main_dependency); |
| } |
| |
| std::unique_ptr<cmCustomCommand> cc = cm::make_unique<cmCustomCommand>( |
| this, outputs, byproducts, depends2, commandLines, comment, workingDir); |
| cc->SetEscapeOldStyle(escapeOldStyle); |
| cc->SetEscapeAllowMakeVars(true); |
| cc->SetImplicitDepends(implicit_depends); |
| cc->SetUsesTerminal(uses_terminal); |
| cc->SetCommandExpandLists(command_expand_lists); |
| cc->SetDepfile(depfile); |
| cc->SetJobPool(job_pool); |
| file->SetCustomCommand(std::move(cc)); |
| |
| this->AddSourceOutputs(file, outputs, byproducts); |
| } |
| return file; |
| } |
| |
| 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) |
| { |
| // 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. |
| std::vector<std::string> no_byproducts; |
| this->AddCustomCommandToTarget( |
| target, no_byproducts, depends, commandLines, |
| cmCustomCommandType::POST_BUILD, comment, nullptr); |
| 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|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) { |
| cmSourceFile* sf = this->AddCustomCommandToOutput( |
| output, depends, source, commandLines, comment, nullptr); |
| if (sf) { |
| addRuleFileToTarget(sf); |
| } |
| } |
| } else { |
| std::string no_main_dependency; |
| std::vector<std::string> depends2 = depends; |
| depends2.push_back(source); |
| |
| // The source may not be a real file. Do not use a main dependency. |
| for (std::string const& output : outputs) { |
| cmSourceFile* sf = this->AddCustomCommandToOutput( |
| output, depends2, no_main_dependency, commandLines, comment, nullptr); |
| if (sf) { |
| addRuleFileToTarget(sf); |
| } |
| } |
| } |
| } |
| |
| bool cmMakefile::AppendCustomCommandToOutput( |
| const std::string& output, const std::vector<std::string>& depends, |
| const cmImplicitDependsList& implicit_depends, |
| const cmCustomCommandLines& commandLines) |
| { |
| // Check as good as we can if there will be a command for this output. |
| if (!this->MightHaveCustomCommand(output)) { |
| return false; |
| } |
| |
| // Validate custom commands. |
| if (this->ValidateCustomCommand(commandLines)) { |
| // Add command factory to allow generator expressions in output. |
| this->CommitAppendCustomCommandToOutput(output, depends, implicit_depends, |
| commandLines); |
| } |
| |
| return true; |
| } |
| |
| void cmMakefile::CommitAppendCustomCommandToOutput( |
| const std::string& output, const std::vector<std::string>& depends, |
| const cmImplicitDependsList& implicit_depends, |
| const cmCustomCommandLines& commandLines) |
| { |
| // Lookup an existing command. |
| if (cmSourceFile* sf = this->GetSourceFileWithOutput(output)) { |
| if (cmCustomCommand* cc = sf->GetCustomCommand()) { |
| cc->AppendCommands(commandLines); |
| cc->AppendDepends(depends); |
| cc->AppendImplicitDepends(implicit_depends); |
| } |
| } |
| } |
| |
| cmUtilityOutput cmMakefile::GetUtilityOutput(cmTarget* target) |
| { |
| std::string force = cmStrCat(this->GetCurrentBinaryDirectory(), |
| "/CMakeFiles/", target->GetName()); |
| std::string forceCMP0049 = target->GetSourceCMP0049(force); |
| { |
| cmSourceFile* sf = nullptr; |
| if (!forceCMP0049.empty()) { |
| sf = this->GetOrCreateSource(forceCMP0049, false, |
| cmSourceFileLocationKind::Known); |
| } |
| // The output is not actually created so mark it symbolic. |
| if (sf) { |
| sf->SetProperty("SYMBOLIC", "1"); |
| } else { |
| cmSystemTools::Error("Could not get source file entry for " + force); |
| } |
| } |
| return { std::move(force), std::move(forceCMP0049) }; |
| } |
| |
| cmTarget* cmMakefile::AddUtilityCommand( |
| const std::string& utilityName, cmCommandOrigin origin, bool excludeFromAll, |
| const char* workingDirectory, const std::vector<std::string>& byproducts, |
| const std::vector<std::string>& depends, |
| const cmCustomCommandLines& commandLines, bool escapeOldStyle, |
| const char* comment, bool uses_terminal, bool command_expand_lists, |
| const std::string& job_pool) |
| { |
| cmTarget* target = |
| this->AddNewUtilityTarget(utilityName, origin, excludeFromAll); |
| |
| // Validate custom commands. |
| if ((commandLines.empty() && depends.empty()) || |
| !this->ValidateCustomCommand(commandLines)) { |
| return target; |
| } |
| |
| // Get the output name of the utility target and mark it generated. |
| cmUtilityOutput force = this->GetUtilityOutput(target); |
| this->GetOrCreateGeneratedSource(force.Name); |
| |
| // Always create the byproduct sources and mark them generated. |
| this->CreateGeneratedSources(byproducts); |
| |
| if (!comment) { |
| // Use an empty comment to avoid generation of default comment. |
| comment = ""; |
| } |
| |
| this->CommitUtilityCommand(target, force, workingDirectory, byproducts, |
| depends, commandLines, escapeOldStyle, comment, |
| uses_terminal, command_expand_lists, job_pool); |
| |
| return target; |
| } |
| |
| void cmMakefile::CommitUtilityCommand( |
| cmTarget* target, const cmUtilityOutput& force, const char* workingDirectory, |
| const std::vector<std::string>& byproducts, |
| const std::vector<std::string>& depends, |
| const cmCustomCommandLines& commandLines, bool escapeOldStyle, |
| const char* comment, bool uses_terminal, bool command_expand_lists, |
| const std::string& job_pool) |
| { |
| std::vector<std::string> forced; |
| forced.push_back(force.Name); |
| std::string no_main_dependency; |
| cmImplicitDependsList no_implicit_depends; |
| bool no_replace = false; |
| cmSourceFile* sf = this->AddCustomCommandToOutput( |
| forced, byproducts, depends, no_main_dependency, no_implicit_depends, |
| commandLines, comment, workingDirectory, no_replace, escapeOldStyle, |
| uses_terminal, command_expand_lists, /*depfile=*/"", job_pool); |
| if (!force.NameCMP0049.empty()) { |
| target->AddSource(force.NameCMP0049); |
| } |
| if (sf) { |
| this->AddTargetByproducts(target, byproducts); |
| } |
| } |
| |
| 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.c_str()); |
| } |
| |
| void cmMakefile::AddCompileOption(std::string const& option) |
| { |
| this->AppendProperty("COMPILE_OPTIONS", option.c_str()); |
| } |
| |
| void cmMakefile::AddLinkOption(std::string const& option) |
| { |
| this->AppendProperty("LINK_OPTIONS", option.c_str()); |
| } |
| |
| void cmMakefile::AddLinkDirectory(std::string const& directory, bool before) |
| { |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| if (before) { |
| this->StateSnapshot.GetDirectory().PrependLinkDirectoriesEntry(directory, |
| lfbt); |
| } else { |
| this->StateSnapshot.GetDirectory().AppendLinkDirectoriesEntry(directory, |
| lfbt); |
| } |
| } |
| |
| 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 (const char* cdefs = this->GetProperty("COMPILE_DEFINITIONS")) { |
| // Expand the list. |
| std::vector<std::string> defs = cmExpandedList(cdefs); |
| |
| // Recompose the list without the definition. |
| auto defEnd = std::remove(defs.begin(), defs.end(), define); |
| auto defBegin = defs.begin(); |
| std::string ndefs = cmJoin(cmMakeRange(defBegin, defEnd), ";"); |
| |
| // Store the new list. |
| this->SetProperty("COMPILE_DEFINITIONS", ndefs.c_str()); |
| } |
| } 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->GetConfigurations(configs); |
| for (std::string const& config : configs) { |
| std::string defPropName = |
| cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config)); |
| const char* 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; |
| |
| // Recursion depth. |
| this->RecursionDepth = parent->RecursionDepth; |
| } |
| |
| 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(); |
| |
| this->CheckForUnusedVariables(); |
| } |
| |
| 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) |
| , ReportError(true) |
| { |
| 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; |
| }; |
| |
| 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(currentStart); |
| |
| BuildsystemFileScope scope(this); |
| |
| // make sure the CMakeFiles dir is there |
| std::string filesDir = cmStrCat( |
| this->StateSnapshot.GetDirectory().GetCurrentBinary(), "/CMakeFiles"); |
| cmSystemTools::MakeDirectory(filesDir); |
| |
| assert(cmSystemTools::FileExists(currentStart, true)); |
| this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentStart); |
| |
| cmListFile listFile; |
| if (!listFile.ParseFile(currentStart.c_str(), this->GetMessenger(), |
| this->Backtrace)) { |
| return; |
| } |
| if (this->IsRootMakefile()) { |
| bool hasVersion = false; |
| // search for the right policy command |
| for (cmListFileFunction const& func : listFile.Functions) { |
| if (func.Name.Lower == "cmake_minimum_required") { |
| hasVersion = true; |
| break; |
| } |
| } |
| // if no policy command is found this is an error if they use any |
| // non advanced functions or a lot of functions |
| if (!hasVersion) { |
| bool isProblem = true; |
| if (listFile.Functions.size() < 30) { |
| // the list of simple commands DO NOT ADD TO THIS LIST!!!!! |
| // these commands must have backwards compatibility forever and |
| // and that is a lot longer than your tiny mind can comprehend mortal |
| std::set<std::string> allowedCommands; |
| allowedCommands.insert("project"); |
| allowedCommands.insert("set"); |
| allowedCommands.insert("if"); |
| allowedCommands.insert("endif"); |
| allowedCommands.insert("else"); |
| allowedCommands.insert("elseif"); |
| allowedCommands.insert("add_executable"); |
| allowedCommands.insert("add_library"); |
| allowedCommands.insert("target_link_libraries"); |
| allowedCommands.insert("option"); |
| allowedCommands.insert("message"); |
| isProblem = false; |
| for (cmListFileFunction const& func : listFile.Functions) { |
| if (!cmContains(allowedCommands, func.Name.Lower)) { |
| 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. |
| this->SetPolicyVersion("2.4", std::string()); |
| } |
| } |
| bool hasProject = false; |
| // search for a project command |
| for (cmListFileFunction const& func : listFile.Functions) { |
| if (func.Name.Lower == "project") { |
| hasProject = true; |
| break; |
| } |
| } |
| // if no project command is found, add one |
| if (!hasProject) { |
| this->GetCMakeInstance()->IssueMessage( |
| MessageType::AUTHOR_WARNING, |
| "No project() command is present. The top-level CMakeLists.txt " |
| "file must contain a literal, direct call to the project() command. " |
| "Add a line of code such as\n" |
| " project(ProjectName)\n" |
| "near the top of the file, but after cmake_minimum_required().\n" |
| "CMake is pretending there is a \"project(Project)\" command on " |
| "the first line.", |
| this->Backtrace); |
| cmListFileFunction project; |
| project.Name.Lower = "project"; |
| project.Arguments.emplace_back("Project", cmListFileArgument::Unquoted, |
| 0); |
| project.Arguments.emplace_back("__CMAKE_INJECTED_PROJECT_COMMAND__", |
| cmListFileArgument::Unquoted, 0); |
| listFile.Functions.insert(listFile.Functions.begin(), project); |
| } |
| } |
| |
| this->ReadListFile(listFile, currentStart); |
| if (cmSystemTools::GetFatalErrorOccured()) { |
| scope.Quiet(); |
| } |
| |
| // at the end handle any old style subdirs |
| std::vector<cmMakefile*> subdirs = this->UnConfiguredDirectories; |
| |
| // for each subdir recurse |
| auto sdi = subdirs.begin(); |
| for (; sdi != subdirs.end(); ++sdi) { |
| (*sdi)->StateSnapshot.InitializeFromParent_ForSubdirsCommand(); |
| this->ConfigureSubDirectory(*sdi); |
| } |
| |
| this->AddCMakeDependFilesFromUser(); |
| } |
| |
| void cmMakefile::ConfigureSubDirectory(cmMakefile* mf) |
| { |
| mf->InitializeFromParent(this); |
| std::string currentStart = mf->GetCurrentSourceDirectory(); |
| if (this->GetCMakeInstance()->GetDebugOutput()) { |
| std::string msg = cmStrCat(" Entering ", currentStart); |
| cmSystemTools::Message(msg); |
| } |
| |
| std::string const currentStartFile = currentStart + "/CMakeLists.txt"; |
| if (!cmSystemTools::FileExists(currentStartFile, true)) { |
| // The file is missing. Check policy CMP0014. |
| std::ostringstream e; |
| /* clang-format off */ |
| e << "The source directory\n" |
| << " " << currentStart << "\n" |
| << "does not contain a CMakeLists.txt file."; |
| /* clang-format on */ |
| switch (this->GetPolicyStatus(cmPolicies::CMP0014)) { |
| case cmPolicies::WARN: |
| // Print the warning. |
| /* clang-format off */ |
| e << "\n" |
| << "CMake does not support this case but it used " |
| << "to work accidentally and is being allowed for " |
| << "compatibility." |
| << "\n" |
| << cmPolicies::GetPolicyWarning(cmPolicies::CMP0014); |
| /* clang-format on */ |
| this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); |
| 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()); |
| } |
| 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) |
| { |
| // 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); |
| |
| cmMakefile* subMf = new cmMakefile(this->GlobalGenerator, newSnapshot); |
| this->GetGlobalGenerator()->AddMakefile(subMf); |
| |
| if (excludeFromAll) { |
| subMf->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
| } |
| |
| if (immediate) { |
| this->ConfigureSubDirectory(subMf); |
| } else { |
| this->UnConfiguredDirectories.push_back(subMf); |
| } |
| |
| this->AddInstallGenerator(new cmInstallSubdirectoryGenerator( |
| subMf, binPath.c_str(), excludeFromAll)); |
| } |
| |
| 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; |
| } |
| |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| std::string entryString = cmJoin(incs, ";"); |
| if (before) { |
| this->StateSnapshot.GetDirectory().PrependIncludeDirectoriesEntry( |
| entryString, lfbt); |
| } else { |
| this->StateSnapshot.GetDirectory().AppendIncludeDirectoriesEntry( |
| entryString, lfbt); |
| } |
| |
| // Property on each target: |
| for (auto& target : this->Targets) { |
| cmTarget& t = target.second; |
| t.InsertInclude(entryString, lfbt, 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) |
| { |
| if (this->VariableInitialized(name)) { |
| this->LogUnused("changing definition", name); |
| } |
| this->StateSnapshot.SetDefinition(name, value); |
| |
| #ifndef CMAKE_BOOTSTRAP |
| cmVariableWatch* vv = this->GetVariableWatch(); |
| if (vv) { |
| vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, |
| value.data(), this); |
| } |
| #endif |
| } |
| |
| void cmMakefile::AddDefinitionBool(const std::string& name, bool value) |
| { |
| this->AddDefinition(name, value ? "ON" : "OFF"); |
| } |
| |
| void cmMakefile::AddCacheDefinition(const std::string& name, const char* value, |
| const char* doc, |
| cmStateEnums::CacheEntryType type, |
| bool force) |
| { |
| const std::string* existingValue = |
| this->GetState()->GetInitializedCacheValue(name); |
| // must be outside the following if() to keep it alive long enough |
| std::string nvalue; |
| |
| if (existingValue && |
| (this->GetState()->GetCacheEntryType(name) == |
| cmStateEnums::UNINITIALIZED)) { |
| // if this is not a force, then use the value from the cache |
| // if it is a force, then use the value being passed in |
| if (!force) { |
| value = existingValue->c_str(); |
| } |
| if (type == cmStateEnums::PATH || type == cmStateEnums::FILEPATH) { |
| std::vector<std::string>::size_type cc; |
| std::vector<std::string> files; |
| nvalue = value ? value : ""; |
| |
| cmExpandList(nvalue, files); |
| nvalue.clear(); |
| for (cc = 0; cc < files.size(); cc++) { |
| if (!cmIsOff(files[cc])) { |
| files[cc] = cmSystemTools::CollapseFullPath(files[cc]); |
| } |
| if (cc > 0) { |
| nvalue += ";"; |
| } |
| nvalue += files[cc]; |
| } |
| |
| this->GetCMakeInstance()->AddCacheEntry(name, nvalue.c_str(), doc, type); |
| nvalue = *this->GetState()->GetInitializedCacheValue(name); |
| value = nvalue.c_str(); |
| } |
| } |
| this->GetCMakeInstance()->AddCacheEntry(name, value, doc, type); |
| // if there was a definition then remove it |
| this->StateSnapshot.RemoveDefinition(name); |
| } |
| |
| void cmMakefile::CheckForUnusedVariables() const |
| { |
| if (!this->WarnUnused) { |
| return; |
| } |
| for (const std::string& key : this->StateSnapshot.UnusedKeys()) { |
| this->LogUnused("out of scope", key); |
| } |
| } |
| |
| 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::LogUnused(const char* reason, const std::string& name) const |
| { |
| if (this->WarnUnused) { |
| std::string path; |
| if (!this->ExecutionStatusStack.empty()) { |
| path = this->GetExecutionContext().FilePath; |
| } else { |
| path = cmStrCat(this->GetCurrentSourceDirectory(), "/CMakeLists.txt"); |
| } |
| |
| if (this->CheckSystemVars || this->IsProjectFile(path.c_str())) { |
| std::ostringstream msg; |
| msg << "unused variable (" << reason << ") \'" << name << "\'"; |
| this->IssueMessage(MessageType::AUTHOR_WARNING, msg.str()); |
| } |
| } |
| } |
| |
| void cmMakefile::RemoveDefinition(const std::string& name) |
| { |
| if (this->VariableInitialized(name)) { |
| this->LogUnused("unsetting", 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) |
| { |
| 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 (const char* linkLibsProp = this->GetProperty("LINK_LIBRARIES")) { |
| std::vector<std::string> linkLibs = cmExpandedList(linkLibsProp); |
| |
| for (auto j = linkLibs.begin(); j != linkLibs.end(); ++j) { |
| std::string libraryName = *j; |
| cmTargetLinkLibraryType libType = GENERAL_LibraryType; |
| if (libraryName == "optimized") { |
| libType = OPTIMIZED_LibraryType; |
| ++j; |
| libraryName = *j; |
| } else if (libraryName == "debug") { |
| libType = DEBUG_LibraryType; |
| ++j; |
| libraryName = *j; |
| } |
| // This is equivalent to the target_link_libraries plain signature. |
| target.AddLinkLibrary(*this, libraryName, libType); |
| target.AppendProperty( |
| "INTERFACE_LINK_LIBRARIES", |
| target.GetDebugGeneratorExpressions(libraryName, libType).c_str()); |
| } |
| } |
| } |
| |
| void cmMakefile::AddAlias(const std::string& lname, std::string const& tgtName) |
| { |
| this->AliasTargets[lname] = tgtName; |
| 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) |
| { |
| auto it = |
| this->Targets |
| .emplace(name, cmTarget(name, type, cmTarget::VisibilityNormal, this)) |
| .first; |
| this->OrderedTargets.push_back(&it->second); |
| this->GetGlobalGenerator()->IndexTarget(&it->second); |
| this->GetStateSnapshot().GetDirectory().AddNormalTargetName(name); |
| return &it->second; |
| } |
| |
| cmTarget* cmMakefile::AddNewUtilityTarget(const std::string& utilityName, |
| cmCommandOrigin origin, |
| bool excludeFromAll) |
| { |
| cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName); |
| target->SetIsGeneratorProvided(origin == cmCommandOrigin::Generator); |
| if (excludeFromAll) { |
| target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
| } |
| return target; |
| } |
| |
| namespace { |
| bool AnyOutputMatches(const std::string& name, |
| const std::vector<std::string>& outputs) |
| { |
| for (std::string const& output : outputs) { |
| std::string::size_type pos = output.rfind(name); |
| // If the output matches exactly |
| if (pos != std::string::npos && pos == output.size() - name.size() && |
| (pos == 0 || output[pos - 1] == '/')) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool AnyTargetCommandOutputMatches( |
| const std::string& name, const std::vector<cmCustomCommand>& commands) |
| { |
| for (cmCustomCommand const& command : commands) { |
| if (AnyOutputMatches(name, command.GetByproducts())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| cmTarget* cmMakefile::LinearGetTargetWithOutput(const std::string& name) const |
| { |
| // We go through the ordered vector of targets to get reproducible results |
| // should multiple names match. |
| for (cmTarget* t : this->OrderedTargets) { |
| // Does the output of any command match the source file name? |
| if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) { |
| return t; |
| } |
| if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) { |
| return t; |
| } |
| if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) { |
| return t; |
| } |
| } |
| return nullptr; |
| } |
| |
| cmSourceFile* cmMakefile::LinearGetSourceFileWithOutput( |
| const std::string& name, cmSourceOutputKind kind, bool& byproduct) const |
| { |
| // Outputs take precedence over byproducts. |
| byproduct = false; |
| cmSourceFile* fallback = nullptr; |
| |
| // Look through all the source files that have custom commands and see if the |
| // custom command has the passed source file as an output. |
| for (cmSourceFile* src : this->SourceFiles) { |
| // Does this source file have a custom command? |
| if (src->GetCustomCommand()) { |
| // Does the output of the custom command match the source file name? |
| if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) { |
| // Return the first matching output. |
| return src; |
| } |
| if (kind == cmSourceOutputKind::OutputOrByproduct) { |
| if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) { |
| // Do not return the source yet as there might be a matching output. |
| fallback = src; |
| } |
| } |
| } |
| } |
| |
| // Did we find a byproduct? |
| byproduct = fallback != nullptr; |
| return fallback; |
| } |
| |
| cmSourcesWithOutput cmMakefile::GetSourcesWithOutput( |
| const std::string& name) const |
| { |
| // Linear search? Also see GetSourceFileWithOutput for detail. |
| if (!cmSystemTools::FileIsFullPath(name)) { |
| cmSourcesWithOutput sources; |
| sources.Target = this->LinearGetTargetWithOutput(name); |
| sources.Source = this->LinearGetSourceFileWithOutput( |
| name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct); |
| return sources; |
| } |
| // Otherwise we use an efficient lookup map. |
| auto o = this->OutputToSource.find(name); |
| if (o != this->OutputToSource.end()) { |
| return o->second.Sources; |
| } |
| return {}; |
| } |
| |
| cmSourceFile* cmMakefile::GetSourceFileWithOutput( |
| const std::string& name, cmSourceOutputKind kind) const |
| { |
| // If the queried path is not absolute we use the backward compatible |
| // linear-time search for an output with a matching suffix. |
| if (!cmSystemTools::FileIsFullPath(name)) { |
| bool byproduct = false; |
| return this->LinearGetSourceFileWithOutput(name, kind, byproduct); |
| } |
| // Otherwise we use an efficient lookup map. |
| auto o = this->OutputToSource.find(name); |
| if (o != this->OutputToSource.end() && |
| (!o->second.Sources.SourceIsByproduct || |
| kind == cmSourceOutputKind::OutputOrByproduct)) { |
| // Source file could also be null pointer for example if we found the |
| // byproduct of a utility target or a PRE_BUILD, PRE_LINK, or POST_BUILD |
| // command of a target. |
| return o->second.Sources.Source; |
| } |
| return nullptr; |
| } |
| |
| bool cmMakefile::MightHaveCustomCommand(const std::string& name) const |
| { |
| // This will have to be changed for delaying custom command creation, because |
| // GetSourceFileWithOutput requires the command to be already created. |
| if (cmSourceFile* sf = this->GetSourceFileWithOutput(name)) { |
| if (sf->GetCustomCommand()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void cmMakefile::AddTargetByproducts( |
| cmTarget* target, const std::vector<std::string>& byproducts) |
| { |
| for (std::string const& o : byproducts) { |
| this->UpdateOutputToSourceMap(o, target); |
| } |
| } |
| |
| void cmMakefile::AddSourceOutputs(cmSourceFile* source, |
| const std::vector<std::string>& outputs, |
| const std::vector<std::string>& byproducts) |
| { |
| for (std::string const& o : outputs) { |
| this->UpdateOutputToSourceMap(o, source, false); |
| } |
| for (std::string const& o : byproducts) { |
| this->UpdateOutputToSourceMap(o, source, true); |
| } |
| } |
| |
| void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct, |
| cmTarget* target) |
| { |
| SourceEntry entry; |
| entry.Sources.Target = target; |
| |
| auto pr = this->OutputToSource.emplace(byproduct, entry); |
| if (!pr.second) { |
| SourceEntry& current = pr.first->second; |
| // Has the target already been set? |
| if (!current.Sources.Target) { |
| current.Sources.Target = target; |
| } else { |
| // Multiple custom commands/targets produce the same output (source file |
| // or target). See also comment in other UpdateOutputToSourceMap |
| // overload. |
| // |
| // TODO: Warn the user about this case. |
| } |
| } |
| } |
| |
| void cmMakefile::UpdateOutputToSourceMap(std::string const& output, |
| cmSourceFile* source, bool byproduct) |
| { |
| SourceEntry entry; |
| entry.Sources.Source = source; |
| entry.Sources.SourceIsByproduct = byproduct; |
| |
| auto pr = this->OutputToSource.emplace(output, entry); |
| if (!pr.second) { |
| SourceEntry& current = pr.first->second; |
| // Outputs take precedence over byproducts |
| if (!current.Sources.Source || |
| (current.Sources.SourceIsByproduct && !byproduct)) { |
| current.Sources.Source = source; |
| current.Sources.SourceIsByproduct = false; |
| } else { |
| // Multiple custom commands produce the same output but may |
| // be attached to a different source file (MAIN_DEPENDENCY). |
| // LinearGetSourceFileWithOutput would return the first one, |
| // so keep the mapping for the first one. |
| // |
| // TODO: Warn the user about this case. However, the VS 8 generator |
| // triggers it for separate generate.stamp rules in ZERO_CHECK and |
| // individual targets. |
| } |
| } |
| } |
| |
| #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) |
| { |
| const char* delimiter = this->GetDefinition("SOURCE_GROUP_DELIMITER"); |
| if (delimiter == nullptr) { |
| delimiter = "\\"; |
| } |
| return this->GetOrCreateSourceGroup(cmTokenize(name, delimiter)); |
| } |
| |
| /** |
| * 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; |
| |
| const char* includeDirs = this->GetProperty("INCLUDE_DIRECTORIES"); |
| if (mightExpandVariablesCMP0019(includeDirs)) { |
| 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.c_str()); |
| } |
| |
| // 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 (mightExpandVariablesCMP0019(includeDirs)) { |
| 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.c_str()); |
| } |
| } |
| |
| if (const char* linkDirsProp = this->GetProperty("LINK_DIRECTORIES")) { |
| if (mightExpandVariablesCMP0019(linkDirsProp)) { |
| std::string d = linkDirsProp; |
| std::string orig = linkDirsProp; |
| 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 (const char* linkLibsProp = this->GetProperty("LINK_LIBRARIES")) { |
| std::vector<std::string> linkLibs = cmExpandedList(linkLibsProp); |
| |
| for (auto l = linkLibs.begin(); l != linkLibs.end(); ++l) { |
| std::string libName = *l; |
| if (libName == "optimized") { |
| ++l; |
| libName = *l; |
| } else if (libName == "debug") { |
| ++l; |
| libName = *l; |
| } |
| if (mightExpandVariablesCMP0019(libName.c_str())) { |
| 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 |
| { |
| const char* value = this->GetDefinition(name); |
| return cmIsOn(value); |
| } |
| |
| bool cmMakefile::IsSet(const std::string& name) const |
| { |
| const char* value = this->GetDefinition(name); |
| if (!value) { |
| return false; |
| } |
| |
| if (!*value) { |
| return false; |
| } |
| |
| if (cmIsNOTFOUND(value)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool cmMakefile::PlatformIs32Bit() const |
| { |
| if (const char* plat_abi = |
| this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { |
| if (strcmp(plat_abi, "ELF X32") == 0) { |
| return false; |
| } |
| } |
| if (const char* sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { |
| return atoi(sizeof_dptr) == 4; |
| } |
| return false; |
| } |
| |
| bool cmMakefile::PlatformIs64Bit() const |
| { |
| if (const char* sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { |
| return atoi(sizeof_dptr) == 8; |
| } |
| return false; |
| } |
| |
| bool cmMakefile::PlatformIsx32() const |
| { |
| if (const char* plat_abi = |
| this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { |
| if (strcmp(plat_abi, "ELF X32") == 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| cmMakefile::AppleSDK cmMakefile::GetAppleSDKType() const |
| { |
|