/*============================================================================ 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 "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmTarget.h" #include "assert.h" #include #include "cmGeneratorExpressionEvaluator.h" #include "cmGeneratorExpressionLexer.h" #include "cmGeneratorExpressionParser.h" #include "cmGeneratorExpressionDAGChecker.h" //---------------------------------------------------------------------------- cmGeneratorExpression::cmGeneratorExpression( cmListFileBacktrace const* backtrace): Backtrace(backtrace) { } //---------------------------------------------------------------------------- cmsys::auto_ptr cmGeneratorExpression::Parse(std::string const& input) { return cmsys::auto_ptr( new cmCompiledGeneratorExpression( this->Backtrace ? *this->Backtrace : cmListFileBacktrace(NULL), input)); } //---------------------------------------------------------------------------- cmsys::auto_ptr cmGeneratorExpression::Parse(const char* input) { return this->Parse(std::string(input ? input : "")); } cmGeneratorExpression::~cmGeneratorExpression() { } //---------------------------------------------------------------------------- const char *cmCompiledGeneratorExpression::Evaluate( cmMakefile* mf, const std::string& config, bool quiet, cmTarget const* headTarget, cmGeneratorExpressionDAGChecker *dagChecker) const { return this->Evaluate(mf, config, quiet, headTarget, headTarget, dagChecker); } //---------------------------------------------------------------------------- const char *cmCompiledGeneratorExpression::Evaluate( cmMakefile* mf, const std::string& config, bool quiet, cmTarget const* headTarget, cmTarget const* currentTarget, cmGeneratorExpressionDAGChecker *dagChecker) const { if (!this->NeedsEvaluation) { return this->Input.c_str(); } this->Output = ""; std::vector::const_iterator it = this->Evaluators.begin(); const std::vector::const_iterator end = this->Evaluators.end(); cmGeneratorExpressionContext context; context.Makefile = mf; context.Config = config; context.Quiet = quiet; context.HadError = false; context.HadContextSensitiveCondition = false; context.HadHeadSensitiveCondition = false; context.SourceSensitiveTargets.clear(); context.HeadTarget = headTarget; context.EvaluateForBuildsystem = this->EvaluateForBuildsystem; context.CurrentTarget = currentTarget ? currentTarget : headTarget; context.Backtrace = this->Backtrace; for ( ; it != end; ++it) { this->Output += (*it)->Evaluate(&context, dagChecker); for(std::set::const_iterator p = context.SeenTargetProperties.begin(); p != context.SeenTargetProperties.end(); ++p) { this->SeenTargetProperties.insert(*p); } if (context.HadError) { this->Output = ""; break; } } this->MaxLanguageStandard = context.MaxLanguageStandard; if (!context.HadError) { this->HadContextSensitiveCondition = context.HadContextSensitiveCondition; this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition; this->SourceSensitiveTargets = context.SourceSensitiveTargets; } this->DependTargets = context.DependTargets; this->AllTargetsSeen = context.AllTargets; // TODO: Return a std::string from here instead? return this->Output.c_str(); } cmCompiledGeneratorExpression::cmCompiledGeneratorExpression( cmListFileBacktrace const& backtrace, const std::string& input) : Backtrace(backtrace), Input(input), HadContextSensitiveCondition(false), HadHeadSensitiveCondition(false), EvaluateForBuildsystem(false) { cmGeneratorExpressionLexer l; std::vector tokens = l.Tokenize(this->Input); this->NeedsEvaluation = l.GetSawGeneratorExpression(); if (this->NeedsEvaluation) { cmGeneratorExpressionParser p(tokens); p.Parse(this->Evaluators); } } //---------------------------------------------------------------------------- cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression() { std::vector::const_iterator it = this->Evaluators.begin(); const std::vector::const_iterator end = this->Evaluators.end(); for ( ; it != end; ++it) { delete *it; } } //---------------------------------------------------------------------------- std::string cmGeneratorExpression::StripEmptyListElements( const std::string &input) { if (input.find(';') == input.npos) { return input; } std::string result; result.reserve(input.size()); const char *c = input.c_str(); const char *last = c; bool skipSemiColons = true; for ( ; *c; ++c) { if(*c == ';') { if(skipSemiColons) { result.append(last, c - last); last = c + 1; } skipSemiColons = true; } else { skipSemiColons = false; } } result.append(last); if (!result.empty() && *(result.end() - 1) == ';') { result.resize(result.size() - 1); } return result; } //---------------------------------------------------------------------------- static std::string stripAllGeneratorExpressions(const std::string &input) { std::string result; std::string::size_type pos = 0; std::string::size_type lastPos = pos; int nestingLevel = 0; while((pos = input.find("$<", lastPos)) != input.npos) { result += input.substr(lastPos, pos - lastPos); pos += 2; nestingLevel = 1; const char *c = input.c_str() + pos; const char * const cStart = c; for ( ; *c; ++c) { if(c[0] == '$' && c[1] == '<') { ++nestingLevel; ++c; continue; } if(c[0] == '>') { --nestingLevel; if (nestingLevel == 0) { break; } } } const std::string::size_type traversed = (c - cStart) + 1; if (!*c) { result += "$<" + input.substr(pos, traversed); } pos += traversed; lastPos = pos; } if (nestingLevel == 0) { result += input.substr(lastPos); } return cmGeneratorExpression::StripEmptyListElements(result); } //---------------------------------------------------------------------------- static void prefixItems(const std::string &content, std::string &result, const std::string &prefix) { std::vector entries; cmGeneratorExpression::Split(content, entries); const char *sep = ""; for(std::vector::const_iterator ei = entries.begin(); ei != entries.end(); ++ei) { result += sep; sep = ";"; if (!cmSystemTools::FileIsFullPath(ei->c_str()) && cmGeneratorExpression::Find(*ei) != 0) { result += prefix; } result += *ei; } } //---------------------------------------------------------------------------- static std::string stripExportInterface(const std::string &input, cmGeneratorExpression::PreprocessContext context, bool resolveRelative) { std::string result; int nestingLevel = 0; std::string::size_type pos = 0; std::string::size_type lastPos = pos; while (true) { std::string::size_type bPos = input.find("$') { --nestingLevel; if (nestingLevel != 0) { continue; } if(context == cmGeneratorExpression::BuildInterface && !gotInstallInterface) { result += input.substr(pos, c - cStart); } else if(context == cmGeneratorExpression::InstallInterface && gotInstallInterface) { const std::string content = input.substr(pos, c - cStart); if (resolveRelative) { prefixItems(content, result, "${_IMPORT_PREFIX}/"); } else { result += content; } } break; } } const std::string::size_type traversed = (c - cStart) + 1; if (!*c) { result += std::string(gotInstallInterface ? "$ &output) { std::string::size_type pos = 0; std::string::size_type lastPos = pos; while((pos = input.find("$<", lastPos)) != input.npos) { std::string part = input.substr(lastPos, pos - lastPos); std::string preGenex; if (!part.empty()) { std::string::size_type startPos = input.rfind(";", pos); if (startPos == std::string::npos) { preGenex = part; part = ""; } else if (startPos != pos - 1 && startPos >= lastPos) { part = input.substr(lastPos, startPos - lastPos); preGenex = input.substr(startPos + 1, pos - startPos - 1); } if(!part.empty()) { cmSystemTools::ExpandListArgument(part, output); } } pos += 2; int nestingLevel = 1; const char *c = input.c_str() + pos; const char * const cStart = c; for ( ; *c; ++c) { if(c[0] == '$' && c[1] == '<') { ++nestingLevel; ++c; continue; } if(c[0] == '>') { --nestingLevel; if (nestingLevel == 0) { break; } } } for ( ; *c; ++c) { // Capture the part after the genex and before the next ';' if(c[0] == ';') { --c; break; } } const std::string::size_type traversed = (c - cStart) + 1; output.push_back(preGenex + "$<" + input.substr(pos, traversed)); pos += traversed; lastPos = pos; } if (lastPos < input.size()) { cmSystemTools::ExpandListArgument(input.substr(lastPos), output); } } //---------------------------------------------------------------------------- std::string cmGeneratorExpression::Preprocess(const std::string &input, PreprocessContext context, bool resolveRelative) { if (context == StripAllGeneratorExpressions) { return stripAllGeneratorExpressions(input); } else if (context == BuildInterface || context == InstallInterface) { return stripExportInterface(input, context, resolveRelative); } assert(0 && "cmGeneratorExpression::Preprocess called with invalid args"); return std::string(); } //---------------------------------------------------------------------------- std::string::size_type cmGeneratorExpression::Find(const std::string &input) { const std::string::size_type openpos = input.find("$<"); if (openpos != std::string::npos && input.find(">", openpos) != std::string::npos) { return openpos; } return std::string::npos; } //---------------------------------------------------------------------------- bool cmGeneratorExpression::IsValidTargetName(const std::string &input) { // The ':' is supported to allow use with IMPORTED targets. At least // Qt 4 and 5 IMPORTED targets use ':' as the namespace delimiter. static cmsys::RegularExpression targetNameValidator("^[A-Za-z0-9_.:+-]+$"); return targetNameValidator.find(input); } //---------------------------------------------------------------------------- void cmCompiledGeneratorExpression::GetMaxLanguageStandard(cmTarget const* tgt, std::map& mapping) { typedef std::map > MapType; MapType::const_iterator it = this->MaxLanguageStandard.find(tgt); if (it != this->MaxLanguageStandard.end()) { mapping = it->second; } }