diff --git a/Source/cmCommandArgumentParserHelper.cxx b/Source/cmCommandArgumentParserHelper.cxx index d446e15ee..cae9256e3 100644 --- a/Source/cmCommandArgumentParserHelper.cxx +++ b/Source/cmCommandArgumentParserHelper.cxx @@ -27,7 +27,7 @@ cmCommandArgumentParserHelper::cmCommandArgumentParserHelper() { this->FileLine = -1; this->FileName = 0; - + this->RemoveEmpty = true; this->EmptyVariable[0] = 0; strcpy(this->DCURLYVariable, "${"); strcpy(this->RCURLYVariable, "}"); @@ -38,6 +38,7 @@ cmCommandArgumentParserHelper::cmCommandArgumentParserHelper() this->NoEscapeMode = false; this->ReplaceAtSyntax = false; + this->AtOnly = false; } @@ -70,6 +71,18 @@ char* cmCommandArgumentParserHelper::ExpandSpecialVariable(const char* key, if ( !key ) { return this->ExpandVariable(var); + } + if(this->AtOnly) + { + std::string ref = "$"; + ref += key; + ref += "{"; + if(var) + { + ref += var; + } + ref += "}"; + return this->AddString(ref.c_str()); } if ( strcmp(key, "ENV") == 0 ) { @@ -92,8 +105,21 @@ char* cmCommandArgumentParserHelper::ExpandSpecialVariable(const char* key, return 0; } -char* cmCommandArgumentParserHelper::ExpandVariable(const char* var) +char* cmCommandArgumentParserHelper::ExpandVariable(const char* var, + bool doingAt) { + // if we are in AtOnly mode, and we are not expanding an @ variable + // then put back the ${var} unexpanded + if(!doingAt && this->AtOnly) + { + std::string ref = "${"; + if(var) + { + ref += var; + } + ref += "}"; + return this->AddString(ref.c_str()); + } if(!var) { return 0; @@ -109,6 +135,10 @@ char* cmCommandArgumentParserHelper::ExpandVariable(const char* var) return this->AddString(ostr.str().c_str()); } const char* value = this->Makefile->GetDefinition(var); + if(!value && !this->RemoveEmpty) + { + return 0; + } if (this->EscapeQuotes && value) { return this->AddString(cmSystemTools::EscapeQuotes(value).c_str()); @@ -120,15 +150,28 @@ char* cmCommandArgumentParserHelper::ExpandVariableForAt(const char* var) { if(this->ReplaceAtSyntax) { - return this->ExpandVariable(var); - } - else - { - std::string ref = "@"; - ref += var; - ref += "@"; - return this->AddString(ref.c_str()); + // try to expand the variable + char* ret = this->ExpandVariable(var, true); + // if the return was 0 and we want to replace empty strings + // then return an empty string + if(!ret && this->RemoveEmpty) + { + return this->AddString(ret); + } + // if the ret was not 0, then return it + if(ret) + { + return ret; + } } + // at this point we want to put it back because of one of these cases: + // - this->ReplaceAtSyntax is false + // - this->ReplaceAtSyntax is true, but this->RemoveEmpty is false, + // and the variable was not defined + std::string ref = "@"; + ref += var; + ref += "@"; + return this->AddString(ref.c_str()); } char* cmCommandArgumentParserHelper::CombineUnions(char* in1, char* in2) diff --git a/Source/cmCommandArgumentParserHelper.h b/Source/cmCommandArgumentParserHelper.h index 7f5078f53..d49952aac 100644 --- a/Source/cmCommandArgumentParserHelper.h +++ b/Source/cmCommandArgumentParserHelper.h @@ -58,7 +58,7 @@ public: char* CombineUnions(char* in1, char* in2); char* ExpandSpecialVariable(const char* key, const char* var); - char* ExpandVariable(const char* var); + char* ExpandVariable(const char* var, bool doingAt=false); char* ExpandVariableForAt(const char* var); void SetResult(const char* value); @@ -70,7 +70,9 @@ public: void SetEscapeQuotes(bool b) { this->EscapeQuotes = b; } void SetNoEscapeMode(bool b) { this->NoEscapeMode = b; } void SetReplaceAtSyntax(bool b) { this->ReplaceAtSyntax = b; } - + void SetRemoveEmpty(bool b) { this->RemoveEmpty = b; } + void SetAtOnly(bool b) { this->AtOnly = b; } + const char* GetError() { return this->ErrorString.c_str(); } char EmptyVariable[1]; char DCURLYVariable[3]; @@ -104,6 +106,8 @@ private: std::string ErrorString; bool NoEscapeMode; bool ReplaceAtSyntax; + bool RemoveEmpty; + bool AtOnly; }; #endif diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index eb16809b9..60929efc3 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -1684,214 +1684,45 @@ const char *cmMakefile::ExpandVariablesInString(std::string& source, // It also supports the $ENV{VAR} syntax where VAR is looked up in // the current environment variables. - bool notParsed = true; - if ( !atOnly ) + cmCommandArgumentParserHelper parser; + parser.SetMakefile(this); + parser.SetLineFile(line, filename); + parser.SetEscapeQuotes(escapeQuotes); + parser.SetNoEscapeMode(noEscapes); + parser.SetReplaceAtSyntax(replaceAt); + parser.SetRemoveEmpty(removeEmpty); + parser.SetAtOnly(atOnly); + int res = parser.ParseString(source.c_str(), 0); + if ( res ) { - cmCommandArgumentParserHelper parser; - parser.SetMakefile(this); - parser.SetLineFile(line, filename); - parser.SetEscapeQuotes(escapeQuotes); - parser.SetNoEscapeMode(noEscapes); - parser.SetReplaceAtSyntax(replaceAt); - int res = parser.ParseString(source.c_str(), 0); - if ( res ) - { - source = parser.GetResult(); - notParsed = false; - } - else - { - cmOStringStream error; - error << "Syntax error in cmake code at\n" - << (filename?filename:"(no filename given)") - << ":" << line << ":\n" - << parser.GetError() << ", when parsing string \"" - << source.c_str() << "\""; - const char* versionValue - = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); - int major = 0; - int minor = 0; - if ( versionValue ) - { - sscanf(versionValue, "%d.%d", &major, &minor); - } - if ( major < 2 || major == 2 && minor < 1 ) - { - cmSystemTools::Error(error.str().c_str()); - cmSystemTools::SetFatalErrorOccured(); - return source.c_str(); - } - else - { - cmSystemTools::Message(error.str().c_str()); - } - } + source = parser.GetResult(); } - - if ( notParsed ) + else { - - // start by look for $ or @ in the string - std::string::size_type markerPos; - if(atOnly) + cmOStringStream error; + error << "Syntax error in cmake code at\n" + << (filename?filename:"(no filename given)") + << ":" << line << ":\n" + << parser.GetError() << ", when parsing string \"" + << source.c_str() << "\""; + const char* versionValue + = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); + int major = 0; + int minor = 0; + if ( versionValue ) { - markerPos = source.find_first_of("@"); + sscanf(versionValue, "%d.%d", &major, &minor); } - else - { - markerPos = source.find_first_of("$@"); - } - // if not found, or found as the last character, then leave quickly as - // nothing needs to be expanded - if((markerPos == std::string::npos) || (markerPos >= source.size()-1)) + if ( major < 2 || major == 2 && minor < 1 ) { + cmSystemTools::Error(error.str().c_str()); + cmSystemTools::SetFatalErrorOccured(); return source.c_str(); } - // current position - std::string::size_type currentPos =0; // start at 0 - std::string result; // string with replacements - // go until the the end of the string - while((markerPos != std::string::npos) && (markerPos < source.size()-1)) + else { - // grab string from currentPos to the start of the variable - // and add it to the result - result += source.substr(currentPos, markerPos - currentPos); - char endVariableMarker; // what is the end of the variable @ or } - int markerStartSize = 1; // size of the start marker 1 or 2 or 5 - if(!atOnly && source[markerPos] == '$') - { - // ${var} case - if(source[markerPos+1] == '{') - { - endVariableMarker = '}'; - markerStartSize = 2; - } - // $ENV{var} case - else if(markerPos+4 < source.size() && - source[markerPos+4] == '{' && - !source.substr(markerPos+1, 3).compare("ENV")) - { - endVariableMarker = '}'; - markerStartSize = 5; - } - else - { - // bogus $ with no { so add $ to result and move on - result += '$'; // add bogus $ back into string - currentPos = markerPos+1; // move on - // set end var to space so we can tell bogus - endVariableMarker = ' '; - } - } - else - { - // @VAR case - endVariableMarker = '@'; - } - // if it was a valid variable (started with @ or ${ or $ENV{ ) - if(endVariableMarker != ' ') - { - markerPos += markerStartSize; // move past marker - // find the end variable marker starting at the markerPos - // make sure it is a valid variable between - std::string::size_type endVariablePos = - source.find_first_not_of( - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", - markerPos); - if(endVariablePos != std::string::npos && - source[endVariablePos] != endVariableMarker) - { - endVariablePos = std::string::npos; - } - if(endVariablePos == std::string::npos) - { - // no end marker found so add the bogus start - if(endVariableMarker == '@') - { - result += '@'; - } - else - { - result += (markerStartSize == 5 ? "$ENV{" : "${"); - } - currentPos = markerPos; - } - else - { - // good variable remove it - std::string var = - source.substr(markerPos, endVariablePos - markerPos); - bool found = false; - if (markerStartSize == 5) // $ENV{ - { - char *ptr = getenv(var.c_str()); - if (ptr) - { - if (escapeQuotes) - { - result += cmSystemTools::EscapeQuotes(ptr); - } - else - { - result += ptr; - } - found = true; - } - } - else - { - const char* lookup = this->GetDefinition(var.c_str()); - if(lookup) - { - if (escapeQuotes) - { - result += cmSystemTools::EscapeQuotes(lookup); - } - else - { - result += lookup; - } - found = true; - } - else if(filename && (var == "CMAKE_CURRENT_LIST_FILE")) - { - result += filename; - found = true; - } - else if(line >= 0 && (var == "CMAKE_CURRENT_LIST_LINE")) - { - cmOStringStream ostr; - ostr << line; - result += ostr.str(); - found = true; - } - } - // if found add to result, if not, then it gets blanked - if (!found) - { - // if no definition is found then add the var back - if(!removeEmpty && endVariableMarker == '@') - { - result += "@"; - result += var; - result += "@"; - } - } - // lookup var, and replace it - currentPos = endVariablePos+1; - } - } - if(atOnly) - { - markerPos = source.find_first_of("@", currentPos); - } - else - { - markerPos = source.find_first_of("$@", currentPos); - } + cmSystemTools::Message(error.str().c_str()); } - result += source.substr(currentPos); // pick up the rest of the string - source = result; } return source.c_str(); }