| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmMakefile.h" |
| |
| #include "cmsys/FStream.hxx" |
| #include "cmsys/RegularExpression.hxx" |
| #include <algorithm> |
| #include <assert.h> |
| #include <cstring> |
| #include <ctype.h> |
| #include <iterator> |
| #include <memory> // IWYU pragma: keep |
| #include <sstream> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <utility> |
| |
| #include "cmAlgorithms.h" |
| #include "cmCommand.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 "cmGeneratorExpression.h" |
| #include "cmGeneratorExpressionEvaluationFile.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmInstallGenerator.h" // IWYU pragma: keep |
| #include "cmListFileCache.h" |
| #include "cmSourceFile.h" |
| #include "cmSourceFileLocation.h" |
| #include "cmState.h" |
| #include "cmStateDirectory.h" |
| #include "cmStateTypes.h" |
| #include "cmSystemTools.h" |
| #include "cmTargetLinkLibraryType.h" |
| #include "cmTest.h" |
| #include "cmTestGenerator.h" // IWYU pragma: keep |
| #include "cmVersion.h" |
| #include "cmWorkingDirectory.h" |
| #include "cm_sys_stat.h" |
| #include "cmake.h" |
| |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| # 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->SuppressWatches = 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_BUILD_WITH_CMAKE) |
| this->AddSourceGroup("", "^.*$"); |
| this->AddSourceGroup("Source Files", CM_SOURCE_REGEX); |
| this->AddSourceGroup("Header Files", CM_HEADER_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->FinalPassCommands); |
| cmDeleteAll(this->FunctionBlockers); |
| 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(cmake::MessageType t, |
| std::string const& text) const |
| { |
| if (!this->ExecutionStatusStack.empty()) { |
| if ((t == cmake::FATAL_ERROR) || (t == cmake::INTERNAL_ERROR)) { |
| this->ExecutionStatusStack.back()->SetNestedError(); |
| } |
| } |
| this->GetCMakeInstance()->IssueMessage(t, text, this->GetBacktrace()); |
| } |
| |
| bool cmMakefile::CheckCMP0037(std::string const& targetName, |
| cmStateEnums::TargetType targetType) const |
| { |
| cmake::MessageType messageType = cmake::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 = cmake::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 == cmake::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(cmake::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::vector<std::string>::const_iterator i = |
| trace_only_this_files.begin(); |
| !trace && i != trace_only_this_files.end(); ++i) { |
| std::string::size_type const pos = full_path.rfind(*i); |
| trace = (pos != std::string::npos) && |
| ((pos + i->size()) == full_path.size()) && |
| (only_filename == cmSystemTools::GetFilenameName(*i)); |
| } |
| // 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 << ")"; |
| cmSystemTools::Message(msg.str().c_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->ExecutionStatusStack.push_back(&status); |
| } |
| |
| ~cmMakefileCall() |
| { |
| this->Makefile->ExecutionStatusStack.pop_back(); |
| this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); |
| } |
| |
| private: |
| cmMakefile* Makefile; |
| }; |
| |
| bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, |
| cmExecutionStatus& status) |
| { |
| bool result = true; |
| |
| // quick return if blocked |
| if (this->IsFunctionBlocked(lff, status)) { |
| // No error. |
| return result; |
| } |
| |
| // Place this call on the call stack. |
| cmMakefileCall stack_manager(this, lff, status); |
| static_cast<void>(stack_manager); |
| |
| // Lookup the command prototype. |
| if (cmCommand* proto = |
| this->GetState()->GetCommandByExactName(lff.Name.Lower)) { |
| // Clone the prototype. |
| std::unique_ptr<cmCommand> pcmd(proto->Clone()); |
| pcmd->SetMakefile(this); |
| |
| // 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 = pcmd->InvokeInitialPass(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) + " " + pcmd->GetError(); |
| this->IssueMessage(cmake::FATAL_ERROR, error); |
| } |
| result = false; |
| if (this->GetCMakeInstance()->GetWorkingMode() != cmake::NORMAL_MODE) { |
| cmSystemTools::SetFatalErrorOccured(); |
| } |
| } else if (pcmd->HasFinalPass()) { |
| // use the command |
| this->FinalPassCommands.push_back(pcmd.release()); |
| } |
| } |
| } else { |
| if (!cmSystemTools::GetFatalErrorOccured()) { |
| std::string error = "Unknown CMake command \""; |
| error += lff.Name.Original; |
| error += "\"."; |
| this->IssueMessage(cmake::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; } |
| |
| 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(cmake::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(cmake::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 char* filename, bool noPolicyScope) |
| { |
| this->AddDefinition("CMAKE_PARENT_LIST_FILE", |
| this->GetDefinition("CMAKE_CURRENT_LIST_FILE")); |
| 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; } |
| |
| private: |
| cmMakefile* Makefile; |
| bool ReportError; |
| }; |
| |
| bool cmMakefile::ReadListFile(const char* 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.c_str()); |
| this->AddDefinition("CMAKE_CURRENT_LIST_DIR", |
| cmSystemTools::GetFilenamePath(filenametoread).c_str()); |
| |
| 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->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.c_str()); |
| this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile.c_str()); |
| this->AddDefinition("CMAKE_CURRENT_LIST_DIR", |
| cmSystemTools::GetFilenamePath(currentFile).c_str()); |
| 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(cmake::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(cmake::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) |
| { |
| std::vector<cmExportBuildFileGenerator*>::iterator 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::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 (cmCommand* fpCommand : this->FinalPassCommands) { |
| fpCommand->FinalPass(); |
| } |
| |
| // 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( |
| cmake::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); |
| } |
| } |
| |
| void cmMakefile::AddCustomCommandToTarget( |
| const std::string& target, const std::vector<std::string>& byproducts, |
| const std::vector<std::string>& depends, |
| const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type, |
| const char* comment, const char* workingDir, bool escapeOldStyle, |
| bool uses_terminal, const std::string& depfile, bool command_expand_lists, |
| ObjectLibraryCommands objLibraryCommands) |
| { |
| // Find the target to which to add the custom command. |
| cmTargets::iterator ti = this->Targets.find(target); |
| |
| if (ti == this->Targets.end()) { |
| cmake::MessageType messageType = cmake::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 = cmake::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."; |
| } |
| IssueMessage(messageType, e.str()); |
| } |
| |
| return; |
| } |
| |
| cmTarget& t = ti->second; |
| if (objLibraryCommands == RejectObjectLibraryCommands && |
| 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(cmake::FATAL_ERROR, e.str()); |
| return; |
| } |
| 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(cmake::FATAL_ERROR, e.str()); |
| return; |
| } |
| |
| // Always create the byproduct sources and mark them generated. |
| for (std::string const& o : byproducts) { |
| if (cmSourceFile* out = this->GetOrCreateSource(o, true)) { |
| out->SetProperty("GENERATED", "1"); |
| } |
| } |
| |
| // 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); |
| switch (type) { |
| case cmTarget::PRE_BUILD: |
| t.AddPreBuildCommand(cc); |
| break; |
| case cmTarget::PRE_LINK: |
| t.AddPreLinkCommand(cc); |
| break; |
| case cmTarget::POST_BUILD: |
| t.AddPostBuildCommand(cc); |
| break; |
| } |
| } |
| |
| 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 cmCustomCommandLines& commandLines, const char* comment, |
| const char* workingDir, bool replace, bool escapeOldStyle, |
| bool uses_terminal, bool command_expand_lists, const std::string& depfile) |
| { |
| // 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. 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(cmake::FATAL_ERROR, e.str()); |
| return nullptr; |
| } |
| } |
| |
| // 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.c_str(), |
| "\" 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"); |
| } |
| |
| // Always create the output sources and mark them generated. |
| for (std::string const& o : outputs) { |
| if (cmSourceFile* out = |
| this->GetOrCreateSource(o, true, cmSourceFileLocationKind::Known)) { |
| out->SetProperty("GENERATED", "1"); |
| } |
| } |
| for (std::string const& o : byproducts) { |
| if (cmSourceFile* out = |
| this->GetOrCreateSource(o, true, cmSourceFileLocationKind::Known)) { |
| out->SetProperty("GENERATED", "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); |
| } |
| |
| cmCustomCommand* cc = new cmCustomCommand( |
| this, outputs, byproducts, depends2, commandLines, comment, workingDir); |
| cc->SetEscapeOldStyle(escapeOldStyle); |
| cc->SetEscapeAllowMakeVars(true); |
| cc->SetUsesTerminal(uses_terminal); |
| cc->SetCommandExpandLists(command_expand_lists); |
| cc->SetDepfile(depfile); |
| file->SetCustomCommand(cc); |
| this->UpdateOutputToSourceMap(outputs, file); |
| } |
| return file; |
| } |
| |
| void cmMakefile::UpdateOutputToSourceMap( |
| std::vector<std::string> const& outputs, cmSourceFile* source) |
| { |
| for (std::string const& o : outputs) { |
| this->UpdateOutputToSourceMap(o, source); |
| } |
| } |
| |
| void cmMakefile::UpdateOutputToSourceMap(std::string const& output, |
| cmSourceFile* source) |
| { |
| OutputToSourceMap::iterator i = this->OutputToSource.find(output); |
| if (i != this->OutputToSource.end()) { |
| // 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. |
| return; |
| } |
| this->OutputToSource[output] = source; |
| } |
| |
| 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) |
| { |
| std::vector<std::string> outputs; |
| outputs.push_back(output); |
| std::vector<std::string> no_byproducts; |
| return this->AddCustomCommandToOutput( |
| outputs, no_byproducts, depends, main_dependency, commandLines, comment, |
| workingDir, replace, escapeOldStyle, uses_terminal, command_expand_lists, |
| depfile); |
| } |
| |
| 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, cmTarget::POST_BUILD, comment, |
| nullptr); |
| return; |
| } |
| |
| // 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)$"); |
| for (std::string const& oi : outputs) { |
| // Get the name of this output. |
| const char* output = oi.c_str(); |
| cmSourceFile* sf; |
| |
| // Choose whether to use a main dependency. |
| if (sourceFiles.find(source)) { |
| // The source looks like a real file. Use it as the main dependency. |
| sf = this->AddCustomCommandToOutput(output, depends, source, |
| commandLines, comment, nullptr); |
| } else { |
| // The source may not be a real file. Do not use a main dependency. |
| std::string no_main_dependency; |
| std::vector<std::string> depends2 = depends; |
| depends2.push_back(source); |
| sf = this->AddCustomCommandToOutput(output, depends2, no_main_dependency, |
| commandLines, comment, nullptr); |
| } |
| |
| // 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 && !sf->GetPropertyAsBool("__CMAKE_RULE")) { |
| cmTargets::iterator ti = this->Targets.find(target); |
| if (ti != this->Targets.end()) { |
| ti->second.AddSource(sf->GetFullPath()); |
| } else { |
| cmSystemTools::Error("Attempt to add a custom rule to a target " |
| "that does not exist yet for target ", |
| target.c_str()); |
| return; |
| } |
| } |
| } |
| } |
| |
| cmTarget* cmMakefile::AddUtilityCommand( |
| const std::string& utilityName, TargetOrigin origin, bool excludeFromAll, |
| const std::vector<std::string>& depends, const char* workingDirectory, |
| const char* command, const char* arg1, const char* arg2, const char* arg3, |
| const char* arg4) |
| { |
| // Construct the command line for the custom command. |
| cmCustomCommandLine commandLine; |
| commandLine.push_back(command); |
| if (arg1) { |
| commandLine.push_back(arg1); |
| } |
| if (arg2) { |
| commandLine.push_back(arg2); |
| } |
| if (arg3) { |
| commandLine.push_back(arg3); |
| } |
| if (arg4) { |
| commandLine.push_back(arg4); |
| } |
| cmCustomCommandLines commandLines; |
| commandLines.push_back(std::move(commandLine)); |
| |
| // Call the real signature of this method. |
| return this->AddUtilityCommand(utilityName, origin, excludeFromAll, |
| workingDirectory, depends, commandLines); |
| } |
| |
| cmTarget* cmMakefile::AddUtilityCommand( |
| const std::string& utilityName, TargetOrigin origin, bool excludeFromAll, |
| const char* workingDirectory, const std::vector<std::string>& depends, |
| const cmCustomCommandLines& commandLines, bool escapeOldStyle, |
| const char* comment, bool uses_terminal, bool command_expand_lists) |
| { |
| std::vector<std::string> no_byproducts; |
| return this->AddUtilityCommand(utilityName, origin, excludeFromAll, |
| workingDirectory, no_byproducts, depends, |
| commandLines, escapeOldStyle, comment, |
| uses_terminal, command_expand_lists); |
| } |
| |
| cmTarget* cmMakefile::AddUtilityCommand( |
| const std::string& utilityName, TargetOrigin 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) |
| { |
| // Create a target instance for this utility. |
| cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName); |
| target->SetIsGeneratorProvided(origin == TargetOrigin::Generator); |
| if (excludeFromAll) { |
| target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
| } |
| if (!comment) { |
| // Use an empty comment to avoid generation of default comment. |
| comment = ""; |
| } |
| |
| // Store the custom command in the target. |
| if (!commandLines.empty() || !depends.empty()) { |
| std::string force = this->GetCurrentBinaryDirectory(); |
| force += cmake::GetCMakeFilesDirectory(); |
| force += "/"; |
| force += utilityName; |
| std::vector<std::string> forced; |
| forced.push_back(force); |
| std::string no_main_dependency; |
| bool no_replace = false; |
| this->AddCustomCommandToOutput( |
| forced, byproducts, depends, no_main_dependency, commandLines, comment, |
| workingDirectory, no_replace, escapeOldStyle, uses_terminal, |
| command_expand_lists); |
| cmSourceFile* sf = target->AddSourceCMP0049(force); |
| |
| // 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.c_str()); |
| } |
| |
| // Always create the byproduct sources and mark them generated. |
| for (std::string const& byproduct : byproducts) { |
| if (cmSourceFile* out = this->GetOrCreateSource( |
| byproduct, true, cmSourceFileLocationKind::Known)) { |
| out->SetProperty("GENERATED", "1"); |
| } |
| } |
| } |
| 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.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(cmake::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( |
| cmake::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; |
| cmSystemTools::ExpandListArgument(cdefs, defs); |
| |
| // Recompose the list without the definition. |
| std::vector<std::string>::const_iterator defEnd = |
| std::remove(defs.begin(), defs.end(), define); |
| std::vector<std::string>::const_iterator 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 = "COMPILE_DEFINITIONS_"; |
| defPropName += 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; |
| } |
| |
| 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_BUILD_WITH_CMAKE) |
| 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_BUILD_WITH_CMAKE) |
| 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 = |
| this->Makefile->StateSnapshot.GetDirectory().GetCurrentSource(); |
| currentStart += "/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_BUILD_WITH_CMAKE) |
| this->GG->GetFileLockPool().PushFileScope(); |
| #endif |
| } |
| |
| ~BuildsystemFileScope() |
| { |
| this->Makefile->PopFunctionBlockerBarrier(this->ReportError); |
| this->Makefile->PopSnapshot(this->ReportError); |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| this->GG->GetFileLockPool().PopFileScope(); |
| #endif |
| this->GG->SetCurrentMakefile(this->CurrentMakefile); |
| this->GG->GetCMakeInstance()->SetCurrentSnapshot(this->Snapshot); |
| } |
| |
| void Quiet() { this->ReportError = false; } |
| |
| private: |
| cmMakefile* Makefile; |
| cmGlobalGenerator* GG; |
| cmMakefile* CurrentMakefile; |
| cmStateSnapshot Snapshot; |
| bool ReportError; |
| }; |
| |
| void cmMakefile::Configure() |
| { |
| std::string currentStart = |
| this->StateSnapshot.GetDirectory().GetCurrentSource(); |
| currentStart += "/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 = this->StateSnapshot.GetDirectory().GetCurrentBinary(); |
| filesDir += cmake::GetCMakeFilesDirectory(); |
| cmSystemTools::MakeDirectory(filesDir); |
| |
| assert(cmSystemTools::FileExists(currentStart, true)); |
| this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentStart.c_str()); |
| |
| 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 (allowedCommands.find(func.Name.Lower) == allowedCommands.end()) { |
| 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) { |
| 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 |
| std::vector<cmMakefile*>::iterator 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 = " Entering "; |
| msg += currentStart; |
| cmSystemTools::Message(msg.c_str()); |
| } |
| |
| 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(cmake::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(cmake::FATAL_ERROR, e.str()); |
| } |
| return; |
| } |
| // finally configure the subdir |
| mf->Configure(); |
| |
| if (this->GetCMakeInstance()->GetDebugOutput()) { |
| std::string msg = " Returning to "; |
| msg += this->GetCurrentSourceDirectory(); |
| cmSystemTools::Message(msg.c_str()); |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| 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, const char* value) |
| { |
| if (!value) { |
| return; |
| } |
| |
| if (this->VariableInitialized(name)) { |
| this->LogUnused("changing definition", name); |
| } |
| this->StateSnapshot.SetDefinition(name, value); |
| |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| cmVariableWatch* vv = this->GetVariableWatch(); |
| if (vv) { |
| vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, |
| value, this); |
| } |
| #endif |
| } |
| |
| 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 : ""; |
| |
| cmSystemTools::ExpandListArgument(nvalue, files); |
| nvalue.clear(); |
| for (cc = 0; cc < files.size(); cc++) { |
| if (!cmSystemTools::IsOff(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::AddDefinition(const std::string& name, bool value) |
| { |
| if (this->VariableInitialized(name)) { |
| this->LogUnused("changing definition", name); |
| } |
| |
| this->StateSnapshot.SetDefinition(name, value ? "ON" : "OFF"); |
| |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| cmVariableWatch* vv = this->GetVariableWatch(); |
| if (vv) { |
| vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, |
| value ? "ON" : "OFF", this); |
| } |
| #endif |
| } |
| |
| void cmMakefile::CheckForUnusedVariables() const |
| { |
| if (!this->WarnUnused) { |
| return; |
| } |
| const std::vector<std::string>& unused = this->StateSnapshot.UnusedKeys(); |
| std::vector<std::string>::const_iterator it = unused.begin(); |
| for (; it != unused.end(); ++it) { |
| this->LogUnused("out of scope", *it); |
| } |
| } |
| |
| 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::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 = this->GetCurrentSourceDirectory(); |
| path += "/CMakeLists.txt"; |
| } |
| |
| if (this->CheckSystemVars || |
| cmSystemTools::IsSubDirectory(path, this->GetHomeDirectory()) || |
| (cmSystemTools::IsSubDirectory(path, this->GetHomeOutputDirectory()) && |
| !cmSystemTools::IsSubDirectory(path, |
| cmake::GetCMakeFilesDirectory()))) { |
| std::ostringstream msg; |
| msg << "unused variable (" << reason << ") \'" << name << "\'"; |
| this->IssueMessage(cmake::AUTHOR_WARNING, msg.str()); |
| } |
| } |
| } |
| |
| void cmMakefile::RemoveDefinition(const std::string& name) |
| { |
| if (this->VariableInitialized(name)) { |
| this->LogUnused("unsetting", name); |
| } |
| this->StateSnapshot.RemoveDefinition(name); |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| 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; |
| cmSystemTools::ExpandListArgument(linkLibsProp, linkLibs); |
| |
| for (std::vector<std::string>::iterator 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) |
| { |
| cmTargets::iterator it = |
| this->Targets |
| .insert(cmTargets::value_type( |
| name, cmTarget(name, type, cmTarget::VisibilityNormal, this))) |
| .first; |
| this->GetGlobalGenerator()->IndexTarget(&it->second); |
| this->GetStateSnapshot().GetDirectory().AddNormalTargetName(name); |
| return &it->second; |
| } |
| |
| cmSourceFile* cmMakefile::LinearGetSourceFileWithOutput( |
| const std::string& name) const |
| { |
| std::string out; |
| |
| // 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? |
| const std::vector<std::string>& outputs = |
| src->GetCustomCommand()->GetOutputs(); |
| for (std::string const& output : outputs) { |
| out = output; |
| std::string::size_type pos = out.rfind(name); |
| // If the output matches exactly |
| if (pos != std::string::npos && pos == out.size() - name.size() && |
| (pos == 0 || out[pos - 1] == '/')) { |
| return src; |
| } |
| } |
| } |
| } |
| |
| // otherwise return NULL |
| return nullptr; |
| } |
| |
| cmSourceFile* cmMakefile::GetSourceFileWithOutput( |
| const std::string& name) 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)) { |
| return this->LinearGetSourceFileWithOutput(name); |
| } |
| // Otherwise we use an efficient lookup map. |
| OutputToSourceMap::const_iterator o = this->OutputToSource.find(name); |
| if (o != this->OutputToSource.end()) { |
| return (*o).second; |
| } |
| return nullptr; |
| } |
| |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| 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.push_back(cmSourceGroup(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( |
| cmSystemTools::tokenize(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 (std::vector<cmSourceGroup>::reverse_iterator 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 (std::vector<cmSourceGroup>::reverse_iterator 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.front(); |
| } |
| #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; |
| cmSystemTools::ExpandListArgument(linkLibsProp, linkLibs); |
| |
| for (std::vector<std::string>::iterator 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(cmake::AUTHOR_WARNING, m.str(), |
| this->Backtrace); |
| } |
| } |
| |
| bool cmMakefile::IsOn(const std::string& name) const |
| { |
| const char* value = this->GetDefinition(name); |
| return cmSystemTools::IsOn(value); |
| } |
| |
| bool cmMakefile::IsSet(const std::string& name) const |
| { |
| const char* value = this->GetDefinition(name); |
| if (!value) { |
| return false; |
| } |
| |
| if (!*value) { |
| return false; |
| } |
| |
| if (cmSystemTools::IsNOTFOUND(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 |
| { |
| std::string sdkRoot; |
| sdkRoot = this->GetSafeDefinition("CMAKE_OSX_SYSROOT"); |
| sdkRoot = cmSystemTools::LowerCase(sdkRoot); |
| |
| struct |
| { |
| std::string name; |
| AppleSDK sdk; |
| } const sdkDatabase[]{ |
| { "appletvos", AppleSDK::AppleTVOS }, |
| { "appletvsimulator", AppleSDK::AppleTVSimulator }, |
| { "iphoneos", AppleSDK::IPhoneOS }, |
| { "iphonesimulator", AppleSDK::IPhoneSimulator }, |
| { "watchos", AppleSDK::WatchOS }, |
| { "watchsimulator", AppleSDK::WatchSimulator }, |
| }; |
| |
| for (auto entry : sdkDatabase) { |
| if (sdkRoot.find(entry.name) == 0 || |
| sdkRoot.find(std::string("/") + entry.name) != std::string::npos) { |
| return entry.sdk; |
| } |
| } |
| |
| return AppleSDK::MacOS; |
| } |
| |
| bool cmMakefile::PlatformIsAppleEmbedded() const |
| { |
| return GetAppleSDKType() != AppleSDK::MacOS; |
| } |
| |
| const char* cmMakefile::GetSONameFlag(const std::string& language) const |
| { |
| std::string name = "CMAKE_SHARED_LIBRARY_SONAME"; |
| if (!language.empty()) { |
| name += "_"; |
| name += language; |
| } |
| name += "_FLAG"; |
| return GetDefinition(name); |
| } |
| |
| 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()); |
| } |
| |
| std::string cmMakefile::GetRequiredDefinition(const std::string& name) const |
| { |
| const char* ret = this->GetDefinition(name); |
| if (!ret) { |
| cmSystemTools::Error("Error required internal CMake variable not " |
| "set, cmake may not be built correctly.\n", |
| "Missing variable is:\n", name.c_str()); |
| return std::string(); |
| } |
| return std::string(ret); |
| } |
| |
| bool cmMakefile::IsDefinitionSet(const std::string& name) const |
| { |
| const std::string* def = this->StateSnapshot.GetDefinition(name); |
| if (!def) { |
| def = this->GetState()->GetInitializedCacheValue(name); |
| } |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| if (cmVariableWatch* vv = this->GetVariableWatch()) { |
| if (!def) { |
| vv->VariableAccessed( |
| name, cmVariableWatch::UNKNOWN_VARIABLE_DEFINED_ACCESS, nullptr, this); |
| } |
| } |
| #endif |
| return def != nullptr; |
| } |
| |
| const std::string* cmMakefile::GetDef(const std::string& name) const |
| { |
| const std::string* def = this->StateSnapshot.GetDefinition(name); |
| if (!def) { |
| def = this->GetState()->GetInitializedCacheValue(name); |
| } |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| cmVariableWatch* vv = this->GetVariableWatch(); |
| if (vv && !this->SuppressWatches) { |
| bool const watch_function_executed = |
| vv->VariableAccessed(name, |
| def ? cmVariableWatch::VARIABLE_READ_ACCESS |
| : cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS, |
| (def ? def->c_str() : nullptr), 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 char* cmMakefile::GetDefinition(const std::string& name) const |
| { |
| const std::string* def = GetDef(name); |
| if (!def) { |
| return nullptr; |
| } |
| return def->c_str(); |
| } |
| |
| const std::string& cmMakefile::GetSafeDefinition(const std::string& name) const |
| { |
| static std::string const empty; |
| const std::string* def = GetDef(name); |
| if (!def) { |
| return empty; |
| } |
| return *def; |
| } |
| |
| std::vector<std::string> cmMakefile::GetDefinitions() const |
| { |
| std::vector<std::string> res = this->StateSnapshot.ClosureKeys(); |
| std::vector<std::string> cacheKeys = this->GetState()->GetCacheEntryKeys(); |
| res.insert(res.end(), cacheKeys.begin(), cacheKeys.end()); |
| 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; |
| cmake::MessageType mtype = cmake::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(cmake::INTERNAL_ERROR, |
| "ExpandVariablesInString @ONLY called " |
| "on something with escapes."); |
| return source; |
| } |
| |
| // Variables used in the WARN case. |
| std::string newResult; |
| std::string newErrorstr; |
| cmake::MessageType newError = cmake::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->SuppressWatches = true; |
| newError = ExpandVariablesInStringNew( |
| newErrorstr, newResult, escapeQuotes, noEscapes, atOnly, filename, |
| line, removeEmpty, replaceAt); |
| this->SuppressWatches = false; |
| CM_FALLTHROUGH; |
| } |
| case cmPolicies::OLD: |
| mtype = |
| 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 = ExpandVariablesInStringNew(errorstr, source, escapeQuotes, |
| noEscapes, atOnly, filename, line, |
| removeEmpty, replaceAt); |
| break; |
| } |
| |
| // If it's an error in either case, just report the error... |
| if (mtype != cmake::LOG) { |
| if (mtype == cmake::FATAL_ERROR) { |
| cmSystemTools::SetFatalErrorOccured(); |
| } |
| 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 = cmPolicies::GetPolicyWarning(cmPolicies::CMP0053); |
| msg += "\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(cmake::AUTHOR_WARNING, msg); |
| } |
| |
| return source; |
| } |
| |
| cmake::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 cmake::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(); |
| |
| |