summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2008-03-07 13:40:36 (GMT)
committerBrad King <brad.king@kitware.com>2008-03-07 13:40:36 (GMT)
commit680104a490250f7b56b67aa3cbb7c113d997e93c (patch)
tree52af6beb6a876f03a1205c050ad8295e338ca573
parent41a59e211ef6b0c9c839545661579934ce9415c1 (diff)
downloadCMake-680104a490250f7b56b67aa3cbb7c113d997e93c.zip
CMake-680104a490250f7b56b67aa3cbb7c113d997e93c.tar.gz
CMake-680104a490250f7b56b67aa3cbb7c113d997e93c.tar.bz2
ENH: New format for warning and error messages
- Add cmMakefile methods IssueError and IssueWarning - Maintain an explicit call stack in cmMakefile - Include context/call-stack info in messages - Nested errors now unwind the call stack - Use new mechanism for policy warnings and errors - Improve policy error message - Include cmExecutionStatus pointer in call stack so that errors deeper in the C++ stack under a command invocation will become errors for the command
-rw-r--r--Source/cmAddCustomTargetCommand.cxx14
-rw-r--r--Source/cmExecutionStatus.h9
-rw-r--r--Source/cmFunctionCommand.cxx18
-rw-r--r--Source/cmListFileCache.cxx12
-rw-r--r--Source/cmListFileCache.h8
-rw-r--r--Source/cmMacroCommand.cxx22
-rw-r--r--Source/cmMakefile.cxx212
-rw-r--r--Source/cmMakefile.h17
-rw-r--r--Source/cmPolicies.cxx35
9 files changed, 236 insertions, 111 deletions
diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx
index 241cdcc..07f00ea 100644
--- a/Source/cmAddCustomTargetCommand.cxx
+++ b/Source/cmAddCustomTargetCommand.cxx
@@ -18,7 +18,8 @@
// cmAddCustomTargetCommand
bool cmAddCustomTargetCommand
-::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
+::InitialPass(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
{
// This enum must be before an enum is used in a switch statment.
// If not there is an ICE on the itanium version of gcc we are running
@@ -45,9 +46,9 @@ bool cmAddCustomTargetCommand
switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP_0001))
{
case cmPolicies::WARN:
- cmSystemTools::Message(
+ this->Makefile->IssueWarning(
this->Makefile->GetPolicies()->GetPolicyWarning
- (cmPolicies::CMP_0001).c_str(),"Warning");
+ (cmPolicies::CMP_0001));
case cmPolicies::OLD:
// if (this->Makefile->IsBWCompatibilityLessThan(2,2))
// {
@@ -60,10 +61,11 @@ bool cmAddCustomTargetCommand
return false;
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
- this->SetError(
+ this->Makefile->IssueError(
this->Makefile->GetPolicies()->GetRequiredPolicyError
- (cmPolicies::CMP_0001).c_str());
- return false;
+ (cmPolicies::CMP_0001).c_str()
+ );
+ return false;
}
}
diff --git a/Source/cmExecutionStatus.h b/Source/cmExecutionStatus.h
index 66a567e..bc1a579 100644
--- a/Source/cmExecutionStatus.h
+++ b/Source/cmExecutionStatus.h
@@ -42,12 +42,19 @@ public:
{ return this->BreakInvoked; }
virtual void Clear()
- { this->ReturnInvoked = false; this->BreakInvoked = false; }
+ {
+ this->ReturnInvoked = false;
+ this->BreakInvoked = false;
+ this->NestedError = false;
+ }
+ virtual void SetNestedError(bool val) { this->NestedError = val; }
+ virtual bool GetNestedError() { return this->NestedError; }
protected:
bool ReturnInvoked;
bool BreakInvoked;
+ bool NestedError;
};
#endif
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx
index 7394709..8bad1a4 100644
--- a/Source/cmFunctionCommand.cxx
+++ b/Source/cmFunctionCommand.cxx
@@ -86,7 +86,7 @@ public:
bool cmFunctionHelperCommand::InvokeInitialPass
(const std::vector<cmListFileArgument>& args,
- cmExecutionStatus &)
+ cmExecutionStatus & inStatus)
{
// Expand the argument list to the function.
std::vector<std::string> expandedArgs;
@@ -157,18 +157,12 @@ bool cmFunctionHelperCommand::InvokeInitialPass
for(unsigned int c = 0; c < this->Functions.size(); ++c)
{
cmExecutionStatus status;
- if (!this->Makefile->ExecuteCommand(this->Functions[c],status))
+ if (!this->Makefile->ExecuteCommand(this->Functions[c],status) ||
+ status.GetNestedError())
{
- cmOStringStream error;
- error << "Error in cmake code at\n"
- << this->Functions[c].FilePath << ":"
- << this->Functions[c].Line << ":\n"
- << "A command failed during the invocation of function \""
- << this->Args[0].c_str() << "\".";
- cmSystemTools::Error(error.str().c_str());
-
- // pop scope on the makefile and return
- this->Makefile->PopScope();
+ // The error message should have already included the call stack
+ // so we do not need to report an error here.
+ inStatus.SetNestedError(true);
return false;
}
if (status.GetReturnInvoked())
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx
index d49797a..04b436e 100644
--- a/Source/cmListFileCache.cxx
+++ b/Source/cmListFileCache.cxx
@@ -152,15 +152,15 @@ bool cmListFile::ParseFile(const char* filename,
switch (mf->GetPolicyStatus(cmPolicies::CMP_0000))
{
case cmPolicies::WARN:
- cmSystemTools::Message(
- mf->GetPolicies()->GetPolicyWarning
- (cmPolicies::CMP_0000).c_str(),"Warning");
+ mf->IssueWarning(
+ mf->GetPolicies()->GetPolicyWarning(cmPolicies::CMP_0000)
+ );
case cmPolicies::OLD:
break;
default:
- cmSystemTools::Error(
- mf->GetPolicies()->GetRequiredPolicyError
- (cmPolicies::CMP_0000).c_str());
+ mf->IssueError(
+ mf->GetPolicies()->GetRequiredPolicyError(cmPolicies::CMP_0000)
+ );
return false;
}
}
diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h
index b1eaa81..18ec0df 100644
--- a/Source/cmListFileCache.h
+++ b/Source/cmListFileCache.h
@@ -50,14 +50,18 @@ struct cmListFileArgument
long Line;
};
-struct cmListFileFunction
+struct cmListFileContext
{
std::string Name;
- std::vector<cmListFileArgument> Arguments;
std::string FilePath;
long Line;
};
+struct cmListFileFunction: public cmListFileContext
+{
+ std::vector<cmListFileArgument> Arguments;
+};
+
struct cmListFile
{
cmListFile()
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index 9f1c904..7c4a644 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -237,24 +237,12 @@ bool cmMacroHelperCommand::InvokeInitialPass
newLFF.Arguments.push_back(arg);
}
cmExecutionStatus status;
- if(!this->Makefile->ExecuteCommand(newLFF,status))
+ if(!this->Makefile->ExecuteCommand(newLFF, status) ||
+ status.GetNestedError())
{
- if(args.size())
- {
- arg.FilePath = args[0].FilePath;
- arg.Line = args[0].Line;
- }
- else
- {
- arg.FilePath = "Unknown";
- arg.Line = 0;
- }
- cmOStringStream error;
- error << "Error in cmake code at\n"
- << arg.FilePath << ":" << arg.Line << ":\n"
- << "A command failed during the invocation of macro \""
- << this->Args[0].c_str() << "\".";
- cmSystemTools::Error(error.str().c_str());
+ // The error message should have already included the call stack
+ // so we do not need to report an error here.
+ inStatus.SetNestedError(true);
return false;
}
if (status.GetReturnInvoked())
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index df93dc0..a3c3846 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -280,6 +280,134 @@ bool cmMakefile::CommandExists(const char* name) const
return this->GetCMakeInstance()->CommandExists(name);
}
+//----------------------------------------------------------------------------
+// Helper function to print a block of text with every line following
+// a given prefix.
+void cmMakefilePrintPrefixed(std::ostream& os, const char* prefix,
+ std::string const& msg)
+{
+ bool newline = true;
+ for(const char* c = msg.c_str(); *c; ++c)
+ {
+ if(newline)
+ {
+ os << prefix;
+ newline = false;
+ }
+ os << *c;
+ if(*c == '\n')
+ {
+ newline = true;
+ }
+ }
+ if(!newline)
+ {
+ os << "\n";
+ }
+}
+
+//----------------------------------------------------------------------------
+void cmMakefile::IssueError(std::string const& msg) const
+{
+ this->IssueMessage(msg, true);
+}
+
+//----------------------------------------------------------------------------
+void cmMakefile::IssueWarning(std::string const& msg) const
+{
+ this->IssueMessage(msg, false);
+}
+
+//----------------------------------------------------------------------------
+void cmMakefile::IssueMessage(std::string const& text, bool isError) const
+{
+ cmOStringStream msg;
+
+ // Construct the message header.
+ if(isError)
+ {
+ msg << "CMake Error:";
+ }
+ else
+ {
+ msg << "CMake Warning:";
+ }
+
+ // Add the immediate context.
+ CallStackType::const_reverse_iterator i = this->CallStack.rbegin();
+ if(i != this->CallStack.rend())
+ {
+ if(isError)
+ {
+ i->Status->SetNestedError(true);
+ }
+ cmListFileContext const& lfc = *i->Context;
+ msg
+ << " at "
+ << this->LocalGenerator->Convert(lfc.FilePath.c_str(),
+ cmLocalGenerator::HOME)
+ << ":" << lfc.Line << " " << lfc.Name;
+ ++i;
+ }
+
+ // Add the message text.
+ msg << " {\n";
+ cmMakefilePrintPrefixed(msg, " ", text);
+ msg << "}";
+
+ // Add the rest of the context.
+ if(i != this->CallStack.rend())
+ {
+ msg << " with call stack {\n";
+ while(i != this->CallStack.rend())
+ {
+ cmListFileContext const& lfc = *i->Context;
+ msg << " "
+ << this->LocalGenerator->Convert(lfc.FilePath.c_str(),
+ cmLocalGenerator::HOME)
+ << ":" << lfc.Line << " " << lfc.Name << "\n";
+ ++i;
+ }
+ msg << "}\n";
+ }
+ else
+ {
+ msg << "\n";
+ }
+
+ // Output the message.
+ if(isError)
+ {
+ cmSystemTools::SetErrorOccured();
+ cmSystemTools::Message(msg.str().c_str(), "Error");
+ }
+ else
+ {
+ cmSystemTools::Message(msg.str().c_str(), "Warning");
+ }
+}
+
+//----------------------------------------------------------------------------
+// Helper class to make sure the call stack is valid.
+class cmMakefileCall
+{
+public:
+ cmMakefileCall(cmMakefile* mf,
+ cmListFileContext const& lfc,
+ cmExecutionStatus& status): Makefile(mf)
+ {
+ cmMakefile::CallStackEntry entry = {&lfc, &status};
+ this->Makefile->CallStack.push_back(entry);
+ }
+ ~cmMakefileCall()
+ {
+ this->Makefile->CallStack.pop_back();
+ }
+private:
+ cmMakefile* Makefile;
+};
+
+//----------------------------------------------------------------------------
bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff,
cmExecutionStatus &status)
{
@@ -294,34 +422,30 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff,
std::string name = lff.Name;
- // execute the command
- cmCommand *rm =
- this->GetCMakeInstance()->GetCommand(name.c_str());
- if(rm)
- {
- // const char* versionValue
- // = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY");
- // int major = 0;
- // int minor = 0;
- // if ( versionValue )
- // {
- // sscanf(versionValue, "%d.%d", &major, &minor);
- // }
- cmCommand* usedCommand = rm->Clone();
- usedCommand->SetMakefile(this);
- bool keepCommand = false;
- if(usedCommand->GetEnabled() && !cmSystemTools::GetFatalErrorOccured() &&
- (!this->GetCMakeInstance()->GetScriptMode() ||
- usedCommand->IsScriptable()))
- {
- if(!usedCommand->InvokeInitialPass(lff.Arguments,status))
+ // 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.c_str()))
+ {
+ // 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()->GetScriptMode() || pcmd->IsScriptable()))
+ {
+ // Try invoking the command.
+ if(!pcmd->InvokeInitialPass(lff.Arguments,status) ||
+ status.GetNestedError())
{
- cmOStringStream error;
- error << "Error in cmake code at\n"
- << lff.FilePath << ":" << lff.Line << ":\n"
- << usedCommand->GetError() << std::endl
- << " Called from: " << this->GetListFileStack().c_str();
- cmSystemTools::Error(error.str().c_str());
+ if(!status.GetNestedError())
+ {
+ // The command invocation requested that we report an error.
+ this->IssueError(pcmd->GetError());
+ }
result = false;
if ( this->GetCMakeInstance()->GetScriptMode() )
{
@@ -331,38 +455,28 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff,
else
{
// use the command
- keepCommand = true;
- this->UsedCommands.push_back(usedCommand);
+ this->UsedCommands.push_back(pcmd.release());
}
}
else if ( this->GetCMakeInstance()->GetScriptMode()
- && !usedCommand->IsScriptable() )
+ && !pcmd->IsScriptable() )
{
- cmOStringStream error;
- error << "Error in cmake code at\n"
- << lff.FilePath << ":" << lff.Line << ":\n"
- << "Command " << usedCommand->GetName()
- << "() is not scriptable" << std::endl;
- cmSystemTools::Error(error.str().c_str());
+ std::string error = "Command ";
+ error += pcmd->GetName();
+ error += "() is not scriptable";
+ this->IssueError(error);
result = false;
cmSystemTools::SetFatalErrorOccured();
}
- // if the Cloned command was not used
- // then delete it
- if(!keepCommand)
- {
- delete usedCommand;
- }
}
else
{
if(!cmSystemTools::GetFatalErrorOccured())
{
- cmOStringStream error;
- error << "Error in cmake code at\n"
- << lff.FilePath << ":" << lff.Line << ":\n"
- << "Unknown CMake command \"" << lff.Name.c_str() << "\".";
- cmSystemTools::Error(error.str().c_str());
+ std::string error = "Unknown CMake command \"";
+ error += lff.Name;
+ error += "\".";
+ this->IssueError(error);
result = false;
cmSystemTools::SetFatalErrorOccured();
}
@@ -3152,8 +3266,8 @@ bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg,
switch (this->GetPolicyStatus(cmPolicies::CMP_0002))
{
case cmPolicies::WARN:
- msg = this->GetPolicies()->
- GetPolicyWarning(cmPolicies::CMP_0002);
+ this->IssueWarning(this->GetPolicies()->
+ GetPolicyWarning(cmPolicies::CMP_0002));
case cmPolicies::OLD:
return true;
case cmPolicies::REQUIRED_IF_USED:
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index db13452..b679ff4 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -41,6 +41,7 @@ class cmSourceFile;
class cmTest;
class cmVariableWatch;
class cmake;
+class cmMakefileCall;
/** \class cmMakefile
* \brief Process the input CMakeLists.txt file.
@@ -783,6 +784,10 @@ public:
void PopScope();
void RaiseScope(const char *var, const char *value);
+ /** Issue messages with the given text plus context information. */
+ void IssueWarning(std::string const& msg) const;
+ void IssueError(std::string const& msg) const;
+
protected:
// add link libraries and directories to the target
void AddGlobalLinkInformation(const char* name, cmTarget& target);
@@ -876,6 +881,18 @@ private:
// stack of list files being read
std::deque<cmStdString> ListFileStack;
+ // stack of commands being invoked.
+ struct CallStackEntry
+ {
+ cmListFileContext const* Context;
+ cmExecutionStatus* Status;
+ };
+ typedef std::deque<CallStackEntry> CallStackType;
+ CallStackType CallStack;
+ friend class cmMakefileCall;
+
+ void IssueMessage(std::string const& text, bool isError) const;
+
cmTarget* FindBasicTarget(const char* name);
std::vector<cmTarget*> ImportedTargetsOwned;
std::map<cmStdString, cmTarget*> ImportedTargets;
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index 0f9c05f..382507b 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -86,7 +86,7 @@ cmPolicies::cmPolicies()
// define all the policies
this->DefinePolicy(CMP_0000, "CMP_0000",
"Missing a CMake version specification. You must have a cmake_policy "
- "or cmake_minimum_required call.",
+ "call.",
"CMake requires that projects specify what version of CMake they have "
"been written to. The easiest way to do this is by placing a call to "
"cmake_policy at the top of your CMakeLists file. For example: "
@@ -348,10 +348,11 @@ std::string cmPolicies::GetPolicyWarning(cmPolicies::PolicyID id)
cmOStringStream msg;
msg <<
- "WARNING: Policy " << pos->second->IDString << " is not set: "
- "" << pos->second->ShortDescription << " "
+ "Policy " << pos->second->IDString << " is not set: "
+ "" << pos->second->ShortDescription << "\n"
"Run \"cmake --help-policy " << pos->second->IDString << "\" for "
- "policy details. Use the cmake_policy command to set the policy "
+ "policy details.\n"
+ "Use the cmake_policy command to set the policy "
"and suppress this warning.";
return msg.str();
}
@@ -370,21 +371,19 @@ std::string cmPolicies::GetRequiredPolicyError(cmPolicies::PolicyID id)
}
cmOStringStream error;
- error <<
- "Error " <<
- pos->second->IDString << ": " <<
- pos->second->ShortDescription <<
- " This behavior is required now. You can suppress this message by "
- "specifying that your listfile is written to handle this new "
- "behavior by adding either\n" <<
- "cmake_policy (NEW " <<
- pos->second->IDString << ")\n or \n. " <<
- "cmake_policy (VERSION " <<
- pos->second->GetVersionString() << " ) or later."
- "Run cmake --help-policy " <<
- pos->second->IDString << " for more information.";
+ error <<
+ "Policy " << pos->second->IDString << " is not set to NEW: "
+ "" << pos->second->ShortDescription << "\n"
+ "Run \"cmake --help-policy " << pos->second->IDString << "\" for "
+ "policy details.\n"
+ "CMake now requires this policy to be set to NEW by the project. "
+ "The policy may be set explicitly using the code\n"
+ " cmake_policy(SET " << pos->second->IDString << " NEW)\n"
+ "or by upgrading all policies with the code\n"
+ " cmake_policy(VERSION " << pos->second->GetVersionString() <<
+ ") # or later\n"
+ "Run \"cmake --help-command cmake_policy\" for more information.";
return error.str();
-
}
///! Get the default status for a policy