| /*============================================================================ |
| CMake - Cross Platform Makefile Generator |
| Copyright 2000-2009 Kitware, Inc., Insight Software Consortium |
| |
| Distributed under the OSI-approved BSD License (the "License"); |
| see accompanying file Copyright.txt for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even the |
| implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the License for more information. |
| ============================================================================*/ |
| #include "cmMakefile.h" |
| #include "cmVersion.h" |
| #include "cmCommand.h" |
| #include "cmSourceFile.h" |
| #include "cmSourceFileLocation.h" |
| #include "cmSystemTools.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmLocalGenerator.h" |
| #include "cmCommands.h" |
| #include "cmCacheManager.h" |
| #include "cmFunctionBlocker.h" |
| #include "cmListFileCache.h" |
| #include "cmCommandArgumentParserHelper.h" |
| #include "cmGeneratorExpression.h" |
| #include "cmTest.h" |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| # include "cmVariableWatch.h" |
| #endif |
| #include "cmInstallGenerator.h" |
| #include "cmTestGenerator.h" |
| #include "cmDefinitions.h" |
| #include "cmake.h" |
| #include <stdlib.h> // required for atoi |
| |
| #include <cmsys/RegularExpression.hxx> |
| #include <cmsys/FStream.hxx> |
| #include <cmsys/auto_ptr.hxx> |
| |
| #include <stack> |
| #include <ctype.h> // for isspace |
| #include <assert.h> |
| |
| class cmMakefile::Internals |
| { |
| public: |
| std::stack<cmDefinitions, std::list<cmDefinitions> > VarStack; |
| std::stack<std::set<std::string> > VarInitStack; |
| std::stack<std::set<std::string> > VarUsageStack; |
| bool IsSourceFileTryCompile; |
| }; |
| |
| // default is not to be building executables |
| cmMakefile::cmMakefile(): Internal(new Internals) |
| { |
| const cmDefinitions& defs = cmDefinitions(); |
| const std::set<std::string> globalKeys = defs.LocalKeys(); |
| this->Internal->VarStack.push(defs); |
| this->Internal->VarInitStack.push(globalKeys); |
| this->Internal->VarUsageStack.push(globalKeys); |
| this->Internal->IsSourceFileTryCompile = false; |
| |
| // Initialize these first since AddDefaultDefinitions calls AddDefinition |
| this->WarnUnused = false; |
| this->CheckSystemVars = false; |
| |
| // Setup the default include file regular expression (match everything). |
| this->IncludeFileRegularExpression = "^.*$"; |
| // Setup the default include complaint regular expression (match nothing). |
| this->ComplainFileRegularExpression = "^$"; |
| // Source and header file extensions that we can handle |
| |
| // Set up a list of source and header extensions |
| // these are used to find files when the extension |
| // is not given |
| // The "c" extension MUST precede the "C" extension. |
| this->SourceFileExtensions.push_back( "c" ); |
| this->SourceFileExtensions.push_back( "C" ); |
| |
| this->SourceFileExtensions.push_back( "c++" ); |
| this->SourceFileExtensions.push_back( "cc" ); |
| this->SourceFileExtensions.push_back( "cpp" ); |
| this->SourceFileExtensions.push_back( "cxx" ); |
| this->SourceFileExtensions.push_back( "m" ); |
| this->SourceFileExtensions.push_back( "M" ); |
| this->SourceFileExtensions.push_back( "mm" ); |
| |
| this->HeaderFileExtensions.push_back( "h" ); |
| this->HeaderFileExtensions.push_back( "hh" ); |
| this->HeaderFileExtensions.push_back( "h++" ); |
| this->HeaderFileExtensions.push_back( "hm" ); |
| this->HeaderFileExtensions.push_back( "hpp" ); |
| this->HeaderFileExtensions.push_back( "hxx" ); |
| this->HeaderFileExtensions.push_back( "in" ); |
| this->HeaderFileExtensions.push_back( "txx" ); |
| |
| this->DefineFlags = " "; |
| this->LocalGenerator = 0; |
| |
| this->AddDefaultDefinitions(); |
| this->Initialize(); |
| this->PreOrder = false; |
| this->GeneratingBuildSystem = false; |
| |
| this->SuppressWatches = false; |
| } |
| |
| cmMakefile::cmMakefile(const cmMakefile& mf): Internal(new Internals) |
| { |
| this->Internal->VarStack.push(mf.Internal->VarStack.top().Closure()); |
| this->Internal->VarInitStack.push(mf.Internal->VarInitStack.top()); |
| this->Internal->VarUsageStack.push(mf.Internal->VarUsageStack.top()); |
| |
| this->Prefix = mf.Prefix; |
| this->AuxSourceDirectories = mf.AuxSourceDirectories; |
| this->cmStartDirectory = mf.cmStartDirectory; |
| this->StartOutputDirectory = mf.StartOutputDirectory; |
| this->cmHomeDirectory = mf.cmHomeDirectory; |
| this->HomeOutputDirectory = mf.HomeOutputDirectory; |
| this->cmCurrentListFile = mf.cmCurrentListFile; |
| this->ProjectName = mf.ProjectName; |
| this->Targets = mf.Targets; |
| this->SourceFiles = mf.SourceFiles; |
| this->Tests = mf.Tests; |
| this->LinkDirectories = mf.LinkDirectories; |
| this->SystemIncludeDirectories = mf.SystemIncludeDirectories; |
| this->ListFiles = mf.ListFiles; |
| this->OutputFiles = mf.OutputFiles; |
| this->LinkLibraries = mf.LinkLibraries; |
| this->InstallGenerators = mf.InstallGenerators; |
| this->TestGenerators = mf.TestGenerators; |
| this->IncludeFileRegularExpression = mf.IncludeFileRegularExpression; |
| this->ComplainFileRegularExpression = mf.ComplainFileRegularExpression; |
| this->SourceFileExtensions = mf.SourceFileExtensions; |
| this->HeaderFileExtensions = mf.HeaderFileExtensions; |
| this->DefineFlags = mf.DefineFlags; |
| this->DefineFlagsOrig = mf.DefineFlagsOrig; |
| |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| this->SourceGroups = mf.SourceGroups; |
| #endif |
| |
| this->LocalGenerator = mf.LocalGenerator; |
| this->FunctionBlockers = mf.FunctionBlockers; |
| this->MacrosMap = mf.MacrosMap; |
| this->SubDirectoryOrder = mf.SubDirectoryOrder; |
| this->Properties = mf.Properties; |
| this->PreOrder = mf.PreOrder; |
| this->WarnUnused = mf.WarnUnused; |
| this->Initialize(); |
| this->CheckSystemVars = mf.CheckSystemVars; |
| this->ListFileStack = mf.ListFileStack; |
| this->OutputToSource = mf.OutputToSource; |
| |
| this->SuppressWatches = mf.SuppressWatches; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::Initialize() |
| { |
| this->cmDefineRegex.compile("#cmakedefine[ \t]+([A-Za-z_0-9]*)"); |
| this->cmDefine01Regex.compile("#cmakedefine01[ \t]+([A-Za-z_0-9]*)"); |
| this->cmAtVarRegex.compile("(@[A-Za-z_0-9/.+-]+@)"); |
| this->cmNamedCurly.compile("^[A-Za-z0-9/_.+-]+{"); |
| |
| // Enter a policy level for this directory. |
| this->PushPolicy(); |
| |
| // Protect the directory-level policies. |
| this->PushPolicyBarrier(); |
| |
| // By default the check is not done. It is enabled by |
| // cmListFileCache in the top level if necessary. |
| this->CheckCMP0000 = false; |
| } |
| |
| unsigned int cmMakefile::GetCacheMajorVersion() const |
| { |
| return this->GetCacheManager()->GetCacheMajorVersion(); |
| } |
| |
| unsigned int cmMakefile::GetCacheMinorVersion() const |
| { |
| return this->GetCacheManager()->GetCacheMinorVersion(); |
| } |
| |
| bool cmMakefile::NeedCacheCompatibility(int major, int minor) const |
| { |
| return this->GetCacheManager()->NeedCacheCompatibility(major, minor); |
| } |
| |
| cmMakefile::~cmMakefile() |
| { |
| for(std::vector<cmInstallGenerator*>::iterator |
| i = this->InstallGenerators.begin(); |
| i != this->InstallGenerators.end(); ++i) |
| { |
| delete *i; |
| } |
| for(std::vector<cmTestGenerator*>::iterator |
| i = this->TestGenerators.begin(); |
| i != this->TestGenerators.end(); ++i) |
| { |
| delete *i; |
| } |
| for(std::vector<cmSourceFile*>::iterator i = this->SourceFiles.begin(); |
| i != this->SourceFiles.end(); ++i) |
| { |
| delete *i; |
| } |
| for(std::map<std::string, cmTest*>::iterator i = this->Tests.begin(); |
| i != this->Tests.end(); ++i) |
| { |
| delete i->second; |
| } |
| for(std::vector<cmTarget*>::iterator |
| i = this->ImportedTargetsOwned.begin(); |
| i != this->ImportedTargetsOwned.end(); ++i) |
| { |
| delete *i; |
| } |
| for(unsigned int i=0; i < this->FinalPassCommands.size(); i++) |
| { |
| delete this->FinalPassCommands[i]; |
| } |
| std::vector<cmFunctionBlocker*>::iterator pos; |
| for (pos = this->FunctionBlockers.begin(); |
| pos != this->FunctionBlockers.end(); ++pos) |
| { |
| cmFunctionBlocker* b = *pos; |
| delete b; |
| } |
| this->FunctionBlockers.clear(); |
| if (this->PolicyStack.size() != 1) |
| { |
| cmSystemTools::Error("Internal CMake Error, Policy Stack has not been" |
| " popped properly"); |
| } |
| } |
| |
| void cmMakefile::PrintStringVector(const char* s, |
| const std::vector<std::string>& v) const |
| { |
| std::cout << s << ": ( \n"; |
| for(std::vector<std::string>::const_iterator i = v.begin(); |
| i != v.end(); ++i) |
| { |
| std::cout << *i << " "; |
| } |
| std::cout << " )\n"; |
| } |
| |
| void cmMakefile |
| ::PrintStringVector(const char* s, |
| const std::vector<std::pair<std::string, bool> >& v) const |
| { |
| std::cout << s << ": ( \n"; |
| for(std::vector<std::pair<std::string, bool> >::const_iterator i |
| = v.begin(); i != v.end(); ++i) |
| { |
| std::cout << i->first << " " << i->second; |
| } |
| std::cout << " )\n"; |
| } |
| |
| |
| // call print on all the classes in the makefile |
| void cmMakefile::Print() const |
| { |
| // print the class lists |
| std::cout << "classes:\n"; |
| |
| std::cout << " this->Targets: "; |
| for (cmTargets::iterator l = this->Targets.begin(); |
| l != this->Targets.end(); l++) |
| { |
| std::cout << l->first << std::endl; |
| } |
| |
| std::cout << " this->StartOutputDirectory; " << |
| this->StartOutputDirectory << std::endl; |
| std::cout << " this->HomeOutputDirectory; " << |
| this->HomeOutputDirectory << std::endl; |
| std::cout << " this->cmStartDirectory; " << |
| this->cmStartDirectory << std::endl; |
| std::cout << " this->cmHomeDirectory; " << |
| this->cmHomeDirectory << std::endl; |
| std::cout << " this->ProjectName; " |
| << this->ProjectName << std::endl; |
| this->PrintStringVector("this->LinkDirectories", this->LinkDirectories); |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| for( std::vector<cmSourceGroup>::const_iterator i = |
| this->SourceGroups.begin(); i != this->SourceGroups.end(); ++i) |
| { |
| std::cout << "Source Group: " << i->GetName() << std::endl; |
| } |
| #endif |
| } |
| |
| bool cmMakefile::CommandExists(const char* name) const |
| { |
| return this->GetCMakeInstance()->CommandExists(name); |
| } |
| |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::IssueMessage(cmake::MessageType t, |
| std::string const& text) const |
| { |
| // Collect context information. |
| cmLocalGenerator* localGen = this->GetLocalGenerator(); |
| if(this->CallStack.empty() && this->GetCMakeInstance()->GetIsInTryCompile()) |
| { |
| localGen = 0; |
| } |
| cmListFileBacktrace backtrace(localGen); |
| if(!this->CallStack.empty()) |
| { |
| if((t == cmake::FATAL_ERROR) || (t == cmake::INTERNAL_ERROR)) |
| { |
| this->CallStack.back().Status->SetNestedError(true); |
| } |
| backtrace = this->GetBacktrace(); |
| } |
| else |
| { |
| cmListFileContext lfc; |
| if(this->ListFileStack.empty()) |
| { |
| // We are not processing the project. Add the directory-level context. |
| lfc.FilePath = this->GetCurrentDirectory(); |
| lfc.FilePath += "/CMakeLists.txt"; |
| } |
| else |
| { |
| // We are processing the project but are not currently executing a |
| // command. Add whatever context information we have. |
| lfc.FilePath = this->ListFileStack.back(); |
| } |
| lfc.Line = 0; |
| backtrace.push_back(lfc); |
| } |
| |
| // Issue the message. |
| this->GetCMakeInstance()->IssueMessage(t, text, backtrace); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmListFileBacktrace cmMakefile::GetBacktrace() const |
| { |
| cmListFileBacktrace backtrace(this->GetLocalGenerator()); |
| for(CallStackType::const_reverse_iterator i = this->CallStack.rbegin(); |
| i != this->CallStack.rend(); ++i) |
| { |
| backtrace.push_back(*i->Context); |
| } |
| return backtrace; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const |
| { |
| cmOStringStream msg; |
| msg << lff.FilePath << "(" << lff.Line << "): "; |
| msg << lff.Name << "("; |
| for(std::vector<cmListFileArgument>::const_iterator i = |
| lff.Arguments.begin(); i != lff.Arguments.end(); ++i) |
| { |
| msg << i->Value; |
| msg << " "; |
| } |
| msg << ")"; |
| cmSystemTools::Message(msg.str().c_str()); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, |
| cmExecutionStatus &status) |
| { |
| bool result = true; |
| |
| // quick return if blocked |
| if(this->IsFunctionBlocked(lff,status)) |
| { |
| // No error. |
| return result; |
| } |
| |
| std::string name = lff.Name; |
| |
| // 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->GetCMakeInstance()->GetCommand(name)) |
| { |
| // Clone the prototype. |
| cmsys::auto_ptr<cmCommand> pcmd(proto->Clone()); |
| pcmd->SetMakefile(this); |
| |
| // Decide whether to invoke the command. |
| if(pcmd->GetEnabled() && !cmSystemTools::GetFatalErrorOccured() && |
| (this->GetCMakeInstance()->GetWorkingMode() != cmake::SCRIPT_MODE |
| || pcmd->IsScriptable())) |
| |
| { |
| // if trace is enabled, print out invoke information |
| if(this->GetCMakeInstance()->GetTrace()) |
| { |
| this->PrintCommandTrace(lff); |
| } |
| // Try invoking the command. |
| if(!pcmd->InvokeInitialPass(lff.Arguments,status) || |
| status.GetNestedError()) |
| { |
| if(!status.GetNestedError()) |
| { |
| // The command invocation requested that we report an error. |
| this->IssueMessage(cmake::FATAL_ERROR, pcmd->GetError()); |
| } |
| 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 ( this->GetCMakeInstance()->GetWorkingMode() == cmake::SCRIPT_MODE |
| && !pcmd->IsScriptable() ) |
| { |
| std::string error = "Command "; |
| error += pcmd->GetName(); |
| error += "() is not scriptable"; |
| this->IssueMessage(cmake::FATAL_ERROR, error); |
| result = false; |
| cmSystemTools::SetFatalErrorOccured(); |
| } |
| } |
| else |
| { |
| if(!cmSystemTools::GetFatalErrorOccured()) |
| { |
| std::string error = "Unknown CMake command \""; |
| error += lff.Name; |
| error += "\"."; |
| this->IssueMessage(cmake::FATAL_ERROR, error); |
| result = false; |
| cmSystemTools::SetFatalErrorOccured(); |
| } |
| } |
| |
| return result; |
| } |
| |
| //---------------------------------------------------------------------------- |
| class cmMakefile::IncludeScope |
| { |
| public: |
| IncludeScope(cmMakefile* mf, const char* fname, bool noPolicyScope); |
| ~IncludeScope(); |
| void Quiet() { this->ReportError = false; } |
| private: |
| cmMakefile* Makefile; |
| const char* File; |
| bool NoPolicyScope; |
| bool CheckCMP0011; |
| bool ReportError; |
| void EnforceCMP0011(); |
| }; |
| |
| //---------------------------------------------------------------------------- |
| cmMakefile::IncludeScope::IncludeScope(cmMakefile* mf, const char* fname, |
| bool noPolicyScope): |
| Makefile(mf), File(fname), NoPolicyScope(noPolicyScope), |
| CheckCMP0011(false), ReportError(true) |
| { |
| 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; |
| case cmPolicies::NEW: |
| // NEW behavior is to push a (strong) scope. |
| this->Makefile->PushPolicy(); |
| break; |
| } |
| } |
| |
| // The included file cannot pop our policy scope. |
| this->Makefile->PushPolicyBarrier(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmMakefile::IncludeScope::~IncludeScope() |
| { |
| // Enforce matching policy scopes inside the included file. |
| this->Makefile->PopPolicyBarrier(this->ReportError); |
| |
| 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->PolicyStack.back().empty()) |
| { |
| 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(); |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::IncludeScope::EnforceCMP0011() |
| { |
| // We check the setting of this policy again because the included |
| // script might actually set this policy for its includer. |
| cmPolicies* policies = this->Makefile->GetPolicies(); |
| switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0011)) |
| { |
| case cmPolicies::WARN: |
| // Warn because the user did not set this policy. |
| { |
| cmOStringStream w; |
| w << policies->GetPolicyWarning(cmPolicies::CMP0011) << "\n" |
| << "The included script\n " << this->File << "\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: |
| { |
| cmOStringStream e; |
| e << policies->GetRequiredPolicyError(cmPolicies::CMP0011) << "\n" |
| << "The included script\n " << this->File << "\n" |
| << "affects policy settings, so it requires this policy to be set."; |
| 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; |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| // Parse the given CMakeLists.txt file executing all commands |
| // |
| bool cmMakefile::ReadListFile(const char* filename_in, |
| const char *external_in, |
| std::string* fullPath, |
| bool noPolicyScope) |
| { |
| std::string currentParentFile |
| = this->GetSafeDefinition("CMAKE_PARENT_LIST_FILE"); |
| std::string currentFile |
| = this->GetSafeDefinition("CMAKE_CURRENT_LIST_FILE"); |
| this->AddDefinition("CMAKE_PARENT_LIST_FILE", filename_in); |
| this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE"); |
| |
| const char* external = 0; |
| std::string external_abs; |
| |
| const char* filename = filename_in; |
| std::string filename_abs; |
| |
| if (external_in) |
| { |
| external_abs = |
| cmSystemTools::CollapseFullPath(external_in, |
| this->cmStartDirectory.c_str()); |
| external = external_abs.c_str(); |
| if (filename_in) |
| { |
| filename_abs = |
| cmSystemTools::CollapseFullPath(filename_in, |
| this->cmStartDirectory.c_str()); |
| filename = filename_abs.c_str(); |
| } |
| } |
| |
| // keep track of the current file being read |
| if (filename) |
| { |
| if(this->cmCurrentListFile != filename) |
| { |
| this->cmCurrentListFile = filename; |
| } |
| } |
| |
| // Now read the input file |
| const char *filenametoread= filename; |
| |
| if( external) |
| { |
| filenametoread= external; |
| } |
| |
| this->AddDefinition("CMAKE_CURRENT_LIST_FILE", filenametoread); |
| this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE"); |
| this->AddDefinition("CMAKE_CURRENT_LIST_DIR", |
| cmSystemTools::GetFilenamePath(filenametoread).c_str()); |
| this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR"); |
| |
| // try to see if the list file is the top most |
| // list file for a project, and if it is, then it |
| // must have a project command. If there is not |
| // one, then cmake will provide one via the |
| // cmListFileCache class. |
| bool requireProjectCommand = false; |
| if(!external && this->cmStartDirectory == this->cmHomeDirectory) |
| { |
| if(cmSystemTools::LowerCase( |
| cmSystemTools::GetFilenameName(filename)) == "cmakelists.txt") |
| { |
| requireProjectCommand = true; |
| } |
| } |
| |
| // push the listfile onto the stack |
| this->ListFileStack.push_back(filenametoread); |
| if(fullPath!=0) |
| { |
| *fullPath=filenametoread; |
| } |
| cmListFile cacheFile; |
| if( !cacheFile.ParseFile(filenametoread, requireProjectCommand, this) ) |
| { |
| // pop the listfile off the stack |
| this->ListFileStack.pop_back(); |
| if(fullPath!=0) |
| { |
| *fullPath = ""; |
| } |
| this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile.c_str()); |
| this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE"); |
| this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile.c_str()); |
| this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE"); |
| this->AddDefinition("CMAKE_CURRENT_LIST_DIR", |
| cmSystemTools::GetFilenamePath(currentFile).c_str()); |
| this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR"); |
| return false; |
| } |
| // add this list file to the list of dependencies |
| this->ListFiles.push_back( filenametoread); |
| |
| // Enforce balanced blocks (if/endif, function/endfunction, etc.). |
| { |
| LexicalPushPop lexScope(this); |
| IncludeScope incScope(this, filenametoread, noPolicyScope); |
| |
| // Run the parsed commands. |
| const size_t numberFunctions = cacheFile.Functions.size(); |
| for(size_t i =0; i < numberFunctions; ++i) |
| { |
| cmExecutionStatus status; |
| this->ExecuteCommand(cacheFile.Functions[i],status); |
| if(cmSystemTools::GetFatalErrorOccured()) |
| { |
| // Exit early due to error. |
| lexScope.Quiet(); |
| incScope.Quiet(); |
| break; |
| } |
| if(status.GetReturnInvoked()) |
| { |
| // Exit early due to return command. |
| break; |
| } |
| } |
| } |
| |
| // If this is the directory-level CMakeLists.txt file then perform |
| // some extra checks. |
| if(this->ListFileStack.size() == 1) |
| { |
| this->EnforceDirectoryLevelRules(); |
| } |
| |
| this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile.c_str()); |
| this->MarkVariableAsUsed("CMAKE_PARENT_LIST_FILE"); |
| this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile.c_str()); |
| this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_FILE"); |
| this->AddDefinition("CMAKE_CURRENT_LIST_DIR", |
| cmSystemTools::GetFilenamePath(currentFile).c_str()); |
| this->MarkVariableAsUsed("CMAKE_CURRENT_LIST_DIR"); |
| |
| // pop the listfile off the stack |
| this->ListFileStack.pop_back(); |
| |
| // Check for unused variables |
| this->CheckForUnusedVariables(); |
| |
| return true; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::EnforceDirectoryLevelRules() const |
| { |
| // Diagnose a violation of CMP0000 if necessary. |
| if(this->CheckCMP0000) |
| { |
| cmOStringStream 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 mimimum required |
| // version. |
| this->IssueMessage(cmake::AUTHOR_WARNING, msg.str()); |
| 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->IssueMessage(cmake::FATAL_ERROR, msg.str()); |
| cmSystemTools::SetFatalErrorOccured(); |
| return; |
| } |
| } |
| } |
| |
| void cmMakefile::AddCommand(cmCommand* wg) |
| { |
| this->GetCMakeInstance()->AddCommand(wg); |
| } |
| |
| // Set the make file |
| void cmMakefile::SetLocalGenerator(cmLocalGenerator* lg) |
| { |
| this->LocalGenerator = lg; |
| // the source groups need to access the global generator |
| // so don't create them until the lg is set |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| this->AddSourceGroup("", "^.*$"); |
| this->AddSourceGroup |
| ("Source Files", |
| "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|f|f90|for|fpp" |
| "|ftn|m|mm|rc|def|r|odl|idl|hpj|bat)$"); |
| this->AddSourceGroup("Header Files", CM_HEADER_REGEX); |
| this->AddSourceGroup("CMake Rules", "\\.rule$"); |
| this->AddSourceGroup("Resources", "\\.plist$"); |
| this->AddSourceGroup("Object Files", "\\.(lo|o|obj)$"); |
| #endif |
| |
| this->WarnUnused = this->GetCMakeInstance()->GetWarnUnused(); |
| this->CheckSystemVars = this->GetCMakeInstance()->GetCheckSystemVars(); |
| } |
| |
| namespace |
| { |
| struct file_not_persistent |
| { |
| bool operator()(const std::string& path) const |
| { |
| return !(path.find("CMakeTmp") == path.npos && |
| cmSystemTools::FileExists(path.c_str())); |
| } |
| }; |
| } |
| |
| 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(std::vector<cmCommand*>::iterator i = this->FinalPassCommands.begin(); |
| i != this->FinalPassCommands.end(); ++i) |
| { |
| (*i)->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 |
| |
| //remove_if will move all items that don't have a valid file name to the |
| //back of the vector |
| std::vector<std::string>::iterator new_output_files_end = std::remove_if( |
| this->OutputFiles.begin(), |
| this->OutputFiles.end(), |
| file_not_persistent() ); |
| //we just have to erase all items at the back |
| this->OutputFiles.erase(new_output_files_end, this->OutputFiles.end() ); |
| |
| //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 |
| std::vector<std::string>::iterator new_list_files_end = std::remove_if( |
| this->ListFiles.begin(), |
| this->ListFiles.end(), |
| file_not_persistent() ); |
| |
| this->ListFiles.erase(new_list_files_end, this->ListFiles.end() ); |
| } |
| |
| // 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->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."); |
| } |
| for (cmTargets::iterator l = this->Targets.begin(); |
| l != this->Targets.end(); l++) |
| { |
| if (l->second.GetType() == cmTarget::INTERFACE_LIBRARY) |
| { |
| continue; |
| } |
| l->second.FinishConfigure(); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmMakefile::AddCustomCommandToTarget(const std::string& target, |
| const std::vector<std::string>& depends, |
| const cmCustomCommandLines& commandLines, |
| cmTarget::CustomCommandType type, |
| const char* comment, |
| const char* workingDir, |
| bool escapeOldStyle) const |
| { |
| // 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; |
| cmOStringStream e; |
| switch(this->GetPolicyStatus(cmPolicies::CMP0040)) |
| { |
| case cmPolicies::WARN: |
| e << (this->GetPolicies() |
| ->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) |
| { |
| e << "The target name \"" << target << "\" is unknown in this context."; |
| IssueMessage(messageType, e.str()); |
| } |
| |
| return; |
| } |
| |
| if(ti->second.GetType() == cmTarget::OBJECT_LIBRARY) |
| { |
| cmOStringStream 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(ti->second.GetType() == cmTarget::INTERFACE_LIBRARY) |
| { |
| cmOStringStream 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; |
| } |
| // Add the command to the appropriate build step for the target. |
| std::vector<std::string> no_output; |
| cmCustomCommand cc(this, no_output, depends, |
| commandLines, comment, workingDir); |
| cc.SetEscapeOldStyle(escapeOldStyle); |
| cc.SetEscapeAllowMakeVars(true); |
| switch(type) |
| { |
| case cmTarget::PRE_BUILD: |
| ti->second.AddPreBuildCommand(cc); |
| break; |
| case cmTarget::PRE_LINK: |
| ti->second.AddPreLinkCommand(cc); |
| break; |
| case cmTarget::POST_BUILD: |
| ti->second.AddPostBuildCommand(cc); |
| break; |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmSourceFile* |
| cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs, |
| const std::vector<std::string>& depends, |
| const std::string& main_dependency, |
| const cmCustomCommandLines& commandLines, |
| const char* comment, |
| const char* workingDir, |
| bool replace, |
| bool escapeOldStyle) |
| { |
| // Make sure there is at least one output. |
| if(outputs.empty()) |
| { |
| cmSystemTools::Error("Attempt to add a custom rule with no output!"); |
| return 0; |
| } |
| |
| // Validate custom commands. TODO: More strict? |
| for(cmCustomCommandLines::const_iterator i=commandLines.begin(); |
| i != commandLines.end(); ++i) |
| { |
| cmCustomCommandLine const& cl = *i; |
| if(!cl.empty() && !cl[0].empty() && cl[0][0] == '"') |
| { |
| cmOStringStream e; |
| e << "COMMAND may not contain literal quotes:\n " << cl[0] << "\n"; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return 0; |
| } |
| } |
| |
| // Choose a source file on which to store the custom command. |
| cmSourceFile* file = 0; |
| 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; |
| } |
| else |
| { |
| // The existing custom command is different. We need to |
| // generate a rule file for this new command. |
| file = 0; |
| } |
| } |
| else if (!file) |
| { |
| file = this->CreateSource(main_dependency); |
| } |
| } |
| |
| // Generate a rule file if the main dependency is not available. |
| if(!file) |
| { |
| cmGlobalGenerator* gg = this->LocalGenerator->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); |
| 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); |
| } |
| file->SetProperty("__CMAKE_RULE", "1"); |
| } |
| |
| // Always create the output sources and mark them generated. |
| for(std::vector<std::string>::const_iterator o = outputs.begin(); |
| o != outputs.end(); ++o) |
| { |
| if(cmSourceFile* out = this->GetOrCreateSource(*o, true)) |
| { |
| 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, depends2, commandLines, |
| comment, workingDir); |
| cc->SetEscapeOldStyle(escapeOldStyle); |
| cc->SetEscapeAllowMakeVars(true); |
| file->SetCustomCommand(cc); |
| this->UpdateOutputToSourceMap(outputs, file); |
| } |
| return file; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmMakefile::UpdateOutputToSourceMap(std::vector<std::string> const& outputs, |
| cmSourceFile* source) |
| { |
| for(std::vector<std::string>::const_iterator o = outputs.begin(); |
| o != outputs.end(); ++o) |
| { |
| 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) |
| { |
| std::vector<std::string> outputs; |
| outputs.push_back(output); |
| return this->AddCustomCommandToOutput(outputs, depends, main_dependency, |
| commandLines, comment, workingDir, |
| replace, escapeOldStyle); |
| } |
| |
| //---------------------------------------------------------------------------- |
| 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. |
| this->AddCustomCommandToTarget(target, depends, commandLines, |
| cmTarget::POST_BUILD, comment, 0); |
| return; |
| } |
| |
| // Each output must get its own copy of this rule. |
| cmsys::RegularExpression sourceFiles("\\.(C|M|c|c\\+\\+|cc|cpp|cxx|m|mm|" |
| "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|" |
| "hm|hpp|hxx|in|txx|inl)$"); |
| for(std::vector<std::string>::const_iterator oi = outputs.begin(); |
| oi != outputs.end(); ++oi) |
| { |
| // 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, 0); |
| } |
| 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, 0); |
| } |
| |
| // 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")) |
| { |
| if (this->Targets.find(target) != this->Targets.end()) |
| { |
| this->Targets[target].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; |
| } |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::AddUtilityCommand(const std::string& utilityName, |
| 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(commandLine); |
| |
| // Call the real signature of this method. |
| this->AddUtilityCommand(utilityName, excludeFromAll, workingDirectory, |
| depends, commandLines); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmTarget* |
| cmMakefile::AddUtilityCommand(const std::string& utilityName, |
| bool excludeFromAll, |
| const char* workingDirectory, |
| const std::vector<std::string>& depends, |
| const cmCustomCommandLines& commandLines, |
| bool escapeOldStyle, const char* comment) |
| { |
| // Create a target instance for this utility. |
| cmTarget* target = this->AddNewTarget(cmTarget::UTILITY, utilityName); |
| 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->GetStartOutputDirectory(); |
| force += cmake::GetCMakeFilesDirectory(); |
| force += "/"; |
| force += utilityName; |
| std::string no_main_dependency = ""; |
| bool no_replace = false; |
| this->AddCustomCommandToOutput(force, depends, |
| no_main_dependency, |
| commandLines, comment, |
| workingDirectory, no_replace, |
| escapeOldStyle); |
| 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()); |
| } |
| } |
| return target; |
| } |
| |
| void cmMakefile::AddDefineFlag(const char* flag) |
| { |
| if (!flag) |
| { |
| return; |
| } |
| |
| // Update the string used for the old DEFINITIONS property. |
| this->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. |
| this->AddDefineFlag(flag, this->DefineFlags); |
| } |
| |
| void cmMakefile::AddDefineFlag(const char* flag, std::string& dflags) |
| { |
| // remove any \n\r |
| std::string ret = flag; |
| std::string::size_type pos = 0; |
| while((pos = ret.find('\n', pos)) != std::string::npos) |
| { |
| ret[pos] = ' '; |
| pos++; |
| } |
| pos = 0; |
| while((pos = ret.find('\r', pos)) != std::string::npos) |
| { |
| ret[pos] = ' '; |
| pos++; |
| } |
| |
| dflags += " "; |
| dflags += ret; |
| } |
| |
| |
| void cmMakefile::RemoveDefineFlag(const char* flag) |
| { |
| // Check the length of the flag to remove. |
| std::string::size_type len = strlen(flag); |
| if(len < 1) |
| { |
| return; |
| } |
| |
| // Update the string used for the old DEFINITIONS property. |
| this->RemoveDefineFlag(flag, len, 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. |
| this->RemoveDefineFlag(flag, len, this->DefineFlags); |
| } |
| |
| void cmMakefile::RemoveDefineFlag(const char* flag, |
| std::string::size_type len, |
| std::string& dflags) |
| { |
| // 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::AddCompileOption(const char* option) |
| { |
| this->AppendProperty("COMPILE_OPTIONS", option); |
| } |
| |
| 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.c_str())) |
| { |
| return false; |
| } |
| |
| // VS6 IDE does not support definition values with spaces in |
| // combination with '"', '$', or ';'. |
| if((this->LocalGenerator->GetGlobalGenerator()->GetName() == |
| "Visual Studio 6") && |
| (def.find(" ") != def.npos && def.find_first_of("\"$;") != def.npos)) |
| { |
| 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.c_str())) |
| { |
| // This definition has a non-trivial value. |
| switch(this->GetPolicyStatus(cmPolicies::CMP0005)) |
| { |
| case cmPolicies::WARN: |
| this->IssueMessage( |
| cmake::AUTHOR_WARNING, |
| this->GetPolicies()->GetPolicyWarning(cmPolicies::CMP0005) |
| ); |
| 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, |
| this->GetPolicies()->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::string ndefs; |
| const char* sep = ""; |
| for(std::vector<std::string>::const_iterator di = defs.begin(); |
| di != defs.end(); ++di) |
| { |
| if(*di != define) |
| { |
| ndefs += sep; |
| sep = ";"; |
| ndefs += *di; |
| } |
| } |
| |
| // 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::AddLinkLibrary(const std::string& lib, |
| cmTarget::LinkLibraryType llt) |
| { |
| cmTarget::LibraryID tmp; |
| tmp.first = lib; |
| tmp.second = llt; |
| this->LinkLibraries.push_back(tmp); |
| } |
| |
| void cmMakefile::AddLinkLibraryForTarget(const std::string& target, |
| const std::string& lib, |
| cmTarget::LinkLibraryType llt) |
| { |
| cmTargets::iterator i = this->Targets.find(target); |
| if ( i != this->Targets.end()) |
| { |
| cmTarget* tgt = |
| this->GetCMakeInstance()->GetGlobalGenerator()->FindTarget(lib); |
| if(tgt) |
| { |
| // if it is not a static or shared library then you can not link to it |
| if(!((tgt->GetType() == cmTarget::STATIC_LIBRARY) || |
| (tgt->GetType() == cmTarget::SHARED_LIBRARY) || |
| (tgt->GetType() == cmTarget::INTERFACE_LIBRARY) || |
| tgt->IsExecutableWithExports())) |
| { |
| cmOStringStream e; |
| e << "Target \"" << lib << "\" of type " |
| << cmTarget::GetTargetTypeName(tgt->GetType()) |
| << " may not be linked into another target. " |
| << "One may link only to STATIC or SHARED libraries, or " |
| << "to executables with the ENABLE_EXPORTS property set."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| } |
| } |
| i->second.AddLinkLibrary( *this, target, lib, llt ); |
| } |
| else |
| { |
| cmOStringStream e; |
| e << "Attempt to add link library \"" |
| << lib << "\" to target \"" |
| << target << "\" which is not built in this directory."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| } |
| } |
| |
| void cmMakefile::AddLinkDirectoryForTarget(const std::string& target, |
| const std::string& d) |
| { |
| cmTargets::iterator i = this->Targets.find(target); |
| if ( i != this->Targets.end()) |
| { |
| if(this->IsAlias(target)) |
| { |
| cmOStringStream e; |
| e << "ALIAS target \"" << target << "\" " |
| << "may not be linked into another target."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return; |
| } |
| i->second.AddLinkDirectory( d ); |
| } |
| else |
| { |
| cmSystemTools::Error |
| ("Attempt to add link directories to non-existent target: ", |
| target.c_str(), " for directory ", d.c_str()); |
| } |
| } |
| |
| void cmMakefile::AddLinkLibrary(const std::string& lib) |
| { |
| this->AddLinkLibrary(lib,cmTarget::GENERAL); |
| } |
| |
| void cmMakefile::AddLinkDirectory(const std::string& dir) |
| { |
| // Don't add a link directory that is already present. Yes, this |
| // linear search results in n^2 behavior, but n won't be getting |
| // much bigger than 20. We cannot use a set because of order |
| // dependency of the link search path. |
| |
| if(dir.empty()) |
| { |
| return; |
| } |
| std::string newdir = dir; |
| // remove trailing slashes |
| if(*dir.rbegin() == '/') |
| { |
| newdir = dir.substr(0, dir.size()-1); |
| } |
| if(std::find(this->LinkDirectories.begin(), |
| this->LinkDirectories.end(), newdir) |
| == this->LinkDirectories.end()) |
| { |
| this->LinkDirectories.push_back(dir); |
| } |
| } |
| |
| void cmMakefile::InitializeFromParent() |
| { |
| cmMakefile *parent = this->LocalGenerator->GetParent()->GetMakefile(); |
| |
| // Initialize definitions with the closure of the parent scope. |
| this->Internal->VarStack.top() = parent->Internal->VarStack.top().Closure(); |
| |
| const std::vector<cmValueWithOrigin>& parentIncludes = |
| parent->GetIncludeDirectoriesEntries(); |
| this->IncludeDirectoriesEntries.insert(this->IncludeDirectoriesEntries.end(), |
| parentIncludes.begin(), |
| parentIncludes.end()); |
| |
| const std::vector<cmValueWithOrigin>& parentOptions = |
| parent->GetCompileOptionsEntries(); |
| this->CompileOptionsEntries.insert(this->CompileOptionsEntries.end(), |
| parentOptions.begin(), |
| parentOptions.end()); |
| |
| const std::vector<cmValueWithOrigin>& parentDefines = |
| parent->GetCompileDefinitionsEntries(); |
| this->CompileDefinitionsEntries.insert(this->CompileDefinitionsEntries.end(), |
| parentDefines.begin(), |
| parentDefines.end()); |
| |
| 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::vector<std::string>::const_iterator ci = configs.begin(); |
| ci != configs.end(); ++ci) |
| { |
| std::string defPropName = "COMPILE_DEFINITIONS_"; |
| defPropName += cmSystemTools::UpperCase(*ci); |
| const char* prop = parent->GetProperty(defPropName); |
| this->SetProperty(defPropName, prop); |
| } |
| } |
| |
| // link libraries |
| this->LinkLibraries = parent->LinkLibraries; |
| |
| // link directories |
| this->LinkDirectories = parent->LinkDirectories; |
| |
| // the initial project name |
| this->ProjectName = parent->ProjectName; |
| |
| // Copy include regular expressions. |
| this->IncludeFileRegularExpression = parent->IncludeFileRegularExpression; |
| this->ComplainFileRegularExpression = parent->ComplainFileRegularExpression; |
| |
| // Imported targets. |
| this->ImportedTargets = parent->ImportedTargets; |
| } |
| |
| void cmMakefile::ConfigureSubDirectory(cmLocalGenerator *lg2) |
| { |
| // copy our variables from the child makefile |
| lg2->GetMakefile()->InitializeFromParent(); |
| lg2->GetMakefile()->MakeStartDirectoriesCurrent(); |
| if (this->GetCMakeInstance()->GetDebugOutput()) |
| { |
| std::string msg=" Entering "; |
| msg += lg2->GetMakefile()->GetCurrentDirectory(); |
| cmSystemTools::Message(msg.c_str()); |
| } |
| // finally configure the subdir |
| lg2->Configure(); |
| if (this->GetCMakeInstance()->GetDebugOutput()) |
| { |
| std::string msg=" Returning to "; |
| msg += this->GetCurrentDirectory(); |
| cmSystemTools::Message(msg.c_str()); |
| } |
| } |
| |
| void cmMakefile::AddSubDirectory(const std::string& sub, |
| bool excludeFromAll, bool preorder) |
| { |
| // the source path must be made full if it isn't already |
| std::string srcPath = sub; |
| if (!cmSystemTools::FileIsFullPath(srcPath.c_str())) |
| { |
| srcPath = this->GetCurrentDirectory(); |
| srcPath += "/"; |
| srcPath += sub; |
| } |
| |
| // binary path must be made full if it isn't already |
| std::string binPath = sub; |
| if (!cmSystemTools::FileIsFullPath(binPath.c_str())) |
| { |
| binPath = this->GetCurrentOutputDirectory(); |
| binPath += "/"; |
| binPath += sub; |
| } |
| |
| |
| this->AddSubDirectory(srcPath, binPath, |
| excludeFromAll, preorder, false); |
| } |
| |
| |
| void cmMakefile::AddSubDirectory(const std::string& srcPath, |
| const std::string& binPath, |
| bool excludeFromAll, bool preorder, |
| bool immediate) |
| { |
| // Make sure the binary directory is unique. |
| if(!this->EnforceUniqueDir(srcPath, binPath)) |
| { |
| return; |
| } |
| |
| // create a new local generator and set its parent |
| cmLocalGenerator *lg2 = |
| this->LocalGenerator->GetGlobalGenerator()->CreateLocalGenerator(); |
| lg2->SetParent(this->LocalGenerator); |
| this->LocalGenerator->GetGlobalGenerator()->AddLocalGenerator(lg2); |
| |
| // set the subdirs start dirs |
| lg2->GetMakefile()->SetStartDirectory(srcPath); |
| lg2->GetMakefile()->SetStartOutputDirectory(binPath); |
| if(excludeFromAll) |
| { |
| lg2->GetMakefile()->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
| } |
| lg2->GetMakefile()->SetPreOrder(preorder); |
| |
| if (immediate) |
| { |
| this->ConfigureSubDirectory(lg2); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::AddIncludeDirectories(const std::vector<std::string> &incs, |
| bool before) |
| { |
| if (incs.empty()) |
| { |
| return; |
| } |
| |
| std::string incString; |
| std::string sep; |
| |
| for(std::vector<std::string>::const_iterator li = incs.begin(); |
| li != incs.end(); ++li) |
| { |
| incString += sep + *li; |
| sep = ";"; |
| } |
| |
| std::vector<cmValueWithOrigin>::iterator position = |
| before ? this->IncludeDirectoriesEntries.begin() |
| : this->IncludeDirectoriesEntries.end(); |
| |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| cmValueWithOrigin entry(incString, lfbt); |
| this->IncludeDirectoriesEntries.insert(position, entry); |
| |
| // Property on each target: |
| for (cmTargets::iterator l = this->Targets.begin(); |
| l != this->Targets.end(); ++l) |
| { |
| cmTarget &t = l->second; |
| t.InsertInclude(entry, before); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmMakefile::AddSystemIncludeDirectories(const std::set<std::string> &incs) |
| { |
| for(std::set<std::string>::const_iterator li = incs.begin(); |
| li != incs.end(); ++li) |
| { |
| this->SystemIncludeDirectories.insert(*li); |
| } |
| |
| for (cmTargets::iterator l = this->Targets.begin(); |
| l != this->Targets.end(); ++l) |
| { |
| cmTarget &t = l->second; |
| t.AddSystemIncludeDirectories(incs); |
| } |
| } |
| |
| void cmMakefile::AddDefinition(const std::string& name, const char* value) |
| { |
| if (!value ) |
| { |
| return; |
| } |
| |
| this->Internal->VarStack.top().Set(name, value); |
| if (this->Internal->VarUsageStack.size() && |
| this->VariableInitialized(name)) |
| { |
| this->CheckForUnused("changing definition", name); |
| this->Internal->VarUsageStack.top().erase(name); |
| } |
| this->Internal->VarInitStack.top().insert(name); |
| |
| #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, |
| cmCacheManager::CacheEntryType type, |
| bool force) |
| { |
| bool haveVal = value ? true : false; |
| std::string val = haveVal ? value : ""; |
| cmCacheManager::CacheIterator it = |
| this->GetCacheManager()->GetCacheIterator(name.c_str()); |
| if(!it.IsAtEnd() && (it.GetType() == cmCacheManager::UNINITIALIZED) && |
| it.Initialized()) |
| { |
| // 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) |
| { |
| val = it.GetValue(); |
| haveVal = true; |
| } |
| if ( type == cmCacheManager::PATH || type == cmCacheManager::FILEPATH ) |
| { |
| std::vector<std::string>::size_type cc; |
| std::vector<std::string> files; |
| std::string nvalue = ""; |
| cmSystemTools::ExpandListArgument(val, files); |
| for ( cc = 0; cc < files.size(); cc ++ ) |
| { |
| if(!cmSystemTools::IsOff(files[cc].c_str())) |
| { |
| files[cc] = cmSystemTools::CollapseFullPath(files[cc].c_str()); |
| } |
| if ( cc > 0 ) |
| { |
| nvalue += ";"; |
| } |
| nvalue += files[cc]; |
| } |
| |
| this->GetCacheManager()->AddCacheEntry(name, nvalue.c_str(), doc, type); |
| val = it.GetValue(); |
| haveVal = true; |
| } |
| |
| } |
| this->GetCacheManager()->AddCacheEntry(name, haveVal ? val.c_str() : 0, doc, |
| type); |
| // if there was a definition then remove it |
| this->Internal->VarStack.top().Set(name, 0); |
| } |
| |
| |
| void cmMakefile::AddDefinition(const std::string& name, bool value) |
| { |
| this->Internal->VarStack.top().Set(name, value? "ON" : "OFF"); |
| if (this->Internal->VarUsageStack.size() && |
| this->VariableInitialized(name)) |
| { |
| this->CheckForUnused("changing definition", name); |
| this->Internal->VarUsageStack.top().erase(name); |
| } |
| this->Internal->VarInitStack.top().insert(name); |
| #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 cmDefinitions& defs = this->Internal->VarStack.top(); |
| const std::set<std::string>& locals = defs.LocalKeys(); |
| std::set<std::string>::const_iterator it = locals.begin(); |
| for (; it != locals.end(); ++it) |
| { |
| this->CheckForUnused("out of scope", *it); |
| } |
| } |
| |
| void cmMakefile::MarkVariableAsUsed(const std::string& var) |
| { |
| this->Internal->VarUsageStack.top().insert(var); |
| } |
| |
| bool cmMakefile::VariableInitialized(const std::string& var) const |
| { |
| if(this->Internal->VarInitStack.top().find(var) != |
| this->Internal->VarInitStack.top().end()) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| bool cmMakefile::VariableUsed(const std::string& var) const |
| { |
| if(this->Internal->VarUsageStack.top().find(var) != |
| this->Internal->VarUsageStack.top().end()) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| void cmMakefile::CheckForUnused(const char* reason, |
| const std::string& name) const |
| { |
| if (this->WarnUnused && !this->VariableUsed(name)) |
| { |
| std::string path; |
| cmListFileBacktrace bt(this->GetLocalGenerator()); |
| if (this->CallStack.size()) |
| { |
| const cmListFileContext* file = this->CallStack.back().Context; |
| bt.push_back(*file); |
| path = file->FilePath.c_str(); |
| } |
| else |
| { |
| path = this->GetStartDirectory(); |
| path += "/CMakeLists.txt"; |
| cmListFileContext lfc; |
| lfc.FilePath = path; |
| lfc.Line = 0; |
| bt.push_back(lfc); |
| } |
| if (this->CheckSystemVars || |
| cmSystemTools::IsSubDirectory(path.c_str(), |
| this->GetHomeDirectory()) || |
| (cmSystemTools::IsSubDirectory(path.c_str(), |
| this->GetHomeOutputDirectory()) && |
| !cmSystemTools::IsSubDirectory(path.c_str(), |
| cmake::GetCMakeFilesDirectory()))) |
| { |
| cmOStringStream msg; |
| msg << "unused variable (" << reason << ") \'" << name << "\'"; |
| this->GetCMakeInstance()->IssueMessage(cmake::AUTHOR_WARNING, |
| msg.str(), |
| bt); |
| } |
| } |
| } |
| |
| void cmMakefile::RemoveDefinition(const std::string& name) |
| { |
| this->Internal->VarStack.top().Set(name, 0); |
| if (this->Internal->VarUsageStack.size() && |
| this->VariableInitialized(name)) |
| { |
| this->CheckForUnused("unsetting", name); |
| this->Internal->VarUsageStack.top().erase(name); |
| } |
| this->Internal->VarInitStack.top().insert(name); |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| cmVariableWatch* vv = this->GetVariableWatch(); |
| if ( vv ) |
| { |
| vv->VariableAccessed(name, cmVariableWatch::VARIABLE_REMOVED_ACCESS, |
| 0, this); |
| } |
| #endif |
| } |
| |
| void cmMakefile::RemoveCacheDefinition(const std::string& name) |
| { |
| this->GetCacheManager()->RemoveCacheEntry(name); |
| } |
| |
| void cmMakefile::SetProjectName(const char* p) |
| { |
| this->ProjectName = p; |
| } |
| |
| |
| void cmMakefile::AddGlobalLinkInformation(const std::string& name, |
| cmTarget& target) |
| { |
| // for these targets do not add anything |
| switch(target.GetType()) |
| { |
| case cmTarget::UTILITY: |
| case cmTarget::GLOBAL_TARGET: |
| case cmTarget::INTERFACE_LIBRARY: |
| return; |
| default:; |
| } |
| std::vector<std::string>::iterator j; |
| for(j = this->LinkDirectories.begin(); |
| j != this->LinkDirectories.end(); ++j) |
| { |
| target.AddLinkDirectory(*j); |
| } |
| target.MergeLinkLibraries( *this, name, this->LinkLibraries ); |
| } |
| |
| |
| void cmMakefile::AddAlias(const std::string& lname, cmTarget *tgt) |
| { |
| this->AliasTargets[lname] = tgt; |
| this->LocalGenerator->GetGlobalGenerator()->AddAlias(lname, tgt); |
| } |
| |
| cmTarget* cmMakefile::AddLibrary(const std::string& lname, |
| cmTarget::TargetType type, |
| const std::vector<std::string> &srcs, |
| bool excludeFromAll) |
| { |
| // wrong type ? default to STATIC |
| if ( (type != cmTarget::STATIC_LIBRARY) |
| && (type != cmTarget::SHARED_LIBRARY) |
| && (type != cmTarget::MODULE_LIBRARY) |
| && (type != cmTarget::OBJECT_LIBRARY) |
| && (type != cmTarget::INTERFACE_LIBRARY)) |
| { |
| this->IssueMessage(cmake::INTERNAL_ERROR, |
| "cmMakefile::AddLibrary given invalid target type."); |
| type = cmTarget::STATIC_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, lname ); |
| if(excludeFromAll) |
| { |
| target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
| } |
| target->AddSources(srcs); |
| this->AddGlobalLinkInformation(lname, *target); |
| return target; |
| } |
| |
| cmTarget* cmMakefile::AddExecutable(const char *exeName, |
| const std::vector<std::string> &srcs, |
| bool excludeFromAll) |
| { |
| cmTarget* target = this->AddNewTarget(cmTarget::EXECUTABLE, exeName); |
| if(excludeFromAll) |
| { |
| target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
| } |
| target->AddSources(srcs); |
| this->AddGlobalLinkInformation(exeName, *target); |
| return target; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmTarget* |
| cmMakefile::AddNewTarget(cmTarget::TargetType type, const std::string& name) |
| { |
| cmTargets::iterator it = |
| this->Targets.insert(cmTargets::value_type(name, cmTarget())).first; |
| cmTarget& target = it->second; |
| target.SetType(type, name); |
| target.SetMakefile(this); |
| this->LocalGenerator->GetGlobalGenerator()->AddTarget(&it->second); |
| 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(std::vector<cmSourceFile*>::const_iterator i = |
| this->SourceFiles.begin(); i != this->SourceFiles.end(); ++i) |
| { |
| // does this source file have a custom command? |
| if ((*i)->GetCustomCommand()) |
| { |
| // Does the output of the custom command match the source file name? |
| const std::vector<std::string>& outputs = |
| (*i)->GetCustomCommand()->GetOutputs(); |
| for(std::vector<std::string>::const_iterator o = outputs.begin(); |
| o != outputs.end(); ++o) |
| { |
| out = *o; |
| std::string::size_type pos = out.rfind(name); |
| // If the output matches exactly |
| if (pos != out.npos && |
| pos == out.size() - name.size() && |
| (pos ==0 || out[pos-1] == '/')) |
| { |
| return *i; |
| } |
| } |
| } |
| } |
| |
| // otherwise return NULL |
| return 0; |
| } |
| |
| 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.c_str())) |
| { |
| 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 0; |
| } |
| |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| cmSourceGroup* |
| cmMakefile::GetSourceGroup(const std::vector<std::string>&name) const |
| { |
| cmSourceGroup* sg = 0; |
| |
| // first look for source group starting with the same as the one we wants |
| for (std::vector<cmSourceGroup>::const_iterator |
| sgIt = this->SourceGroups.begin(); |
| sgIt != this->SourceGroups.end(); ++sgIt) |
| { |
| std::string sgName = sgIt->GetName(); |
| if(sgName == name[0]) |
| { |
| sg = const_cast<cmSourceGroup*>(&(*sgIt)); |
| break; |
| } |
| } |
| |
| if(sg != 0) |
| { |
| // iterate through its children to find match source group |
| for(unsigned int i=1; i<name.size(); ++i) |
| { |
| sg = sg->LookupChild(name[i].c_str()); |
| if(sg == 0) |
| { |
| break; |
| } |
| } |
| } |
| return sg; |
| } |
| |
| void cmMakefile::AddSourceGroup(const std::string& name, |
| const char* regex) |
| { |
| std::vector<std::string> nameVector; |
| nameVector.push_back(name); |
| AddSourceGroup(nameVector, regex); |
| } |
| |
| void cmMakefile::AddSourceGroup(const std::vector<std::string>& name, |
| const char* regex) |
| { |
| cmSourceGroup* sg = 0; |
| 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 != 0) |
| { |
| break; |
| } |
| } |
| |
| // i now contains the index of the last found component |
| if(i==lastElement) |
| { |
| // group already exists, replace its regular expression |
| if ( regex ) |
| { |
| // 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; |
| } |
| else if(i==-1) |
| { |
| // group does not exist nor belong to any existing group |
| // add its first component |
| this->SourceGroups.push_back(cmSourceGroup(name[0].c_str(), regex)); |
| sg = this->GetSourceGroup(currentName); |
| i = 0; // last component found |
| } |
| |
| // build the whole source group path |
| const char* fullname = sg->GetFullName(); |
| cmGlobalGenerator* gg = this->LocalGenerator->GetGlobalGenerator(); |
| if(strlen(fullname)) |
| { |
| std::string guidName = "SG_Filter_"; |
| guidName += fullname; |
| gg->CreateGUID(guidName); |
| } |
| for(++i; i<=lastElement; ++i) |
| { |
| sg->AddChild(cmSourceGroup(name[i].c_str(), 0, sg->GetFullName())); |
| sg = sg->LookupChild(name[i].c_str()); |
| fullname = sg->GetFullName(); |
| if(strlen(fullname)) |
| { |
| std::string guidName = "SG_Filter_"; |
| guidName += fullname; |
| gg->CreateGUID(guidName); |
| } |
| } |
| |
| sg->SetGroupRegex(regex); |
| } |
| |
| #endif |
| |
| void cmMakefile::AddExtraDirectory(const char* dir) |
| { |
| this->AuxSourceDirectories.push_back(dir); |
| } |
| |
| 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; |
| } |
| cmOStringStream 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) |
| { |
| w << "Evaluated directory INCLUDE_DIRECTORIES\n" |
| << " " << includeDirs << "\n" |
| << "as\n" |
| << " " << dirs << "\n"; |
| } |
| this->SetProperty("INCLUDE_DIRECTORIES", dirs.c_str()); |
| } |
| |
| // Also for each target's INCLUDE_DIRECTORIES property: |
| for (cmTargets::iterator l = this->Targets.begin(); |
| l != this->Targets.end(); ++l) |
| { |
| cmTarget &t = l->second; |
| if (t.GetType() == cmTarget::INTERFACE_LIBRARY) |
| { |
| continue; |
| } |
| includeDirs = t.GetProperty("INCLUDE_DIRECTORIES"); |
| if(mightExpandVariablesCMP0019(includeDirs)) |
| { |
| std::string dirs = includeDirs; |
| this->ExpandVariablesInString(dirs, true, true); |
| if(pol == cmPolicies::WARN && dirs != includeDirs) |
| { |
| w << "Evaluated target " << t.GetName() << " INCLUDE_DIRECTORIES\n" |
| << " " << includeDirs << "\n" |
| << "as\n" |
| << " " << dirs << "\n"; |
| } |
| t.SetProperty("INCLUDE_DIRECTORIES", dirs.c_str()); |
| } |
| } |
| |
| for(std::vector<std::string>::iterator d = this->LinkDirectories.begin(); |
| d != this->LinkDirectories.end(); ++d) |
| { |
| if(mightExpandVariablesCMP0019(d->c_str())) |
| { |
| std::string orig = *d; |
| this->ExpandVariablesInString(*d, true, true); |
| if(pol == cmPolicies::WARN && *d != orig) |
| { |
| w << "Evaluated link directory\n" |
| << " " << orig << "\n" |
| << "as\n" |
| << " " << *d << "\n"; |
| } |
| } |
| } |
| for(cmTarget::LinkLibraryVectorType::iterator l = |
| this->LinkLibraries.begin(); |
| l != this->LinkLibraries.end(); ++l) |
| { |
| if(mightExpandVariablesCMP0019(l->first.c_str())) |
| { |
| std::string orig = l->first; |
| this->ExpandVariablesInString(l->first, true, true); |
| if(pol == cmPolicies::WARN && l->first != orig) |
| { |
| w << "Evaluated link library\n" |
| << " " << orig << "\n" |
| << "as\n" |
| << " " << l->first << "\n"; |
| } |
| } |
| } |
| |
| if(!w.str().empty()) |
| { |
| cmOStringStream m; |
| m << this->GetPolicies()->GetPolicyWarning(cmPolicies::CMP0019) |
| << "\n" |
| << "The following variable evaluations were encountered:\n" |
| << w.str(); |
| this->IssueMessage(cmake::AUTHOR_WARNING, m.str()); |
| } |
| } |
| |
| 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::PlatformIs64Bit() const |
| { |
| if(const char* sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) |
| { |
| return atoi(sizeof_dptr) == 8; |
| } |
| return false; |
| } |
| |
| 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(const char* 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()) ) |
| { |
| if ( this->IsOn("CMAKE_DISABLE_IN_SOURCE_BUILD") ) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| // Check if this is a subdirectory of the source tree but not a |
| // subdirectory of the build tree |
| if ( cmSystemTools::IsSubDirectory(fileName, |
| this->GetHomeDirectory()) && |
| !cmSystemTools::IsSubDirectory(fileName, |
| this->GetHomeOutputDirectory()) ) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| const char* 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 be not be built correctly.\n", |
| "Missing variable is:\n", |
| name.c_str()); |
| return ""; |
| } |
| return ret; |
| } |
| |
| bool cmMakefile::IsDefinitionSet(const std::string& name) const |
| { |
| const char* def = this->Internal->VarStack.top().Get(name); |
| this->Internal->VarUsageStack.top().insert(name); |
| if(!def) |
| { |
| def = this->GetCacheManager()->GetCacheValue(name); |
| } |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| if(cmVariableWatch* vv = this->GetVariableWatch()) |
| { |
| if(!def) |
| { |
| vv->VariableAccessed |
| (name, cmVariableWatch::UNKNOWN_VARIABLE_DEFINED_ACCESS, |
| def, this); |
| } |
| } |
| #endif |
| return def?true:false; |
| } |
| |
| const char* cmMakefile::GetDefinition(const std::string& name) const |
| { |
| if (this->WarnUnused) |
| { |
| this->Internal->VarUsageStack.top().insert(name); |
| } |
| const char* def = this->Internal->VarStack.top().Get(name); |
| if(!def) |
| { |
| def = this->GetCacheManager()->GetCacheValue(name); |
| } |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| cmVariableWatch* vv = this->GetVariableWatch(); |
| if ( vv && !this->SuppressWatches ) |
| { |
| if ( def ) |
| { |
| vv->VariableAccessed(name, cmVariableWatch::VARIABLE_READ_ACCESS, |
| def, this); |
| } |
| else |
| { |
| vv->VariableAccessed(name, |
| cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS, def, this); |
| } |
| } |
| #endif |
| return def; |
| } |
| |
| const char* cmMakefile::GetSafeDefinition(const std::string& def) const |
| { |
| const char* ret = this->GetDefinition(def); |
| if(!ret) |
| { |
| return ""; |
| } |
| return ret; |
| } |
| |
| std::vector<std::string> cmMakefile |
| ::GetDefinitions(int cacheonly /* = 0 */) const |
| { |
| std::set<std::string> definitions; |
| if ( !cacheonly ) |
| { |
| definitions = this->Internal->VarStack.top().ClosureKeys(); |
| } |
| cmCacheManager::CacheIterator cit = |
| this->GetCacheManager()->GetCacheIterator(); |
| for ( cit.Begin(); !cit.IsAtEnd(); cit.Next() ) |
| { |
| definitions.insert(cit.GetName()); |
| } |
| |
| std::vector<std::string> res; |
| |
| std::set<std::string>::iterator fit; |
| for ( fit = definitions.begin(); fit != definitions.end(); fit ++ ) |
| { |
| res.push_back(*fit); |
| } |
| return res; |
| } |
| |
| |
| const char *cmMakefile::ExpandVariablesInString(std::string& source) const |
| { |
| return this->ExpandVariablesInString(source, false, false); |
| } |
| |
| const char *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.c_str(); |
| } |
| |
| // 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; |
| } |
| 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 = |
| this->GetPolicies()->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.c_str(); |
| } |
| |
| 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("$@\\") == source.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 = ""; |
| |
| // Look for one @VAR@ at a time. |
| const char* in = input.c_str(); |
| while(this->cmAtVarRegex.find(in)) |
| { |
| // Get the range of the string to replace. |
| const char* first = in + this->cmAtVarRegex.start(); |
| const char* last = in + this->cmAtVarRegex.end(); |
| |
| // Store the unchanged part of the string now. |
| source.append(in, first-in); |
| |
| // Lookup the definition of VAR. |
| std::string var(first+1, last-first-2); |
| if(const char* val = this->GetDefinition(var)) |
| { |
| // Store the value in the output escaping as requested. |
| if(escapeQuotes) |
| { |
| source.append(cmSystemTools::EscapeQuotes(val)); |
| } |
| else |
| { |
| source.append(val); |
| } |
| } |
| |
| // Continue looking for @VAR@ further along the string. |
| in = last; |
| } |
| |
| // Append the rest of the unchanged part of the string. |
| source.append(in); |
| |
| return cmake::LOG; |
| } |
| |
| // This method replaces ${VAR} and @VAR@ where VAR is looked up |
| // with GetDefinition(), if not found in the map, nothing is expanded. |
| // It also supports the $ENV{VAR} syntax where VAR is looked up in |
| // the current environment variables. |
| |
| cmCommandArgumentParserHelper parser; |
| parser.SetMakefile(this); |
| parser.SetLineFile(line, filename); |
| parser.SetEscapeQuotes(escapeQuotes); |
| parser.SetNoEscapeMode(noEscapes); |
| parser.SetReplaceAtSyntax(replaceAt); |
| parser.SetRemoveEmpty(removeEmpty); |
| int res = parser.ParseString(source.c_str(), 0); |
| const char* emsg = parser.GetError(); |
| cmake::MessageType mtype = cmake::LOG; |
| if ( res && !emsg[0] ) |
| { |
| source = parser.GetResult(); |
| } |
| else |
| { |
| // Construct the main error message. |
| cmOStringStream error; |
| error << "Syntax error in cmake code "; |
| if(filename && line > 0) |
| { |
| // This filename and line number may be more specific than the |
| // command context because one command invocation can have |
| // arguments on multiple lines. |
| error << "at\n" |
| << " " << filename << ":" << line << "\n"; |
| } |
| error << "when parsing string\n" |
| << " " << source << "\n"; |
| error << emsg; |
| |
| // If the parser failed ("res" is false) then this is a real |
| // argument parsing error, so the policy applies. Otherwise the |
| // parser reported an error message without failing because the |
| // helper implementation is unhappy, which has always reported an |
| // error. |
| mtype = cmake::FATAL_ERROR; |
| if(!res) |
| { |
| // This is a real argument parsing error. Use policy CMP0010 to |
| // decide whether it is an error. |
| switch(this->GetPolicyStatus(cmPolicies::CMP0010)) |
| { |
| case cmPolicies::WARN: |
| error << "\n" |
| << (this->GetPolicies() |
| ->GetPolicyWarning(cmPolicies::CMP0010)); |
| case cmPolicies::OLD: |
| // OLD behavior is to just warn and continue. |
| mtype = cmake::AUTHOR_WARNING; |
| break; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| error << "\n" |
| << (this->GetPolicies() |
| ->GetRequiredPolicyError(cmPolicies::CMP0010)); |
| case cmPolicies::NEW: |
| // NEW behavior is to report the error. |
| break; |
| } |
| } |
| errorstr = error.str(); |
| } |
| return mtype; |
| } |
| |
| typedef enum |
| { |
| NORMAL, |
| ENVIRONMENT, |
| CACHE |
| } t_domain; |
| struct t_lookup |
| { |
| t_lookup(): domain(NORMAL), loc(0) {} |
| t_domain domain; |
| size_t loc; |
| }; |
| |
| cmake::MessageType cmMakefile::ExpandVariablesInStringNew( |
| std::string& errorstr, |
| std::string& source, |
| bool escapeQuotes, |
| bool noEscapes, |
| bool atOnly, |
| const char* filename, |
| long line, |
| bool removeEmpty, |
| bool replaceAt) const |
| { |
| // This method replaces ${VAR} and @VAR@ where VAR is looked up |
| // with GetDefinition(), if not found in the map, nothing is expanded. |
| // It also supports the $ENV{VAR} syntax where VAR is looked up in |
| // the current environment variables. |
| |
| const char* in = source.c_str(); |
| const char* last = in; |
| std::string result; |
| result.reserve(source.size()); |
| std::stack<t_lookup> openstack; |
| bool error = false; |
| bool done = false; |
| openstack.push(t_lookup()); |
| cmake::MessageType mtype = cmake::LOG; |
| |
| do |
| { |
| char inc = *in; |
| switch(inc) |
| { |
| case '}': |
| if(openstack.size() > 1) |
| { |
| t_lookup var = openstack.top(); |
| openstack.pop(); |
| result.append(last, in - last); |
| std::string const& lookup = result.substr(var.loc); |
| const char* value = NULL; |
| std::string varresult; |
| static const std::string lineVar = "CMAKE_CURRENT_LIST_LINE"; |
| switch(var.domain) |
| { |
| case NORMAL: |
| if(filename && lookup == lineVar) |
| { |
| cmOStringStream ostr; |
| ostr << line; |
| varresult = ostr.str(); |
| } |
| else |
| { |
| value = this->GetDefinition(lookup); |
| } |
| break; |
| case ENVIRONMENT: |
| value = cmSystemTools::GetEnv(lookup.c_str()); |
| break; |
| case CACHE: |
| value = this->GetCacheManager()->GetCacheValue(lookup); |
| break; |
| } |
| // Get the string we're meant to append to. |
| if(value) |
| { |
| if(escapeQuotes) |
| { |
| varresult = cmSystemTools::EscapeQuotes(value); |
| } |
| else |
| { |
| varresult = value; |
| } |
| } |
| else if(!removeEmpty) |
| { |
| // 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(lookup)) |
| { |
| if (this->CheckSystemVars || |
| cmSystemTools::IsSubDirectory(filename, |
| this->GetHomeDirectory()) || |
| cmSystemTools::IsSubDirectory(filename, |
| this->GetHomeOutputDirectory())) |
| { |
| cmOStringStream msg; |
| cmListFileBacktrace bt(this->GetLocalGenerator()); |
| cmListFileContext lfc; |
| lfc.FilePath = filename; |
| lfc.Line = line; |
| bt.push_back(lfc); |
| msg << "uninitialized variable \'" << lookup << "\'"; |
| this->GetCMakeInstance()->IssueMessage(cmake::AUTHOR_WARNING, |
| msg.str().c_str(), bt); |
| } |
| } |
| } |
| result.replace(var.loc, result.size() - var.loc, varresult); |
| // Start looking from here on out. |
| last = in + 1; |
| } |
| break; |
| case '$': |
| if(!atOnly) |
| { |
| t_lookup lookup; |
| const char* next = in + 1; |
| const char* start = NULL; |
| char nextc = *next; |
| if(nextc == '{') |
| { |
| // Looking for a variable. |
| start = in + 2; |
| lookup.domain = NORMAL; |
| } |
| else if(nextc == '<') |
| { |
| } |
| else if(!nextc) |
| { |
| result.append(last, next - last); |
| last = next; |
| } |
| else if(cmHasLiteralPrefix(next, "ENV{")) |
| { |
| // Looking for an environment variable. |
| start = in + 5; |
| lookup.domain = ENVIRONMENT; |
| } |
| else if(cmHasLiteralPrefix(next, "CACHE{")) |
| { |
| // Looking for a cache variable. |
| start = in + 7; |
| lookup.domain = CACHE; |
| } |
| else |
| { |
| if(this->cmNamedCurly.find(next)) |
| { |
| errorstr = "Syntax $" |
| + std::string(next, this->cmNamedCurly.end()) |
| + "{} is not supported. Only ${}, $ENV{}, " |
| "and $CACHE{} are allowed."; |
| mtype = cmake::FATAL_ERROR; |
| error = true; |
| } |
| } |
| if(start) |
| { |
| result.append(last, in - last); |
| last = start; |
| in = start - 1; |
| lookup.loc = result.size(); |
| openstack.push(lookup); |
| } |
| break; |
| } |
| case '\\': |
| if(!noEscapes) |
| { |
| const char* next = in + 1; |
| char nextc = *next; |
| if(nextc == 't') |
| { |
| result.append(last, in - last); |
| result.append("\t"); |
| last = next + 1; |
| } |
| else if(nextc == 'n') |
| { |
| result.append(last, in - last); |
| result.append("\n"); |
| last = next + 1; |
| } |
| else if(nextc == 'r') |
| { |
| result.append(last, in - last); |
| result.append("\r"); |
| last = next + 1; |
| } |
| else if(nextc == ';' && openstack.size() == 1) |
| { |
| // Handled in ExpandListArgument; pass the backslash literally. |
| } |
| else if (isalnum(nextc) || nextc == '\0') |
| { |
| errorstr += "Invalid character escape '\\"; |
| if (nextc) |
| { |
| errorstr += nextc; |
| errorstr += "'."; |
| } |
| else |
| { |
| errorstr += "' (at end of input)."; |
| } |
| error = true; |
| } |
| else |
| { |
| // Take what we've found so far, skipping the escape character. |
| result.append(last, in - last); |
| // Start tracking from the next character. |
| last = in + 1; |
| } |
| // Skip the next character since it was escaped, but don't read past |
| // the end of the string. |
| if(*last) |
| { |
| ++in; |
| } |
| } |
| break; |
| case '\n': |
| // Onto the next line. |
| ++line; |
| break; |
| case '\0': |
| done = true; |
| break; |
| case '@': |
| if(replaceAt) |
| { |
| const char* nextAt = strchr(in + 1, '@'); |
| if(nextAt && nextAt != in + 1 && |
| nextAt == in + 1 + strspn(in + 1, |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| "abcdefghijklmnopqrstuvwxyz" |
| "0123456789/_.+-")) |
| { |
| std::string variable(in + 1, nextAt - in - 1); |
| std::string varresult = this->GetSafeDefinition(variable); |
| if(escapeQuotes) |
| { |
| varresult = cmSystemTools::EscapeQuotes(varresult); |
| } |
| // Skip over the variable. |
| result.append(last, in - last); |
| result.append(varresult); |
| in = nextAt; |
| last = in + 1; |
| break; |
| } |
| } |
| // Failed to find a valid @ expansion; treat it as literal. |
| /* FALLTHROUGH */ |
| default: |
| { |
| if(openstack.size() > 1 && |
| !(isalnum(inc) || inc == '_' || |
| inc == '/' || inc == '.' || |
| inc == '+' || inc == '-')) |
| { |
| errorstr += "Invalid character (\'"; |
| errorstr += inc; |
| result.append(last, in - last); |
| errorstr += "\') in a variable name: " |
| "'" + result.substr(openstack.top().loc) + "'"; |
| mtype = cmake::FATAL_ERROR; |
| error = true; |
| } |
| break; |
| } |
| } |
| // Look at the next character. |
| } while(!error && !done && *++in); |
| |
| // Check for open variable references yet. |
| if(!error && openstack.size() != 1) |
| { |
| // There's an open variable reference waiting. Policy CMP0010 flags |
| // whether this is an error or not. The new parser now enforces |
| // CMP0010 as well. |
| errorstr += "There is an unterminated variable reference."; |
| error = true; |
| } |
| |
| if(error) |
| { |
| cmOStringStream emsg; |
| emsg << "Syntax error in cmake code "; |
| if(filename) |
| { |
| // This filename and line number may be more specific than the |
| // command context because one command invocation can have |
| // arguments on multiple lines. |
| emsg << "at\n" |
| << " " << filename << ":" << line << "\n"; |
| } |
| emsg << "when parsing string\n" |
| << " " << source << "\n"; |
| emsg << errorstr; |
| mtype = cmake::FATAL_ERROR; |
| errorstr = emsg.str(); |
| } |
| else |
| { |
| // Append the rest of the unchanged part of the string. |
| result.append(last); |
| |
| source = result; |
| } |
| |
| return mtype; |
| } |
| |
| void cmMakefile::RemoveVariablesInString(std::string& source, |
| bool atOnly) const |
| { |
| if(!atOnly) |
| { |
| cmsys::RegularExpression var("(\\${[A-Za-z_0-9]*})"); |
| while (var.find(source)) |
| { |
| source.erase(var.start(),var.end() - var.start()); |
| } |
| } |
| |
| if(!atOnly) |
| { |
| cmsys::RegularExpression varb("(\\$ENV{[A-Za-z_0-9]*})"); |
| while (varb.find(source)) |
| { |
| source.erase(varb.start(),varb.end() - varb.start()); |
| } |
| } |
| cmsys::RegularExpression var2("(@[A-Za-z_0-9]*@)"); |
| while (var2.find(source)) |
| { |
| source.erase(var2.start(),var2.end() - var2.start()); |
| } |
| } |
| |
| /** |
| * Add the default definitions to the makefile. These values must not |
| * be dependent on anything that isn't known when this cmMakefile instance |
| * is constructed. |
| */ |
| void cmMakefile::AddDefaultDefinitions() |
| { |
| /* Up to CMake 2.4 here only WIN32, UNIX and APPLE were set. |
| With CMake must separate between target and host platform. In most cases |
| the tests for WIN32, UNIX and APPLE will be for the target system, so an |
| additional set of variables for the host system is required -> |
| CMAKE_HOST_WIN32, CMAKE_HOST_UNIX, CMAKE_HOST_APPLE. |
| WIN32, UNIX and APPLE are now set in the platform files in |
| Modules/Platforms/. |
| To keep cmake scripts (-P) and custom language and compiler modules |
| working, these variables are still also set here in this place, but they |
| will be reset in CMakeSystemSpecificInformation.cmake before the platform |
| files are executed. */ |
| #if defined(_WIN32) |
| this->AddDefinition("WIN32", "1"); |
| this->AddDefinition("CMAKE_HOST_WIN32", "1"); |
| #else |
| this->AddDefinition("UNIX", "1"); |
| this->AddDefinition("CMAKE_HOST_UNIX", "1"); |
| #endif |
| #if defined(__CYGWIN__) |
| if(cmSystemTools::IsOn(cmSystemTools::GetEnv("CMAKE_LEGACY_CYGWIN_WIN32"))) |
| { |
| this->AddDefinition("WIN32", "1"); |
| this->AddDefinition("CMAKE_HOST_WIN32", "1"); |
| } |
| #endif |
| #if defined(__APPLE__) |
| this->AddDefinition("APPLE", "1"); |
| this->AddDefinition("CMAKE_HOST_APPLE", "1"); |
| #endif |
| |
| char temp[1024]; |
| sprintf(temp, "%d", cmVersion::GetMinorVersion()); |
| this->AddDefinition("CMAKE_MINOR_VERSION", temp); |
| sprintf(temp, "%d", cmVersion::GetMajorVersion()); |
| this->AddDefinition("CMAKE_MAJOR_VERSION", temp); |
| sprintf(temp, "%d", cmVersion::GetPatchVersion()); |
| this->AddDefinition("CMAKE_PATCH_VERSION", temp); |
| sprintf(temp, "%d", cmVersion::GetTweakVersion()); |
| this->AddDefinition("CMAKE_TWEAK_VERSION", temp); |
| this->AddDefinition("CMAKE_VERSION", cmVersion::GetCMakeVersion()); |
| |
| this->AddDefinition("CMAKE_FILES_DIRECTORY", |
| cmake::GetCMakeFilesDirectory()); |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string |
| cmMakefile::GetConfigurations(std::vector<std::string>& configs, |
| bool single) const |
| { |
| if(this->LocalGenerator->GetGlobalGenerator()->IsMultiConfig()) |
| { |
| if(const char* configTypes = |
| this->GetDefinition("CMAKE_CONFIGURATION_TYPES")) |
| { |
| cmSystemTools::ExpandListArgument(configTypes, configs); |
| } |
| return ""; |
| } |
| else |
| { |
| const std::string& buildType = this->GetSafeDefinition("CMAKE_BUILD_TYPE"); |
| if(single && !buildType.empty()) |
| { |
| configs.push_back(buildType); |
| } |
| return buildType; |
| } |
| } |
| |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| /** |
| * 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 char* 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 |
| |
| bool cmMakefile::IsFunctionBlocked(const cmListFileFunction& lff, |
| cmExecutionStatus &status) |
| { |
| // if there are no blockers get out of here |
| if (this->FunctionBlockers.begin() == this->FunctionBlockers.end()) |
| { |
| return false; |
| } |
| |
| // loop over all function blockers to see if any block this command |
| // evaluate in reverse, this is critical for balanced IF statements etc |
| std::vector<cmFunctionBlocker*>::reverse_iterator pos; |
| for (pos = this->FunctionBlockers.rbegin(); |
| pos != this->FunctionBlockers.rend(); ++pos) |
| { |
| if((*pos)->IsFunctionBlocked(lff, *this, status)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::PushFunctionBlockerBarrier() |
| { |
| this->FunctionBlockerBarriers.push_back(this->FunctionBlockers.size()); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::PopFunctionBlockerBarrier(bool reportError) |
| { |
| // Remove any extra entries pushed on the barrier. |
| FunctionBlockersType::size_type barrier = |
| this->FunctionBlockerBarriers.back(); |
| while(this->FunctionBlockers.size() > barrier) |
| { |
| cmsys::auto_ptr<cmFunctionBlocker> fb(this->FunctionBlockers.back()); |
| this->FunctionBlockers.pop_back(); |
| if(reportError) |
| { |
| // Report the context in which the unclosed block was opened. |
| cmListFileContext const& lfc = fb->GetStartingContext(); |
| cmOStringStream e; |
| e << "A logical block opening on the line\n" |
| << " " << lfc << "\n" |
| << "is not closed."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| reportError = false; |
| } |
| } |
| |
| // Remove the barrier. |
| this->FunctionBlockerBarriers.pop_back(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::ExpandArguments( |
| std::vector<cmListFileArgument> const& inArgs, |
| std::vector<std::string>& outArgs) const |
| { |
| std::vector<cmListFileArgument>::const_iterator i; |
| std::string value; |
| outArgs.reserve(inArgs.size()); |
| for(i = inArgs.begin(); i != inArgs.end(); ++i) |
| { |
| // No expansion in a bracket argument. |
| if(i->Delim == cmListFileArgument::Bracket) |
| { |
| outArgs.push_back(i->Value); |
| continue; |
| } |
| // Expand the variables in the argument. |
| value = i->Value; |
| this->ExpandVariablesInString(value, false, false, false, |
| i->FilePath, i->Line, |
| false, false); |
| |
| // If the argument is quoted, it should be one argument. |
| // Otherwise, it may be a list of arguments. |
| if(i->Delim == cmListFileArgument::Quoted) |
| { |
| outArgs.push_back(value); |
| } |
| else |
| { |
| cmSystemTools::ExpandListArgument(value, outArgs); |
| } |
| } |
| return !cmSystemTools::GetFatalErrorOccured(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::ExpandArguments( |
| std::vector<cmListFileArgument> const& inArgs, |
| std::vector<cmExpandedCommandArgument>& outArgs) const |
| { |
| std::vector<cmListFileArgument>::const_iterator i; |
| std::string value; |
| outArgs.reserve(inArgs.size()); |
| for(i = inArgs.begin(); i != inArgs.end(); ++i) |
| { |
| // No expansion in a bracket argument. |
| if(i->Delim == cmListFileArgument::Bracket) |
| { |
| outArgs.push_back(cmExpandedCommandArgument(i->Value, true)); |
| continue; |
| } |
| // Expand the variables in the argument. |
| value = i->Value; |
| this->ExpandVariablesInString(value, false, false, false, |
| i->FilePath, i->Line, |
| false, false); |
| |
| // If the argument is quoted, it should be one argument. |
| // Otherwise, it may be a list of arguments. |
| if(i->Delim == cmListFileArgument::Quoted) |
| { |
| outArgs.push_back(cmExpandedCommandArgument(value, true)); |
| } |
| else |
| { |
| std::vector<std::string> stringArgs; |
| cmSystemTools::ExpandListArgument(value, stringArgs); |
| for(size_t j = 0; j < stringArgs.size(); ++j) |
| { |
| outArgs.push_back(cmExpandedCommandArgument(stringArgs[j], false)); |
| } |
| } |
| } |
| return !cmSystemTools::GetFatalErrorOccured(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::AddFunctionBlocker(cmFunctionBlocker* fb) |
| { |
| if(!this->CallStack.empty()) |
| { |
| // Record the context in which the blocker is created. |
| fb->SetStartingContext(*(this->CallStack.back().Context)); |
| } |
| |
| this->FunctionBlockers.push_back(fb); |
| } |
| |
| cmsys::auto_ptr<cmFunctionBlocker> |
| cmMakefile::RemoveFunctionBlocker(cmFunctionBlocker* fb, |
| const cmListFileFunction& lff) |
| { |
| // Find the function blocker stack barrier for the current scope. |
| // We only remove a blocker whose index is not less than the barrier. |
| FunctionBlockersType::size_type barrier = 0; |
| if(!this->FunctionBlockerBarriers.empty()) |
| { |
| barrier = this->FunctionBlockerBarriers.back(); |
| } |
| |
| // Search for the function blocker whose scope this command ends. |
| for(FunctionBlockersType::size_type |
| i = this->FunctionBlockers.size(); i > barrier; --i) |
| { |
| std::vector<cmFunctionBlocker*>::iterator pos = |
| this->FunctionBlockers.begin() + (i - 1); |
| if (*pos == fb) |
| { |
| // Warn if the arguments do not match, but always remove. |
| if(!(*pos)->ShouldRemove(lff, *this)) |
| { |
| cmListFileContext const& lfc = fb->GetStartingContext(); |
| cmOStringStream e; |
| e << "A logical block opening on the line\n" |
| << " " << lfc << "\n" |
| << "closes on the line\n" |
| << " " << lff << "\n" |
| << "with mis-matching arguments."; |
| this->IssueMessage(cmake::AUTHOR_WARNING, e.str()); |
| } |
| cmFunctionBlocker* b = *pos; |
| this->FunctionBlockers.erase(pos); |
| return cmsys::auto_ptr<cmFunctionBlocker>(b); |
| } |
| } |
| |
| return cmsys::auto_ptr<cmFunctionBlocker>(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmMakefile::LexicalPushPop::LexicalPushPop(cmMakefile* mf): |
| Makefile(mf), ReportError(true) |
| { |
| this->Makefile->PushFunctionBlockerBarrier(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmMakefile::LexicalPushPop::~LexicalPushPop() |
| { |
| this->Makefile->PopFunctionBlockerBarrier(this->ReportError); |
| } |
| |
| void cmMakefile::SetHomeDirectory(const std::string& dir) |
| { |
| this->cmHomeDirectory = dir; |
| cmSystemTools::ConvertToUnixSlashes(this->cmHomeDirectory); |
| this->AddDefinition("CMAKE_SOURCE_DIR", this->GetHomeDirectory()); |
| if ( !this->GetDefinition("CMAKE_CURRENT_SOURCE_DIR") ) |
| { |
| this->AddDefinition("CMAKE_CURRENT_SOURCE_DIR", this->GetHomeDirectory()); |
| } |
| } |
| |
| void cmMakefile::SetHomeOutputDirectory(const std::string& lib) |
| { |
| this->HomeOutputDirectory = lib; |
| cmSystemTools::ConvertToUnixSlashes(this->HomeOutputDirectory); |
| this->AddDefinition("CMAKE_BINARY_DIR", this->GetHomeOutputDirectory()); |
| if ( !this->GetDefinition("CMAKE_CURRENT_BINARY_DIR") ) |
| { |
| this->AddDefinition("CMAKE_CURRENT_BINARY_DIR", |
| this->GetHomeOutputDirectory()); |
| } |
| } |
| |
| void cmMakefile::SetScriptModeFile(const char* scriptfile) |
| { |
| this->AddDefinition("CMAKE_SCRIPT_MODE_FILE", scriptfile); |
| } |
| |
| void cmMakefile::SetArgcArgv(const std::vector<std::string>& args) |
| { |
| cmOStringStream strStream; |
| strStream << args.size(); |
| this->AddDefinition("CMAKE_ARGC", strStream.str().c_str()); |
| //this->MarkVariableAsUsed("CMAKE_ARGC"); |
| |
| for (unsigned int t = 0; t < args.size(); ++t) |
| { |
| cmOStringStream tmpStream; |
| tmpStream << "CMAKE_ARGV" << t; |
| this->AddDefinition(tmpStream.str(), args[t].c_str()); |
| //this->MarkVariableAsUsed(tmpStream.str().c_str()); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmSourceFile* cmMakefile::GetSource(const std::string& sourceName) const |
| { |
| cmSourceFileLocation sfl(this, sourceName); |
| for(std::vector<cmSourceFile*>::const_iterator |
| sfi = this->SourceFiles.begin(); |
| sfi != this->SourceFiles.end(); ++sfi) |
| { |
| cmSourceFile* sf = *sfi; |
| if(sf->Matches(sfl)) |
| { |
| return sf; |
| } |
| } |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmSourceFile* cmMakefile::CreateSource(const std::string& sourceName, |
| bool generated) |
| { |
| cmSourceFile* sf = new cmSourceFile(this, sourceName); |
| if(generated) |
| { |
| sf->SetProperty("GENERATED", "1"); |
| } |
| this->SourceFiles.push_back(sf); |
| return sf; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmSourceFile* cmMakefile::GetOrCreateSource(const std::string& sourceName, |
| bool generated) |
| { |
| if(cmSourceFile* esf = this->GetSource(sourceName)) |
| { |
| return esf; |
| } |
| else |
| { |
| return this->CreateSource(sourceName, generated); |
| } |
| } |
| |
| void cmMakefile::EnableLanguage(std::vector<std::string> const & lang, |
| bool optional) |
| { |
| this->AddDefinition("CMAKE_CFG_INTDIR", |
| this->LocalGenerator->GetGlobalGenerator() |
| ->GetCMakeCFGIntDir()); |
| this->LocalGenerator->GetGlobalGenerator()->EnableLanguage(lang, this, |
| optional); |
| } |
| |
| void cmMakefile::ExpandSourceListArguments( |
| std::vector<std::string> const& arguments, |
| std::vector<std::string>& newargs, unsigned int /* start */) const |
| { |
| // now expand the args |
| unsigned int i; |
| for(i = 0; i < arguments.size(); ++i) |
| { |
| // List expansion will have been done already. |
| newargs.push_back(arguments[i]); |
| } |
| } |
| |
| int cmMakefile::TryCompile(const std::string& srcdir, |
| const std::string& bindir, |
| const std::string& projectName, |
| const std::string& targetName, |
| bool fast, |
| const std::vector<std::string> *cmakeArgs, |
| std::string& output) |
| { |
| this->Internal->IsSourceFileTryCompile = fast; |
| // does the binary directory exist ? If not create it... |
| if (!cmSystemTools::FileIsDirectory(bindir.c_str())) |
| { |
| cmSystemTools::MakeDirectory(bindir.c_str()); |
| } |
| |
| // change to the tests directory and run cmake |
| // use the cmake object instead of calling cmake |
| std::string cwd = cmSystemTools::GetCurrentWorkingDirectory(); |
| cmSystemTools::ChangeDirectory(bindir.c_str()); |
| |
| // make sure the same generator is used |
| // use this program as the cmake to be run, it should not |
| // be run that way but the cmake object requires a vailid path |
| std::string cmakeCommand = this->GetDefinition("CMAKE_COMMAND"); |
| cmake cm; |
| cm.SetIsInTryCompile(true); |
| cmGlobalGenerator *gg = cm.CreateGlobalGenerator |
| (this->LocalGenerator->GetGlobalGenerator()->GetName()); |
| if (!gg) |
| { |
| cmSystemTools::Error( |
| "Internal CMake error, TryCompile bad GlobalGenerator"); |
| // return to the original directory |
| cmSystemTools::ChangeDirectory(cwd.c_str()); |
| this->Internal->IsSourceFileTryCompile = false; |
| return 1; |
| } |
| cm.SetGlobalGenerator(gg); |
| |
| // do a configure |
| cm.SetHomeDirectory(srcdir); |
| cm.SetHomeOutputDirectory(bindir); |
| cm.SetStartDirectory(srcdir); |
| cm.SetStartOutputDirectory(bindir); |
| cm.SetGeneratorPlatform(this->GetCMakeInstance()->GetGeneratorPlatform()); |
| cm.SetGeneratorToolset(this->GetCMakeInstance()->GetGeneratorToolset()); |
| cm.LoadCache(); |
| if(!gg->IsMultiConfig()) |
| { |
| if(const char* config = |
| this->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION")) |
| { |
| // Tell the single-configuration generator which one to use. |
| // Add this before the user-provided CMake arguments in case |
| // one of the arguments is -DCMAKE_BUILD_TYPE=... |
| cm.AddCacheEntry("CMAKE_BUILD_TYPE", config, |
| "Build configuration", cmCacheManager::STRING); |
| } |
| } |
| // if cmake args were provided then pass them in |
| if (cmakeArgs) |
| { |
| // FIXME: Workaround to ignore unused CLI variables in try-compile. |
| // |
| // Ideally we should use SetArgs to honor options like --warn-unused-vars. |
| // However, there is a subtle problem when certain arguments are passed to |
| // a macro wrapping around try_compile or try_run that does not escape |
| // semicolons in its parameters but just passes ${ARGV} or ${ARGN}. In |
| // this case a list argument like "-DVAR=a;b" gets split into multiple |
| // cmake arguments "-DVAR=a" and "b". Currently SetCacheArgs ignores |
| // argument "b" and uses just "-DVAR=a", leading to a subtle bug in that |
| // the try_compile or try_run does not get the proper value of VAR. If we |
| // call SetArgs here then it would treat "b" as the source directory and |
| // cause an error such as "The source directory .../CMakeFiles/CMakeTmp/b |
| // does not exist", thus breaking the try_compile or try_run completely. |
| // |
| // Strictly speaking the bug is in the wrapper macro because the CMake |
| // language has always flattened nested lists and the macro should escape |
| // the semicolons in its arguments before forwarding them. However, this |
| // bug is so subtle that projects typically work anyway, usually because |
| // the value VAR=a is sufficient for the try_compile or try_run to get the |
| // correct result. Calling SetArgs here would break such projects that |
| // previously built. Instead we work around the issue by never reporting |
| // unused arguments and ignoring options such as --warn-unused-vars. |
| cm.SetWarnUnusedCli(false); |
| //cm.SetArgs(*cmakeArgs, true); |
| |
| cm.SetCacheArgs(*cmakeArgs); |
| } |
| // to save time we pass the EnableLanguage info directly |
| gg->EnableLanguagesFromGenerator |
| (this->LocalGenerator->GetGlobalGenerator(), this); |
| if(this->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) |
| { |
| cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", |
| "TRUE", "", cmCacheManager::INTERNAL); |
| } |
| else |
| { |
| cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", |
| "FALSE", "", cmCacheManager::INTERNAL); |
| } |
| if (cm.Configure() != 0) |
| { |
| cmSystemTools::Error( |
| "Internal CMake error, TryCompile configure of cmake failed"); |
| // return to the original directory |
| cmSystemTools::ChangeDirectory(cwd.c_str()); |
| this->Internal->IsSourceFileTryCompile = false; |
| return 1; |
| } |
| |
| if (cm.Generate() != 0) |
| { |
| cmSystemTools::Error( |
| "Internal CMake error, TryCompile generation of cmake failed"); |
| // return to the original directory |
| cmSystemTools::ChangeDirectory(cwd.c_str()); |
| this->Internal->IsSourceFileTryCompile = false; |
| return 1; |
| } |
| |
| // finally call the generator to actually build the resulting project |
| int ret = |
| this->LocalGenerator->GetGlobalGenerator()->TryCompile(srcdir,bindir, |
| projectName, |
| targetName, |
| fast, |
| output, |
| this); |
| |
| cmSystemTools::ChangeDirectory(cwd.c_str()); |
| this->Internal->IsSourceFileTryCompile = false; |
| return ret; |
| } |
| |
| bool cmMakefile::GetIsSourceFileTryCompile() const |
| { |
| return this->Internal->IsSourceFileTryCompile; |
| } |
| |
| cmake *cmMakefile::GetCMakeInstance() const |
| { |
| if ( this->LocalGenerator && this->LocalGenerator->GetGlobalGenerator() ) |
| { |
| return this->LocalGenerator->GetGlobalGenerator()->GetCMakeInstance(); |
| } |
| return 0; |
| } |
| |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| cmVariableWatch *cmMakefile::GetVariableWatch() const |
| { |
| if ( this->GetCMakeInstance() && |
| this->GetCMakeInstance()->GetVariableWatch() ) |
| { |
| return this->GetCMakeInstance()->GetVariableWatch(); |
| } |
| return 0; |
| } |
| #endif |
| |
| void cmMakefile::AddMacro(const char* name, const char* signature) |
| { |
| if ( !name || !signature ) |
| { |
| return; |
| } |
| this->MacrosMap[name] = signature; |
| } |
| |
| void cmMakefile::GetListOfMacros(std::string& macros) const |
| { |
| StringStringMap::const_iterator it; |
| macros = ""; |
| int cc = 0; |
| for ( it = this->MacrosMap.begin(); it != this->MacrosMap.end(); ++it ) |
| { |
| if ( cc > 0 ) |
| { |
| macros += ";"; |
| } |
| macros += it->first; |
| cc ++; |
| } |
| } |
| |
| cmCacheManager *cmMakefile::GetCacheManager() const |
| { |
| return this->GetCMakeInstance()->GetCacheManager(); |
| } |
| |
| void cmMakefile::DisplayStatus(const char* message, float s) const |
| { |
| cmake* cm = this->GetLocalGenerator()->GetGlobalGenerator() |
| ->GetCMakeInstance(); |
| if (cm->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) |
| { |
| // don't output any STATUS message in FIND_PACKAGE_MODE, since they will |
| // directly be fed to the compiler, which will be confused. |
| return; |
| } |
| cm->UpdateProgress(message, s); |
| } |
| |
| std::string cmMakefile::GetModulesFile(const char* filename) const |
| { |
| std::string result; |
| |
| // We search the module always in CMAKE_ROOT and in CMAKE_MODULE_PATH, |
| // and then decide based on the policy setting which one to return. |
| // See CMP0017 for more details. |
| // The specific problem was that KDE 4.5.0 installs a |
| // FindPackageHandleStandardArgs.cmake which doesn't have the new features |
| // of FPHSA.cmake introduced in CMake 2.8.3 yet, and by setting |
| // CMAKE_MODULE_PATH also e.g. FindZLIB.cmake from cmake included |
| // FPHSA.cmake from kdelibs and not from CMake, and tried to use the |
| // new features, which were not there in the version from kdelibs, and so |
| // failed (" |
| std::string moduleInCMakeRoot; |
| std::string moduleInCMakeModulePath; |
| |
| // Always search in CMAKE_MODULE_PATH: |
| const char* cmakeModulePath = this->GetDefinition("CMAKE_MODULE_PATH"); |
| if(cmakeModulePath) |
| { |
| std::vector<std::string> modulePath; |
| cmSystemTools::ExpandListArgument(cmakeModulePath, modulePath); |
| |
| //Look through the possible module directories. |
| for(std::vector<std::string>::iterator i = modulePath.begin(); |
| i != modulePath.end(); ++i) |
| { |
| std::string itempl = *i; |
| cmSystemTools::ConvertToUnixSlashes(itempl); |
| itempl += "/"; |
| itempl += filename; |
| if(cmSystemTools::FileExists(itempl.c_str())) |
| { |
| moduleInCMakeModulePath = itempl; |
| break; |
| } |
| } |
| } |
| |
| // Always search in the standard modules location. |
| const char* cmakeRoot = this->GetDefinition("CMAKE_ROOT"); |
| if(cmakeRoot) |
| { |
| moduleInCMakeRoot = cmakeRoot; |
| moduleInCMakeRoot += "/Modules/"; |
| moduleInCMakeRoot += filename; |
| cmSystemTools::ConvertToUnixSlashes(moduleInCMakeRoot); |
| if(!cmSystemTools::FileExists(moduleInCMakeRoot.c_str())) |
| { |
| moduleInCMakeRoot = ""; |
| } |
| } |
| |
| // Normally, prefer the files found in CMAKE_MODULE_PATH. Only when the file |
| // from which we are being called is located itself in CMAKE_ROOT, then |
| // prefer results from CMAKE_ROOT depending on the policy setting. |
| result = moduleInCMakeModulePath; |
| if (result.size() == 0) |
| { |
| result = moduleInCMakeRoot; |
| } |
| |
| if ((moduleInCMakeModulePath.size()>0) && (moduleInCMakeRoot.size()>0)) |
| { |
| const char* currentFile = this->GetDefinition("CMAKE_CURRENT_LIST_FILE"); |
| std::string mods = cmakeRoot + std::string("/Modules/"); |
| if (currentFile && strncmp(currentFile, mods.c_str(), mods.size()) == 0) |
| { |
| switch (this->GetPolicyStatus(cmPolicies::CMP0017)) |
| { |
| case cmPolicies::WARN: |
| { |
| cmOStringStream e; |
| e << "File " << currentFile << " includes " |
| << moduleInCMakeModulePath |
| << " (found via CMAKE_MODULE_PATH) which shadows " |
| << moduleInCMakeRoot << ". This may cause errors later on .\n" |
| << this->GetPolicies()->GetPolicyWarning(cmPolicies::CMP0017); |
| |
| this->IssueMessage(cmake::AUTHOR_WARNING, e.str()); |
| // break; // fall through to OLD behaviour |
| } |
| case cmPolicies::OLD: |
| result = moduleInCMakeModulePath; |
| break; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| case cmPolicies::NEW: |
| result = moduleInCMakeRoot; |
| break; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| void cmMakefile::ConfigureString(const std::string& input, |
| std::string& output, bool atOnly, |
| bool escapeQuotes) const |
| { |
| // Split input to handle one line at a time. |
| std::string::const_iterator lineStart = input.begin(); |
| while(lineStart != input.end()) |
| { |
| // Find the end of this line. |
| std::string::const_iterator lineEnd = lineStart; |
| while(lineEnd != input.end() && *lineEnd != '\n') |
| { |
| ++lineEnd; |
| } |
| |
| // Copy the line. |
| std::string line(lineStart, lineEnd); |
| |
| // Skip the newline character. |
| bool haveNewline = (lineEnd != input.end()); |
| if(haveNewline) |
| { |
| ++lineEnd; |
| } |
| |
| // Replace #cmakedefine instances. |
| if(this->cmDefineRegex.find(line)) |
| { |
| const char* def = |
| this->GetDefinition(this->cmDefineRegex.match(1)); |
| if(!cmSystemTools::IsOff(def)) |
| { |
| cmSystemTools::ReplaceString(line, "#cmakedefine", "#define"); |
| output += line; |
| } |
| else |
| { |
| output += "/* #undef "; |
| output += this->cmDefineRegex.match(1); |
| output += " */"; |
| } |
| } |
| else if(this->cmDefine01Regex.find(line)) |
| { |
| const char* def = |
| this->GetDefinition(this->cmDefine01Regex.match(1)); |
| cmSystemTools::ReplaceString(line, "#cmakedefine01", "#define"); |
| output += line; |
| if(!cmSystemTools::IsOff(def)) |
| { |
| output += " 1"; |
| } |
| else |
| { |
| output += " 0"; |
| } |
| } |
| else |
| { |
| output += line; |
| } |
| |
| if(haveNewline) |
| { |
| output += "\n"; |
| } |
| |
| // Move to the next line. |
| lineStart = lineEnd; |
| } |
| |
| // Perform variable replacements. |
| this->ExpandVariablesInString(output, escapeQuotes, true, |
| atOnly, 0, -1, true, true); |
| } |
| |
| int cmMakefile::ConfigureFile(const char* infile, const char* outfile, |
| bool copyonly, bool atOnly, bool escapeQuotes, |
| const cmNewLineStyle& newLine) |
| { |
| int res = 1; |
| if ( !this->CanIWriteThisFile(outfile) ) |
| { |
| cmSystemTools::Error("Attempt to write file: ", |
| outfile, " into a source directory."); |
| return 0; |
| } |
| if ( !cmSystemTools::FileExists(infile) ) |
| { |
| cmSystemTools::Error("File ", infile, " does not exist."); |
| return 0; |
| } |
| std::string soutfile = outfile; |
| std::string sinfile = infile; |
| this->AddCMakeDependFile(sinfile); |
| cmSystemTools::ConvertToUnixSlashes(soutfile); |
| |
| // Re-generate if non-temporary outputs are missing. |
| //when we finalize the configuration we will remove all |
| //output files that now don't exist. |
| this->AddCMakeOutputFile(soutfile); |
| |
| mode_t perm = 0; |
| cmSystemTools::GetPermissions(sinfile.c_str(), perm); |
| std::string::size_type pos = soutfile.rfind('/'); |
| if(pos != std::string::npos) |
| { |
| std::string path = soutfile.substr(0, pos); |
| cmSystemTools::MakeDirectory(path.c_str()); |
| } |
| |
| if(copyonly) |
| { |
| if ( !cmSystemTools::CopyFileIfDifferent(sinfile.c_str(), |
| soutfile.c_str())) |
| { |
| return 0; |
| } |
| } |
| else |
| { |
| std::string newLineCharacters; |
| std::ios_base::openmode omode = std::ios_base::out | std::ios_base::trunc; |
| if (newLine.IsValid()) |
| { |
| newLineCharacters = newLine.GetCharacters(); |
| omode |= std::ios::binary; |
| } |
| else |
| { |
| newLineCharacters = "\n"; |
| } |
| std::string tempOutputFile = soutfile; |
| tempOutputFile += ".tmp"; |
| cmsys::ofstream fout(tempOutputFile.c_str(), omode); |
| if(!fout) |
| { |
| cmSystemTools::Error( |
| "Could not open file for write in copy operation ", |
| tempOutputFile.c_str()); |
| cmSystemTools::ReportLastSystemError(""); |
| return 0; |
| } |
| cmsys::ifstream fin(sinfile.c_str()); |
| if(!fin) |
| { |
| cmSystemTools::Error("Could not open file for read in copy operation ", |
| sinfile.c_str()); |
| return 0; |
| } |
| |
| cmsys::FStream::BOM bom = cmsys::FStream::ReadBOM(fin); |
| if(bom != cmsys::FStream::BOM_None && |
| bom != cmsys::FStream::BOM_UTF8) |
| { |
| cmOStringStream e; |
| e << "File starts with a Byte-Order-Mark that is not UTF-8:\n " |
| << sinfile; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return 0; |
| } |
| // rewind to copy BOM to output file |
| fin.seekg(0); |
| |
| |
| // now copy input to output and expand variables in the |
| // input file at the same time |
| std::string inLine; |
| std::string outLine; |
| while( cmSystemTools::GetLineFromStream(fin, inLine) ) |
| { |
| outLine = ""; |
| this->ConfigureString(inLine, outLine, atOnly, escapeQuotes); |
| fout << outLine.c_str() << newLineCharacters; |
| } |
| // close the files before attempting to copy |
| fin.close(); |
| fout.close(); |
| if ( !cmSystemTools::CopyFileIfDifferent(tempOutputFile.c_str(), |
| soutfile.c_str()) ) |
| { |
| res = 0; |
| } |
| else |
| { |
| cmSystemTools::SetPermissions(soutfile.c_str(), perm); |
| } |
| cmSystemTools::RemoveFile(tempOutputFile.c_str()); |
| } |
| return res; |
| } |
| |
| void cmMakefile::SetProperty(const std::string& prop, const char* value) |
| { |
| if ( prop == "LINK_DIRECTORIES" ) |
| { |
| std::vector<std::string> varArgsExpanded; |
| if(value) |
| { |
| cmSystemTools::ExpandListArgument(value, varArgsExpanded); |
| } |
| this->SetLinkDirectories(varArgsExpanded); |
| return; |
| } |
| if (prop == "INCLUDE_DIRECTORIES") |
| { |
| this->IncludeDirectoriesEntries.clear(); |
| if (!value) |
| { |
| return; |
| } |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| this->IncludeDirectoriesEntries.push_back( |
| cmValueWithOrigin(value, lfbt)); |
| return; |
| } |
| if (prop == "COMPILE_OPTIONS") |
| { |
| this->CompileOptionsEntries.clear(); |
| if (!value) |
| { |
| return; |
| } |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| this->CompileOptionsEntries.push_back(cmValueWithOrigin(value, lfbt)); |
| return; |
| } |
| if (prop == "COMPILE_DEFINITIONS") |
| { |
| this->CompileDefinitionsEntries.clear(); |
| if (!value) |
| { |
| return; |
| } |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| cmValueWithOrigin entry(value, lfbt); |
| this->CompileDefinitionsEntries.push_back(entry); |
| return; |
| } |
| |
| if ( prop == "INCLUDE_REGULAR_EXPRESSION" ) |
| { |
| this->SetIncludeRegularExpression(value); |
| return; |
| } |
| |
| if ( prop == "ADDITIONAL_MAKE_CLEAN_FILES" ) |
| { |
| // This property is not inherrited |
| if ( strcmp(this->GetCurrentDirectory(), |
| this->GetStartDirectory()) != 0 ) |
| { |
| return; |
| } |
| } |
| |
| this->Properties.SetProperty(prop,value,cmProperty::DIRECTORY); |
| } |
| |
| void cmMakefile::AppendProperty(const std::string& prop, |
| const char* value, |
| bool asString) |
| { |
| if (prop == "INCLUDE_DIRECTORIES") |
| { |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| this->IncludeDirectoriesEntries.push_back( |
| cmValueWithOrigin(value, lfbt)); |
| return; |
| } |
| if (prop == "COMPILE_OPTIONS") |
| { |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| this->CompileOptionsEntries.push_back( |
| cmValueWithOrigin(value, lfbt)); |
| return; |
| } |
| if (prop == "COMPILE_DEFINITIONS") |
| { |
| cmListFileBacktrace lfbt = this->GetBacktrace(); |
| this->CompileDefinitionsEntries.push_back( |
| cmValueWithOrigin(value, lfbt)); |
| return; |
| } |
| if ( prop == "LINK_DIRECTORIES" ) |
| { |
| std::vector<std::string> varArgsExpanded; |
| cmSystemTools::ExpandListArgument(value, varArgsExpanded); |
| for(std::vector<std::string>::const_iterator vi = varArgsExpanded.begin(); |
| vi != varArgsExpanded.end(); ++vi) |
| { |
| this->AddLinkDirectory(*vi); |
| } |
| return; |
| } |
| |
| this->Properties.AppendProperty(prop,value,cmProperty::DIRECTORY,asString); |
| } |
| |
| const char *cmMakefile::GetProperty(const std::string& prop) const |
| { |
| return this->GetProperty(prop, cmProperty::DIRECTORY); |
| } |
| |
| const char *cmMakefile::GetProperty(const std::string& prop, |
| cmProperty::ScopeType scope) const |
| { |
| // watch for specific properties |
| static std::string output; |
| output = ""; |
| if (prop == "PARENT_DIRECTORY") |
| { |
| if(cmLocalGenerator* plg = this->LocalGenerator->GetParent()) |
| { |
| output = plg->GetMakefile()->GetStartDirectory(); |
| } |
| return output.c_str(); |
| } |
| else if (prop == "INCLUDE_REGULAR_EXPRESSION" ) |
| { |
| output = this->GetIncludeRegularExpression(); |
| return output.c_str(); |
| } |
| else if (prop == "LISTFILE_STACK") |
| { |
| for (std::deque<std::string>::const_iterator |
| i = this->ListFileStack.begin(); |
| i != this->ListFileStack.end(); ++i) |
| { |
| if (i != this->ListFileStack.begin()) |
| { |
| output += ";"; |
| } |
| output += *i; |
| } |
| return output.c_str(); |
| } |
| else if (prop == "VARIABLES" || prop == "CACHE_VARIABLES") |
| { |
| int cacheonly = 0; |
| if ( prop == "CACHE_VARIABLES" ) |
| { |
| cacheonly = 1; |
| } |
| std::vector<std::string> vars = this->GetDefinitions(cacheonly); |
| for (unsigned int cc = 0; cc < vars.size(); cc ++ ) |
| { |
| if ( cc > 0 ) |
| { |
| output += ";"; |
| } |
| output += vars[cc]; |
| } |
| return output.c_str(); |
| } |
| else if (prop == "MACROS") |
| { |
| this->GetListOfMacros(output); |
| return output.c_str(); |
| } |
| else if (prop == "DEFINITIONS") |
| { |
| output += this->DefineFlagsOrig; |
| return output.c_str(); |
| } |
| else if (prop == "LINK_DIRECTORIES") |
| { |
| cmOStringStream str; |
| for (std::vector<std::string>::const_iterator |
| it = this->GetLinkDirectories().begin(); |
| it != this->GetLinkDirectories().end(); |
| ++ it ) |
| { |
| if ( it != this->GetLinkDirectories().begin()) |
| { |
| str << ";"; |
| } |
| str << it->c_str(); |
| } |
| output = str.str(); |
| return output.c_str(); |
| } |
| else if (prop == "INCLUDE_DIRECTORIES") |
| { |
| std::string sep; |
| for (std::vector<cmValueWithOrigin>::const_iterator |
| it = this->IncludeDirectoriesEntries.begin(), |
| end = this->IncludeDirectoriesEntries.end(); |
| it != end; ++it) |
| { |
| output += sep; |
| output += it->Value; |
| sep = ";"; |
| } |
| return output.c_str(); |
| } |
| else if (prop == "COMPILE_OPTIONS") |
| { |
| std::string sep; |
| for (std::vector<cmValueWithOrigin>::const_iterator |
| it = this->CompileOptionsEntries.begin(), |
| end = this->CompileOptionsEntries.end(); |
| it != end; ++it) |
| { |
| output += sep; |
| output += it->Value; |
| sep = ";"; |
| } |
| return output.c_str(); |
| } |
| else if (prop == "COMPILE_DEFINITIONS") |
| { |
| std::string sep; |
| for (std::vector<cmValueWithOrigin>::const_iterator |
| it = this->CompileDefinitionsEntries.begin(), |
| end = this->CompileDefinitionsEntries.end(); |
| it != end; ++it) |
| { |
| output += sep; |
| output += it->Value; |
| sep = ";"; |
| } |
| return output.c_str(); |
| } |
| |
| bool chain = false; |
| const char *retVal = |
| this->Properties.GetPropertyValue(prop, scope, chain); |
| if (chain) |
| { |
| if(this->LocalGenerator->GetParent()) |
| { |
| return this->LocalGenerator->GetParent()->GetMakefile()-> |
| GetProperty(prop, scope); |
| } |
| return this->GetCMakeInstance()->GetProperty(prop,scope); |
| } |
| |
| return retVal; |
| } |
| |
| bool cmMakefile::GetPropertyAsBool(const std::string& prop) const |
| { |
| return cmSystemTools::IsOn(this->GetProperty(prop)); |
| } |
| |
| //---------------------------------------------------------------------------- |
| const char* cmMakefile::GetFeature(const std::string& feature, |
| const std::string& config) |
| { |
| // TODO: Define accumulation policy for features (prepend, append, replace). |
| // Currently we always replace. |
| if(!config.empty()) |
| { |
| std::string featureConfig = feature; |
| featureConfig += "_"; |
| featureConfig += cmSystemTools::UpperCase(config); |
| if(const char* value = this->GetProperty(featureConfig)) |
| { |
| return value; |
| } |
| } |
| if(const char* value = this->GetProperty(feature)) |
| { |
| return value; |
| } |
| if(cmLocalGenerator* parent = this->LocalGenerator->GetParent()) |
| { |
| return parent->GetMakefile()->GetFeature(feature, config); |
| } |
| return 0; |
| } |
| |
| cmTarget* cmMakefile::FindTarget(const std::string& name, |
| bool excludeAliases) const |
| { |
| if (!excludeAliases) |
| { |
| TargetMap::const_iterator i = this->AliasTargets.find(name); |
| if (i != this->AliasTargets.end()) |
| { |
| return i->second; |
| } |
| } |
| cmTargets::iterator i = this->Targets.find( name ); |
| if ( i != this->Targets.end() ) |
| { |
| return &i->second; |
| } |
| |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmTest* cmMakefile::CreateTest(const std::string& testName) |
| { |
| cmTest* test = this->GetTest(testName); |
| if ( test ) |
| { |
| return test; |
| } |
| test = new cmTest(this); |
| test->SetName(testName); |
| this->Tests[testName] = test; |
| return test; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmTest* cmMakefile::GetTest(const std::string& testName) const |
| { |
| std::map<std::string, cmTest*>::const_iterator |
| mi = this->Tests.find(testName); |
| if(mi != this->Tests.end()) |
| { |
| return mi->second; |
| } |
| return 0; |
| } |
| |
| void cmMakefile::AddCMakeDependFilesFromUser() |
| { |
| std::vector<std::string> deps; |
| if(const char* deps_str = this->GetProperty("CMAKE_CONFIGURE_DEPENDS")) |
| { |
| cmSystemTools::ExpandListArgument(deps_str, deps); |
| } |
| for(std::vector<std::string>::iterator i = deps.begin(); |
| i != deps.end(); ++i) |
| { |
| if(cmSystemTools::FileIsFullPath(i->c_str())) |
| { |
| this->AddCMakeDependFile(*i); |
| } |
| else |
| { |
| std::string f = this->GetCurrentDirectory(); |
| f += "/"; |
| f += *i; |
| this->AddCMakeDependFile(f); |
| } |
| } |
| } |
| |
| std::string cmMakefile::GetListFileStack() const |
| { |
| cmOStringStream tmp; |
| size_t depth = this->ListFileStack.size(); |
| if (depth > 0) |
| { |
| std::deque<std::string>::const_iterator it = this->ListFileStack.end(); |
| do |
| { |
| if (depth != this->ListFileStack.size()) |
| { |
| tmp << "\n "; |
| } |
| --it; |
| tmp << "["; |
| tmp << depth; |
| tmp << "]\t"; |
| tmp << *it; |
| depth--; |
| } |
| while (it != this->ListFileStack.begin()); |
| } |
| return tmp.str(); |
| } |
| |
| |
| void cmMakefile::PushScope() |
| { |
| cmDefinitions* parent = &this->Internal->VarStack.top(); |
| const std::set<std::string>& init = this->Internal->VarInitStack.top(); |
| const std::set<std::string>& usage = this->Internal->VarUsageStack.top(); |
| this->Internal->VarStack.push(cmDefinitions(parent)); |
| this->Internal->VarInitStack.push(init); |
| this->Internal->VarUsageStack.push(usage); |
| } |
| |
| void cmMakefile::PopScope() |
| { |
| cmDefinitions* current = &this->Internal->VarStack.top(); |
| std::set<std::string> init = this->Internal->VarInitStack.top(); |
| std::set<std::string> usage = this->Internal->VarUsageStack.top(); |
| const std::set<std::string>& locals = current->LocalKeys(); |
| // Remove initialization and usage information for variables in the local |
| // scope. |
| std::set<std::string>::const_iterator it = locals.begin(); |
| for (; it != locals.end(); ++it) |
| { |
| init.erase(*it); |
| if (!this->VariableUsed(*it)) |
| { |
| this->CheckForUnused("out of scope", *it); |
| } |
| else |
| { |
| usage.erase(*it); |
| } |
| } |
| this->Internal->VarStack.pop(); |
| this->Internal->VarInitStack.pop(); |
| this->Internal->VarUsageStack.pop(); |
| // Push initialization and usage up to the parent scope. |
| it = init.begin(); |
| for (; it != init.end(); ++it) |
| { |
| this->Internal->VarInitStack.top().insert(*it); |
| } |
| it = usage.begin(); |
| for (; it != usage.end(); ++it) |
| { |
| this->Internal->VarUsageStack.top().insert(*it); |
| } |
| } |
| |
| void cmMakefile::RaiseScope(const std::string& var, const char *varDef) |
| { |
| if (var.empty()) |
| { |
| return; |
| } |
| |
| cmDefinitions& cur = this->Internal->VarStack.top(); |
| if(cmDefinitions* up = cur.GetParent()) |
| { |
| // First localize the definition in the current scope. |
| cur.Get(var); |
| |
| // Now update the definition in the parent scope. |
| up->Set(var, varDef); |
| } |
| else if(cmLocalGenerator* plg = this->LocalGenerator->GetParent()) |
| { |
| // Update the definition in the parent directory top scope. This |
| // directory's scope was initialized by the closure of the parent |
| // scope, so we do not need to localize the definition first. |
| cmMakefile* parent = plg->GetMakefile(); |
| if (varDef) |
| { |
| parent->AddDefinition(var, varDef); |
| } |
| else |
| { |
| parent->RemoveDefinition(var); |
| } |
| } |
| else |
| { |
| cmOStringStream m; |
| m << "Cannot set \"" << var << "\": current scope has no parent."; |
| this->IssueMessage(cmake::AUTHOR_WARNING, m.str()); |
| } |
| } |
| |
| |
| // define properties |
| void cmMakefile::DefineProperties(cmake *cm) |
| { |
| cm->DefineProperty |
| ("RULE_LAUNCH_COMPILE", cmProperty::DIRECTORY, |
| "", "", true); |
| cm->DefineProperty |
| ("RULE_LAUNCH_LINK", cmProperty::DIRECTORY, |
| "", "", true); |
| cm->DefineProperty |
| ("RULE_LAUNCH_CUSTOM", cmProperty::DIRECTORY, |
| "", "", true); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmTarget* |
| cmMakefile::AddImportedTarget(const std::string& name, |
| cmTarget::TargetType type, |
| bool global) |
| { |
| // Create the target. |
| cmsys::auto_ptr<cmTarget> target(new cmTarget); |
| target->SetType(type, name); |
| target->MarkAsImported(); |
| target->SetMakefile(this); |
| |
| // Add to the set of available imported targets. |
| this->ImportedTargets[name] = target.get(); |
| if(global) |
| { |
| this->LocalGenerator->GetGlobalGenerator()->AddTarget(target.get()); |
| } |
| |
| // Transfer ownership to this cmMakefile object. |
| this->ImportedTargetsOwned.push_back(target.get()); |
| return target.release(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmTarget* cmMakefile::FindTargetToUse(const std::string& name, |
| bool excludeAliases) const |
| { |
| // Look for an imported target. These take priority because they |
| // are more local in scope and do not have to be globally unique. |
| TargetMap::const_iterator |
| imported = this->ImportedTargets.find(name); |
| if(imported != this->ImportedTargets.end()) |
| { |
| return imported->second; |
| } |
| |
| // Look for a target built in this directory. |
| if(cmTarget* t = this->FindTarget(name, excludeAliases)) |
| { |
| return t; |
| } |
| |
| // Look for a target built in this project. |
| return this->LocalGenerator->GetGlobalGenerator()->FindTarget(name, |
| excludeAliases); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::IsAlias(const std::string& name) const |
| { |
| if (this->AliasTargets.find(name) != this->AliasTargets.end()) |
| return true; |
| return this->GetLocalGenerator()->GetGlobalGenerator()->IsAlias( |
| name); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmGeneratorTarget* |
| cmMakefile::FindGeneratorTargetToUse(const std::string& name) const |
| { |
| if (cmTarget *t = this->FindTargetToUse(name)) |
| { |
| return this->LocalGenerator->GetGlobalGenerator()->GetGeneratorTarget(t); |
| } |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg, |
| bool isCustom) const |
| { |
| if(this->IsAlias(name)) |
| { |
| cmOStringStream e; |
| e << "cannot create target \"" << name |
| << "\" because an alias with the same name already exists."; |
| msg = e.str(); |
| return false; |
| } |
| if(cmTarget* existing = this->FindTargetToUse(name)) |
| { |
| // The name given conflicts with an existing target. Produce an |
| // error in a compatible way. |
| if(existing->IsImported()) |
| { |
| // Imported targets were not supported in previous versions. |
| // This is new code, so we can make it an error. |
| cmOStringStream e; |
| e << "cannot create target \"" << name |
| << "\" because an imported target with the same name already exists."; |
| msg = e.str(); |
| return false; |
| } |
| else |
| { |
| // target names must be globally unique |
| switch (this->GetPolicyStatus(cmPolicies::CMP0002)) |
| { |
| case cmPolicies::WARN: |
| this->IssueMessage(cmake::AUTHOR_WARNING, this->GetPolicies()-> |
| GetPolicyWarning(cmPolicies::CMP0002)); |
| case cmPolicies::OLD: |
| return true; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| this->IssueMessage(cmake::FATAL_ERROR, |
| this->GetPolicies()->GetRequiredPolicyError(cmPolicies::CMP0002) |
| ); |
| return true; |
| case cmPolicies::NEW: |
| break; |
| } |
| |
| // The conflict is with a non-imported target. |
| // Allow this if the user has requested support. |
| cmake* cm = |
| this->LocalGenerator->GetGlobalGenerator()->GetCMakeInstance(); |
| if(isCustom && existing->GetType() == cmTarget::UTILITY && |
| this != existing->GetMakefile() && |
| cm->GetPropertyAsBool("ALLOW_DUPLICATE_CUSTOM_TARGETS")) |
| { |
| return true; |
| } |
| |
| // Produce an error that tells the user how to work around the |
| // problem. |
| cmOStringStream e; |
| e << "cannot create target \"" << name |
| << "\" because another target with the same name already exists. " |
| << "The existing target is "; |
| switch(existing->GetType()) |
| { |
| case cmTarget::EXECUTABLE: |
| e << "an executable "; |
| break; |
| case cmTarget::STATIC_LIBRARY: |
| e << "a static library "; |
| break; |
| case cmTarget::SHARED_LIBRARY: |
| e << "a shared library "; |
| break; |
| case cmTarget::MODULE_LIBRARY: |
| e << "a module library "; |
| break; |
| case cmTarget::UTILITY: |
| e << "a custom target "; |
| break; |
| case cmTarget::INTERFACE_LIBRARY: |
| e << "an interface library "; |
| break; |
| default: break; |
| } |
| e << "created in source directory \"" |
| << existing->GetMakefile()->GetCurrentDirectory() << "\". " |
| << "See documentation for policy CMP0002 for more details."; |
| msg = e.str(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::EnforceUniqueDir(const std::string& srcPath, |
| const std::string& binPath) const |
| { |
| // Make sure the binary directory is unique. |
| cmGlobalGenerator* gg = this->LocalGenerator->GetGlobalGenerator(); |
| if(gg->BinaryDirectoryIsNew(binPath)) |
| { |
| return true; |
| } |
| cmOStringStream e; |
| switch (this->GetPolicyStatus(cmPolicies::CMP0013)) |
| { |
| case cmPolicies::WARN: |
| // Print the warning. |
| e << this->GetPolicies()->GetPolicyWarning(cmPolicies::CMP0013) |
| << "\n" |
| << "The binary directory\n" |
| << " " << binPath << "\n" |
| << "is already used to build a source directory. " |
| << "This command uses it to build source directory\n" |
| << " " << srcPath << "\n" |
| << "which can generate conflicting build files. " |
| << "CMake does not support this use case but it used " |
| << "to work accidentally and is being allowed for " |
| << "compatibility."; |
| this->IssueMessage(cmake::AUTHOR_WARNING, e.str()); |
| case cmPolicies::OLD: |
| // OLD behavior does not warn. |
| return true; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| e << this->GetPolicies()->GetRequiredPolicyError(cmPolicies::CMP0013) |
| << "\n"; |
| case cmPolicies::NEW: |
| // NEW behavior prints the error. |
| e << "The binary directory\n" |
| << " " << binPath << "\n" |
| << "is already used to build a source directory. " |
| << "It cannot be used to build source directory\n" |
| << " " << srcPath << "\n" |
| << "Specify a unique binary directory name."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| break; |
| } |
| |
| return false; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::AddQtUiFileWithOptions(cmSourceFile *sf) |
| { |
| this->QtUiFilesWithOptions.push_back(sf); |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::vector<cmSourceFile*> cmMakefile::GetQtUiFilesWithOptions() const |
| { |
| return this->QtUiFilesWithOptions; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmPolicies::PolicyStatus |
| cmMakefile::GetPolicyStatus(cmPolicies::PolicyID id) const |
| { |
| // Get the current setting of the policy. |
| cmPolicies::PolicyStatus cur = this->GetPolicyStatusInternal(id); |
| |
| // If the policy is required to be set to NEW but is not, ignore the |
| // current setting and tell the caller. |
| if(cur != cmPolicies::NEW) |
| { |
| if(cur == cmPolicies::REQUIRED_ALWAYS || |
| cur == cmPolicies::REQUIRED_IF_USED) |
| { |
| return cur; |
| } |
| cmPolicies::PolicyStatus def = this->GetPolicies()->GetPolicyStatus(id); |
| if(def == cmPolicies::REQUIRED_ALWAYS || |
| def == cmPolicies::REQUIRED_IF_USED) |
| { |
| return def; |
| } |
| } |
| |
| // The current setting is okay. |
| return cur; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmPolicies::PolicyStatus |
| cmMakefile::GetPolicyStatusInternal(cmPolicies::PolicyID id) const |
| { |
| // Is the policy set in our stack? |
| for(PolicyStackType::const_reverse_iterator psi = this->PolicyStack.rbegin(); |
| psi != this->PolicyStack.rend(); ++psi) |
| { |
| PolicyStackEntry::const_iterator pse = psi->find(id); |
| if(pse != psi->end()) |
| { |
| return pse->second; |
| } |
| } |
| |
| // If we have a parent directory, recurse up to it. |
| if(this->LocalGenerator->GetParent()) |
| { |
| cmMakefile* parent = this->LocalGenerator->GetParent()->GetMakefile(); |
| return parent->GetPolicyStatusInternal(id); |
| } |
| |
| // The policy is not set. Use the default for this CMake version. |
| return this->GetPolicies()->GetPolicyStatus(id); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) |
| { |
| // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting. |
| if(!var.empty()) |
| { |
| if(const char* val = this->GetDefinition(var)) |
| { |
| return cmSystemTools::IsOn(val); |
| } |
| } |
| // Enable optional policy warnings with --debug-output or --trace. |
| cmake* cm = this->GetCMakeInstance(); |
| return cm->GetDebugOutput() || cm->GetTrace(); |
| } |
| |
| bool cmMakefile::SetPolicy(const char *id, |
| cmPolicies::PolicyStatus status) |
| { |
| cmPolicies::PolicyID pid; |
| if (!this->GetPolicies()->GetPolicyID(id, /* out */ pid)) |
| { |
| cmOStringStream e; |
| e << "Policy \"" << id << "\" is not known to this version of CMake."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return false; |
| } |
| return this->SetPolicy(pid,status); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, |
| cmPolicies::PolicyStatus status) |
| { |
| // A REQUIRED_ALWAYS policy may be set only to NEW. |
| if(status != cmPolicies::NEW && |
| this->GetPolicies()->GetPolicyStatus(id) == |
| cmPolicies::REQUIRED_ALWAYS) |
| { |
| std::string msg = |
| this->GetPolicies()->GetRequiredAlwaysPolicyError(id); |
| this->IssueMessage(cmake::FATAL_ERROR, msg); |
| return false; |
| } |
| |
| // Update the policy stack from the top to the top-most strong entry. |
| bool previous_was_weak = true; |
| for(PolicyStackType::reverse_iterator psi = this->PolicyStack.rbegin(); |
| previous_was_weak && psi != this->PolicyStack.rend(); ++psi) |
| { |
| (*psi)[id] = status; |
| previous_was_weak = psi->Weak; |
| } |
| |
| // Special hook for presenting compatibility variable as soon as |
| // the user requests it. |
| if(id == cmPolicies::CMP0001 && |
| (status == cmPolicies::WARN || status == cmPolicies::OLD)) |
| { |
| if(!(this->GetCacheManager() |
| ->GetCacheValue("CMAKE_BACKWARDS_COMPATIBILITY"))) |
| { |
| // Set it to 2.4 because that is the last version where the |
| // variable had meaning. |
| this->AddCacheDefinition |
| ("CMAKE_BACKWARDS_COMPATIBILITY", "2.4", |
| "For backwards compatibility, what version of CMake " |
| "commands and " |
| "syntax should this version of CMake try to support.", |
| cmCacheManager::STRING); |
| } |
| } |
| |
| return true; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmMakefile::PolicyPushPop::PolicyPushPop(cmMakefile* m, bool weak, |
| cmPolicies::PolicyMap const& pm): |
| Makefile(m), ReportError(true) |
| { |
| this->Makefile->PushPolicy(weak, pm); |
| this->Makefile->PushPolicyBarrier(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmMakefile::PolicyPushPop::~PolicyPushPop() |
| { |
| this->Makefile->PopPolicyBarrier(this->ReportError); |
| this->Makefile->PopPolicy(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::PushPolicy(bool weak, cmPolicies::PolicyMap const& pm) |
| { |
| // Allocate a new stack entry. |
| this->PolicyStack.push_back(PolicyStackEntry(pm, weak)); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::PopPolicy() |
| { |
| if(this->PolicyStack.size() > this->PolicyBarriers.back()) |
| { |
| this->PolicyStack.pop_back(); |
| } |
| else |
| { |
| this->IssueMessage(cmake::FATAL_ERROR, |
| "cmake_policy POP without matching PUSH"); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::PushPolicyBarrier() |
| { |
| this->PolicyBarriers.push_back(this->PolicyStack.size()); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::PopPolicyBarrier(bool reportError) |
| { |
| // Remove any extra entries pushed on the barrier. |
| PolicyStackType::size_type barrier = this->PolicyBarriers.back(); |
| while(this->PolicyStack.size() > barrier) |
| { |
| if(reportError) |
| { |
| this->IssueMessage(cmake::FATAL_ERROR, |
| "cmake_policy PUSH without matching POP"); |
| reportError = false; |
| } |
| this->PopPolicy(); |
| } |
| |
| // Remove the barrier. |
| this->PolicyBarriers.pop_back(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::SetPolicyVersion(const char *version) |
| { |
| return this->GetCMakeInstance()->GetPolicies()-> |
| ApplyPolicyVersion(this,version); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmPolicies *cmMakefile::GetPolicies() const |
| { |
| if (!this->GetCMakeInstance()) |
| { |
| return 0; |
| } |
| return this->GetCMakeInstance()->GetPolicies(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::HasCMP0054AlreadyBeenReported( |
| cmListFileContext context) const |
| { |
| cmCMP0054Id id(context); |
| |
| bool alreadyReported = |
| this->CMP0054ReportedIds.find(id) != this->CMP0054ReportedIds.end(); |
| |
| if(!alreadyReported) |
| { |
| this->CMP0054ReportedIds.insert(id); |
| } |
| |
| return alreadyReported; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) |
| { |
| /* Record the setting of every policy. */ |
| typedef cmPolicies::PolicyID PolicyID; |
| for(PolicyID pid = cmPolicies::CMP0000; |
| pid != cmPolicies::CMPCOUNT; pid = PolicyID(pid+1)) |
| { |
| pm[pid] = this->GetPolicyStatus(pid); |
| } |
| } |
| |
| #define FEATURE_STRING(F) , #F |
| static const char * const C_FEATURES[] = { |
| 0 |
| FOR_EACH_C_FEATURE(FEATURE_STRING) |
| }; |
| |
| static const char * const CXX_FEATURES[] = { |
| 0 |
| FOR_EACH_CXX_FEATURE(FEATURE_STRING) |
| }; |
| #undef FEATURE_STRING |
| |
| static const char * const C_STANDARDS[] = { |
| "90" |
| , "99" |
| , "11" |
| }; |
| static const char * const CXX_STANDARDS[] = { |
| "98" |
| , "11" |
| , "14" |
| }; |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile:: |
| AddRequiredTargetFeature(cmTarget *target, const std::string& feature, |
| std::string *error) const |
| { |
| if (cmGeneratorExpression::Find(feature) != std::string::npos) |
| { |
| target->AppendProperty("COMPILE_FEATURES", feature.c_str()); |
| return true; |
| } |
| |
| std::string lang; |
| if (!this->CompileFeatureKnown(target, feature, lang, error)) |
| { |
| return false; |
| } |
| |
| const char* features = this->CompileFeaturesAvailable(lang, error); |
| if (!features) |
| { |
| return false; |
| } |
| |
| std::vector<std::string> availableFeatures; |
| cmSystemTools::ExpandListArgument(features, availableFeatures); |
| if (std::find(availableFeatures.begin(), |
| availableFeatures.end(), |
| feature) == availableFeatures.end()) |
| { |
| cmOStringStream e; |
| e << "The compiler feature \"" << feature |
| << "\" is not known to " << lang << " compiler\n\"" |
| << this->GetDefinition("CMAKE_" + lang + "_COMPILER_ID") |
| << "\"\nversion " |
| << this->GetDefinition("CMAKE_" + lang + "_COMPILER_VERSION") << "."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return false; |
| } |
| |
| target->AppendProperty("COMPILE_FEATURES", feature.c_str()); |
| |
| return lang == "C" |
| ? this->AddRequiredTargetCFeature(target, feature) |
| : this->AddRequiredTargetCxxFeature(target, feature); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile:: |
| CompileFeatureKnown(cmTarget const* target, const std::string& feature, |
| std::string& lang, std::string *error) const |
| { |
| assert(cmGeneratorExpression::Find(feature) == std::string::npos); |
| |
| bool isCFeature = std::find_if(cmArrayBegin(C_FEATURES) + 1, |
| cmArrayEnd(C_FEATURES), cmStrCmp(feature)) |
| != cmArrayEnd(C_FEATURES); |
| if (isCFeature) |
| { |
| lang = "C"; |
| return true; |
| } |
| bool isCxxFeature = std::find_if(cmArrayBegin(CXX_FEATURES) + 1, |
| cmArrayEnd(CXX_FEATURES), cmStrCmp(feature)) |
| != cmArrayEnd(CXX_FEATURES); |
| if (isCxxFeature) |
| { |
| lang = "CXX"; |
| return true; |
| } |
| cmOStringStream e; |
| if (error) |
| { |
| e << "specified"; |
| } |
| else |
| { |
| e << "Specified"; |
| } |
| e << " unknown feature \"" << feature << "\" for " |
| "target \"" << target->GetName() << "\"."; |
| if (error) |
| { |
| *error = e.str(); |
| } |
| else |
| { |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| } |
| return false; |
| } |
| |
| //---------------------------------------------------------------------------- |
| const char* cmMakefile:: |
| CompileFeaturesAvailable(const std::string& lang, std::string *error) const |
| { |
| const char* featuresKnown = |
| this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES"); |
| |
| if (!featuresKnown || !*featuresKnown) |
| { |
| cmOStringStream e; |
| if (error) |
| { |
| e << "no"; |
| } |
| else |
| { |
| e << "No"; |
| } |
| e << " known features for " << lang << " compiler\n\"" |
| << this->GetDefinition("CMAKE_" + lang + "_COMPILER_ID") |
| << "\"\nversion " |
| << this->GetDefinition("CMAKE_" + lang + "_COMPILER_VERSION") << "."; |
| if (error) |
| { |
| *error = e.str(); |
| } |
| else |
| { |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| } |
| return 0; |
| } |
| return featuresKnown; |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::HaveStandardAvailable(cmTarget const* target, |
| std::string const& lang, |
| const std::string& feature) const |
| { |
| return lang == "C" |
| ? this->HaveCStandardAvailable(target, feature) |
| : this->HaveCxxStandardAvailable(target, feature); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile:: |
| HaveCStandardAvailable(cmTarget const* target, |
| const std::string& feature) const |
| { |
| bool needC90 = false; |
| bool needC99 = false; |
| bool needC11 = false; |
| |
| this->CheckNeededCLanguage(feature, needC90, needC99, needC11); |
| |
| const char *existingCStandard = target->GetProperty("C_STANDARD"); |
| if (!existingCStandard) |
| { |
| existingCStandard = this->GetDefinition("CMAKE_C_STANDARD_DEFAULT"); |
| } |
| |
| if (std::find_if(cmArrayBegin(C_STANDARDS), cmArrayEnd(C_STANDARDS), |
| cmStrCmp(existingCStandard)) == cmArrayEnd(C_STANDARDS)) |
| { |
| cmOStringStream e; |
| e << "The C_STANDARD property on target \"" << target->GetName() |
| << "\" contained an invalid value: \"" << existingCStandard << "\"."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str().c_str()); |
| return false; |
| } |
| |
| const char * const *existingCIt = existingCStandard |
| ? std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp(existingCStandard)) |
| : cmArrayEnd(C_STANDARDS); |
| |
| if (needC11 && existingCStandard && existingCIt < |
| std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp("11"))) |
| { |
| return false; |
| } |
| else if(needC99 && existingCStandard && existingCIt < |
| std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp("99"))) |
| { |
| return false; |
| } |
| else if(needC90 && existingCStandard && existingCIt < |
| std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp("90"))) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::IsLaterStandard(std::string const& lang, |
| std::string const& lhs, |
| std::string const& rhs) |
| { |
| if (lang == "C") |
| { |
| const char * const *rhsIt = std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp(rhs)); |
| |
| return std::find_if(rhsIt, cmArrayEnd(C_STANDARDS), |
| cmStrCmp(lhs)) != cmArrayEnd(C_STANDARDS); |
| } |
| const char * const *rhsIt = std::find_if(cmArrayBegin(CXX_STANDARDS), |
| cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp(rhs)); |
| |
| return std::find_if(rhsIt, cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp(lhs)) != cmArrayEnd(CXX_STANDARDS); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, |
| const std::string& feature) const |
| { |
| bool needCxx98 = false; |
| bool needCxx11 = false; |
| bool needCxx14 = false; |
| this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14); |
| |
| const char *existingCxxStandard = target->GetProperty("CXX_STANDARD"); |
| if (!existingCxxStandard) |
| { |
| existingCxxStandard = this->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT"); |
| } |
| |
| if (std::find_if(cmArrayBegin(CXX_STANDARDS), cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp(existingCxxStandard)) == cmArrayEnd(CXX_STANDARDS)) |
| { |
| cmOStringStream e; |
| e << "The CXX_STANDARD property on target \"" << target->GetName() |
| << "\" contained an invalid value: \"" << existingCxxStandard << "\"."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return false; |
| } |
| |
| const char * const *existingCxxIt = existingCxxStandard |
| ? std::find_if(cmArrayBegin(CXX_STANDARDS), |
| cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp(existingCxxStandard)) |
| : cmArrayEnd(CXX_STANDARDS); |
| |
| if (needCxx11 && existingCxxIt < std::find_if(cmArrayBegin(CXX_STANDARDS), |
| cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp("11"))) |
| { |
| return false; |
| } |
| else if(needCxx98 && existingCxxIt < |
| std::find_if(cmArrayBegin(CXX_STANDARDS), |
| cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp("98"))) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::CheckNeededCxxLanguage(const std::string& feature, |
| bool& needCxx98, |
| bool& needCxx11, |
| bool& needCxx14) const |
| { |
| if (const char *propCxx98 = |
| this->GetDefinition("CMAKE_CXX98_COMPILE_FEATURES")) |
| { |
| std::vector<std::string> props; |
| cmSystemTools::ExpandListArgument(propCxx98, props); |
| needCxx98 = std::find(props.begin(), props.end(), feature) != props.end(); |
| } |
| if (const char *propCxx11 = |
| this->GetDefinition("CMAKE_CXX11_COMPILE_FEATURES")) |
| { |
| std::vector<std::string> props; |
| cmSystemTools::ExpandListArgument(propCxx11, props); |
| needCxx11 = std::find(props.begin(), props.end(), feature) != props.end(); |
| } |
| if (const char *propCxx14 = |
| this->GetDefinition("CMAKE_CXX14_COMPILE_FEATURES")) |
| { |
| std::vector<std::string> props; |
| cmSystemTools::ExpandListArgument(propCxx14, props); |
| needCxx14 = std::find(props.begin(), props.end(), feature) != props.end(); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile:: |
| AddRequiredTargetCxxFeature(cmTarget *target, |
| const std::string& feature) const |
| { |
| bool needCxx98 = false; |
| bool needCxx11 = false; |
| bool needCxx14 = false; |
| |
| this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14); |
| |
| const char *existingCxxStandard = target->GetProperty("CXX_STANDARD"); |
| if (existingCxxStandard) |
| { |
| if (std::find_if(cmArrayBegin(CXX_STANDARDS), cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp(existingCxxStandard)) == cmArrayEnd(CXX_STANDARDS)) |
| { |
| cmOStringStream e; |
| e << "The CXX_STANDARD property on target \"" << target->GetName() |
| << "\" contained an invalid value: \"" << existingCxxStandard << "\"."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return false; |
| } |
| } |
| const char * const *existingCxxIt = existingCxxStandard |
| ? std::find_if(cmArrayBegin(CXX_STANDARDS), |
| cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp(existingCxxStandard)) |
| : cmArrayEnd(CXX_STANDARDS); |
| |
| bool setCxx98 = needCxx98 && !existingCxxStandard; |
| bool setCxx11 = needCxx11 && !existingCxxStandard; |
| bool setCxx14 = needCxx14 && !existingCxxStandard; |
| |
| if (needCxx14 && existingCxxStandard && existingCxxIt < |
| std::find_if(cmArrayBegin(CXX_STANDARDS), |
| cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp("14"))) |
| { |
| setCxx14 = true; |
| } |
| else if (needCxx11 && existingCxxStandard && existingCxxIt < |
| std::find_if(cmArrayBegin(CXX_STANDARDS), |
| cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp("11"))) |
| { |
| setCxx11 = true; |
| } |
| else if(needCxx98 && existingCxxStandard && existingCxxIt < |
| std::find_if(cmArrayBegin(CXX_STANDARDS), |
| cmArrayEnd(CXX_STANDARDS), |
| cmStrCmp("98"))) |
| { |
| setCxx98 = true; |
| } |
| |
| if (setCxx14) |
| { |
| target->SetProperty("CXX_STANDARD", "14"); |
| } |
| else if (setCxx11) |
| { |
| target->SetProperty("CXX_STANDARD", "11"); |
| } |
| else if (setCxx98) |
| { |
| target->SetProperty("CXX_STANDARD", "98"); |
| } |
| return true; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmMakefile::CheckNeededCLanguage(const std::string& feature, |
| bool& needC90, |
| bool& needC99, |
| bool& needC11) const |
| { |
| if (const char *propC90 = |
| this->GetDefinition("CMAKE_C90_COMPILE_FEATURES")) |
| { |
| std::vector<std::string> props; |
| cmSystemTools::ExpandListArgument(propC90, props); |
| needC90 = std::find(props.begin(), props.end(), feature) != props.end(); |
| } |
| if (const char *propC99 = |
| this->GetDefinition("CMAKE_C99_COMPILE_FEATURES")) |
| { |
| std::vector<std::string> props; |
| cmSystemTools::ExpandListArgument(propC99, props); |
| needC99 = std::find(props.begin(), props.end(), feature) != props.end(); |
| } |
| if (const char *propC11 = |
| this->GetDefinition("CMAKE_C11_COMPILE_FEATURES")) |
| { |
| std::vector<std::string> props; |
| cmSystemTools::ExpandListArgument(propC11, props); |
| needC11 = std::find(props.begin(), props.end(), feature) != props.end(); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmMakefile:: |
| AddRequiredTargetCFeature(cmTarget *target, const std::string& feature) const |
| { |
| bool needC90 = false; |
| bool needC99 = false; |
| bool needC11 = false; |
| |
| this->CheckNeededCLanguage(feature, needC90, needC99, needC11); |
| |
| const char *existingCStandard = target->GetProperty("C_STANDARD"); |
| if (existingCStandard) |
| { |
| if (std::find_if(cmArrayBegin(C_STANDARDS), cmArrayEnd(C_STANDARDS), |
| cmStrCmp(existingCStandard)) == cmArrayEnd(C_STANDARDS)) |
| { |
| cmOStringStream e; |
| e << "The C_STANDARD property on target \"" << target->GetName() |
| << "\" contained an invalid value: \"" << existingCStandard << "\"."; |
| this->IssueMessage(cmake::FATAL_ERROR, e.str().c_str()); |
| return false; |
| } |
| } |
| const char * const *existingCIt = existingCStandard |
| ? std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp(existingCStandard)) |
| : cmArrayEnd(C_STANDARDS); |
| |
| bool setC90 = needC90 && !existingCStandard; |
| bool setC99 = needC99 && !existingCStandard; |
| bool setC11 = needC11 && !existingCStandard; |
| |
| if (needC11 && existingCStandard && existingCIt < |
| std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp("11"))) |
| { |
| setC11 = true; |
| } |
| else if(needC99 && existingCStandard && existingCIt < |
| std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp("99"))) |
| { |
| setC99 = true; |
| } |
| else if(needC90 && existingCStandard && existingCIt < |
| std::find_if(cmArrayBegin(C_STANDARDS), |
| cmArrayEnd(C_STANDARDS), |
| cmStrCmp("90"))) |
| { |
| setC90 = true; |
| } |
| |
| if (setC11) |
| { |
| target->SetProperty("C_STANDARD", "11"); |
| } |
| else if (setC99) |
| { |
| target->SetProperty("C_STANDARD", "99"); |
| } |
| else if (setC90) |
| { |
| target->SetProperty("C_STANDARD", "90"); |
| } |
| return true; |
| } |