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
This commit is contained in:
parent
41a59e211e
commit
680104a490
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
// 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()))
|
||||
{
|
||||
// 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()))
|
||||
// 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()))
|
||||
{
|
||||
if(!usedCommand->InvokeInitialPass(lff.Arguments,status))
|
||||
// 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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue